xref: /freebsd/contrib/libfido2/tools/token.c (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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_maxcredblob(uint64_t maxcredblob)
174 {
175 	printf("maxcredblob: %d\n", (int)maxcredblob);
176 }
177 
178 static void
179 print_maxcredidlen(uint64_t maxcredidlen)
180 {
181 	printf("maxcredlen: %d\n", (int)maxcredidlen);
182 }
183 
184 static void
185 print_maxlargeblob(uint64_t maxlargeblob)
186 {
187 	printf("maxlargeblob: %d\n", (int)maxlargeblob);
188 }
189 
190 static void
191 print_maxrpid_minpinlen(uint64_t maxrpid)
192 {
193 	if (maxrpid > 0)
194 		printf("maxrpids in minpinlen: %d\n", (int)maxrpid);
195 }
196 
197 static void
198 print_minpinlen(uint64_t minpinlen)
199 {
200 	if (minpinlen > 0)
201 		printf("minpinlen: %d\n", (int)minpinlen);
202 }
203 
204 static void
205 print_uv_attempts(uint64_t uv_attempts)
206 {
207 	if (uv_attempts > 0)
208 		printf("platform uv attempt(s): %d\n", (int)uv_attempts);
209 }
210 
211 static void
212 print_uv_modality(uint64_t uv_modality)
213 {
214 	uint64_t mode;
215 	bool printed = false;
216 
217 	if (uv_modality == 0)
218 		return;
219 
220 	printf("uv modality: 0x%x (", (int)uv_modality);
221 
222 	for (size_t i = 0; i < 64; i++) {
223 		mode = 1ULL << i;
224 		if ((uv_modality & mode) == 0)
225 			continue;
226 		if (printed)
227 			printf(", ");
228 		switch (mode) {
229 		case FIDO_UV_MODE_TUP:
230 			printf("test of user presence");
231 			break;
232 		case FIDO_UV_MODE_FP:
233 			printf("fingerprint check");
234 			break;
235 		case FIDO_UV_MODE_PIN:
236 			printf("pin check");
237 			break;
238 		case FIDO_UV_MODE_VOICE:
239 			printf("voice recognition");
240 			break;
241 		case FIDO_UV_MODE_FACE:
242 			printf("face recognition");
243 			break;
244 		case FIDO_UV_MODE_LOCATION:
245 			printf("location check");
246 			break;
247 		case FIDO_UV_MODE_EYE:
248 			printf("eyeprint check");
249 			break;
250 		case FIDO_UV_MODE_DRAWN:
251 			printf("drawn pattern check");
252 			break;
253 		case FIDO_UV_MODE_HAND:
254 			printf("handprint verification");
255 			break;
256 		case FIDO_UV_MODE_NONE:
257 			printf("none");
258 			break;
259 		case FIDO_UV_MODE_ALL:
260 			printf("all required");
261 			break;
262 		case FIDO_UV_MODE_EXT_PIN:
263 			printf("external pin");
264 			break;
265 		case FIDO_UV_MODE_EXT_DRAWN:
266 			printf("external drawn pattern check");
267 			break;
268 		default:
269 			printf("unknown 0x%llx", (unsigned long long)mode);
270 			break;
271 		}
272 		printed = true;
273 	}
274 
275 	printf(")\n");
276 }
277 
278 static void
279 print_rk_remaining(int64_t rk_remaining)
280 {
281 	if (rk_remaining != -1)
282 		printf("remaining rk(s): %d\n", (int)rk_remaining);
283 }
284 
285 static void
286 print_fwversion(uint64_t fwversion)
287 {
288 	printf("fwversion: 0x%x\n", (int)fwversion);
289 }
290 
291 static void
292 print_byte_array(const char *label, const uint8_t *ba, size_t len)
293 {
294 	if (len == 0)
295 		return;
296 
297 	printf("%s: ", label);
298 
299 	for (size_t i = 0; i < len; i++)
300 		printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
301 
302 	printf("\n");
303 }
304 
305 int
306 token_info(int argc, char **argv, char *path)
307 {
308 	char			*cred_id = NULL;
309 	char			*rp_id = NULL;
310 	fido_cbor_info_t	*ci = NULL;
311 	fido_dev_t		*dev = NULL;
312 	int			 ch;
313 	int			 credman = 0;
314 	int			 r;
315 	int			 retrycnt;
316 
317 	optind = 1;
318 
319 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
320 		switch (ch) {
321 		case 'c':
322 			credman = 1;
323 			break;
324 		case 'i':
325 			cred_id = optarg;
326 			break;
327 		case 'k':
328 			rp_id = optarg;
329 			break;
330 		default:
331 			break; /* ignore */
332 		}
333 	}
334 
335 	if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
336 		usage();
337 
338 	dev = open_dev(path);
339 
340 	if (credman)
341 		return (credman_get_metadata(dev, path));
342 	if (cred_id && rp_id)
343 		return (credman_print_rk(dev, path, rp_id, cred_id));
344 	if (cred_id || rp_id)
345 		usage();
346 
347 	print_attr(dev);
348 
349 	if (fido_dev_is_fido2(dev) == false)
350 		goto end;
351 	if ((ci = fido_cbor_info_new()) == NULL)
352 		errx(1, "fido_cbor_info_new");
353 	if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
354 		errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
355 
356 	/* print supported protocol versions */
357 	print_str_array("version", fido_cbor_info_versions_ptr(ci),
358 	    fido_cbor_info_versions_len(ci));
359 
360 	/* print supported extensions */
361 	print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
362 	    fido_cbor_info_extensions_len(ci));
363 
364 	/* print supported transports */
365 	print_str_array("transport", fido_cbor_info_transports_ptr(ci),
366 	    fido_cbor_info_transports_len(ci));
367 
368 	/* print supported algorithms */
369 	print_algorithms(ci);
370 
371 	/* print aaguid */
372 	print_aaguid(fido_cbor_info_aaguid_ptr(ci),
373 	    fido_cbor_info_aaguid_len(ci));
374 
375 	/* print supported options */
376 	print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
377 	    fido_cbor_info_options_value_ptr(ci),
378 	    fido_cbor_info_options_len(ci));
379 
380 	/* print certifications */
381 	print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci),
382 	    fido_cbor_info_certs_value_ptr(ci),
383 	    fido_cbor_info_certs_len(ci));
384 
385 	/* print firmware version */
386 	print_fwversion(fido_cbor_info_fwversion(ci));
387 
388 	/* print maximum message size */
389 	print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
390 
391 	/* print maximum number of credentials allowed in credential lists */
392 	print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci));
393 
394 	/* print maximum length of a credential ID */
395 	print_maxcredidlen(fido_cbor_info_maxcredidlen(ci));
396 
397 	/* print maximum length of credBlob */
398 	print_maxcredblob(fido_cbor_info_maxcredbloblen(ci));
399 
400 	/* print maximum length of serialized largeBlob array */
401 	print_maxlargeblob(fido_cbor_info_maxlargeblob(ci));
402 
403 	/* print maximum number of RP IDs in fido_dev_set_pin_minlen_rpid() */
404 	print_maxrpid_minpinlen(fido_cbor_info_maxrpid_minpinlen(ci));
405 
406 	/* print estimated number of resident credentials */
407 	print_rk_remaining(fido_cbor_info_rk_remaining(ci));
408 
409 	/* print minimum pin length */
410 	print_minpinlen(fido_cbor_info_minpinlen(ci));
411 
412 	/* print supported pin protocols */
413 	print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
414 	    fido_cbor_info_protocols_len(ci));
415 
416 	if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK)
417 		printf("pin retries: undefined\n");
418 	else
419 		printf("pin retries: %d\n", retrycnt);
420 
421 	printf("pin change required: %s\n",
422 	    fido_cbor_info_new_pin_required(ci) ? "true" : "false");
423 
424 	if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK)
425 		printf("uv retries: undefined\n");
426 	else
427 		printf("uv retries: %d\n", retrycnt);
428 
429 	/* print platform uv attempts */
430 	print_uv_attempts(fido_cbor_info_uv_attempts(ci));
431 
432 	/* print supported uv mechanisms */
433 	print_uv_modality(fido_cbor_info_uv_modality(ci));
434 
435 	bio_info(dev);
436 
437 	fido_cbor_info_free(&ci);
438 end:
439 	fido_dev_close(dev);
440 	fido_dev_free(&dev);
441 
442 	exit(0);
443 }
444 
445 int
446 token_reset(char *path)
447 {
448 	fido_dev_t *dev = NULL;
449 	int r;
450 
451 	if (path == NULL)
452 		usage();
453 
454 	dev = open_dev(path);
455 	if ((r = fido_dev_reset(dev)) != FIDO_OK)
456 		errx(1, "fido_dev_reset: %s", fido_strerr(r));
457 
458 	fido_dev_close(dev);
459 	fido_dev_free(&dev);
460 
461 	exit(0);
462 }
463 
464 int
465 token_get(int argc, char **argv, char *path)
466 {
467 	char	*id = NULL;
468 	char	*key = NULL;
469 	char	*name = NULL;
470 	int	 blob = 0;
471 	int	 ch;
472 
473 	optind = 1;
474 
475 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
476 		switch (ch) {
477 		case 'b':
478 			blob = 1;
479 			break;
480 		case 'i':
481 			id = optarg;
482 			break;
483 		case 'k':
484 			key = optarg;
485 			break;
486 		case 'n':
487 			name = optarg;
488 			break;
489 		default:
490 			break; /* ignore */
491 		}
492 	}
493 
494 	argc -= optind;
495 	argv += optind;
496 
497 	if (blob == 0 || argc != 2)
498 		usage();
499 
500 	return blob_get(path, key, name, id, argv[0]);
501 }
502 
503 int
504 token_set(int argc, char **argv, char *path)
505 {
506 	char	*id = NULL;
507 	char	*key = NULL;
508 	char	*len = NULL;
509 	char	*display_name = NULL;
510 	char	*name = NULL;
511 	char	*rpid = NULL;
512 	int	 blob = 0;
513 	int	 cred = 0;
514 	int	 ch;
515 	int	 enroll = 0;
516 	int	 ea = 0;
517 	int	 uv = 0;
518 	bool	 force = false;
519 
520 	optind = 1;
521 
522 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
523 		switch (ch) {
524 		case 'a':
525 			ea = 1;
526 			break;
527 		case 'b':
528 			blob = 1;
529 			break;
530 		case 'c':
531 			cred = 1;
532 			break;
533 		case 'e':
534 			enroll = 1;
535 			break;
536 		case 'f':
537 			force = true;
538 			break;
539 		case 'i':
540 			id = optarg;
541 			break;
542 		case 'k':
543 			key = optarg;
544 			break;
545 		case 'l':
546 			len = optarg;
547 			break;
548 		case 'p':
549 			display_name = optarg;
550 			break;
551 		case 'm':
552 			rpid = optarg;
553 			break;
554 		case 'n':
555 			name = optarg;
556 			break;
557 		case 'u':
558 			uv = 1;
559 			break;
560 		default:
561 			break; /* ignore */
562 		}
563 	}
564 
565 	argc -= optind;
566 	argv += optind;
567 
568 	if (path == NULL)
569 		usage();
570 
571 	if (blob) {
572 		if (argc != 2)
573 			usage();
574 		return (blob_set(path, key, name, id, argv[0]));
575 	}
576 
577 	if (cred) {
578 		if (!id || !key)
579 			usage();
580 		if (!name && !display_name)
581 			usage();
582 		return (credman_update_rk(path, key, id, name, display_name));
583 	}
584 
585 	if (enroll) {
586 		if (ea || uv)
587 			usage();
588 		if (id && name)
589 			return (bio_set_name(path, id, name));
590 		if (!id && !name)
591 			return (bio_enroll(path));
592 		usage();
593 	}
594 
595 	if (ea) {
596 		if (uv)
597 			usage();
598 		return (config_entattest(path));
599 	}
600 
601 	if (len)
602 		return (config_pin_minlen(path, len));
603 	if (rpid)
604 		return (config_pin_minlen_rpid(path, rpid));
605 	if (force)
606 		return (config_force_pin_change(path));
607 	if (uv)
608 		return (config_always_uv(path, 1));
609 
610 	return (pin_set(path));
611 }
612 
613 int
614 token_list(int argc, char **argv, char *path)
615 {
616 	fido_dev_info_t *devlist;
617 	size_t ndevs;
618 	const char *rp_id = NULL;
619 	int blobs = 0;
620 	int enrolls = 0;
621 	int keys = 0;
622 	int rplist = 0;
623 	int ch;
624 	int r;
625 
626 	optind = 1;
627 
628 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
629 		switch (ch) {
630 		case 'b':
631 			blobs = 1;
632 			break;
633 		case 'e':
634 			enrolls = 1;
635 			break;
636 		case 'k':
637 			keys = 1;
638 			rp_id = optarg;
639 			break;
640 		case 'r':
641 			rplist = 1;
642 			break;
643 		default:
644 			break; /* ignore */
645 		}
646 	}
647 
648 	if (blobs || enrolls || keys || rplist) {
649 		if (path == NULL)
650 			usage();
651 		if (blobs)
652 			return (blob_list(path));
653 		if (enrolls)
654 			return (bio_list(path));
655 		if (keys)
656 			return (credman_list_rk(path, rp_id));
657 		if (rplist)
658 			return (credman_list_rp(path));
659 		/* NOTREACHED */
660 	}
661 
662 	if ((devlist = fido_dev_info_new(64)) == NULL)
663 		errx(1, "fido_dev_info_new");
664 	if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
665 		errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
666 
667 	for (size_t i = 0; i < ndevs; i++) {
668 		const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
669 		printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
670 		    fido_dev_info_path(di),
671 		    (uint16_t)fido_dev_info_vendor(di),
672 		    (uint16_t)fido_dev_info_product(di),
673 		    fido_dev_info_manufacturer_string(di),
674 		    fido_dev_info_product_string(di));
675 	}
676 
677 	fido_dev_info_free(&devlist, ndevs);
678 
679 	exit(0);
680 }
681 
682 int
683 token_delete(int argc, char **argv, char *path)
684 {
685 	char		*id = NULL;
686 	char		*key = NULL;
687 	char		*name = NULL;
688 	int		 blob = 0;
689 	int		 ch;
690 	int		 enroll = 0;
691 	int		 uv = 0;
692 
693 	optind = 1;
694 
695 	while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
696 		switch (ch) {
697 		case 'b':
698 			blob = 1;
699 			break;
700 		case 'e':
701 			enroll = 1;
702 			break;
703 		case 'i':
704 			id = optarg;
705 			break;
706 		case 'k':
707 			key = optarg;
708 			break;
709 		case 'n':
710 			name = optarg;
711 			break;
712 		case 'u':
713 			uv = 1;
714 			break;
715 		default:
716 			break; /* ignore */
717 		}
718 	}
719 
720 	if (path == NULL)
721 		usage();
722 
723 	if (blob)
724 		return (blob_delete(path, key, name, id));
725 
726 	if (id) {
727 		if (uv)
728 			usage();
729 		if (enroll == 0)
730 			return (credman_delete_rk(path, id));
731 		return (bio_delete(path, id));
732 	}
733 
734 	if (uv == 0)
735 		usage();
736 
737 	return (config_always_uv(path, 0));
738 }
739