1 /*
2 * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7 #include <openssl/sha.h>
8
9 #include "fido.h"
10 #include "fido/es256.h"
11 #include "fido/rs256.h"
12 #include "fido/eddsa.h"
13
14 static int
adjust_assert_count(const cbor_item_t * key,const cbor_item_t * val,void * arg)15 adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
16 {
17 fido_assert_t *assert = arg;
18 uint64_t n;
19
20 /* numberOfCredentials; see section 6.2 */
21 if (cbor_isa_uint(key) == false ||
22 cbor_int_get_width(key) != CBOR_INT_8 ||
23 cbor_get_uint8(key) != 5) {
24 fido_log_debug("%s: cbor_type", __func__);
25 return (0); /* ignore */
26 }
27
28 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
29 fido_log_debug("%s: cbor_decode_uint64", __func__);
30 return (-1);
31 }
32
33 if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
34 (size_t)n < assert->stmt_cnt) {
35 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
36 __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
37 return (-1);
38 }
39
40 if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
41 fido_log_debug("%s: fido_assert_set_count", __func__);
42 return (-1);
43 }
44
45 assert->stmt_len = 0; /* XXX */
46
47 return (0);
48 }
49
50 static int
parse_assert_reply(const cbor_item_t * key,const cbor_item_t * val,void * arg)51 parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
52 {
53 fido_assert_stmt *stmt = arg;
54
55 if (cbor_isa_uint(key) == false ||
56 cbor_int_get_width(key) != CBOR_INT_8) {
57 fido_log_debug("%s: cbor type", __func__);
58 return (0); /* ignore */
59 }
60
61 switch (cbor_get_uint8(key)) {
62 case 1: /* credential id */
63 return (cbor_decode_cred_id(val, &stmt->id));
64 case 2: /* authdata */
65 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
66 &stmt->authdata, &stmt->authdata_ext));
67 case 3: /* signature */
68 return (fido_blob_decode(val, &stmt->sig));
69 case 4: /* user attributes */
70 return (cbor_decode_user(val, &stmt->user));
71 case 7: /* large blob key */
72 return (fido_blob_decode(val, &stmt->largeblob_key));
73 default: /* ignore */
74 fido_log_debug("%s: cbor type", __func__);
75 return (0);
76 }
77 }
78
79 static int
fido_dev_get_assert_tx(fido_dev_t * dev,fido_assert_t * assert,const es256_pk_t * pk,const fido_blob_t * ecdh,const char * pin,int * ms)80 fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
81 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
82 {
83 fido_blob_t f;
84 fido_opt_t uv = assert->uv;
85 cbor_item_t *argv[7];
86 const uint8_t cmd = CTAP_CBOR_ASSERT;
87 int r;
88
89 memset(argv, 0, sizeof(argv));
90 memset(&f, 0, sizeof(f));
91
92 /* do we have everything we need? */
93 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
94 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
95 (void *)assert->rp_id, (void *)assert->cdh.ptr);
96 r = FIDO_ERR_INVALID_ARGUMENT;
97 goto fail;
98 }
99
100 if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
101 (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
102 fido_log_debug("%s: cbor encode", __func__);
103 r = FIDO_ERR_INTERNAL;
104 goto fail;
105 }
106
107 /* allowed credentials */
108 if (assert->allow_list.len) {
109 const fido_blob_array_t *cl = &assert->allow_list;
110 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
111 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
112 r = FIDO_ERR_INTERNAL;
113 goto fail;
114 }
115 }
116
117 if (assert->ext.mask)
118 if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
119 pk)) == NULL) {
120 fido_log_debug("%s: cbor_encode_assert_ext", __func__);
121 r = FIDO_ERR_INTERNAL;
122 goto fail;
123 }
124
125 /* user verification */
126 if (pin != NULL || (uv == FIDO_OPT_TRUE &&
127 fido_dev_supports_permissions(dev))) {
128 if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
129 pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) {
130 fido_log_debug("%s: cbor_add_uv_params", __func__);
131 goto fail;
132 }
133 uv = FIDO_OPT_OMIT;
134 }
135
136 /* options */
137 if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
138 if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
139 fido_log_debug("%s: cbor_encode_assert_opt", __func__);
140 r = FIDO_ERR_INTERNAL;
141 goto fail;
142 }
143
144 /* frame and transmit */
145 if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
146 fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
147 fido_log_debug("%s: fido_tx", __func__);
148 r = FIDO_ERR_TX;
149 goto fail;
150 }
151
152 r = FIDO_OK;
153 fail:
154 cbor_vector_free(argv, nitems(argv));
155 free(f.ptr);
156
157 return (r);
158 }
159
160 static int
fido_dev_get_assert_rx(fido_dev_t * dev,fido_assert_t * assert,int * ms)161 fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
162 {
163 unsigned char reply[FIDO_MAXMSG];
164 int reply_len;
165 int r;
166
167 fido_assert_reset_rx(assert);
168
169 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
170 ms)) < 0) {
171 fido_log_debug("%s: fido_rx", __func__);
172 return (FIDO_ERR_RX);
173 }
174
175 /* start with room for a single assertion */
176 if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
177 return (FIDO_ERR_INTERNAL);
178
179 assert->stmt_len = 0;
180 assert->stmt_cnt = 1;
181
182 /* adjust as needed */
183 if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
184 adjust_assert_count)) != FIDO_OK) {
185 fido_log_debug("%s: adjust_assert_count", __func__);
186 return (r);
187 }
188
189 /* parse the first assertion */
190 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
191 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
192 fido_log_debug("%s: parse_assert_reply", __func__);
193 return (r);
194 }
195
196 assert->stmt_len++;
197
198 return (FIDO_OK);
199 }
200
201 static int
fido_get_next_assert_tx(fido_dev_t * dev,int * ms)202 fido_get_next_assert_tx(fido_dev_t *dev, int *ms)
203 {
204 const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
205
206 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
207 fido_log_debug("%s: fido_tx", __func__);
208 return (FIDO_ERR_TX);
209 }
210
211 return (FIDO_OK);
212 }
213
214 static int
fido_get_next_assert_rx(fido_dev_t * dev,fido_assert_t * assert,int * ms)215 fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
216 {
217 unsigned char reply[FIDO_MAXMSG];
218 int reply_len;
219 int r;
220
221 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
222 ms)) < 0) {
223 fido_log_debug("%s: fido_rx", __func__);
224 return (FIDO_ERR_RX);
225 }
226
227 /* sanity check */
228 if (assert->stmt_len >= assert->stmt_cnt) {
229 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
230 assert->stmt_len, assert->stmt_cnt);
231 return (FIDO_ERR_INTERNAL);
232 }
233
234 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
235 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
236 fido_log_debug("%s: parse_assert_reply", __func__);
237 return (r);
238 }
239
240 return (FIDO_OK);
241 }
242
243 static int
fido_dev_get_assert_wait(fido_dev_t * dev,fido_assert_t * assert,const es256_pk_t * pk,const fido_blob_t * ecdh,const char * pin,int * ms)244 fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
245 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
246 {
247 int r;
248
249 if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin,
250 ms)) != FIDO_OK ||
251 (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
252 return (r);
253
254 while (assert->stmt_len < assert->stmt_cnt) {
255 if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK ||
256 (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
257 return (r);
258 assert->stmt_len++;
259 }
260
261 return (FIDO_OK);
262 }
263
264 static int
decrypt_hmac_secrets(const fido_dev_t * dev,fido_assert_t * assert,const fido_blob_t * key)265 decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
266 const fido_blob_t *key)
267 {
268 for (size_t i = 0; i < assert->stmt_cnt; i++) {
269 fido_assert_stmt *stmt = &assert->stmt[i];
270 if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
271 if (aes256_cbc_dec(dev, key,
272 &stmt->authdata_ext.hmac_secret_enc,
273 &stmt->hmac_secret) < 0) {
274 fido_log_debug("%s: aes256_cbc_dec %zu",
275 __func__, i);
276 return (-1);
277 }
278 }
279 }
280
281 return (0);
282 }
283
284 int
fido_dev_get_assert(fido_dev_t * dev,fido_assert_t * assert,const char * pin)285 fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
286 {
287 fido_blob_t *ecdh = NULL;
288 es256_pk_t *pk = NULL;
289 int ms = dev->timeout_ms;
290 int r;
291
292 #ifdef USE_WINHELLO
293 if (dev->flags & FIDO_DEV_WINHELLO)
294 return (fido_winhello_get_assert(dev, assert, pin, ms));
295 #endif
296
297 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
298 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
299 (void *)assert->rp_id, (void *)assert->cdh.ptr);
300 return (FIDO_ERR_INVALID_ARGUMENT);
301 }
302
303 if (fido_dev_is_fido2(dev) == false) {
304 if (pin != NULL || assert->ext.mask != 0)
305 return (FIDO_ERR_UNSUPPORTED_OPTION);
306 return (u2f_authenticate(dev, assert, &ms));
307 }
308
309 if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
310 fido_dev_supports_permissions(dev)) ||
311 (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
312 if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
313 fido_log_debug("%s: fido_do_ecdh", __func__);
314 goto fail;
315 }
316 }
317
318 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms);
319 if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
320 if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
321 fido_log_debug("%s: decrypt_hmac_secrets", __func__);
322 r = FIDO_ERR_INTERNAL;
323 goto fail;
324 }
325
326 fail:
327 es256_pk_free(&pk);
328 fido_blob_free(&ecdh);
329
330 return (r);
331 }
332
333 int
fido_check_flags(uint8_t flags,fido_opt_t up,fido_opt_t uv)334 fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
335 {
336 fido_log_debug("%s: flags=%02x", __func__, flags);
337 fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
338
339 if (up == FIDO_OPT_TRUE &&
340 (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
341 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
342 return (-1); /* user not present */
343 }
344
345 if (uv == FIDO_OPT_TRUE &&
346 (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
347 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
348 return (-1); /* user not verified */
349 }
350
351 return (0);
352 }
353
354 static int
check_extensions(int authdata_ext,int ext)355 check_extensions(int authdata_ext, int ext)
356 {
357 /* XXX: largeBlobKey is not part of extensions map */
358 ext &= ~FIDO_EXT_LARGEBLOB_KEY;
359 if (authdata_ext != ext) {
360 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
361 authdata_ext, ext);
362 return (-1);
363 }
364
365 return (0);
366 }
367
368 int
fido_get_signed_hash(int cose_alg,fido_blob_t * dgst,const fido_blob_t * clientdata,const fido_blob_t * authdata_cbor)369 fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
370 const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
371 {
372 cbor_item_t *item = NULL;
373 unsigned char *authdata_ptr = NULL;
374 size_t authdata_len;
375 struct cbor_load_result cbor;
376 const EVP_MD *md = NULL;
377 EVP_MD_CTX *ctx = NULL;
378 int ok = -1;
379
380 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
381 &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
382 cbor_bytestring_is_definite(item) == false) {
383 fido_log_debug("%s: authdata", __func__);
384 goto fail;
385 }
386
387 authdata_ptr = cbor_bytestring_handle(item);
388 authdata_len = cbor_bytestring_length(item);
389
390 if (cose_alg != COSE_EDDSA) {
391 if (dgst->len < SHA256_DIGEST_LENGTH ||
392 (md = EVP_sha256()) == NULL ||
393 (ctx = EVP_MD_CTX_new()) == NULL ||
394 EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
395 EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 ||
396 EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
397 EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
398 fido_log_debug("%s: sha256", __func__);
399 goto fail;
400 }
401 dgst->len = SHA256_DIGEST_LENGTH;
402 } else {
403 if (SIZE_MAX - authdata_len < clientdata->len ||
404 dgst->len < authdata_len + clientdata->len) {
405 fido_log_debug("%s: memcpy", __func__);
406 goto fail;
407 }
408 memcpy(dgst->ptr, authdata_ptr, authdata_len);
409 memcpy(dgst->ptr + authdata_len, clientdata->ptr,
410 clientdata->len);
411 dgst->len = authdata_len + clientdata->len;
412 }
413
414 ok = 0;
415 fail:
416 if (item != NULL)
417 cbor_decref(&item);
418
419 EVP_MD_CTX_free(ctx);
420
421 return (ok);
422 }
423
424 int
fido_assert_verify(const fido_assert_t * assert,size_t idx,int cose_alg,const void * pk)425 fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
426 const void *pk)
427 {
428 unsigned char buf[1024]; /* XXX */
429 fido_blob_t dgst;
430 const fido_assert_stmt *stmt = NULL;
431 int ok = -1;
432 int r;
433
434 dgst.ptr = buf;
435 dgst.len = sizeof(buf);
436
437 if (idx >= assert->stmt_len || pk == NULL) {
438 r = FIDO_ERR_INVALID_ARGUMENT;
439 goto out;
440 }
441
442 stmt = &assert->stmt[idx];
443
444 /* do we have everything we need? */
445 if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
446 stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
447 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
448 __func__, (void *)assert->cdh.ptr, assert->rp_id,
449 (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
450 r = FIDO_ERR_INVALID_ARGUMENT;
451 goto out;
452 }
453
454 if (fido_check_flags(stmt->authdata.flags, assert->up,
455 assert->uv) < 0) {
456 fido_log_debug("%s: fido_check_flags", __func__);
457 r = FIDO_ERR_INVALID_PARAM;
458 goto out;
459 }
460
461 if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
462 fido_log_debug("%s: check_extensions", __func__);
463 r = FIDO_ERR_INVALID_PARAM;
464 goto out;
465 }
466
467 if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
468 fido_log_debug("%s: fido_check_rp_id", __func__);
469 r = FIDO_ERR_INVALID_PARAM;
470 goto out;
471 }
472
473 if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
474 &stmt->authdata_cbor) < 0) {
475 fido_log_debug("%s: fido_get_signed_hash", __func__);
476 r = FIDO_ERR_INTERNAL;
477 goto out;
478 }
479
480 switch (cose_alg) {
481 case COSE_ES256:
482 ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig);
483 break;
484 case COSE_RS256:
485 ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig);
486 break;
487 case COSE_EDDSA:
488 ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig);
489 break;
490 default:
491 fido_log_debug("%s: unsupported cose_alg %d", __func__,
492 cose_alg);
493 r = FIDO_ERR_UNSUPPORTED_OPTION;
494 goto out;
495 }
496
497 if (ok < 0)
498 r = FIDO_ERR_INVALID_SIG;
499 else
500 r = FIDO_OK;
501 out:
502 explicit_bzero(buf, sizeof(buf));
503
504 return (r);
505 }
506
507 int
fido_assert_set_clientdata(fido_assert_t * assert,const unsigned char * data,size_t data_len)508 fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
509 size_t data_len)
510 {
511 if (!fido_blob_is_empty(&assert->cdh) ||
512 fido_blob_set(&assert->cd, data, data_len) < 0) {
513 return (FIDO_ERR_INVALID_ARGUMENT);
514 }
515 if (fido_sha256(&assert->cdh, data, data_len) < 0) {
516 fido_blob_reset(&assert->cd);
517 return (FIDO_ERR_INTERNAL);
518 }
519
520 return (FIDO_OK);
521 }
522
523 int
fido_assert_set_clientdata_hash(fido_assert_t * assert,const unsigned char * hash,size_t hash_len)524 fido_assert_set_clientdata_hash(fido_assert_t *assert,
525 const unsigned char *hash, size_t hash_len)
526 {
527 if (!fido_blob_is_empty(&assert->cd) ||
528 fido_blob_set(&assert->cdh, hash, hash_len) < 0)
529 return (FIDO_ERR_INVALID_ARGUMENT);
530
531 return (FIDO_OK);
532 }
533
534 int
fido_assert_set_hmac_salt(fido_assert_t * assert,const unsigned char * salt,size_t salt_len)535 fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
536 size_t salt_len)
537 {
538 if ((salt_len != 32 && salt_len != 64) ||
539 fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
540 return (FIDO_ERR_INVALID_ARGUMENT);
541
542 return (FIDO_OK);
543 }
544
545 int
fido_assert_set_hmac_secret(fido_assert_t * assert,size_t idx,const unsigned char * secret,size_t secret_len)546 fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
547 const unsigned char *secret, size_t secret_len)
548 {
549 if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
550 fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
551 secret_len) < 0)
552 return (FIDO_ERR_INVALID_ARGUMENT);
553
554 return (FIDO_OK);
555 }
556
557 int
fido_assert_set_rp(fido_assert_t * assert,const char * id)558 fido_assert_set_rp(fido_assert_t *assert, const char *id)
559 {
560 if (assert->rp_id != NULL) {
561 free(assert->rp_id);
562 assert->rp_id = NULL;
563 }
564
565 if (id == NULL)
566 return (FIDO_ERR_INVALID_ARGUMENT);
567
568 if ((assert->rp_id = strdup(id)) == NULL)
569 return (FIDO_ERR_INTERNAL);
570
571 return (FIDO_OK);
572 }
573
574 int
fido_assert_allow_cred(fido_assert_t * assert,const unsigned char * ptr,size_t len)575 fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
576 size_t len)
577 {
578 fido_blob_t id;
579 fido_blob_t *list_ptr;
580 int r;
581
582 memset(&id, 0, sizeof(id));
583
584 if (assert->allow_list.len == SIZE_MAX) {
585 r = FIDO_ERR_INVALID_ARGUMENT;
586 goto fail;
587 }
588
589 if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
590 recallocarray(assert->allow_list.ptr, assert->allow_list.len,
591 assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
592 r = FIDO_ERR_INVALID_ARGUMENT;
593 goto fail;
594 }
595
596 list_ptr[assert->allow_list.len++] = id;
597 assert->allow_list.ptr = list_ptr;
598
599 return (FIDO_OK);
600 fail:
601 free(id.ptr);
602
603 return (r);
604
605 }
606
607 int
fido_assert_set_extensions(fido_assert_t * assert,int ext)608 fido_assert_set_extensions(fido_assert_t *assert, int ext)
609 {
610 if (ext == 0)
611 assert->ext.mask = 0;
612 else {
613 if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
614 return (FIDO_ERR_INVALID_ARGUMENT);
615 assert->ext.mask |= ext;
616 }
617
618 return (FIDO_OK);
619 }
620
621 int
fido_assert_set_options(fido_assert_t * assert,bool up,bool uv)622 fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
623 {
624 assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
625 assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
626
627 return (FIDO_OK);
628 }
629
630 int
fido_assert_set_up(fido_assert_t * assert,fido_opt_t up)631 fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
632 {
633 assert->up = up;
634
635 return (FIDO_OK);
636 }
637
638 int
fido_assert_set_uv(fido_assert_t * assert,fido_opt_t uv)639 fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
640 {
641 assert->uv = uv;
642
643 return (FIDO_OK);
644 }
645
646 const unsigned char *
fido_assert_clientdata_hash_ptr(const fido_assert_t * assert)647 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
648 {
649 return (assert->cdh.ptr);
650 }
651
652 size_t
fido_assert_clientdata_hash_len(const fido_assert_t * assert)653 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
654 {
655 return (assert->cdh.len);
656 }
657
658 fido_assert_t *
fido_assert_new(void)659 fido_assert_new(void)
660 {
661 return (calloc(1, sizeof(fido_assert_t)));
662 }
663
664 void
fido_assert_reset_tx(fido_assert_t * assert)665 fido_assert_reset_tx(fido_assert_t *assert)
666 {
667 free(assert->rp_id);
668 fido_blob_reset(&assert->cd);
669 fido_blob_reset(&assert->cdh);
670 fido_blob_reset(&assert->ext.hmac_salt);
671 fido_free_blob_array(&assert->allow_list);
672 memset(&assert->ext, 0, sizeof(assert->ext));
673 memset(&assert->allow_list, 0, sizeof(assert->allow_list));
674 assert->rp_id = NULL;
675 assert->up = FIDO_OPT_OMIT;
676 assert->uv = FIDO_OPT_OMIT;
677 }
678
fido_assert_reset_extattr(fido_assert_extattr_t * ext)679 static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
680 {
681 fido_blob_reset(&ext->hmac_secret_enc);
682 fido_blob_reset(&ext->blob);
683 memset(ext, 0, sizeof(*ext));
684 }
685
686 void
fido_assert_reset_rx(fido_assert_t * assert)687 fido_assert_reset_rx(fido_assert_t *assert)
688 {
689 for (size_t i = 0; i < assert->stmt_cnt; i++) {
690 free(assert->stmt[i].user.icon);
691 free(assert->stmt[i].user.name);
692 free(assert->stmt[i].user.display_name);
693 fido_blob_reset(&assert->stmt[i].user.id);
694 fido_blob_reset(&assert->stmt[i].id);
695 fido_blob_reset(&assert->stmt[i].hmac_secret);
696 fido_blob_reset(&assert->stmt[i].authdata_cbor);
697 fido_blob_reset(&assert->stmt[i].largeblob_key);
698 fido_blob_reset(&assert->stmt[i].sig);
699 fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
700 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
701 }
702 free(assert->stmt);
703 assert->stmt = NULL;
704 assert->stmt_len = 0;
705 assert->stmt_cnt = 0;
706 }
707
708 void
fido_assert_free(fido_assert_t ** assert_p)709 fido_assert_free(fido_assert_t **assert_p)
710 {
711 fido_assert_t *assert;
712
713 if (assert_p == NULL || (assert = *assert_p) == NULL)
714 return;
715 fido_assert_reset_tx(assert);
716 fido_assert_reset_rx(assert);
717 free(assert);
718 *assert_p = NULL;
719 }
720
721 size_t
fido_assert_count(const fido_assert_t * assert)722 fido_assert_count(const fido_assert_t *assert)
723 {
724 return (assert->stmt_len);
725 }
726
727 const char *
fido_assert_rp_id(const fido_assert_t * assert)728 fido_assert_rp_id(const fido_assert_t *assert)
729 {
730 return (assert->rp_id);
731 }
732
733 uint8_t
fido_assert_flags(const fido_assert_t * assert,size_t idx)734 fido_assert_flags(const fido_assert_t *assert, size_t idx)
735 {
736 if (idx >= assert->stmt_len)
737 return (0);
738
739 return (assert->stmt[idx].authdata.flags);
740 }
741
742 uint32_t
fido_assert_sigcount(const fido_assert_t * assert,size_t idx)743 fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
744 {
745 if (idx >= assert->stmt_len)
746 return (0);
747
748 return (assert->stmt[idx].authdata.sigcount);
749 }
750
751 const unsigned char *
fido_assert_authdata_ptr(const fido_assert_t * assert,size_t idx)752 fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
753 {
754 if (idx >= assert->stmt_len)
755 return (NULL);
756
757 return (assert->stmt[idx].authdata_cbor.ptr);
758 }
759
760 size_t
fido_assert_authdata_len(const fido_assert_t * assert,size_t idx)761 fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
762 {
763 if (idx >= assert->stmt_len)
764 return (0);
765
766 return (assert->stmt[idx].authdata_cbor.len);
767 }
768
769 const unsigned char *
fido_assert_sig_ptr(const fido_assert_t * assert,size_t idx)770 fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
771 {
772 if (idx >= assert->stmt_len)
773 return (NULL);
774
775 return (assert->stmt[idx].sig.ptr);
776 }
777
778 size_t
fido_assert_sig_len(const fido_assert_t * assert,size_t idx)779 fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
780 {
781 if (idx >= assert->stmt_len)
782 return (0);
783
784 return (assert->stmt[idx].sig.len);
785 }
786
787 const unsigned char *
fido_assert_id_ptr(const fido_assert_t * assert,size_t idx)788 fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
789 {
790 if (idx >= assert->stmt_len)
791 return (NULL);
792
793 return (assert->stmt[idx].id.ptr);
794 }
795
796 size_t
fido_assert_id_len(const fido_assert_t * assert,size_t idx)797 fido_assert_id_len(const fido_assert_t *assert, size_t idx)
798 {
799 if (idx >= assert->stmt_len)
800 return (0);
801
802 return (assert->stmt[idx].id.len);
803 }
804
805 const unsigned char *
fido_assert_user_id_ptr(const fido_assert_t * assert,size_t idx)806 fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
807 {
808 if (idx >= assert->stmt_len)
809 return (NULL);
810
811 return (assert->stmt[idx].user.id.ptr);
812 }
813
814 size_t
fido_assert_user_id_len(const fido_assert_t * assert,size_t idx)815 fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
816 {
817 if (idx >= assert->stmt_len)
818 return (0);
819
820 return (assert->stmt[idx].user.id.len);
821 }
822
823 const char *
fido_assert_user_icon(const fido_assert_t * assert,size_t idx)824 fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
825 {
826 if (idx >= assert->stmt_len)
827 return (NULL);
828
829 return (assert->stmt[idx].user.icon);
830 }
831
832 const char *
fido_assert_user_name(const fido_assert_t * assert,size_t idx)833 fido_assert_user_name(const fido_assert_t *assert, size_t idx)
834 {
835 if (idx >= assert->stmt_len)
836 return (NULL);
837
838 return (assert->stmt[idx].user.name);
839 }
840
841 const char *
fido_assert_user_display_name(const fido_assert_t * assert,size_t idx)842 fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
843 {
844 if (idx >= assert->stmt_len)
845 return (NULL);
846
847 return (assert->stmt[idx].user.display_name);
848 }
849
850 const unsigned char *
fido_assert_hmac_secret_ptr(const fido_assert_t * assert,size_t idx)851 fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
852 {
853 if (idx >= assert->stmt_len)
854 return (NULL);
855
856 return (assert->stmt[idx].hmac_secret.ptr);
857 }
858
859 size_t
fido_assert_hmac_secret_len(const fido_assert_t * assert,size_t idx)860 fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
861 {
862 if (idx >= assert->stmt_len)
863 return (0);
864
865 return (assert->stmt[idx].hmac_secret.len);
866 }
867
868 const unsigned char *
fido_assert_largeblob_key_ptr(const fido_assert_t * assert,size_t idx)869 fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
870 {
871 if (idx >= assert->stmt_len)
872 return (NULL);
873
874 return (assert->stmt[idx].largeblob_key.ptr);
875 }
876
877 size_t
fido_assert_largeblob_key_len(const fido_assert_t * assert,size_t idx)878 fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
879 {
880 if (idx >= assert->stmt_len)
881 return (0);
882
883 return (assert->stmt[idx].largeblob_key.len);
884 }
885
886 const unsigned char *
fido_assert_blob_ptr(const fido_assert_t * assert,size_t idx)887 fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
888 {
889 if (idx >= assert->stmt_len)
890 return (NULL);
891
892 return (assert->stmt[idx].authdata_ext.blob.ptr);
893 }
894
895 size_t
fido_assert_blob_len(const fido_assert_t * assert,size_t idx)896 fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
897 {
898 if (idx >= assert->stmt_len)
899 return (0);
900
901 return (assert->stmt[idx].authdata_ext.blob.len);
902 }
903
904 static void
fido_assert_clean_authdata(fido_assert_stmt * stmt)905 fido_assert_clean_authdata(fido_assert_stmt *stmt)
906 {
907 fido_blob_reset(&stmt->authdata_cbor);
908 fido_assert_reset_extattr(&stmt->authdata_ext);
909 memset(&stmt->authdata, 0, sizeof(stmt->authdata));
910 }
911
912 int
fido_assert_set_authdata(fido_assert_t * assert,size_t idx,const unsigned char * ptr,size_t len)913 fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
914 const unsigned char *ptr, size_t len)
915 {
916 cbor_item_t *item = NULL;
917 fido_assert_stmt *stmt = NULL;
918 struct cbor_load_result cbor;
919 int r;
920
921 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
922 return (FIDO_ERR_INVALID_ARGUMENT);
923
924 stmt = &assert->stmt[idx];
925 fido_assert_clean_authdata(stmt);
926
927 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
928 fido_log_debug("%s: cbor_load", __func__);
929 r = FIDO_ERR_INVALID_ARGUMENT;
930 goto fail;
931 }
932
933 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
934 &stmt->authdata, &stmt->authdata_ext) < 0) {
935 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
936 r = FIDO_ERR_INVALID_ARGUMENT;
937 goto fail;
938 }
939
940 r = FIDO_OK;
941 fail:
942 if (item != NULL)
943 cbor_decref(&item);
944
945 if (r != FIDO_OK)
946 fido_assert_clean_authdata(stmt);
947
948 return (r);
949 }
950
951 int
fido_assert_set_authdata_raw(fido_assert_t * assert,size_t idx,const unsigned char * ptr,size_t len)952 fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
953 const unsigned char *ptr, size_t len)
954 {
955 cbor_item_t *item = NULL;
956 fido_assert_stmt *stmt = NULL;
957 int r;
958
959 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
960 return (FIDO_ERR_INVALID_ARGUMENT);
961
962 stmt = &assert->stmt[idx];
963 fido_assert_clean_authdata(stmt);
964
965 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
966 fido_log_debug("%s: cbor_build_bytestring", __func__);
967 r = FIDO_ERR_INTERNAL;
968 goto fail;
969 }
970
971 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
972 &stmt->authdata, &stmt->authdata_ext) < 0) {
973 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
974 r = FIDO_ERR_INVALID_ARGUMENT;
975 goto fail;
976 }
977
978 r = FIDO_OK;
979 fail:
980 if (item != NULL)
981 cbor_decref(&item);
982
983 if (r != FIDO_OK)
984 fido_assert_clean_authdata(stmt);
985
986 return (r);
987 }
988
989 int
fido_assert_set_sig(fido_assert_t * a,size_t idx,const unsigned char * ptr,size_t len)990 fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
991 size_t len)
992 {
993 if (idx >= a->stmt_len || ptr == NULL || len == 0)
994 return (FIDO_ERR_INVALID_ARGUMENT);
995 if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
996 return (FIDO_ERR_INTERNAL);
997
998 return (FIDO_OK);
999 }
1000
1001 /* XXX shrinking leaks memory; fortunately that shouldn't happen */
1002 int
fido_assert_set_count(fido_assert_t * assert,size_t n)1003 fido_assert_set_count(fido_assert_t *assert, size_t n)
1004 {
1005 void *new_stmt;
1006
1007 #ifdef FIDO_FUZZ
1008 if (n > UINT8_MAX) {
1009 fido_log_debug("%s: n > UINT8_MAX", __func__);
1010 return (FIDO_ERR_INTERNAL);
1011 }
1012 #endif
1013
1014 new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1015 sizeof(fido_assert_stmt));
1016 if (new_stmt == NULL)
1017 return (FIDO_ERR_INTERNAL);
1018
1019 assert->stmt = new_stmt;
1020 assert->stmt_cnt = n;
1021 assert->stmt_len = n;
1022
1023 return (FIDO_OK);
1024 }
1025