xref: /freebsd/contrib/libfido2/src/assert.c (revision 96950419f15510287080c557174e0d8409f06956)
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
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 		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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
727 fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
728 {
729 	return (assert->cdh.ptr);
730 }
731 
732 size_t
733 fido_assert_clientdata_hash_len(const fido_assert_t *assert)
734 {
735 	return (assert->cdh.len);
736 }
737 
738 fido_assert_t *
739 fido_assert_new(void)
740 {
741 	return (calloc(1, sizeof(fido_assert_t)));
742 }
743 
744 void
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
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
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
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
802 fido_assert_count(const fido_assert_t *assert)
803 {
804 	return (assert->stmt_len);
805 }
806 
807 const char *
808 fido_assert_rp_id(const fido_assert_t *assert)
809 {
810 	return (assert->rp_id);
811 }
812 
813 uint8_t
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
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 *
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
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 *
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
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 *
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
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 *
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
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 *
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 *
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 *
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 *
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
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 *
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
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 *
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
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
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
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
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
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
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