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
15static int
16adjust_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
51static int
52parse_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
84static int
85fido_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;
158fail:
159	cbor_vector_free(argv, nitems(argv));
160	free(f.ptr);
161
162	return (r);
163}
164
165static int
166fido_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;
209out:
210	freezero(msg, FIDO_MAXMSG);
211
212	return (r);
213}
214
215static int
216fido_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
228static int
229fido_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;
261out:
262	freezero(msg, FIDO_MAXMSG);
263
264	return (r);
265}
266
267static int
268fido_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
288static int
289decrypt_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
308int
309fido_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
350fail:
351	es256_pk_free(&pk);
352	fido_blob_free(&ecdh);
353
354	return (r);
355}
356
357int
358fido_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
378static int
379check_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
392static int
393get_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
416static int
417get_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
440static int
441get_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
455int
456fido_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	}
490fail:
491	if (item != NULL)
492		cbor_decref(&item);
493
494	return (ok);
495}
496
497int
498fido_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;
577out:
578	explicit_bzero(buf, sizeof(buf));
579
580	return (r);
581}
582
583int
584fido_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
599int
600fido_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
610int
611fido_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
621int
622fido_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
633int
634fido_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
651int
652fido_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
668int
669fido_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
678int
679fido_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);
704fail:
705	free(id.ptr);
706
707	return (r);
708}
709
710int
711fido_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
719int
720fido_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
733int
734fido_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
742int
743fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
744{
745	assert->up = up;
746
747	return (FIDO_OK);
748}
749
750int
751fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
752{
753	assert->uv = uv;
754
755	return (FIDO_OK);
756}
757
758const unsigned char *
759fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
760{
761	return (assert->cdh.ptr);
762}
763
764size_t
765fido_assert_clientdata_hash_len(const fido_assert_t *assert)
766{
767	return (assert->cdh.len);
768}
769
770fido_assert_t *
771fido_assert_new(void)
772{
773	return (calloc(1, sizeof(fido_assert_t)));
774}
775
776void
777fido_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
792static void
793fido_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
800void
801fido_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
823void
824fido_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
836size_t
837fido_assert_count(const fido_assert_t *assert)
838{
839	return (assert->stmt_len);
840}
841
842const char *
843fido_assert_rp_id(const fido_assert_t *assert)
844{
845	return (assert->rp_id);
846}
847
848uint8_t
849fido_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
857uint32_t
858fido_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
866const unsigned char *
867fido_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
875size_t
876fido_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
884const unsigned char *
885fido_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
893size_t
894fido_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
902const unsigned char *
903fido_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
911size_t
912fido_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
920const unsigned char *
921fido_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
929size_t
930fido_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
938const unsigned char *
939fido_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
947size_t
948fido_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
956const char *
957fido_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
965const char *
966fido_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
974const char *
975fido_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
983const unsigned char *
984fido_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
992size_t
993fido_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
1001const unsigned char *
1002fido_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
1010size_t
1011fido_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
1019const unsigned char *
1020fido_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
1028size_t
1029fido_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
1037static void
1038fido_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
1046int
1047fido_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;
1081fail:
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
1091int
1092fido_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;
1125fail:
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
1135int
1136fido_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 */
1148int
1149fido_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