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(¶m);
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