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
format_flags(char * ret,size_t retlen,uint8_t flags)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
print_attr(const fido_dev_t * dev)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
print_str_array(const char * label,char * const * sa,size_t len)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
print_opt_array(const char * label,char * const * name,const bool * value,size_t len)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
print_cert_array(const char * label,char * const * name,const uint64_t * value,size_t len)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
print_algorithms(const fido_cbor_info_t * ci)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
print_aaguid(const unsigned char * buf,size_t buflen)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
print_maxmsgsiz(uint64_t maxmsgsiz)161 print_maxmsgsiz(uint64_t maxmsgsiz)
162 {
163 printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
164 }
165
166 static void
print_maxcredcntlst(uint64_t maxcredcntlst)167 print_maxcredcntlst(uint64_t maxcredcntlst)
168 {
169 printf("maxcredcntlst: %d\n", (int)maxcredcntlst);
170 }
171
172 static void
print_maxcredblob(uint64_t maxcredblob)173 print_maxcredblob(uint64_t maxcredblob)
174 {
175 printf("maxcredblob: %d\n", (int)maxcredblob);
176 }
177
178 static void
print_maxcredidlen(uint64_t maxcredidlen)179 print_maxcredidlen(uint64_t maxcredidlen)
180 {
181 printf("maxcredlen: %d\n", (int)maxcredidlen);
182 }
183
184 static void
print_maxlargeblob(uint64_t maxlargeblob)185 print_maxlargeblob(uint64_t maxlargeblob)
186 {
187 printf("maxlargeblob: %d\n", (int)maxlargeblob);
188 }
189
190 static void
print_maxrpid_minpinlen(uint64_t maxrpid)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
print_minpinlen(uint64_t minpinlen)198 print_minpinlen(uint64_t minpinlen)
199 {
200 if (minpinlen > 0)
201 printf("minpinlen: %d\n", (int)minpinlen);
202 }
203
204 static void
print_uv_attempts(uint64_t uv_attempts)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
print_uv_modality(uint64_t uv_modality)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
print_rk_remaining(int64_t rk_remaining)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
print_fwversion(uint64_t fwversion)286 print_fwversion(uint64_t fwversion)
287 {
288 printf("fwversion: 0x%x\n", (int)fwversion);
289 }
290
291 static void
print_byte_array(const char * label,const uint8_t * ba,size_t len)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
token_info(int argc,char ** argv,char * path)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
token_reset(char * path)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
token_get(int argc,char ** argv,char * path)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
token_set(int argc,char ** argv,char * path)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
token_list(int argc,char ** argv,char * path)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
token_delete(int argc,char ** argv,char * path)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