xref: /openbsd-src/lib/libfido2/src/cbor.c (revision ab19a69ebe1d1275c01611de862453c36b3d15b9)
1d75efeb7Sdjm /*
2*ab19a69eSdjm  * 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/hmac.h>
8d75efeb7Sdjm #include <openssl/sha.h>
9d75efeb7Sdjm #include "fido.h"
10d75efeb7Sdjm 
11d75efeb7Sdjm static int
check_key_type(cbor_item_t * item)12d75efeb7Sdjm check_key_type(cbor_item_t *item)
13d75efeb7Sdjm {
14d75efeb7Sdjm 	if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
15d75efeb7Sdjm 	    item->type == CBOR_TYPE_STRING)
16d75efeb7Sdjm 		return (0);
17d75efeb7Sdjm 
1832a20e26Sdjm 	fido_log_debug("%s: invalid type: %d", __func__, item->type);
19d75efeb7Sdjm 
20d75efeb7Sdjm 	return (-1);
21d75efeb7Sdjm }
22d75efeb7Sdjm 
23d75efeb7Sdjm /*
24d75efeb7Sdjm  * Validate CTAP2 canonical CBOR encoding rules for maps.
25d75efeb7Sdjm  */
26d75efeb7Sdjm static int
ctap_check_cbor(cbor_item_t * prev,cbor_item_t * curr)27d75efeb7Sdjm ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
28d75efeb7Sdjm {
29d75efeb7Sdjm 	size_t	curr_len;
30d75efeb7Sdjm 	size_t	prev_len;
31d75efeb7Sdjm 
32d75efeb7Sdjm 	if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
33d75efeb7Sdjm 		return (-1);
34d75efeb7Sdjm 
35d75efeb7Sdjm 	if (prev->type != curr->type) {
36d75efeb7Sdjm 		if (prev->type < curr->type)
37d75efeb7Sdjm 			return (0);
3832a20e26Sdjm 		fido_log_debug("%s: unsorted types", __func__);
39d75efeb7Sdjm 		return (-1);
40d75efeb7Sdjm 	}
41d75efeb7Sdjm 
42d75efeb7Sdjm 	if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
43d75efeb7Sdjm 		if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
44d75efeb7Sdjm 		    cbor_get_int(curr) > cbor_get_int(prev))
45d75efeb7Sdjm 			return (0);
46d75efeb7Sdjm 	} else {
47d75efeb7Sdjm 		curr_len = cbor_string_length(curr);
48d75efeb7Sdjm 		prev_len = cbor_string_length(prev);
49d75efeb7Sdjm 
50d75efeb7Sdjm 		if (curr_len > prev_len || (curr_len == prev_len &&
51d75efeb7Sdjm 		    memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
52d75efeb7Sdjm 		    curr_len) < 0))
53d75efeb7Sdjm 			return (0);
54d75efeb7Sdjm 	}
55d75efeb7Sdjm 
5632a20e26Sdjm 	fido_log_debug("%s: invalid cbor", __func__);
57d75efeb7Sdjm 
58d75efeb7Sdjm 	return (-1);
59d75efeb7Sdjm }
60d75efeb7Sdjm 
61d75efeb7Sdjm int
cbor_map_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,const cbor_item_t *,void *))62d75efeb7Sdjm cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
63d75efeb7Sdjm     const cbor_item_t *, void *))
64d75efeb7Sdjm {
65d75efeb7Sdjm 	struct cbor_pair	*v;
66d75efeb7Sdjm 	size_t			 n;
67d75efeb7Sdjm 
68d75efeb7Sdjm 	if ((v = cbor_map_handle(item)) == NULL) {
6932a20e26Sdjm 		fido_log_debug("%s: cbor_map_handle", __func__);
70d75efeb7Sdjm 		return (-1);
71d75efeb7Sdjm 	}
72d75efeb7Sdjm 
73d75efeb7Sdjm 	n = cbor_map_size(item);
74d75efeb7Sdjm 
75d75efeb7Sdjm 	for (size_t i = 0; i < n; i++) {
76d75efeb7Sdjm 		if (v[i].key == NULL || v[i].value == NULL) {
7732a20e26Sdjm 			fido_log_debug("%s: key=%p, value=%p for i=%zu",
7832a20e26Sdjm 			    __func__, (void *)v[i].key, (void *)v[i].value, i);
79d75efeb7Sdjm 			return (-1);
80d75efeb7Sdjm 		}
81d75efeb7Sdjm 		if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
8232a20e26Sdjm 			fido_log_debug("%s: ctap_check_cbor", __func__);
83d75efeb7Sdjm 			return (-1);
84d75efeb7Sdjm 		}
85d75efeb7Sdjm 		if (f(v[i].key, v[i].value, arg) < 0) {
8632a20e26Sdjm 			fido_log_debug("%s: iterator < 0 on i=%zu", __func__,
8732a20e26Sdjm 			    i);
88d75efeb7Sdjm 			return (-1);
89d75efeb7Sdjm 		}
90d75efeb7Sdjm 	}
91d75efeb7Sdjm 
92d75efeb7Sdjm 	return (0);
93d75efeb7Sdjm }
94d75efeb7Sdjm 
95d75efeb7Sdjm int
cbor_array_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,void *))96d75efeb7Sdjm cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
97d75efeb7Sdjm     void *))
98d75efeb7Sdjm {
99d75efeb7Sdjm 	cbor_item_t	**v;
100d75efeb7Sdjm 	size_t		  n;
101d75efeb7Sdjm 
102d75efeb7Sdjm 	if ((v = cbor_array_handle(item)) == NULL) {
10332a20e26Sdjm 		fido_log_debug("%s: cbor_array_handle", __func__);
104d75efeb7Sdjm 		return (-1);
105d75efeb7Sdjm 	}
106d75efeb7Sdjm 
107d75efeb7Sdjm 	n = cbor_array_size(item);
108d75efeb7Sdjm 
109d75efeb7Sdjm 	for (size_t i = 0; i < n; i++)
110d75efeb7Sdjm 		if (v[i] == NULL || f(v[i], arg) < 0) {
11132a20e26Sdjm 			fido_log_debug("%s: iterator < 0 on i=%zu,%p",
11232a20e26Sdjm 			    __func__, i, (void *)v[i]);
113d75efeb7Sdjm 			return (-1);
114d75efeb7Sdjm 		}
115d75efeb7Sdjm 
116d75efeb7Sdjm 	return (0);
117d75efeb7Sdjm }
118d75efeb7Sdjm 
119d75efeb7Sdjm int
cbor_parse_reply(const unsigned char * blob,size_t blob_len,void * arg,int (* parser)(const cbor_item_t *,const cbor_item_t *,void *))12032a20e26Sdjm cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg,
121d75efeb7Sdjm     int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
122d75efeb7Sdjm {
123d75efeb7Sdjm 	cbor_item_t		*item = NULL;
124d75efeb7Sdjm 	struct cbor_load_result	 cbor;
125d75efeb7Sdjm 	int			 r;
126d75efeb7Sdjm 
127d75efeb7Sdjm 	if (blob_len < 1) {
12832a20e26Sdjm 		fido_log_debug("%s: blob_len=%zu", __func__, blob_len);
129d75efeb7Sdjm 		r = FIDO_ERR_RX;
130d75efeb7Sdjm 		goto fail;
131d75efeb7Sdjm 	}
132d75efeb7Sdjm 
133d75efeb7Sdjm 	if (blob[0] != FIDO_OK) {
13432a20e26Sdjm 		fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
135d75efeb7Sdjm 		r = blob[0];
136d75efeb7Sdjm 		goto fail;
137d75efeb7Sdjm 	}
138d75efeb7Sdjm 
139d75efeb7Sdjm 	if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
14032a20e26Sdjm 		fido_log_debug("%s: cbor_load", __func__);
141d75efeb7Sdjm 		r = FIDO_ERR_RX_NOT_CBOR;
142d75efeb7Sdjm 		goto fail;
143d75efeb7Sdjm 	}
144d75efeb7Sdjm 
145d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
146d75efeb7Sdjm 	    cbor_map_is_definite(item) == false) {
14732a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
148d75efeb7Sdjm 		r = FIDO_ERR_RX_INVALID_CBOR;
149d75efeb7Sdjm 		goto fail;
150d75efeb7Sdjm 	}
151d75efeb7Sdjm 
152d75efeb7Sdjm 	if (cbor_map_iter(item, arg, parser) < 0) {
15332a20e26Sdjm 		fido_log_debug("%s: cbor_map_iter", __func__);
154d75efeb7Sdjm 		r = FIDO_ERR_RX_INVALID_CBOR;
155d75efeb7Sdjm 		goto fail;
156d75efeb7Sdjm 	}
157d75efeb7Sdjm 
158d75efeb7Sdjm 	r = FIDO_OK;
159d75efeb7Sdjm fail:
160d75efeb7Sdjm 	if (item != NULL)
161d75efeb7Sdjm 		cbor_decref(&item);
162d75efeb7Sdjm 
163d75efeb7Sdjm 	return (r);
164d75efeb7Sdjm }
165d75efeb7Sdjm 
166d75efeb7Sdjm void
cbor_vector_free(cbor_item_t ** item,size_t len)167d75efeb7Sdjm cbor_vector_free(cbor_item_t **item, size_t len)
168d75efeb7Sdjm {
169d75efeb7Sdjm 	for (size_t i = 0; i < len; i++)
170d75efeb7Sdjm 		if (item[i] != NULL)
171d75efeb7Sdjm 			cbor_decref(&item[i]);
172d75efeb7Sdjm }
173d75efeb7Sdjm 
174d75efeb7Sdjm int
cbor_bytestring_copy(const cbor_item_t * item,unsigned char ** buf,size_t * len)175d75efeb7Sdjm cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
176d75efeb7Sdjm {
177d75efeb7Sdjm 	if (*buf != NULL || *len != 0) {
17832a20e26Sdjm 		fido_log_debug("%s: dup", __func__);
179d75efeb7Sdjm 		return (-1);
180d75efeb7Sdjm 	}
181d75efeb7Sdjm 
182d75efeb7Sdjm 	if (cbor_isa_bytestring(item) == false ||
183d75efeb7Sdjm 	    cbor_bytestring_is_definite(item) == false) {
18432a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
185d75efeb7Sdjm 		return (-1);
186d75efeb7Sdjm 	}
187d75efeb7Sdjm 
188d75efeb7Sdjm 	*len = cbor_bytestring_length(item);
189d75efeb7Sdjm 	if ((*buf = malloc(*len)) == NULL) {
190d75efeb7Sdjm 		*len = 0;
191d75efeb7Sdjm 		return (-1);
192d75efeb7Sdjm 	}
193d75efeb7Sdjm 
194d75efeb7Sdjm 	memcpy(*buf, cbor_bytestring_handle(item), *len);
195d75efeb7Sdjm 
196d75efeb7Sdjm 	return (0);
197d75efeb7Sdjm }
198d75efeb7Sdjm 
199d75efeb7Sdjm int
cbor_string_copy(const cbor_item_t * item,char ** str)200d75efeb7Sdjm cbor_string_copy(const cbor_item_t *item, char **str)
201d75efeb7Sdjm {
202d75efeb7Sdjm 	size_t len;
203d75efeb7Sdjm 
204d75efeb7Sdjm 	if (*str != NULL) {
20532a20e26Sdjm 		fido_log_debug("%s: dup", __func__);
206d75efeb7Sdjm 		return (-1);
207d75efeb7Sdjm 	}
208d75efeb7Sdjm 
209d75efeb7Sdjm 	if (cbor_isa_string(item) == false ||
210d75efeb7Sdjm 	    cbor_string_is_definite(item) == false) {
21132a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
212d75efeb7Sdjm 		return (-1);
213d75efeb7Sdjm 	}
214d75efeb7Sdjm 
215d75efeb7Sdjm 	if ((len = cbor_string_length(item)) == SIZE_MAX ||
216d75efeb7Sdjm 	    (*str = malloc(len + 1)) == NULL)
217d75efeb7Sdjm 		return (-1);
218d75efeb7Sdjm 
219d75efeb7Sdjm 	memcpy(*str, cbor_string_handle(item), len);
220d75efeb7Sdjm 	(*str)[len] = '\0';
221d75efeb7Sdjm 
222d75efeb7Sdjm 	return (0);
223d75efeb7Sdjm }
224d75efeb7Sdjm 
225d75efeb7Sdjm int
cbor_add_bytestring(cbor_item_t * item,const char * key,const unsigned char * value,size_t value_len)226d75efeb7Sdjm cbor_add_bytestring(cbor_item_t *item, const char *key,
227d75efeb7Sdjm     const unsigned char *value, size_t value_len)
228d75efeb7Sdjm {
229d75efeb7Sdjm 	struct cbor_pair pair;
230d75efeb7Sdjm 	int ok = -1;
231d75efeb7Sdjm 
232d75efeb7Sdjm 	memset(&pair, 0, sizeof(pair));
233d75efeb7Sdjm 
234d75efeb7Sdjm 	if ((pair.key = cbor_build_string(key)) == NULL ||
235d75efeb7Sdjm 	    (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
23632a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
237d75efeb7Sdjm 		goto fail;
238d75efeb7Sdjm 	}
239d75efeb7Sdjm 
240d75efeb7Sdjm 	if (!cbor_map_add(item, pair)) {
24132a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
242d75efeb7Sdjm 		goto fail;
243d75efeb7Sdjm 	}
244d75efeb7Sdjm 
245d75efeb7Sdjm 	ok = 0;
246d75efeb7Sdjm fail:
247d75efeb7Sdjm 	if (pair.key)
248d75efeb7Sdjm 		cbor_decref(&pair.key);
249d75efeb7Sdjm 	if (pair.value)
250d75efeb7Sdjm 		cbor_decref(&pair.value);
251d75efeb7Sdjm 
252d75efeb7Sdjm 	return (ok);
253d75efeb7Sdjm }
254d75efeb7Sdjm 
255d75efeb7Sdjm int
cbor_add_string(cbor_item_t * item,const char * key,const char * value)256d75efeb7Sdjm cbor_add_string(cbor_item_t *item, const char *key, const char *value)
257d75efeb7Sdjm {
258d75efeb7Sdjm 	struct cbor_pair pair;
259d75efeb7Sdjm 	int ok = -1;
260d75efeb7Sdjm 
261d75efeb7Sdjm 	memset(&pair, 0, sizeof(pair));
262d75efeb7Sdjm 
263d75efeb7Sdjm 	if ((pair.key = cbor_build_string(key)) == NULL ||
264d75efeb7Sdjm 	    (pair.value = cbor_build_string(value)) == NULL) {
26532a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
266d75efeb7Sdjm 		goto fail;
267d75efeb7Sdjm 	}
268d75efeb7Sdjm 
269d75efeb7Sdjm 	if (!cbor_map_add(item, pair)) {
27032a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
271d75efeb7Sdjm 		goto fail;
272d75efeb7Sdjm 	}
273d75efeb7Sdjm 
274d75efeb7Sdjm 	ok = 0;
275d75efeb7Sdjm fail:
276d75efeb7Sdjm 	if (pair.key)
277d75efeb7Sdjm 		cbor_decref(&pair.key);
278d75efeb7Sdjm 	if (pair.value)
279d75efeb7Sdjm 		cbor_decref(&pair.value);
280d75efeb7Sdjm 
281d75efeb7Sdjm 	return (ok);
282d75efeb7Sdjm }
283d75efeb7Sdjm 
284d75efeb7Sdjm int
cbor_add_bool(cbor_item_t * item,const char * key,fido_opt_t value)285d75efeb7Sdjm cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
286d75efeb7Sdjm {
287d75efeb7Sdjm 	struct cbor_pair pair;
288d75efeb7Sdjm 	int ok = -1;
289d75efeb7Sdjm 
290d75efeb7Sdjm 	memset(&pair, 0, sizeof(pair));
291d75efeb7Sdjm 
292d75efeb7Sdjm 	if ((pair.key = cbor_build_string(key)) == NULL ||
293d75efeb7Sdjm 	    (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
29432a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
295d75efeb7Sdjm 		goto fail;
296d75efeb7Sdjm 	}
297d75efeb7Sdjm 
298d75efeb7Sdjm 	if (!cbor_map_add(item, pair)) {
29932a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
30032a20e26Sdjm 		goto fail;
30132a20e26Sdjm 	}
30232a20e26Sdjm 
30332a20e26Sdjm 	ok = 0;
30432a20e26Sdjm fail:
30532a20e26Sdjm 	if (pair.key)
30632a20e26Sdjm 		cbor_decref(&pair.key);
30732a20e26Sdjm 	if (pair.value)
30832a20e26Sdjm 		cbor_decref(&pair.value);
30932a20e26Sdjm 
31032a20e26Sdjm 	return (ok);
31132a20e26Sdjm }
31232a20e26Sdjm 
31332a20e26Sdjm static int
cbor_add_uint8(cbor_item_t * item,const char * key,uint8_t value)31432a20e26Sdjm cbor_add_uint8(cbor_item_t *item, const char *key, uint8_t value)
31532a20e26Sdjm {
31632a20e26Sdjm 	struct cbor_pair pair;
31732a20e26Sdjm 	int ok = -1;
31832a20e26Sdjm 
31932a20e26Sdjm 	memset(&pair, 0, sizeof(pair));
32032a20e26Sdjm 
32132a20e26Sdjm 	if ((pair.key = cbor_build_string(key)) == NULL ||
32232a20e26Sdjm 	    (pair.value = cbor_build_uint8(value)) == NULL) {
32332a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
32432a20e26Sdjm 		goto fail;
32532a20e26Sdjm 	}
32632a20e26Sdjm 
32732a20e26Sdjm 	if (!cbor_map_add(item, pair)) {
32832a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
329d75efeb7Sdjm 		goto fail;
330d75efeb7Sdjm 	}
331d75efeb7Sdjm 
332d75efeb7Sdjm 	ok = 0;
333d75efeb7Sdjm fail:
334d75efeb7Sdjm 	if (pair.key)
335d75efeb7Sdjm 		cbor_decref(&pair.key);
336d75efeb7Sdjm 	if (pair.value)
337d75efeb7Sdjm 		cbor_decref(&pair.value);
338d75efeb7Sdjm 
339d75efeb7Sdjm 	return (ok);
340d75efeb7Sdjm }
341d75efeb7Sdjm 
342d75efeb7Sdjm static int
cbor_add_arg(cbor_item_t * item,uint8_t n,cbor_item_t * arg)343d75efeb7Sdjm cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
344d75efeb7Sdjm {
345d75efeb7Sdjm 	struct cbor_pair pair;
346d75efeb7Sdjm 	int ok = -1;
347d75efeb7Sdjm 
348d75efeb7Sdjm 	memset(&pair, 0, sizeof(pair));
349d75efeb7Sdjm 
350d75efeb7Sdjm 	if (arg == NULL)
351d75efeb7Sdjm 		return (0); /* empty argument */
352d75efeb7Sdjm 
353d75efeb7Sdjm 	if ((pair.key = cbor_build_uint8(n)) == NULL) {
35432a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
355d75efeb7Sdjm 		goto fail;
356d75efeb7Sdjm 	}
357d75efeb7Sdjm 
358d75efeb7Sdjm 	pair.value = arg;
359d75efeb7Sdjm 
360d75efeb7Sdjm 	if (!cbor_map_add(item, pair)) {
36132a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
362d75efeb7Sdjm 		goto fail;
363d75efeb7Sdjm 	}
364d75efeb7Sdjm 
365d75efeb7Sdjm 	ok = 0;
366d75efeb7Sdjm fail:
367d75efeb7Sdjm 	if (pair.key)
368d75efeb7Sdjm 		cbor_decref(&pair.key);
369d75efeb7Sdjm 
370d75efeb7Sdjm 	return (ok);
371d75efeb7Sdjm }
372d75efeb7Sdjm 
373d75efeb7Sdjm cbor_item_t *
cbor_flatten_vector(cbor_item_t * argv[],size_t argc)374d75efeb7Sdjm cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
375d75efeb7Sdjm {
376d75efeb7Sdjm 	cbor_item_t	*map;
377d75efeb7Sdjm 	uint8_t		 i;
378d75efeb7Sdjm 
379d75efeb7Sdjm 	if (argc > UINT8_MAX - 1)
380d75efeb7Sdjm 		return (NULL);
381d75efeb7Sdjm 
382d75efeb7Sdjm 	if ((map = cbor_new_definite_map(argc)) == NULL)
383d75efeb7Sdjm 		return (NULL);
384d75efeb7Sdjm 
385d75efeb7Sdjm 	for (i = 0; i < argc; i++)
386739189a3Sdjm 		if (cbor_add_arg(map, (uint8_t)(i + 1), argv[i]) < 0)
387d75efeb7Sdjm 			break;
388d75efeb7Sdjm 
389d75efeb7Sdjm 	if (i != argc) {
390d75efeb7Sdjm 		cbor_decref(&map);
391d75efeb7Sdjm 		map = NULL;
392d75efeb7Sdjm 	}
393d75efeb7Sdjm 
394d75efeb7Sdjm 	return (map);
395d75efeb7Sdjm }
396d75efeb7Sdjm 
397d75efeb7Sdjm int
cbor_build_frame(uint8_t cmd,cbor_item_t * argv[],size_t argc,fido_blob_t * f)398d75efeb7Sdjm cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
399d75efeb7Sdjm {
400d75efeb7Sdjm 	cbor_item_t	*flat = NULL;
401d75efeb7Sdjm 	unsigned char	*cbor = NULL;
402d75efeb7Sdjm 	size_t		 cbor_len;
403d75efeb7Sdjm 	size_t		 cbor_alloc_len;
404d75efeb7Sdjm 	int		 ok = -1;
405d75efeb7Sdjm 
406d75efeb7Sdjm 	if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
407d75efeb7Sdjm 		goto fail;
408d75efeb7Sdjm 
409d75efeb7Sdjm 	cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
410d75efeb7Sdjm 	if (cbor_len == 0 || cbor_len == SIZE_MAX) {
41132a20e26Sdjm 		fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len);
412d75efeb7Sdjm 		goto fail;
413d75efeb7Sdjm 	}
414d75efeb7Sdjm 
415d75efeb7Sdjm 	if ((f->ptr = malloc(cbor_len + 1)) == NULL)
416d75efeb7Sdjm 		goto fail;
417d75efeb7Sdjm 
418d75efeb7Sdjm 	f->len = cbor_len + 1;
419d75efeb7Sdjm 	f->ptr[0] = cmd;
420d75efeb7Sdjm 	memcpy(f->ptr + 1, cbor, f->len - 1);
421d75efeb7Sdjm 
422d75efeb7Sdjm 	ok = 0;
423d75efeb7Sdjm fail:
424d75efeb7Sdjm 	if (flat != NULL)
425d75efeb7Sdjm 		cbor_decref(&flat);
426d75efeb7Sdjm 
427d75efeb7Sdjm 	free(cbor);
428d75efeb7Sdjm 
429d75efeb7Sdjm 	return (ok);
430d75efeb7Sdjm }
431d75efeb7Sdjm 
432d75efeb7Sdjm cbor_item_t *
cbor_encode_rp_entity(const fido_rp_t * rp)43332a20e26Sdjm cbor_encode_rp_entity(const fido_rp_t *rp)
434d75efeb7Sdjm {
435d75efeb7Sdjm 	cbor_item_t *item = NULL;
436d75efeb7Sdjm 
437d75efeb7Sdjm 	if ((item = cbor_new_definite_map(2)) == NULL)
438d75efeb7Sdjm 		return (NULL);
439d75efeb7Sdjm 
440d75efeb7Sdjm 	if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
441d75efeb7Sdjm 	    (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
442d75efeb7Sdjm 		cbor_decref(&item);
443d75efeb7Sdjm 		return (NULL);
444d75efeb7Sdjm 	}
445d75efeb7Sdjm 
446d75efeb7Sdjm 	return (item);
447d75efeb7Sdjm }
448d75efeb7Sdjm 
449d75efeb7Sdjm cbor_item_t *
cbor_encode_user_entity(const fido_user_t * user)45032a20e26Sdjm cbor_encode_user_entity(const fido_user_t *user)
451d75efeb7Sdjm {
452d75efeb7Sdjm 	cbor_item_t		*item = NULL;
453d75efeb7Sdjm 	const fido_blob_t	*id = &user->id;
454d75efeb7Sdjm 	const char		*display = user->display_name;
455d75efeb7Sdjm 
456d75efeb7Sdjm 	if ((item = cbor_new_definite_map(4)) == NULL)
457d75efeb7Sdjm 		return (NULL);
458d75efeb7Sdjm 
459d75efeb7Sdjm 	if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
460d75efeb7Sdjm 	    (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
461d75efeb7Sdjm 	    (user->name && cbor_add_string(item, "name", user->name) < 0) ||
462d75efeb7Sdjm 	    (display && cbor_add_string(item, "displayName", display) < 0)) {
463d75efeb7Sdjm 		cbor_decref(&item);
464d75efeb7Sdjm 		return (NULL);
465d75efeb7Sdjm 	}
466d75efeb7Sdjm 
467d75efeb7Sdjm 	return (item);
468d75efeb7Sdjm }
469d75efeb7Sdjm 
470d75efeb7Sdjm cbor_item_t *
cbor_encode_pubkey_param(int cose_alg)47132a20e26Sdjm cbor_encode_pubkey_param(int cose_alg)
472d75efeb7Sdjm {
473d75efeb7Sdjm 	cbor_item_t		*item = NULL;
474d75efeb7Sdjm 	cbor_item_t		*body = NULL;
475d75efeb7Sdjm 	struct cbor_pair	 alg;
476d75efeb7Sdjm 	int			 ok = -1;
477d75efeb7Sdjm 
478d75efeb7Sdjm 	memset(&alg, 0, sizeof(alg));
479d75efeb7Sdjm 
480d75efeb7Sdjm 	if ((item = cbor_new_definite_array(1)) == NULL ||
481d75efeb7Sdjm 	    (body = cbor_new_definite_map(2)) == NULL ||
482d75efeb7Sdjm 	    cose_alg > -1 || cose_alg < INT16_MIN)
483d75efeb7Sdjm 		goto fail;
484d75efeb7Sdjm 
485d75efeb7Sdjm 	alg.key = cbor_build_string("alg");
486d75efeb7Sdjm 
487d75efeb7Sdjm 	if (-cose_alg - 1 > UINT8_MAX)
488d75efeb7Sdjm 		alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
489d75efeb7Sdjm 	else
490d75efeb7Sdjm 		alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
491d75efeb7Sdjm 
492d75efeb7Sdjm 	if (alg.key == NULL || alg.value == NULL) {
49332a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
494d75efeb7Sdjm 		goto fail;
495d75efeb7Sdjm 	}
496d75efeb7Sdjm 
497d75efeb7Sdjm 	if (cbor_map_add(body, alg) == false ||
498d75efeb7Sdjm 	    cbor_add_string(body, "type", "public-key") < 0 ||
499d75efeb7Sdjm 	    cbor_array_push(item, body) == false)
500d75efeb7Sdjm 		goto fail;
501d75efeb7Sdjm 
502d75efeb7Sdjm 	ok  = 0;
503d75efeb7Sdjm fail:
504d75efeb7Sdjm 	if (ok < 0) {
505d75efeb7Sdjm 		if (item != NULL) {
506d75efeb7Sdjm 			cbor_decref(&item);
507d75efeb7Sdjm 			item = NULL;
508d75efeb7Sdjm 		}
509d75efeb7Sdjm 	}
510d75efeb7Sdjm 
511d75efeb7Sdjm 	if (body != NULL)
512d75efeb7Sdjm 		cbor_decref(&body);
513d75efeb7Sdjm 	if (alg.key != NULL)
514d75efeb7Sdjm 		cbor_decref(&alg.key);
515d75efeb7Sdjm 	if (alg.value != NULL)
516d75efeb7Sdjm 		cbor_decref(&alg.value);
517d75efeb7Sdjm 
518d75efeb7Sdjm 	return (item);
519d75efeb7Sdjm }
520d75efeb7Sdjm 
521d75efeb7Sdjm cbor_item_t *
cbor_encode_pubkey(const fido_blob_t * pubkey)52232a20e26Sdjm cbor_encode_pubkey(const fido_blob_t *pubkey)
523d75efeb7Sdjm {
524d75efeb7Sdjm 	cbor_item_t *cbor_key = NULL;
525d75efeb7Sdjm 
526d75efeb7Sdjm 	if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
527d75efeb7Sdjm 	    cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
528d75efeb7Sdjm 	    cbor_add_string(cbor_key, "type", "public-key") < 0) {
529d75efeb7Sdjm 		if (cbor_key)
530d75efeb7Sdjm 			cbor_decref(&cbor_key);
531d75efeb7Sdjm 		return (NULL);
532d75efeb7Sdjm 	}
533d75efeb7Sdjm 
534d75efeb7Sdjm 	return (cbor_key);
535d75efeb7Sdjm }
536d75efeb7Sdjm 
537d75efeb7Sdjm cbor_item_t *
cbor_encode_pubkey_list(const fido_blob_array_t * list)53832a20e26Sdjm cbor_encode_pubkey_list(const fido_blob_array_t *list)
539d75efeb7Sdjm {
540d75efeb7Sdjm 	cbor_item_t	*array = NULL;
541d75efeb7Sdjm 	cbor_item_t	*key = NULL;
542d75efeb7Sdjm 
543d75efeb7Sdjm 	if ((array = cbor_new_definite_array(list->len)) == NULL)
544d75efeb7Sdjm 		goto fail;
545d75efeb7Sdjm 
546d75efeb7Sdjm 	for (size_t i = 0; i < list->len; i++) {
54732a20e26Sdjm 		if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL ||
548d75efeb7Sdjm 		    cbor_array_push(array, key) == false)
549d75efeb7Sdjm 			goto fail;
550d75efeb7Sdjm 		cbor_decref(&key);
551d75efeb7Sdjm 	}
552d75efeb7Sdjm 
553d75efeb7Sdjm 	return (array);
554d75efeb7Sdjm fail:
555d75efeb7Sdjm 	if (key != NULL)
556d75efeb7Sdjm 		cbor_decref(&key);
557d75efeb7Sdjm 	if (array != NULL)
558d75efeb7Sdjm 		cbor_decref(&array);
559d75efeb7Sdjm 
560d75efeb7Sdjm 	return (NULL);
561d75efeb7Sdjm }
562d75efeb7Sdjm 
563*ab19a69eSdjm cbor_item_t *
cbor_encode_str_array(const fido_str_array_t * a)564*ab19a69eSdjm cbor_encode_str_array(const fido_str_array_t *a)
565*ab19a69eSdjm {
566*ab19a69eSdjm 	cbor_item_t	*array = NULL;
567*ab19a69eSdjm 	cbor_item_t	*entry = NULL;
568*ab19a69eSdjm 
569*ab19a69eSdjm 	if ((array = cbor_new_definite_array(a->len)) == NULL)
570*ab19a69eSdjm 		goto fail;
571*ab19a69eSdjm 
572*ab19a69eSdjm 	for (size_t i = 0; i < a->len; i++) {
573*ab19a69eSdjm 		if ((entry = cbor_build_string(a->ptr[i])) == NULL ||
574*ab19a69eSdjm 		    cbor_array_push(array, entry) == false)
575*ab19a69eSdjm 			goto fail;
576*ab19a69eSdjm 		cbor_decref(&entry);
577*ab19a69eSdjm 	}
578*ab19a69eSdjm 
579*ab19a69eSdjm 	return (array);
580*ab19a69eSdjm fail:
581*ab19a69eSdjm 	if (entry != NULL)
582*ab19a69eSdjm 		cbor_decref(&entry);
583*ab19a69eSdjm 	if (array != NULL)
584*ab19a69eSdjm 		cbor_decref(&array);
585*ab19a69eSdjm 
586*ab19a69eSdjm 	return (NULL);
587*ab19a69eSdjm }
588*ab19a69eSdjm 
589c4a807edSdjm static int
cbor_encode_largeblob_key_ext(cbor_item_t * map)590c4a807edSdjm cbor_encode_largeblob_key_ext(cbor_item_t *map)
591c4a807edSdjm {
592c4a807edSdjm 	if (map == NULL ||
593c4a807edSdjm 	    cbor_add_bool(map, "largeBlobKey", FIDO_OPT_TRUE) < 0)
594c4a807edSdjm 		return (-1);
595c4a807edSdjm 
596c4a807edSdjm 	return (0);
597c4a807edSdjm }
598c4a807edSdjm 
599d75efeb7Sdjm cbor_item_t *
cbor_encode_cred_ext(const fido_cred_ext_t * ext,const fido_blob_t * blob)600c4a807edSdjm cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
601d75efeb7Sdjm {
602d75efeb7Sdjm 	cbor_item_t *item = NULL;
60332a20e26Sdjm 	size_t size = 0;
604d75efeb7Sdjm 
605c4a807edSdjm 	if (ext->mask & FIDO_EXT_CRED_BLOB)
606c4a807edSdjm 		size++;
60732a20e26Sdjm 	if (ext->mask & FIDO_EXT_HMAC_SECRET)
60832a20e26Sdjm 		size++;
60932a20e26Sdjm 	if (ext->mask & FIDO_EXT_CRED_PROTECT)
61032a20e26Sdjm 		size++;
611c4a807edSdjm 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
612c4a807edSdjm 		size++;
613*ab19a69eSdjm 	if (ext->mask & FIDO_EXT_MINPINLEN)
614*ab19a69eSdjm 		size++;
615c4a807edSdjm 
61632a20e26Sdjm 	if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
617d75efeb7Sdjm 		return (NULL);
618d75efeb7Sdjm 
619c4a807edSdjm 	if (ext->mask & FIDO_EXT_CRED_BLOB) {
620c4a807edSdjm 		if (cbor_add_bytestring(item, "credBlob", blob->ptr,
621c4a807edSdjm 		    blob->len) < 0) {
622d75efeb7Sdjm 			cbor_decref(&item);
623d75efeb7Sdjm 			return (NULL);
624d75efeb7Sdjm 		}
62532a20e26Sdjm 	}
62632a20e26Sdjm 	if (ext->mask & FIDO_EXT_CRED_PROTECT) {
627739189a3Sdjm 		if (ext->prot < 0 || ext->prot > UINT8_MAX ||
628739189a3Sdjm 		    cbor_add_uint8(item, "credProtect",
629739189a3Sdjm 		    (uint8_t)ext->prot) < 0) {
63032a20e26Sdjm 			cbor_decref(&item);
63132a20e26Sdjm 			return (NULL);
63232a20e26Sdjm 		}
63332a20e26Sdjm 	}
634c4a807edSdjm 	if (ext->mask & FIDO_EXT_HMAC_SECRET) {
635c4a807edSdjm 		if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
636c4a807edSdjm 			cbor_decref(&item);
637c4a807edSdjm 			return (NULL);
638c4a807edSdjm 		}
639c4a807edSdjm 	}
640c4a807edSdjm 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
641c4a807edSdjm 		if (cbor_encode_largeblob_key_ext(item) < 0) {
642c4a807edSdjm 			cbor_decref(&item);
643c4a807edSdjm 			return (NULL);
644c4a807edSdjm 		}
645c4a807edSdjm 	}
646*ab19a69eSdjm 	if (ext->mask & FIDO_EXT_MINPINLEN) {
647*ab19a69eSdjm 		if (cbor_add_bool(item, "minPinLength", FIDO_OPT_TRUE) < 0) {
648*ab19a69eSdjm 			cbor_decref(&item);
649*ab19a69eSdjm 			return (NULL);
650*ab19a69eSdjm 		}
651*ab19a69eSdjm 	}
652d75efeb7Sdjm 
653d75efeb7Sdjm 	return (item);
654d75efeb7Sdjm }
655d75efeb7Sdjm 
656d75efeb7Sdjm cbor_item_t *
cbor_encode_cred_opt(fido_opt_t rk,fido_opt_t uv)657c4a807edSdjm cbor_encode_cred_opt(fido_opt_t rk, fido_opt_t uv)
658d75efeb7Sdjm {
659d75efeb7Sdjm 	cbor_item_t *item = NULL;
660d75efeb7Sdjm 
661d75efeb7Sdjm 	if ((item = cbor_new_definite_map(2)) == NULL)
662d75efeb7Sdjm 		return (NULL);
663d75efeb7Sdjm 	if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
664d75efeb7Sdjm 	    (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
665d75efeb7Sdjm 		cbor_decref(&item);
666d75efeb7Sdjm 		return (NULL);
667d75efeb7Sdjm 	}
668d75efeb7Sdjm 
669d75efeb7Sdjm 	return (item);
670d75efeb7Sdjm }
671d75efeb7Sdjm 
672d75efeb7Sdjm cbor_item_t *
cbor_encode_assert_opt(fido_opt_t up,fido_opt_t uv)673c4a807edSdjm cbor_encode_assert_opt(fido_opt_t up, fido_opt_t uv)
674d75efeb7Sdjm {
675d75efeb7Sdjm 	cbor_item_t *item = NULL;
676d75efeb7Sdjm 
677d75efeb7Sdjm 	if ((item = cbor_new_definite_map(2)) == NULL)
678d75efeb7Sdjm 		return (NULL);
679d75efeb7Sdjm 	if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
680d75efeb7Sdjm 	    (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
681d75efeb7Sdjm 		cbor_decref(&item);
682d75efeb7Sdjm 		return (NULL);
683d75efeb7Sdjm 	}
684d75efeb7Sdjm 
685d75efeb7Sdjm 	return (item);
686d75efeb7Sdjm }
687d75efeb7Sdjm 
688d75efeb7Sdjm cbor_item_t *
cbor_encode_pin_auth(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * data)689c4a807edSdjm cbor_encode_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
690c4a807edSdjm     const fido_blob_t *data)
691d75efeb7Sdjm {
692d75efeb7Sdjm 	const EVP_MD	*md = NULL;
693d75efeb7Sdjm 	unsigned char	 dgst[SHA256_DIGEST_LENGTH];
694d75efeb7Sdjm 	unsigned int	 dgst_len;
695c4a807edSdjm 	size_t		 outlen;
696c4a807edSdjm 	uint8_t		 prot;
697c4a807edSdjm 	fido_blob_t	 key;
698d75efeb7Sdjm 
699c4a807edSdjm 	key.ptr = secret->ptr;
700c4a807edSdjm 	key.len = secret->len;
701c4a807edSdjm 
702c4a807edSdjm 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
703c4a807edSdjm 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
704c4a807edSdjm 		return (NULL);
705c4a807edSdjm 	}
706c4a807edSdjm 
707c4a807edSdjm 	/* select hmac portion of the shared secret */
708c4a807edSdjm 	if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
709c4a807edSdjm 		key.len = 32;
710c4a807edSdjm 
711c4a807edSdjm 	if ((md = EVP_sha256()) == NULL || HMAC(md, key.ptr,
712c4a807edSdjm 	    (int)key.len, data->ptr, data->len, dgst,
713d75efeb7Sdjm 	    &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
714d75efeb7Sdjm 		return (NULL);
715d75efeb7Sdjm 
716c4a807edSdjm 	outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
717c4a807edSdjm 
718c4a807edSdjm 	return (cbor_build_bytestring(dgst, outlen));
719d75efeb7Sdjm }
720d75efeb7Sdjm 
721d75efeb7Sdjm cbor_item_t *
cbor_encode_pin_opt(const fido_dev_t * dev)722c4a807edSdjm cbor_encode_pin_opt(const fido_dev_t *dev)
723d75efeb7Sdjm {
724c4a807edSdjm 	uint8_t	    prot;
725d75efeb7Sdjm 
726c4a807edSdjm 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
727c4a807edSdjm 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
728d75efeb7Sdjm 		return (NULL);
729d75efeb7Sdjm 	}
730d75efeb7Sdjm 
731c4a807edSdjm 	return (cbor_build_uint8(prot));
732d75efeb7Sdjm }
733d75efeb7Sdjm 
734d75efeb7Sdjm cbor_item_t *
cbor_encode_change_pin_auth(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * new_pin_enc,const fido_blob_t * pin_hash_enc)735c4a807edSdjm cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
736c4a807edSdjm     const fido_blob_t *new_pin_enc, const fido_blob_t *pin_hash_enc)
737d75efeb7Sdjm {
738d75efeb7Sdjm 	unsigned char	 dgst[SHA256_DIGEST_LENGTH];
739d75efeb7Sdjm 	unsigned int	 dgst_len;
740d75efeb7Sdjm 	cbor_item_t	*item = NULL;
741d75efeb7Sdjm 	const EVP_MD	*md = NULL;
742d75efeb7Sdjm 	HMAC_CTX	*ctx = NULL;
743c4a807edSdjm 	fido_blob_t	 key;
744c4a807edSdjm 	uint8_t		 prot;
745c4a807edSdjm 	size_t		 outlen;
746d75efeb7Sdjm 
747c4a807edSdjm 	key.ptr = secret->ptr;
748c4a807edSdjm 	key.len = secret->len;
749d75efeb7Sdjm 
750c4a807edSdjm 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
751c4a807edSdjm 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
752d75efeb7Sdjm 		goto fail;
753d75efeb7Sdjm 	}
754d75efeb7Sdjm 
755c4a807edSdjm 	if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
756c4a807edSdjm 		key.len = 32;
757d75efeb7Sdjm 
758d75efeb7Sdjm 	if ((ctx = HMAC_CTX_new()) == NULL ||
759d75efeb7Sdjm 	    (md = EVP_sha256())  == NULL ||
760c4a807edSdjm 	    HMAC_Init_ex(ctx, key.ptr, (int)key.len, md, NULL) == 0 ||
761c4a807edSdjm 	    HMAC_Update(ctx, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
762c4a807edSdjm 	    HMAC_Update(ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
763c4a807edSdjm 	    HMAC_Final(ctx, dgst, &dgst_len) == 0 ||
764c4a807edSdjm 	    dgst_len != SHA256_DIGEST_LENGTH) {
76532a20e26Sdjm 		fido_log_debug("%s: HMAC", __func__);
766d75efeb7Sdjm 		goto fail;
767d75efeb7Sdjm 	}
768d75efeb7Sdjm 
769c4a807edSdjm 	outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
770c4a807edSdjm 
771c4a807edSdjm 	if ((item = cbor_build_bytestring(dgst, outlen)) == NULL) {
77232a20e26Sdjm 		fido_log_debug("%s: cbor_build_bytestring", __func__);
773d75efeb7Sdjm 		goto fail;
774d75efeb7Sdjm 	}
775d75efeb7Sdjm 
776d75efeb7Sdjm fail:
777d75efeb7Sdjm 	HMAC_CTX_free(ctx);
778d75efeb7Sdjm 
779d75efeb7Sdjm 	return (item);
780d75efeb7Sdjm }
781d75efeb7Sdjm 
782c4a807edSdjm static int
cbor_encode_hmac_secret_param(const fido_dev_t * dev,cbor_item_t * item,const fido_blob_t * ecdh,const es256_pk_t * pk,const fido_blob_t * salt)783c4a807edSdjm cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
784c4a807edSdjm     const fido_blob_t *ecdh, const es256_pk_t *pk, const fido_blob_t *salt)
785d75efeb7Sdjm {
786d75efeb7Sdjm 	cbor_item_t		*param = NULL;
787c4a807edSdjm 	cbor_item_t		*argv[4];
788d75efeb7Sdjm 	struct cbor_pair	 pair;
789c4a807edSdjm 	fido_blob_t		*enc = NULL;
790*ab19a69eSdjm 	uint8_t			 prot;
791c4a807edSdjm 	int			 r;
792d75efeb7Sdjm 
793d75efeb7Sdjm 	memset(argv, 0, sizeof(argv));
794d75efeb7Sdjm 	memset(&pair, 0, sizeof(pair));
795d75efeb7Sdjm 
796c4a807edSdjm 	if (item == NULL || ecdh == NULL || pk == NULL || salt->ptr == NULL) {
797c4a807edSdjm 		fido_log_debug("%s: ecdh=%p, pk=%p, salt->ptr=%p", __func__,
798c4a807edSdjm 		    (const void *)ecdh, (const void *)pk,
799c4a807edSdjm 		    (const void *)salt->ptr);
800c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
801d75efeb7Sdjm 		goto fail;
802d75efeb7Sdjm 	}
803d75efeb7Sdjm 
804c4a807edSdjm 	if (salt->len != 32 && salt->len != 64) {
805c4a807edSdjm 		fido_log_debug("%s: salt->len=%zu", __func__, salt->len);
806c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
807c4a807edSdjm 		goto fail;
808c4a807edSdjm 	}
809c4a807edSdjm 
810c4a807edSdjm 	if ((enc = fido_blob_new()) == NULL ||
811c4a807edSdjm 	    aes256_cbc_enc(dev, ecdh, salt, enc) < 0) {
812c4a807edSdjm 		fido_log_debug("%s: aes256_cbc_enc", __func__);
813c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
814d75efeb7Sdjm 		goto fail;
815d75efeb7Sdjm 	}
816d75efeb7Sdjm 
817*ab19a69eSdjm 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
818*ab19a69eSdjm 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
819*ab19a69eSdjm 		r = FIDO_ERR_INTERNAL;
820*ab19a69eSdjm 		goto fail;
821*ab19a69eSdjm 	}
822*ab19a69eSdjm 
823d75efeb7Sdjm 	/* XXX not pin, but salt */
824d75efeb7Sdjm 	if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
825c4a807edSdjm 	    (argv[1] = fido_blob_encode(enc)) == NULL ||
826c4a807edSdjm 	    (argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL ||
827*ab19a69eSdjm 	    (prot != 1 && (argv[3] = cbor_build_uint8(prot)) == NULL)) {
82832a20e26Sdjm 		fido_log_debug("%s: cbor encode", __func__);
829c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
830d75efeb7Sdjm 		goto fail;
831d75efeb7Sdjm 	}
832d75efeb7Sdjm 
833c4a807edSdjm 	if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) {
83432a20e26Sdjm 		fido_log_debug("%s: cbor_flatten_vector", __func__);
835c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
836d75efeb7Sdjm 		goto fail;
837d75efeb7Sdjm 	}
838d75efeb7Sdjm 
839d75efeb7Sdjm 	if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
84032a20e26Sdjm 		fido_log_debug("%s: cbor_build", __func__);
841c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
842d75efeb7Sdjm 		goto fail;
843d75efeb7Sdjm 	}
844d75efeb7Sdjm 
845d75efeb7Sdjm 	pair.value = param;
846d75efeb7Sdjm 
847d75efeb7Sdjm 	if (!cbor_map_add(item, pair)) {
84832a20e26Sdjm 		fido_log_debug("%s: cbor_map_add", __func__);
849c4a807edSdjm 		r = FIDO_ERR_INTERNAL;
850d75efeb7Sdjm 		goto fail;
851d75efeb7Sdjm 	}
852d75efeb7Sdjm 
853c4a807edSdjm 	r = FIDO_OK;
854c4a807edSdjm 
855d75efeb7Sdjm fail:
856c4a807edSdjm 	cbor_vector_free(argv, nitems(argv));
857d75efeb7Sdjm 
858d75efeb7Sdjm 	if (param != NULL)
859d75efeb7Sdjm 		cbor_decref(&param);
860d75efeb7Sdjm 	if (pair.key != NULL)
861d75efeb7Sdjm 		cbor_decref(&pair.key);
862d75efeb7Sdjm 
863c4a807edSdjm 	fido_blob_free(&enc);
864c4a807edSdjm 
865c4a807edSdjm 	return (r);
866c4a807edSdjm }
867c4a807edSdjm 
868c4a807edSdjm cbor_item_t *
cbor_encode_assert_ext(fido_dev_t * dev,const fido_assert_ext_t * ext,const fido_blob_t * ecdh,const es256_pk_t * pk)869c4a807edSdjm cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext,
870c4a807edSdjm     const fido_blob_t *ecdh, const es256_pk_t *pk)
871c4a807edSdjm {
872c4a807edSdjm 	cbor_item_t *item = NULL;
873c4a807edSdjm 	size_t size = 0;
874c4a807edSdjm 
875c4a807edSdjm 	if (ext->mask & FIDO_EXT_CRED_BLOB)
876c4a807edSdjm 		size++;
877c4a807edSdjm 	if (ext->mask & FIDO_EXT_HMAC_SECRET)
878c4a807edSdjm 		size++;
879c4a807edSdjm 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
880c4a807edSdjm 		size++;
881c4a807edSdjm 	if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
882c4a807edSdjm 		return (NULL);
883c4a807edSdjm 
884c4a807edSdjm 	if (ext->mask & FIDO_EXT_CRED_BLOB) {
885c4a807edSdjm 		if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) {
886c4a807edSdjm 			cbor_decref(&item);
887c4a807edSdjm 			return (NULL);
888c4a807edSdjm 		}
889c4a807edSdjm 	}
890c4a807edSdjm 	if (ext->mask & FIDO_EXT_HMAC_SECRET) {
891c4a807edSdjm 		if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk,
892c4a807edSdjm 		    &ext->hmac_salt) < 0) {
893c4a807edSdjm 			cbor_decref(&item);
894c4a807edSdjm 			return (NULL);
895c4a807edSdjm 		}
896c4a807edSdjm 	}
897c4a807edSdjm 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
898c4a807edSdjm 		if (cbor_encode_largeblob_key_ext(item) < 0) {
899c4a807edSdjm 			cbor_decref(&item);
900c4a807edSdjm 			return (NULL);
901c4a807edSdjm 		}
902c4a807edSdjm 	}
903c4a807edSdjm 
904d75efeb7Sdjm 	return (item);
905d75efeb7Sdjm }
906d75efeb7Sdjm 
907d75efeb7Sdjm int
cbor_decode_fmt(const cbor_item_t * item,char ** fmt)90832a20e26Sdjm cbor_decode_fmt(const cbor_item_t *item, char **fmt)
909d75efeb7Sdjm {
910d75efeb7Sdjm 	char	*type = NULL;
911d75efeb7Sdjm 
912d75efeb7Sdjm 	if (cbor_string_copy(item, &type) < 0) {
91332a20e26Sdjm 		fido_log_debug("%s: cbor_string_copy", __func__);
914d75efeb7Sdjm 		return (-1);
915d75efeb7Sdjm 	}
916d75efeb7Sdjm 
917c4a807edSdjm 	if (strcmp(type, "packed") && strcmp(type, "fido-u2f") &&
918*ab19a69eSdjm 	    strcmp(type, "none") && strcmp(type, "tpm")) {
91932a20e26Sdjm 		fido_log_debug("%s: type=%s", __func__, type);
920d75efeb7Sdjm 		free(type);
921d75efeb7Sdjm 		return (-1);
922d75efeb7Sdjm 	}
923d75efeb7Sdjm 
924d75efeb7Sdjm 	*fmt = type;
925d75efeb7Sdjm 
926d75efeb7Sdjm 	return (0);
927d75efeb7Sdjm }
928d75efeb7Sdjm 
929d75efeb7Sdjm struct cose_key {
930d75efeb7Sdjm 	int kty;
931d75efeb7Sdjm 	int alg;
932d75efeb7Sdjm 	int crv;
933d75efeb7Sdjm };
934d75efeb7Sdjm 
935d75efeb7Sdjm static int
find_cose_alg(const cbor_item_t * key,const cbor_item_t * val,void * arg)936d75efeb7Sdjm find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
937d75efeb7Sdjm {
938d75efeb7Sdjm 	struct cose_key *cose_key = arg;
939d75efeb7Sdjm 
940d75efeb7Sdjm 	if (cbor_isa_uint(key) == true &&
941d75efeb7Sdjm 	    cbor_int_get_width(key) == CBOR_INT_8) {
942d75efeb7Sdjm 		switch (cbor_get_uint8(key)) {
943d75efeb7Sdjm 		case 1:
944d75efeb7Sdjm 			if (cbor_isa_uint(val) == false ||
945d75efeb7Sdjm 			    cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
94632a20e26Sdjm 				fido_log_debug("%s: kty", __func__);
947d75efeb7Sdjm 				return (-1);
948d75efeb7Sdjm 			}
949d75efeb7Sdjm 
950d75efeb7Sdjm 			cose_key->kty = (int)cbor_get_int(val);
951d75efeb7Sdjm 
952d75efeb7Sdjm 			break;
953d75efeb7Sdjm 		case 3:
954d75efeb7Sdjm 			if (cbor_isa_negint(val) == false ||
955d75efeb7Sdjm 			    cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
95632a20e26Sdjm 				fido_log_debug("%s: alg", __func__);
957d75efeb7Sdjm 				return (-1);
958d75efeb7Sdjm 			}
959d75efeb7Sdjm 
960d75efeb7Sdjm 			cose_key->alg = -(int)cbor_get_int(val) - 1;
961d75efeb7Sdjm 
962d75efeb7Sdjm 			break;
963d75efeb7Sdjm 		}
964d75efeb7Sdjm 	} else if (cbor_isa_negint(key) == true &&
965d75efeb7Sdjm 	    cbor_int_get_width(key) == CBOR_INT_8) {
966d75efeb7Sdjm 		if (cbor_get_uint8(key) == 0) {
967d75efeb7Sdjm 			/* get crv if not rsa, otherwise ignore */
968d75efeb7Sdjm 			if (cbor_isa_uint(val) == true &&
969d75efeb7Sdjm 			    cbor_get_int(val) <= INT_MAX &&
970d75efeb7Sdjm 			    cose_key->crv == 0)
971d75efeb7Sdjm 				cose_key->crv = (int)cbor_get_int(val);
972d75efeb7Sdjm 		}
973d75efeb7Sdjm 	}
974d75efeb7Sdjm 
975d75efeb7Sdjm 	return (0);
976d75efeb7Sdjm }
977d75efeb7Sdjm 
978d75efeb7Sdjm static int
get_cose_alg(const cbor_item_t * item,int * cose_alg)979d75efeb7Sdjm get_cose_alg(const cbor_item_t *item, int *cose_alg)
980d75efeb7Sdjm {
981d75efeb7Sdjm 	struct cose_key cose_key;
982d75efeb7Sdjm 
983d75efeb7Sdjm 	memset(&cose_key, 0, sizeof(cose_key));
984d75efeb7Sdjm 
985d75efeb7Sdjm 	*cose_alg = 0;
986d75efeb7Sdjm 
987d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
988d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
989d75efeb7Sdjm 	    cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
99032a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
991d75efeb7Sdjm 		return (-1);
992d75efeb7Sdjm 	}
993d75efeb7Sdjm 
994d75efeb7Sdjm 	switch (cose_key.alg) {
995d75efeb7Sdjm 	case COSE_ES256:
996d75efeb7Sdjm 		if (cose_key.kty != COSE_KTY_EC2 ||
997d75efeb7Sdjm 		    cose_key.crv != COSE_P256) {
99832a20e26Sdjm 			fido_log_debug("%s: invalid kty/crv", __func__);
999d75efeb7Sdjm 			return (-1);
1000d75efeb7Sdjm 		}
1001d75efeb7Sdjm 
1002d75efeb7Sdjm 		break;
1003d75efeb7Sdjm 	case COSE_EDDSA:
1004d75efeb7Sdjm 		if (cose_key.kty != COSE_KTY_OKP ||
1005d75efeb7Sdjm 		    cose_key.crv != COSE_ED25519) {
100632a20e26Sdjm 			fido_log_debug("%s: invalid kty/crv", __func__);
1007d75efeb7Sdjm 			return (-1);
1008d75efeb7Sdjm 		}
1009d75efeb7Sdjm 
1010d75efeb7Sdjm 		break;
1011d75efeb7Sdjm 	case COSE_RS256:
1012d75efeb7Sdjm 		if (cose_key.kty != COSE_KTY_RSA) {
101332a20e26Sdjm 			fido_log_debug("%s: invalid kty/crv", __func__);
1014d75efeb7Sdjm 			return (-1);
1015d75efeb7Sdjm 		}
1016d75efeb7Sdjm 
1017d75efeb7Sdjm 		break;
1018d75efeb7Sdjm 	default:
101932a20e26Sdjm 		fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg);
1020d75efeb7Sdjm 
1021d75efeb7Sdjm 		return (-1);
1022d75efeb7Sdjm 	}
1023d75efeb7Sdjm 
1024d75efeb7Sdjm 	*cose_alg = cose_key.alg;
1025d75efeb7Sdjm 
1026d75efeb7Sdjm 	return (0);
1027d75efeb7Sdjm }
1028d75efeb7Sdjm 
1029d75efeb7Sdjm int
cbor_decode_pubkey(const cbor_item_t * item,int * type,void * key)103032a20e26Sdjm cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
1031d75efeb7Sdjm {
1032d75efeb7Sdjm 	if (get_cose_alg(item, type) < 0) {
103332a20e26Sdjm 		fido_log_debug("%s: get_cose_alg", __func__);
1034d75efeb7Sdjm 		return (-1);
1035d75efeb7Sdjm 	}
1036d75efeb7Sdjm 
1037d75efeb7Sdjm 	switch (*type) {
1038d75efeb7Sdjm 	case COSE_ES256:
1039d75efeb7Sdjm 		if (es256_pk_decode(item, key) < 0) {
104032a20e26Sdjm 			fido_log_debug("%s: es256_pk_decode", __func__);
1041d75efeb7Sdjm 			return (-1);
1042d75efeb7Sdjm 		}
1043d75efeb7Sdjm 		break;
1044d75efeb7Sdjm 	case COSE_RS256:
1045d75efeb7Sdjm 		if (rs256_pk_decode(item, key) < 0) {
104632a20e26Sdjm 			fido_log_debug("%s: rs256_pk_decode", __func__);
1047d75efeb7Sdjm 			return (-1);
1048d75efeb7Sdjm 		}
1049d75efeb7Sdjm 		break;
1050d75efeb7Sdjm 	case COSE_EDDSA:
1051d75efeb7Sdjm 		if (eddsa_pk_decode(item, key) < 0) {
105232a20e26Sdjm 			fido_log_debug("%s: eddsa_pk_decode", __func__);
1053d75efeb7Sdjm 			return (-1);
1054d75efeb7Sdjm 		}
1055d75efeb7Sdjm 		break;
1056d75efeb7Sdjm 	default:
105732a20e26Sdjm 		fido_log_debug("%s: invalid cose_alg %d", __func__, *type);
1058d75efeb7Sdjm 		return (-1);
1059d75efeb7Sdjm 	}
1060d75efeb7Sdjm 
1061d75efeb7Sdjm 	return (0);
1062d75efeb7Sdjm }
1063d75efeb7Sdjm 
1064d75efeb7Sdjm static int
decode_attcred(const unsigned char ** buf,size_t * len,int cose_alg,fido_attcred_t * attcred)1065d75efeb7Sdjm decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
1066d75efeb7Sdjm     fido_attcred_t *attcred)
1067d75efeb7Sdjm {
1068d75efeb7Sdjm 	cbor_item_t		*item = NULL;
1069d75efeb7Sdjm 	struct cbor_load_result	 cbor;
1070d75efeb7Sdjm 	uint16_t		 id_len;
1071d75efeb7Sdjm 	int			 ok = -1;
1072d75efeb7Sdjm 
1073c4a807edSdjm 	fido_log_xxd(*buf, *len, "%s", __func__);
1074d75efeb7Sdjm 
107532a20e26Sdjm 	if (fido_buf_read(buf, len, &attcred->aaguid,
107632a20e26Sdjm 	    sizeof(attcred->aaguid)) < 0) {
107732a20e26Sdjm 		fido_log_debug("%s: fido_buf_read aaguid", __func__);
1078d75efeb7Sdjm 		return (-1);
1079d75efeb7Sdjm 	}
1080d75efeb7Sdjm 
108132a20e26Sdjm 	if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
108232a20e26Sdjm 		fido_log_debug("%s: fido_buf_read id_len", __func__);
1083d75efeb7Sdjm 		return (-1);
1084d75efeb7Sdjm 	}
1085d75efeb7Sdjm 
1086d75efeb7Sdjm 	attcred->id.len = (size_t)be16toh(id_len);
1087d75efeb7Sdjm 	if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
1088d75efeb7Sdjm 		return (-1);
1089d75efeb7Sdjm 
109032a20e26Sdjm 	fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
1091d75efeb7Sdjm 
109232a20e26Sdjm 	if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
109332a20e26Sdjm 		fido_log_debug("%s: fido_buf_read id", __func__);
1094d75efeb7Sdjm 		return (-1);
1095d75efeb7Sdjm 	}
1096d75efeb7Sdjm 
1097d75efeb7Sdjm 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
109832a20e26Sdjm 		fido_log_debug("%s: cbor_load", __func__);
1099d75efeb7Sdjm 		goto fail;
1100d75efeb7Sdjm 	}
1101d75efeb7Sdjm 
110232a20e26Sdjm 	if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
110332a20e26Sdjm 		fido_log_debug("%s: cbor_decode_pubkey", __func__);
1104d75efeb7Sdjm 		goto fail;
1105d75efeb7Sdjm 	}
1106d75efeb7Sdjm 
1107d75efeb7Sdjm 	if (attcred->type != cose_alg) {
110832a20e26Sdjm 		fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
1109d75efeb7Sdjm 		    attcred->type, cose_alg);
1110d75efeb7Sdjm 		goto fail;
1111d75efeb7Sdjm 	}
1112d75efeb7Sdjm 
1113d75efeb7Sdjm 	*buf += cbor.read;
1114d75efeb7Sdjm 	*len -= cbor.read;
1115d75efeb7Sdjm 
1116d75efeb7Sdjm 	ok = 0;
1117d75efeb7Sdjm fail:
1118d75efeb7Sdjm 	if (item != NULL)
1119d75efeb7Sdjm 		cbor_decref(&item);
1120d75efeb7Sdjm 
1121d75efeb7Sdjm 	return (ok);
1122d75efeb7Sdjm }
1123d75efeb7Sdjm 
1124d75efeb7Sdjm static int
decode_cred_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)1125c4a807edSdjm decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1126d75efeb7Sdjm {
112732a20e26Sdjm 	fido_cred_ext_t	*authdata_ext = arg;
1128d75efeb7Sdjm 	char		*type = NULL;
1129d75efeb7Sdjm 	int		 ok = -1;
1130d75efeb7Sdjm 
113132a20e26Sdjm 	if (cbor_string_copy(key, &type) < 0) {
113232a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1133d75efeb7Sdjm 		ok = 0; /* ignore */
1134d75efeb7Sdjm 		goto out;
1135d75efeb7Sdjm 	}
1136d75efeb7Sdjm 
113732a20e26Sdjm 	if (strcmp(type, "hmac-secret") == 0) {
1138d75efeb7Sdjm 		if (cbor_isa_float_ctrl(val) == false ||
1139d75efeb7Sdjm 		    cbor_float_get_width(val) != CBOR_FLOAT_0 ||
114032a20e26Sdjm 		    cbor_is_bool(val) == false) {
114132a20e26Sdjm 			fido_log_debug("%s: cbor type", __func__);
1142d75efeb7Sdjm 			goto out;
1143d75efeb7Sdjm 		}
1144d75efeb7Sdjm 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
114532a20e26Sdjm 			authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
114632a20e26Sdjm 	} else if (strcmp(type, "credProtect") == 0) {
114732a20e26Sdjm 		if (cbor_isa_uint(val) == false ||
114832a20e26Sdjm 		    cbor_int_get_width(val) != CBOR_INT_8) {
114932a20e26Sdjm 			fido_log_debug("%s: cbor type", __func__);
115032a20e26Sdjm 			goto out;
115132a20e26Sdjm 		}
115232a20e26Sdjm 		authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
115332a20e26Sdjm 		authdata_ext->prot = cbor_get_uint8(val);
1154c4a807edSdjm 	} else if (strcmp(type, "credBlob") == 0) {
1155c4a807edSdjm 		if (cbor_isa_float_ctrl(val) == false ||
1156c4a807edSdjm 		    cbor_float_get_width(val) != CBOR_FLOAT_0 ||
1157c4a807edSdjm 		    cbor_is_bool(val) == false) {
1158c4a807edSdjm 			fido_log_debug("%s: cbor type", __func__);
1159c4a807edSdjm 			goto out;
1160c4a807edSdjm 		}
1161c4a807edSdjm 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1162c4a807edSdjm 			authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1163*ab19a69eSdjm 	} else if (strcmp(type, "minPinLength") == 0) {
1164*ab19a69eSdjm 		if (cbor_isa_uint(val) == false ||
1165*ab19a69eSdjm 		    cbor_int_get_width(val) != CBOR_INT_8) {
1166*ab19a69eSdjm 			fido_log_debug("%s: cbor type", __func__);
1167*ab19a69eSdjm 			goto out;
1168*ab19a69eSdjm 		}
1169*ab19a69eSdjm 		authdata_ext->mask |= FIDO_EXT_MINPINLEN;
1170*ab19a69eSdjm 		authdata_ext->minpinlen = cbor_get_uint8(val);
117132a20e26Sdjm 	}
1172d75efeb7Sdjm 
1173d75efeb7Sdjm 	ok = 0;
1174d75efeb7Sdjm out:
1175d75efeb7Sdjm 	free(type);
1176d75efeb7Sdjm 
1177d75efeb7Sdjm 	return (ok);
1178d75efeb7Sdjm }
1179d75efeb7Sdjm 
1180d75efeb7Sdjm static int
decode_cred_extensions(const unsigned char ** buf,size_t * len,fido_cred_ext_t * authdata_ext)1181c4a807edSdjm decode_cred_extensions(const unsigned char **buf, size_t *len,
118232a20e26Sdjm     fido_cred_ext_t *authdata_ext)
1183d75efeb7Sdjm {
1184d75efeb7Sdjm 	cbor_item_t		*item = NULL;
1185d75efeb7Sdjm 	struct cbor_load_result	 cbor;
1186d75efeb7Sdjm 	int			 ok = -1;
1187d75efeb7Sdjm 
118832a20e26Sdjm 	memset(authdata_ext, 0, sizeof(*authdata_ext));
1189d75efeb7Sdjm 
1190c4a807edSdjm 	fido_log_xxd(*buf, *len, "%s", __func__);
1191c4a807edSdjm 
1192d75efeb7Sdjm 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
119332a20e26Sdjm 		fido_log_debug("%s: cbor_load", __func__);
1194d75efeb7Sdjm 		goto fail;
1195d75efeb7Sdjm 	}
1196d75efeb7Sdjm 
1197d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1198d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1199c4a807edSdjm 	    cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
120032a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1201d75efeb7Sdjm 		goto fail;
1202d75efeb7Sdjm 	}
1203d75efeb7Sdjm 
1204d75efeb7Sdjm 	*buf += cbor.read;
1205d75efeb7Sdjm 	*len -= cbor.read;
1206d75efeb7Sdjm 
1207d75efeb7Sdjm 	ok = 0;
1208d75efeb7Sdjm fail:
1209d75efeb7Sdjm 	if (item != NULL)
1210d75efeb7Sdjm 		cbor_decref(&item);
1211d75efeb7Sdjm 
1212d75efeb7Sdjm 	return (ok);
1213d75efeb7Sdjm }
1214d75efeb7Sdjm 
1215d75efeb7Sdjm static int
decode_assert_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)1216c4a807edSdjm decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
1217c4a807edSdjm     void *arg)
1218d75efeb7Sdjm {
1219c4a807edSdjm 	fido_assert_extattr_t	*authdata_ext = arg;
1220d75efeb7Sdjm 	char			*type = NULL;
1221d75efeb7Sdjm 	int			 ok = -1;
1222d75efeb7Sdjm 
1223c4a807edSdjm 	if (cbor_string_copy(key, &type) < 0) {
122432a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1225d75efeb7Sdjm 		ok = 0; /* ignore */
1226d75efeb7Sdjm 		goto out;
1227d75efeb7Sdjm 	}
1228d75efeb7Sdjm 
1229c4a807edSdjm 	if (strcmp(type, "hmac-secret") == 0) {
1230c4a807edSdjm 		if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) {
1231c4a807edSdjm 			fido_log_debug("%s: fido_blob_decode", __func__);
1232c4a807edSdjm 			goto out;
1233c4a807edSdjm 		}
1234c4a807edSdjm 		authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
1235c4a807edSdjm 	} else if (strcmp(type, "credBlob") == 0) {
1236c4a807edSdjm 		if (fido_blob_decode(val, &authdata_ext->blob) < 0) {
1237c4a807edSdjm 			fido_log_debug("%s: fido_blob_decode", __func__);
1238c4a807edSdjm 			goto out;
1239c4a807edSdjm 		}
1240c4a807edSdjm 		authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1241c4a807edSdjm 	}
1242c4a807edSdjm 
1243c4a807edSdjm 	ok = 0;
1244d75efeb7Sdjm out:
1245d75efeb7Sdjm 	free(type);
1246d75efeb7Sdjm 
1247d75efeb7Sdjm 	return (ok);
1248d75efeb7Sdjm }
1249d75efeb7Sdjm 
1250d75efeb7Sdjm static int
decode_assert_extensions(const unsigned char ** buf,size_t * len,fido_assert_extattr_t * authdata_ext)1251c4a807edSdjm decode_assert_extensions(const unsigned char **buf, size_t *len,
1252c4a807edSdjm     fido_assert_extattr_t *authdata_ext)
1253d75efeb7Sdjm {
1254d75efeb7Sdjm 	cbor_item_t		*item = NULL;
1255d75efeb7Sdjm 	struct cbor_load_result	 cbor;
1256d75efeb7Sdjm 	int			 ok = -1;
1257d75efeb7Sdjm 
1258c4a807edSdjm 	fido_log_xxd(*buf, *len, "%s", __func__);
1259d75efeb7Sdjm 
1260d75efeb7Sdjm 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
126132a20e26Sdjm 		fido_log_debug("%s: cbor_load", __func__);
1262d75efeb7Sdjm 		goto fail;
1263d75efeb7Sdjm 	}
1264d75efeb7Sdjm 
1265d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1266d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1267c4a807edSdjm 	    cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
126832a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1269d75efeb7Sdjm 		goto fail;
1270d75efeb7Sdjm 	}
1271d75efeb7Sdjm 
1272d75efeb7Sdjm 	*buf += cbor.read;
1273d75efeb7Sdjm 	*len -= cbor.read;
1274d75efeb7Sdjm 
1275d75efeb7Sdjm 	ok = 0;
1276d75efeb7Sdjm fail:
1277d75efeb7Sdjm 	if (item != NULL)
1278d75efeb7Sdjm 		cbor_decref(&item);
1279d75efeb7Sdjm 
1280d75efeb7Sdjm 	return (ok);
1281d75efeb7Sdjm }
1282d75efeb7Sdjm 
1283d75efeb7Sdjm int
cbor_decode_cred_authdata(const cbor_item_t * item,int cose_alg,fido_blob_t * authdata_cbor,fido_authdata_t * authdata,fido_attcred_t * attcred,fido_cred_ext_t * authdata_ext)128432a20e26Sdjm cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1285d75efeb7Sdjm     fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
128632a20e26Sdjm     fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
1287d75efeb7Sdjm {
1288d75efeb7Sdjm 	const unsigned char	*buf = NULL;
1289d75efeb7Sdjm 	size_t			 len;
1290d75efeb7Sdjm 	size_t			 alloc_len;
1291d75efeb7Sdjm 
1292d75efeb7Sdjm 	if (cbor_isa_bytestring(item) == false ||
1293d75efeb7Sdjm 	    cbor_bytestring_is_definite(item) == false) {
129432a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1295d75efeb7Sdjm 		return (-1);
1296d75efeb7Sdjm 	}
1297d75efeb7Sdjm 
1298d75efeb7Sdjm 	if (authdata_cbor->ptr != NULL ||
1299d75efeb7Sdjm 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1300d75efeb7Sdjm 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
130132a20e26Sdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1302d75efeb7Sdjm 		return (-1);
1303d75efeb7Sdjm 	}
1304d75efeb7Sdjm 
1305d75efeb7Sdjm 	buf = cbor_bytestring_handle(item);
1306d75efeb7Sdjm 	len = cbor_bytestring_length(item);
1307c4a807edSdjm 	fido_log_xxd(buf, len, "%s", __func__);
1308d75efeb7Sdjm 
130932a20e26Sdjm 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
131032a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
1311d75efeb7Sdjm 		return (-1);
1312d75efeb7Sdjm 	}
1313d75efeb7Sdjm 
1314d75efeb7Sdjm 	authdata->sigcount = be32toh(authdata->sigcount);
1315d75efeb7Sdjm 
1316d75efeb7Sdjm 	if (attcred != NULL) {
1317d75efeb7Sdjm 		if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1318d75efeb7Sdjm 		    decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1319d75efeb7Sdjm 			return (-1);
1320d75efeb7Sdjm 	}
1321d75efeb7Sdjm 
1322d75efeb7Sdjm 	if (authdata_ext != NULL) {
1323d75efeb7Sdjm 		if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
1324c4a807edSdjm 		    decode_cred_extensions(&buf, &len, authdata_ext) < 0)
1325d75efeb7Sdjm 			return (-1);
1326d75efeb7Sdjm 	}
1327d75efeb7Sdjm 
1328d75efeb7Sdjm 	/* XXX we should probably ensure that len == 0 at this point */
1329d75efeb7Sdjm 
1330d75efeb7Sdjm 	return (FIDO_OK);
1331d75efeb7Sdjm }
1332d75efeb7Sdjm 
1333d75efeb7Sdjm int
cbor_decode_assert_authdata(const cbor_item_t * item,fido_blob_t * authdata_cbor,fido_authdata_t * authdata,fido_assert_extattr_t * authdata_ext)133432a20e26Sdjm cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
1335c4a807edSdjm     fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
1336d75efeb7Sdjm {
1337d75efeb7Sdjm 	const unsigned char	*buf = NULL;
1338d75efeb7Sdjm 	size_t			 len;
1339d75efeb7Sdjm 	size_t			 alloc_len;
1340d75efeb7Sdjm 
1341d75efeb7Sdjm 	if (cbor_isa_bytestring(item) == false ||
1342d75efeb7Sdjm 	    cbor_bytestring_is_definite(item) == false) {
134332a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1344d75efeb7Sdjm 		return (-1);
1345d75efeb7Sdjm 	}
1346d75efeb7Sdjm 
1347d75efeb7Sdjm 	if (authdata_cbor->ptr != NULL ||
1348d75efeb7Sdjm 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1349d75efeb7Sdjm 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
135032a20e26Sdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1351d75efeb7Sdjm 		return (-1);
1352d75efeb7Sdjm 	}
1353d75efeb7Sdjm 
1354d75efeb7Sdjm 	buf = cbor_bytestring_handle(item);
1355d75efeb7Sdjm 	len = cbor_bytestring_length(item);
1356d75efeb7Sdjm 
135732a20e26Sdjm 	fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1358d75efeb7Sdjm 
135932a20e26Sdjm 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
136032a20e26Sdjm 		fido_log_debug("%s: fido_buf_read", __func__);
1361d75efeb7Sdjm 		return (-1);
1362d75efeb7Sdjm 	}
1363d75efeb7Sdjm 
1364d75efeb7Sdjm 	authdata->sigcount = be32toh(authdata->sigcount);
1365d75efeb7Sdjm 
1366d75efeb7Sdjm 	if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
1367c4a807edSdjm 		if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
1368c4a807edSdjm 			fido_log_debug("%s: decode_assert_extensions",
1369c4a807edSdjm 			    __func__);
1370d75efeb7Sdjm 			return (-1);
1371d75efeb7Sdjm 		}
1372d75efeb7Sdjm 	}
1373d75efeb7Sdjm 
1374d75efeb7Sdjm 	/* XXX we should probably ensure that len == 0 at this point */
1375d75efeb7Sdjm 
1376d75efeb7Sdjm 	return (FIDO_OK);
1377d75efeb7Sdjm }
1378d75efeb7Sdjm 
1379d75efeb7Sdjm static int
decode_x5c(const cbor_item_t * item,void * arg)1380d75efeb7Sdjm decode_x5c(const cbor_item_t *item, void *arg)
1381d75efeb7Sdjm {
1382d75efeb7Sdjm 	fido_blob_t *x5c = arg;
1383d75efeb7Sdjm 
1384d75efeb7Sdjm 	if (x5c->len)
1385d75efeb7Sdjm 		return (0); /* ignore */
1386d75efeb7Sdjm 
1387c4a807edSdjm 	return (fido_blob_decode(item, x5c));
1388d75efeb7Sdjm }
1389d75efeb7Sdjm 
1390d75efeb7Sdjm static int
decode_attstmt_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1391d75efeb7Sdjm decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1392d75efeb7Sdjm {
1393d75efeb7Sdjm 	fido_attstmt_t	*attstmt = arg;
1394d75efeb7Sdjm 	char		*name = NULL;
1395d75efeb7Sdjm 	int		 ok = -1;
1396d75efeb7Sdjm 
1397d75efeb7Sdjm 	if (cbor_string_copy(key, &name) < 0) {
139832a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1399d75efeb7Sdjm 		ok = 0; /* ignore */
1400d75efeb7Sdjm 		goto out;
1401d75efeb7Sdjm 	}
1402d75efeb7Sdjm 
1403d75efeb7Sdjm 	if (!strcmp(name, "alg")) {
1404d75efeb7Sdjm 		if (cbor_isa_negint(val) == false ||
1405739189a3Sdjm 		    cbor_get_int(val) > UINT16_MAX) {
140632a20e26Sdjm 			fido_log_debug("%s: alg", __func__);
1407d75efeb7Sdjm 			goto out;
1408d75efeb7Sdjm 		}
1409*ab19a69eSdjm 		attstmt->alg = -(int)cbor_get_int(val) - 1;
1410*ab19a69eSdjm 		if (attstmt->alg != COSE_ES256 && attstmt->alg != COSE_RS256 &&
1411*ab19a69eSdjm 		    attstmt->alg != COSE_EDDSA && attstmt->alg != COSE_RS1) {
1412*ab19a69eSdjm 			fido_log_debug("%s: unsupported attstmt->alg=%d",
1413*ab19a69eSdjm 			    __func__, attstmt->alg);
1414739189a3Sdjm 			goto out;
1415739189a3Sdjm 		}
1416d75efeb7Sdjm 	} else if (!strcmp(name, "sig")) {
1417c4a807edSdjm 		if (fido_blob_decode(val, &attstmt->sig) < 0) {
141832a20e26Sdjm 			fido_log_debug("%s: sig", __func__);
1419d75efeb7Sdjm 			goto out;
1420d75efeb7Sdjm 		}
1421d75efeb7Sdjm 	} else if (!strcmp(name, "x5c")) {
1422d75efeb7Sdjm 		if (cbor_isa_array(val) == false ||
1423d75efeb7Sdjm 		    cbor_array_is_definite(val) == false ||
1424d75efeb7Sdjm 		    cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
142532a20e26Sdjm 			fido_log_debug("%s: x5c", __func__);
1426d75efeb7Sdjm 			goto out;
1427d75efeb7Sdjm 		}
1428*ab19a69eSdjm 	} else if (!strcmp(name, "certInfo")) {
1429*ab19a69eSdjm 		if (fido_blob_decode(val, &attstmt->certinfo) < 0) {
1430*ab19a69eSdjm 			fido_log_debug("%s: certinfo", __func__);
1431*ab19a69eSdjm 			goto out;
1432*ab19a69eSdjm 		}
1433*ab19a69eSdjm 	} else if (!strcmp(name, "pubArea")) {
1434*ab19a69eSdjm 		if (fido_blob_decode(val, &attstmt->pubarea) < 0) {
1435*ab19a69eSdjm 			fido_log_debug("%s: pubarea", __func__);
1436*ab19a69eSdjm 			goto out;
1437*ab19a69eSdjm 		}
1438d75efeb7Sdjm 	}
1439d75efeb7Sdjm 
1440d75efeb7Sdjm 	ok = 0;
1441d75efeb7Sdjm out:
1442d75efeb7Sdjm 	free(name);
1443d75efeb7Sdjm 
1444d75efeb7Sdjm 	return (ok);
1445d75efeb7Sdjm }
1446d75efeb7Sdjm 
1447d75efeb7Sdjm int
cbor_decode_attstmt(const cbor_item_t * item,fido_attstmt_t * attstmt)144832a20e26Sdjm cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1449d75efeb7Sdjm {
1450*ab19a69eSdjm 	size_t alloc_len;
1451*ab19a69eSdjm 
1452d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1453d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1454d75efeb7Sdjm 	    cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
145532a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1456d75efeb7Sdjm 		return (-1);
1457d75efeb7Sdjm 	}
1458d75efeb7Sdjm 
1459*ab19a69eSdjm 	if (attstmt->cbor.ptr != NULL ||
1460*ab19a69eSdjm 	    (attstmt->cbor.len = cbor_serialize_alloc(item,
1461*ab19a69eSdjm 	    &attstmt->cbor.ptr, &alloc_len)) == 0) {
1462*ab19a69eSdjm 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1463*ab19a69eSdjm 		return (-1);
1464*ab19a69eSdjm 	}
1465*ab19a69eSdjm 
1466d75efeb7Sdjm 	return (0);
1467d75efeb7Sdjm }
1468d75efeb7Sdjm 
1469d75efeb7Sdjm int
cbor_decode_uint64(const cbor_item_t * item,uint64_t * n)147032a20e26Sdjm cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1471d75efeb7Sdjm {
1472d75efeb7Sdjm 	if (cbor_isa_uint(item) == false) {
147332a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1474d75efeb7Sdjm 		return (-1);
1475d75efeb7Sdjm 	}
1476d75efeb7Sdjm 
1477d75efeb7Sdjm 	*n = cbor_get_int(item);
1478d75efeb7Sdjm 
1479d75efeb7Sdjm 	return (0);
1480d75efeb7Sdjm }
1481d75efeb7Sdjm 
1482d75efeb7Sdjm static int
decode_cred_id_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1483d75efeb7Sdjm decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1484d75efeb7Sdjm {
1485d75efeb7Sdjm 	fido_blob_t	*id = arg;
1486d75efeb7Sdjm 	char		*name = NULL;
1487d75efeb7Sdjm 	int		 ok = -1;
1488d75efeb7Sdjm 
1489d75efeb7Sdjm 	if (cbor_string_copy(key, &name) < 0) {
149032a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1491d75efeb7Sdjm 		ok = 0; /* ignore */
1492d75efeb7Sdjm 		goto out;
1493d75efeb7Sdjm 	}
1494d75efeb7Sdjm 
1495d75efeb7Sdjm 	if (!strcmp(name, "id"))
1496c4a807edSdjm 		if (fido_blob_decode(val, id) < 0) {
149732a20e26Sdjm 			fido_log_debug("%s: cbor_bytestring_copy", __func__);
1498d75efeb7Sdjm 			goto out;
1499d75efeb7Sdjm 		}
1500d75efeb7Sdjm 
1501d75efeb7Sdjm 	ok = 0;
1502d75efeb7Sdjm out:
1503d75efeb7Sdjm 	free(name);
1504d75efeb7Sdjm 
1505d75efeb7Sdjm 	return (ok);
1506d75efeb7Sdjm }
1507d75efeb7Sdjm 
1508d75efeb7Sdjm int
cbor_decode_cred_id(const cbor_item_t * item,fido_blob_t * id)150932a20e26Sdjm cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1510d75efeb7Sdjm {
1511d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1512d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1513d75efeb7Sdjm 	    cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
151432a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1515d75efeb7Sdjm 		return (-1);
1516d75efeb7Sdjm 	}
1517d75efeb7Sdjm 
1518d75efeb7Sdjm 	return (0);
1519d75efeb7Sdjm }
1520d75efeb7Sdjm 
1521d75efeb7Sdjm static int
decode_user_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1522d75efeb7Sdjm decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1523d75efeb7Sdjm {
1524d75efeb7Sdjm 	fido_user_t	*user = arg;
1525d75efeb7Sdjm 	char		*name = NULL;
1526d75efeb7Sdjm 	int		 ok = -1;
1527d75efeb7Sdjm 
1528d75efeb7Sdjm 	if (cbor_string_copy(key, &name) < 0) {
152932a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1530d75efeb7Sdjm 		ok = 0; /* ignore */
1531d75efeb7Sdjm 		goto out;
1532d75efeb7Sdjm 	}
1533d75efeb7Sdjm 
1534d75efeb7Sdjm 	if (!strcmp(name, "icon")) {
1535d75efeb7Sdjm 		if (cbor_string_copy(val, &user->icon) < 0) {
153632a20e26Sdjm 			fido_log_debug("%s: icon", __func__);
1537d75efeb7Sdjm 			goto out;
1538d75efeb7Sdjm 		}
1539d75efeb7Sdjm 	} else if (!strcmp(name, "name")) {
1540d75efeb7Sdjm 		if (cbor_string_copy(val, &user->name) < 0) {
154132a20e26Sdjm 			fido_log_debug("%s: name", __func__);
1542d75efeb7Sdjm 			goto out;
1543d75efeb7Sdjm 		}
1544d75efeb7Sdjm 	} else if (!strcmp(name, "displayName")) {
1545d75efeb7Sdjm 		if (cbor_string_copy(val, &user->display_name) < 0) {
154632a20e26Sdjm 			fido_log_debug("%s: display_name", __func__);
1547d75efeb7Sdjm 			goto out;
1548d75efeb7Sdjm 		}
1549d75efeb7Sdjm 	} else if (!strcmp(name, "id")) {
1550c4a807edSdjm 		if (fido_blob_decode(val, &user->id) < 0) {
155132a20e26Sdjm 			fido_log_debug("%s: id", __func__);
1552d75efeb7Sdjm 			goto out;
1553d75efeb7Sdjm 		}
1554d75efeb7Sdjm 	}
1555d75efeb7Sdjm 
1556d75efeb7Sdjm 	ok = 0;
1557d75efeb7Sdjm out:
1558d75efeb7Sdjm 	free(name);
1559d75efeb7Sdjm 
1560d75efeb7Sdjm 	return (ok);
1561d75efeb7Sdjm }
1562d75efeb7Sdjm 
1563d75efeb7Sdjm int
cbor_decode_user(const cbor_item_t * item,fido_user_t * user)156432a20e26Sdjm cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1565d75efeb7Sdjm {
1566d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1567d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1568d75efeb7Sdjm 	    cbor_map_iter(item, user, decode_user_entry) < 0) {
156932a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1570d75efeb7Sdjm 		return (-1);
1571d75efeb7Sdjm 	}
1572d75efeb7Sdjm 
1573d75efeb7Sdjm 	return (0);
1574d75efeb7Sdjm }
1575d75efeb7Sdjm 
1576d75efeb7Sdjm static int
decode_rp_entity_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1577d75efeb7Sdjm decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1578d75efeb7Sdjm     void *arg)
1579d75efeb7Sdjm {
1580d75efeb7Sdjm 	fido_rp_t	*rp = arg;
1581d75efeb7Sdjm 	char		*name = NULL;
1582d75efeb7Sdjm 	int		 ok = -1;
1583d75efeb7Sdjm 
1584d75efeb7Sdjm 	if (cbor_string_copy(key, &name) < 0) {
158532a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1586d75efeb7Sdjm 		ok = 0; /* ignore */
1587d75efeb7Sdjm 		goto out;
1588d75efeb7Sdjm 	}
1589d75efeb7Sdjm 
1590d75efeb7Sdjm 	if (!strcmp(name, "id")) {
1591d75efeb7Sdjm 		if (cbor_string_copy(val, &rp->id) < 0) {
159232a20e26Sdjm 			fido_log_debug("%s: id", __func__);
1593d75efeb7Sdjm 			goto out;
1594d75efeb7Sdjm 		}
1595d75efeb7Sdjm 	} else if (!strcmp(name, "name")) {
1596d75efeb7Sdjm 		if (cbor_string_copy(val, &rp->name) < 0) {
159732a20e26Sdjm 			fido_log_debug("%s: name", __func__);
1598d75efeb7Sdjm 			goto out;
1599d75efeb7Sdjm 		}
1600d75efeb7Sdjm 	}
1601d75efeb7Sdjm 
1602d75efeb7Sdjm 	ok = 0;
1603d75efeb7Sdjm out:
1604d75efeb7Sdjm 	free(name);
1605d75efeb7Sdjm 
1606d75efeb7Sdjm 	return (ok);
1607d75efeb7Sdjm }
1608d75efeb7Sdjm 
1609d75efeb7Sdjm int
cbor_decode_rp_entity(const cbor_item_t * item,fido_rp_t * rp)161032a20e26Sdjm cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1611d75efeb7Sdjm {
1612d75efeb7Sdjm 	if (cbor_isa_map(item) == false ||
1613d75efeb7Sdjm 	    cbor_map_is_definite(item) == false ||
1614d75efeb7Sdjm 	    cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
161532a20e26Sdjm 		fido_log_debug("%s: cbor type", __func__);
1616d75efeb7Sdjm 		return (-1);
1617d75efeb7Sdjm 	}
1618d75efeb7Sdjm 
1619d75efeb7Sdjm 	return (0);
1620d75efeb7Sdjm }
1621c4a807edSdjm 
1622c4a807edSdjm cbor_item_t *
cbor_build_uint(const uint64_t value)1623c4a807edSdjm cbor_build_uint(const uint64_t value)
1624c4a807edSdjm {
1625c4a807edSdjm 	if (value <= UINT8_MAX)
1626c4a807edSdjm 		return cbor_build_uint8((uint8_t)value);
1627c4a807edSdjm 	else if (value <= UINT16_MAX)
1628c4a807edSdjm 		return cbor_build_uint16((uint16_t)value);
1629c4a807edSdjm 	else if (value <= UINT32_MAX)
1630c4a807edSdjm 		return cbor_build_uint32((uint32_t)value);
1631c4a807edSdjm 
1632c4a807edSdjm 	return cbor_build_uint64(value);
1633c4a807edSdjm }
1634c4a807edSdjm 
1635c4a807edSdjm int
cbor_array_append(cbor_item_t ** array,cbor_item_t * item)1636c4a807edSdjm cbor_array_append(cbor_item_t **array, cbor_item_t *item)
1637c4a807edSdjm {
1638c4a807edSdjm 	cbor_item_t **v, *ret;
1639c4a807edSdjm 	size_t n;
1640c4a807edSdjm 
1641c4a807edSdjm 	if ((v = cbor_array_handle(*array)) == NULL ||
1642c4a807edSdjm 	    (n = cbor_array_size(*array)) == SIZE_MAX ||
1643c4a807edSdjm 	    (ret = cbor_new_definite_array(n + 1)) == NULL)
1644c4a807edSdjm 		return -1;
1645c4a807edSdjm 	for (size_t i = 0; i < n; i++) {
1646c4a807edSdjm 		if (cbor_array_push(ret, v[i]) == 0) {
1647c4a807edSdjm 			cbor_decref(&ret);
1648c4a807edSdjm 			return -1;
1649c4a807edSdjm 		}
1650c4a807edSdjm 	}
1651c4a807edSdjm 	if (cbor_array_push(ret, item) == 0) {
1652c4a807edSdjm 		cbor_decref(&ret);
1653c4a807edSdjm 		return -1;
1654c4a807edSdjm 	}
1655c4a807edSdjm 	cbor_decref(array);
1656c4a807edSdjm 	*array = ret;
1657c4a807edSdjm 
1658c4a807edSdjm 	return 0;
1659c4a807edSdjm }
1660c4a807edSdjm 
1661c4a807edSdjm int
cbor_array_drop(cbor_item_t ** array,size_t idx)1662c4a807edSdjm cbor_array_drop(cbor_item_t **array, size_t idx)
1663c4a807edSdjm {
1664c4a807edSdjm 	cbor_item_t **v, *ret;
1665c4a807edSdjm 	size_t n;
1666c4a807edSdjm 
1667c4a807edSdjm 	if ((v = cbor_array_handle(*array)) == NULL ||
1668c4a807edSdjm 	    (n = cbor_array_size(*array)) == 0 || idx >= n ||
1669c4a807edSdjm 	    (ret = cbor_new_definite_array(n - 1)) == NULL)
1670c4a807edSdjm 		return -1;
1671c4a807edSdjm 	for (size_t i = 0; i < n; i++) {
1672c4a807edSdjm 		if (i != idx && cbor_array_push(ret, v[i]) == 0) {
1673c4a807edSdjm 			cbor_decref(&ret);
1674c4a807edSdjm 			return -1;
1675c4a807edSdjm 		}
1676c4a807edSdjm 	}
1677c4a807edSdjm 	cbor_decref(array);
1678c4a807edSdjm 	*array = ret;
1679c4a807edSdjm 
1680c4a807edSdjm 	return 0;
1681c4a807edSdjm }
1682