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_maxcredidlen(uint64_t maxcredidlen)173 print_maxcredidlen(uint64_t maxcredidlen)
174 {
175 printf("maxcredlen: %d\n", (int)maxcredidlen);
176 }
177
178 static void
print_maxlargeblob(uint64_t maxlargeblob)179 print_maxlargeblob(uint64_t maxlargeblob)
180 {
181 printf("maxlargeblob: %d\n", (int)maxlargeblob);
182 }
183
184 static void
print_maxrpid_minpinlen(uint64_t maxrpid)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
print_minpinlen(uint64_t minpinlen)192 print_minpinlen(uint64_t minpinlen)
193 {
194 if (minpinlen > 0)
195 printf("minpinlen: %d\n", (int)minpinlen);
196 }
197
198 static void
print_uv_attempts(uint64_t uv_attempts)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
print_uv_modality(uint64_t uv_modality)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
print_rk_remaining(int64_t rk_remaining)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
print_fwversion(uint64_t fwversion)280 print_fwversion(uint64_t fwversion)
281 {
282 printf("fwversion: 0x%x\n", (int)fwversion);
283 }
284
285 static void
print_byte_array(const char * label,const uint8_t * ba,size_t len)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
token_info(int argc,char ** argv,char * path)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
token_reset(char * path)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
token_get(int argc,char ** argv,char * path)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
token_set(int argc,char ** argv,char * path)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
token_list(int argc,char ** argv,char * path)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
token_delete(int argc,char ** argv,char * path)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