xref: /netbsd/external/bsd/libfido2/dist/src/cbor.c (revision b93b3e8c)
1ca37791eSchristos /*
2ca37791eSchristos  * Copyright (c) 2018 Yubico AB. All rights reserved.
3ca37791eSchristos  * Use of this source code is governed by a BSD-style
4ca37791eSchristos  * license that can be found in the LICENSE file.
5ca37791eSchristos  */
6ca37791eSchristos 
7ca37791eSchristos #include <openssl/hmac.h>
8ca37791eSchristos #include <openssl/sha.h>
9ca37791eSchristos #include "fido.h"
10ca37791eSchristos 
11ca37791eSchristos static int
check_key_type(cbor_item_t * item)12ca37791eSchristos check_key_type(cbor_item_t *item)
13ca37791eSchristos {
14ca37791eSchristos 	if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
15ca37791eSchristos 	    item->type == CBOR_TYPE_STRING)
16ca37791eSchristos 		return (0);
17ca37791eSchristos 
18ca37791eSchristos 	fido_log_debug("%s: invalid type: %d", __func__, item->type);
19ca37791eSchristos 
20ca37791eSchristos 	return (-1);
21ca37791eSchristos }
22ca37791eSchristos 
23ca37791eSchristos /*
24ca37791eSchristos  * Validate CTAP2 canonical CBOR encoding rules for maps.
25ca37791eSchristos  */
26ca37791eSchristos static int
ctap_check_cbor(cbor_item_t * prev,cbor_item_t * curr)27ca37791eSchristos ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
28ca37791eSchristos {
29ca37791eSchristos 	size_t	curr_len;
30ca37791eSchristos 	size_t	prev_len;
31ca37791eSchristos 
32ca37791eSchristos 	if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
33ca37791eSchristos 		return (-1);
34ca37791eSchristos 
35ca37791eSchristos 	if (prev->type != curr->type) {
36ca37791eSchristos 		if (prev->type < curr->type)
37ca37791eSchristos 			return (0);
38ca37791eSchristos 		fido_log_debug("%s: unsorted types", __func__);
39ca37791eSchristos 		return (-1);
40ca37791eSchristos 	}
41ca37791eSchristos 
42ca37791eSchristos 	if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
43ca37791eSchristos 		if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
44ca37791eSchristos 		    cbor_get_int(curr) > cbor_get_int(prev))
45ca37791eSchristos 			return (0);
46ca37791eSchristos 	} else {
47ca37791eSchristos 		curr_len = cbor_string_length(curr);
48ca37791eSchristos 		prev_len = cbor_string_length(prev);
49ca37791eSchristos 
50ca37791eSchristos 		if (curr_len > prev_len || (curr_len == prev_len &&
51ca37791eSchristos 		    memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
52ca37791eSchristos 		    curr_len) < 0))
53ca37791eSchristos 			return (0);
54ca37791eSchristos 	}
55ca37791eSchristos 
56ca37791eSchristos 	fido_log_debug("%s: invalid cbor", __func__);
57ca37791eSchristos 
58ca37791eSchristos 	return (-1);
59ca37791eSchristos }
60ca37791eSchristos 
61ca37791eSchristos int
cbor_map_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,const cbor_item_t *,void *))62ca37791eSchristos cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
63ca37791eSchristos     const cbor_item_t *, void *))
64ca37791eSchristos {
65ca37791eSchristos 	struct cbor_pair	*v;
66ca37791eSchristos 	size_t			 n;
67ca37791eSchristos 
68ca37791eSchristos 	if ((v = cbor_map_handle(item)) == NULL) {
69ca37791eSchristos 		fido_log_debug("%s: cbor_map_handle", __func__);
70ca37791eSchristos 		return (-1);
71ca37791eSchristos 	}
72ca37791eSchristos 
73ca37791eSchristos 	n = cbor_map_size(item);
74ca37791eSchristos 
75ca37791eSchristos 	for (size_t i = 0; i < n; i++) {
76ca37791eSchristos 		if (v[i].key == NULL || v[i].value == NULL) {
77ca37791eSchristos 			fido_log_debug("%s: key=%p, value=%p for i=%zu",
78ca37791eSchristos 			    __func__, (void *)v[i].key, (void *)v[i].value, i);
79ca37791eSchristos 			return (-1);
80ca37791eSchristos 		}
81ca37791eSchristos 		if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
82ca37791eSchristos 			fido_log_debug("%s: ctap_check_cbor", __func__);
83ca37791eSchristos 			return (-1);
84ca37791eSchristos 		}
85ca37791eSchristos 		if (f(v[i].key, v[i].value, arg) < 0) {
86ca37791eSchristos 			fido_log_debug("%s: iterator < 0 on i=%zu", __func__,
87ca37791eSchristos 			    i);
88ca37791eSchristos 			return (-1);
89ca37791eSchristos 		}
90ca37791eSchristos 	}
91ca37791eSchristos 
92ca37791eSchristos 	return (0);
93ca37791eSchristos }
94ca37791eSchristos 
95ca37791eSchristos int
cbor_array_iter(const cbor_item_t * item,void * arg,int (* f)(const cbor_item_t *,void *))96ca37791eSchristos cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
97ca37791eSchristos     void *))
98ca37791eSchristos {
99ca37791eSchristos 	cbor_item_t	**v;
100ca37791eSchristos 	size_t		  n;
101ca37791eSchristos 
102ca37791eSchristos 	if ((v = cbor_array_handle(item)) == NULL) {
103ca37791eSchristos 		fido_log_debug("%s: cbor_array_handle", __func__);
104ca37791eSchristos 		return (-1);
105ca37791eSchristos 	}
106ca37791eSchristos 
107ca37791eSchristos 	n = cbor_array_size(item);
108ca37791eSchristos 
109ca37791eSchristos 	for (size_t i = 0; i < n; i++)
110ca37791eSchristos 		if (v[i] == NULL || f(v[i], arg) < 0) {
111ca37791eSchristos 			fido_log_debug("%s: iterator < 0 on i=%zu,%p",
112ca37791eSchristos 			    __func__, i, (void *)v[i]);
113ca37791eSchristos 			return (-1);
114ca37791eSchristos 		}
115ca37791eSchristos 
116ca37791eSchristos 	return (0);
117ca37791eSchristos }
118ca37791eSchristos 
119ca37791eSchristos 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 *))120ca37791eSchristos cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg,
121ca37791eSchristos     int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
122ca37791eSchristos {
123ca37791eSchristos 	cbor_item_t		*item = NULL;
124ca37791eSchristos 	struct cbor_load_result	 cbor;
125ca37791eSchristos 	int			 r;
126ca37791eSchristos 
127ca37791eSchristos 	if (blob_len < 1) {
128ca37791eSchristos 		fido_log_debug("%s: blob_len=%zu", __func__, blob_len);
129ca37791eSchristos 		r = FIDO_ERR_RX;
130ca37791eSchristos 		goto fail;
131ca37791eSchristos 	}
132ca37791eSchristos 
133ca37791eSchristos 	if (blob[0] != FIDO_OK) {
134ca37791eSchristos 		fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
135ca37791eSchristos 		r = blob[0];
136ca37791eSchristos 		goto fail;
137ca37791eSchristos 	}
138ca37791eSchristos 
139ca37791eSchristos 	if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
140ca37791eSchristos 		fido_log_debug("%s: cbor_load", __func__);
141ca37791eSchristos 		r = FIDO_ERR_RX_NOT_CBOR;
142ca37791eSchristos 		goto fail;
143ca37791eSchristos 	}
144ca37791eSchristos 
145ca37791eSchristos 	if (cbor_isa_map(item) == false ||
146ca37791eSchristos 	    cbor_map_is_definite(item) == false) {
147ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
148ca37791eSchristos 		r = FIDO_ERR_RX_INVALID_CBOR;
149ca37791eSchristos 		goto fail;
150ca37791eSchristos 	}
151ca37791eSchristos 
152ca37791eSchristos 	if (cbor_map_iter(item, arg, parser) < 0) {
153ca37791eSchristos 		fido_log_debug("%s: cbor_map_iter", __func__);
154ca37791eSchristos 		r = FIDO_ERR_RX_INVALID_CBOR;
155ca37791eSchristos 		goto fail;
156ca37791eSchristos 	}
157ca37791eSchristos 
158ca37791eSchristos 	r = FIDO_OK;
159ca37791eSchristos fail:
160ca37791eSchristos 	if (item != NULL)
161ca37791eSchristos 		cbor_decref(&item);
162ca37791eSchristos 
163ca37791eSchristos 	return (r);
164ca37791eSchristos }
165ca37791eSchristos 
166ca37791eSchristos void
cbor_vector_free(cbor_item_t ** item,size_t len)167ca37791eSchristos cbor_vector_free(cbor_item_t **item, size_t len)
168ca37791eSchristos {
169ca37791eSchristos 	for (size_t i = 0; i < len; i++)
170ca37791eSchristos 		if (item[i] != NULL)
171ca37791eSchristos 			cbor_decref(&item[i]);
172ca37791eSchristos }
173ca37791eSchristos 
174ca37791eSchristos int
cbor_bytestring_copy(const cbor_item_t * item,unsigned char ** buf,size_t * len)175ca37791eSchristos cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
176ca37791eSchristos {
177ca37791eSchristos 	if (*buf != NULL || *len != 0) {
178ca37791eSchristos 		fido_log_debug("%s: dup", __func__);
179ca37791eSchristos 		return (-1);
180ca37791eSchristos 	}
181ca37791eSchristos 
182ca37791eSchristos 	if (cbor_isa_bytestring(item) == false ||
183ca37791eSchristos 	    cbor_bytestring_is_definite(item) == false) {
184ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
185ca37791eSchristos 		return (-1);
186ca37791eSchristos 	}
187ca37791eSchristos 
188ca37791eSchristos 	*len = cbor_bytestring_length(item);
189ca37791eSchristos 	if ((*buf = malloc(*len)) == NULL) {
190ca37791eSchristos 		*len = 0;
191ca37791eSchristos 		return (-1);
192ca37791eSchristos 	}
193ca37791eSchristos 
194ca37791eSchristos 	memcpy(*buf, cbor_bytestring_handle(item), *len);
195ca37791eSchristos 
196ca37791eSchristos 	return (0);
197ca37791eSchristos }
198ca37791eSchristos 
199ca37791eSchristos int
cbor_string_copy(const cbor_item_t * item,char ** str)200ca37791eSchristos cbor_string_copy(const cbor_item_t *item, char **str)
201ca37791eSchristos {
202ca37791eSchristos 	size_t len;
203ca37791eSchristos 
204ca37791eSchristos 	if (*str != NULL) {
205ca37791eSchristos 		fido_log_debug("%s: dup", __func__);
206ca37791eSchristos 		return (-1);
207ca37791eSchristos 	}
208ca37791eSchristos 
209ca37791eSchristos 	if (cbor_isa_string(item) == false ||
210ca37791eSchristos 	    cbor_string_is_definite(item) == false) {
211ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
212ca37791eSchristos 		return (-1);
213ca37791eSchristos 	}
214ca37791eSchristos 
215ca37791eSchristos 	if ((len = cbor_string_length(item)) == SIZE_MAX ||
216ca37791eSchristos 	    (*str = malloc(len + 1)) == NULL)
217ca37791eSchristos 		return (-1);
218ca37791eSchristos 
219ca37791eSchristos 	memcpy(*str, cbor_string_handle(item), len);
220ca37791eSchristos 	(*str)[len] = '\0';
221ca37791eSchristos 
222ca37791eSchristos 	return (0);
223ca37791eSchristos }
224ca37791eSchristos 
225ca37791eSchristos int
cbor_add_bytestring(cbor_item_t * item,const char * key,const unsigned char * value,size_t value_len)226ca37791eSchristos cbor_add_bytestring(cbor_item_t *item, const char *key,
227ca37791eSchristos     const unsigned char *value, size_t value_len)
228ca37791eSchristos {
229ca37791eSchristos 	struct cbor_pair pair;
230ca37791eSchristos 	int ok = -1;
231ca37791eSchristos 
232ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
233ca37791eSchristos 
234ca37791eSchristos 	if ((pair.key = cbor_build_string(key)) == NULL ||
235ca37791eSchristos 	    (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
236ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
237ca37791eSchristos 		goto fail;
238ca37791eSchristos 	}
239ca37791eSchristos 
240ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
241ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
242ca37791eSchristos 		goto fail;
243ca37791eSchristos 	}
244ca37791eSchristos 
245ca37791eSchristos 	ok = 0;
246ca37791eSchristos fail:
247ca37791eSchristos 	if (pair.key)
248ca37791eSchristos 		cbor_decref(&pair.key);
249ca37791eSchristos 	if (pair.value)
250ca37791eSchristos 		cbor_decref(&pair.value);
251ca37791eSchristos 
252ca37791eSchristos 	return (ok);
253ca37791eSchristos }
254ca37791eSchristos 
255ca37791eSchristos int
cbor_add_string(cbor_item_t * item,const char * key,const char * value)256ca37791eSchristos cbor_add_string(cbor_item_t *item, const char *key, const char *value)
257ca37791eSchristos {
258ca37791eSchristos 	struct cbor_pair pair;
259ca37791eSchristos 	int ok = -1;
260ca37791eSchristos 
261ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
262ca37791eSchristos 
263ca37791eSchristos 	if ((pair.key = cbor_build_string(key)) == NULL ||
264ca37791eSchristos 	    (pair.value = cbor_build_string(value)) == NULL) {
265ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
266ca37791eSchristos 		goto fail;
267ca37791eSchristos 	}
268ca37791eSchristos 
269ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
270ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
271ca37791eSchristos 		goto fail;
272ca37791eSchristos 	}
273ca37791eSchristos 
274ca37791eSchristos 	ok = 0;
275ca37791eSchristos fail:
276ca37791eSchristos 	if (pair.key)
277ca37791eSchristos 		cbor_decref(&pair.key);
278ca37791eSchristos 	if (pair.value)
279ca37791eSchristos 		cbor_decref(&pair.value);
280ca37791eSchristos 
281ca37791eSchristos 	return (ok);
282ca37791eSchristos }
283ca37791eSchristos 
284ca37791eSchristos int
cbor_add_bool(cbor_item_t * item,const char * key,fido_opt_t value)285ca37791eSchristos cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
286ca37791eSchristos {
287ca37791eSchristos 	struct cbor_pair pair;
288ca37791eSchristos 	int ok = -1;
289ca37791eSchristos 
290ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
291ca37791eSchristos 
292ca37791eSchristos 	if ((pair.key = cbor_build_string(key)) == NULL ||
293ca37791eSchristos 	    (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
294ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
295ca37791eSchristos 		goto fail;
296ca37791eSchristos 	}
297ca37791eSchristos 
298ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
299ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
300ca37791eSchristos 		goto fail;
301ca37791eSchristos 	}
302ca37791eSchristos 
303ca37791eSchristos 	ok = 0;
304ca37791eSchristos fail:
305ca37791eSchristos 	if (pair.key)
306ca37791eSchristos 		cbor_decref(&pair.key);
307ca37791eSchristos 	if (pair.value)
308ca37791eSchristos 		cbor_decref(&pair.value);
309ca37791eSchristos 
310ca37791eSchristos 	return (ok);
311ca37791eSchristos }
312ca37791eSchristos 
313ca37791eSchristos static int
cbor_add_uint8(cbor_item_t * item,const char * key,uint8_t value)314ca37791eSchristos cbor_add_uint8(cbor_item_t *item, const char *key, uint8_t value)
315ca37791eSchristos {
316ca37791eSchristos 	struct cbor_pair pair;
317ca37791eSchristos 	int ok = -1;
318ca37791eSchristos 
319ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
320ca37791eSchristos 
321ca37791eSchristos 	if ((pair.key = cbor_build_string(key)) == NULL ||
322ca37791eSchristos 	    (pair.value = cbor_build_uint8(value)) == NULL) {
323ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
324ca37791eSchristos 		goto fail;
325ca37791eSchristos 	}
326ca37791eSchristos 
327ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
328ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
329ca37791eSchristos 		goto fail;
330ca37791eSchristos 	}
331ca37791eSchristos 
332ca37791eSchristos 	ok = 0;
333ca37791eSchristos fail:
334ca37791eSchristos 	if (pair.key)
335ca37791eSchristos 		cbor_decref(&pair.key);
336ca37791eSchristos 	if (pair.value)
337ca37791eSchristos 		cbor_decref(&pair.value);
338ca37791eSchristos 
339ca37791eSchristos 	return (ok);
340ca37791eSchristos }
341ca37791eSchristos 
342ca37791eSchristos static int
cbor_add_arg(cbor_item_t * item,uint8_t n,cbor_item_t * arg)343ca37791eSchristos cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
344ca37791eSchristos {
345ca37791eSchristos 	struct cbor_pair pair;
346ca37791eSchristos 	int ok = -1;
347ca37791eSchristos 
348ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
349ca37791eSchristos 
350ca37791eSchristos 	if (arg == NULL)
351ca37791eSchristos 		return (0); /* empty argument */
352ca37791eSchristos 
353ca37791eSchristos 	if ((pair.key = cbor_build_uint8(n)) == NULL) {
354ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
355ca37791eSchristos 		goto fail;
356ca37791eSchristos 	}
357ca37791eSchristos 
358ca37791eSchristos 	pair.value = arg;
359ca37791eSchristos 
360ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
361ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
362ca37791eSchristos 		goto fail;
363ca37791eSchristos 	}
364ca37791eSchristos 
365ca37791eSchristos 	ok = 0;
366ca37791eSchristos fail:
367ca37791eSchristos 	if (pair.key)
368ca37791eSchristos 		cbor_decref(&pair.key);
369ca37791eSchristos 
370ca37791eSchristos 	return (ok);
371ca37791eSchristos }
372ca37791eSchristos 
373ca37791eSchristos cbor_item_t *
cbor_flatten_vector(cbor_item_t * argv[],size_t argc)374ca37791eSchristos cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
375ca37791eSchristos {
376ca37791eSchristos 	cbor_item_t	*map;
377ca37791eSchristos 	uint8_t		 i;
378ca37791eSchristos 
379ca37791eSchristos 	if (argc > UINT8_MAX - 1)
380ca37791eSchristos 		return (NULL);
381ca37791eSchristos 
382ca37791eSchristos 	if ((map = cbor_new_definite_map(argc)) == NULL)
383ca37791eSchristos 		return (NULL);
384ca37791eSchristos 
385ca37791eSchristos 	for (i = 0; i < argc; i++)
3865d97138dSchristos 		if (cbor_add_arg(map, (uint8_t)(i + 1), argv[i]) < 0)
387ca37791eSchristos 			break;
388ca37791eSchristos 
389ca37791eSchristos 	if (i != argc) {
390ca37791eSchristos 		cbor_decref(&map);
391ca37791eSchristos 		map = NULL;
392ca37791eSchristos 	}
393ca37791eSchristos 
394ca37791eSchristos 	return (map);
395ca37791eSchristos }
396ca37791eSchristos 
397ca37791eSchristos int
cbor_build_frame(uint8_t cmd,cbor_item_t * argv[],size_t argc,fido_blob_t * f)398ca37791eSchristos cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
399ca37791eSchristos {
400ca37791eSchristos 	cbor_item_t	*flat = NULL;
401ca37791eSchristos 	unsigned char	*cbor = NULL;
402ca37791eSchristos 	size_t		 cbor_len;
403ca37791eSchristos 	size_t		 cbor_alloc_len;
404ca37791eSchristos 	int		 ok = -1;
405ca37791eSchristos 
406ca37791eSchristos 	if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
407ca37791eSchristos 		goto fail;
408ca37791eSchristos 
409ca37791eSchristos 	cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
410ca37791eSchristos 	if (cbor_len == 0 || cbor_len == SIZE_MAX) {
411ca37791eSchristos 		fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len);
412ca37791eSchristos 		goto fail;
413ca37791eSchristos 	}
414ca37791eSchristos 
415ca37791eSchristos 	if ((f->ptr = malloc(cbor_len + 1)) == NULL)
416ca37791eSchristos 		goto fail;
417ca37791eSchristos 
418ca37791eSchristos 	f->len = cbor_len + 1;
419ca37791eSchristos 	f->ptr[0] = cmd;
420ca37791eSchristos 	memcpy(f->ptr + 1, cbor, f->len - 1);
421ca37791eSchristos 
422ca37791eSchristos 	ok = 0;
423ca37791eSchristos fail:
424ca37791eSchristos 	if (flat != NULL)
425ca37791eSchristos 		cbor_decref(&flat);
426ca37791eSchristos 
427ca37791eSchristos 	free(cbor);
428ca37791eSchristos 
429ca37791eSchristos 	return (ok);
430ca37791eSchristos }
431ca37791eSchristos 
432ca37791eSchristos cbor_item_t *
cbor_encode_rp_entity(const fido_rp_t * rp)433ca37791eSchristos cbor_encode_rp_entity(const fido_rp_t *rp)
434ca37791eSchristos {
435ca37791eSchristos 	cbor_item_t *item = NULL;
436ca37791eSchristos 
437ca37791eSchristos 	if ((item = cbor_new_definite_map(2)) == NULL)
438ca37791eSchristos 		return (NULL);
439ca37791eSchristos 
440ca37791eSchristos 	if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
441ca37791eSchristos 	    (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
442ca37791eSchristos 		cbor_decref(&item);
443ca37791eSchristos 		return (NULL);
444ca37791eSchristos 	}
445ca37791eSchristos 
446ca37791eSchristos 	return (item);
447ca37791eSchristos }
448ca37791eSchristos 
449ca37791eSchristos cbor_item_t *
cbor_encode_user_entity(const fido_user_t * user)450ca37791eSchristos cbor_encode_user_entity(const fido_user_t *user)
451ca37791eSchristos {
452ca37791eSchristos 	cbor_item_t		*item = NULL;
453ca37791eSchristos 	const fido_blob_t	*id = &user->id;
454ca37791eSchristos 	const char		*display = user->display_name;
455ca37791eSchristos 
456ca37791eSchristos 	if ((item = cbor_new_definite_map(4)) == NULL)
457ca37791eSchristos 		return (NULL);
458ca37791eSchristos 
459ca37791eSchristos 	if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
460ca37791eSchristos 	    (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
461ca37791eSchristos 	    (user->name && cbor_add_string(item, "name", user->name) < 0) ||
462ca37791eSchristos 	    (display && cbor_add_string(item, "displayName", display) < 0)) {
463ca37791eSchristos 		cbor_decref(&item);
464ca37791eSchristos 		return (NULL);
465ca37791eSchristos 	}
466ca37791eSchristos 
467ca37791eSchristos 	return (item);
468ca37791eSchristos }
469ca37791eSchristos 
470ca37791eSchristos cbor_item_t *
cbor_encode_pubkey_param(int cose_alg)471ca37791eSchristos cbor_encode_pubkey_param(int cose_alg)
472ca37791eSchristos {
473ca37791eSchristos 	cbor_item_t		*item = NULL;
474ca37791eSchristos 	cbor_item_t		*body = NULL;
475ca37791eSchristos 	struct cbor_pair	 alg;
476ca37791eSchristos 	int			 ok = -1;
477ca37791eSchristos 
478ca37791eSchristos 	memset(&alg, 0, sizeof(alg));
479ca37791eSchristos 
480ca37791eSchristos 	if ((item = cbor_new_definite_array(1)) == NULL ||
481ca37791eSchristos 	    (body = cbor_new_definite_map(2)) == NULL ||
482ca37791eSchristos 	    cose_alg > -1 || cose_alg < INT16_MIN)
483ca37791eSchristos 		goto fail;
484ca37791eSchristos 
485ca37791eSchristos 	alg.key = cbor_build_string("alg");
486ca37791eSchristos 
487ca37791eSchristos 	if (-cose_alg - 1 > UINT8_MAX)
488ca37791eSchristos 		alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
489ca37791eSchristos 	else
490ca37791eSchristos 		alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
491ca37791eSchristos 
492ca37791eSchristos 	if (alg.key == NULL || alg.value == NULL) {
493ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
494ca37791eSchristos 		goto fail;
495ca37791eSchristos 	}
496ca37791eSchristos 
497ca37791eSchristos 	if (cbor_map_add(body, alg) == false ||
498ca37791eSchristos 	    cbor_add_string(body, "type", "public-key") < 0 ||
499ca37791eSchristos 	    cbor_array_push(item, body) == false)
500ca37791eSchristos 		goto fail;
501ca37791eSchristos 
502ca37791eSchristos 	ok  = 0;
503ca37791eSchristos fail:
504ca37791eSchristos 	if (ok < 0) {
505ca37791eSchristos 		if (item != NULL) {
506ca37791eSchristos 			cbor_decref(&item);
507ca37791eSchristos 			item = NULL;
508ca37791eSchristos 		}
509ca37791eSchristos 	}
510ca37791eSchristos 
511ca37791eSchristos 	if (body != NULL)
512ca37791eSchristos 		cbor_decref(&body);
513ca37791eSchristos 	if (alg.key != NULL)
514ca37791eSchristos 		cbor_decref(&alg.key);
515ca37791eSchristos 	if (alg.value != NULL)
516ca37791eSchristos 		cbor_decref(&alg.value);
517ca37791eSchristos 
518ca37791eSchristos 	return (item);
519ca37791eSchristos }
520ca37791eSchristos 
521ca37791eSchristos cbor_item_t *
cbor_encode_pubkey(const fido_blob_t * pubkey)522ca37791eSchristos cbor_encode_pubkey(const fido_blob_t *pubkey)
523ca37791eSchristos {
524ca37791eSchristos 	cbor_item_t *cbor_key = NULL;
525ca37791eSchristos 
526ca37791eSchristos 	if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
527ca37791eSchristos 	    cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
528ca37791eSchristos 	    cbor_add_string(cbor_key, "type", "public-key") < 0) {
529ca37791eSchristos 		if (cbor_key)
530ca37791eSchristos 			cbor_decref(&cbor_key);
531ca37791eSchristos 		return (NULL);
532ca37791eSchristos 	}
533ca37791eSchristos 
534ca37791eSchristos 	return (cbor_key);
535ca37791eSchristos }
536ca37791eSchristos 
537ca37791eSchristos cbor_item_t *
cbor_encode_pubkey_list(const fido_blob_array_t * list)538ca37791eSchristos cbor_encode_pubkey_list(const fido_blob_array_t *list)
539ca37791eSchristos {
540ca37791eSchristos 	cbor_item_t	*array = NULL;
541ca37791eSchristos 	cbor_item_t	*key = NULL;
542ca37791eSchristos 
543ca37791eSchristos 	if ((array = cbor_new_definite_array(list->len)) == NULL)
544ca37791eSchristos 		goto fail;
545ca37791eSchristos 
546ca37791eSchristos 	for (size_t i = 0; i < list->len; i++) {
547ca37791eSchristos 		if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL ||
548ca37791eSchristos 		    cbor_array_push(array, key) == false)
549ca37791eSchristos 			goto fail;
550ca37791eSchristos 		cbor_decref(&key);
551ca37791eSchristos 	}
552ca37791eSchristos 
553ca37791eSchristos 	return (array);
554ca37791eSchristos fail:
555ca37791eSchristos 	if (key != NULL)
556ca37791eSchristos 		cbor_decref(&key);
557ca37791eSchristos 	if (array != NULL)
558ca37791eSchristos 		cbor_decref(&array);
559ca37791eSchristos 
560ca37791eSchristos 	return (NULL);
561ca37791eSchristos }
562ca37791eSchristos 
56398a5e356Schristos static int
cbor_encode_largeblob_key_ext(cbor_item_t * map)56498a5e356Schristos cbor_encode_largeblob_key_ext(cbor_item_t *map)
56598a5e356Schristos {
56698a5e356Schristos 	if (map == NULL ||
56798a5e356Schristos 	    cbor_add_bool(map, "largeBlobKey", FIDO_OPT_TRUE) < 0)
56898a5e356Schristos 		return (-1);
56998a5e356Schristos 
57098a5e356Schristos 	return (0);
57198a5e356Schristos }
57298a5e356Schristos 
573ca37791eSchristos cbor_item_t *
cbor_encode_cred_ext(const fido_cred_ext_t * ext,const fido_blob_t * blob)57498a5e356Schristos cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
575ca37791eSchristos {
576ca37791eSchristos 	cbor_item_t *item = NULL;
577ca37791eSchristos 	size_t size = 0;
578ca37791eSchristos 
57998a5e356Schristos 	if (ext->mask & FIDO_EXT_CRED_BLOB)
58098a5e356Schristos 		size++;
581ca37791eSchristos 	if (ext->mask & FIDO_EXT_HMAC_SECRET)
582ca37791eSchristos 		size++;
583ca37791eSchristos 	if (ext->mask & FIDO_EXT_CRED_PROTECT)
584ca37791eSchristos 		size++;
58598a5e356Schristos 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
58698a5e356Schristos 		size++;
58798a5e356Schristos 
588ca37791eSchristos 	if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
589ca37791eSchristos 		return (NULL);
590ca37791eSchristos 
59198a5e356Schristos 	if (ext->mask & FIDO_EXT_CRED_BLOB) {
59298a5e356Schristos 		if (cbor_add_bytestring(item, "credBlob", blob->ptr,
59398a5e356Schristos 		    blob->len) < 0) {
594ca37791eSchristos 			cbor_decref(&item);
595ca37791eSchristos 			return (NULL);
596ca37791eSchristos 		}
597ca37791eSchristos 	}
598ca37791eSchristos 	if (ext->mask & FIDO_EXT_CRED_PROTECT) {
5995d97138dSchristos 		if (ext->prot < 0 || ext->prot > UINT8_MAX ||
6005d97138dSchristos 		    cbor_add_uint8(item, "credProtect",
6015d97138dSchristos 		    (uint8_t)ext->prot) < 0) {
602ca37791eSchristos 			cbor_decref(&item);
603ca37791eSchristos 			return (NULL);
604ca37791eSchristos 		}
605ca37791eSchristos 	}
60698a5e356Schristos 	if (ext->mask & FIDO_EXT_HMAC_SECRET) {
60798a5e356Schristos 		if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
60898a5e356Schristos 			cbor_decref(&item);
60998a5e356Schristos 			return (NULL);
61098a5e356Schristos 		}
61198a5e356Schristos 	}
61298a5e356Schristos 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
61398a5e356Schristos 		if (cbor_encode_largeblob_key_ext(item) < 0) {
61498a5e356Schristos 			cbor_decref(&item);
61598a5e356Schristos 			return (NULL);
61698a5e356Schristos 		}
61798a5e356Schristos 	}
618ca37791eSchristos 
619ca37791eSchristos 	return (item);
620ca37791eSchristos }
621ca37791eSchristos 
622ca37791eSchristos cbor_item_t *
cbor_encode_cred_opt(fido_opt_t rk,fido_opt_t uv)62398a5e356Schristos cbor_encode_cred_opt(fido_opt_t rk, fido_opt_t uv)
624ca37791eSchristos {
625ca37791eSchristos 	cbor_item_t *item = NULL;
626ca37791eSchristos 
627ca37791eSchristos 	if ((item = cbor_new_definite_map(2)) == NULL)
628ca37791eSchristos 		return (NULL);
629ca37791eSchristos 	if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
630ca37791eSchristos 	    (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
631ca37791eSchristos 		cbor_decref(&item);
632ca37791eSchristos 		return (NULL);
633ca37791eSchristos 	}
634ca37791eSchristos 
635ca37791eSchristos 	return (item);
636ca37791eSchristos }
637ca37791eSchristos 
638ca37791eSchristos cbor_item_t *
cbor_encode_assert_opt(fido_opt_t up,fido_opt_t uv)63998a5e356Schristos cbor_encode_assert_opt(fido_opt_t up, fido_opt_t uv)
640ca37791eSchristos {
641ca37791eSchristos 	cbor_item_t *item = NULL;
642ca37791eSchristos 
643ca37791eSchristos 	if ((item = cbor_new_definite_map(2)) == NULL)
644ca37791eSchristos 		return (NULL);
645ca37791eSchristos 	if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
646ca37791eSchristos 	    (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
647ca37791eSchristos 		cbor_decref(&item);
648ca37791eSchristos 		return (NULL);
649ca37791eSchristos 	}
650ca37791eSchristos 
651ca37791eSchristos 	return (item);
652ca37791eSchristos }
653ca37791eSchristos 
654ca37791eSchristos cbor_item_t *
cbor_encode_pin_auth(const fido_dev_t * dev,const fido_blob_t * secret,const fido_blob_t * data)65598a5e356Schristos cbor_encode_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
65698a5e356Schristos     const fido_blob_t *data)
657ca37791eSchristos {
658ca37791eSchristos 	const EVP_MD	*md = NULL;
659ca37791eSchristos 	unsigned char	 dgst[SHA256_DIGEST_LENGTH];
660ca37791eSchristos 	unsigned int	 dgst_len;
66198a5e356Schristos 	size_t		 outlen;
66298a5e356Schristos 	uint8_t		 prot;
66398a5e356Schristos 	fido_blob_t	 key;
664ca37791eSchristos 
66598a5e356Schristos 
66698a5e356Schristos 	key.ptr = secret->ptr;
66798a5e356Schristos 	key.len = secret->len;
66898a5e356Schristos 
66998a5e356Schristos 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
67098a5e356Schristos 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
67198a5e356Schristos 		return (NULL);
67298a5e356Schristos 	}
67398a5e356Schristos 
67498a5e356Schristos 	/* select hmac portion of the shared secret */
67598a5e356Schristos 	if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
67698a5e356Schristos 		key.len = 32;
67798a5e356Schristos 
67898a5e356Schristos 	if ((md = EVP_sha256()) == NULL || HMAC(md, key.ptr,
67998a5e356Schristos 	    (int)key.len, data->ptr, data->len, dgst,
680ca37791eSchristos 	    &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
681ca37791eSchristos 		return (NULL);
682ca37791eSchristos 
68398a5e356Schristos 	outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
68498a5e356Schristos 
68598a5e356Schristos 	return (cbor_build_bytestring(dgst, outlen));
686ca37791eSchristos }
687ca37791eSchristos 
688ca37791eSchristos cbor_item_t *
cbor_encode_pin_opt(const fido_dev_t * dev)68998a5e356Schristos cbor_encode_pin_opt(const fido_dev_t *dev)
690ca37791eSchristos {
69198a5e356Schristos 	uint8_t	    prot;
692ca37791eSchristos 
69398a5e356Schristos 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
69498a5e356Schristos 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
695ca37791eSchristos 		return (NULL);
696ca37791eSchristos 	}
697ca37791eSchristos 
69898a5e356Schristos 	return (cbor_build_uint8(prot));
699ca37791eSchristos }
700ca37791eSchristos 
701ca37791eSchristos 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)70298a5e356Schristos cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
70398a5e356Schristos     const fido_blob_t *new_pin_enc, const fido_blob_t *pin_hash_enc)
704ca37791eSchristos {
705ca37791eSchristos 	unsigned char	 dgst[SHA256_DIGEST_LENGTH];
706ca37791eSchristos 	unsigned int	 dgst_len;
707ca37791eSchristos 	cbor_item_t	*item = NULL;
708ca37791eSchristos 	const EVP_MD	*md = NULL;
709ca37791eSchristos #if OPENSSL_VERSION_NUMBER < 0x10100000L
710ca37791eSchristos 	HMAC_CTX	 ctx;
711ca37791eSchristos #else
712ca37791eSchristos 	HMAC_CTX	*ctx = NULL;
713ca37791eSchristos #endif
71498a5e356Schristos 	fido_blob_t	 key;
71598a5e356Schristos 	uint8_t		 prot;
71698a5e356Schristos 	size_t		 outlen;
717ca37791eSchristos 
71898a5e356Schristos 	key.ptr = secret->ptr;
71998a5e356Schristos 	key.len = secret->len;
720ca37791eSchristos 
72198a5e356Schristos 	if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
72298a5e356Schristos 		fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
723ca37791eSchristos 		goto fail;
724ca37791eSchristos 	}
725ca37791eSchristos 
72698a5e356Schristos 	if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
72798a5e356Schristos 		key.len = 32;
728ca37791eSchristos 
729ca37791eSchristos #if OPENSSL_VERSION_NUMBER < 0x10100000L
730ca37791eSchristos 	HMAC_CTX_init(&ctx);
731ca37791eSchristos 
732ca37791eSchristos 	if ((md = EVP_sha256()) == NULL ||
73398a5e356Schristos 	    HMAC_Init_ex(&ctx, key.ptr, (int)key.len, md, NULL) == 0 ||
73498a5e356Schristos 	    HMAC_Update(&ctx, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
73598a5e356Schristos 	    HMAC_Update(&ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
73698a5e356Schristos 	    HMAC_Final(&ctx, dgst, &dgst_len) == 0 ||
73798a5e356Schristos 	    dgst_len != SHA256_DIGEST_LENGTH) {
738ca37791eSchristos 		fido_log_debug("%s: HMAC", __func__);
739ca37791eSchristos 		goto fail;
740ca37791eSchristos 	}
741ca37791eSchristos #else
742ca37791eSchristos 	if ((ctx = HMAC_CTX_new()) == NULL ||
743ca37791eSchristos 	    (md = EVP_sha256())  == NULL ||
74498a5e356Schristos 	    HMAC_Init_ex(ctx, key.ptr, (int)key.len, md, NULL) == 0 ||
74598a5e356Schristos 	    HMAC_Update(ctx, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
74698a5e356Schristos 	    HMAC_Update(ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
74798a5e356Schristos 	    HMAC_Final(ctx, dgst, &dgst_len) == 0 ||
74898a5e356Schristos 	    dgst_len != SHA256_DIGEST_LENGTH) {
749ca37791eSchristos 		fido_log_debug("%s: HMAC", __func__);
750ca37791eSchristos 		goto fail;
751ca37791eSchristos 	}
752ca37791eSchristos #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
753ca37791eSchristos 
75498a5e356Schristos 	outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
75598a5e356Schristos 
75698a5e356Schristos 	if ((item = cbor_build_bytestring(dgst, outlen)) == NULL) {
757ca37791eSchristos 		fido_log_debug("%s: cbor_build_bytestring", __func__);
758ca37791eSchristos 		goto fail;
759ca37791eSchristos 	}
760ca37791eSchristos 
761ca37791eSchristos fail:
762ca37791eSchristos #if OPENSSL_VERSION_NUMBER >= 0x10100000L
763ca37791eSchristos 	if (ctx != NULL)
764ca37791eSchristos 		HMAC_CTX_free(ctx);
765ca37791eSchristos #endif
766ca37791eSchristos 
767ca37791eSchristos 	return (item);
768ca37791eSchristos }
769ca37791eSchristos 
77098a5e356Schristos 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)77198a5e356Schristos cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
77298a5e356Schristos     const fido_blob_t *ecdh, const es256_pk_t *pk, const fido_blob_t *salt)
773ca37791eSchristos {
774ca37791eSchristos 	cbor_item_t		*param = NULL;
77598a5e356Schristos 	cbor_item_t		*argv[4];
776ca37791eSchristos 	struct cbor_pair	 pair;
77798a5e356Schristos 	fido_blob_t		*enc = NULL;
77898a5e356Schristos 	int			 r;
779ca37791eSchristos 
780ca37791eSchristos 	memset(argv, 0, sizeof(argv));
781ca37791eSchristos 	memset(&pair, 0, sizeof(pair));
782ca37791eSchristos 
78398a5e356Schristos 	if (item == NULL || ecdh == NULL || pk == NULL || salt->ptr == NULL) {
78498a5e356Schristos 		fido_log_debug("%s: ecdh=%p, pk=%p, salt->ptr=%p", __func__,
78598a5e356Schristos 		    (const void *)ecdh, (const void *)pk,
78698a5e356Schristos 		    (const void *)salt->ptr);
78798a5e356Schristos 		r = FIDO_ERR_INTERNAL;
788ca37791eSchristos 		goto fail;
789ca37791eSchristos 	}
790ca37791eSchristos 
79198a5e356Schristos 	if (salt->len != 32 && salt->len != 64) {
79298a5e356Schristos 		fido_log_debug("%s: salt->len=%zu", __func__, salt->len);
79398a5e356Schristos 		r = FIDO_ERR_INTERNAL;
79498a5e356Schristos 		goto fail;
79598a5e356Schristos 	}
79698a5e356Schristos 
79798a5e356Schristos 	if ((enc = fido_blob_new()) == NULL ||
79898a5e356Schristos 	    aes256_cbc_enc(dev, ecdh, salt, enc) < 0) {
79998a5e356Schristos 		fido_log_debug("%s: aes256_cbc_enc", __func__);
80098a5e356Schristos 		r = FIDO_ERR_INTERNAL;
801ca37791eSchristos 		goto fail;
802ca37791eSchristos 	}
803ca37791eSchristos 
804ca37791eSchristos 	/* XXX not pin, but salt */
805ca37791eSchristos 	if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
80698a5e356Schristos 	    (argv[1] = fido_blob_encode(enc)) == NULL ||
80798a5e356Schristos 	    (argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL ||
80898a5e356Schristos 	    (argv[3] = cbor_encode_pin_opt(dev)) == NULL) {
809ca37791eSchristos 		fido_log_debug("%s: cbor encode", __func__);
81098a5e356Schristos 		r = FIDO_ERR_INTERNAL;
811ca37791eSchristos 		goto fail;
812ca37791eSchristos 	}
813ca37791eSchristos 
81498a5e356Schristos 	if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) {
815ca37791eSchristos 		fido_log_debug("%s: cbor_flatten_vector", __func__);
81698a5e356Schristos 		r = FIDO_ERR_INTERNAL;
817ca37791eSchristos 		goto fail;
818ca37791eSchristos 	}
819ca37791eSchristos 
820ca37791eSchristos 	if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
821ca37791eSchristos 		fido_log_debug("%s: cbor_build", __func__);
82298a5e356Schristos 		r = FIDO_ERR_INTERNAL;
823ca37791eSchristos 		goto fail;
824ca37791eSchristos 	}
825ca37791eSchristos 
826ca37791eSchristos 	pair.value = param;
827ca37791eSchristos 
828ca37791eSchristos 	if (!cbor_map_add(item, pair)) {
829ca37791eSchristos 		fido_log_debug("%s: cbor_map_add", __func__);
83098a5e356Schristos 		r = FIDO_ERR_INTERNAL;
831ca37791eSchristos 		goto fail;
832ca37791eSchristos 	}
833ca37791eSchristos 
83498a5e356Schristos 	r = FIDO_OK;
83598a5e356Schristos 
836ca37791eSchristos fail:
83798a5e356Schristos 	cbor_vector_free(argv, nitems(argv));
838ca37791eSchristos 
839ca37791eSchristos 	if (param != NULL)
840ca37791eSchristos 		cbor_decref(&param);
841ca37791eSchristos 	if (pair.key != NULL)
842ca37791eSchristos 		cbor_decref(&pair.key);
843ca37791eSchristos 
84498a5e356Schristos 	fido_blob_free(&enc);
84598a5e356Schristos 
84698a5e356Schristos 	return (r);
84798a5e356Schristos }
84898a5e356Schristos 
84998a5e356Schristos 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)85098a5e356Schristos cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext,
85198a5e356Schristos     const fido_blob_t *ecdh, const es256_pk_t *pk)
85298a5e356Schristos {
85398a5e356Schristos 	cbor_item_t *item = NULL;
85498a5e356Schristos 	size_t size = 0;
85598a5e356Schristos 
85698a5e356Schristos 	if (ext->mask & FIDO_EXT_CRED_BLOB)
85798a5e356Schristos 		size++;
85898a5e356Schristos 	if (ext->mask & FIDO_EXT_HMAC_SECRET)
85998a5e356Schristos 		size++;
86098a5e356Schristos 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
86198a5e356Schristos 		size++;
86298a5e356Schristos 	if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
86398a5e356Schristos 		return (NULL);
86498a5e356Schristos 
86598a5e356Schristos 	if (ext->mask & FIDO_EXT_CRED_BLOB) {
86698a5e356Schristos 		if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) {
86798a5e356Schristos 			cbor_decref(&item);
86898a5e356Schristos 			return (NULL);
86998a5e356Schristos 		}
87098a5e356Schristos 	}
87198a5e356Schristos 	if (ext->mask & FIDO_EXT_HMAC_SECRET) {
87298a5e356Schristos 		if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk,
87398a5e356Schristos 		    &ext->hmac_salt) < 0) {
87498a5e356Schristos 			cbor_decref(&item);
87598a5e356Schristos 			return (NULL);
87698a5e356Schristos 		}
87798a5e356Schristos 	}
87898a5e356Schristos 	if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
87998a5e356Schristos 		if (cbor_encode_largeblob_key_ext(item) < 0) {
88098a5e356Schristos 			cbor_decref(&item);
88198a5e356Schristos 			return (NULL);
88298a5e356Schristos 		}
88398a5e356Schristos 	}
88498a5e356Schristos 
885ca37791eSchristos 	return (item);
886ca37791eSchristos }
887ca37791eSchristos 
888ca37791eSchristos int
cbor_decode_fmt(const cbor_item_t * item,char ** fmt)889ca37791eSchristos cbor_decode_fmt(const cbor_item_t *item, char **fmt)
890ca37791eSchristos {
891ca37791eSchristos 	char	*type = NULL;
892ca37791eSchristos 
893ca37791eSchristos 	if (cbor_string_copy(item, &type) < 0) {
894ca37791eSchristos 		fido_log_debug("%s: cbor_string_copy", __func__);
895ca37791eSchristos 		return (-1);
896ca37791eSchristos 	}
897ca37791eSchristos 
898*b93b3e8cSchristos 	if (strcmp(type, "packed") && strcmp(type, "fido-u2f") &&
899*b93b3e8cSchristos 	    strcmp(type, "none")) {
900ca37791eSchristos 		fido_log_debug("%s: type=%s", __func__, type);
901ca37791eSchristos 		free(type);
902ca37791eSchristos 		return (-1);
903ca37791eSchristos 	}
904ca37791eSchristos 
905ca37791eSchristos 	*fmt = type;
906ca37791eSchristos 
907ca37791eSchristos 	return (0);
908ca37791eSchristos }
909ca37791eSchristos 
910ca37791eSchristos struct cose_key {
911ca37791eSchristos 	int kty;
912ca37791eSchristos 	int alg;
913ca37791eSchristos 	int crv;
914ca37791eSchristos };
915ca37791eSchristos 
916ca37791eSchristos static int
find_cose_alg(const cbor_item_t * key,const cbor_item_t * val,void * arg)917ca37791eSchristos find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
918ca37791eSchristos {
919ca37791eSchristos 	struct cose_key *cose_key = arg;
920ca37791eSchristos 
921ca37791eSchristos 	if (cbor_isa_uint(key) == true &&
922ca37791eSchristos 	    cbor_int_get_width(key) == CBOR_INT_8) {
923ca37791eSchristos 		switch (cbor_get_uint8(key)) {
924ca37791eSchristos 		case 1:
925ca37791eSchristos 			if (cbor_isa_uint(val) == false ||
926ca37791eSchristos 			    cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
927ca37791eSchristos 				fido_log_debug("%s: kty", __func__);
928ca37791eSchristos 				return (-1);
929ca37791eSchristos 			}
930ca37791eSchristos 
931ca37791eSchristos 			cose_key->kty = (int)cbor_get_int(val);
932ca37791eSchristos 
933ca37791eSchristos 			break;
934ca37791eSchristos 		case 3:
935ca37791eSchristos 			if (cbor_isa_negint(val) == false ||
936ca37791eSchristos 			    cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
937ca37791eSchristos 				fido_log_debug("%s: alg", __func__);
938ca37791eSchristos 				return (-1);
939ca37791eSchristos 			}
940ca37791eSchristos 
941ca37791eSchristos 			cose_key->alg = -(int)cbor_get_int(val) - 1;
942ca37791eSchristos 
943ca37791eSchristos 			break;
944ca37791eSchristos 		}
945ca37791eSchristos 	} else if (cbor_isa_negint(key) == true &&
946ca37791eSchristos 	    cbor_int_get_width(key) == CBOR_INT_8) {
947ca37791eSchristos 		if (cbor_get_uint8(key) == 0) {
948ca37791eSchristos 			/* get crv if not rsa, otherwise ignore */
949ca37791eSchristos 			if (cbor_isa_uint(val) == true &&
950ca37791eSchristos 			    cbor_get_int(val) <= INT_MAX &&
951ca37791eSchristos 			    cose_key->crv == 0)
952ca37791eSchristos 				cose_key->crv = (int)cbor_get_int(val);
953ca37791eSchristos 		}
954ca37791eSchristos 	}
955ca37791eSchristos 
956ca37791eSchristos 	return (0);
957ca37791eSchristos }
958ca37791eSchristos 
959ca37791eSchristos static int
get_cose_alg(const cbor_item_t * item,int * cose_alg)960ca37791eSchristos get_cose_alg(const cbor_item_t *item, int *cose_alg)
961ca37791eSchristos {
962ca37791eSchristos 	struct cose_key cose_key;
963ca37791eSchristos 
964ca37791eSchristos 	memset(&cose_key, 0, sizeof(cose_key));
965ca37791eSchristos 
966ca37791eSchristos 	*cose_alg = 0;
967ca37791eSchristos 
968ca37791eSchristos 	if (cbor_isa_map(item) == false ||
969ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
970ca37791eSchristos 	    cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
971ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
972ca37791eSchristos 		return (-1);
973ca37791eSchristos 	}
974ca37791eSchristos 
975ca37791eSchristos 	switch (cose_key.alg) {
976ca37791eSchristos 	case COSE_ES256:
977ca37791eSchristos 		if (cose_key.kty != COSE_KTY_EC2 ||
978ca37791eSchristos 		    cose_key.crv != COSE_P256) {
979ca37791eSchristos 			fido_log_debug("%s: invalid kty/crv", __func__);
980ca37791eSchristos 			return (-1);
981ca37791eSchristos 		}
982ca37791eSchristos 
983ca37791eSchristos 		break;
984ca37791eSchristos 	case COSE_EDDSA:
985ca37791eSchristos 		if (cose_key.kty != COSE_KTY_OKP ||
986ca37791eSchristos 		    cose_key.crv != COSE_ED25519) {
987ca37791eSchristos 			fido_log_debug("%s: invalid kty/crv", __func__);
988ca37791eSchristos 			return (-1);
989ca37791eSchristos 		}
990ca37791eSchristos 
991ca37791eSchristos 		break;
992ca37791eSchristos 	case COSE_RS256:
993ca37791eSchristos 		if (cose_key.kty != COSE_KTY_RSA) {
994ca37791eSchristos 			fido_log_debug("%s: invalid kty/crv", __func__);
995ca37791eSchristos 			return (-1);
996ca37791eSchristos 		}
997ca37791eSchristos 
998ca37791eSchristos 		break;
999ca37791eSchristos 	default:
1000ca37791eSchristos 		fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg);
1001ca37791eSchristos 
1002ca37791eSchristos 		return (-1);
1003ca37791eSchristos 	}
1004ca37791eSchristos 
1005ca37791eSchristos 	*cose_alg = cose_key.alg;
1006ca37791eSchristos 
1007ca37791eSchristos 	return (0);
1008ca37791eSchristos }
1009ca37791eSchristos 
1010ca37791eSchristos int
cbor_decode_pubkey(const cbor_item_t * item,int * type,void * key)1011ca37791eSchristos cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
1012ca37791eSchristos {
1013ca37791eSchristos 	if (get_cose_alg(item, type) < 0) {
1014ca37791eSchristos 		fido_log_debug("%s: get_cose_alg", __func__);
1015ca37791eSchristos 		return (-1);
1016ca37791eSchristos 	}
1017ca37791eSchristos 
1018ca37791eSchristos 	switch (*type) {
1019ca37791eSchristos 	case COSE_ES256:
1020ca37791eSchristos 		if (es256_pk_decode(item, key) < 0) {
1021ca37791eSchristos 			fido_log_debug("%s: es256_pk_decode", __func__);
1022ca37791eSchristos 			return (-1);
1023ca37791eSchristos 		}
1024ca37791eSchristos 		break;
1025ca37791eSchristos 	case COSE_RS256:
1026ca37791eSchristos 		if (rs256_pk_decode(item, key) < 0) {
1027ca37791eSchristos 			fido_log_debug("%s: rs256_pk_decode", __func__);
1028ca37791eSchristos 			return (-1);
1029ca37791eSchristos 		}
1030ca37791eSchristos 		break;
1031ca37791eSchristos 	case COSE_EDDSA:
1032ca37791eSchristos 		if (eddsa_pk_decode(item, key) < 0) {
1033ca37791eSchristos 			fido_log_debug("%s: eddsa_pk_decode", __func__);
1034ca37791eSchristos 			return (-1);
1035ca37791eSchristos 		}
1036ca37791eSchristos 		break;
1037ca37791eSchristos 	default:
1038ca37791eSchristos 		fido_log_debug("%s: invalid cose_alg %d", __func__, *type);
1039ca37791eSchristos 		return (-1);
1040ca37791eSchristos 	}
1041ca37791eSchristos 
1042ca37791eSchristos 	return (0);
1043ca37791eSchristos }
1044ca37791eSchristos 
1045ca37791eSchristos static int
decode_attcred(const unsigned char ** buf,size_t * len,int cose_alg,fido_attcred_t * attcred)1046ca37791eSchristos decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
1047ca37791eSchristos     fido_attcred_t *attcred)
1048ca37791eSchristos {
1049ca37791eSchristos 	cbor_item_t		*item = NULL;
1050ca37791eSchristos 	struct cbor_load_result	 cbor;
1051ca37791eSchristos 	uint16_t		 id_len;
1052ca37791eSchristos 	int			 ok = -1;
1053ca37791eSchristos 
105498a5e356Schristos 	fido_log_xxd(*buf, *len, "%s", __func__);
1055ca37791eSchristos 
1056ca37791eSchristos 	if (fido_buf_read(buf, len, &attcred->aaguid,
1057ca37791eSchristos 	    sizeof(attcred->aaguid)) < 0) {
1058ca37791eSchristos 		fido_log_debug("%s: fido_buf_read aaguid", __func__);
1059ca37791eSchristos 		return (-1);
1060ca37791eSchristos 	}
1061ca37791eSchristos 
1062ca37791eSchristos 	if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
1063ca37791eSchristos 		fido_log_debug("%s: fido_buf_read id_len", __func__);
1064ca37791eSchristos 		return (-1);
1065ca37791eSchristos 	}
1066ca37791eSchristos 
1067ca37791eSchristos 	attcred->id.len = (size_t)be16toh(id_len);
1068ca37791eSchristos 	if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
1069ca37791eSchristos 		return (-1);
1070ca37791eSchristos 
1071ca37791eSchristos 	fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
1072ca37791eSchristos 
1073ca37791eSchristos 	if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
1074ca37791eSchristos 		fido_log_debug("%s: fido_buf_read id", __func__);
1075ca37791eSchristos 		return (-1);
1076ca37791eSchristos 	}
1077ca37791eSchristos 
1078ca37791eSchristos 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1079ca37791eSchristos 		fido_log_debug("%s: cbor_load", __func__);
1080ca37791eSchristos 		goto fail;
1081ca37791eSchristos 	}
1082ca37791eSchristos 
1083ca37791eSchristos 	if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
1084ca37791eSchristos 		fido_log_debug("%s: cbor_decode_pubkey", __func__);
1085ca37791eSchristos 		goto fail;
1086ca37791eSchristos 	}
1087ca37791eSchristos 
1088ca37791eSchristos 	if (attcred->type != cose_alg) {
1089ca37791eSchristos 		fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
1090ca37791eSchristos 		    attcred->type, cose_alg);
1091ca37791eSchristos 		goto fail;
1092ca37791eSchristos 	}
1093ca37791eSchristos 
1094ca37791eSchristos 	*buf += cbor.read;
1095ca37791eSchristos 	*len -= cbor.read;
1096ca37791eSchristos 
1097ca37791eSchristos 	ok = 0;
1098ca37791eSchristos fail:
1099ca37791eSchristos 	if (item != NULL)
1100ca37791eSchristos 		cbor_decref(&item);
1101ca37791eSchristos 
1102ca37791eSchristos 	return (ok);
1103ca37791eSchristos }
1104ca37791eSchristos 
1105ca37791eSchristos static int
decode_cred_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)110698a5e356Schristos decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1107ca37791eSchristos {
1108ca37791eSchristos 	fido_cred_ext_t	*authdata_ext = arg;
1109ca37791eSchristos 	char		*type = NULL;
1110ca37791eSchristos 	int		 ok = -1;
1111ca37791eSchristos 
1112ca37791eSchristos 	if (cbor_string_copy(key, &type) < 0) {
1113ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1114ca37791eSchristos 		ok = 0; /* ignore */
1115ca37791eSchristos 		goto out;
1116ca37791eSchristos 	}
1117ca37791eSchristos 
1118ca37791eSchristos 	if (strcmp(type, "hmac-secret") == 0) {
1119ca37791eSchristos 		if (cbor_isa_float_ctrl(val) == false ||
1120ca37791eSchristos 		    cbor_float_get_width(val) != CBOR_FLOAT_0 ||
1121ca37791eSchristos 		    cbor_is_bool(val) == false) {
1122ca37791eSchristos 			fido_log_debug("%s: cbor type", __func__);
1123ca37791eSchristos 			goto out;
1124ca37791eSchristos 		}
1125ca37791eSchristos 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1126ca37791eSchristos 			authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
1127ca37791eSchristos 	} else if (strcmp(type, "credProtect") == 0) {
1128ca37791eSchristos 		if (cbor_isa_uint(val) == false ||
1129ca37791eSchristos 		    cbor_int_get_width(val) != CBOR_INT_8) {
1130ca37791eSchristos 			fido_log_debug("%s: cbor type", __func__);
1131ca37791eSchristos 			goto out;
1132ca37791eSchristos 		}
1133ca37791eSchristos 		authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
1134ca37791eSchristos 		authdata_ext->prot = cbor_get_uint8(val);
113598a5e356Schristos 	} else if (strcmp(type, "credBlob") == 0) {
113698a5e356Schristos 		if (cbor_isa_float_ctrl(val) == false ||
113798a5e356Schristos 		    cbor_float_get_width(val) != CBOR_FLOAT_0 ||
113898a5e356Schristos 		    cbor_is_bool(val) == false) {
113998a5e356Schristos 			fido_log_debug("%s: cbor type", __func__);
114098a5e356Schristos 			goto out;
114198a5e356Schristos 		}
114298a5e356Schristos 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
114398a5e356Schristos 			authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1144ca37791eSchristos 	}
1145ca37791eSchristos 
1146ca37791eSchristos 	ok = 0;
1147ca37791eSchristos out:
1148ca37791eSchristos 	free(type);
1149ca37791eSchristos 
1150ca37791eSchristos 	return (ok);
1151ca37791eSchristos }
1152ca37791eSchristos 
1153ca37791eSchristos static int
decode_cred_extensions(const unsigned char ** buf,size_t * len,fido_cred_ext_t * authdata_ext)115498a5e356Schristos decode_cred_extensions(const unsigned char **buf, size_t *len,
1155ca37791eSchristos     fido_cred_ext_t *authdata_ext)
1156ca37791eSchristos {
1157ca37791eSchristos 	cbor_item_t		*item = NULL;
1158ca37791eSchristos 	struct cbor_load_result	 cbor;
1159ca37791eSchristos 	int			 ok = -1;
1160ca37791eSchristos 
1161ca37791eSchristos 	memset(authdata_ext, 0, sizeof(*authdata_ext));
1162ca37791eSchristos 
116398a5e356Schristos 	fido_log_xxd(*buf, *len, "%s", __func__);
116498a5e356Schristos 
1165ca37791eSchristos 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1166ca37791eSchristos 		fido_log_debug("%s: cbor_load", __func__);
1167ca37791eSchristos 		goto fail;
1168ca37791eSchristos 	}
1169ca37791eSchristos 
1170ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1171ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
117298a5e356Schristos 	    cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
1173ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1174ca37791eSchristos 		goto fail;
1175ca37791eSchristos 	}
1176ca37791eSchristos 
1177ca37791eSchristos 	*buf += cbor.read;
1178ca37791eSchristos 	*len -= cbor.read;
1179ca37791eSchristos 
1180ca37791eSchristos 	ok = 0;
1181ca37791eSchristos fail:
1182ca37791eSchristos 	if (item != NULL)
1183ca37791eSchristos 		cbor_decref(&item);
1184ca37791eSchristos 
1185ca37791eSchristos 	return (ok);
1186ca37791eSchristos }
1187ca37791eSchristos 
1188ca37791eSchristos static int
decode_assert_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)118998a5e356Schristos decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
119098a5e356Schristos     void *arg)
1191ca37791eSchristos {
119298a5e356Schristos 	fido_assert_extattr_t	*authdata_ext = arg;
1193ca37791eSchristos 	char			*type = NULL;
1194ca37791eSchristos 	int			 ok = -1;
1195ca37791eSchristos 
119698a5e356Schristos 	if (cbor_string_copy(key, &type) < 0) {
1197ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1198ca37791eSchristos 		ok = 0; /* ignore */
1199ca37791eSchristos 		goto out;
1200ca37791eSchristos 	}
1201ca37791eSchristos 
120298a5e356Schristos 	if (strcmp(type, "hmac-secret") == 0) {
120398a5e356Schristos 		if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) {
120498a5e356Schristos 			fido_log_debug("%s: fido_blob_decode", __func__);
120598a5e356Schristos 			goto out;
120698a5e356Schristos 		}
120798a5e356Schristos 		authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
120898a5e356Schristos 	} else if (strcmp(type, "credBlob") == 0) {
120998a5e356Schristos 		if (fido_blob_decode(val, &authdata_ext->blob) < 0) {
121098a5e356Schristos 			fido_log_debug("%s: fido_blob_decode", __func__);
121198a5e356Schristos 			goto out;
121298a5e356Schristos 		}
121398a5e356Schristos 		authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
121498a5e356Schristos 	}
121598a5e356Schristos 
121698a5e356Schristos 	ok = 0;
1217ca37791eSchristos out:
1218ca37791eSchristos 	free(type);
1219ca37791eSchristos 
1220ca37791eSchristos 	return (ok);
1221ca37791eSchristos }
1222ca37791eSchristos 
1223ca37791eSchristos static int
decode_assert_extensions(const unsigned char ** buf,size_t * len,fido_assert_extattr_t * authdata_ext)122498a5e356Schristos decode_assert_extensions(const unsigned char **buf, size_t *len,
122598a5e356Schristos     fido_assert_extattr_t *authdata_ext)
1226ca37791eSchristos {
1227ca37791eSchristos 	cbor_item_t		*item = NULL;
1228ca37791eSchristos 	struct cbor_load_result	 cbor;
1229ca37791eSchristos 	int			 ok = -1;
1230ca37791eSchristos 
123198a5e356Schristos 	fido_log_xxd(*buf, *len, "%s", __func__);
1232ca37791eSchristos 
1233ca37791eSchristos 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1234ca37791eSchristos 		fido_log_debug("%s: cbor_load", __func__);
1235ca37791eSchristos 		goto fail;
1236ca37791eSchristos 	}
1237ca37791eSchristos 
1238ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1239ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
124098a5e356Schristos 	    cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
1241ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1242ca37791eSchristos 		goto fail;
1243ca37791eSchristos 	}
1244ca37791eSchristos 
1245ca37791eSchristos 	*buf += cbor.read;
1246ca37791eSchristos 	*len -= cbor.read;
1247ca37791eSchristos 
1248ca37791eSchristos 	ok = 0;
1249ca37791eSchristos fail:
1250ca37791eSchristos 	if (item != NULL)
1251ca37791eSchristos 		cbor_decref(&item);
1252ca37791eSchristos 
1253ca37791eSchristos 	return (ok);
1254ca37791eSchristos }
1255ca37791eSchristos 
1256ca37791eSchristos 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)1257ca37791eSchristos cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1258ca37791eSchristos     fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
1259ca37791eSchristos     fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
1260ca37791eSchristos {
1261ca37791eSchristos 	const unsigned char	*buf = NULL;
1262ca37791eSchristos 	size_t			 len;
1263ca37791eSchristos 	size_t			 alloc_len;
1264ca37791eSchristos 
1265ca37791eSchristos 	if (cbor_isa_bytestring(item) == false ||
1266ca37791eSchristos 	    cbor_bytestring_is_definite(item) == false) {
1267ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1268ca37791eSchristos 		return (-1);
1269ca37791eSchristos 	}
1270ca37791eSchristos 
1271ca37791eSchristos 	if (authdata_cbor->ptr != NULL ||
1272ca37791eSchristos 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1273ca37791eSchristos 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
1274ca37791eSchristos 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1275ca37791eSchristos 		return (-1);
1276ca37791eSchristos 	}
1277ca37791eSchristos 
1278ca37791eSchristos 	buf = cbor_bytestring_handle(item);
1279ca37791eSchristos 	len = cbor_bytestring_length(item);
128098a5e356Schristos 	fido_log_xxd(buf, len, "%s", __func__);
1281ca37791eSchristos 
1282ca37791eSchristos 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1283ca37791eSchristos 		fido_log_debug("%s: fido_buf_read", __func__);
1284ca37791eSchristos 		return (-1);
1285ca37791eSchristos 	}
1286ca37791eSchristos 
1287ca37791eSchristos 	authdata->sigcount = be32toh(authdata->sigcount);
1288ca37791eSchristos 
1289ca37791eSchristos 	if (attcred != NULL) {
1290ca37791eSchristos 		if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1291ca37791eSchristos 		    decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1292ca37791eSchristos 			return (-1);
1293ca37791eSchristos 	}
1294ca37791eSchristos 
1295ca37791eSchristos 	if (authdata_ext != NULL) {
1296ca37791eSchristos 		if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
129798a5e356Schristos 		    decode_cred_extensions(&buf, &len, authdata_ext) < 0)
1298ca37791eSchristos 			return (-1);
1299ca37791eSchristos 	}
1300ca37791eSchristos 
1301ca37791eSchristos 	/* XXX we should probably ensure that len == 0 at this point */
1302ca37791eSchristos 
1303ca37791eSchristos 	return (FIDO_OK);
1304ca37791eSchristos }
1305ca37791eSchristos 
1306ca37791eSchristos 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)1307ca37791eSchristos cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
130898a5e356Schristos     fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
1309ca37791eSchristos {
1310ca37791eSchristos 	const unsigned char	*buf = NULL;
1311ca37791eSchristos 	size_t			 len;
1312ca37791eSchristos 	size_t			 alloc_len;
1313ca37791eSchristos 
1314ca37791eSchristos 	if (cbor_isa_bytestring(item) == false ||
1315ca37791eSchristos 	    cbor_bytestring_is_definite(item) == false) {
1316ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1317ca37791eSchristos 		return (-1);
1318ca37791eSchristos 	}
1319ca37791eSchristos 
1320ca37791eSchristos 	if (authdata_cbor->ptr != NULL ||
1321ca37791eSchristos 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1322ca37791eSchristos 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
1323ca37791eSchristos 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1324ca37791eSchristos 		return (-1);
1325ca37791eSchristos 	}
1326ca37791eSchristos 
1327ca37791eSchristos 	buf = cbor_bytestring_handle(item);
1328ca37791eSchristos 	len = cbor_bytestring_length(item);
1329ca37791eSchristos 
1330ca37791eSchristos 	fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1331ca37791eSchristos 
1332ca37791eSchristos 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1333ca37791eSchristos 		fido_log_debug("%s: fido_buf_read", __func__);
1334ca37791eSchristos 		return (-1);
1335ca37791eSchristos 	}
1336ca37791eSchristos 
1337ca37791eSchristos 	authdata->sigcount = be32toh(authdata->sigcount);
1338ca37791eSchristos 
1339ca37791eSchristos 	if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
134098a5e356Schristos 		if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
134198a5e356Schristos 			fido_log_debug("%s: decode_assert_extensions",
134298a5e356Schristos 			    __func__);
1343ca37791eSchristos 			return (-1);
1344ca37791eSchristos 		}
1345ca37791eSchristos 	}
1346ca37791eSchristos 
1347ca37791eSchristos 	/* XXX we should probably ensure that len == 0 at this point */
1348ca37791eSchristos 
1349ca37791eSchristos 	return (FIDO_OK);
1350ca37791eSchristos }
1351ca37791eSchristos 
1352ca37791eSchristos static int
decode_x5c(const cbor_item_t * item,void * arg)1353ca37791eSchristos decode_x5c(const cbor_item_t *item, void *arg)
1354ca37791eSchristos {
1355ca37791eSchristos 	fido_blob_t *x5c = arg;
1356ca37791eSchristos 
1357ca37791eSchristos 	if (x5c->len)
1358ca37791eSchristos 		return (0); /* ignore */
1359ca37791eSchristos 
136098a5e356Schristos 	return (fido_blob_decode(item, x5c));
1361ca37791eSchristos }
1362ca37791eSchristos 
1363ca37791eSchristos static int
decode_attstmt_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1364ca37791eSchristos decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1365ca37791eSchristos {
1366ca37791eSchristos 	fido_attstmt_t	*attstmt = arg;
1367ca37791eSchristos 	char		*name = NULL;
1368ca37791eSchristos 	int		 cose_alg = 0;
1369ca37791eSchristos 	int		 ok = -1;
1370ca37791eSchristos 
1371ca37791eSchristos 	if (cbor_string_copy(key, &name) < 0) {
1372ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1373ca37791eSchristos 		ok = 0; /* ignore */
1374ca37791eSchristos 		goto out;
1375ca37791eSchristos 	}
1376ca37791eSchristos 
1377ca37791eSchristos 	if (!strcmp(name, "alg")) {
1378ca37791eSchristos 		if (cbor_isa_negint(val) == false ||
1379ca37791eSchristos 		    cbor_get_int(val) > UINT16_MAX) {
1380ca37791eSchristos 			fido_log_debug("%s: alg", __func__);
1381ca37791eSchristos 			goto out;
1382ca37791eSchristos 		}
1383ca37791eSchristos 		if ((cose_alg = -(int)cbor_get_int(val) - 1) != COSE_ES256 &&
1384ca37791eSchristos 		    cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) {
1385ca37791eSchristos 			fido_log_debug("%s: unsupported cose_alg=%d", __func__,
1386ca37791eSchristos 			    cose_alg);
1387ca37791eSchristos 			goto out;
1388ca37791eSchristos 		}
1389ca37791eSchristos 	} else if (!strcmp(name, "sig")) {
139098a5e356Schristos 		if (fido_blob_decode(val, &attstmt->sig) < 0) {
1391ca37791eSchristos 			fido_log_debug("%s: sig", __func__);
1392ca37791eSchristos 			goto out;
1393ca37791eSchristos 		}
1394ca37791eSchristos 	} else if (!strcmp(name, "x5c")) {
1395ca37791eSchristos 		if (cbor_isa_array(val) == false ||
1396ca37791eSchristos 		    cbor_array_is_definite(val) == false ||
1397ca37791eSchristos 		    cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
1398ca37791eSchristos 			fido_log_debug("%s: x5c", __func__);
1399ca37791eSchristos 			goto out;
1400ca37791eSchristos 		}
1401ca37791eSchristos 	}
1402ca37791eSchristos 
1403ca37791eSchristos 	ok = 0;
1404ca37791eSchristos out:
1405ca37791eSchristos 	free(name);
1406ca37791eSchristos 
1407ca37791eSchristos 	return (ok);
1408ca37791eSchristos }
1409ca37791eSchristos 
1410ca37791eSchristos int
cbor_decode_attstmt(const cbor_item_t * item,fido_attstmt_t * attstmt)1411ca37791eSchristos cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1412ca37791eSchristos {
1413ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1414ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
1415ca37791eSchristos 	    cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
1416ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1417ca37791eSchristos 		return (-1);
1418ca37791eSchristos 	}
1419ca37791eSchristos 
1420ca37791eSchristos 	return (0);
1421ca37791eSchristos }
1422ca37791eSchristos 
1423ca37791eSchristos int
cbor_decode_uint64(const cbor_item_t * item,uint64_t * n)1424ca37791eSchristos cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1425ca37791eSchristos {
1426ca37791eSchristos 	if (cbor_isa_uint(item) == false) {
1427ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1428ca37791eSchristos 		return (-1);
1429ca37791eSchristos 	}
1430ca37791eSchristos 
1431ca37791eSchristos 	*n = cbor_get_int(item);
1432ca37791eSchristos 
1433ca37791eSchristos 	return (0);
1434ca37791eSchristos }
1435ca37791eSchristos 
1436ca37791eSchristos static int
decode_cred_id_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1437ca37791eSchristos decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1438ca37791eSchristos {
1439ca37791eSchristos 	fido_blob_t	*id = arg;
1440ca37791eSchristos 	char		*name = NULL;
1441ca37791eSchristos 	int		 ok = -1;
1442ca37791eSchristos 
1443ca37791eSchristos 	if (cbor_string_copy(key, &name) < 0) {
1444ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1445ca37791eSchristos 		ok = 0; /* ignore */
1446ca37791eSchristos 		goto out;
1447ca37791eSchristos 	}
1448ca37791eSchristos 
1449ca37791eSchristos 	if (!strcmp(name, "id"))
145098a5e356Schristos 		if (fido_blob_decode(val, id) < 0) {
1451ca37791eSchristos 			fido_log_debug("%s: cbor_bytestring_copy", __func__);
1452ca37791eSchristos 			goto out;
1453ca37791eSchristos 		}
1454ca37791eSchristos 
1455ca37791eSchristos 	ok = 0;
1456ca37791eSchristos out:
1457ca37791eSchristos 	free(name);
1458ca37791eSchristos 
1459ca37791eSchristos 	return (ok);
1460ca37791eSchristos }
1461ca37791eSchristos 
1462ca37791eSchristos int
cbor_decode_cred_id(const cbor_item_t * item,fido_blob_t * id)1463ca37791eSchristos cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1464ca37791eSchristos {
1465ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1466ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
1467ca37791eSchristos 	    cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
1468ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1469ca37791eSchristos 		return (-1);
1470ca37791eSchristos 	}
1471ca37791eSchristos 
1472ca37791eSchristos 	return (0);
1473ca37791eSchristos }
1474ca37791eSchristos 
1475ca37791eSchristos static int
decode_user_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1476ca37791eSchristos decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1477ca37791eSchristos {
1478ca37791eSchristos 	fido_user_t	*user = arg;
1479ca37791eSchristos 	char		*name = NULL;
1480ca37791eSchristos 	int		 ok = -1;
1481ca37791eSchristos 
1482ca37791eSchristos 	if (cbor_string_copy(key, &name) < 0) {
1483ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1484ca37791eSchristos 		ok = 0; /* ignore */
1485ca37791eSchristos 		goto out;
1486ca37791eSchristos 	}
1487ca37791eSchristos 
1488ca37791eSchristos 	if (!strcmp(name, "icon")) {
1489ca37791eSchristos 		if (cbor_string_copy(val, &user->icon) < 0) {
1490ca37791eSchristos 			fido_log_debug("%s: icon", __func__);
1491ca37791eSchristos 			goto out;
1492ca37791eSchristos 		}
1493ca37791eSchristos 	} else if (!strcmp(name, "name")) {
1494ca37791eSchristos 		if (cbor_string_copy(val, &user->name) < 0) {
1495ca37791eSchristos 			fido_log_debug("%s: name", __func__);
1496ca37791eSchristos 			goto out;
1497ca37791eSchristos 		}
1498ca37791eSchristos 	} else if (!strcmp(name, "displayName")) {
1499ca37791eSchristos 		if (cbor_string_copy(val, &user->display_name) < 0) {
1500ca37791eSchristos 			fido_log_debug("%s: display_name", __func__);
1501ca37791eSchristos 			goto out;
1502ca37791eSchristos 		}
1503ca37791eSchristos 	} else if (!strcmp(name, "id")) {
150498a5e356Schristos 		if (fido_blob_decode(val, &user->id) < 0) {
1505ca37791eSchristos 			fido_log_debug("%s: id", __func__);
1506ca37791eSchristos 			goto out;
1507ca37791eSchristos 		}
1508ca37791eSchristos 	}
1509ca37791eSchristos 
1510ca37791eSchristos 	ok = 0;
1511ca37791eSchristos out:
1512ca37791eSchristos 	free(name);
1513ca37791eSchristos 
1514ca37791eSchristos 	return (ok);
1515ca37791eSchristos }
1516ca37791eSchristos 
1517ca37791eSchristos int
cbor_decode_user(const cbor_item_t * item,fido_user_t * user)1518ca37791eSchristos cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1519ca37791eSchristos {
1520ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1521ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
1522ca37791eSchristos 	    cbor_map_iter(item, user, decode_user_entry) < 0) {
1523ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1524ca37791eSchristos 		return (-1);
1525ca37791eSchristos 	}
1526ca37791eSchristos 
1527ca37791eSchristos 	return (0);
1528ca37791eSchristos }
1529ca37791eSchristos 
1530ca37791eSchristos static int
decode_rp_entity_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1531ca37791eSchristos decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1532ca37791eSchristos     void *arg)
1533ca37791eSchristos {
1534ca37791eSchristos 	fido_rp_t	*rp = arg;
1535ca37791eSchristos 	char		*name = NULL;
1536ca37791eSchristos 	int		 ok = -1;
1537ca37791eSchristos 
1538ca37791eSchristos 	if (cbor_string_copy(key, &name) < 0) {
1539ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1540ca37791eSchristos 		ok = 0; /* ignore */
1541ca37791eSchristos 		goto out;
1542ca37791eSchristos 	}
1543ca37791eSchristos 
1544ca37791eSchristos 	if (!strcmp(name, "id")) {
1545ca37791eSchristos 		if (cbor_string_copy(val, &rp->id) < 0) {
1546ca37791eSchristos 			fido_log_debug("%s: id", __func__);
1547ca37791eSchristos 			goto out;
1548ca37791eSchristos 		}
1549ca37791eSchristos 	} else if (!strcmp(name, "name")) {
1550ca37791eSchristos 		if (cbor_string_copy(val, &rp->name) < 0) {
1551ca37791eSchristos 			fido_log_debug("%s: name", __func__);
1552ca37791eSchristos 			goto out;
1553ca37791eSchristos 		}
1554ca37791eSchristos 	}
1555ca37791eSchristos 
1556ca37791eSchristos 	ok = 0;
1557ca37791eSchristos out:
1558ca37791eSchristos 	free(name);
1559ca37791eSchristos 
1560ca37791eSchristos 	return (ok);
1561ca37791eSchristos }
1562ca37791eSchristos 
1563ca37791eSchristos int
cbor_decode_rp_entity(const cbor_item_t * item,fido_rp_t * rp)1564ca37791eSchristos cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1565ca37791eSchristos {
1566ca37791eSchristos 	if (cbor_isa_map(item) == false ||
1567ca37791eSchristos 	    cbor_map_is_definite(item) == false ||
1568ca37791eSchristos 	    cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
1569ca37791eSchristos 		fido_log_debug("%s: cbor type", __func__);
1570ca37791eSchristos 		return (-1);
1571ca37791eSchristos 	}
1572ca37791eSchristos 
1573ca37791eSchristos 	return (0);
1574ca37791eSchristos }
157598a5e356Schristos 
157698a5e356Schristos cbor_item_t *
cbor_build_uint(const uint64_t value)157798a5e356Schristos cbor_build_uint(const uint64_t value)
157898a5e356Schristos {
157998a5e356Schristos 	if (value <= UINT8_MAX)
158098a5e356Schristos 		return cbor_build_uint8((uint8_t)value);
158198a5e356Schristos 	else if (value <= UINT16_MAX)
158298a5e356Schristos 		return cbor_build_uint16((uint16_t)value);
158398a5e356Schristos 	else if (value <= UINT32_MAX)
158498a5e356Schristos 		return cbor_build_uint32((uint32_t)value);
158598a5e356Schristos 
158698a5e356Schristos 	return cbor_build_uint64(value);
158798a5e356Schristos }
158898a5e356Schristos 
158998a5e356Schristos int
cbor_array_append(cbor_item_t ** array,cbor_item_t * item)159098a5e356Schristos cbor_array_append(cbor_item_t **array, cbor_item_t *item)
159198a5e356Schristos {
159298a5e356Schristos 	cbor_item_t **v, *ret;
159398a5e356Schristos 	size_t n;
159498a5e356Schristos 
159598a5e356Schristos 	if ((v = cbor_array_handle(*array)) == NULL ||
159698a5e356Schristos 	    (n = cbor_array_size(*array)) == SIZE_MAX ||
159798a5e356Schristos 	    (ret = cbor_new_definite_array(n + 1)) == NULL)
159898a5e356Schristos 		return -1;
159998a5e356Schristos 	for (size_t i = 0; i < n; i++) {
160098a5e356Schristos 		if (cbor_array_push(ret, v[i]) == 0) {
160198a5e356Schristos 			cbor_decref(&ret);
160298a5e356Schristos 			return -1;
160398a5e356Schristos 		}
160498a5e356Schristos 	}
160598a5e356Schristos 	if (cbor_array_push(ret, item) == 0) {
160698a5e356Schristos 		cbor_decref(&ret);
160798a5e356Schristos 		return -1;
160898a5e356Schristos 	}
160998a5e356Schristos 	cbor_decref(array);
161098a5e356Schristos 	*array = ret;
161198a5e356Schristos 
161298a5e356Schristos 	return 0;
161398a5e356Schristos }
161498a5e356Schristos 
161598a5e356Schristos int
cbor_array_drop(cbor_item_t ** array,size_t idx)161698a5e356Schristos cbor_array_drop(cbor_item_t **array, size_t idx)
161798a5e356Schristos {
161898a5e356Schristos 	cbor_item_t **v, *ret;
161998a5e356Schristos 	size_t n;
162098a5e356Schristos 
162198a5e356Schristos 	if ((v = cbor_array_handle(*array)) == NULL ||
162298a5e356Schristos 	    (n = cbor_array_size(*array)) == 0 || idx >= n ||
162398a5e356Schristos 	    (ret = cbor_new_definite_array(n - 1)) == NULL)
162498a5e356Schristos 		return -1;
162598a5e356Schristos 	for (size_t i = 0; i < n; i++) {
162698a5e356Schristos 		if (i != idx && cbor_array_push(ret, v[i]) == 0) {
162798a5e356Schristos 			cbor_decref(&ret);
162898a5e356Schristos 			return -1;
162998a5e356Schristos 		}
163098a5e356Schristos 	}
163198a5e356Schristos 	cbor_decref(array);
163298a5e356Schristos 	*array = ret;
163398a5e356Schristos 
163498a5e356Schristos 	return 0;
163598a5e356Schristos }
1636