xref: /freebsd/contrib/libfido2/src/info.c (revision f5463265955b829775bbb32e1fd0bc11dafc36ce)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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 **
458 fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
459 {
460 	return (ci->versions.ptr);
461 }
462 
463 size_t
464 fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
465 {
466 	return (ci->versions.len);
467 }
468 
469 char **
470 fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
471 {
472 	return (ci->extensions.ptr);
473 }
474 
475 size_t
476 fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
477 {
478 	return (ci->extensions.len);
479 }
480 
481 char **
482 fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
483 {
484 	return (ci->transports.ptr);
485 }
486 
487 size_t
488 fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
489 {
490 	return (ci->transports.len);
491 }
492 
493 const unsigned char *
494 fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
495 {
496 	return (ci->aaguid);
497 }
498 
499 size_t
500 fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
501 {
502 	return (sizeof(ci->aaguid));
503 }
504 
505 char **
506 fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
507 {
508 	return (ci->options.name);
509 }
510 
511 const bool *
512 fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
513 {
514 	return (ci->options.value);
515 }
516 
517 size_t
518 fido_cbor_info_options_len(const fido_cbor_info_t *ci)
519 {
520 	return (ci->options.len);
521 }
522 
523 uint64_t
524 fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
525 {
526 	return (ci->maxcredbloblen);
527 }
528 
529 uint64_t
530 fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
531 {
532 	return (ci->maxmsgsiz);
533 }
534 
535 uint64_t
536 fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
537 {
538 	return (ci->maxcredcntlst);
539 }
540 
541 uint64_t
542 fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
543 {
544 	return (ci->maxcredidlen);
545 }
546 
547 uint64_t
548 fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
549 {
550 	return (ci->maxlargeblob);
551 }
552 
553 uint64_t
554 fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
555 {
556 	return (ci->fwversion);
557 }
558 
559 uint64_t
560 fido_cbor_info_minpinlen(const fido_cbor_info_t *ci)
561 {
562 	return (ci->minpinlen);
563 }
564 
565 uint64_t
566 fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci)
567 {
568 	return (ci->maxrpid_minlen);
569 }
570 
571 uint64_t
572 fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci)
573 {
574 	return (ci->uv_attempts);
575 }
576 
577 uint64_t
578 fido_cbor_info_uv_modality(const fido_cbor_info_t *ci)
579 {
580 	return (ci->uv_modality);
581 }
582 
583 int64_t
584 fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci)
585 {
586 	return (ci->rk_remaining);
587 }
588 
589 const uint8_t *
590 fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
591 {
592 	return (ci->protocols.ptr);
593 }
594 
595 size_t
596 fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
597 {
598 	return (ci->protocols.len);
599 }
600 
601 size_t
602 fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
603 {
604 	return (ci->algorithms.len);
605 }
606 
607 const char *
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
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
626 fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci)
627 {
628 	return (ci->new_pin_reqd);
629 }
630 
631 char **
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 *
638 fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci)
639 {
640 	return (ci->certs.value);
641 }
642 
643 size_t
644 fido_cbor_info_certs_len(const fido_cbor_info_t *ci)
645 {
646 	return (ci->certs.len);
647 }
648