jcs's openbsd hax
openbsd
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
14static int
15adjust_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
50static int
51parse_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
79static int
80fido_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;
153fail:
154 cbor_vector_free(argv, nitems(argv));
155 free(f.ptr);
156
157 return (r);
158}
159
160static int
161fido_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
201static int
202fido_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
214static int
215fido_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
243static int
244fido_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
264static int
265decrypt_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
284int
285fido_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
326fail:
327 es256_pk_free(&pk);
328 fido_blob_free(&ecdh);
329
330 return (r);
331}
332
333int
334fido_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
354static int
355check_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
368int
369fido_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;
415fail:
416 if (item != NULL)
417 cbor_decref(&item);
418
419 EVP_MD_CTX_free(ctx);
420
421 return (ok);
422}
423
424int
425fido_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;
501out:
502 explicit_bzero(buf, sizeof(buf));
503
504 return (r);
505}
506
507int
508fido_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
523int
524fido_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
534int
535fido_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
545int
546fido_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
557int
558fido_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
574int
575fido_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);
600fail:
601 free(id.ptr);
602
603 return (r);
604
605}
606
607int
608fido_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
621int
622fido_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
630int
631fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
632{
633 assert->up = up;
634
635 return (FIDO_OK);
636}
637
638int
639fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
640{
641 assert->uv = uv;
642
643 return (FIDO_OK);
644}
645
646const unsigned char *
647fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
648{
649 return (assert->cdh.ptr);
650}
651
652size_t
653fido_assert_clientdata_hash_len(const fido_assert_t *assert)
654{
655 return (assert->cdh.len);
656}
657
658fido_assert_t *
659fido_assert_new(void)
660{
661 return (calloc(1, sizeof(fido_assert_t)));
662}
663
664void
665fido_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
679static 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
686void
687fido_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
708void
709fido_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
721size_t
722fido_assert_count(const fido_assert_t *assert)
723{
724 return (assert->stmt_len);
725}
726
727const char *
728fido_assert_rp_id(const fido_assert_t *assert)
729{
730 return (assert->rp_id);
731}
732
733uint8_t
734fido_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
742uint32_t
743fido_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
751const unsigned char *
752fido_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
760size_t
761fido_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
769const unsigned char *
770fido_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
778size_t
779fido_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
787const unsigned char *
788fido_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
796size_t
797fido_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
805const unsigned char *
806fido_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
814size_t
815fido_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
823const char *
824fido_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
832const char *
833fido_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
841const char *
842fido_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
850const unsigned char *
851fido_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
859size_t
860fido_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
868const unsigned char *
869fido_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
877size_t
878fido_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
886const unsigned char *
887fido_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
895size_t
896fido_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
904static void
905fido_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
912int
913fido_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;
941fail:
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
951int
952fido_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;
979fail:
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
989int
990fido_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 */
1002int
1003fido_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}