Add files via upload

This commit is contained in:
Token2
2024-05-24 11:45:21 +02:00
committed by GitHub
parent ca68f70495
commit 976966cc30
25 changed files with 5322 additions and 0 deletions

55
tools/CMakeLists.txt Normal file
View File

@@ -0,0 +1,55 @@
# 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
list(APPEND COMPAT_SOURCES
../openbsd-compat/bsd-getpagesize.c
../openbsd-compat/explicit_bzero.c
../openbsd-compat/freezero.c
../openbsd-compat/recallocarray.c
../openbsd-compat/strlcat.c
../openbsd-compat/strlcpy.c
../openbsd-compat/strsep.c
)
if(WIN32 AND NOT CYGWIN AND NOT MSYS)
list(APPEND COMPAT_SOURCES
../openbsd-compat/bsd-getline.c
../openbsd-compat/endian_win32.c
../openbsd-compat/explicit_bzero_win32.c
../openbsd-compat/getopt_long.c
../openbsd-compat/readpassphrase_win32.c
)
if (BUILD_SHARED_LIBS)
list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c)
endif()
else()
list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c)
endif()
if(NOT MSVC)
set_source_files_properties(assert_get.c assert_verify.c base64.c bio.c
config.c cred_make.c cred_verify.c credman.c
fido2-token.c pin.c token.c util.c
PROPERTIES COMPILE_FLAGS "${EXTRA_CFLAGS}")
endif()
add_executable(fido2-token
fido2-token.c
base64.c
bio.c
config.c
credman.c
largeblob.c
pin.c
token.c
util.c
${COMPAT_SOURCES}
)
target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} ${_FIDO2_LIBRARY})
install(TARGETS fido2-token
DESTINATION ${CMAKE_INSTALL_BINDIR})

328
tools/assert_get.c Normal file
View File

@@ -0,0 +1,328 @@
/*
* Copyright (c) 2018-2023 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 <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"
struct toggle {
fido_opt_t up;
fido_opt_t uv;
fido_opt_t pin;
};
static const char *
opt2str(fido_opt_t v)
{
switch (v) {
case FIDO_OPT_OMIT:
return "omit";
case FIDO_OPT_TRUE:
return "true";
case FIDO_OPT_FALSE:
return "false";
default:
return "unknown";
}
}
static void
parse_toggle(const char *str, struct toggle *opt)
{
fido_opt_t *k;
fido_opt_t v;
char *assignment;
char *key;
char *val;
if ((assignment = strdup(str)) == NULL)
err(1, "strdup");
if ((val = strchr(assignment, '=')) == NULL)
errx(1, "invalid assignment '%s'", assignment);
key = assignment;
*val++ = '\0';
if (!strcmp(val, "true"))
v = FIDO_OPT_TRUE;
else if (!strcmp(val, "false"))
v = FIDO_OPT_FALSE;
else
errx(1, "unknown value '%s'", val);
if (!strcmp(key, "up"))
k = &opt->up;
else if (!strcmp(key, "uv"))
k = &opt->uv;
else if (!strcmp(key, "pin"))
k = &opt->pin;
else
errx(1, "unknown key '%s'", key);
free(assignment);
*k = v;
}
static fido_assert_t *
prepare_assert(FILE *in_f, int flags, const struct toggle *opt)
{
fido_assert_t *assert = NULL;
struct blob cdh;
struct blob id;
struct blob hmac_salt;
char *rpid = NULL;
int r;
memset(&cdh, 0, sizeof(cdh));
memset(&id, 0, sizeof(id));
memset(&hmac_salt, 0, sizeof(hmac_salt));
r = base64_read(in_f, &cdh);
r |= string_read(in_f, &rpid);
if ((flags & FLAG_RK) == 0)
r |= base64_read(in_f, &id);
if (flags & FLAG_HMAC)
r |= base64_read(in_f, &hmac_salt);
if (r < 0)
errx(1, "input error");
if (flags & FLAG_DEBUG) {
fprintf(stderr, "client data%s:\n",
flags & FLAG_CD ? "" : " hash");
xxd(cdh.ptr, cdh.len);
fprintf(stderr, "relying party id: %s\n", rpid);
if ((flags & FLAG_RK) == 0) {
fprintf(stderr, "credential id:\n");
xxd(id.ptr, id.len);
}
fprintf(stderr, "up=%s\n", opt2str(opt->up));
fprintf(stderr, "uv=%s\n", opt2str(opt->uv));
fprintf(stderr, "pin=%s\n", opt2str(opt->pin));
}
if ((assert = fido_assert_new()) == NULL)
errx(1, "fido_assert_new");
if (flags & FLAG_CD)
r = fido_assert_set_clientdata(assert, cdh.ptr, cdh.len);
else
r = fido_assert_set_clientdata_hash(assert, cdh.ptr, cdh.len);
if (r != FIDO_OK || (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK)
errx(1, "fido_assert_set: %s", fido_strerr(r));
if ((r = fido_assert_set_up(assert, opt->up)) != FIDO_OK)
errx(1, "fido_assert_set_up: %s", fido_strerr(r));
if ((r = fido_assert_set_uv(assert, opt->uv)) != FIDO_OK)
errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
if (flags & FLAG_HMAC) {
if ((r = fido_assert_set_extensions(assert,
FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
errx(1, "fido_assert_set_extensions: %s",
fido_strerr(r));
if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr,
hmac_salt.len)) != FIDO_OK)
errx(1, "fido_assert_set_hmac_salt: %s",
fido_strerr(r));
}
if (flags & FLAG_LARGEBLOB) {
if ((r = fido_assert_set_extensions(assert,
FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK)
errx(1, "fido_assert_set_extensions: %s", fido_strerr(r));
}
if ((flags & FLAG_RK) == 0) {
if ((r = fido_assert_allow_cred(assert, id.ptr,
id.len)) != FIDO_OK)
errx(1, "fido_assert_allow_cred: %s", fido_strerr(r));
}
free(hmac_salt.ptr);
free(cdh.ptr);
free(id.ptr);
free(rpid);
return (assert);
}
static void
print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags)
{
char *cdh = NULL;
char *authdata = NULL;
char *sig = NULL;
char *user_id = NULL;
char *hmac_secret = NULL;
char *key = NULL;
int r;
r = base64_encode(fido_assert_clientdata_hash_ptr(assert),
fido_assert_clientdata_hash_len(assert), &cdh);
r |= base64_encode(fido_assert_authdata_ptr(assert, idx),
fido_assert_authdata_len(assert, 0), &authdata);
r |= base64_encode(fido_assert_sig_ptr(assert, idx),
fido_assert_sig_len(assert, idx), &sig);
if (flags & FLAG_RK)
r |= base64_encode(fido_assert_user_id_ptr(assert, idx),
fido_assert_user_id_len(assert, idx), &user_id);
if (flags & FLAG_HMAC)
r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx),
fido_assert_hmac_secret_len(assert, idx), &hmac_secret);
if (flags & FLAG_LARGEBLOB)
r |= base64_encode(fido_assert_largeblob_key_ptr(assert, idx),
fido_assert_largeblob_key_len(assert, idx), &key);
if (r < 0)
errx(1, "output error");
fprintf(out_f, "%s\n", cdh);
fprintf(out_f, "%s\n", fido_assert_rp_id(assert));
fprintf(out_f, "%s\n", authdata);
fprintf(out_f, "%s\n", sig);
if (flags & FLAG_RK)
fprintf(out_f, "%s\n", user_id);
if (hmac_secret) {
fprintf(out_f, "%s\n", hmac_secret);
explicit_bzero(hmac_secret, strlen(hmac_secret));
}
if (key) {
fprintf(out_f, "%s\n", key);
explicit_bzero(key, strlen(key));
}
free(key);
free(hmac_secret);
free(cdh);
free(authdata);
free(sig);
free(user_id);
}
int
assert_get(int argc, char **argv)
{
fido_dev_t *dev = NULL;
fido_assert_t *assert = NULL;
struct toggle opt;
char prompt[1024];
char pin[128];
char *in_path = NULL;
char *out_path = NULL;
FILE *in_f = NULL;
FILE *out_f = NULL;
int flags = 0;
int ch;
int r;
opt.up = opt.uv = opt.pin = FIDO_OPT_OMIT;
while ((ch = getopt(argc, argv, "bdhi:o:prt:uvw")) != -1) {
switch (ch) {
case 'b':
flags |= FLAG_LARGEBLOB;
break;
case 'd':
flags |= FLAG_DEBUG;
break;
case 'h':
flags |= FLAG_HMAC;
break;
case 'i':
in_path = optarg;
break;
case 'o':
out_path = optarg;
break;
case 'p':
opt.up = FIDO_OPT_TRUE;
break;
case 'r':
flags |= FLAG_RK;
break;
case 't' :
parse_toggle(optarg, &opt);
break;
case 'u':
flags |= FLAG_U2F;
break;
case 'v':
/* -v implies both pin and uv for historical reasons */
opt.pin = FIDO_OPT_TRUE;
opt.uv = FIDO_OPT_TRUE;
break;
case 'w':
flags |= FLAG_CD;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1)
usage();
in_f = open_read(in_path);
out_f = open_write(out_path);
fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
assert = prepare_assert(in_f, flags, &opt);
dev = open_dev(argv[0]);
if (flags & FLAG_U2F)
fido_dev_force_u2f(dev);
if (opt.pin == FIDO_OPT_TRUE) {
r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
argv[0]);
if (r < 0 || (size_t)r >= sizeof(prompt))
errx(1, "snprintf");
if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
errx(1, "readpassphrase");
if (strlen(pin) < 4 || strlen(pin) > 63) {
explicit_bzero(pin, sizeof(pin));
errx(1, "invalid PIN length");
}
r = fido_dev_get_assert(dev, assert, pin);
} else
r = fido_dev_get_assert(dev, assert, NULL);
explicit_bzero(pin, sizeof(pin));
if (r != FIDO_OK)
errx(1, "fido_dev_get_assert: %s", fido_strerr(r));
if (flags & FLAG_RK) {
for (size_t idx = 0; idx < fido_assert_count(assert); idx++)
print_assert(out_f, assert, idx, flags);
} else {
if (fido_assert_count(assert) != 1)
errx(1, "fido_assert_count: %zu",
fido_assert_count(assert));
print_assert(out_f, assert, 0, flags);
}
fido_dev_close(dev);
fido_dev_free(&dev);
fido_assert_free(&assert);
fclose(in_f);
fclose(out_f);
in_f = NULL;
out_f = NULL;
exit(0);
}

208
tools/assert_verify.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* 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 <fido/es256.h>
#include <fido/es384.h>
#include <fido/rs256.h>
#include <fido/eddsa.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 fido_assert_t *
prepare_assert(FILE *in_f, int flags)
{
fido_assert_t *assert = NULL;
struct blob cdh;
struct blob authdata;
struct blob sig;
char *rpid = NULL;
int r;
memset(&cdh, 0, sizeof(cdh));
memset(&authdata, 0, sizeof(authdata));
memset(&sig, 0, sizeof(sig));
r = base64_read(in_f, &cdh);
r |= string_read(in_f, &rpid);
r |= base64_read(in_f, &authdata);
r |= base64_read(in_f, &sig);
if (r < 0)
errx(1, "input error");
if (flags & FLAG_DEBUG) {
fprintf(stderr, "client data hash:\n");
xxd(cdh.ptr, cdh.len);
fprintf(stderr, "relying party id: %s\n", rpid);
fprintf(stderr, "authenticator data:\n");
xxd(authdata.ptr, authdata.len);
fprintf(stderr, "signature:\n");
xxd(sig.ptr, sig.len);
}
if ((assert = fido_assert_new()) == NULL)
errx(1, "fido_assert_new");
if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK)
errx(1, "fido_assert_count: %s", fido_strerr(r));
if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr,
cdh.len)) != FIDO_OK ||
(r = fido_assert_set_rp(assert, rpid)) != FIDO_OK ||
(r = fido_assert_set_authdata(assert, 0, authdata.ptr,
authdata.len)) != FIDO_OK ||
(r = fido_assert_set_sig(assert, 0, sig.ptr, sig.len)) != FIDO_OK)
errx(1, "fido_assert_set: %s", fido_strerr(r));
if (flags & FLAG_UP) {
if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
errx(1, "fido_assert_set_up: %s", fido_strerr(r));
}
if (flags & FLAG_UV) {
if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
}
if (flags & FLAG_HMAC) {
if ((r = fido_assert_set_extensions(assert,
FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
errx(1, "fido_assert_set_extensions: %s",
fido_strerr(r));
}
free(cdh.ptr);
free(authdata.ptr);
free(sig.ptr);
free(rpid);
return (assert);
}
static void *
load_pubkey(int type, const char *file)
{
EC_KEY *ec = NULL;
RSA *rsa = NULL;
EVP_PKEY *eddsa = NULL;
es256_pk_t *es256_pk = NULL;
es384_pk_t *es384_pk = NULL;
rs256_pk_t *rs256_pk = NULL;
eddsa_pk_t *eddsa_pk = NULL;
void *pk = NULL;
switch (type) {
case COSE_ES256:
if ((ec = read_ec_pubkey(file)) == NULL)
errx(1, "read_ec_pubkey");
if ((es256_pk = es256_pk_new()) == NULL)
errx(1, "es256_pk_new");
if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
errx(1, "es256_pk_from_EC_KEY");
pk = es256_pk;
EC_KEY_free(ec);
break;
case COSE_ES384:
if ((ec = read_ec_pubkey(file)) == NULL)
errx(1, "read_ec_pubkey");
if ((es384_pk = es384_pk_new()) == NULL)
errx(1, "es384_pk_new");
if (es384_pk_from_EC_KEY(es384_pk, ec) != FIDO_OK)
errx(1, "es384_pk_from_EC_KEY");
pk = es384_pk;
EC_KEY_free(ec);
break;
case COSE_RS256:
if ((rsa = read_rsa_pubkey(file)) == NULL)
errx(1, "read_rsa_pubkey");
if ((rs256_pk = rs256_pk_new()) == NULL)
errx(1, "rs256_pk_new");
if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
errx(1, "rs256_pk_from_RSA");
pk = rs256_pk;
RSA_free(rsa);
break;
case COSE_EDDSA:
if ((eddsa = read_eddsa_pubkey(file)) == NULL)
errx(1, "read_eddsa_pubkey");
if ((eddsa_pk = eddsa_pk_new()) == NULL)
errx(1, "eddsa_pk_new");
if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
errx(1, "eddsa_pk_from_EVP_PKEY");
pk = eddsa_pk;
EVP_PKEY_free(eddsa);
break;
default:
errx(1, "invalid type %d", type);
}
return (pk);
}
int
assert_verify(int argc, char **argv)
{
fido_assert_t *assert = NULL;
void *pk = NULL;
char *in_path = NULL;
FILE *in_f = NULL;
int type = COSE_ES256;
int flags = 0;
int ch;
int r;
while ((ch = getopt(argc, argv, "dhi:pv")) != -1) {
switch (ch) {
case 'd':
flags |= FLAG_DEBUG;
break;
case 'h':
flags |= FLAG_HMAC;
break;
case 'i':
in_path = optarg;
break;
case 'p':
flags |= FLAG_UP;
break;
case 'v':
flags |= FLAG_UV;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1 || argc > 2)
usage();
in_f = open_read(in_path);
if (argc > 1 && cose_type(argv[1], &type) < 0)
errx(1, "unknown type %s", argv[1]);
fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
pk = load_pubkey(type, argv[0]);
assert = prepare_assert(in_f, flags);
if ((r = fido_assert_verify(assert, 0, type, pk)) != FIDO_OK)
errx(1, "fido_assert_verify: %s", fido_strerr(r));
fido_assert_free(&assert);
fclose(in_f);
in_f = NULL;
exit(0);
}

135
tools/base64.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018 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 <openssl/bio.h>
#include <openssl/evp.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
int
base64_encode(const void *ptr, size_t len, char **out)
{
BIO *bio_b64 = NULL;
BIO *bio_mem = NULL;
char *b64_ptr = NULL;
long b64_len;
int n;
int ok = -1;
if (ptr == NULL || out == NULL || len > INT_MAX)
return (-1);
*out = NULL;
if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
goto fail;
if ((bio_mem = BIO_new(BIO_s_mem())) == NULL)
goto fail;
BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
BIO_push(bio_b64, bio_mem);
n = BIO_write(bio_b64, ptr, (int)len);
if (n < 0 || (size_t)n != len)
goto fail;
if (BIO_flush(bio_b64) < 0)
goto fail;
b64_len = BIO_get_mem_data(bio_b64, &b64_ptr);
if (b64_len < 0 || (size_t)b64_len == SIZE_MAX || b64_ptr == NULL)
goto fail;
if ((*out = calloc(1, (size_t)b64_len + 1)) == NULL)
goto fail;
memcpy(*out, b64_ptr, (size_t)b64_len);
ok = 0;
fail:
BIO_free(bio_b64);
BIO_free(bio_mem);
return (ok);
}
int
base64_decode(const char *in, void **ptr, size_t *len)
{
BIO *bio_mem = NULL;
BIO *bio_b64 = NULL;
size_t alloc_len;
int n;
int ok = -1;
if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX)
return (-1);
*ptr = NULL;
*len = 0;
if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
goto fail;
if ((bio_mem = BIO_new_mem_buf((const void *)in, -1)) == NULL)
goto fail;
BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
BIO_push(bio_b64, bio_mem);
alloc_len = strlen(in);
if ((*ptr = calloc(1, alloc_len)) == NULL)
goto fail;
n = BIO_read(bio_b64, *ptr, (int)alloc_len);
if (n <= 0 || BIO_eof(bio_b64) == 0)
goto fail;
*len = (size_t)n;
ok = 0;
fail:
BIO_free(bio_b64);
BIO_free(bio_mem);
if (ok < 0) {
free(*ptr);
*ptr = NULL;
*len = 0;
}
return (ok);
}
int
base64_read(FILE *f, struct blob *out)
{
char *line = NULL;
size_t linesize = 0;
ssize_t n;
out->ptr = NULL;
out->len = 0;
if ((n = getline(&line, &linesize, f)) <= 0 ||
(size_t)n != strlen(line)) {
free(line); /* XXX should be free'd _even_ if getline() fails */
return (-1);
}
if (base64_decode(line, (void **)&out->ptr, &out->len) < 0) {
free(line);
return (-1);
}
free(line);
return (0);
}

278
tools/bio.c Normal file
View File

@@ -0,0 +1,278 @@
/*
* Copyright (c) 2019 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 <fido/bio.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 int
print_template(const fido_bio_template_array_t *ta, size_t idx)
{
const fido_bio_template_t *t = NULL;
char *id = NULL;
if ((t = fido_bio_template(ta, idx)) == NULL) {
warnx("fido_bio_template");
return -1;
}
if (base64_encode(fido_bio_template_id_ptr(t),
fido_bio_template_id_len(t), &id) < 0) {
warnx("output error");
return -1;
}
printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t));
free(id);
return 0;
}
int
bio_list(const char *path)
{
fido_bio_template_array_t *ta = NULL;
fido_dev_t *dev = NULL;
char *pin = NULL;
int r, ok = 1;
if ((ta = fido_bio_template_array_new()) == NULL)
errx(1, "fido_bio_template_array_new");
dev = open_dev(path);
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_bio_dev_get_template_array(dev, ta, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
if (r != FIDO_OK) {
warnx("fido_bio_dev_get_template_array: %s", fido_strerr(r));
goto out;
}
for (size_t i = 0; i < fido_bio_template_array_count(ta); i++)
if (print_template(ta, i) < 0)
goto out;
ok = 0;
out:
fido_bio_template_array_free(&ta);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
bio_set_name(const char *path, const char *id, const char *name)
{
fido_bio_template_t *t = NULL;
fido_dev_t *dev = NULL;
char *pin = NULL;
void *id_blob_ptr = NULL;
size_t id_blob_len = 0;
int r, ok = 1;
if ((t = fido_bio_template_new()) == NULL)
errx(1, "fido_bio_template_new");
if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
errx(1, "base64_decode");
if ((r = fido_bio_template_set_name(t, name)) != FIDO_OK)
errx(1, "fido_bio_template_set_name: %s", fido_strerr(r));
if ((r = fido_bio_template_set_id(t, id_blob_ptr,
id_blob_len)) != FIDO_OK)
errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
dev = open_dev(path);
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_bio_dev_set_template_name(dev, t, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
if (r != FIDO_OK) {
warnx("fido_bio_dev_set_template_name: %s", fido_strerr(r));
goto out;
}
ok = 0;
out:
free(id_blob_ptr);
fido_bio_template_free(&t);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
static const char *
enroll_strerr(uint8_t n)
{
switch (n) {
case FIDO_BIO_ENROLL_FP_GOOD:
return "Sample ok";
case FIDO_BIO_ENROLL_FP_TOO_HIGH:
return "Sample too high";
case FIDO_BIO_ENROLL_FP_TOO_LOW:
return "Sample too low";
case FIDO_BIO_ENROLL_FP_TOO_LEFT:
return "Sample too left";
case FIDO_BIO_ENROLL_FP_TOO_RIGHT:
return "Sample too right";
case FIDO_BIO_ENROLL_FP_TOO_FAST:
return "Sample too fast";
case FIDO_BIO_ENROLL_FP_TOO_SLOW:
return "Sample too slow";
case FIDO_BIO_ENROLL_FP_POOR_QUALITY:
return "Poor quality sample";
case FIDO_BIO_ENROLL_FP_TOO_SKEWED:
return "Sample too skewed";
case FIDO_BIO_ENROLL_FP_TOO_SHORT:
return "Sample too short";
case FIDO_BIO_ENROLL_FP_MERGE_FAILURE:
return "Sample merge failure";
case FIDO_BIO_ENROLL_FP_EXISTS:
return "Sample exists";
case FIDO_BIO_ENROLL_FP_DATABASE_FULL:
return "Fingerprint database full";
case FIDO_BIO_ENROLL_NO_USER_ACTIVITY:
return "No user activity";
case FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION:
return "No user presence transition";
default:
return "Unknown error";
}
}
int
bio_enroll(const char *path)
{
fido_bio_template_t *t = NULL;
fido_bio_enroll_t *e = NULL;
fido_dev_t *dev = NULL;
char *pin = NULL;
int r, ok = 1;
if ((t = fido_bio_template_new()) == NULL)
errx(1, "fido_bio_template_new");
if ((e = fido_bio_enroll_new()) == NULL)
errx(1, "fido_bio_enroll_new");
dev = open_dev(path);
if ((pin = get_pin(path)) == NULL)
goto out;
printf("Touch your security key.\n");
r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
if (r != FIDO_OK) {
warnx("fido_bio_dev_enroll_begin: %s", fido_strerr(r));
goto out;
}
printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
while (fido_bio_enroll_remaining_samples(e) > 0) {
printf("Touch your security key (%u sample%s left).\n",
(unsigned)fido_bio_enroll_remaining_samples(e),
plural(fido_bio_enroll_remaining_samples(e)));
if ((r = fido_bio_dev_enroll_continue(dev, t, e,
10000)) != FIDO_OK) {
fido_dev_cancel(dev);
warnx("fido_bio_dev_enroll_continue: %s",
fido_strerr(r));
goto out;
}
printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
}
ok = 0;
out:
fido_bio_template_free(&t);
fido_bio_enroll_free(&e);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
bio_delete(const char *path, const char *id)
{
fido_bio_template_t *t = NULL;
fido_dev_t *dev = NULL;
char *pin = NULL;
void *id_blob_ptr = NULL;
size_t id_blob_len = 0;
int r, ok = 1;
if ((t = fido_bio_template_new()) == NULL)
errx(1, "fido_bio_template_new");
if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
errx(1, "base64_decode");
if ((r = fido_bio_template_set_id(t, id_blob_ptr,
id_blob_len)) != FIDO_OK)
errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
dev = open_dev(path);
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_bio_dev_enroll_remove(dev, t, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
if (r != FIDO_OK) {
warnx("fido_bio_dev_enroll_remove: %s", fido_strerr(r));
goto out;
}
ok = 0;
out:
free(id_blob_ptr);
fido_bio_template_free(&t);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
static const char *
type_str(uint8_t t)
{
switch (t) {
case 1:
return "touch";
case 2:
return "swipe";
default:
return "unknown";
}
}
void
bio_info(fido_dev_t *dev)
{
fido_bio_info_t *i = NULL;
if ((i = fido_bio_info_new()) == NULL) {
warnx("fido_bio_info_new");
return;
}
if (fido_bio_dev_get_info(dev, i) != FIDO_OK) {
fido_bio_info_free(&i);
return;
}
printf("sensor type: %u (%s)\n", (unsigned)fido_bio_info_type(i),
type_str(fido_bio_info_type(i)));
printf("max samples: %u\n", (unsigned)fido_bio_info_max_samples(i));
fido_bio_info_free(&i);
}

198
tools/config.c Normal file
View File

@@ -0,0 +1,198 @@
/*
* Copyright (c) 2020 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fido.h>
#include <fido/config.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
int
config_entattest(char *path)
{
fido_dev_t *dev;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
if ((r = fido_dev_enable_entattest(dev, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_enable_entattest(dev, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_dev_enable_entattest: %s (0x%x)",
fido_strerr(r), r);
goto out;
}
ok = 0;
out:
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
config_always_uv(char *path, int toggle)
{
fido_dev_t *dev;
char *pin = NULL;
int v, r, ok = 1;
dev = open_dev(path);
if (get_devopt(dev, "alwaysUv", &v) < 0) {
warnx("%s: getdevopt", __func__);
goto out;
}
if (v == -1) {
warnx("%s: option not found", __func__);
goto out;
}
if (v == toggle) {
ok = 0;
goto out;
}
if ((r = fido_dev_toggle_always_uv(dev, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_toggle_always_uv(dev, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_dev_toggle_always_uv: %s (0x%x)",
fido_strerr(r), r);
goto out;
}
ok = 0;
out:
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
config_pin_minlen(char *path, const char *pinlen)
{
fido_dev_t *dev;
char *pin = NULL;
int len, r, ok = 1;
dev = open_dev(path);
if ((len = base10(pinlen)) < 0 || len > 63) {
warnx("%s: len > 63", __func__);
goto out;
}
if ((r = fido_dev_set_pin_minlen(dev, (size_t)len, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_set_pin_minlen(dev, (size_t)len, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_dev_set_pin_minlen: %s (0x%x)", fido_strerr(r), r);
goto out;
}
ok = 0;
out:
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
config_force_pin_change(char *path)
{
fido_dev_t *dev;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
if ((r = fido_dev_force_pin_change(dev, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_force_pin_change(dev, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_dev_force_pin_change: %s (0x%x)", fido_strerr(r), r);
goto out;
}
ok = 0;
out:
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
config_pin_minlen_rpid(char *path, const char *rpids)
{
fido_dev_t *dev;
char *otmp, *tmp, *cp;
char *pin = NULL, **rpid = NULL;
int r, ok = 1;
size_t n;
if ((tmp = strdup(rpids)) == NULL)
err(1, "strdup");
otmp = tmp;
for (n = 0; (cp = strsep(&tmp, ",")) != NULL; n++) {
if (n == SIZE_MAX || (rpid = recallocarray(rpid, n, n + 1,
sizeof(*rpid))) == NULL)
err(1, "recallocarray");
if ((rpid[n] = strdup(cp)) == NULL)
err(1, "strdup");
if (*rpid[n] == '\0')
errx(1, "empty rpid");
}
free(otmp);
if (rpid == NULL || n == 0)
errx(1, "could not parse rp_id");
dev = open_dev(path);
if ((r = fido_dev_set_pin_minlen_rpid(dev, (const char * const *)rpid,
n, NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_set_pin_minlen_rpid(dev, (const char * const *)rpid,
n, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_dev_set_pin_minlen_rpid: %s (0x%x)",
fido_strerr(r), r);
goto out;
}
ok = 0;
out:
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}

255
tools/cred_make.c Normal file
View File

@@ -0,0 +1,255 @@
/*
* Copyright (c) 2018-2023 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 <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 fido_cred_t *
prepare_cred(FILE *in_f, int type, int flags)
{
fido_cred_t *cred = NULL;
struct blob cdh;
struct blob uid;
char *rpid = NULL;
char *uname = NULL;
int r;
memset(&cdh, 0, sizeof(cdh));
memset(&uid, 0, sizeof(uid));
r = base64_read(in_f, &cdh);
r |= string_read(in_f, &rpid);
r |= string_read(in_f, &uname);
r |= base64_read(in_f, &uid);
if (r < 0)
errx(1, "input error");
if (flags & FLAG_DEBUG) {
fprintf(stderr, "client data%s:\n",
flags & FLAG_CD ? "" : " hash");
xxd(cdh.ptr, cdh.len);
fprintf(stderr, "relying party id: %s\n", rpid);
fprintf(stderr, "user name: %s\n", uname);
fprintf(stderr, "user id:\n");
xxd(uid.ptr, uid.len);
}
if ((cred = fido_cred_new()) == NULL)
errx(1, "fido_cred_new");
if (flags & FLAG_CD)
r = fido_cred_set_clientdata(cred, cdh.ptr, cdh.len);
else
r = fido_cred_set_clientdata_hash(cred, cdh.ptr, cdh.len);
if (r != FIDO_OK || (r = fido_cred_set_type(cred, type)) != FIDO_OK ||
(r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK ||
(r = fido_cred_set_user(cred, uid.ptr, uid.len, uname, NULL,
NULL)) != FIDO_OK)
errx(1, "fido_cred_set: %s", fido_strerr(r));
if (flags & FLAG_RK) {
if ((r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
errx(1, "fido_cred_set_rk: %s", fido_strerr(r));
}
if (flags & FLAG_UV) {
if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
errx(1, "fido_cred_set_uv: %s", fido_strerr(r));
}
if (flags & FLAG_HMAC) {
if ((r = fido_cred_set_extensions(cred,
FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
errx(1, "fido_cred_set_extensions: %s", fido_strerr(r));
}
if (flags & FLAG_LARGEBLOB) {
if ((r = fido_cred_set_extensions(cred,
FIDO_EXT_LARGEBLOB_KEY)) != FIDO_OK)
errx(1, "fido_cred_set_extensions: %s", fido_strerr(r));
}
free(cdh.ptr);
free(uid.ptr);
free(rpid);
free(uname);
return (cred);
}
static void
print_attcred(FILE *out_f, const fido_cred_t *cred)
{
char *cdh = NULL;
char *authdata = NULL;
char *id = NULL;
char *sig = NULL;
char *x5c = NULL;
char *key = NULL;
int r;
r = base64_encode(fido_cred_clientdata_hash_ptr(cred),
fido_cred_clientdata_hash_len(cred), &cdh);
r |= base64_encode(fido_cred_authdata_ptr(cred),
fido_cred_authdata_len(cred), &authdata);
r |= base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred),
&id);
r |= base64_encode(fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
&sig);
if (fido_cred_x5c_ptr(cred) != NULL)
r |= base64_encode(fido_cred_x5c_ptr(cred),
fido_cred_x5c_len(cred), &x5c);
if (fido_cred_largeblob_key_ptr(cred) != NULL)
r |= base64_encode(fido_cred_largeblob_key_ptr(cred),
fido_cred_largeblob_key_len(cred), &key);
if (r < 0)
errx(1, "output error");
fprintf(out_f, "%s\n", cdh);
fprintf(out_f, "%s\n", fido_cred_rp_id(cred));
fprintf(out_f, "%s\n", fido_cred_fmt(cred));
fprintf(out_f, "%s\n", authdata);
fprintf(out_f, "%s\n", id);
fprintf(out_f, "%s\n", sig);
if (x5c != NULL)
fprintf(out_f, "%s\n", x5c);
if (key != NULL) {
fprintf(out_f, "%s\n", key);
explicit_bzero(key, strlen(key));
}
free(cdh);
free(authdata);
free(id);
free(sig);
free(x5c);
free(key);
}
int
cred_make(int argc, char **argv)
{
fido_dev_t *dev = NULL;
fido_cred_t *cred = NULL;
char prompt[1024];
char pin[128];
char *in_path = NULL;
char *out_path = NULL;
FILE *in_f = NULL;
FILE *out_f = NULL;
int type = COSE_ES256;
int flags = 0;
int cred_protect = -1;
int ch;
int r;
while ((ch = getopt(argc, argv, "bc:dhi:o:qruvw")) != -1) {
switch (ch) {
case 'b':
flags |= FLAG_LARGEBLOB;
break;
case 'c':
if ((cred_protect = base10(optarg)) < 0)
errx(1, "-c: invalid argument '%s'", optarg);
break;
case 'd':
flags |= FLAG_DEBUG;
break;
case 'h':
flags |= FLAG_HMAC;
break;
case 'i':
in_path = optarg;
break;
case 'o':
out_path = optarg;
break;
case 'q':
flags |= FLAG_QUIET;
break;
case 'r':
flags |= FLAG_RK;
break;
case 'u':
flags |= FLAG_U2F;
break;
case 'v':
flags |= FLAG_UV;
break;
case 'w':
flags |= FLAG_CD;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1 || argc > 2)
usage();
in_f = open_read(in_path);
out_f = open_write(out_path);
if (argc > 1 && cose_type(argv[1], &type) < 0)
errx(1, "unknown type %s", argv[1]);
fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
cred = prepare_cred(in_f, type, flags);
dev = open_dev(argv[0]);
if (flags & FLAG_U2F)
fido_dev_force_u2f(dev);
if (cred_protect > 0) {
r = fido_cred_set_prot(cred, cred_protect);
if (r != FIDO_OK) {
errx(1, "fido_cred_set_prot: %s", fido_strerr(r));
}
}
r = fido_dev_make_cred(dev, cred, NULL);
if (r == FIDO_ERR_PIN_REQUIRED && !(flags & FLAG_QUIET)) {
r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
argv[0]);
if (r < 0 || (size_t)r >= sizeof(prompt))
errx(1, "snprintf");
if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
errx(1, "readpassphrase");
if (strlen(pin) < 4 || strlen(pin) > 63) {
explicit_bzero(pin, sizeof(pin));
errx(1, "invalid PIN length");
}
r = fido_dev_make_cred(dev, cred, pin);
}
explicit_bzero(pin, sizeof(pin));
if (r != FIDO_OK)
errx(1, "fido_dev_make_cred: %s", fido_strerr(r));
print_attcred(out_f, cred);
fido_dev_close(dev);
fido_dev_free(&dev);
fido_cred_free(&cred);
fclose(in_f);
fclose(out_f);
in_f = NULL;
out_f = NULL;
exit(0);
}

182
tools/cred_verify.c Normal file
View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) 2018 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 <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 fido_cred_t *
prepare_cred(FILE *in_f, int type, int flags)
{
fido_cred_t *cred = NULL;
struct blob cdh;
struct blob authdata;
struct blob id;
struct blob sig;
struct blob x5c;
char *rpid = NULL;
char *fmt = NULL;
int r;
memset(&cdh, 0, sizeof(cdh));
memset(&authdata, 0, sizeof(authdata));
memset(&id, 0, sizeof(id));
memset(&sig, 0, sizeof(sig));
memset(&x5c, 0, sizeof(x5c));
r = base64_read(in_f, &cdh);
r |= string_read(in_f, &rpid);
r |= string_read(in_f, &fmt);
r |= base64_read(in_f, &authdata);
r |= base64_read(in_f, &id);
r |= base64_read(in_f, &sig);
if (r < 0)
errx(1, "input error");
(void)base64_read(in_f, &x5c);
if (flags & FLAG_DEBUG) {
fprintf(stderr, "client data hash:\n");
xxd(cdh.ptr, cdh.len);
fprintf(stderr, "relying party id: %s\n", rpid);
fprintf(stderr, "format: %s\n", fmt);
fprintf(stderr, "authenticator data:\n");
xxd(authdata.ptr, authdata.len);
fprintf(stderr, "credential id:\n");
xxd(id.ptr, id.len);
fprintf(stderr, "signature:\n");
xxd(sig.ptr, sig.len);
fprintf(stderr, "x509:\n");
xxd(x5c.ptr, x5c.len);
}
if ((cred = fido_cred_new()) == NULL)
errx(1, "fido_cred_new");
if ((r = fido_cred_set_type(cred, type)) != FIDO_OK ||
(r = fido_cred_set_clientdata_hash(cred, cdh.ptr,
cdh.len)) != FIDO_OK ||
(r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK ||
(r = fido_cred_set_authdata(cred, authdata.ptr,
authdata.len)) != FIDO_OK ||
(r = fido_cred_set_sig(cred, sig.ptr, sig.len)) != FIDO_OK ||
(r = fido_cred_set_fmt(cred, fmt)) != FIDO_OK)
errx(1, "fido_cred_set: %s", fido_strerr(r));
if (x5c.ptr != NULL) {
if ((r = fido_cred_set_x509(cred, x5c.ptr, x5c.len)) != FIDO_OK)
errx(1, "fido_cred_set_x509: %s", fido_strerr(r));
}
if (flags & FLAG_UV) {
if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
errx(1, "fido_cred_set_uv: %s", fido_strerr(r));
}
if (flags & FLAG_HMAC) {
if ((r = fido_cred_set_extensions(cred,
FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
errx(1, "fido_cred_set_extensions: %s", fido_strerr(r));
}
free(cdh.ptr);
free(authdata.ptr);
free(id.ptr);
free(sig.ptr);
free(x5c.ptr);
free(rpid);
free(fmt);
return (cred);
}
int
cred_verify(int argc, char **argv)
{
fido_cred_t *cred = NULL;
char *in_path = NULL;
char *out_path = NULL;
FILE *in_f = NULL;
FILE *out_f = NULL;
int type = COSE_ES256;
int flags = 0;
int cred_prot = -1;
int ch;
int r;
while ((ch = getopt(argc, argv, "c:dhi:o:v")) != -1) {
switch (ch) {
case 'c':
if ((cred_prot = base10(optarg)) < 0)
errx(1, "-c: invalid argument '%s'", optarg);
break;
case 'd':
flags |= FLAG_DEBUG;
break;
case 'h':
flags |= FLAG_HMAC;
break;
case 'i':
in_path = optarg;
break;
case 'o':
out_path = optarg;
break;
case 'v':
flags |= FLAG_UV;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 1)
usage();
in_f = open_read(in_path);
out_f = open_write(out_path);
if (argc > 0 && cose_type(argv[0], &type) < 0)
errx(1, "unknown type %s", argv[0]);
fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
cred = prepare_cred(in_f, type, flags);
if (cred_prot > 0) {
r = fido_cred_set_prot(cred, cred_prot);
if (r != FIDO_OK) {
errx(1, "fido_cred_set_prot: %s", fido_strerr(r));
}
}
if (fido_cred_x5c_ptr(cred) == NULL) {
if ((r = fido_cred_verify_self(cred)) != FIDO_OK)
errx(1, "fido_cred_verify_self: %s", fido_strerr(r));
} else {
if ((r = fido_cred_verify(cred)) != FIDO_OK)
errx(1, "fido_cred_verify: %s", fido_strerr(r));
}
print_cred(out_f, type, cred);
fido_cred_free(&cred);
fclose(in_f);
fclose(out_f);
in_f = NULL;
out_f = NULL;
exit(0);
}

330
tools/credman.c Normal file
View File

@@ -0,0 +1,330 @@
/*
* Copyright (c) 2019 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 <fido/credman.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"
int
credman_get_metadata(fido_dev_t *dev, const char *path)
{
fido_credman_metadata_t *metadata = NULL;
char *pin = NULL;
int r, ok = 1;
if ((metadata = fido_credman_metadata_new()) == NULL) {
warnx("fido_credman_metadata_new");
goto out;
}
if ((r = fido_credman_get_dev_metadata(dev, metadata,
NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_get_dev_metadata(dev, metadata, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_get_dev_metadata: %s", fido_strerr(r));
goto out;
}
printf("existing rk(s): %u\n",
(unsigned)fido_credman_rk_existing(metadata));
printf("remaining rk(s): %u\n",
(unsigned)fido_credman_rk_remaining(metadata));
ok = 0;
out:
fido_credman_metadata_free(&metadata);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
static int
print_rp(fido_credman_rp_t *rp, size_t idx)
{
char *rp_id_hash = NULL;
if (base64_encode(fido_credman_rp_id_hash_ptr(rp, idx),
fido_credman_rp_id_hash_len(rp, idx), &rp_id_hash) < 0) {
warnx("output error");
return -1;
}
printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash,
fido_credman_rp_id(rp, idx));
free(rp_id_hash);
return 0;
}
int
credman_list_rp(const char *path)
{
fido_credman_rp_t *rp = NULL;
fido_dev_t *dev = NULL;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
if ((rp = fido_credman_rp_new()) == NULL) {
warnx("fido_credman_rp_new");
goto out;
}
if ((r = fido_credman_get_dev_rp(dev, rp, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_get_dev_rp(dev, rp, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
goto out;
}
for (size_t i = 0; i < fido_credman_rp_count(rp); i++)
if (print_rp(rp, i) < 0)
goto out;
ok = 0;
out:
fido_credman_rp_free(&rp);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
static int
print_rk(const fido_credman_rk_t *rk, size_t idx)
{
const fido_cred_t *cred;
char *id = NULL;
char *user_id = NULL;
const char *type;
const char *prot;
if ((cred = fido_credman_rk(rk, idx)) == NULL) {
warnx("fido_credman_rk");
return -1;
}
if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred),
&id) < 0 || base64_encode(fido_cred_user_id_ptr(cred),
fido_cred_user_id_len(cred), &user_id) < 0) {
warnx("output error");
return -1;
}
type = cose_string(fido_cred_type(cred));
prot = prot_string(fido_cred_prot(cred));
printf("%02u: %s %s %s %s %s\n", (unsigned)idx, id,
fido_cred_display_name(cred), user_id, type, prot);
free(user_id);
free(id);
return 0;
}
int
credman_list_rk(const char *path, const char *rp_id)
{
fido_dev_t *dev = NULL;
fido_credman_rk_t *rk = NULL;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
if ((rk = fido_credman_rk_new()) == NULL) {
warnx("fido_credman_rk_new");
goto out;
}
if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_get_dev_rk: %s", fido_strerr(r));
goto out;
}
for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
if (print_rk(rk, i) < 0)
goto out;
ok = 0;
out:
fido_credman_rk_free(&rk);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
credman_print_rk(fido_dev_t *dev, const char *path, const char *rp_id,
const char *cred_id)
{
fido_credman_rk_t *rk = NULL;
const fido_cred_t *cred = NULL;
char *pin = NULL;
void *cred_id_ptr = NULL;
size_t cred_id_len = 0;
int r, ok = 1;
if ((rk = fido_credman_rk_new()) == NULL) {
warnx("fido_credman_rk_new");
goto out;
}
if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) {
warnx("base64_decode");
goto out;
}
if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_get_dev_rk: %s", fido_strerr(r));
goto out;
}
for (size_t i = 0; i < fido_credman_rk_count(rk); i++) {
if ((cred = fido_credman_rk(rk, i)) == NULL ||
fido_cred_id_ptr(cred) == NULL) {
warnx("output error");
goto out;
}
if (cred_id_len != fido_cred_id_len(cred) ||
memcmp(cred_id_ptr, fido_cred_id_ptr(cred), cred_id_len))
continue;
print_cred(stdout, fido_cred_type(cred), cred);
ok = 0;
goto out;
}
warnx("credential not found");
out:
free(cred_id_ptr);
fido_credman_rk_free(&rk);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
credman_delete_rk(const char *path, const char *id)
{
fido_dev_t *dev = NULL;
char *pin = NULL;
void *id_ptr = NULL;
size_t id_len = 0;
int r, ok = 1;
dev = open_dev(path);
if (base64_decode(id, &id_ptr, &id_len) < 0) {
warnx("base64_decode");
goto out;
}
if ((r = fido_credman_del_dev_rk(dev, id_ptr, id_len,
NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_del_dev_rk: %s", fido_strerr(r));
goto out;
}
ok = 0;
out:
free(id_ptr);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
credman_update_rk(const char *path, const char *user_id, const char *cred_id,
const char *name, const char *display_name)
{
fido_dev_t *dev = NULL;
fido_cred_t *cred = NULL;
char *pin = NULL;
void *user_id_ptr = NULL;
void *cred_id_ptr = NULL;
size_t user_id_len = 0;
size_t cred_id_len = 0;
int r, ok = 1;
dev = open_dev(path);
if (base64_decode(user_id, &user_id_ptr, &user_id_len) < 0 ||
base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) {
warnx("base64_decode");
goto out;
}
if ((cred = fido_cred_new()) == NULL) {
warnx("fido_cred_new");
goto out;
}
if ((r = fido_cred_set_id(cred, cred_id_ptr, cred_id_len)) != FIDO_OK) {
warnx("fido_cred_set_id: %s", fido_strerr(r));
goto out;
}
if ((r = fido_cred_set_user(cred, user_id_ptr, user_id_len, name,
display_name, NULL)) != FIDO_OK) {
warnx("fido_cred_set_user: %s", fido_strerr(r));
goto out;
}
if ((r = fido_credman_set_dev_rk(dev, cred, NULL)) != FIDO_OK &&
should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_set_dev_rk(dev, cred, pin);
freezero(pin, PINBUF_LEN);
pin = NULL;
}
if (r != FIDO_OK) {
warnx("fido_credman_set_dev_rk: %s", fido_strerr(r));
goto out;
}
ok = 0;
out:
free(user_id_ptr);
free(cred_id_ptr);
fido_dev_close(dev);
fido_dev_free(&dev);
fido_cred_free(&cred);
exit(ok);
}

103
tools/extern.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2018-2023 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
*/
#ifndef _EXTERN_H_
#define _EXTERN_H_
#include <sys/types.h>
#include <openssl/ec.h>
#include <fido.h>
#include <stddef.h>
#include <stdio.h>
struct blob {
unsigned char *ptr;
size_t len;
};
#define TOKEN_OPT "CDGILPRSVabcdefi:k:l:m:n:p:w:ru"
#define FLAG_DEBUG 0x001
#define FLAG_QUIET 0x002
#define FLAG_RK 0x004
#define FLAG_UV 0x008
#define FLAG_U2F 0x010
#define FLAG_HMAC 0x020
#define FLAG_UP 0x040
#define FLAG_LARGEBLOB 0x080
#define FLAG_CD 0x100
extern char* global_pin;
#define PINBUF_LEN 256
EC_KEY *read_ec_pubkey(const char *);
fido_dev_t *open_dev(const char *);
FILE *open_read(const char *);
FILE *open_write(const char *);
char *get_pin(const char *);
const char *plural(size_t);
const char *cose_string(int);
const char *prot_string(int);
int assert_get(int, char **);
int assert_verify(int, char **);
int base64_decode(const char *, void **, size_t *);
int base64_encode(const void *, size_t, char **);
int base64_read(FILE *, struct blob *);
int bio_delete(const char *, const char *);
int bio_enroll(const char *);
void bio_info(fido_dev_t *);
int bio_list(const char *);
int bio_set_name(const char *, const char *, const char *);
int blob_clean(const char *);
int blob_list(const char *);
int blob_delete(const char *, const char *, const char *, const char *);
int blob_get(const char *, const char *, const char *, const char *,
const char *);
int blob_set(const char *, const char *, const char *, const char *,
const char *);
int config_always_uv(char *, int);
int config_entattest(char *);
int config_force_pin_change(char *);
int config_pin_minlen(char *, const char *);
int config_pin_minlen_rpid(char *, const char *);
int cose_type(const char *, int *);
int cred_make(int, char **);
int cred_verify(int, char **);
int credman_delete_rk(const char *, const char *);
int credman_update_rk(const char *, const char *, const char *, const char *,
const char *);
int credman_get_metadata(fido_dev_t *, const char *);
int credman_list_rk(const char *, const char *);
int credman_list_rp(const char *);
int credman_print_rk(fido_dev_t *, const char *, const char *, const char *);
int get_devopt(fido_dev_t *, const char *, int *);
int pin_change(char *);
int pin_set(char *);
int should_retry_with_pin(const fido_dev_t *, int);
int string_read(FILE *, char **);
int token_config(int, char **, char *);
int token_delete(int, char **, char *);
int token_get(int, char **, char *);
int token_info(int, char **, char *);
int token_list(int, char **, char *);
int token_reset(char *);
int token_set(int, char **, char *);
int write_es256_pubkey(FILE *, const void *, size_t);
int write_es384_pubkey(FILE *, const void *, size_t);
int write_rsa_pubkey(FILE *, const void *, size_t);
int read_file(const char *, u_char **, size_t *);
int write_file(const char *, const u_char *, size_t);
RSA *read_rsa_pubkey(const char *);
EVP_PKEY *read_eddsa_pubkey(const char *);
int write_eddsa_pubkey(FILE *, const void *, size_t);
void print_cred(FILE *, int, const fido_cred_t *);
void usage(void);
void xxd(const void *, size_t);
int base10(const char *);
#endif /* _EXTERN_H_ */

15
tools/fido2-attach.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Copyright (c) 2020 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
DEV=""
while [ -z "${DEV}" ]; do
sleep .5
DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')"
done
printf '%s\n' "${DEV}"

13
tools/fido2-detach.sh Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Copyright (c) 2020 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
DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')"
while [ -n "${DEV}" ]; do
sleep .5
DEV="$(fido2-token -L | sed 's/^\(.*\): .*$/\1/;q')"
done

124
tools/fido2-token.c Normal file
View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2018 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 <stdio.h>
#include <stdlib.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
static int action;
void
usage(void)
{
fprintf(stderr,
"usage: fido2-token -C [-d] device\n"
" fido2-token -Db [-k key_path] [-i cred_id -n rp_id] device\n"
" fido2-token -Dei template_id device\n"
" fido2-token -Du device\n"
" fido2-token -Gb [-k key_path] [-i cred_id -n rp_id] blob_path device\n"
" fido2-token -I [-cd] [-k rp_id -i cred_id] device\n"
" fido2-token -L [-bder] [-k rp_id] [device]\n"
" fido2-token -R [-d] device\n"
" fido2-token -S [-adefu] [-l pin_length] [-i template_id -n template_name] device\n"
" fido2-token -Sb [-k key_path] [-i cred_id -n rp_id] blob_path device\n"
" fido2-token -Sc -i cred_id -k user_id -n name -p display_name device\n"
" fido2-token -Sm rp_id device\n"
" fido2-token -V\n"
);
exit(1);
}
char* global_pin = NULL;
static void
setaction(int ch)
{
if (action)
usage();
action = ch;
}
int
main(int argc, char **argv)
{
int ch;
int flags = 0;
char *device;
// Parse command line arguments
for (int i = 1; i < argc - 1; i++) {
if (strcmp(argv[i], "-w") == 0) {
// Set global_pin from command line argument
global_pin = strdup(argv[i + 1]);
break;
}
}
while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
switch (ch) {
case 'a':
case 'b':
case 'c':
case 'e':
case 'f':
case 'i':
case 'k':
case 'l':
case 'm':
case 'n':
case 'w':
case 'p':
case 'r':
case 'u':
break; /* ignore */
case 'd':
flags = FIDO_DEBUG;
break;
default:
setaction(ch);
break;
}
}
if (argc - optind < 1)
device = NULL;
else
device = argv[argc - 1];
fido_init(flags);
switch (action) {
case 'C':
return (pin_change(device));
case 'D':
return (token_delete(argc, argv, device));
case 'G':
return (token_get(argc, argv, device));
case 'I':
return (token_info(argc, argv, device));
case 'L':
return (token_list(argc, argv, device));
case 'R':
return (token_reset(device));
case 'S':
return (token_set(argc, argv, device));
case 'V':
fprintf(stderr, "%d.%d.%d\n", _FIDO_MAJOR, _FIDO_MINOR,
_FIDO_PATCH);
exit(0);
}
usage();
/* NOTREACHED */
}

76
tools/fido2-unprot.sh Normal file
View File

@@ -0,0 +1,76 @@
#!/bin/sh
# Copyright (c) 2020 Fabian Henneke.
# 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
if [ $(uname) != "Linux" ] ; then
echo "Can only run on Linux"
exit 1
fi
TOKEN_VERSION=$(${FIDO_TOOLS_PREFIX}fido2-token -V 2>&1)
if [ $? -ne 0 ] ; then
echo "Please install libfido2 1.5.0 or higher"
exit
fi
TOKEN_VERSION_MAJOR=$(echo "$TOKEN_VERSION" | cut -d. -f1)
TOKEN_VERSION_MINOR=$(echo "$TOKEN_VERSION" | cut -d. -f2)
if [ $TOKEN_VERSION_MAJOR -eq 0 -o $TOKEN_VERSION_MAJOR -eq 1 -a $TOKEN_VERSION_MINOR -lt 5 ] ; then
echo "Please install libfido2 1.5.0 or higher (current version: $TOKEN_VERSION)"
exit 1
fi
set -e
TOKEN_OUTPUT=$(${FIDO_TOOLS_PREFIX}fido2-token -L)
DEV_PATH_NAMES=$(echo "$TOKEN_OUTPUT" | sed -r 's/^(.*): .*\((.*)\)$/\1 \2/g')
DEV_COUNT=$(echo "$DEV_PATH_NAMES" | wc -l)
for i in $(seq 1 $DEV_COUNT)
do
DEV_PATH_NAME=$(echo "$DEV_PATH_NAMES" | sed "${i}q;d")
DEV_PATH=$(echo "$DEV_PATH_NAME" | cut -d' ' -f1)
DEV_NAME=$(echo "$DEV_PATH_NAME" | cut -d' ' -f1 --complement)
DEV_PRETTY=$(echo "$DEV_NAME (at '$DEV_PATH')")
if expr match "$(${FIDO_TOOLS_PREFIX}fido2-token -I $DEV_PATH)" ".* credMgmt.* clientPin.*\|.* clientPin.* credMgmt.*" > /dev/null ; then
printf "Enter PIN for $DEV_PRETTY once (ignore further prompts): "
stty -echo
read PIN
stty echo
printf "\n"
RESIDENT_RPS=$(echo "${PIN}\n" | setsid -w ${FIDO_TOOLS_PREFIX}fido2-token -L -r $DEV_PATH | cut -d' ' -f3)
printf "\n"
RESIDENT_RPS_COUNT=$(echo "$RESIDENT_RPS" | wc -l)
FOUND=0
for j in $(seq 1 $DEV_RESIDENT_RPS_COUNT)
do
RESIDENT_RP=$(echo "$RESIDENT_RPS" | sed "${j}q;d")
UNPROT_CREDS=$(echo "${PIN}\n" | setsid -w ${FIDO_TOOLS_PREFIX}fido2-token -L -k $RESIDENT_RP $DEV_PATH | grep ' uvopt$' | cut -d' ' -f2,3,4)
printf "\n"
UNPROT_CREDS_COUNT=$(echo "$UNPROT_CREDS" | wc -l)
if [ $UNPROT_CREDS_COUNT -gt 0 ] ; then
FOUND=1
echo "Unprotected credentials on $DEV_PRETTY for '$RESIDENT_RP':"
echo "$UNPROT_CREDS"
fi
done
if [ $FOUND -eq 0 ] ; then
echo "No unprotected credentials on $DEV_PRETTY"
fi
else
echo "$DEV_PRETTY cannot enumerate credentials"
echo "Discovering unprotected SSH credentials only..."
STUB_HASH=$(echo -n "" | openssl sha256 -binary | base64)
printf "$STUB_HASH\nssh:\n" | ${FIDO_TOOLS_PREFIX}fido2-assert -G -r -t up=false $DEV_PATH 2> /dev/null || ASSERT_EXIT_CODE=$?
if [ $ASSERT_EXIT_CODE -eq 0 ] ; then
echo "Found an unprotected SSH credential on $DEV_PRETTY!"
else
echo "No unprotected SSH credentials (default settings) on $DEV_PRETTY"
fi
fi
printf "\n"
done

22
tools/include_check.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
# Copyright (c) 2019 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
check() {
for f in $(find $1 -maxdepth 1 -name '*.h'); do
echo "#include \"$f\"" | \
cc $CFLAGS -Isrc -xc -c - -o /dev/null 2>&1
echo "$f $CFLAGS $?"
done
}
check examples
check fuzz
check openbsd-compat
CFLAGS="${CFLAGS} -D_FIDO_INTERNAL" check src
check src/fido.h
check src/fido
check tools

618
tools/largeblob.c Normal file
View File

@@ -0,0 +1,618 @@
/*
* Copyright (c) 2020-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 <sys/types.h>
#include <sys/stat.h>
#include <fido.h>
#include <fido/credman.h>
#include <cbor.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <zlib.h>
#include "../openbsd-compat/openbsd-compat.h"
#include "extern.h"
#define BOUND (1024UL * 1024UL)
struct rkmap {
fido_credman_rp_t *rp; /* known rps */
fido_credman_rk_t **rk; /* rk per rp */
};
static void
free_rkmap(struct rkmap *map)
{
if (map->rp != NULL) {
for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++)
fido_credman_rk_free(&map->rk[i]);
fido_credman_rp_free(&map->rp);
}
free(map->rk);
}
static int
map_known_rps(fido_dev_t *dev, const char *path, struct rkmap *map)
{
const char *rp_id;
char *pin = NULL;
size_t n;
int r, ok = -1;
if ((map->rp = fido_credman_rp_new()) == NULL) {
warnx("%s: fido_credman_rp_new", __func__);
goto out;
}
if ((pin = get_pin(path)) == NULL)
goto out;
if ((r = fido_credman_get_dev_rp(dev, map->rp, pin)) != FIDO_OK) {
warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
goto out;
}
if ((n = fido_credman_rp_count(map->rp)) > UINT8_MAX) {
warnx("%s: fido_credman_rp_count > UINT8_MAX", __func__);
goto out;
}
if ((map->rk = calloc(n, sizeof(*map->rk))) == NULL) {
warnx("%s: calloc", __func__);
goto out;
}
for (size_t i = 0; i < n; i++) {
if ((rp_id = fido_credman_rp_id(map->rp, i)) == NULL) {
warnx("%s: fido_credman_rp_id %zu", __func__, i);
goto out;
}
if ((map->rk[i] = fido_credman_rk_new()) == NULL) {
warnx("%s: fido_credman_rk_new", __func__);
goto out;
}
if ((r = fido_credman_get_dev_rk(dev, rp_id, map->rk[i],
pin)) != FIDO_OK) {
warnx("%s: fido_credman_get_dev_rk %s: %s", __func__,
rp_id, fido_strerr(r));
goto out;
}
}
ok = 0;
out:
freezero(pin, PINBUF_LEN);
return ok;
}
static int
lookup_key(const char *path, fido_dev_t *dev, const char *rp_id,
const struct blob *cred_id, char **pin, struct blob *key)
{
fido_credman_rk_t *rk = NULL;
const fido_cred_t *cred = NULL;
size_t i, n;
int r, ok = -1;
if ((rk = fido_credman_rk_new()) == NULL) {
warnx("%s: fido_credman_rk_new", __func__);
goto out;
}
if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin)) != FIDO_OK &&
*pin == NULL && should_retry_with_pin(dev, r)) {
if ((*pin = get_pin(path)) == NULL)
goto out;
r = fido_credman_get_dev_rk(dev, rp_id, rk, *pin);
}
if (r != FIDO_OK) {
warnx("%s: fido_credman_get_dev_rk: %s", __func__,
fido_strerr(r));
goto out;
}
if ((n = fido_credman_rk_count(rk)) == 0) {
warnx("%s: rp id not found", __func__);
goto out;
}
if (n == 1 && cred_id->len == 0) {
/* use the credential we found */
cred = fido_credman_rk(rk, 0);
} else {
if (cred_id->len == 0) {
warnx("%s: multiple credentials found", __func__);
goto out;
}
for (i = 0; i < n; i++) {
const fido_cred_t *x = fido_credman_rk(rk, i);
if (fido_cred_id_len(x) <= cred_id->len &&
!memcmp(fido_cred_id_ptr(x), cred_id->ptr,
fido_cred_id_len(x))) {
cred = x;
break;
}
}
}
if (cred == NULL) {
warnx("%s: credential not found", __func__);
goto out;
}
if (fido_cred_largeblob_key_ptr(cred) == NULL) {
warnx("%s: no associated blob key", __func__);
goto out;
}
key->len = fido_cred_largeblob_key_len(cred);
if ((key->ptr = malloc(key->len)) == NULL) {
warnx("%s: malloc", __func__);
goto out;
}
memcpy(key->ptr, fido_cred_largeblob_key_ptr(cred), key->len);
ok = 0;
out:
fido_credman_rk_free(&rk);
return ok;
}
static int
load_key(const char *keyf, const char *cred_id64, const char *rp_id,
const char *path, fido_dev_t *dev, char **pin, struct blob *key)
{
struct blob cred_id;
FILE *fp;
int r;
memset(&cred_id, 0, sizeof(cred_id));
if (keyf != NULL) {
if (rp_id != NULL || cred_id64 != NULL)
usage();
fp = open_read(keyf);
if ((r = base64_read(fp, key)) < 0)
warnx("%s: base64_read %s", __func__, keyf);
fclose(fp);
return r;
}
if (rp_id == NULL)
usage();
if (cred_id64 != NULL && base64_decode(cred_id64, (void *)&cred_id.ptr,
&cred_id.len) < 0) {
warnx("%s: base64_decode %s", __func__, cred_id64);
return -1;
}
r = lookup_key(path, dev, rp_id, &cred_id, pin, key);
free(cred_id.ptr);
return r;
}
int
blob_set(const char *path, const char *keyf, const char *rp_id,
const char *cred_id64, const char *blobf)
{
fido_dev_t *dev;
struct blob key, blob;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
memset(&key, 0, sizeof(key));
memset(&blob, 0, sizeof(blob));
if (read_file(blobf, &blob.ptr, &blob.len) < 0 ||
load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
goto out;
if ((r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
blob.len, pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_largeblob_set(dev, key.ptr, key.len, blob.ptr,
blob.len, pin);
}
if (r != FIDO_OK) {
warnx("fido_dev_largeblob_set: %s", fido_strerr(r));
goto out;
}
ok = 0; /* success */
out:
freezero(key.ptr, key.len);
freezero(blob.ptr, blob.len);
freezero(pin, PINBUF_LEN);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
blob_get(const char *path, const char *keyf, const char *rp_id,
const char *cred_id64, const char *blobf)
{
fido_dev_t *dev;
struct blob key, blob;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
memset(&key, 0, sizeof(key));
memset(&blob, 0, sizeof(blob));
if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
goto out;
if ((r = fido_dev_largeblob_get(dev, key.ptr, key.len, &blob.ptr,
&blob.len)) != FIDO_OK) {
warnx("fido_dev_largeblob_get: %s", fido_strerr(r));
goto out;
}
if (write_file(blobf, blob.ptr, blob.len) < 0)
goto out;
ok = 0; /* success */
out:
freezero(key.ptr, key.len);
freezero(blob.ptr, blob.len);
freezero(pin, PINBUF_LEN);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
int
blob_delete(const char *path, const char *keyf, const char *rp_id,
const char *cred_id64)
{
fido_dev_t *dev;
struct blob key;
char *pin = NULL;
int r, ok = 1;
dev = open_dev(path);
memset(&key, 0, sizeof(key));
if (load_key(keyf, cred_id64, rp_id, path, dev, &pin, &key) < 0)
goto out;
if ((r = fido_dev_largeblob_remove(dev, key.ptr, key.len,
pin)) != FIDO_OK && should_retry_with_pin(dev, r)) {
if ((pin = get_pin(path)) == NULL)
goto out;
r = fido_dev_largeblob_remove(dev, key.ptr, key.len, pin);
}
if (r != FIDO_OK) {
warnx("fido_dev_largeblob_remove: %s", fido_strerr(r));
goto out;
}
ok = 0; /* success */
out:
freezero(key.ptr, key.len);
freezero(pin, PINBUF_LEN);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}
static int
try_decompress(const struct blob *in, uint64_t origsiz, int wbits)
{
struct blob out;
z_stream zs;
u_int ilen, olen;
int ok = -1;
memset(&zs, 0, sizeof(zs));
memset(&out, 0, sizeof(out));
if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND)
return -1;
if (origsiz > SIZE_MAX || origsiz > UINT_MAX ||
(olen = (u_int)origsiz) > BOUND)
return -1;
if (inflateInit2(&zs, wbits) != Z_OK)
return -1;
if ((out.ptr = calloc(1, olen)) == NULL)
goto fail;
out.len = olen;
zs.next_in = in->ptr;
zs.avail_in = ilen;
zs.next_out = out.ptr;
zs.avail_out = olen;
if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
goto fail;
if (zs.avail_out != 0)
goto fail;
ok = 0;
fail:
if (inflateEnd(&zs) != Z_OK)
ok = -1;
freezero(out.ptr, out.len);
return ok;
}
static int
decompress(const struct blob *plaintext, uint64_t origsiz)
{
if (try_decompress(plaintext, origsiz, MAX_WBITS) == 0) /* rfc1950 */
return 0;
return try_decompress(plaintext, origsiz, -MAX_WBITS); /* rfc1951 */
}
static int
decode(const struct blob *ciphertext, const struct blob *nonce,
uint64_t origsiz, const fido_cred_t *cred)
{
uint8_t aad[4 + sizeof(uint64_t)];
EVP_CIPHER_CTX *ctx = NULL;
const EVP_CIPHER *cipher;
struct blob plaintext;
uint64_t tmp;
int ok = -1;
memset(&plaintext, 0, sizeof(plaintext));
if (nonce->len != 12)
return -1;
if (cred == NULL ||
fido_cred_largeblob_key_ptr(cred) == NULL ||
fido_cred_largeblob_key_len(cred) != 32)
return -1;
if (ciphertext->len > UINT_MAX ||
ciphertext->len > SIZE_MAX - 16 ||
ciphertext->len < 16)
return -1;
plaintext.len = ciphertext->len - 16;
if ((plaintext.ptr = calloc(1, plaintext.len)) == NULL)
return -1;
if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
(cipher = EVP_aes_256_gcm()) == NULL ||
EVP_CipherInit(ctx, cipher, fido_cred_largeblob_key_ptr(cred),
nonce->ptr, 0) == 0)
goto out;
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
ciphertext->ptr + ciphertext->len - 16) == 0)
goto out;
aad[0] = 0x62; /* b */
aad[1] = 0x6c; /* l */
aad[2] = 0x6f; /* o */
aad[3] = 0x62; /* b */
tmp = htole64(origsiz);
memcpy(&aad[4], &tmp, sizeof(uint64_t));
if (EVP_Cipher(ctx, NULL, aad, (u_int)sizeof(aad)) < 0 ||
EVP_Cipher(ctx, plaintext.ptr, ciphertext->ptr,
(u_int)plaintext.len) < 0 ||
EVP_Cipher(ctx, NULL, NULL, 0) < 0)
goto out;
if (decompress(&plaintext, origsiz) < 0)
goto out;
ok = 0;
out:
freezero(plaintext.ptr, plaintext.len);
if (ctx != NULL)
EVP_CIPHER_CTX_free(ctx);
return ok;
}
static const fido_cred_t *
try_rp(const fido_credman_rk_t *rk, const struct blob *ciphertext,
const struct blob *nonce, uint64_t origsiz)
{
const fido_cred_t *cred;
for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
if ((cred = fido_credman_rk(rk, i)) != NULL &&
decode(ciphertext, nonce, origsiz, cred) == 0)
return cred;
return NULL;
}
static int
decode_cbor_blob(struct blob *out, const cbor_item_t *item)
{
if (out->ptr != NULL ||
cbor_isa_bytestring(item) == false ||
cbor_bytestring_is_definite(item) == false)
return -1;
out->len = cbor_bytestring_length(item);
if ((out->ptr = malloc(out->len)) == NULL)
return -1;
memcpy(out->ptr, cbor_bytestring_handle(item), out->len);
return 0;
}
static int
decode_blob_entry(const cbor_item_t *item, struct blob *ciphertext,
struct blob *nonce, uint64_t *origsiz)
{
struct cbor_pair *v;
if (item == NULL)
return -1;
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false ||
(v = cbor_map_handle(item)) == NULL)
return -1;
if (cbor_map_size(item) > UINT8_MAX)
return -1;
for (size_t i = 0; i < cbor_map_size(item); i++) {
if (cbor_isa_uint(v[i].key) == false ||
cbor_int_get_width(v[i].key) != CBOR_INT_8)
continue; /* ignore */
switch (cbor_get_uint8(v[i].key)) {
case 1: /* ciphertext */
if (decode_cbor_blob(ciphertext, v[i].value) < 0)
return -1;
break;
case 2: /* nonce */
if (decode_cbor_blob(nonce, v[i].value) < 0)
return -1;
break;
case 3: /* origSize */
if (*origsiz != 0 ||
cbor_isa_uint(v[i].value) == false ||
(*origsiz = cbor_get_int(v[i].value)) > SIZE_MAX)
return -1;
}
}
if (ciphertext->ptr == NULL || nonce->ptr == NULL || *origsiz == 0)
return -1;
return 0;
}
static void
print_blob_entry(size_t idx, const cbor_item_t *item, const struct rkmap *map)
{
struct blob ciphertext, nonce;
const fido_cred_t *cred = NULL;
const char *rp_id = NULL;
char *cred_id = NULL;
uint64_t origsiz = 0;
memset(&ciphertext, 0, sizeof(ciphertext));
memset(&nonce, 0, sizeof(nonce));
if (decode_blob_entry(item, &ciphertext, &nonce, &origsiz) < 0) {
printf("%02zu: <skipped: bad cbor>\n", idx);
goto out;
}
for (size_t i = 0; i < fido_credman_rp_count(map->rp); i++) {
if ((cred = try_rp(map->rk[i], &ciphertext, &nonce,
origsiz)) != NULL) {
rp_id = fido_credman_rp_id(map->rp, i);
break;
}
}
if (cred == NULL) {
if ((cred_id = strdup("<unknown>")) == NULL) {
printf("%02zu: <skipped: strdup failed>\n", idx);
goto out;
}
} else {
if (base64_encode(fido_cred_id_ptr(cred),
fido_cred_id_len(cred), &cred_id) < 0) {
printf("%02zu: <skipped: base64_encode failed>\n", idx);
goto out;
}
}
if (rp_id == NULL)
rp_id = "<unknown>";
printf("%02zu: %4zu %4zu %s %s\n", idx, ciphertext.len,
(size_t)origsiz, cred_id, rp_id);
out:
free(ciphertext.ptr);
free(nonce.ptr);
free(cred_id);
}
static cbor_item_t *
get_cbor_array(fido_dev_t *dev)
{
struct cbor_load_result cbor_result;
cbor_item_t *item = NULL;
u_char *cbor_ptr = NULL;
size_t cbor_len;
int r, ok = -1;
if ((r = fido_dev_largeblob_get_array(dev, &cbor_ptr,
&cbor_len)) != FIDO_OK) {
warnx("%s: fido_dev_largeblob_get_array: %s", __func__,
fido_strerr(r));
goto out;
}
if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
warnx("%s: cbor_load", __func__);
goto out;
}
if (cbor_result.read != cbor_len) {
warnx("%s: cbor_result.read (%zu) != cbor_len (%zu)", __func__,
cbor_result.read, cbor_len);
/* continue */
}
if (cbor_isa_array(item) == false ||
cbor_array_is_definite(item) == false) {
warnx("%s: cbor type", __func__);
goto out;
}
if (cbor_array_size(item) > UINT8_MAX) {
warnx("%s: cbor_array_size > UINT8_MAX", __func__);
goto out;
}
if (cbor_array_size(item) == 0) {
ok = 0; /* nothing to do */
goto out;
}
printf("total map size: %zu byte%s\n", cbor_len, plural(cbor_len));
ok = 0;
out:
if (ok < 0 && item != NULL) {
cbor_decref(&item);
item = NULL;
}
free(cbor_ptr);
return item;
}
int
blob_list(const char *path)
{
struct rkmap map;
fido_dev_t *dev = NULL;
cbor_item_t *item = NULL, **v;
int ok = 1;
memset(&map, 0, sizeof(map));
dev = open_dev(path);
if (map_known_rps(dev, path, &map) < 0 ||
(item = get_cbor_array(dev)) == NULL)
goto out;
if (cbor_array_size(item) == 0) {
ok = 0; /* nothing to do */
goto out;
}
if ((v = cbor_array_handle(item)) == NULL) {
warnx("%s: cbor_array_handle", __func__);
goto out;
}
for (size_t i = 0; i < cbor_array_size(item); i++)
print_blob_entry(i, v[i], &map);
ok = 0; /* success */
out:
free_rkmap(&map);
if (item != NULL)
cbor_decref(&item);
fido_dev_close(dev);
fido_dev_free(&dev);
exit(ok);
}

159
tools/pin.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright (c) 2018 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"
int
pin_set(char *path)
{
fido_dev_t *dev = NULL;
char prompt[1024];
char pin1[128];
char pin2[128];
int r;
int status = 1;
dev = open_dev(path);
r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path);
if (r < 0 || (size_t)r >= sizeof(prompt)) {
warnx("snprintf");
goto out;
}
if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) {
warnx("readpassphrase");
goto out;
}
r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: ");
if (r < 0 || (size_t)r >= sizeof(prompt)) {
warnx("snprintf");
goto out;
}
if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) {
warnx("readpassphrase");
goto out;
}
if (strcmp(pin1, pin2) != 0) {
fprintf(stderr, "PINs do not match. Try again.\n");
goto out;
}
if (strlen(pin1) < 4 || strlen(pin1) > 63) {
fprintf(stderr, "invalid PIN length\n");
goto out;
}
if ((r = fido_dev_set_pin(dev, pin1, NULL)) != FIDO_OK) {
warnx("fido_dev_set_pin: %s", fido_strerr(r));
goto out;
}
fido_dev_close(dev);
fido_dev_free(&dev);
status = 0;
out:
explicit_bzero(pin1, sizeof(pin1));
explicit_bzero(pin2, sizeof(pin2));
exit(status);
}
int
pin_change(char *path)
{
fido_dev_t *dev = NULL;
char prompt[1024];
char pin0[128];
char pin1[128];
char pin2[128];
int r;
int status = 1;
if (path == NULL)
usage();
dev = open_dev(path);
r = snprintf(prompt, sizeof(prompt), "Enter current PIN for %s: ", path);
if (r < 0 || (size_t)r >= sizeof(prompt)) {
warnx("snprintf");
goto out;
}
if (!readpassphrase(prompt, pin0, sizeof(pin0), RPP_ECHO_OFF)) {
warnx("readpassphrase");
goto out;
}
if (strlen(pin0) < 4 || strlen(pin0) > 63) {
warnx("invalid PIN length");
goto out;
}
r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path);
if (r < 0 || (size_t)r >= sizeof(prompt)) {
warnx("snprintf");
goto out;
}
if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) {
warnx("readpassphrase");
goto out;
}
r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: ");
if (r < 0 || (size_t)r >= sizeof(prompt)) {
warnx("snprintf");
goto out;
}
if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) {
warnx("readpassphrase");
goto out;
}
if (strcmp(pin1, pin2) != 0) {
fprintf(stderr, "PINs do not match. Try again.\n");
goto out;
}
if (strlen(pin1) < 4 || strlen(pin1) > 63) {
fprintf(stderr, "invalid PIN length\n");
goto out;
}
if ((r = fido_dev_set_pin(dev, pin1, pin0)) != FIDO_OK) {
warnx("fido_dev_set_pin: %s", fido_strerr(r));
goto out;
}
fido_dev_close(dev);
fido_dev_free(&dev);
status = 0;
out:
explicit_bzero(pin0, sizeof(pin0));
explicit_bzero(pin1, sizeof(pin1));
explicit_bzero(pin2, sizeof(pin2));
exit(status);
}

304
tools/test.sh Normal file
View File

@@ -0,0 +1,304 @@
#!/bin/sh -ex
# Copyright (c) 2021-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
# usage: ./test.sh "$(mktemp -d fido2test-XXXXXXXX)" device
# Please note that this test script:
# - is incomplete;
# - assumes CTAP 2.1-like hmac-secret;
# - should pass as-is on a YubiKey with a PIN set;
# - may otherwise require set +e above;
# - can be executed with UV=1 to run additional UV tests;
# - was last tested on 2022-01-11 with firmware 5.4.3.
cd "$1"
DEV="$2"
TYPE="es256"
#TYPE="es384"
#TYPE="eddsa"
make_cred() {
sed /^$/d > cred_param << EOF
$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64)
$1
some user name
$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64)
EOF
fido2-cred -M $2 "${DEV}" "${TYPE}" > "$3" < cred_param
}
verify_cred() {
fido2-cred -V $1 "${TYPE}" > cred_out < "$2"
head -1 cred_out > "$3"
tail -n +2 cred_out > "$4"
}
get_assert() {
sed /^$/d > assert_param << EOF
$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64)
$1
$(cat $3)
$(cat $4)
EOF
fido2-assert -G $2 "${DEV}" > "$5" < assert_param
}
verify_assert() {
fido2-assert -V $1 "$2" "${TYPE}" < "$3"
}
dd if=/dev/urandom bs=32 count=1 | base64 > hmac-salt
# u2f
if [ "x${TYPE}" = "xes256" ]; then
make_cred no.tld "-u" u2f
! make_cred no.tld "-ru" /dev/null
! make_cred no.tld "-uc1" /dev/null
! make_cred no.tld "-uc2" /dev/null
verify_cred "--" u2f u2f-cred u2f-pubkey
! verify_cred "-h" u2f /dev/null /dev/null
! verify_cred "-v" u2f /dev/null /dev/null
verify_cred "-c0" u2f /dev/null /dev/null
! verify_cred "-c1" u2f /dev/null /dev/null
! verify_cred "-c2" u2f /dev/null /dev/null
! verify_cred "-c3" u2f /dev/null /dev/null
fi
# wrap (non-resident)
make_cred no.tld "--" wrap
verify_cred "--" wrap wrap-cred wrap-pubkey
! verify_cred "-h" wrap /dev/null /dev/null
! verify_cred "-v" wrap /dev/null /dev/null
verify_cred "-c0" wrap /dev/null /dev/null
! verify_cred "-c1" wrap /dev/null /dev/null
! verify_cred "-c2" wrap /dev/null /dev/null
! verify_cred "-c3" wrap /dev/null /dev/null
# wrap (non-resident) + hmac-secret
make_cred no.tld "-h" wrap-hs
! verify_cred "--" wrap-hs /dev/null /dev/null
verify_cred "-h" wrap-hs wrap-hs-cred wrap-hs-pubkey
! verify_cred "-v" wrap-hs /dev/null /dev/null
verify_cred "-hc0" wrap-hs /dev/null /dev/null
! verify_cred "-c0" wrap-hs /dev/null /dev/null
! verify_cred "-c1" wrap-hs /dev/null /dev/null
! verify_cred "-c2" wrap-hs /dev/null /dev/null
! verify_cred "-c3" wrap-hs /dev/null /dev/null
# resident
make_cred no.tld "-r" rk
verify_cred "--" rk rk-cred rk-pubkey
! verify_cred "-h" rk /dev/null /dev/null
! verify_cred "-v" rk /dev/null /dev/null
verify_cred "-c0" rk /dev/null /dev/null
! verify_cred "-c1" rk /dev/null /dev/null
! verify_cred "-c2" rk /dev/null /dev/null
! verify_cred "-c3" rk /dev/null /dev/null
# resident + hmac-secret
make_cred no.tld "-hr" rk-hs
! verify_cred "--" rk-hs rk-hs-cred rk-hs-pubkey
verify_cred "-h" rk-hs /dev/null /dev/null
! verify_cred "-v" rk-hs /dev/null /dev/null
verify_cred "-hc0" rk-hs /dev/null /dev/null
! verify_cred "-c0" rk-hs /dev/null /dev/null
! verify_cred "-c1" rk-hs /dev/null /dev/null
! verify_cred "-c2" rk-hs /dev/null /dev/null
! verify_cred "-c3" rk-hs /dev/null /dev/null
# u2f
if [ "x${TYPE}" = "xes256" ]; then
get_assert no.tld "-u" u2f-cred /dev/null u2f-assert
! get_assert no.tld "-u -t up=false" u2f-cred /dev/null /dev/null
verify_assert "--" u2f-pubkey u2f-assert
verify_assert "-p" u2f-pubkey u2f-assert
fi
# wrap (non-resident)
get_assert no.tld "--" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true" wrap-cred /dev/null wrap-assert
verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-p" wrap-pubkey wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
! verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t pin=true" wrap-cred /dev/null wrap-assert
! verify_assert "-p" wrap-pubkey wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
! verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t pin=false" wrap-cred /dev/null wrap-assert
! verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-h" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
get_assert no.tld "-h -t pin=true" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t pin=false" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
verify_assert "-hp" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t pin=true" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
verify_assert "-hp" wrap-pubkey wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
verify_assert "-hpv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t pin=false" wrap-cred hmac-salt wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
verify_assert "-hp" wrap-pubkey wrap-assert
! get_assert no.tld "-h -t up=false" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t pin=true" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t pin=false" wrap-cred hmac-salt wrap-assert
if [ "x${UV}" != "x" ]; then
get_assert no.tld "-t uv=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t uv=true -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t uv=true -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t uv=false" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-t uv=false -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t uv=false -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=true" wrap-cred /dev/null wrap-assert
verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=true -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=true -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=false" wrap-cred /dev/null wrap-assert
verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=false -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-pv" wrap-pubkey wrap-assert
get_assert no.tld "-t up=true -t uv=false -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "-p" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=true -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=true -t pin=false" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=false" wrap-cred /dev/null wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=false -t pin=true" wrap-cred /dev/null wrap-assert
verify_assert "-v" wrap-pubkey wrap-assert
get_assert no.tld "-t up=false -t uv=false -t pin=false" wrap-cred /dev/null wrap-assert
! verify_assert "--" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=false" wrap-cred hmac-salt wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert
verify_assert "-h" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hpv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hpv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert
verify_assert "-hpv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=false" wrap-cred hmac-salt wrap-assert
verify_assert "-hp" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert
verify_assert "-hpv" wrap-pubkey wrap-assert
get_assert no.tld "-h -t up=true -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert
verify_assert "-hp" wrap-pubkey wrap-assert
! get_assert no.tld "-h -t up=false -t uv=true" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t uv=true -t pin=true" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t uv=true -t pin=false" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t uv=false" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t uv=false -t pin=true" wrap-cred hmac-salt wrap-assert
! get_assert no.tld "-h -t up=false -t uv=false -t pin=false" wrap-cred hmac-salt wrap-assert
fi
# resident
get_assert no.tld "-r" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -h" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t pin=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t pin=false" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t pin=true" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t pin=false" /dev/null hmac-salt wrap-assert
if [ "x${UV}" != "x" ]; then
get_assert no.tld "-r -t uv=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t uv=true -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t uv=true -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t uv=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t uv=false -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t uv=false -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=true -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=true -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=false -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=true -t uv=false -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=true -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=true -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=false -t pin=true" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -t up=false -t uv=false -t pin=false" /dev/null /dev/null wrap-assert
get_assert no.tld "-r -h -t uv=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t uv=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=false" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert
get_assert no.tld "-r -h -t up=true -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=true" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=true -t pin=true" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=true -t pin=false" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=false" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=false -t pin=true" /dev/null hmac-salt wrap-assert
! get_assert no.tld "-r -h -t up=false -t uv=false -t pin=false" /dev/null hmac-salt wrap-assert
fi
exit 0

738
tools/token.c Normal file
View File

@@ -0,0 +1,738 @@
/*
* 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_maxcredblob(uint64_t maxcredblob)
{
printf("maxcredblob: %d\n", (int)maxcredblob);
}
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 credBlob */
print_maxcredblob(fido_cbor_info_maxcredbloblen(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));
}

655
tools/util.c Normal file
View File

@@ -0,0 +1,655 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <fido.h>
#include <fido/es256.h>
#include <fido/es384.h>
#include <fido/rs256.h>
#include <fido/eddsa.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../openbsd-compat/openbsd-compat.h"
#ifdef _MSC_VER
#include "../openbsd-compat/posix_win.h"
#endif
#include "extern.h"
char*
get_pin(const char* path)
{
char* pin;
char prompt[1024];
int r, ok = -1;
if ((pin = calloc(1, PINBUF_LEN)) == NULL) {
warn("%s: calloc", __func__);
return NULL;
}
// Check if global_pin is empty
if (global_pin && global_pin[0] != '\0') {
// Use global_pin
strncpy(pin, global_pin, PINBUF_LEN - 1);
ok = 0;
goto out;
}
if ((r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path)) < 0 || (size_t)r >= sizeof(prompt)) {
warn("%s: snprintf", __func__);
goto out;
}
if (!readpassphrase(prompt, pin, PINBUF_LEN, RPP_ECHO_OFF)) {
warnx("%s: readpassphrase", __func__);
goto out;
}
ok = 0;
out:
if (ok < 0) {
freezero(pin, PINBUF_LEN);
pin = NULL;
}
return pin;
}
FILE *
open_write(const char *file)
{
int fd;
FILE *f;
if (file == NULL || strcmp(file, "-") == 0)
return (stdout);
if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0)
err(1, "open %s", file);
if ((f = fdopen(fd, "w")) == NULL)
err(1, "fdopen %s", file);
return (f);
}
FILE *
open_read(const char *file)
{
int fd;
FILE *f;
if (file == NULL || strcmp(file, "-") == 0) {
#ifdef FIDO_FUZZ
setvbuf(stdin, NULL, _IONBF, 0);
#endif
return (stdin);
}
if ((fd = open(file, O_RDONLY)) < 0)
err(1, "open %s", file);
if ((f = fdopen(fd, "r")) == NULL)
err(1, "fdopen %s", file);
return (f);
}
int
base10(const char *str)
{
char *ep;
long long ll;
ll = strtoll(str, &ep, 10);
if (str == ep || *ep != '\0')
return (-1);
else if (ll == LLONG_MIN && errno == ERANGE)
return (-1);
else if (ll == LLONG_MAX && errno == ERANGE)
return (-1);
else if (ll < 0 || ll > INT_MAX)
return (-1);
return ((int)ll);
}
void
xxd(const void *buf, size_t count)
{
const uint8_t *ptr = buf;
size_t i;
fprintf(stderr, " ");
for (i = 0; i < count; i++) {
fprintf(stderr, "%02x ", *ptr++);
if ((i + 1) % 16 == 0 && i + 1 < count)
fprintf(stderr, "\n ");
}
fprintf(stderr, "\n");
fflush(stderr);
}
int
string_read(FILE *f, char **out)
{
char *line = NULL;
size_t linesize = 0;
ssize_t n;
*out = NULL;
if ((n = getline(&line, &linesize, f)) <= 0 ||
(size_t)n != strlen(line)) {
free(line);
return (-1);
}
line[n - 1] = '\0'; /* trim \n */
*out = line;
return (0);
}
fido_dev_t *
open_dev(const char *path)
{
fido_dev_t *dev;
int r;
if ((dev = fido_dev_new()) == NULL)
errx(1, "fido_dev_new");
r = fido_dev_open(dev, path);
if (r != FIDO_OK)
errx(1, "fido_dev_open %s: %s", path, fido_strerr(r));
return (dev);
}
int
get_devopt(fido_dev_t *dev, const char *name, int *val)
{
fido_cbor_info_t *cbor_info;
char * const *names;
const bool *values;
int r, ok = -1;
if ((cbor_info = fido_cbor_info_new()) == NULL) {
warnx("fido_cbor_info_new");
goto out;
}
if ((r = fido_dev_get_cbor_info(dev, cbor_info)) != FIDO_OK) {
warnx("fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
goto out;
}
if ((names = fido_cbor_info_options_name_ptr(cbor_info)) == NULL ||
(values = fido_cbor_info_options_value_ptr(cbor_info)) == NULL) {
warnx("fido_dev_get_cbor_info: NULL name/value pointer");
goto out;
}
*val = -1;
for (size_t i = 0; i < fido_cbor_info_options_len(cbor_info); i++)
if (strcmp(names[i], name) == 0) {
*val = values[i];
break;
}
ok = 0;
out:
fido_cbor_info_free(&cbor_info);
return (ok);
}
EC_KEY *
read_ec_pubkey(const char *path)
{
FILE *fp = NULL;
EVP_PKEY *pkey = NULL;
EC_KEY *ec = NULL;
if ((fp = fopen(path, "r")) == NULL) {
warn("fopen");
goto fail;
}
if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
warnx("PEM_read_PUBKEY");
goto fail;
}
if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
warnx("EVP_PKEY_get1_EC_KEY");
goto fail;
}
fail:
if (fp) {
fclose(fp);
}
if (pkey) {
EVP_PKEY_free(pkey);
}
return (ec);
}
int
write_es256_pubkey(FILE *f, const void *ptr, size_t len)
{
EVP_PKEY *pkey = NULL;
es256_pk_t *pk = NULL;
int ok = -1;
if ((pk = es256_pk_new()) == NULL) {
warnx("es256_pk_new");
goto fail;
}
if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
warnx("es256_pk_from_ptr");
goto fail;
}
if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
warnx("es256_pk_to_EVP_PKEY");
goto fail;
}
if (PEM_write_PUBKEY(f, pkey) == 0) {
warnx("PEM_write_PUBKEY");
goto fail;
}
ok = 0;
fail:
es256_pk_free(&pk);
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return (ok);
}
int
write_es384_pubkey(FILE *f, const void *ptr, size_t len)
{
EVP_PKEY *pkey = NULL;
es384_pk_t *pk = NULL;
int ok = -1;
if ((pk = es384_pk_new()) == NULL) {
warnx("es384_pk_new");
goto fail;
}
if (es384_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
warnx("es384_pk_from_ptr");
goto fail;
}
if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
warnx("es384_pk_to_EVP_PKEY");
goto fail;
}
if (PEM_write_PUBKEY(f, pkey) == 0) {
warnx("PEM_write_PUBKEY");
goto fail;
}
ok = 0;
fail:
es384_pk_free(&pk);
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return (ok);
}
RSA *
read_rsa_pubkey(const char *path)
{
FILE *fp = NULL;
EVP_PKEY *pkey = NULL;
RSA *rsa = NULL;
if ((fp = fopen(path, "r")) == NULL) {
warn("fopen");
goto fail;
}
if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
warnx("PEM_read_PUBKEY");
goto fail;
}
if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
warnx("EVP_PKEY_get1_RSA");
goto fail;
}
fail:
if (fp) {
fclose(fp);
}
if (pkey) {
EVP_PKEY_free(pkey);
}
return (rsa);
}
int
write_rsa_pubkey(FILE *f, const void *ptr, size_t len)
{
EVP_PKEY *pkey = NULL;
rs256_pk_t *pk = NULL;
int ok = -1;
if ((pk = rs256_pk_new()) == NULL) {
warnx("rs256_pk_new");
goto fail;
}
if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
warnx("rs256_pk_from_ptr");
goto fail;
}
if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
warnx("rs256_pk_to_EVP_PKEY");
goto fail;
}
if (PEM_write_PUBKEY(f, pkey) == 0) {
warnx("PEM_write_PUBKEY");
goto fail;
}
ok = 0;
fail:
rs256_pk_free(&pk);
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return (ok);
}
EVP_PKEY *
read_eddsa_pubkey(const char *path)
{
FILE *fp = NULL;
EVP_PKEY *pkey = NULL;
if ((fp = fopen(path, "r")) == NULL) {
warn("fopen");
goto fail;
}
if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
warnx("PEM_read_PUBKEY");
goto fail;
}
fail:
if (fp) {
fclose(fp);
}
return (pkey);
}
int
write_eddsa_pubkey(FILE *f, const void *ptr, size_t len)
{
EVP_PKEY *pkey = NULL;
eddsa_pk_t *pk = NULL;
int ok = -1;
if ((pk = eddsa_pk_new()) == NULL) {
warnx("eddsa_pk_new");
goto fail;
}
if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
warnx("eddsa_pk_from_ptr");
goto fail;
}
if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
warnx("eddsa_pk_to_EVP_PKEY");
goto fail;
}
if (PEM_write_PUBKEY(f, pkey) == 0) {
warnx("PEM_write_PUBKEY");
goto fail;
}
ok = 0;
fail:
eddsa_pk_free(&pk);
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
return (ok);
}
void
print_cred(FILE *out_f, int type, const fido_cred_t *cred)
{
char *id;
int r;
r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id);
if (r < 0)
errx(1, "output error");
fprintf(out_f, "%s\n", id);
switch (type) {
case COSE_ES256:
write_es256_pubkey(out_f, fido_cred_pubkey_ptr(cred),
fido_cred_pubkey_len(cred));
break;
case COSE_ES384:
write_es384_pubkey(out_f, fido_cred_pubkey_ptr(cred),
fido_cred_pubkey_len(cred));
break;
case COSE_RS256:
write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
fido_cred_pubkey_len(cred));
break;
case COSE_EDDSA:
write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
fido_cred_pubkey_len(cred));
break;
default:
errx(1, "print_cred: unknown type");
}
free(id);
}
int
cose_type(const char *str, int *type)
{
if (strcmp(str, "es256") == 0)
*type = COSE_ES256;
else if (strcmp(str, "es384") == 0)
*type = COSE_ES384;
else if (strcmp(str, "rs256") == 0)
*type = COSE_RS256;
else if (strcmp(str, "eddsa") == 0)
*type = COSE_EDDSA;
else {
*type = 0;
return (-1);
}
return (0);
}
const char *
cose_string(int type)
{
switch (type) {
case COSE_ES256:
return ("es256");
case COSE_ES384:
return ("es384");
case COSE_RS256:
return ("rs256");
case COSE_EDDSA:
return ("eddsa");
default:
return ("unknown");
}
}
const char *
prot_string(int prot)
{
switch (prot) {
case FIDO_CRED_PROT_UV_OPTIONAL:
return ("uvopt");
case FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID:
return ("uvopt+id");
case FIDO_CRED_PROT_UV_REQUIRED:
return ("uvreq");
default:
return ("unknown");
}
}
int
read_file(const char *path, u_char **ptr, size_t *len)
{
int fd, ok = -1;
struct stat st;
ssize_t n;
*ptr = NULL;
*len = 0;
if ((fd = open(path, O_RDONLY)) < 0) {
warn("%s: open %s", __func__, path);
goto fail;
}
if (fstat(fd, &st) < 0) {
warn("%s: stat %s", __func__, path);
goto fail;
}
if (st.st_size < 0) {
warnx("%s: stat %s: invalid size", __func__, path);
goto fail;
}
*len = (size_t)st.st_size;
if ((*ptr = malloc(*len)) == NULL) {
warn("%s: malloc", __func__);
goto fail;
}
if ((n = read(fd, *ptr, *len)) < 0) {
warn("%s: read", __func__);
goto fail;
}
if ((size_t)n != *len) {
warnx("%s: read", __func__);
goto fail;
}
ok = 0;
fail:
if (fd != -1) {
close(fd);
}
if (ok < 0) {
free(*ptr);
*ptr = NULL;
*len = 0;
}
return ok;
}
int
write_file(const char *path, const u_char *ptr, size_t len)
{
int fd, ok = -1;
ssize_t n;
if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) < 0) {
warn("%s: open %s", __func__, path);
goto fail;
}
if ((n = write(fd, ptr, len)) < 0) {
warn("%s: write", __func__);
goto fail;
}
if ((size_t)n != len) {
warnx("%s: write", __func__);
goto fail;
}
ok = 0;
fail:
if (fd != -1) {
close(fd);
}
return ok;
}
const char *
plural(size_t x)
{
return x == 1 ? "" : "s";
}
int
should_retry_with_pin(const fido_dev_t *dev, int r)
{
if (fido_dev_has_pin(dev) == false) {
return 0;
}
switch (r) {
case FIDO_ERR_PIN_REQUIRED:
case FIDO_ERR_UNAUTHORIZED_PERM:
case FIDO_ERR_UV_BLOCKED:
case FIDO_ERR_UV_INVALID:
return 1;
}
return 0;
}