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