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