xref: /freebsd/contrib/libfido2/src/cbor.c (revision febb0da5bf4bc99828ebede7abcb039514ac367a)
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_attobj(const cbor_item_t * key,const cbor_item_t * val,void * arg)1136 decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1137 {
1138 	fido_cred_t *cred = arg;
1139 	char *name = NULL;
1140 	int ok = -1;
1141 
1142 	if (cbor_string_copy(key, &name) < 0) {
1143 		fido_log_debug("%s: cbor type", __func__);
1144 		ok = 0; /* ignore */
1145 		goto fail;
1146 	}
1147 
1148 	if (!strcmp(name, "fmt")) {
1149 		if (cbor_decode_fmt(val, &cred->fmt) < 0) {
1150 			fido_log_debug("%s: cbor_decode_fmt", __func__);
1151 			goto fail;
1152 		}
1153 	} else if (!strcmp(name, "attStmt")) {
1154 		if (cbor_decode_attstmt(val, &cred->attstmt) < 0) {
1155 			fido_log_debug("%s: cbor_decode_attstmt", __func__);
1156 			goto fail;
1157 		}
1158 	} else if (!strcmp(name, "authData")) {
1159 		if (fido_blob_decode(val, &cred->authdata_raw) < 0) {
1160 			fido_log_debug("%s: fido_blob_decode", __func__);
1161 			goto fail;
1162 		}
1163 		if (cbor_decode_cred_authdata(val, cred->type,
1164 		    &cred->authdata_cbor, &cred->authdata, &cred->attcred,
1165 		    &cred->authdata_ext) < 0) {
1166 			fido_log_debug("%s: cbor_decode_cred_authdata",
1167 			    __func__);
1168 			goto fail;
1169 		}
1170 	}
1171 
1172 	ok = 0;
1173 fail:
1174 	free(name);
1175 
1176 	return (ok);
1177 }
1178 
1179 /* XXX introduce fido_attobj_t? */
1180 int
cbor_decode_attobj(const cbor_item_t * item,fido_cred_t * cred)1181 cbor_decode_attobj(const cbor_item_t *item, fido_cred_t *cred)
1182 {
1183 	if (cbor_isa_map(item) == false ||
1184 	    cbor_map_is_definite(item) == false ||
1185 	    cbor_map_iter(item, cred, decode_attobj) < 0) {
1186 		fido_log_debug("%s: cbor type", __func__);
1187 		return (-1);
1188 	}
1189 
1190 	return (0);
1191 }
1192 
1193 static int
decode_cred_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)1194 decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1195 {
1196 	fido_cred_ext_t	*authdata_ext = arg;
1197 	char		*type = NULL;
1198 	int		 ok = -1;
1199 
1200 	if (cbor_string_copy(key, &type) < 0) {
1201 		fido_log_debug("%s: cbor type", __func__);
1202 		ok = 0; /* ignore */
1203 		goto out;
1204 	}
1205 
1206 	if (strcmp(type, "hmac-secret") == 0) {
1207 		if (cbor_decode_bool(val, NULL) < 0) {
1208 			fido_log_debug("%s: cbor_decode_bool", __func__);
1209 			goto out;
1210 		}
1211 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1212 			authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
1213 	} else if (strcmp(type, "credProtect") == 0) {
1214 		if (cbor_isa_uint(val) == false ||
1215 		    cbor_int_get_width(val) != CBOR_INT_8) {
1216 			fido_log_debug("%s: cbor type", __func__);
1217 			goto out;
1218 		}
1219 		authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
1220 		authdata_ext->prot = cbor_get_uint8(val);
1221 	} else if (strcmp(type, "credBlob") == 0) {
1222 		if (cbor_decode_bool(val, NULL) < 0) {
1223 			fido_log_debug("%s: cbor_decode_bool", __func__);
1224 			goto out;
1225 		}
1226 		if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1227 			authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1228 	} else if (strcmp(type, "minPinLength") == 0) {
1229 		if (cbor_isa_uint(val) == false ||
1230 		    cbor_int_get_width(val) != CBOR_INT_8) {
1231 			fido_log_debug("%s: cbor type", __func__);
1232 			goto out;
1233 		}
1234 		authdata_ext->mask |= FIDO_EXT_MINPINLEN;
1235 		authdata_ext->minpinlen = cbor_get_uint8(val);
1236 	}
1237 
1238 	ok = 0;
1239 out:
1240 	free(type);
1241 
1242 	return (ok);
1243 }
1244 
1245 static int
decode_cred_extensions(const unsigned char ** buf,size_t * len,fido_cred_ext_t * authdata_ext)1246 decode_cred_extensions(const unsigned char **buf, size_t *len,
1247     fido_cred_ext_t *authdata_ext)
1248 {
1249 	cbor_item_t		*item = NULL;
1250 	struct cbor_load_result	 cbor;
1251 	int			 ok = -1;
1252 
1253 	memset(authdata_ext, 0, sizeof(*authdata_ext));
1254 
1255 	fido_log_xxd(*buf, *len, "%s", __func__);
1256 
1257 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1258 		fido_log_debug("%s: cbor_load", __func__);
1259 		goto fail;
1260 	}
1261 
1262 	if (cbor_isa_map(item) == false ||
1263 	    cbor_map_is_definite(item) == false ||
1264 	    cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
1265 		fido_log_debug("%s: cbor type", __func__);
1266 		goto fail;
1267 	}
1268 
1269 	*buf += cbor.read;
1270 	*len -= cbor.read;
1271 
1272 	ok = 0;
1273 fail:
1274 	if (item != NULL)
1275 		cbor_decref(&item);
1276 
1277 	return (ok);
1278 }
1279 
1280 static int
decode_assert_extension(const cbor_item_t * key,const cbor_item_t * val,void * arg)1281 decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
1282     void *arg)
1283 {
1284 	fido_assert_extattr_t	*authdata_ext = arg;
1285 	char			*type = NULL;
1286 	int			 ok = -1;
1287 
1288 	if (cbor_string_copy(key, &type) < 0) {
1289 		fido_log_debug("%s: cbor type", __func__);
1290 		ok = 0; /* ignore */
1291 		goto out;
1292 	}
1293 
1294 	if (strcmp(type, "hmac-secret") == 0) {
1295 		if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) {
1296 			fido_log_debug("%s: fido_blob_decode", __func__);
1297 			goto out;
1298 		}
1299 		authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
1300 	} else if (strcmp(type, "credBlob") == 0) {
1301 		if (fido_blob_decode(val, &authdata_ext->blob) < 0) {
1302 			fido_log_debug("%s: fido_blob_decode", __func__);
1303 			goto out;
1304 		}
1305 		authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
1306 	}
1307 
1308 	ok = 0;
1309 out:
1310 	free(type);
1311 
1312 	return (ok);
1313 }
1314 
1315 static int
decode_assert_extensions(const unsigned char ** buf,size_t * len,fido_assert_extattr_t * authdata_ext)1316 decode_assert_extensions(const unsigned char **buf, size_t *len,
1317     fido_assert_extattr_t *authdata_ext)
1318 {
1319 	cbor_item_t		*item = NULL;
1320 	struct cbor_load_result	 cbor;
1321 	int			 ok = -1;
1322 
1323 	fido_log_xxd(*buf, *len, "%s", __func__);
1324 
1325 	if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1326 		fido_log_debug("%s: cbor_load", __func__);
1327 		goto fail;
1328 	}
1329 
1330 	if (cbor_isa_map(item) == false ||
1331 	    cbor_map_is_definite(item) == false ||
1332 	    cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
1333 		fido_log_debug("%s: cbor type", __func__);
1334 		goto fail;
1335 	}
1336 
1337 	*buf += cbor.read;
1338 	*len -= cbor.read;
1339 
1340 	ok = 0;
1341 fail:
1342 	if (item != NULL)
1343 		cbor_decref(&item);
1344 
1345 	return (ok);
1346 }
1347 
1348 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)1349 cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1350     fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
1351     fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext)
1352 {
1353 	const unsigned char	*buf = NULL;
1354 	size_t			 len;
1355 	size_t			 alloc_len;
1356 
1357 	if (cbor_isa_bytestring(item) == false ||
1358 	    cbor_bytestring_is_definite(item) == false) {
1359 		fido_log_debug("%s: cbor type", __func__);
1360 		return (-1);
1361 	}
1362 
1363 	if (authdata_cbor->ptr != NULL ||
1364 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1365 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
1366 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1367 		return (-1);
1368 	}
1369 
1370 	buf = cbor_bytestring_handle(item);
1371 	len = cbor_bytestring_length(item);
1372 	fido_log_xxd(buf, len, "%s", __func__);
1373 
1374 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1375 		fido_log_debug("%s: fido_buf_read", __func__);
1376 		return (-1);
1377 	}
1378 
1379 	authdata->sigcount = be32toh(authdata->sigcount);
1380 
1381 	if (attcred != NULL) {
1382 		if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1383 		    decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1384 			return (-1);
1385 	}
1386 
1387 	if (authdata_ext != NULL) {
1388 		if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
1389 		    decode_cred_extensions(&buf, &len, authdata_ext) < 0)
1390 			return (-1);
1391 	}
1392 
1393 	/* XXX we should probably ensure that len == 0 at this point */
1394 
1395 	return (FIDO_OK);
1396 }
1397 
1398 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)1399 cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
1400     fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
1401 {
1402 	const unsigned char	*buf = NULL;
1403 	size_t			 len;
1404 	size_t			 alloc_len;
1405 
1406 	if (cbor_isa_bytestring(item) == false ||
1407 	    cbor_bytestring_is_definite(item) == false) {
1408 		fido_log_debug("%s: cbor type", __func__);
1409 		return (-1);
1410 	}
1411 
1412 	if (authdata_cbor->ptr != NULL ||
1413 	    (authdata_cbor->len = cbor_serialize_alloc(item,
1414 	    &authdata_cbor->ptr, &alloc_len)) == 0) {
1415 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1416 		return (-1);
1417 	}
1418 
1419 	buf = cbor_bytestring_handle(item);
1420 	len = cbor_bytestring_length(item);
1421 
1422 	fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1423 
1424 	if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1425 		fido_log_debug("%s: fido_buf_read", __func__);
1426 		return (-1);
1427 	}
1428 
1429 	authdata->sigcount = be32toh(authdata->sigcount);
1430 
1431 	if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
1432 		if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
1433 			fido_log_debug("%s: decode_assert_extensions",
1434 			    __func__);
1435 			return (-1);
1436 		}
1437 	}
1438 
1439 	/* XXX we should probably ensure that len == 0 at this point */
1440 
1441 	return (FIDO_OK);
1442 }
1443 
1444 static int
decode_x5c(const cbor_item_t * item,void * arg)1445 decode_x5c(const cbor_item_t *item, void *arg)
1446 {
1447 	fido_blob_array_t *x5c = arg;
1448 	fido_blob_t *list_ptr = NULL;
1449 	fido_blob_t x5c_blob;
1450 
1451 	memset(&x5c_blob, 0, sizeof(x5c_blob));
1452 
1453 	if (fido_blob_decode(item, &x5c_blob) < 0) {
1454 		fido_log_debug("%s: fido_blob_decode", __func__);
1455 		return (-1);
1456 	}
1457 
1458 	if (x5c->len == SIZE_MAX) {
1459 		fido_blob_reset(&x5c_blob);
1460 		return (-1);
1461 	}
1462 
1463 	if ((list_ptr = recallocarray(x5c->ptr, x5c->len,
1464 	    x5c->len + 1, sizeof(x5c_blob))) == NULL) {
1465 		fido_blob_reset(&x5c_blob);
1466 		return (-1);
1467 	}
1468 
1469 	list_ptr[x5c->len++] = x5c_blob;
1470 	x5c->ptr = list_ptr;
1471 
1472 	return (0);
1473 }
1474 
1475 static int
decode_x5c_array(const cbor_item_t * item,fido_blob_array_t * arr)1476 decode_x5c_array(const cbor_item_t *item, fido_blob_array_t *arr)
1477 {
1478 	if (arr->len) {
1479 		fido_log_debug("%s: dup", __func__);
1480 		return (-1);
1481 	}
1482 	if (cbor_isa_array(item) == false ||
1483 	    cbor_array_is_definite(item) == false) {
1484 		fido_log_debug("%s: cbor", __func__);
1485 		return (-1);
1486 	}
1487 	return (cbor_array_iter(item, arr, decode_x5c));
1488 }
1489 
1490 static int
decode_attstmt_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1491 decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1492 {
1493 	fido_attstmt_t	*attstmt = 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, "alg")) {
1504 		if (cbor_isa_negint(val) == false ||
1505 		    cbor_get_int(val) > UINT16_MAX) {
1506 			fido_log_debug("%s: alg", __func__);
1507 			goto out;
1508 		}
1509 		attstmt->alg = -(int)cbor_get_int(val) - 1;
1510 		if (attstmt->alg != COSE_ES256 && attstmt->alg != COSE_ES384 &&
1511 		    attstmt->alg != COSE_RS256 && attstmt->alg != COSE_EDDSA &&
1512 		    attstmt->alg != COSE_RS1) {
1513 			fido_log_debug("%s: unsupported attstmt->alg=%d",
1514 			    __func__, attstmt->alg);
1515 			goto out;
1516 		}
1517 	} else if (!strcmp(name, "sig")) {
1518 		if (fido_blob_decode(val, &attstmt->sig) < 0) {
1519 			fido_log_debug("%s: sig", __func__);
1520 			goto out;
1521 		}
1522 	} else if (!strcmp(name, "x5c")) {
1523 		if (decode_x5c_array(val, &attstmt->x5c)) {
1524 			fido_log_debug("%s: x5c", __func__);
1525 			goto out;
1526 		}
1527 	} else if (!strcmp(name, "certInfo")) {
1528 		if (fido_blob_decode(val, &attstmt->certinfo) < 0) {
1529 			fido_log_debug("%s: certinfo", __func__);
1530 			goto out;
1531 		}
1532 	} else if (!strcmp(name, "pubArea")) {
1533 		if (fido_blob_decode(val, &attstmt->pubarea) < 0) {
1534 			fido_log_debug("%s: pubarea", __func__);
1535 			goto out;
1536 		}
1537 	}
1538 
1539 	ok = 0;
1540 out:
1541 	free(name);
1542 
1543 	return (ok);
1544 }
1545 
1546 int
cbor_decode_attstmt(const cbor_item_t * item,fido_attstmt_t * attstmt)1547 cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1548 {
1549 	size_t alloc_len;
1550 
1551 	if (cbor_isa_map(item) == false ||
1552 	    cbor_map_is_definite(item) == false ||
1553 	    cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
1554 		fido_log_debug("%s: cbor type", __func__);
1555 		return (-1);
1556 	}
1557 
1558 	if (attstmt->cbor.ptr != NULL ||
1559 	    (attstmt->cbor.len = cbor_serialize_alloc(item,
1560 	    &attstmt->cbor.ptr, &alloc_len)) == 0) {
1561 		fido_log_debug("%s: cbor_serialize_alloc", __func__);
1562 		return (-1);
1563 	}
1564 
1565 	return (0);
1566 }
1567 
1568 int
cbor_decode_uint64(const cbor_item_t * item,uint64_t * n)1569 cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1570 {
1571 	if (cbor_isa_uint(item) == false) {
1572 		fido_log_debug("%s: cbor type", __func__);
1573 		return (-1);
1574 	}
1575 
1576 	*n = cbor_get_int(item);
1577 
1578 	return (0);
1579 }
1580 
1581 static int
decode_cred_id_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1582 decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1583 {
1584 	fido_blob_t	*id = arg;
1585 	char		*name = NULL;
1586 	int		 ok = -1;
1587 
1588 	if (cbor_string_copy(key, &name) < 0) {
1589 		fido_log_debug("%s: cbor type", __func__);
1590 		ok = 0; /* ignore */
1591 		goto out;
1592 	}
1593 
1594 	if (!strcmp(name, "id"))
1595 		if (fido_blob_decode(val, id) < 0) {
1596 			fido_log_debug("%s: cbor_bytestring_copy", __func__);
1597 			goto out;
1598 		}
1599 
1600 	ok = 0;
1601 out:
1602 	free(name);
1603 
1604 	return (ok);
1605 }
1606 
1607 int
cbor_decode_cred_id(const cbor_item_t * item,fido_blob_t * id)1608 cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1609 {
1610 	if (cbor_isa_map(item) == false ||
1611 	    cbor_map_is_definite(item) == false ||
1612 	    cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
1613 		fido_log_debug("%s: cbor type", __func__);
1614 		return (-1);
1615 	}
1616 
1617 	return (0);
1618 }
1619 
1620 static int
decode_user_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1621 decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1622 {
1623 	fido_user_t	*user = arg;
1624 	char		*name = NULL;
1625 	int		 ok = -1;
1626 
1627 	if (cbor_string_copy(key, &name) < 0) {
1628 		fido_log_debug("%s: cbor type", __func__);
1629 		ok = 0; /* ignore */
1630 		goto out;
1631 	}
1632 
1633 	if (!strcmp(name, "icon")) {
1634 		if (cbor_string_copy(val, &user->icon) < 0) {
1635 			fido_log_debug("%s: icon", __func__);
1636 			goto out;
1637 		}
1638 	} else if (!strcmp(name, "name")) {
1639 		if (cbor_string_copy(val, &user->name) < 0) {
1640 			fido_log_debug("%s: name", __func__);
1641 			goto out;
1642 		}
1643 	} else if (!strcmp(name, "displayName")) {
1644 		if (cbor_string_copy(val, &user->display_name) < 0) {
1645 			fido_log_debug("%s: display_name", __func__);
1646 			goto out;
1647 		}
1648 	} else if (!strcmp(name, "id")) {
1649 		if (fido_blob_decode(val, &user->id) < 0) {
1650 			fido_log_debug("%s: id", __func__);
1651 			goto out;
1652 		}
1653 	}
1654 
1655 	ok = 0;
1656 out:
1657 	free(name);
1658 
1659 	return (ok);
1660 }
1661 
1662 int
cbor_decode_user(const cbor_item_t * item,fido_user_t * user)1663 cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1664 {
1665 	if (cbor_isa_map(item) == false ||
1666 	    cbor_map_is_definite(item) == false ||
1667 	    cbor_map_iter(item, user, decode_user_entry) < 0) {
1668 		fido_log_debug("%s: cbor type", __func__);
1669 		return (-1);
1670 	}
1671 
1672 	return (0);
1673 }
1674 
1675 static int
decode_rp_entity_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)1676 decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1677     void *arg)
1678 {
1679 	fido_rp_t	*rp = arg;
1680 	char		*name = NULL;
1681 	int		 ok = -1;
1682 
1683 	if (cbor_string_copy(key, &name) < 0) {
1684 		fido_log_debug("%s: cbor type", __func__);
1685 		ok = 0; /* ignore */
1686 		goto out;
1687 	}
1688 
1689 	if (!strcmp(name, "id")) {
1690 		if (cbor_string_copy(val, &rp->id) < 0) {
1691 			fido_log_debug("%s: id", __func__);
1692 			goto out;
1693 		}
1694 	} else if (!strcmp(name, "name")) {
1695 		if (cbor_string_copy(val, &rp->name) < 0) {
1696 			fido_log_debug("%s: name", __func__);
1697 			goto out;
1698 		}
1699 	}
1700 
1701 	ok = 0;
1702 out:
1703 	free(name);
1704 
1705 	return (ok);
1706 }
1707 
1708 int
cbor_decode_rp_entity(const cbor_item_t * item,fido_rp_t * rp)1709 cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1710 {
1711 	if (cbor_isa_map(item) == false ||
1712 	    cbor_map_is_definite(item) == false ||
1713 	    cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
1714 		fido_log_debug("%s: cbor type", __func__);
1715 		return (-1);
1716 	}
1717 
1718 	return (0);
1719 }
1720 
1721 int
cbor_decode_bool(const cbor_item_t * item,bool * v)1722 cbor_decode_bool(const cbor_item_t *item, bool *v)
1723 {
1724 	if (cbor_isa_float_ctrl(item) == false ||
1725 	    cbor_float_get_width(item) != CBOR_FLOAT_0 ||
1726 	    cbor_is_bool(item) == false) {
1727 		fido_log_debug("%s: cbor type", __func__);
1728 		return (-1);
1729 	}
1730 
1731 	if (v != NULL)
1732 		*v = cbor_ctrl_value(item) == CBOR_CTRL_TRUE;
1733 
1734 	return (0);
1735 }
1736 
1737 cbor_item_t *
cbor_build_uint(const uint64_t value)1738 cbor_build_uint(const uint64_t value)
1739 {
1740 	if (value <= UINT8_MAX)
1741 		return cbor_build_uint8((uint8_t)value);
1742 	else if (value <= UINT16_MAX)
1743 		return cbor_build_uint16((uint16_t)value);
1744 	else if (value <= UINT32_MAX)
1745 		return cbor_build_uint32((uint32_t)value);
1746 
1747 	return cbor_build_uint64(value);
1748 }
1749 
1750 int
cbor_array_append(cbor_item_t ** array,cbor_item_t * item)1751 cbor_array_append(cbor_item_t **array, cbor_item_t *item)
1752 {
1753 	cbor_item_t **v, *ret;
1754 	size_t n;
1755 
1756 	if ((v = cbor_array_handle(*array)) == NULL ||
1757 	    (n = cbor_array_size(*array)) == SIZE_MAX ||
1758 	    (ret = cbor_new_definite_array(n + 1)) == NULL)
1759 		return -1;
1760 	for (size_t i = 0; i < n; i++) {
1761 		if (cbor_array_push(ret, v[i]) == 0) {
1762 			cbor_decref(&ret);
1763 			return -1;
1764 		}
1765 	}
1766 	if (cbor_array_push(ret, item) == 0) {
1767 		cbor_decref(&ret);
1768 		return -1;
1769 	}
1770 	cbor_decref(array);
1771 	*array = ret;
1772 
1773 	return 0;
1774 }
1775 
1776 int
cbor_array_drop(cbor_item_t ** array,size_t idx)1777 cbor_array_drop(cbor_item_t **array, size_t idx)
1778 {
1779 	cbor_item_t **v, *ret;
1780 	size_t n;
1781 
1782 	if ((v = cbor_array_handle(*array)) == NULL ||
1783 	    (n = cbor_array_size(*array)) == 0 || idx >= n ||
1784 	    (ret = cbor_new_definite_array(n - 1)) == NULL)
1785 		return -1;
1786 	for (size_t i = 0; i < n; i++) {
1787 		if (i != idx && cbor_array_push(ret, v[i]) == 0) {
1788 			cbor_decref(&ret);
1789 			return -1;
1790 		}
1791 	}
1792 	cbor_decref(array);
1793 	*array = ret;
1794 
1795 	return 0;
1796 }
1797