Home | History | Annotate | Line # | Download | only in tools
      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