xref: /freebsd/contrib/libfido2/src/assert.c (revision f540a43052c12c76d3453ead881248d5467a1ab0)
10afa8e06SEd Maste /*
20afa8e06SEd Maste  * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
50afa8e06SEd Maste  */
60afa8e06SEd Maste 
70afa8e06SEd Maste #include <openssl/sha.h>
80afa8e06SEd Maste 
90afa8e06SEd Maste #include "fido.h"
100afa8e06SEd Maste #include "fido/es256.h"
110afa8e06SEd Maste #include "fido/rs256.h"
120afa8e06SEd Maste #include "fido/eddsa.h"
130afa8e06SEd Maste 
140afa8e06SEd Maste static int
150afa8e06SEd Maste adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
160afa8e06SEd Maste {
170afa8e06SEd Maste 	fido_assert_t	*assert = arg;
180afa8e06SEd Maste 	uint64_t	 n;
190afa8e06SEd Maste 
200afa8e06SEd Maste 	/* numberOfCredentials; see section 6.2 */
210afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
220afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8 ||
230afa8e06SEd Maste 	    cbor_get_uint8(key) != 5) {
240afa8e06SEd Maste 		fido_log_debug("%s: cbor_type", __func__);
250afa8e06SEd Maste 		return (0); /* ignore */
260afa8e06SEd Maste 	}
270afa8e06SEd Maste 
280afa8e06SEd Maste 	if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
290afa8e06SEd Maste 		fido_log_debug("%s: cbor_decode_uint64", __func__);
300afa8e06SEd Maste 		return (-1);
310afa8e06SEd Maste 	}
320afa8e06SEd Maste 
330afa8e06SEd Maste 	if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
340afa8e06SEd Maste 	    (size_t)n < assert->stmt_cnt) {
350afa8e06SEd Maste 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
360afa8e06SEd Maste 		    __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
370afa8e06SEd Maste 		return (-1);
380afa8e06SEd Maste 	}
390afa8e06SEd Maste 
400afa8e06SEd Maste 	if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
410afa8e06SEd Maste 		fido_log_debug("%s: fido_assert_set_count", __func__);
420afa8e06SEd Maste 		return (-1);
430afa8e06SEd Maste 	}
440afa8e06SEd Maste 
450afa8e06SEd Maste 	assert->stmt_len = 0; /* XXX */
460afa8e06SEd Maste 
470afa8e06SEd Maste 	return (0);
480afa8e06SEd Maste }
490afa8e06SEd Maste 
500afa8e06SEd Maste static int
510afa8e06SEd Maste parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
520afa8e06SEd Maste {
530afa8e06SEd Maste 	fido_assert_stmt *stmt = arg;
540afa8e06SEd Maste 
550afa8e06SEd Maste 	if (cbor_isa_uint(key) == false ||
560afa8e06SEd Maste 	    cbor_int_get_width(key) != CBOR_INT_8) {
570afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
580afa8e06SEd Maste 		return (0); /* ignore */
590afa8e06SEd Maste 	}
600afa8e06SEd Maste 
610afa8e06SEd Maste 	switch (cbor_get_uint8(key)) {
620afa8e06SEd Maste 	case 1: /* credential id */
630afa8e06SEd Maste 		return (cbor_decode_cred_id(val, &stmt->id));
640afa8e06SEd Maste 	case 2: /* authdata */
650afa8e06SEd Maste 		return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
660afa8e06SEd Maste 		    &stmt->authdata, &stmt->authdata_ext));
670afa8e06SEd Maste 	case 3: /* signature */
680afa8e06SEd Maste 		return (fido_blob_decode(val, &stmt->sig));
690afa8e06SEd Maste 	case 4: /* user attributes */
700afa8e06SEd Maste 		return (cbor_decode_user(val, &stmt->user));
710afa8e06SEd Maste 	case 7: /* large blob key */
720afa8e06SEd Maste 		return (fido_blob_decode(val, &stmt->largeblob_key));
730afa8e06SEd Maste 	default: /* ignore */
740afa8e06SEd Maste 		fido_log_debug("%s: cbor type", __func__);
750afa8e06SEd Maste 		return (0);
760afa8e06SEd Maste 	}
770afa8e06SEd Maste }
780afa8e06SEd Maste 
790afa8e06SEd Maste static int
800afa8e06SEd Maste fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
81*f540a430SEd Maste     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
820afa8e06SEd Maste {
830afa8e06SEd Maste 	fido_blob_t	 f;
840afa8e06SEd Maste 	fido_opt_t	 uv = assert->uv;
850afa8e06SEd Maste 	cbor_item_t	*argv[7];
860afa8e06SEd Maste 	const uint8_t	 cmd = CTAP_CBOR_ASSERT;
870afa8e06SEd Maste 	int		 r;
880afa8e06SEd Maste 
890afa8e06SEd Maste 	memset(argv, 0, sizeof(argv));
900afa8e06SEd Maste 	memset(&f, 0, sizeof(f));
910afa8e06SEd Maste 
920afa8e06SEd Maste 	/* do we have everything we need? */
930afa8e06SEd Maste 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
940afa8e06SEd Maste 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
950afa8e06SEd Maste 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
960afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
970afa8e06SEd Maste 		goto fail;
980afa8e06SEd Maste 	}
990afa8e06SEd Maste 
1000afa8e06SEd Maste 	if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
1010afa8e06SEd Maste 	    (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
1020afa8e06SEd Maste 		fido_log_debug("%s: cbor encode", __func__);
1030afa8e06SEd Maste 		r = FIDO_ERR_INTERNAL;
1040afa8e06SEd Maste 		goto fail;
1050afa8e06SEd Maste 	}
1060afa8e06SEd Maste 
1070afa8e06SEd Maste 	/* allowed credentials */
1080afa8e06SEd Maste 	if (assert->allow_list.len) {
1090afa8e06SEd Maste 		const fido_blob_array_t *cl = &assert->allow_list;
1100afa8e06SEd Maste 		if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
1110afa8e06SEd Maste 			fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
1120afa8e06SEd Maste 			r = FIDO_ERR_INTERNAL;
1130afa8e06SEd Maste 			goto fail;
1140afa8e06SEd Maste 		}
1150afa8e06SEd Maste 	}
1160afa8e06SEd Maste 
1170afa8e06SEd Maste 	if (assert->ext.mask)
1180afa8e06SEd Maste 		if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
1190afa8e06SEd Maste 		    pk)) == NULL) {
1200afa8e06SEd Maste 			fido_log_debug("%s: cbor_encode_assert_ext", __func__);
1210afa8e06SEd Maste 			r = FIDO_ERR_INTERNAL;
1220afa8e06SEd Maste 			goto fail;
1230afa8e06SEd Maste 		}
1240afa8e06SEd Maste 
1250afa8e06SEd Maste 	/* user verification */
1260afa8e06SEd Maste 	if (pin != NULL || (uv == FIDO_OPT_TRUE &&
1270afa8e06SEd Maste 	    fido_dev_supports_permissions(dev))) {
1280afa8e06SEd Maste 		if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
129*f540a430SEd Maste 		    pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) {
1300afa8e06SEd Maste 			fido_log_debug("%s: cbor_add_uv_params", __func__);
1310afa8e06SEd Maste 			goto fail;
1320afa8e06SEd Maste 		}
1330afa8e06SEd Maste 		uv = FIDO_OPT_OMIT;
1340afa8e06SEd Maste 	}
1350afa8e06SEd Maste 
1360afa8e06SEd Maste 	/* options */
1370afa8e06SEd Maste 	if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
1380afa8e06SEd Maste 		if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
1390afa8e06SEd Maste 			fido_log_debug("%s: cbor_encode_assert_opt", __func__);
1400afa8e06SEd Maste 			r = FIDO_ERR_INTERNAL;
1410afa8e06SEd Maste 			goto fail;
1420afa8e06SEd Maste 		}
1430afa8e06SEd Maste 
1440afa8e06SEd Maste 	/* frame and transmit */
1450afa8e06SEd Maste 	if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
146*f540a430SEd Maste 	    fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
1470afa8e06SEd Maste 		fido_log_debug("%s: fido_tx", __func__);
1480afa8e06SEd Maste 		r = FIDO_ERR_TX;
1490afa8e06SEd Maste 		goto fail;
1500afa8e06SEd Maste 	}
1510afa8e06SEd Maste 
1520afa8e06SEd Maste 	r = FIDO_OK;
1530afa8e06SEd Maste fail:
1540afa8e06SEd Maste 	cbor_vector_free(argv, nitems(argv));
1550afa8e06SEd Maste 	free(f.ptr);
1560afa8e06SEd Maste 
1570afa8e06SEd Maste 	return (r);
1580afa8e06SEd Maste }
1590afa8e06SEd Maste 
1600afa8e06SEd Maste static int
161*f540a430SEd Maste fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
1620afa8e06SEd Maste {
1630afa8e06SEd Maste 	unsigned char	reply[FIDO_MAXMSG];
1640afa8e06SEd Maste 	int		reply_len;
1650afa8e06SEd Maste 	int		r;
1660afa8e06SEd Maste 
1670afa8e06SEd Maste 	fido_assert_reset_rx(assert);
1680afa8e06SEd Maste 
1690afa8e06SEd Maste 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
1700afa8e06SEd Maste 	    ms)) < 0) {
1710afa8e06SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
1720afa8e06SEd Maste 		return (FIDO_ERR_RX);
1730afa8e06SEd Maste 	}
1740afa8e06SEd Maste 
1750afa8e06SEd Maste 	/* start with room for a single assertion */
1760afa8e06SEd Maste 	if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
1770afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
1780afa8e06SEd Maste 
1790afa8e06SEd Maste 	assert->stmt_len = 0;
1800afa8e06SEd Maste 	assert->stmt_cnt = 1;
1810afa8e06SEd Maste 
1820afa8e06SEd Maste 	/* adjust as needed */
1830afa8e06SEd Maste 	if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
1840afa8e06SEd Maste 	    adjust_assert_count)) != FIDO_OK) {
1850afa8e06SEd Maste 		fido_log_debug("%s: adjust_assert_count", __func__);
1860afa8e06SEd Maste 		return (r);
1870afa8e06SEd Maste 	}
1880afa8e06SEd Maste 
1890afa8e06SEd Maste 	/* parse the first assertion */
1900afa8e06SEd Maste 	if ((r = cbor_parse_reply(reply, (size_t)reply_len,
1910afa8e06SEd Maste 	    &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
1920afa8e06SEd Maste 		fido_log_debug("%s: parse_assert_reply", __func__);
1930afa8e06SEd Maste 		return (r);
1940afa8e06SEd Maste 	}
1950afa8e06SEd Maste 
1960afa8e06SEd Maste 	assert->stmt_len++;
1970afa8e06SEd Maste 
1980afa8e06SEd Maste 	return (FIDO_OK);
1990afa8e06SEd Maste }
2000afa8e06SEd Maste 
2010afa8e06SEd Maste static int
202*f540a430SEd Maste fido_get_next_assert_tx(fido_dev_t *dev, int *ms)
2030afa8e06SEd Maste {
2040afa8e06SEd Maste 	const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
2050afa8e06SEd Maste 
206*f540a430SEd Maste 	if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
2070afa8e06SEd Maste 		fido_log_debug("%s: fido_tx", __func__);
2080afa8e06SEd Maste 		return (FIDO_ERR_TX);
2090afa8e06SEd Maste 	}
2100afa8e06SEd Maste 
2110afa8e06SEd Maste 	return (FIDO_OK);
2120afa8e06SEd Maste }
2130afa8e06SEd Maste 
2140afa8e06SEd Maste static int
215*f540a430SEd Maste fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
2160afa8e06SEd Maste {
2170afa8e06SEd Maste 	unsigned char	reply[FIDO_MAXMSG];
2180afa8e06SEd Maste 	int		reply_len;
2190afa8e06SEd Maste 	int		r;
2200afa8e06SEd Maste 
2210afa8e06SEd Maste 	if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
2220afa8e06SEd Maste 	    ms)) < 0) {
2230afa8e06SEd Maste 		fido_log_debug("%s: fido_rx", __func__);
2240afa8e06SEd Maste 		return (FIDO_ERR_RX);
2250afa8e06SEd Maste 	}
2260afa8e06SEd Maste 
2270afa8e06SEd Maste 	/* sanity check */
2280afa8e06SEd Maste 	if (assert->stmt_len >= assert->stmt_cnt) {
2290afa8e06SEd Maste 		fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
2300afa8e06SEd Maste 		    assert->stmt_len, assert->stmt_cnt);
2310afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
2320afa8e06SEd Maste 	}
2330afa8e06SEd Maste 
2340afa8e06SEd Maste 	if ((r = cbor_parse_reply(reply, (size_t)reply_len,
2350afa8e06SEd Maste 	    &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
2360afa8e06SEd Maste 		fido_log_debug("%s: parse_assert_reply", __func__);
2370afa8e06SEd Maste 		return (r);
2380afa8e06SEd Maste 	}
2390afa8e06SEd Maste 
2400afa8e06SEd Maste 	return (FIDO_OK);
2410afa8e06SEd Maste }
2420afa8e06SEd Maste 
2430afa8e06SEd Maste static int
2440afa8e06SEd Maste fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
245*f540a430SEd Maste     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
2460afa8e06SEd Maste {
2470afa8e06SEd Maste 	int r;
2480afa8e06SEd Maste 
249*f540a430SEd Maste 	if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin,
250*f540a430SEd Maste 	    ms)) != FIDO_OK ||
2510afa8e06SEd Maste 	    (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
2520afa8e06SEd Maste 		return (r);
2530afa8e06SEd Maste 
2540afa8e06SEd Maste 	while (assert->stmt_len < assert->stmt_cnt) {
255*f540a430SEd Maste 		if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK ||
2560afa8e06SEd Maste 		    (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
2570afa8e06SEd Maste 			return (r);
2580afa8e06SEd Maste 		assert->stmt_len++;
2590afa8e06SEd Maste 	}
2600afa8e06SEd Maste 
2610afa8e06SEd Maste 	return (FIDO_OK);
2620afa8e06SEd Maste }
2630afa8e06SEd Maste 
2640afa8e06SEd Maste static int
2650afa8e06SEd Maste decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
2660afa8e06SEd Maste     const fido_blob_t *key)
2670afa8e06SEd Maste {
2680afa8e06SEd Maste 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
2690afa8e06SEd Maste 		fido_assert_stmt *stmt = &assert->stmt[i];
2700afa8e06SEd Maste 		if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
2710afa8e06SEd Maste 			if (aes256_cbc_dec(dev, key,
2720afa8e06SEd Maste 			    &stmt->authdata_ext.hmac_secret_enc,
2730afa8e06SEd Maste 			    &stmt->hmac_secret) < 0) {
2740afa8e06SEd Maste 				fido_log_debug("%s: aes256_cbc_dec %zu",
2750afa8e06SEd Maste 				    __func__, i);
2760afa8e06SEd Maste 				return (-1);
2770afa8e06SEd Maste 			}
2780afa8e06SEd Maste 		}
2790afa8e06SEd Maste 	}
2800afa8e06SEd Maste 
2810afa8e06SEd Maste 	return (0);
2820afa8e06SEd Maste }
2830afa8e06SEd Maste 
2840afa8e06SEd Maste int
2850afa8e06SEd Maste fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
2860afa8e06SEd Maste {
2870afa8e06SEd Maste 	fido_blob_t	*ecdh = NULL;
2880afa8e06SEd Maste 	es256_pk_t	*pk = NULL;
289*f540a430SEd Maste 	int		 ms = dev->timeout_ms;
2900afa8e06SEd Maste 	int		 r;
2910afa8e06SEd Maste 
2920afa8e06SEd Maste #ifdef USE_WINHELLO
2930afa8e06SEd Maste 	if (dev->flags & FIDO_DEV_WINHELLO)
294*f540a430SEd Maste 		return (fido_winhello_get_assert(dev, assert, pin, ms));
2950afa8e06SEd Maste #endif
2960afa8e06SEd Maste 
2970afa8e06SEd Maste 	if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
2980afa8e06SEd Maste 		fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
2990afa8e06SEd Maste 		    (void *)assert->rp_id, (void *)assert->cdh.ptr);
3000afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
3010afa8e06SEd Maste 	}
3020afa8e06SEd Maste 
3030afa8e06SEd Maste 	if (fido_dev_is_fido2(dev) == false) {
3040afa8e06SEd Maste 		if (pin != NULL || assert->ext.mask != 0)
3050afa8e06SEd Maste 			return (FIDO_ERR_UNSUPPORTED_OPTION);
306*f540a430SEd Maste 		return (u2f_authenticate(dev, assert, &ms));
3070afa8e06SEd Maste 	}
3080afa8e06SEd Maste 
3090afa8e06SEd Maste 	if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
3100afa8e06SEd Maste 	    fido_dev_supports_permissions(dev)) ||
3110afa8e06SEd Maste 	    (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
312*f540a430SEd Maste 		if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
3130afa8e06SEd Maste 			fido_log_debug("%s: fido_do_ecdh", __func__);
3140afa8e06SEd Maste 			goto fail;
3150afa8e06SEd Maste 		}
3160afa8e06SEd Maste 	}
3170afa8e06SEd Maste 
318*f540a430SEd Maste 	r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms);
3190afa8e06SEd Maste 	if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
3200afa8e06SEd Maste 		if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
3210afa8e06SEd Maste 			fido_log_debug("%s: decrypt_hmac_secrets", __func__);
3220afa8e06SEd Maste 			r = FIDO_ERR_INTERNAL;
3230afa8e06SEd Maste 			goto fail;
3240afa8e06SEd Maste 		}
3250afa8e06SEd Maste 
3260afa8e06SEd Maste fail:
3270afa8e06SEd Maste 	es256_pk_free(&pk);
3280afa8e06SEd Maste 	fido_blob_free(&ecdh);
3290afa8e06SEd Maste 
3300afa8e06SEd Maste 	return (r);
3310afa8e06SEd Maste }
3320afa8e06SEd Maste 
3330afa8e06SEd Maste int
3340afa8e06SEd Maste fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
3350afa8e06SEd Maste {
3360afa8e06SEd Maste 	fido_log_debug("%s: flags=%02x", __func__, flags);
3370afa8e06SEd Maste 	fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
3380afa8e06SEd Maste 
3390afa8e06SEd Maste 	if (up == FIDO_OPT_TRUE &&
3400afa8e06SEd Maste 	    (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
3410afa8e06SEd Maste 		fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
3420afa8e06SEd Maste 		return (-1); /* user not present */
3430afa8e06SEd Maste 	}
3440afa8e06SEd Maste 
3450afa8e06SEd Maste 	if (uv == FIDO_OPT_TRUE &&
3460afa8e06SEd Maste 	    (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
3470afa8e06SEd Maste 		fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
3480afa8e06SEd Maste 		return (-1); /* user not verified */
3490afa8e06SEd Maste 	}
3500afa8e06SEd Maste 
3510afa8e06SEd Maste 	return (0);
3520afa8e06SEd Maste }
3530afa8e06SEd Maste 
3540afa8e06SEd Maste static int
3550afa8e06SEd Maste check_extensions(int authdata_ext, int ext)
3560afa8e06SEd Maste {
3570afa8e06SEd Maste 	/* XXX: largeBlobKey is not part of extensions map */
3580afa8e06SEd Maste 	ext &= ~FIDO_EXT_LARGEBLOB_KEY;
3590afa8e06SEd Maste 	if (authdata_ext != ext) {
3600afa8e06SEd Maste 		fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
3610afa8e06SEd Maste 		    authdata_ext, ext);
3620afa8e06SEd Maste 		return (-1);
3630afa8e06SEd Maste 	}
3640afa8e06SEd Maste 
3650afa8e06SEd Maste 	return (0);
3660afa8e06SEd Maste }
3670afa8e06SEd Maste 
3680afa8e06SEd Maste int
3690afa8e06SEd Maste fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
3700afa8e06SEd Maste     const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
3710afa8e06SEd Maste {
3720afa8e06SEd Maste 	cbor_item_t		*item = NULL;
3730afa8e06SEd Maste 	unsigned char		*authdata_ptr = NULL;
3740afa8e06SEd Maste 	size_t			 authdata_len;
3750afa8e06SEd Maste 	struct cbor_load_result	 cbor;
376*f540a430SEd Maste 	const EVP_MD		*md = NULL;
377*f540a430SEd Maste 	EVP_MD_CTX		*ctx = NULL;
3780afa8e06SEd Maste 	int			 ok = -1;
3790afa8e06SEd Maste 
3800afa8e06SEd Maste 	if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
3810afa8e06SEd Maste 	    &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
3820afa8e06SEd Maste 	    cbor_bytestring_is_definite(item) == false) {
3830afa8e06SEd Maste 		fido_log_debug("%s: authdata", __func__);
3840afa8e06SEd Maste 		goto fail;
3850afa8e06SEd Maste 	}
3860afa8e06SEd Maste 
3870afa8e06SEd Maste 	authdata_ptr = cbor_bytestring_handle(item);
3880afa8e06SEd Maste 	authdata_len = cbor_bytestring_length(item);
3890afa8e06SEd Maste 
3900afa8e06SEd Maste 	if (cose_alg != COSE_EDDSA) {
391*f540a430SEd Maste 		if (dgst->len < SHA256_DIGEST_LENGTH ||
392*f540a430SEd Maste 		    (md = EVP_sha256()) == NULL ||
393*f540a430SEd Maste 		    (ctx = EVP_MD_CTX_new()) == NULL ||
394*f540a430SEd Maste 		    EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
395*f540a430SEd Maste 		    EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 ||
396*f540a430SEd Maste 		    EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
397*f540a430SEd Maste 		    EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
3980afa8e06SEd Maste 			fido_log_debug("%s: sha256", __func__);
3990afa8e06SEd Maste 			goto fail;
4000afa8e06SEd Maste 		}
4010afa8e06SEd Maste 		dgst->len = SHA256_DIGEST_LENGTH;
4020afa8e06SEd Maste 	} else {
4030afa8e06SEd Maste 		if (SIZE_MAX - authdata_len < clientdata->len ||
4040afa8e06SEd Maste 		    dgst->len < authdata_len + clientdata->len) {
4050afa8e06SEd Maste 			fido_log_debug("%s: memcpy", __func__);
4060afa8e06SEd Maste 			goto fail;
4070afa8e06SEd Maste 		}
4080afa8e06SEd Maste 		memcpy(dgst->ptr, authdata_ptr, authdata_len);
4090afa8e06SEd Maste 		memcpy(dgst->ptr + authdata_len, clientdata->ptr,
4100afa8e06SEd Maste 		    clientdata->len);
4110afa8e06SEd Maste 		dgst->len = authdata_len + clientdata->len;
4120afa8e06SEd Maste 	}
4130afa8e06SEd Maste 
4140afa8e06SEd Maste 	ok = 0;
4150afa8e06SEd Maste fail:
4160afa8e06SEd Maste 	if (item != NULL)
4170afa8e06SEd Maste 		cbor_decref(&item);
4180afa8e06SEd Maste 
419*f540a430SEd Maste 	EVP_MD_CTX_free(ctx);
4200afa8e06SEd Maste 
4210afa8e06SEd Maste 	return (ok);
4220afa8e06SEd Maste }
4230afa8e06SEd Maste 
4240afa8e06SEd Maste int
4250afa8e06SEd Maste fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
4260afa8e06SEd Maste     const void *pk)
4270afa8e06SEd Maste {
4280afa8e06SEd Maste 	unsigned char		 buf[1024]; /* XXX */
4290afa8e06SEd Maste 	fido_blob_t		 dgst;
4300afa8e06SEd Maste 	const fido_assert_stmt	*stmt = NULL;
4310afa8e06SEd Maste 	int			 ok = -1;
4320afa8e06SEd Maste 	int			 r;
4330afa8e06SEd Maste 
4340afa8e06SEd Maste 	dgst.ptr = buf;
4350afa8e06SEd Maste 	dgst.len = sizeof(buf);
4360afa8e06SEd Maste 
4370afa8e06SEd Maste 	if (idx >= assert->stmt_len || pk == NULL) {
4380afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
4390afa8e06SEd Maste 		goto out;
4400afa8e06SEd Maste 	}
4410afa8e06SEd Maste 
4420afa8e06SEd Maste 	stmt = &assert->stmt[idx];
4430afa8e06SEd Maste 
4440afa8e06SEd Maste 	/* do we have everything we need? */
4450afa8e06SEd Maste 	if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
4460afa8e06SEd Maste 	    stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
4470afa8e06SEd Maste 		fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
4480afa8e06SEd Maste 		    __func__, (void *)assert->cdh.ptr, assert->rp_id,
4490afa8e06SEd Maste 		    (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
4500afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
4510afa8e06SEd Maste 		goto out;
4520afa8e06SEd Maste 	}
4530afa8e06SEd Maste 
4540afa8e06SEd Maste 	if (fido_check_flags(stmt->authdata.flags, assert->up,
4550afa8e06SEd Maste 	    assert->uv) < 0) {
4560afa8e06SEd Maste 		fido_log_debug("%s: fido_check_flags", __func__);
4570afa8e06SEd Maste 		r = FIDO_ERR_INVALID_PARAM;
4580afa8e06SEd Maste 		goto out;
4590afa8e06SEd Maste 	}
4600afa8e06SEd Maste 
4610afa8e06SEd Maste 	if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
4620afa8e06SEd Maste 		fido_log_debug("%s: check_extensions", __func__);
4630afa8e06SEd Maste 		r = FIDO_ERR_INVALID_PARAM;
4640afa8e06SEd Maste 		goto out;
4650afa8e06SEd Maste 	}
4660afa8e06SEd Maste 
4670afa8e06SEd Maste 	if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
4680afa8e06SEd Maste 		fido_log_debug("%s: fido_check_rp_id", __func__);
4690afa8e06SEd Maste 		r = FIDO_ERR_INVALID_PARAM;
4700afa8e06SEd Maste 		goto out;
4710afa8e06SEd Maste 	}
4720afa8e06SEd Maste 
4730afa8e06SEd Maste 	if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh,
4740afa8e06SEd Maste 	    &stmt->authdata_cbor) < 0) {
4750afa8e06SEd Maste 		fido_log_debug("%s: fido_get_signed_hash", __func__);
4760afa8e06SEd Maste 		r = FIDO_ERR_INTERNAL;
4770afa8e06SEd Maste 		goto out;
4780afa8e06SEd Maste 	}
4790afa8e06SEd Maste 
4800afa8e06SEd Maste 	switch (cose_alg) {
4810afa8e06SEd Maste 	case COSE_ES256:
482*f540a430SEd Maste 		ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig);
4830afa8e06SEd Maste 		break;
4840afa8e06SEd Maste 	case COSE_RS256:
485*f540a430SEd Maste 		ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig);
4860afa8e06SEd Maste 		break;
4870afa8e06SEd Maste 	case COSE_EDDSA:
488*f540a430SEd Maste 		ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig);
4890afa8e06SEd Maste 		break;
4900afa8e06SEd Maste 	default:
4910afa8e06SEd Maste 		fido_log_debug("%s: unsupported cose_alg %d", __func__,
4920afa8e06SEd Maste 		    cose_alg);
4930afa8e06SEd Maste 		r = FIDO_ERR_UNSUPPORTED_OPTION;
4940afa8e06SEd Maste 		goto out;
4950afa8e06SEd Maste 	}
4960afa8e06SEd Maste 
4970afa8e06SEd Maste 	if (ok < 0)
4980afa8e06SEd Maste 		r = FIDO_ERR_INVALID_SIG;
4990afa8e06SEd Maste 	else
5000afa8e06SEd Maste 		r = FIDO_OK;
5010afa8e06SEd Maste out:
5020afa8e06SEd Maste 	explicit_bzero(buf, sizeof(buf));
5030afa8e06SEd Maste 
5040afa8e06SEd Maste 	return (r);
5050afa8e06SEd Maste }
5060afa8e06SEd Maste 
5070afa8e06SEd Maste int
5080afa8e06SEd Maste fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
5090afa8e06SEd Maste     size_t data_len)
5100afa8e06SEd Maste {
5110afa8e06SEd Maste 	if (!fido_blob_is_empty(&assert->cdh) ||
5120afa8e06SEd Maste 	    fido_blob_set(&assert->cd, data, data_len) < 0) {
5130afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5140afa8e06SEd Maste 	}
5150afa8e06SEd Maste 	if (fido_sha256(&assert->cdh, data, data_len) < 0) {
5160afa8e06SEd Maste 		fido_blob_reset(&assert->cd);
5170afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
5180afa8e06SEd Maste 	}
5190afa8e06SEd Maste 
5200afa8e06SEd Maste 	return (FIDO_OK);
5210afa8e06SEd Maste }
5220afa8e06SEd Maste 
5230afa8e06SEd Maste int
5240afa8e06SEd Maste fido_assert_set_clientdata_hash(fido_assert_t *assert,
5250afa8e06SEd Maste     const unsigned char *hash, size_t hash_len)
5260afa8e06SEd Maste {
5270afa8e06SEd Maste 	if (!fido_blob_is_empty(&assert->cd) ||
5280afa8e06SEd Maste 	    fido_blob_set(&assert->cdh, hash, hash_len) < 0)
5290afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5300afa8e06SEd Maste 
5310afa8e06SEd Maste 	return (FIDO_OK);
5320afa8e06SEd Maste }
5330afa8e06SEd Maste 
5340afa8e06SEd Maste int
5350afa8e06SEd Maste fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
5360afa8e06SEd Maste     size_t salt_len)
5370afa8e06SEd Maste {
5380afa8e06SEd Maste 	if ((salt_len != 32 && salt_len != 64) ||
5390afa8e06SEd Maste 	    fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
5400afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5410afa8e06SEd Maste 
5420afa8e06SEd Maste 	return (FIDO_OK);
5430afa8e06SEd Maste }
5440afa8e06SEd Maste 
5450afa8e06SEd Maste int
5460afa8e06SEd Maste fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
5470afa8e06SEd Maste     const unsigned char *secret, size_t secret_len)
5480afa8e06SEd Maste {
5490afa8e06SEd Maste 	if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
5500afa8e06SEd Maste 	    fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
5510afa8e06SEd Maste 	    secret_len) < 0)
5520afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5530afa8e06SEd Maste 
5540afa8e06SEd Maste 	return (FIDO_OK);
5550afa8e06SEd Maste }
5560afa8e06SEd Maste 
5570afa8e06SEd Maste int
5580afa8e06SEd Maste fido_assert_set_rp(fido_assert_t *assert, const char *id)
5590afa8e06SEd Maste {
5600afa8e06SEd Maste 	if (assert->rp_id != NULL) {
5610afa8e06SEd Maste 		free(assert->rp_id);
5620afa8e06SEd Maste 		assert->rp_id = NULL;
5630afa8e06SEd Maste 	}
5640afa8e06SEd Maste 
5650afa8e06SEd Maste 	if (id == NULL)
5660afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
5670afa8e06SEd Maste 
5680afa8e06SEd Maste 	if ((assert->rp_id = strdup(id)) == NULL)
5690afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
5700afa8e06SEd Maste 
5710afa8e06SEd Maste 	return (FIDO_OK);
5720afa8e06SEd Maste }
5730afa8e06SEd Maste 
5740afa8e06SEd Maste int
5750afa8e06SEd Maste fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
5760afa8e06SEd Maste     size_t len)
5770afa8e06SEd Maste {
5780afa8e06SEd Maste 	fido_blob_t	 id;
5790afa8e06SEd Maste 	fido_blob_t	*list_ptr;
5800afa8e06SEd Maste 	int		 r;
5810afa8e06SEd Maste 
5820afa8e06SEd Maste 	memset(&id, 0, sizeof(id));
5830afa8e06SEd Maste 
5840afa8e06SEd Maste 	if (assert->allow_list.len == SIZE_MAX) {
5850afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
5860afa8e06SEd Maste 		goto fail;
5870afa8e06SEd Maste 	}
5880afa8e06SEd Maste 
5890afa8e06SEd Maste 	if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
5900afa8e06SEd Maste 	    recallocarray(assert->allow_list.ptr, assert->allow_list.len,
5910afa8e06SEd Maste 	    assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
5920afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
5930afa8e06SEd Maste 		goto fail;
5940afa8e06SEd Maste 	}
5950afa8e06SEd Maste 
5960afa8e06SEd Maste 	list_ptr[assert->allow_list.len++] = id;
5970afa8e06SEd Maste 	assert->allow_list.ptr = list_ptr;
5980afa8e06SEd Maste 
5990afa8e06SEd Maste 	return (FIDO_OK);
6000afa8e06SEd Maste fail:
6010afa8e06SEd Maste 	free(id.ptr);
6020afa8e06SEd Maste 
6030afa8e06SEd Maste 	return (r);
6040afa8e06SEd Maste 
6050afa8e06SEd Maste }
6060afa8e06SEd Maste 
6070afa8e06SEd Maste int
6080afa8e06SEd Maste fido_assert_set_extensions(fido_assert_t *assert, int ext)
6090afa8e06SEd Maste {
6100afa8e06SEd Maste 	if (ext == 0)
6110afa8e06SEd Maste 		assert->ext.mask = 0;
6120afa8e06SEd Maste 	else {
6130afa8e06SEd Maste 		if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
6140afa8e06SEd Maste 			return (FIDO_ERR_INVALID_ARGUMENT);
6150afa8e06SEd Maste 		assert->ext.mask |= ext;
6160afa8e06SEd Maste 	}
6170afa8e06SEd Maste 
6180afa8e06SEd Maste 	return (FIDO_OK);
6190afa8e06SEd Maste }
6200afa8e06SEd Maste 
6210afa8e06SEd Maste int
6220afa8e06SEd Maste fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
6230afa8e06SEd Maste {
6240afa8e06SEd Maste 	assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
6250afa8e06SEd Maste 	assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
6260afa8e06SEd Maste 
6270afa8e06SEd Maste 	return (FIDO_OK);
6280afa8e06SEd Maste }
6290afa8e06SEd Maste 
6300afa8e06SEd Maste int
6310afa8e06SEd Maste fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
6320afa8e06SEd Maste {
6330afa8e06SEd Maste 	assert->up = up;
6340afa8e06SEd Maste 
6350afa8e06SEd Maste 	return (FIDO_OK);
6360afa8e06SEd Maste }
6370afa8e06SEd Maste 
6380afa8e06SEd Maste int
6390afa8e06SEd Maste fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
6400afa8e06SEd Maste {
6410afa8e06SEd Maste 	assert->uv = uv;
6420afa8e06SEd Maste 
6430afa8e06SEd Maste 	return (FIDO_OK);
6440afa8e06SEd Maste }
6450afa8e06SEd Maste 
6460afa8e06SEd Maste const unsigned char *
6470afa8e06SEd Maste fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
6480afa8e06SEd Maste {
6490afa8e06SEd Maste 	return (assert->cdh.ptr);
6500afa8e06SEd Maste }
6510afa8e06SEd Maste 
6520afa8e06SEd Maste size_t
6530afa8e06SEd Maste fido_assert_clientdata_hash_len(const fido_assert_t *assert)
6540afa8e06SEd Maste {
6550afa8e06SEd Maste 	return (assert->cdh.len);
6560afa8e06SEd Maste }
6570afa8e06SEd Maste 
6580afa8e06SEd Maste fido_assert_t *
6590afa8e06SEd Maste fido_assert_new(void)
6600afa8e06SEd Maste {
6610afa8e06SEd Maste 	return (calloc(1, sizeof(fido_assert_t)));
6620afa8e06SEd Maste }
6630afa8e06SEd Maste 
6640afa8e06SEd Maste void
6650afa8e06SEd Maste fido_assert_reset_tx(fido_assert_t *assert)
6660afa8e06SEd Maste {
6670afa8e06SEd Maste 	free(assert->rp_id);
6680afa8e06SEd Maste 	fido_blob_reset(&assert->cd);
6690afa8e06SEd Maste 	fido_blob_reset(&assert->cdh);
6700afa8e06SEd Maste 	fido_blob_reset(&assert->ext.hmac_salt);
6710afa8e06SEd Maste 	fido_free_blob_array(&assert->allow_list);
6720afa8e06SEd Maste 	memset(&assert->ext, 0, sizeof(assert->ext));
6730afa8e06SEd Maste 	memset(&assert->allow_list, 0, sizeof(assert->allow_list));
6740afa8e06SEd Maste 	assert->rp_id = NULL;
6750afa8e06SEd Maste 	assert->up = FIDO_OPT_OMIT;
6760afa8e06SEd Maste 	assert->uv = FIDO_OPT_OMIT;
6770afa8e06SEd Maste }
6780afa8e06SEd Maste 
6790afa8e06SEd Maste static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
6800afa8e06SEd Maste {
6810afa8e06SEd Maste 	fido_blob_reset(&ext->hmac_secret_enc);
6820afa8e06SEd Maste 	fido_blob_reset(&ext->blob);
6830afa8e06SEd Maste 	memset(ext, 0, sizeof(*ext));
6840afa8e06SEd Maste }
6850afa8e06SEd Maste 
6860afa8e06SEd Maste void
6870afa8e06SEd Maste fido_assert_reset_rx(fido_assert_t *assert)
6880afa8e06SEd Maste {
6890afa8e06SEd Maste 	for (size_t i = 0; i < assert->stmt_cnt; i++) {
6900afa8e06SEd Maste 		free(assert->stmt[i].user.icon);
6910afa8e06SEd Maste 		free(assert->stmt[i].user.name);
6920afa8e06SEd Maste 		free(assert->stmt[i].user.display_name);
6930afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].user.id);
6940afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].id);
6950afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].hmac_secret);
6960afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].authdata_cbor);
6970afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].largeblob_key);
6980afa8e06SEd Maste 		fido_blob_reset(&assert->stmt[i].sig);
6990afa8e06SEd Maste 		fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
7000afa8e06SEd Maste 		memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
7010afa8e06SEd Maste 	}
7020afa8e06SEd Maste 	free(assert->stmt);
7030afa8e06SEd Maste 	assert->stmt = NULL;
7040afa8e06SEd Maste 	assert->stmt_len = 0;
7050afa8e06SEd Maste 	assert->stmt_cnt = 0;
7060afa8e06SEd Maste }
7070afa8e06SEd Maste 
7080afa8e06SEd Maste void
7090afa8e06SEd Maste fido_assert_free(fido_assert_t **assert_p)
7100afa8e06SEd Maste {
7110afa8e06SEd Maste 	fido_assert_t *assert;
7120afa8e06SEd Maste 
7130afa8e06SEd Maste 	if (assert_p == NULL || (assert = *assert_p) == NULL)
7140afa8e06SEd Maste 		return;
7150afa8e06SEd Maste 	fido_assert_reset_tx(assert);
7160afa8e06SEd Maste 	fido_assert_reset_rx(assert);
7170afa8e06SEd Maste 	free(assert);
7180afa8e06SEd Maste 	*assert_p = NULL;
7190afa8e06SEd Maste }
7200afa8e06SEd Maste 
7210afa8e06SEd Maste size_t
7220afa8e06SEd Maste fido_assert_count(const fido_assert_t *assert)
7230afa8e06SEd Maste {
7240afa8e06SEd Maste 	return (assert->stmt_len);
7250afa8e06SEd Maste }
7260afa8e06SEd Maste 
7270afa8e06SEd Maste const char *
7280afa8e06SEd Maste fido_assert_rp_id(const fido_assert_t *assert)
7290afa8e06SEd Maste {
7300afa8e06SEd Maste 	return (assert->rp_id);
7310afa8e06SEd Maste }
7320afa8e06SEd Maste 
7330afa8e06SEd Maste uint8_t
7340afa8e06SEd Maste fido_assert_flags(const fido_assert_t *assert, size_t idx)
7350afa8e06SEd Maste {
7360afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7370afa8e06SEd Maste 		return (0);
7380afa8e06SEd Maste 
7390afa8e06SEd Maste 	return (assert->stmt[idx].authdata.flags);
7400afa8e06SEd Maste }
7410afa8e06SEd Maste 
7420afa8e06SEd Maste uint32_t
7430afa8e06SEd Maste fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
7440afa8e06SEd Maste {
7450afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7460afa8e06SEd Maste 		return (0);
7470afa8e06SEd Maste 
7480afa8e06SEd Maste 	return (assert->stmt[idx].authdata.sigcount);
7490afa8e06SEd Maste }
7500afa8e06SEd Maste 
7510afa8e06SEd Maste const unsigned char *
7520afa8e06SEd Maste fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
7530afa8e06SEd Maste {
7540afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7550afa8e06SEd Maste 		return (NULL);
7560afa8e06SEd Maste 
7570afa8e06SEd Maste 	return (assert->stmt[idx].authdata_cbor.ptr);
7580afa8e06SEd Maste }
7590afa8e06SEd Maste 
7600afa8e06SEd Maste size_t
7610afa8e06SEd Maste fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
7620afa8e06SEd Maste {
7630afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7640afa8e06SEd Maste 		return (0);
7650afa8e06SEd Maste 
7660afa8e06SEd Maste 	return (assert->stmt[idx].authdata_cbor.len);
7670afa8e06SEd Maste }
7680afa8e06SEd Maste 
7690afa8e06SEd Maste const unsigned char *
7700afa8e06SEd Maste fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
7710afa8e06SEd Maste {
7720afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7730afa8e06SEd Maste 		return (NULL);
7740afa8e06SEd Maste 
7750afa8e06SEd Maste 	return (assert->stmt[idx].sig.ptr);
7760afa8e06SEd Maste }
7770afa8e06SEd Maste 
7780afa8e06SEd Maste size_t
7790afa8e06SEd Maste fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
7800afa8e06SEd Maste {
7810afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7820afa8e06SEd Maste 		return (0);
7830afa8e06SEd Maste 
7840afa8e06SEd Maste 	return (assert->stmt[idx].sig.len);
7850afa8e06SEd Maste }
7860afa8e06SEd Maste 
7870afa8e06SEd Maste const unsigned char *
7880afa8e06SEd Maste fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
7890afa8e06SEd Maste {
7900afa8e06SEd Maste 	if (idx >= assert->stmt_len)
7910afa8e06SEd Maste 		return (NULL);
7920afa8e06SEd Maste 
7930afa8e06SEd Maste 	return (assert->stmt[idx].id.ptr);
7940afa8e06SEd Maste }
7950afa8e06SEd Maste 
7960afa8e06SEd Maste size_t
7970afa8e06SEd Maste fido_assert_id_len(const fido_assert_t *assert, size_t idx)
7980afa8e06SEd Maste {
7990afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8000afa8e06SEd Maste 		return (0);
8010afa8e06SEd Maste 
8020afa8e06SEd Maste 	return (assert->stmt[idx].id.len);
8030afa8e06SEd Maste }
8040afa8e06SEd Maste 
8050afa8e06SEd Maste const unsigned char *
8060afa8e06SEd Maste fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
8070afa8e06SEd Maste {
8080afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8090afa8e06SEd Maste 		return (NULL);
8100afa8e06SEd Maste 
8110afa8e06SEd Maste 	return (assert->stmt[idx].user.id.ptr);
8120afa8e06SEd Maste }
8130afa8e06SEd Maste 
8140afa8e06SEd Maste size_t
8150afa8e06SEd Maste fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
8160afa8e06SEd Maste {
8170afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8180afa8e06SEd Maste 		return (0);
8190afa8e06SEd Maste 
8200afa8e06SEd Maste 	return (assert->stmt[idx].user.id.len);
8210afa8e06SEd Maste }
8220afa8e06SEd Maste 
8230afa8e06SEd Maste const char *
8240afa8e06SEd Maste fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
8250afa8e06SEd Maste {
8260afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8270afa8e06SEd Maste 		return (NULL);
8280afa8e06SEd Maste 
8290afa8e06SEd Maste 	return (assert->stmt[idx].user.icon);
8300afa8e06SEd Maste }
8310afa8e06SEd Maste 
8320afa8e06SEd Maste const char *
8330afa8e06SEd Maste fido_assert_user_name(const fido_assert_t *assert, size_t idx)
8340afa8e06SEd Maste {
8350afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8360afa8e06SEd Maste 		return (NULL);
8370afa8e06SEd Maste 
8380afa8e06SEd Maste 	return (assert->stmt[idx].user.name);
8390afa8e06SEd Maste }
8400afa8e06SEd Maste 
8410afa8e06SEd Maste const char *
8420afa8e06SEd Maste fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
8430afa8e06SEd Maste {
8440afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8450afa8e06SEd Maste 		return (NULL);
8460afa8e06SEd Maste 
8470afa8e06SEd Maste 	return (assert->stmt[idx].user.display_name);
8480afa8e06SEd Maste }
8490afa8e06SEd Maste 
8500afa8e06SEd Maste const unsigned char *
8510afa8e06SEd Maste fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
8520afa8e06SEd Maste {
8530afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8540afa8e06SEd Maste 		return (NULL);
8550afa8e06SEd Maste 
8560afa8e06SEd Maste 	return (assert->stmt[idx].hmac_secret.ptr);
8570afa8e06SEd Maste }
8580afa8e06SEd Maste 
8590afa8e06SEd Maste size_t
8600afa8e06SEd Maste fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
8610afa8e06SEd Maste {
8620afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8630afa8e06SEd Maste 		return (0);
8640afa8e06SEd Maste 
8650afa8e06SEd Maste 	return (assert->stmt[idx].hmac_secret.len);
8660afa8e06SEd Maste }
8670afa8e06SEd Maste 
8680afa8e06SEd Maste const unsigned char *
8690afa8e06SEd Maste fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
8700afa8e06SEd Maste {
8710afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8720afa8e06SEd Maste 		return (NULL);
8730afa8e06SEd Maste 
8740afa8e06SEd Maste 	return (assert->stmt[idx].largeblob_key.ptr);
8750afa8e06SEd Maste }
8760afa8e06SEd Maste 
8770afa8e06SEd Maste size_t
8780afa8e06SEd Maste fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
8790afa8e06SEd Maste {
8800afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8810afa8e06SEd Maste 		return (0);
8820afa8e06SEd Maste 
8830afa8e06SEd Maste 	return (assert->stmt[idx].largeblob_key.len);
8840afa8e06SEd Maste }
8850afa8e06SEd Maste 
8860afa8e06SEd Maste const unsigned char *
8870afa8e06SEd Maste fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
8880afa8e06SEd Maste {
8890afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8900afa8e06SEd Maste 		return (NULL);
8910afa8e06SEd Maste 
8920afa8e06SEd Maste 	return (assert->stmt[idx].authdata_ext.blob.ptr);
8930afa8e06SEd Maste }
8940afa8e06SEd Maste 
8950afa8e06SEd Maste size_t
8960afa8e06SEd Maste fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
8970afa8e06SEd Maste {
8980afa8e06SEd Maste 	if (idx >= assert->stmt_len)
8990afa8e06SEd Maste 		return (0);
9000afa8e06SEd Maste 
9010afa8e06SEd Maste 	return (assert->stmt[idx].authdata_ext.blob.len);
9020afa8e06SEd Maste }
9030afa8e06SEd Maste 
9040afa8e06SEd Maste static void
9050afa8e06SEd Maste fido_assert_clean_authdata(fido_assert_stmt *stmt)
9060afa8e06SEd Maste {
9070afa8e06SEd Maste 	fido_blob_reset(&stmt->authdata_cbor);
9080afa8e06SEd Maste 	fido_assert_reset_extattr(&stmt->authdata_ext);
9090afa8e06SEd Maste 	memset(&stmt->authdata, 0, sizeof(stmt->authdata));
9100afa8e06SEd Maste }
9110afa8e06SEd Maste 
9120afa8e06SEd Maste int
9130afa8e06SEd Maste fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
9140afa8e06SEd Maste     const unsigned char *ptr, size_t len)
9150afa8e06SEd Maste {
9160afa8e06SEd Maste 	cbor_item_t		*item = NULL;
9170afa8e06SEd Maste 	fido_assert_stmt	*stmt = NULL;
9180afa8e06SEd Maste 	struct cbor_load_result	 cbor;
9190afa8e06SEd Maste 	int			 r;
9200afa8e06SEd Maste 
9210afa8e06SEd Maste 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
9220afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
9230afa8e06SEd Maste 
9240afa8e06SEd Maste 	stmt = &assert->stmt[idx];
9250afa8e06SEd Maste 	fido_assert_clean_authdata(stmt);
9260afa8e06SEd Maste 
9270afa8e06SEd Maste 	if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
9280afa8e06SEd Maste 		fido_log_debug("%s: cbor_load", __func__);
9290afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
9300afa8e06SEd Maste 		goto fail;
9310afa8e06SEd Maste 	}
9320afa8e06SEd Maste 
9330afa8e06SEd Maste 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
9340afa8e06SEd Maste 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
9350afa8e06SEd Maste 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
9360afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
9370afa8e06SEd Maste 		goto fail;
9380afa8e06SEd Maste 	}
9390afa8e06SEd Maste 
9400afa8e06SEd Maste 	r = FIDO_OK;
9410afa8e06SEd Maste fail:
9420afa8e06SEd Maste 	if (item != NULL)
9430afa8e06SEd Maste 		cbor_decref(&item);
9440afa8e06SEd Maste 
9450afa8e06SEd Maste 	if (r != FIDO_OK)
9460afa8e06SEd Maste 		fido_assert_clean_authdata(stmt);
9470afa8e06SEd Maste 
9480afa8e06SEd Maste 	return (r);
9490afa8e06SEd Maste }
9500afa8e06SEd Maste 
9510afa8e06SEd Maste int
9520afa8e06SEd Maste fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
9530afa8e06SEd Maste     const unsigned char *ptr, size_t len)
9540afa8e06SEd Maste {
9550afa8e06SEd Maste 	cbor_item_t		*item = NULL;
9560afa8e06SEd Maste 	fido_assert_stmt	*stmt = NULL;
9570afa8e06SEd Maste 	int			 r;
9580afa8e06SEd Maste 
9590afa8e06SEd Maste 	if (idx >= assert->stmt_len || ptr == NULL || len == 0)
9600afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
9610afa8e06SEd Maste 
9620afa8e06SEd Maste 	stmt = &assert->stmt[idx];
9630afa8e06SEd Maste 	fido_assert_clean_authdata(stmt);
9640afa8e06SEd Maste 
9650afa8e06SEd Maste 	if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
9660afa8e06SEd Maste 		fido_log_debug("%s: cbor_build_bytestring", __func__);
9670afa8e06SEd Maste 		r = FIDO_ERR_INTERNAL;
9680afa8e06SEd Maste 		goto fail;
9690afa8e06SEd Maste 	}
9700afa8e06SEd Maste 
9710afa8e06SEd Maste 	if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
9720afa8e06SEd Maste 	    &stmt->authdata, &stmt->authdata_ext) < 0) {
9730afa8e06SEd Maste 		fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
9740afa8e06SEd Maste 		r = FIDO_ERR_INVALID_ARGUMENT;
9750afa8e06SEd Maste 		goto fail;
9760afa8e06SEd Maste 	}
9770afa8e06SEd Maste 
9780afa8e06SEd Maste 	r = FIDO_OK;
9790afa8e06SEd Maste fail:
9800afa8e06SEd Maste 	if (item != NULL)
9810afa8e06SEd Maste 		cbor_decref(&item);
9820afa8e06SEd Maste 
9830afa8e06SEd Maste 	if (r != FIDO_OK)
9840afa8e06SEd Maste 		fido_assert_clean_authdata(stmt);
9850afa8e06SEd Maste 
9860afa8e06SEd Maste 	return (r);
9870afa8e06SEd Maste }
9880afa8e06SEd Maste 
9890afa8e06SEd Maste int
9900afa8e06SEd Maste fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
9910afa8e06SEd Maste     size_t len)
9920afa8e06SEd Maste {
9930afa8e06SEd Maste 	if (idx >= a->stmt_len || ptr == NULL || len == 0)
9940afa8e06SEd Maste 		return (FIDO_ERR_INVALID_ARGUMENT);
9950afa8e06SEd Maste 	if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
9960afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
9970afa8e06SEd Maste 
9980afa8e06SEd Maste 	return (FIDO_OK);
9990afa8e06SEd Maste }
10000afa8e06SEd Maste 
10010afa8e06SEd Maste /* XXX shrinking leaks memory; fortunately that shouldn't happen */
10020afa8e06SEd Maste int
10030afa8e06SEd Maste fido_assert_set_count(fido_assert_t *assert, size_t n)
10040afa8e06SEd Maste {
10050afa8e06SEd Maste 	void *new_stmt;
10060afa8e06SEd Maste 
10070afa8e06SEd Maste #ifdef FIDO_FUZZ
10080afa8e06SEd Maste 	if (n > UINT8_MAX) {
10090afa8e06SEd Maste 		fido_log_debug("%s: n > UINT8_MAX", __func__);
10100afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
10110afa8e06SEd Maste 	}
10120afa8e06SEd Maste #endif
10130afa8e06SEd Maste 
10140afa8e06SEd Maste 	new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
10150afa8e06SEd Maste 	    sizeof(fido_assert_stmt));
10160afa8e06SEd Maste 	if (new_stmt == NULL)
10170afa8e06SEd Maste 		return (FIDO_ERR_INTERNAL);
10180afa8e06SEd Maste 
10190afa8e06SEd Maste 	assert->stmt = new_stmt;
10200afa8e06SEd Maste 	assert->stmt_cnt = n;
10210afa8e06SEd Maste 	assert->stmt_len = n;
10220afa8e06SEd Maste 
10230afa8e06SEd Maste 	return (FIDO_OK);
10240afa8e06SEd Maste }
1025