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