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