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