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 "fido.h"
9
10 static int
decode_string(const cbor_item_t * item,void * arg)11 decode_string(const cbor_item_t *item, void *arg)
12 {
13 fido_str_array_t *a = arg;
14 const size_t i = a->len;
15
16 /* keep ptr[x] and len consistent */
17 if (cbor_string_copy(item, &a->ptr[i]) < 0) {
18 fido_log_debug("%s: cbor_string_copy", __func__);
19 return (-1);
20 }
21
22 a->len++;
23
24 return (0);
25 }
26
27 static int
decode_string_array(const cbor_item_t * item,fido_str_array_t * v)28 decode_string_array(const cbor_item_t *item, fido_str_array_t *v)
29 {
30 v->ptr = NULL;
31 v->len = 0;
32
33 if (cbor_isa_array(item) == false ||
34 cbor_array_is_definite(item) == false) {
35 fido_log_debug("%s: cbor type", __func__);
36 return (-1);
37 }
38
39 v->ptr = calloc(cbor_array_size(item), sizeof(char *));
40 if (v->ptr == NULL)
41 return (-1);
42
43 if (cbor_array_iter(item, v, decode_string) < 0) {
44 fido_log_debug("%s: decode_string", __func__);
45 return (-1);
46 }
47
48 return (0);
49 }
50
51 static int
decode_aaguid(const cbor_item_t * item,unsigned char * aaguid,size_t aaguid_len)52 decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
53 {
54 if (cbor_isa_bytestring(item) == false ||
55 cbor_bytestring_is_definite(item) == false ||
56 cbor_bytestring_length(item) != aaguid_len) {
57 fido_log_debug("%s: cbor type", __func__);
58 return (-1);
59 }
60
61 memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
62
63 return (0);
64 }
65
66 static int
decode_option(const cbor_item_t * key,const cbor_item_t * val,void * arg)67 decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
68 {
69 fido_opt_array_t *o = arg;
70 const size_t i = o->len;
71
72 if (cbor_decode_bool(val, NULL) < 0) {
73 fido_log_debug("%s: cbor_decode_bool", __func__);
74 return (0); /* ignore */
75 }
76
77 if (cbor_string_copy(key, &o->name[i]) < 0) {
78 fido_log_debug("%s: cbor_string_copy", __func__);
79 return (0); /* ignore */
80 }
81
82 /* keep name/value and len consistent */
83 o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
84 o->len++;
85
86 return (0);
87 }
88
89 static int
decode_options(const cbor_item_t * item,fido_opt_array_t * o)90 decode_options(const cbor_item_t *item, fido_opt_array_t *o)
91 {
92 o->name = NULL;
93 o->value = NULL;
94 o->len = 0;
95
96 if (cbor_isa_map(item) == false ||
97 cbor_map_is_definite(item) == false) {
98 fido_log_debug("%s: cbor type", __func__);
99 return (-1);
100 }
101
102 o->name = calloc(cbor_map_size(item), sizeof(char *));
103 o->value = calloc(cbor_map_size(item), sizeof(bool));
104 if (o->name == NULL || o->value == NULL)
105 return (-1);
106
107 return (cbor_map_iter(item, o, decode_option));
108 }
109
110 static int
decode_protocol(const cbor_item_t * item,void * arg)111 decode_protocol(const cbor_item_t *item, void *arg)
112 {
113 fido_byte_array_t *p = arg;
114 const size_t i = p->len;
115
116 if (cbor_isa_uint(item) == false ||
117 cbor_int_get_width(item) != CBOR_INT_8) {
118 fido_log_debug("%s: cbor type", __func__);
119 return (-1);
120 }
121
122 /* keep ptr[x] and len consistent */
123 p->ptr[i] = cbor_get_uint8(item);
124 p->len++;
125
126 return (0);
127 }
128
129 static int
decode_protocols(const cbor_item_t * item,fido_byte_array_t * p)130 decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
131 {
132 p->ptr = NULL;
133 p->len = 0;
134
135 if (cbor_isa_array(item) == false ||
136 cbor_array_is_definite(item) == false) {
137 fido_log_debug("%s: cbor type", __func__);
138 return (-1);
139 }
140
141 p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
142 if (p->ptr == NULL)
143 return (-1);
144
145 if (cbor_array_iter(item, p, decode_protocol) < 0) {
146 fido_log_debug("%s: decode_protocol", __func__);
147 return (-1);
148 }
149
150 return (0);
151 }
152
153 static int
decode_algorithm_entry(const cbor_item_t * key,const cbor_item_t * val,void * arg)154 decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val,
155 void *arg)
156 {
157 fido_algo_t *alg = arg;
158 char *name = NULL;
159 int ok = -1;
160
161 if (cbor_string_copy(key, &name) < 0) {
162 fido_log_debug("%s: cbor type", __func__);
163 ok = 0; /* ignore */
164 goto out;
165 }
166
167 if (!strcmp(name, "alg")) {
168 if (cbor_isa_negint(val) == false ||
169 cbor_get_int(val) > INT_MAX || alg->cose != 0) {
170 fido_log_debug("%s: alg", __func__);
171 goto out;
172 }
173 alg->cose = -(int)cbor_get_int(val) - 1;
174 } else if (!strcmp(name, "type")) {
175 if (cbor_string_copy(val, &alg->type) < 0) {
176 fido_log_debug("%s: type", __func__);
177 goto out;
178 }
179 }
180
181 ok = 0;
182 out:
183 free(name);
184
185 return (ok);
186 }
187
188 static int
decode_algorithm(const cbor_item_t * item,void * arg)189 decode_algorithm(const cbor_item_t *item, void *arg)
190 {
191 fido_algo_array_t *aa = arg;
192 const size_t i = aa->len;
193
194 if (cbor_isa_map(item) == false ||
195 cbor_map_is_definite(item) == false) {
196 fido_log_debug("%s: cbor type", __func__);
197 return (-1);
198 }
199
200 memset(&aa->ptr[i], 0, sizeof(aa->ptr[i]));
201
202 if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) {
203 fido_log_debug("%s: decode_algorithm_entry", __func__);
204 fido_algo_free(&aa->ptr[i]);
205 return (-1);
206 }
207
208 /* keep ptr[x] and len consistent */
209 aa->len++;
210
211 return (0);
212 }
213
214 static int
decode_algorithms(const cbor_item_t * item,fido_algo_array_t * aa)215 decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa)
216 {
217 aa->ptr = NULL;
218 aa->len = 0;
219
220 if (cbor_isa_array(item) == false ||
221 cbor_array_is_definite(item) == false) {
222 fido_log_debug("%s: cbor type", __func__);
223 return (-1);
224 }
225
226 aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t));
227 if (aa->ptr == NULL)
228 return (-1);
229
230 if (cbor_array_iter(item, aa, decode_algorithm) < 0) {
231 fido_log_debug("%s: decode_algorithm", __func__);
232 return (-1);
233 }
234
235 return (0);
236 }
237
238 static int
decode_cert(const cbor_item_t * key,const cbor_item_t * val,void * arg)239 decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg)
240 {
241 fido_cert_array_t *c = arg;
242 const size_t i = c->len;
243
244 if (cbor_is_int(val) == false) {
245 fido_log_debug("%s: cbor_is_int", __func__);
246 return (0); /* ignore */
247 }
248
249 if (cbor_string_copy(key, &c->name[i]) < 0) {
250 fido_log_debug("%s: cbor_string_copy", __func__);
251 return (0); /* ignore */
252 }
253
254 /* keep name/value and len consistent */
255 c->value[i] = cbor_get_int(val);
256 c->len++;
257
258 return (0);
259 }
260
261 static int
decode_certs(const cbor_item_t * item,fido_cert_array_t * c)262 decode_certs(const cbor_item_t *item, fido_cert_array_t *c)
263 {
264 c->name = NULL;
265 c->value = NULL;
266 c->len = 0;
267
268 if (cbor_isa_map(item) == false ||
269 cbor_map_is_definite(item) == false) {
270 fido_log_debug("%s: cbor type", __func__);
271 return (-1);
272 }
273
274 c->name = calloc(cbor_map_size(item), sizeof(char *));
275 c->value = calloc(cbor_map_size(item), sizeof(uint64_t));
276 if (c->name == NULL || c->value == NULL)
277 return (-1);
278
279 return (cbor_map_iter(item, c, decode_cert));
280 }
281
282 static int
parse_reply_element(const cbor_item_t * key,const cbor_item_t * val,void * arg)283 parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
284 {
285 fido_cbor_info_t *ci = arg;
286 uint64_t x;
287
288 if (cbor_isa_uint(key) == false ||
289 cbor_int_get_width(key) != CBOR_INT_8) {
290 fido_log_debug("%s: cbor type", __func__);
291 return (0); /* ignore */
292 }
293
294 switch (cbor_get_uint8(key)) {
295 case 1: /* versions */
296 return (decode_string_array(val, &ci->versions));
297 case 2: /* extensions */
298 return (decode_string_array(val, &ci->extensions));
299 case 3: /* aaguid */
300 return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
301 case 4: /* options */
302 return (decode_options(val, &ci->options));
303 case 5: /* maxMsgSize */
304 return (cbor_decode_uint64(val, &ci->maxmsgsiz));
305 case 6: /* pinProtocols */
306 return (decode_protocols(val, &ci->protocols));
307 case 7: /* maxCredentialCountInList */
308 return (cbor_decode_uint64(val, &ci->maxcredcntlst));
309 case 8: /* maxCredentialIdLength */
310 return (cbor_decode_uint64(val, &ci->maxcredidlen));
311 case 9: /* transports */
312 return (decode_string_array(val, &ci->transports));
313 case 10: /* algorithms */
314 return (decode_algorithms(val, &ci->algorithms));
315 case 11: /* maxSerializedLargeBlobArray */
316 return (cbor_decode_uint64(val, &ci->maxlargeblob));
317 case 12: /* forcePINChange */
318 return (cbor_decode_bool(val, &ci->new_pin_reqd));
319 case 13: /* minPINLength */
320 return (cbor_decode_uint64(val, &ci->minpinlen));
321 case 14: /* fwVersion */
322 return (cbor_decode_uint64(val, &ci->fwversion));
323 case 15: /* maxCredBlobLen */
324 return (cbor_decode_uint64(val, &ci->maxcredbloblen));
325 case 16: /* maxRPIDsForSetMinPINLength */
326 return (cbor_decode_uint64(val, &ci->maxrpid_minlen));
327 case 17: /* preferredPlatformUvAttempts */
328 return (cbor_decode_uint64(val, &ci->uv_attempts));
329 case 18: /* uvModality */
330 return (cbor_decode_uint64(val, &ci->uv_modality));
331 case 19: /* certifications */
332 return (decode_certs(val, &ci->certs));
333 case 20: /* remainingDiscoverableCredentials */
334 if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) {
335 fido_log_debug("%s: cbor_decode_uint64", __func__);
336 return (-1);
337 }
338 ci->rk_remaining = (int64_t)x;
339 return (0);
340 default: /* ignore */
341 fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key));
342 return (0);
343 }
344 }
345
346 static int
fido_dev_get_cbor_info_tx(fido_dev_t * dev,int * ms)347 fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
348 {
349 const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
350
351 fido_log_debug("%s: dev=%p", __func__, (void *)dev);
352
353 if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
354 fido_log_debug("%s: fido_tx", __func__);
355 return (FIDO_ERR_TX);
356 }
357
358 return (FIDO_OK);
359 }
360
361 static int
fido_dev_get_cbor_info_rx(fido_dev_t * dev,fido_cbor_info_t * ci,int * ms)362 fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
363 {
364 unsigned char *msg;
365 int msglen;
366 int r;
367
368 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
369 (void *)ci, *ms);
370
371 fido_cbor_info_reset(ci);
372
373 if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
374 r = FIDO_ERR_INTERNAL;
375 goto out;
376 }
377
378 if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
379 fido_log_debug("%s: fido_rx", __func__);
380 r = FIDO_ERR_RX;
381 goto out;
382 }
383
384 r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element);
385 out:
386 freezero(msg, FIDO_MAXMSG);
387
388 return (r);
389 }
390
391 int
fido_dev_get_cbor_info_wait(fido_dev_t * dev,fido_cbor_info_t * ci,int * ms)392 fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
393 {
394 int r;
395
396 #ifdef USE_WINHELLO
397 if (dev->flags & FIDO_DEV_WINHELLO)
398 return (fido_winhello_get_cbor_info(dev, ci));
399 #endif
400 if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK ||
401 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
402 return (r);
403
404 return (FIDO_OK);
405 }
406
407 int
fido_dev_get_cbor_info(fido_dev_t * dev,fido_cbor_info_t * ci)408 fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
409 {
410 int ms = dev->timeout_ms;
411
412 return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
413 }
414
415 /*
416 * get/set functions for fido_cbor_info_t; always at the end of the file
417 */
418
419 fido_cbor_info_t *
fido_cbor_info_new(void)420 fido_cbor_info_new(void)
421 {
422 fido_cbor_info_t *ci;
423
424 if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL)
425 return (NULL);
426
427 fido_cbor_info_reset(ci);
428
429 return (ci);
430 }
431
432 void
fido_cbor_info_reset(fido_cbor_info_t * ci)433 fido_cbor_info_reset(fido_cbor_info_t *ci)
434 {
435 fido_str_array_free(&ci->versions);
436 fido_str_array_free(&ci->extensions);
437 fido_str_array_free(&ci->transports);
438 fido_opt_array_free(&ci->options);
439 fido_byte_array_free(&ci->protocols);
440 fido_algo_array_free(&ci->algorithms);
441 fido_cert_array_free(&ci->certs);
442 ci->rk_remaining = -1;
443 }
444
445 void
fido_cbor_info_free(fido_cbor_info_t ** ci_p)446 fido_cbor_info_free(fido_cbor_info_t **ci_p)
447 {
448 fido_cbor_info_t *ci;
449
450 if (ci_p == NULL || (ci = *ci_p) == NULL)
451 return;
452 fido_cbor_info_reset(ci);
453 free(ci);
454 *ci_p = NULL;
455 }
456
457 char **
fido_cbor_info_versions_ptr(const fido_cbor_info_t * ci)458 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
459 {
460 return (ci->versions.ptr);
461 }
462
463 size_t
fido_cbor_info_versions_len(const fido_cbor_info_t * ci)464 fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
465 {
466 return (ci->versions.len);
467 }
468
469 char **
fido_cbor_info_extensions_ptr(const fido_cbor_info_t * ci)470 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
471 {
472 return (ci->extensions.ptr);
473 }
474
475 size_t
fido_cbor_info_extensions_len(const fido_cbor_info_t * ci)476 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
477 {
478 return (ci->extensions.len);
479 }
480
481 char **
fido_cbor_info_transports_ptr(const fido_cbor_info_t * ci)482 fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
483 {
484 return (ci->transports.ptr);
485 }
486
487 size_t
fido_cbor_info_transports_len(const fido_cbor_info_t * ci)488 fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
489 {
490 return (ci->transports.len);
491 }
492
493 const unsigned char *
fido_cbor_info_aaguid_ptr(const fido_cbor_info_t * ci)494 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
495 {
496 return (ci->aaguid);
497 }
498
499 size_t
fido_cbor_info_aaguid_len(const fido_cbor_info_t * ci)500 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
501 {
502 return (sizeof(ci->aaguid));
503 }
504
505 char **
fido_cbor_info_options_name_ptr(const fido_cbor_info_t * ci)506 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
507 {
508 return (ci->options.name);
509 }
510
511 const bool *
fido_cbor_info_options_value_ptr(const fido_cbor_info_t * ci)512 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
513 {
514 return (ci->options.value);
515 }
516
517 size_t
fido_cbor_info_options_len(const fido_cbor_info_t * ci)518 fido_cbor_info_options_len(const fido_cbor_info_t *ci)
519 {
520 return (ci->options.len);
521 }
522
523 uint64_t
fido_cbor_info_maxcredbloblen(const fido_cbor_info_t * ci)524 fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
525 {
526 return (ci->maxcredbloblen);
527 }
528
529 uint64_t
fido_cbor_info_maxmsgsiz(const fido_cbor_info_t * ci)530 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
531 {
532 return (ci->maxmsgsiz);
533 }
534
535 uint64_t
fido_cbor_info_maxcredcntlst(const fido_cbor_info_t * ci)536 fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
537 {
538 return (ci->maxcredcntlst);
539 }
540
541 uint64_t
fido_cbor_info_maxcredidlen(const fido_cbor_info_t * ci)542 fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
543 {
544 return (ci->maxcredidlen);
545 }
546
547 uint64_t
fido_cbor_info_maxlargeblob(const fido_cbor_info_t * ci)548 fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
549 {
550 return (ci->maxlargeblob);
551 }
552
553 uint64_t
fido_cbor_info_fwversion(const fido_cbor_info_t * ci)554 fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
555 {
556 return (ci->fwversion);
557 }
558
559 uint64_t
fido_cbor_info_minpinlen(const fido_cbor_info_t * ci)560 fido_cbor_info_minpinlen(const fido_cbor_info_t *ci)
561 {
562 return (ci->minpinlen);
563 }
564
565 uint64_t
fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t * ci)566 fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci)
567 {
568 return (ci->maxrpid_minlen);
569 }
570
571 uint64_t
fido_cbor_info_uv_attempts(const fido_cbor_info_t * ci)572 fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci)
573 {
574 return (ci->uv_attempts);
575 }
576
577 uint64_t
fido_cbor_info_uv_modality(const fido_cbor_info_t * ci)578 fido_cbor_info_uv_modality(const fido_cbor_info_t *ci)
579 {
580 return (ci->uv_modality);
581 }
582
583 int64_t
fido_cbor_info_rk_remaining(const fido_cbor_info_t * ci)584 fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci)
585 {
586 return (ci->rk_remaining);
587 }
588
589 const uint8_t *
fido_cbor_info_protocols_ptr(const fido_cbor_info_t * ci)590 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
591 {
592 return (ci->protocols.ptr);
593 }
594
595 size_t
fido_cbor_info_protocols_len(const fido_cbor_info_t * ci)596 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
597 {
598 return (ci->protocols.len);
599 }
600
601 size_t
fido_cbor_info_algorithm_count(const fido_cbor_info_t * ci)602 fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
603 {
604 return (ci->algorithms.len);
605 }
606
607 const char *
fido_cbor_info_algorithm_type(const fido_cbor_info_t * ci,size_t idx)608 fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx)
609 {
610 if (idx >= ci->algorithms.len)
611 return (NULL);
612
613 return (ci->algorithms.ptr[idx].type);
614 }
615
616 int
fido_cbor_info_algorithm_cose(const fido_cbor_info_t * ci,size_t idx)617 fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx)
618 {
619 if (idx >= ci->algorithms.len)
620 return (0);
621
622 return (ci->algorithms.ptr[idx].cose);
623 }
624
625 bool
fido_cbor_info_new_pin_required(const fido_cbor_info_t * ci)626 fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci)
627 {
628 return (ci->new_pin_reqd);
629 }
630
631 char **
fido_cbor_info_certs_name_ptr(const fido_cbor_info_t * ci)632 fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci)
633 {
634 return (ci->certs.name);
635 }
636
637 const uint64_t *
fido_cbor_info_certs_value_ptr(const fido_cbor_info_t * ci)638 fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci)
639 {
640 return (ci->certs.value);
641 }
642
643 size_t
fido_cbor_info_certs_len(const fido_cbor_info_t * ci)644 fido_cbor_info_certs_len(const fido_cbor_info_t *ci)
645 {
646 return (ci->certs.len);
647 }
648