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_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