xref: /freebsd/contrib/libfido2/tools/token.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 
17 #include "../openbsd-compat/openbsd-compat.h"
18 #include "extern.h"
19 
20 static void
21 format_flags(char *ret, size_t retlen, uint8_t flags)
22 {
23 	memset(ret, 0, retlen);
24 
25 	if (flags & FIDO_CAP_WINK) {
26 		if (strlcat(ret, "wink,", retlen) >= retlen)
27 			goto toolong;
28 	} else {
29 		if (strlcat(ret, "nowink,", retlen) >= retlen)
30 			goto toolong;
31 	}
32 
33 	if (flags & FIDO_CAP_CBOR) {
34 		if (strlcat(ret, " cbor,", retlen) >= retlen)
35 			goto toolong;
36 	} else {
37 		if (strlcat(ret, " nocbor,", retlen) >= retlen)
38 			goto toolong;
39 	}
40 
41 	if (flags & FIDO_CAP_NMSG) {
42 		if (strlcat(ret, " nomsg", retlen) >= retlen)
43 			goto toolong;
44 	} else {
45 		if (strlcat(ret, " msg", retlen) >= retlen)
46 			goto toolong;
47 	}
48 
49 	return;
50 toolong:
51 	strlcpy(ret, "toolong", retlen);
52 }
53 
54 static void
55 print_attr(const fido_dev_t *dev)
56 {
57 	char flags_txt[128];
58 
59 	printf("proto: 0x%02x\n", fido_dev_protocol(dev));
60 	printf("major: 0x%02x\n", fido_dev_major(dev));
61 	printf("minor: 0x%02x\n", fido_dev_minor(dev));
62 	printf("build: 0x%02x\n", fido_dev_build(dev));
63 
64 	format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
65 	printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
66 }
67 
68 static void
69 print_str_array(const char *label, char * const *sa, size_t len)
70 {
71 	if (len == 0)
72 		return;
73 
74 	printf("%s strings: ", label);
75 
76 	for (size_t i = 0; i < len; i++)
77 		printf("%s%s", i > 0 ? ", " : "", sa[i]);
78 
79 	printf("\n");
80 }
81 
82 static void
83 print_opt_array(const char *label, char * const *name, const bool *value,
84     size_t len)
85 {
86 	if (len == 0)
87 		return;
88 
89 	printf("%s: ", label);
90 
91 	for (size_t i = 0; i < len; i++)
92 		printf("%s%s%s", i > 0 ? ", " : "",
93 		    value[i] ? "" : "no", name[i]);
94 
95 	printf("\n");
96 }
97 
98 static void
99 print_cert_array(const char *label, char * const *name, const uint64_t *value,
100     size_t len)
101 {
102 	if (len == 0)
103 		return;
104 
105 	printf("%s: ", label);
106 
107 	for (size_t i = 0; i < len; i++)
108 		printf("%s%s %llu", i > 0 ? ", " : "", name[i],
109 		    (unsigned long long)value[i]);
110 
111 	printf("\n");
112 }
113 
114 static void
115 print_algorithms(const fido_cbor_info_t *ci)
116 {
117 	const char *cose, *type;
118 	size_t len;
119 
120 	if ((len = fido_cbor_info_algorithm_count(ci)) == 0)
121 		return;
122 
123 	printf("algorithms: ");
124 
125 	for (size_t i = 0; i < len; i++) {
126 		cose = type = "unknown";
127 		switch (fido_cbor_info_algorithm_cose(ci, i)) {
128 		case COSE_ES256:
129 			cose = "es256";
130 			break;
131 		case COSE_ES384:
132 			cose = "es384";
133 			break;
134 		case COSE_RS256:
135 			cose = "rs256";
136 			break;
137 		case COSE_EDDSA:
138 			cose = "eddsa";
139 			break;
140 		}
141 		if (fido_cbor_info_algorithm_type(ci, i) != NULL)
142 			type = fido_cbor_info_algorithm_type(ci, i);
143 		printf("%s%s (%s)", i > 0 ? ", " : "", cose, type);
144 	}
145 
146 	printf("\n");
147 }
148 
149 static void
150 print_aaguid(const unsigned char *buf, size_t buflen)
151 {
152 	printf("aaguid: ");
153 
154 	while (buflen--)
155 		printf("%02x", *buf++);
156 
157 	printf("\n");
158 }
159 
160 static void
161 print_maxmsgsiz(uint64_t maxmsgsiz)
162 {
163 	printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
164 }
165 
166 static void
167 print_maxcredcntlst(uint64_t maxcredcntlst)
168 {
169 	printf("maxcredcntlst: %d\n", (int)maxcredcntlst);
170 }
171 
172 static void
173 print_maxcredidlen(uint64_t maxcredidlen)
174 {
175 	printf("maxcredlen: %d\n", (int)maxcredidlen);
176 }
177 
178 static void
179 print_maxlargeblob(uint64_t maxlargeblob)
180 {
181 	printf("maxlargeblob: %d\n", (int)maxlargeblob);
182 }
183 
184 static void
185 print_maxrpid_minpinlen(uint64_t maxrpid)
186 {
187 	if (maxrpid > 0)
188 		printf("maxrpids in minpinlen: %d\n", (int)maxrpid);
189 }
190 
191 static void
192 print_minpinlen(uint64_t minpinlen)
193 {
194 	if (minpinlen > 0)
195 		printf("minpinlen: %d\n", (int)minpinlen);
196 }
197 
198 static void
199 print_uv_attempts(uint64_t uv_attempts)
200 {
201 	if (uv_attempts > 0)
202 		printf("platform uv attempt(s): %d\n", (int)uv_attempts);
203 }
204 
205 static void
206 print_uv_modality(uint64_t uv_modality)
207 {
208 	uint64_t mode;
209 	bool printed = false;
210 
211 	if (uv_modality == 0)
212 		return;
213 
214 	printf("uv modality: 0x%x (", (int)uv_modality);
215 
216 	for (size_t i = 0; i < 64; i++) {
217 		mode = 1ULL << i;
218 		if ((uv_modality & mode) == 0)
219 			continue;
220 		if (printed)
221 			printf(", ");
222 		switch (mode) {
223 		case FIDO_UV_MODE_TUP:
224 			printf("test of user presence");
225 			break;
226 		case FIDO_UV_MODE_FP:
227 			printf("fingerprint check");
228 			break;
229 		case FIDO_UV_MODE_PIN:
230 			printf("pin check");
231 			break;
232 		case FIDO_UV_MODE_VOICE:
233 			printf("voice recognition");
234 			break;
235 		case FIDO_UV_MODE_FACE:
236 			printf("face recognition");
237 			break;
238 		case FIDO_UV_MODE_LOCATION:
239 			printf("location check");
240 			break;
241 		case FIDO_UV_MODE_EYE:
242 			printf("eyeprint check");
243 			break;
244 		case FIDO_UV_MODE_DRAWN:
245 			printf("drawn pattern check");
246 			break;
247 		case FIDO_UV_MODE_HAND:
248 			printf("handprint verification");
249 			break;
250 		case FIDO_UV_MODE_NONE:
251 			printf("none");
252 			break;
253 		case FIDO_UV_MODE_ALL:
254 			printf("all required");
255 			break;
256 		case FIDO_UV_MODE_EXT_PIN:
257 			printf("external pin");
258 			break;
259 		case FIDO_UV_MODE_EXT_DRAWN:
260 			printf("external drawn pattern check");
261 			break;
262 		default:
263 			printf("unknown 0x%llx", (unsigned long long)mode);
264 			break;
265 		}
266 		printed = true;
267 	}
268 
269 	printf(")\n");
270 }
271 
272 static void
273 print_rk_remaining(int64_t rk_remaining)
274 {
275 	if (rk_remaining != -1)
276 		printf("remaining rk(s): %d\n", (int)rk_remaining);
277 }
278 
279 static void
280 print_fwversion(uint64_t fwversion)
281 {
282 	printf("fwversion: 0x%x\n", (int)fwversion);
283 }
284 
285 static void
286 print_byte_array(const char *label, const uint8_t *ba, size_t len)
287 {
288 	if (len == 0)
289 		return;
290 
291 	printf("%s: ", label);
292 
293 	for (size_t i = 0; i < len; i++)
294 		printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
295 
296 	printf("\n");
297 }
298 
299 int
300 token_info(int argc, char **argv, char *path)
301 {
302 	char			*cred_id = NULL;
303 	char			*rp_id = NULL;
304 	fido_cbor_info_t	*ci = NULL;
305 	fido_dev_t		*dev = NULL;
306 	int			 ch;
307 	int			 credman = 0;
308 	int			 r;
309 	int			 retrycnt;
310 
311 	optind = 1;
312 
313 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
314 		switch (ch) {
315 		case 'c':
316 			credman = 1;
317 			break;
318 		case 'i':
319 			cred_id = optarg;
320 			break;
321 		case 'k':
322 			rp_id = optarg;
323 			break;
324 		default:
325 			break; /* ignore */
326 		}
327 	}
328 
329 	if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
330 		usage();
331 
332 	dev = open_dev(path);
333 
334 	if (credman)
335 		return (credman_get_metadata(dev, path));
336 	if (cred_id && rp_id)
337 		return (credman_print_rk(dev, path, rp_id, cred_id));
338 	if (cred_id || rp_id)
339 		usage();
340 
341 	print_attr(dev);
342 
343 	if (fido_dev_is_fido2(dev) == false)
344 		goto end;
345 	if ((ci = fido_cbor_info_new()) == NULL)
346 		errx(1, "fido_cbor_info_new");
347 	if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
348 		errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
349 
350 	/* print supported protocol versions */
351 	print_str_array("version", fido_cbor_info_versions_ptr(ci),
352 	    fido_cbor_info_versions_len(ci));
353 
354 	/* print supported extensions */
355 	print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
356 	    fido_cbor_info_extensions_len(ci));
357 
358 	/* print supported transports */
359 	print_str_array("transport", fido_cbor_info_transports_ptr(ci),
360 	    fido_cbor_info_transports_len(ci));
361 
362 	/* print supported algorithms */
363 	print_algorithms(ci);
364 
365 	/* print aaguid */
366 	print_aaguid(fido_cbor_info_aaguid_ptr(ci),
367 	    fido_cbor_info_aaguid_len(ci));
368 
369 	/* print supported options */
370 	print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
371 	    fido_cbor_info_options_value_ptr(ci),
372 	    fido_cbor_info_options_len(ci));
373 
374 	/* print certifications */
375 	print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci),
376 	    fido_cbor_info_certs_value_ptr(ci),
377 	    fido_cbor_info_certs_len(ci));
378 
379 	/* print firmware version */
380 	print_fwversion(fido_cbor_info_fwversion(ci));
381 
382 	/* print maximum message size */
383 	print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
384 
385 	/* print maximum number of credentials allowed in credential lists */
386 	print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci));
387 
388 	/* print maximum length of a credential ID */
389 	print_maxcredidlen(fido_cbor_info_maxcredidlen(ci));
390 
391 	/* print maximum length of serialized largeBlob array */
392 	print_maxlargeblob(fido_cbor_info_maxlargeblob(ci));
393 
394 	/* print maximum number of RP IDs in fido_dev_set_pin_minlen_rpid() */
395 	print_maxrpid_minpinlen(fido_cbor_info_maxrpid_minpinlen(ci));
396 
397 	/* print estimated number of resident credentials */
398 	print_rk_remaining(fido_cbor_info_rk_remaining(ci));
399 
400 	/* print minimum pin length */
401 	print_minpinlen(fido_cbor_info_minpinlen(ci));
402 
403 	/* print supported pin protocols */
404 	print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
405 	    fido_cbor_info_protocols_len(ci));
406 
407 	if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK)
408 		printf("pin retries: undefined\n");
409 	else
410 		printf("pin retries: %d\n", retrycnt);
411 
412 	printf("pin change required: %s\n",
413 	    fido_cbor_info_new_pin_required(ci) ? "true" : "false");
414 
415 	if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK)
416 		printf("uv retries: undefined\n");
417 	else
418 		printf("uv retries: %d\n", retrycnt);
419 
420 	/* print platform uv attempts */
421 	print_uv_attempts(fido_cbor_info_uv_attempts(ci));
422 
423 	/* print supported uv mechanisms */
424 	print_uv_modality(fido_cbor_info_uv_modality(ci));
425 
426 	bio_info(dev);
427 
428 	fido_cbor_info_free(&ci);
429 end:
430 	fido_dev_close(dev);
431 	fido_dev_free(&dev);
432 
433 	exit(0);
434 }
435 
436 int
437 token_reset(char *path)
438 {
439 	fido_dev_t *dev = NULL;
440 	int r;
441 
442 	if (path == NULL)
443 		usage();
444 
445 	dev = open_dev(path);
446 	if ((r = fido_dev_reset(dev)) != FIDO_OK)
447 		errx(1, "fido_dev_reset: %s", fido_strerr(r));
448 
449 	fido_dev_close(dev);
450 	fido_dev_free(&dev);
451 
452 	exit(0);
453 }
454 
455 int
456 token_get(int argc, char **argv, char *path)
457 {
458 	char	*id = NULL;
459 	char	*key = NULL;
460 	char	*name = NULL;
461 	int	 blob = 0;
462 	int	 ch;
463 
464 	optind = 1;
465 
466 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
467 		switch (ch) {
468 		case 'b':
469 			blob = 1;
470 			break;
471 		case 'i':
472 			id = optarg;
473 			break;
474 		case 'k':
475 			key = optarg;
476 			break;
477 		case 'n':
478 			name = optarg;
479 			break;
480 		default:
481 			break; /* ignore */
482 		}
483 	}
484 
485 	argc -= optind;
486 	argv += optind;
487 
488 	if (blob == 0 || argc != 2)
489 		usage();
490 
491 	return blob_get(path, key, name, id, argv[0]);
492 }
493 
494 int
495 token_set(int argc, char **argv, char *path)
496 {
497 	char	*id = NULL;
498 	char	*key = NULL;
499 	char	*len = NULL;
500 	char	*display_name = NULL;
501 	char	*name = NULL;
502 	char	*rpid = NULL;
503 	int	 blob = 0;
504 	int	 cred = 0;
505 	int	 ch;
506 	int	 enroll = 0;
507 	int	 ea = 0;
508 	int	 uv = 0;
509 	bool	 force = false;
510 
511 	optind = 1;
512 
513 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
514 		switch (ch) {
515 		case 'a':
516 			ea = 1;
517 			break;
518 		case 'b':
519 			blob = 1;
520 			break;
521 		case 'c':
522 			cred = 1;
523 			break;
524 		case 'e':
525 			enroll = 1;
526 			break;
527 		case 'f':
528 			force = true;
529 			break;
530 		case 'i':
531 			id = optarg;
532 			break;
533 		case 'k':
534 			key = optarg;
535 			break;
536 		case 'l':
537 			len = optarg;
538 			break;
539 		case 'p':
540 			display_name = optarg;
541 			break;
542 		case 'm':
543 			rpid = optarg;
544 			break;
545 		case 'n':
546 			name = optarg;
547 			break;
548 		case 'u':
549 			uv = 1;
550 			break;
551 		default:
552 			break; /* ignore */
553 		}
554 	}
555 
556 	argc -= optind;
557 	argv += optind;
558 
559 	if (path == NULL)
560 		usage();
561 
562 	if (blob) {
563 		if (argc != 2)
564 			usage();
565 		return (blob_set(path, key, name, id, argv[0]));
566 	}
567 
568 	if (cred) {
569 		if (!id || !key)
570 			usage();
571 		if (!name && !display_name)
572 			usage();
573 		return (credman_update_rk(path, key, id, name, display_name));
574 	}
575 
576 	if (enroll) {
577 		if (ea || uv)
578 			usage();
579 		if (id && name)
580 			return (bio_set_name(path, id, name));
581 		if (!id && !name)
582 			return (bio_enroll(path));
583 		usage();
584 	}
585 
586 	if (ea) {
587 		if (uv)
588 			usage();
589 		return (config_entattest(path));
590 	}
591 
592 	if (len)
593 		return (config_pin_minlen(path, len));
594 	if (rpid)
595 		return (config_pin_minlen_rpid(path, rpid));
596 	if (force)
597 		return (config_force_pin_change(path));
598 	if (uv)
599 		return (config_always_uv(path, 1));
600 
601 	return (pin_set(path));
602 }
603 
604 int
605 token_list(int argc, char **argv, char *path)
606 {
607 	fido_dev_info_t *devlist;
608 	size_t ndevs;
609 	const char *rp_id = NULL;
610 	int blobs = 0;
611 	int enrolls = 0;
612 	int keys = 0;
613 	int rplist = 0;
614 	int ch;
615 	int r;
616 
617 	optind = 1;
618 
619 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
620 		switch (ch) {
621 		case 'b':
622 			blobs = 1;
623 			break;
624 		case 'e':
625 			enrolls = 1;
626 			break;
627 		case 'k':
628 			keys = 1;
629 			rp_id = optarg;
630 			break;
631 		case 'r':
632 			rplist = 1;
633 			break;
634 		default:
635 			break; /* ignore */
636 		}
637 	}
638 
639 	if (blobs || enrolls || keys || rplist) {
640 		if (path == NULL)
641 			usage();
642 		if (blobs)
643 			return (blob_list(path));
644 		if (enrolls)
645 			return (bio_list(path));
646 		if (keys)
647 			return (credman_list_rk(path, rp_id));
648 		if (rplist)
649 			return (credman_list_rp(path));
650 		/* NOTREACHED */
651 	}
652 
653 	if ((devlist = fido_dev_info_new(64)) == NULL)
654 		errx(1, "fido_dev_info_new");
655 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
656 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
657 
658 	for (size_t i = 0; i < ndevs; i++) {
659 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
660 		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
661 		    fido_dev_info_path(di),
662 		    (uint16_t)fido_dev_info_vendor(di),
663 		    (uint16_t)fido_dev_info_product(di),
664 		    fido_dev_info_manufacturer_string(di),
665 		    fido_dev_info_product_string(di));
666 	}
667 
668 	fido_dev_info_free(&devlist, ndevs);
669 
670 	exit(0);
671 }
672 
673 int
674 token_delete(int argc, char **argv, char *path)
675 {
676 	char		*id = NULL;
677 	char		*key = NULL;
678 	char		*name = NULL;
679 	int		 blob = 0;
680 	int		 ch;
681 	int		 enroll = 0;
682 	int		 uv = 0;
683 
684 	optind = 1;
685 
686 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
687 		switch (ch) {
688 		case 'b':
689 			blob = 1;
690 			break;
691 		case 'e':
692 			enroll = 1;
693 			break;
694 		case 'i':
695 			id = optarg;
696 			break;
697 		case 'k':
698 			key = optarg;
699 			break;
700 		case 'n':
701 			name = optarg;
702 			break;
703 		case 'u':
704 			uv = 1;
705 			break;
706 		default:
707 			break; /* ignore */
708 		}
709 	}
710 
711 	if (path == NULL)
712 		usage();
713 
714 	if (blob)
715 		return (blob_delete(path, key, name, id));
716 
717 	if (id) {
718 		if (uv)
719 			usage();
720 		if (enroll == 0)
721 			return (credman_delete_rk(path, id));
722 		return (bio_delete(path, id));
723 	}
724 
725 	if (uv == 0)
726 		usage();
727 
728 	return (config_always_uv(path, 0));
729 }
730