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