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