diff options
Diffstat (limited to 'tools/token.c')
-rw-r--r-- | tools/token.c | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/tools/token.c b/tools/token.c new file mode 100644 index 0000000..366d5a1 --- /dev/null +++ b/tools/token.c @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2018-2022 Yubico AB. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <fido.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "../openbsd-compat/openbsd-compat.h" +#include "extern.h" + +static void +format_flags(char *ret, size_t retlen, uint8_t flags) +{ + memset(ret, 0, retlen); + + if (flags & FIDO_CAP_WINK) { + if (strlcat(ret, "wink,", retlen) >= retlen) + goto toolong; + } else { + if (strlcat(ret, "nowink,", retlen) >= retlen) + goto toolong; + } + + if (flags & FIDO_CAP_CBOR) { + if (strlcat(ret, " cbor,", retlen) >= retlen) + goto toolong; + } else { + if (strlcat(ret, " nocbor,", retlen) >= retlen) + goto toolong; + } + + if (flags & FIDO_CAP_NMSG) { + if (strlcat(ret, " nomsg", retlen) >= retlen) + goto toolong; + } else { + if (strlcat(ret, " msg", retlen) >= retlen) + goto toolong; + } + + return; +toolong: + strlcpy(ret, "toolong", retlen); +} + +static void +print_attr(const fido_dev_t *dev) +{ + char flags_txt[128]; + + printf("proto: 0x%02x\n", fido_dev_protocol(dev)); + printf("major: 0x%02x\n", fido_dev_major(dev)); + printf("minor: 0x%02x\n", fido_dev_minor(dev)); + printf("build: 0x%02x\n", fido_dev_build(dev)); + + format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev)); + printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt); +} + +static void +print_str_array(const char *label, char * const *sa, size_t len) +{ + if (len == 0) + return; + + printf("%s strings: ", label); + + for (size_t i = 0; i < len; i++) + printf("%s%s", i > 0 ? ", " : "", sa[i]); + + printf("\n"); +} + +static void +print_opt_array(const char *label, char * const *name, const bool *value, + size_t len) +{ + if (len == 0) + return; + + printf("%s: ", label); + + for (size_t i = 0; i < len; i++) + printf("%s%s%s", i > 0 ? ", " : "", + value[i] ? "" : "no", name[i]); + + printf("\n"); +} + +static void +print_cert_array(const char *label, char * const *name, const uint64_t *value, + size_t len) +{ + if (len == 0) + return; + + printf("%s: ", label); + + for (size_t i = 0; i < len; i++) + printf("%s%s %llu", i > 0 ? ", " : "", name[i], + (unsigned long long)value[i]); + + printf("\n"); +} + +static void +print_algorithms(const fido_cbor_info_t *ci) +{ + const char *cose, *type; + size_t len; + + if ((len = fido_cbor_info_algorithm_count(ci)) == 0) + return; + + printf("algorithms: "); + + for (size_t i = 0; i < len; i++) { + cose = type = "unknown"; + switch (fido_cbor_info_algorithm_cose(ci, i)) { + case COSE_ES256: + cose = "es256"; + break; + case COSE_ES384: + cose = "es384"; + break; + case COSE_RS256: + cose = "rs256"; + break; + case COSE_EDDSA: + cose = "eddsa"; + break; + } + if (fido_cbor_info_algorithm_type(ci, i) != NULL) + type = fido_cbor_info_algorithm_type(ci, i); + printf("%s%s (%s)", i > 0 ? ", " : "", cose, type); + } + + printf("\n"); +} + +static void +print_aaguid(const unsigned char *buf, size_t buflen) +{ + printf("aaguid: "); + + while (buflen--) + printf("%02x", *buf++); + + printf("\n"); +} + +static void +print_maxmsgsiz(uint64_t maxmsgsiz) +{ + printf("maxmsgsiz: %d\n", (int)maxmsgsiz); +} + +static void +print_maxcredcntlst(uint64_t maxcredcntlst) +{ + printf("maxcredcntlst: %d\n", (int)maxcredcntlst); +} + +static void +print_maxcredidlen(uint64_t maxcredidlen) +{ + printf("maxcredlen: %d\n", (int)maxcredidlen); +} + +static void +print_maxlargeblob(uint64_t maxlargeblob) +{ + printf("maxlargeblob: %d\n", (int)maxlargeblob); +} + +static void +print_maxrpid_minpinlen(uint64_t maxrpid) +{ + if (maxrpid > 0) + printf("maxrpids in minpinlen: %d\n", (int)maxrpid); +} + +static void +print_minpinlen(uint64_t minpinlen) +{ + if (minpinlen > 0) + printf("minpinlen: %d\n", (int)minpinlen); +} + +static void +print_uv_attempts(uint64_t uv_attempts) +{ + if (uv_attempts > 0) + printf("platform uv attempt(s): %d\n", (int)uv_attempts); +} + +static void +print_uv_modality(uint64_t uv_modality) +{ + uint64_t mode; + bool printed = false; + + if (uv_modality == 0) + return; + + printf("uv modality: 0x%x (", (int)uv_modality); + + for (size_t i = 0; i < 64; i++) { + mode = 1ULL << i; + if ((uv_modality & mode) == 0) + continue; + if (printed) + printf(", "); + switch (mode) { + case FIDO_UV_MODE_TUP: + printf("test of user presence"); + break; + case FIDO_UV_MODE_FP: + printf("fingerprint check"); + break; + case FIDO_UV_MODE_PIN: + printf("pin check"); + break; + case FIDO_UV_MODE_VOICE: + printf("voice recognition"); + break; + case FIDO_UV_MODE_FACE: + printf("face recognition"); + break; + case FIDO_UV_MODE_LOCATION: + printf("location check"); + break; + case FIDO_UV_MODE_EYE: + printf("eyeprint check"); + break; + case FIDO_UV_MODE_DRAWN: + printf("drawn pattern check"); + break; + case FIDO_UV_MODE_HAND: + printf("handprint verification"); + break; + case FIDO_UV_MODE_NONE: + printf("none"); + break; + case FIDO_UV_MODE_ALL: + printf("all required"); + break; + case FIDO_UV_MODE_EXT_PIN: + printf("external pin"); + break; + case FIDO_UV_MODE_EXT_DRAWN: + printf("external drawn pattern check"); + break; + default: + printf("unknown 0x%llx", (unsigned long long)mode); + break; + } + printed = true; + } + + printf(")\n"); +} + +static void +print_rk_remaining(int64_t rk_remaining) +{ + if (rk_remaining != -1) + printf("remaining rk(s): %d\n", (int)rk_remaining); +} + +static void +print_fwversion(uint64_t fwversion) +{ + printf("fwversion: 0x%x\n", (int)fwversion); +} + +static void +print_byte_array(const char *label, const uint8_t *ba, size_t len) +{ + if (len == 0) + return; + + printf("%s: ", label); + + for (size_t i = 0; i < len; i++) + printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]); + + printf("\n"); +} + +int +token_info(int argc, char **argv, char *path) +{ + char *cred_id = NULL; + char *rp_id = NULL; + fido_cbor_info_t *ci = NULL; + fido_dev_t *dev = NULL; + int ch; + int credman = 0; + int r; + int retrycnt; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'c': + credman = 1; + break; + case 'i': + cred_id = optarg; + break; + case 'k': + rp_id = optarg; + break; + default: + break; /* ignore */ + } + } + + if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL))) + usage(); + + dev = open_dev(path); + + if (credman) + return (credman_get_metadata(dev, path)); + if (cred_id && rp_id) + return (credman_print_rk(dev, path, rp_id, cred_id)); + if (cred_id || rp_id) + usage(); + + print_attr(dev); + + if (fido_dev_is_fido2(dev) == false) + goto end; + if ((ci = fido_cbor_info_new()) == NULL) + errx(1, "fido_cbor_info_new"); + if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK) + errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r); + + /* print supported protocol versions */ + print_str_array("version", fido_cbor_info_versions_ptr(ci), + fido_cbor_info_versions_len(ci)); + + /* print supported extensions */ + print_str_array("extension", fido_cbor_info_extensions_ptr(ci), + fido_cbor_info_extensions_len(ci)); + + /* print supported transports */ + print_str_array("transport", fido_cbor_info_transports_ptr(ci), + fido_cbor_info_transports_len(ci)); + + /* print supported algorithms */ + print_algorithms(ci); + + /* print aaguid */ + print_aaguid(fido_cbor_info_aaguid_ptr(ci), + fido_cbor_info_aaguid_len(ci)); + + /* print supported options */ + print_opt_array("options", fido_cbor_info_options_name_ptr(ci), + fido_cbor_info_options_value_ptr(ci), + fido_cbor_info_options_len(ci)); + + /* print certifications */ + print_cert_array("certifications", fido_cbor_info_certs_name_ptr(ci), + fido_cbor_info_certs_value_ptr(ci), + fido_cbor_info_certs_len(ci)); + + /* print firmware version */ + print_fwversion(fido_cbor_info_fwversion(ci)); + + /* print maximum message size */ + print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci)); + + /* print maximum number of credentials allowed in credential lists */ + print_maxcredcntlst(fido_cbor_info_maxcredcntlst(ci)); + + /* print maximum length of a credential ID */ + print_maxcredidlen(fido_cbor_info_maxcredidlen(ci)); + + /* print maximum length of serialized largeBlob array */ + print_maxlargeblob(fido_cbor_info_maxlargeblob(ci)); + + /* print maximum number of RP IDs in fido_dev_set_pin_minlen_rpid() */ + print_maxrpid_minpinlen(fido_cbor_info_maxrpid_minpinlen(ci)); + + /* print estimated number of resident credentials */ + print_rk_remaining(fido_cbor_info_rk_remaining(ci)); + + /* print minimum pin length */ + print_minpinlen(fido_cbor_info_minpinlen(ci)); + + /* print supported pin protocols */ + print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci), + fido_cbor_info_protocols_len(ci)); + + if (fido_dev_get_retry_count(dev, &retrycnt) != FIDO_OK) + printf("pin retries: undefined\n"); + else + printf("pin retries: %d\n", retrycnt); + + printf("pin change required: %s\n", + fido_cbor_info_new_pin_required(ci) ? "true" : "false"); + + if (fido_dev_get_uv_retry_count(dev, &retrycnt) != FIDO_OK) + printf("uv retries: undefined\n"); + else + printf("uv retries: %d\n", retrycnt); + + /* print platform uv attempts */ + print_uv_attempts(fido_cbor_info_uv_attempts(ci)); + + /* print supported uv mechanisms */ + print_uv_modality(fido_cbor_info_uv_modality(ci)); + + bio_info(dev); + + fido_cbor_info_free(&ci); +end: + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +token_reset(char *path) +{ + fido_dev_t *dev = NULL; + int r; + + if (path == NULL) + usage(); + + dev = open_dev(path); + if ((r = fido_dev_reset(dev)) != FIDO_OK) + errx(1, "fido_dev_reset: %s", fido_strerr(r)); + + fido_dev_close(dev); + fido_dev_free(&dev); + + exit(0); +} + +int +token_get(int argc, char **argv, char *path) +{ + char *id = NULL; + char *key = NULL; + char *name = NULL; + int blob = 0; + int ch; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'b': + blob = 1; + break; + case 'i': + id = optarg; + break; + case 'k': + key = optarg; + break; + case 'n': + name = optarg; + break; + default: + break; /* ignore */ + } + } + + argc -= optind; + argv += optind; + + if (blob == 0 || argc != 2) + usage(); + + return blob_get(path, key, name, id, argv[0]); +} + +int +token_set(int argc, char **argv, char *path) +{ + char *id = NULL; + char *key = NULL; + char *len = NULL; + char *display_name = NULL; + char *name = NULL; + char *rpid = NULL; + int blob = 0; + int cred = 0; + int ch; + int enroll = 0; + int ea = 0; + int uv = 0; + bool force = false; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'a': + ea = 1; + break; + case 'b': + blob = 1; + break; + case 'c': + cred = 1; + break; + case 'e': + enroll = 1; + break; + case 'f': + force = true; + break; + case 'i': + id = optarg; + break; + case 'k': + key = optarg; + break; + case 'l': + len = optarg; + break; + case 'p': + display_name = optarg; + break; + case 'm': + rpid = optarg; + break; + case 'n': + name = optarg; + break; + case 'u': + uv = 1; + break; + default: + break; /* ignore */ + } + } + + argc -= optind; + argv += optind; + + if (path == NULL) + usage(); + + if (blob) { + if (argc != 2) + usage(); + return (blob_set(path, key, name, id, argv[0])); + } + + if (cred) { + if (!id || !key) + usage(); + if (!name && !display_name) + usage(); + return (credman_update_rk(path, key, id, name, display_name)); + } + + if (enroll) { + if (ea || uv) + usage(); + if (id && name) + return (bio_set_name(path, id, name)); + if (!id && !name) + return (bio_enroll(path)); + usage(); + } + + if (ea) { + if (uv) + usage(); + return (config_entattest(path)); + } + + if (len) + return (config_pin_minlen(path, len)); + if (rpid) + return (config_pin_minlen_rpid(path, rpid)); + if (force) + return (config_force_pin_change(path)); + if (uv) + return (config_always_uv(path, 1)); + + return (pin_set(path)); +} + +int +token_list(int argc, char **argv, char *path) +{ + fido_dev_info_t *devlist; + size_t ndevs; + const char *rp_id = NULL; + int blobs = 0; + int enrolls = 0; + int keys = 0; + int rplist = 0; + int ch; + int r; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'b': + blobs = 1; + break; + case 'e': + enrolls = 1; + break; + case 'k': + keys = 1; + rp_id = optarg; + break; + case 'r': + rplist = 1; + break; + default: + break; /* ignore */ + } + } + + if (blobs || enrolls || keys || rplist) { + if (path == NULL) + usage(); + if (blobs) + return (blob_list(path)); + if (enrolls) + return (bio_list(path)); + if (keys) + return (credman_list_rk(path, rp_id)); + if (rplist) + return (credman_list_rp(path)); + /* NOTREACHED */ + } + + if ((devlist = fido_dev_info_new(64)) == NULL) + errx(1, "fido_dev_info_new"); + if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) + errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); + + for (size_t i = 0; i < ndevs; i++) { + const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); + printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n", + fido_dev_info_path(di), + (uint16_t)fido_dev_info_vendor(di), + (uint16_t)fido_dev_info_product(di), + fido_dev_info_manufacturer_string(di), + fido_dev_info_product_string(di)); + } + + fido_dev_info_free(&devlist, ndevs); + + exit(0); +} + +int +token_delete(int argc, char **argv, char *path) +{ + char *id = NULL; + char *key = NULL; + char *name = NULL; + int blob = 0; + int ch; + int enroll = 0; + int uv = 0; + + optind = 1; + + while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) { + switch (ch) { + case 'b': + blob = 1; + break; + case 'e': + enroll = 1; + break; + case 'i': + id = optarg; + break; + case 'k': + key = optarg; + break; + case 'n': + name = optarg; + break; + case 'u': + uv = 1; + break; + default: + break; /* ignore */ + } + } + + if (path == NULL) + usage(); + + if (blob) + return (blob_delete(path, key, name, id)); + + if (id) { + if (uv) + usage(); + if (enroll == 0) + return (credman_delete_rk(path, id)); + return (bio_delete(path, id)); + } + + if (uv == 0) + usage(); + + return (config_always_uv(path, 0)); +} |