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