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