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