mirror of
https://github.com/token2/fido2-manage.git
synced 2026-04-09 02:35:39 +00:00
Add files via upload
This commit is contained in:
156
src/CMakeLists.txt
Normal file
156
src/CMakeLists.txt
Normal file
@@ -0,0 +1,156 @@
|
||||
# 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
|
||||
|
||||
add_definitions(-D_FIDO_INTERNAL)
|
||||
|
||||
list(APPEND FIDO_SOURCES
|
||||
aes256.c
|
||||
assert.c
|
||||
authkey.c
|
||||
bio.c
|
||||
blob.c
|
||||
buf.c
|
||||
cbor.c
|
||||
compress.c
|
||||
config.c
|
||||
cred.c
|
||||
credman.c
|
||||
dev.c
|
||||
ecdh.c
|
||||
eddsa.c
|
||||
err.c
|
||||
es256.c
|
||||
es384.c
|
||||
hid.c
|
||||
info.c
|
||||
io.c
|
||||
iso7816.c
|
||||
largeblob.c
|
||||
log.c
|
||||
pin.c
|
||||
random.c
|
||||
reset.c
|
||||
rs1.c
|
||||
rs256.c
|
||||
time.c
|
||||
touch.c
|
||||
tpm.c
|
||||
types.c
|
||||
u2f.c
|
||||
util.c
|
||||
)
|
||||
|
||||
if(FUZZ)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/clock.c)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/pcsc.c)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/prng.c)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/udev.c)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c)
|
||||
list(APPEND FIDO_SOURCES ../fuzz/wrap.c)
|
||||
endif()
|
||||
|
||||
if(NFC_LINUX)
|
||||
list(APPEND FIDO_SOURCES netlink.c nfc.c nfc_linux.c)
|
||||
endif()
|
||||
|
||||
if(USE_PCSC)
|
||||
list(APPEND FIDO_SOURCES nfc.c pcsc.c)
|
||||
endif()
|
||||
|
||||
if(USE_HIDAPI)
|
||||
list(APPEND FIDO_SOURCES hid_hidapi.c)
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
list(APPEND FIDO_SOURCES hid_unix.c)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
list(APPEND FIDO_SOURCES hid_win.c)
|
||||
if(USE_WINHELLO)
|
||||
list(APPEND FIDO_SOURCES winhello.c)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
list(APPEND FIDO_SOURCES hid_osx.c)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
list(APPEND FIDO_SOURCES hid_linux.c hid_unix.c)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
list(APPEND FIDO_SOURCES hid_netbsd.c hid_unix.c)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
list(APPEND FIDO_SOURCES hid_openbsd.c hid_unix.c)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD")
|
||||
list(APPEND FIDO_SOURCES hid_freebsd.c hid_unix.c)
|
||||
else()
|
||||
message(FATAL_ERROR "please define a hid backend for your platform")
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
set_source_files_properties(${FIDO_SOURCES}
|
||||
PROPERTIES COMPILE_FLAGS "${EXTRA_CFLAGS}")
|
||||
endif()
|
||||
|
||||
list(APPEND COMPAT_SOURCES
|
||||
../openbsd-compat/bsd-asprintf.c
|
||||
../openbsd-compat/bsd-getpagesize.c
|
||||
../openbsd-compat/clock_gettime.c
|
||||
../openbsd-compat/endian_win32.c
|
||||
../openbsd-compat/explicit_bzero.c
|
||||
../openbsd-compat/explicit_bzero_win32.c
|
||||
../openbsd-compat/freezero.c
|
||||
../openbsd-compat/recallocarray.c
|
||||
../openbsd-compat/strlcat.c
|
||||
../openbsd-compat/timingsafe_bcmp.c
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND BASE_LIBRARIES wsock32 ws2_32 bcrypt setupapi hid)
|
||||
if(USE_PCSC)
|
||||
list(APPEND BASE_LIBRARIES winscard)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
list(APPEND BASE_LIBRARIES "-framework CoreFoundation"
|
||||
"-framework IOKit")
|
||||
if(USE_PCSC)
|
||||
list(APPEND BASE_LIBRARIES "-framework PCSC")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND TARGET_LIBRARIES
|
||||
${CBOR_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${UDEV_LIBRARIES}
|
||||
${BASE_LIBRARIES}
|
||||
${HIDAPI_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${PCSC_LIBRARIES}
|
||||
)
|
||||
|
||||
# static library
|
||||
if(BUILD_STATIC_LIBS)
|
||||
add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES})
|
||||
if(WIN32 AND NOT MINGW)
|
||||
set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static)
|
||||
endif()
|
||||
target_link_libraries(fido2 ${TARGET_LIBRARIES})
|
||||
install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
# dynamic library
|
||||
if(BUILD_SHARED_LIBS)
|
||||
add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES})
|
||||
set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
|
||||
VERSION ${FIDO_VERSION} SOVERSION ${FIDO_MAJOR})
|
||||
target_link_libraries(fido2_shared ${TARGET_LIBRARIES})
|
||||
install(TARGETS fido2_shared
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
install(FILES fido.h DESTINATION include)
|
||||
install(DIRECTORY fido DESTINATION include)
|
||||
|
||||
configure_file(libfido2.pc.in libfido2.pc @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
216
src/aes256.c
Normal file
216
src/aes256.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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"
|
||||
|
||||
static int
|
||||
aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in,
|
||||
fido_blob_t *out, int encrypt)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
const EVP_CIPHER *cipher;
|
||||
int ok = -1;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (key->len != 32) {
|
||||
fido_log_debug("%s: invalid key len %zu", __func__, key->len);
|
||||
goto fail;
|
||||
}
|
||||
if (in->len > UINT_MAX || in->len % 16 || in->len == 0) {
|
||||
fido_log_debug("%s: invalid input len %zu", __func__, in->len);
|
||||
goto fail;
|
||||
}
|
||||
out->len = in->len;
|
||||
if ((out->ptr = calloc(1, out->len)) == NULL) {
|
||||
fido_log_debug("%s: calloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
|
||||
(cipher = EVP_aes_256_cbc()) == NULL) {
|
||||
fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 ||
|
||||
EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) {
|
||||
fido_log_debug("%s: EVP_Cipher", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ctx != NULL)
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
if (ok < 0)
|
||||
fido_blob_reset(out);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in,
|
||||
fido_blob_t *out, int encrypt)
|
||||
{
|
||||
u_char iv[16];
|
||||
|
||||
memset(&iv, 0, sizeof(iv));
|
||||
|
||||
return aes256_cbc(key, iv, in, out, encrypt);
|
||||
}
|
||||
|
||||
static int
|
||||
aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in,
|
||||
fido_blob_t *out, int encrypt)
|
||||
{
|
||||
fido_blob_t key, cin, cout;
|
||||
u_char iv[16];
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (secret->len != 64) {
|
||||
fido_log_debug("%s: invalid secret len %zu", __func__,
|
||||
secret->len);
|
||||
return -1;
|
||||
}
|
||||
if (in->len < sizeof(iv)) {
|
||||
fido_log_debug("%s: invalid input len %zu", __func__, in->len);
|
||||
return -1;
|
||||
}
|
||||
if (encrypt) {
|
||||
if (fido_get_random(iv, sizeof(iv)) < 0) {
|
||||
fido_log_debug("%s: fido_get_random", __func__);
|
||||
return -1;
|
||||
}
|
||||
cin = *in;
|
||||
} else {
|
||||
memcpy(iv, in->ptr, sizeof(iv));
|
||||
cin.ptr = in->ptr + sizeof(iv);
|
||||
cin.len = in->len - sizeof(iv);
|
||||
}
|
||||
key.ptr = secret->ptr + 32;
|
||||
key.len = secret->len - 32;
|
||||
if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0)
|
||||
return -1;
|
||||
if (encrypt) {
|
||||
if (cout.len > SIZE_MAX - sizeof(iv) ||
|
||||
(out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) {
|
||||
fido_blob_reset(&cout);
|
||||
return -1;
|
||||
}
|
||||
out->len = sizeof(iv) + cout.len;
|
||||
memcpy(out->ptr, iv, sizeof(iv));
|
||||
memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len);
|
||||
fido_blob_reset(&cout);
|
||||
} else
|
||||
*out = cout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce,
|
||||
const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out,
|
||||
int encrypt)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
const EVP_CIPHER *cipher;
|
||||
size_t textlen;
|
||||
int ok = -1;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) {
|
||||
fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__,
|
||||
nonce->len, key->len, aad->len);
|
||||
goto fail;
|
||||
}
|
||||
if (in->len > UINT_MAX || in->len > SIZE_MAX - 16 || in->len < 16) {
|
||||
fido_log_debug("%s: invalid input len %zu", __func__, in->len);
|
||||
goto fail;
|
||||
}
|
||||
/* add tag to (on encrypt) or trim tag from the output (on decrypt) */
|
||||
out->len = encrypt ? in->len + 16 : in->len - 16;
|
||||
if ((out->ptr = calloc(1, out->len)) == NULL) {
|
||||
fido_log_debug("%s: calloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
|
||||
(cipher = EVP_aes_256_gcm()) == NULL) {
|
||||
fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) {
|
||||
fido_log_debug("%s: EVP_CipherInit", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (encrypt)
|
||||
textlen = in->len;
|
||||
else {
|
||||
textlen = in->len - 16;
|
||||
/* point openssl at the mac tag */
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
|
||||
in->ptr + in->len - 16) == 0) {
|
||||
fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* the last EVP_Cipher() will either compute or verify the mac tag */
|
||||
if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 ||
|
||||
EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 ||
|
||||
EVP_Cipher(ctx, NULL, NULL, 0) < 0) {
|
||||
fido_log_debug("%s: EVP_Cipher", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (encrypt) {
|
||||
/* append the mac tag */
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
|
||||
out->ptr + out->len - 16) == 0) {
|
||||
fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ctx != NULL)
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
if (ok < 0)
|
||||
fido_blob_reset(out);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret,
|
||||
const fido_blob_t *in, fido_blob_t *out)
|
||||
{
|
||||
return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
|
||||
in, out, 1) : aes256_cbc_proto1(secret, in, out, 1);
|
||||
}
|
||||
|
||||
int
|
||||
aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret,
|
||||
const fido_blob_t *in, fido_blob_t *out)
|
||||
{
|
||||
return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
|
||||
in, out, 0) : aes256_cbc_proto1(secret, in, out, 0);
|
||||
}
|
||||
|
||||
int
|
||||
aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce,
|
||||
const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
|
||||
{
|
||||
return aes256_gcm(key, nonce, aad, in, out, 1);
|
||||
}
|
||||
|
||||
int
|
||||
aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce,
|
||||
const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
|
||||
{
|
||||
return aes256_gcm(key, nonce, aad, in, out, 0);
|
||||
}
|
||||
1170
src/assert.c
Normal file
1170
src/assert.c
Normal file
File diff suppressed because it is too large
Load Diff
107
src/authkey.c
Normal file
107
src/authkey.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int
|
||||
parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
es256_pk_t *authkey = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 1) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
return (es256_pk_decode(val, authkey));
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_authkey_tx(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
cbor_item_t *argv[2];
|
||||
int r;
|
||||
|
||||
fido_log_debug("%s: dev=%p", __func__, (void *)dev);
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
/* add command parameters */
|
||||
if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(2)) == NULL) {
|
||||
fido_log_debug("%s: cbor_build", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* frame and transmit */
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev,
|
||||
(void *)authkey, *ms);
|
||||
|
||||
memset(authkey, 0, sizeof(*authkey));
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = cbor_parse_reply(msg, (size_t)msglen, authkey, parse_authkey);
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = fido_dev_authkey_tx(dev, ms)) != FIDO_OK ||
|
||||
(r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey, int *ms)
|
||||
{
|
||||
return (fido_dev_authkey_wait(dev, authkey, ms));
|
||||
}
|
||||
903
src/bio.c
Normal file
903
src/bio.c
Normal file
@@ -0,0 +1,903 @@
|
||||
/*
|
||||
* Copyright (c) 2019-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/bio.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#define CMD_ENROLL_BEGIN 0x01
|
||||
#define CMD_ENROLL_NEXT 0x02
|
||||
#define CMD_ENROLL_CANCEL 0x03
|
||||
#define CMD_ENUM 0x04
|
||||
#define CMD_SET_NAME 0x05
|
||||
#define CMD_ENROLL_REMOVE 0x06
|
||||
#define CMD_GET_INFO 0x07
|
||||
|
||||
static int
|
||||
bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
|
||||
cbor_item_t **param, fido_blob_t *hmac_data)
|
||||
{
|
||||
const uint8_t prefix[2] = { 0x01 /* modality */, cmd };
|
||||
int ok = -1;
|
||||
size_t cbor_alloc_len;
|
||||
size_t cbor_len;
|
||||
unsigned char *cbor = NULL;
|
||||
|
||||
if (argv == NULL || param == NULL)
|
||||
return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
|
||||
|
||||
if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
|
||||
fido_log_debug("%s: cbor_flatten_vector", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
|
||||
&cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
|
||||
fido_log_debug("%s: cbor_serialize_alloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(hmac_data->ptr, prefix, sizeof(prefix));
|
||||
memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
|
||||
hmac_data->len = cbor_len + sizeof(prefix);
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
free(cbor);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
bio_get_cmd(const fido_dev_t *dev)
|
||||
{
|
||||
if (dev->flags & (FIDO_DEV_BIO_SET|FIDO_DEV_BIO_UNSET))
|
||||
return (CTAP_CBOR_BIO_ENROLL);
|
||||
|
||||
return (CTAP_CBOR_BIO_ENROLL_PRE);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
|
||||
const char *pin, const fido_blob_t *token, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[5];
|
||||
es256_pk_t *pk = NULL;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
fido_blob_t f;
|
||||
fido_blob_t hmac;
|
||||
const uint8_t cmd = bio_get_cmd(dev);
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(&hmac, 0, sizeof(hmac));
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
/* modality, subCommand */
|
||||
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(subcmd)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* subParams */
|
||||
if (pin || token) {
|
||||
if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2],
|
||||
&hmac) < 0) {
|
||||
fido_log_debug("%s: bio_prepare_hmac", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* pinProtocol, pinAuth */
|
||||
if (pin) {
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
|
||||
NULL, &argv[4], &argv[3], ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: cbor_add_uv_params", __func__);
|
||||
goto fail;
|
||||
}
|
||||
} else if (token) {
|
||||
if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) {
|
||||
fido_log_debug("%s: encode pin", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* framing and transmission */
|
||||
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ecdh);
|
||||
free(f.ptr);
|
||||
free(hmac.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static void
|
||||
bio_reset_template(fido_bio_template_t *t)
|
||||
{
|
||||
free(t->name);
|
||||
t->name = NULL;
|
||||
fido_blob_reset(&t->id);
|
||||
}
|
||||
|
||||
static void
|
||||
bio_reset_template_array(fido_bio_template_array_t *ta)
|
||||
{
|
||||
for (size_t i = 0; i < ta->n_alloc; i++)
|
||||
bio_reset_template(&ta->ptr[i]);
|
||||
|
||||
free(ta->ptr);
|
||||
ta->ptr = NULL;
|
||||
memset(ta, 0, sizeof(*ta));
|
||||
}
|
||||
|
||||
static int
|
||||
decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_bio_template_t *t = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* id */
|
||||
return (fido_blob_decode(val, &t->id));
|
||||
case 2: /* name */
|
||||
return (cbor_string_copy(val, &t->name));
|
||||
}
|
||||
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
static int
|
||||
decode_template_array(const cbor_item_t *item, void *arg)
|
||||
{
|
||||
fido_bio_template_array_t *ta = arg;
|
||||
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (ta->n_rx >= ta->n_alloc) {
|
||||
fido_log_debug("%s: n_rx >= n_alloc", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
|
||||
fido_log_debug("%s: decode_template", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ta->n_rx++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_bio_template_array_t *ta = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 7) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_isa_array(val) == false ||
|
||||
cbor_array_is_definite(val) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
|
||||
fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
|
||||
__func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
|
||||
return (-1);
|
||||
|
||||
ta->n_alloc = cbor_array_size(val);
|
||||
|
||||
if (cbor_array_iter(val, ta, decode_template_array) < 0) {
|
||||
fido_log_debug("%s: decode_template_array", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
bio_reset_template_array(ta);
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, ta,
|
||||
bio_parse_template_array)) != FIDO_OK) {
|
||||
fido_log_debug("%s: bio_parse_template_array" , __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL, ms)) != FIDO_OK ||
|
||||
(r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
|
||||
const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
if (pin == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (bio_get_template_array_wait(dev, ta, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[2];
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
|
||||
(argv[1] = cbor_build_string(t->name)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL,
|
||||
ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
if (pin == NULL || t->name == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (bio_set_template_name_wait(dev, t, pin, &ms));
|
||||
}
|
||||
|
||||
static void
|
||||
bio_reset_enroll(fido_bio_enroll_t *e)
|
||||
{
|
||||
e->remaining_samples = 0;
|
||||
e->last_status = 0;
|
||||
|
||||
if (e->token)
|
||||
fido_blob_free(&e->token);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_bio_enroll_t *e = arg;
|
||||
uint64_t x;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 5:
|
||||
if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
e->last_status = (uint8_t)x;
|
||||
break;
|
||||
case 6:
|
||||
if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
e->remaining_samples = (uint8_t)x;
|
||||
break;
|
||||
default:
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_blob_t *id = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 4) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
return (fido_blob_decode(val, id));
|
||||
}
|
||||
|
||||
static int
|
||||
bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
|
||||
fido_bio_enroll_t *e, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
bio_reset_template(t);
|
||||
|
||||
e->remaining_samples = 0;
|
||||
e->last_status = 0;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, e,
|
||||
bio_parse_enroll_status)) != FIDO_OK) {
|
||||
fido_log_debug("%s: bio_parse_enroll_status", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, &t->id,
|
||||
bio_parse_template_id)) != FIDO_OK) {
|
||||
fido_log_debug("%s: bio_parse_template_id", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
|
||||
fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[3];
|
||||
const uint8_t cmd = CMD_ENROLL_BEGIN;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
|
||||
(r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
|
||||
fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
|
||||
{
|
||||
es256_pk_t *pk = NULL;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
fido_blob_t *token = NULL;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
if (pin == NULL || e->token != NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if ((token = fido_blob_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
|
||||
pk, NULL, token, &ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_get_uv_token", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
e->token = token;
|
||||
token = NULL;
|
||||
fail:
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ecdh);
|
||||
fido_blob_free(&token);
|
||||
|
||||
if (r != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
e->remaining_samples = 0;
|
||||
e->last_status = 0;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, e,
|
||||
bio_parse_enroll_status)) != FIDO_OK) {
|
||||
fido_log_debug("%s: bio_parse_enroll_status", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[3];
|
||||
const uint8_t cmd = CMD_ENROLL_NEXT;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
|
||||
(argv[2] = cbor_build_uint(timo_ms)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
|
||||
(r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
fido_bio_enroll_t *e, uint32_t timo_ms)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
if (e->token == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
bio_enroll_cancel_wait(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
const uint8_t cmd = CMD_ENROLL_CANCEL;
|
||||
int r;
|
||||
|
||||
if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
return (r);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_enroll_cancel(fido_dev_t *dev)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (bio_enroll_cancel_wait(dev, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[1];
|
||||
const uint8_t cmd = CMD_ENROLL_REMOVE;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
|
||||
const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (bio_enroll_remove_wait(dev, t, pin, &ms));
|
||||
}
|
||||
|
||||
static void
|
||||
bio_reset_info(fido_bio_info_t *i)
|
||||
{
|
||||
i->type = 0;
|
||||
i->max_samples = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_bio_info_t *i = arg;
|
||||
uint64_t x;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 2:
|
||||
if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
i->type = (uint8_t)x;
|
||||
break;
|
||||
case 3:
|
||||
if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
i->max_samples = (uint8_t)x;
|
||||
break;
|
||||
default:
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
bio_reset_info(i);
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, i,
|
||||
bio_parse_info)) != FIDO_OK) {
|
||||
fido_log_debug("%s: bio_parse_info" , __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL,
|
||||
ms)) != FIDO_OK ||
|
||||
(r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: tx/rx", __func__);
|
||||
return (r);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (bio_get_info_wait(dev, i, &ms));
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_bio_template_name(const fido_bio_template_t *t)
|
||||
{
|
||||
return (t->name);
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
fido_bio_template_id_ptr(const fido_bio_template_t *t)
|
||||
{
|
||||
return (t->id.ptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_bio_template_id_len(const fido_bio_template_t *t)
|
||||
{
|
||||
return (t->id.len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_bio_template_array_count(const fido_bio_template_array_t *ta)
|
||||
{
|
||||
return (ta->n_rx);
|
||||
}
|
||||
|
||||
fido_bio_template_array_t *
|
||||
fido_bio_template_array_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_bio_template_array_t)));
|
||||
}
|
||||
|
||||
fido_bio_template_t *
|
||||
fido_bio_template_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_bio_template_t)));
|
||||
}
|
||||
|
||||
void
|
||||
fido_bio_template_array_free(fido_bio_template_array_t **tap)
|
||||
{
|
||||
fido_bio_template_array_t *ta;
|
||||
|
||||
if (tap == NULL || (ta = *tap) == NULL)
|
||||
return;
|
||||
|
||||
bio_reset_template_array(ta);
|
||||
free(ta);
|
||||
*tap = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fido_bio_template_free(fido_bio_template_t **tp)
|
||||
{
|
||||
fido_bio_template_t *t;
|
||||
|
||||
if (tp == NULL || (t = *tp) == NULL)
|
||||
return;
|
||||
|
||||
bio_reset_template(t);
|
||||
free(t);
|
||||
*tp = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
|
||||
{
|
||||
free(t->name);
|
||||
t->name = NULL;
|
||||
|
||||
if (name && (t->name = strdup(name)) == NULL)
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
|
||||
size_t len)
|
||||
{
|
||||
fido_blob_reset(&t->id);
|
||||
|
||||
if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
const fido_bio_template_t *
|
||||
fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
|
||||
{
|
||||
if (idx >= ta->n_alloc)
|
||||
return (NULL);
|
||||
|
||||
return (&ta->ptr[idx]);
|
||||
}
|
||||
|
||||
fido_bio_enroll_t *
|
||||
fido_bio_enroll_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_bio_enroll_t)));
|
||||
}
|
||||
|
||||
fido_bio_info_t *
|
||||
fido_bio_info_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_bio_info_t)));
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_bio_info_type(const fido_bio_info_t *i)
|
||||
{
|
||||
return (i->type);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_bio_info_max_samples(const fido_bio_info_t *i)
|
||||
{
|
||||
return (i->max_samples);
|
||||
}
|
||||
|
||||
void
|
||||
fido_bio_enroll_free(fido_bio_enroll_t **ep)
|
||||
{
|
||||
fido_bio_enroll_t *e;
|
||||
|
||||
if (ep == NULL || (e = *ep) == NULL)
|
||||
return;
|
||||
|
||||
bio_reset_enroll(e);
|
||||
|
||||
free(e);
|
||||
*ep = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fido_bio_info_free(fido_bio_info_t **ip)
|
||||
{
|
||||
fido_bio_info_t *i;
|
||||
|
||||
if (ip == NULL || (i = *ip) == NULL)
|
||||
return;
|
||||
|
||||
free(i);
|
||||
*ip = NULL;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
|
||||
{
|
||||
return (e->remaining_samples);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
|
||||
{
|
||||
return (e->last_status);
|
||||
}
|
||||
134
src/blob.c
Normal file
134
src/blob.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
fido_blob_t *
|
||||
fido_blob_new(void)
|
||||
{
|
||||
return calloc(1, sizeof(fido_blob_t));
|
||||
}
|
||||
|
||||
void
|
||||
fido_blob_reset(fido_blob_t *b)
|
||||
{
|
||||
freezero(b->ptr, b->len);
|
||||
explicit_bzero(b, sizeof(*b));
|
||||
}
|
||||
|
||||
int
|
||||
fido_blob_set(fido_blob_t *b, const u_char *ptr, size_t len)
|
||||
{
|
||||
fido_blob_reset(b);
|
||||
|
||||
if (ptr == NULL || len == 0) {
|
||||
fido_log_debug("%s: ptr=%p, len=%zu", __func__,
|
||||
(const void *)ptr, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((b->ptr = malloc(len)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(b->ptr, ptr, len);
|
||||
b->len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_blob_append(fido_blob_t *b, const u_char *ptr, size_t len)
|
||||
{
|
||||
u_char *tmp;
|
||||
|
||||
if (ptr == NULL || len == 0) {
|
||||
fido_log_debug("%s: ptr=%p, len=%zu", __func__,
|
||||
(const void *)ptr, len);
|
||||
return -1;
|
||||
}
|
||||
if (SIZE_MAX - b->len < len) {
|
||||
fido_log_debug("%s: overflow", __func__);
|
||||
return -1;
|
||||
}
|
||||
if ((tmp = realloc(b->ptr, b->len + len)) == NULL) {
|
||||
fido_log_debug("%s: realloc", __func__);
|
||||
return -1;
|
||||
}
|
||||
b->ptr = tmp;
|
||||
memcpy(&b->ptr[b->len], ptr, len);
|
||||
b->len += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_blob_free(fido_blob_t **bp)
|
||||
{
|
||||
fido_blob_t *b;
|
||||
|
||||
if (bp == NULL || (b = *bp) == NULL)
|
||||
return;
|
||||
|
||||
fido_blob_reset(b);
|
||||
free(b);
|
||||
*bp = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fido_free_blob_array(fido_blob_array_t *array)
|
||||
{
|
||||
if (array->ptr == NULL)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < array->len; i++) {
|
||||
fido_blob_t *b = &array->ptr[i];
|
||||
freezero(b->ptr, b->len);
|
||||
b->ptr = NULL;
|
||||
}
|
||||
|
||||
free(array->ptr);
|
||||
array->ptr = NULL;
|
||||
array->len = 0;
|
||||
}
|
||||
|
||||
cbor_item_t *
|
||||
fido_blob_encode(const fido_blob_t *b)
|
||||
{
|
||||
if (b == NULL || b->ptr == NULL)
|
||||
return NULL;
|
||||
|
||||
return cbor_build_bytestring(b->ptr, b->len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_blob_decode(const cbor_item_t *item, fido_blob_t *b)
|
||||
{
|
||||
return cbor_bytestring_copy(item, &b->ptr, &b->len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_blob_is_empty(const fido_blob_t *b)
|
||||
{
|
||||
return b->ptr == NULL || b->len == 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_blob_serialise(fido_blob_t *b, const cbor_item_t *item)
|
||||
{
|
||||
size_t alloc;
|
||||
|
||||
if (!fido_blob_is_empty(b))
|
||||
return -1;
|
||||
if ((b->len = cbor_serialize_alloc(item, &b->ptr, &alloc)) == 0) {
|
||||
b->ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
src/blob.h
Normal file
42
src/blob.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _BLOB_H
|
||||
#define _BLOB_H
|
||||
|
||||
#include <cbor.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct fido_blob {
|
||||
unsigned char *ptr;
|
||||
size_t len;
|
||||
} fido_blob_t;
|
||||
|
||||
typedef struct fido_blob_array {
|
||||
fido_blob_t *ptr;
|
||||
size_t len;
|
||||
} fido_blob_array_t;
|
||||
|
||||
cbor_item_t *fido_blob_encode(const fido_blob_t *);
|
||||
fido_blob_t *fido_blob_new(void);
|
||||
int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
|
||||
int fido_blob_is_empty(const fido_blob_t *);
|
||||
int fido_blob_set(fido_blob_t *, const u_char *, size_t);
|
||||
int fido_blob_append(fido_blob_t *, const u_char *, size_t);
|
||||
void fido_blob_free(fido_blob_t **);
|
||||
void fido_blob_reset(fido_blob_t *);
|
||||
void fido_free_blob_array(fido_blob_array_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_BLOB_H */
|
||||
34
src/buf.c
Normal file
34
src/buf.c
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
int
|
||||
fido_buf_read(const unsigned char **buf, size_t *len, void *dst, size_t count)
|
||||
{
|
||||
if (count > *len)
|
||||
return (-1);
|
||||
|
||||
memcpy(dst, *buf, count);
|
||||
*buf += count;
|
||||
*len -= count;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_buf_write(unsigned char **buf, size_t *len, const void *src, size_t count)
|
||||
{
|
||||
if (count > *len)
|
||||
return (-1);
|
||||
|
||||
memcpy(*buf, src, count);
|
||||
*buf += count;
|
||||
*len -= count;
|
||||
|
||||
return (0);
|
||||
}
|
||||
1796
src/cbor.c
Normal file
1796
src/cbor.c
Normal file
File diff suppressed because it is too large
Load Diff
168
src/compress.c
Normal file
168
src/compress.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 <zlib.h>
|
||||
#include "fido.h"
|
||||
|
||||
#define BOUND (1024UL * 1024UL)
|
||||
|
||||
/* zlib inflate (raw + headers) */
|
||||
static int
|
||||
rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
|
||||
{
|
||||
u_long ilen, olen;
|
||||
int z;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
|
||||
origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
|
||||
fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
|
||||
in->len, origsiz);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if ((out->ptr = calloc(1, olen)) == NULL)
|
||||
return FIDO_ERR_INTERNAL;
|
||||
out->len = olen;
|
||||
|
||||
if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
|
||||
olen > SIZE_MAX || olen != out->len) {
|
||||
fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
|
||||
__func__, z, olen, out->len);
|
||||
fido_blob_reset(out);
|
||||
return FIDO_ERR_COMPRESS;
|
||||
}
|
||||
|
||||
return FIDO_OK;
|
||||
}
|
||||
|
||||
/* raw inflate */
|
||||
static int
|
||||
rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
|
||||
{
|
||||
z_stream zs;
|
||||
u_int ilen, olen;
|
||||
int r, z;
|
||||
|
||||
memset(&zs, 0, sizeof(zs));
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
|
||||
origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
|
||||
fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
|
||||
in->len, origsiz);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
|
||||
fido_log_debug("%s: inflateInit2: %d", __func__, z);
|
||||
return FIDO_ERR_COMPRESS;
|
||||
}
|
||||
|
||||
if ((out->ptr = calloc(1, olen)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
out->len = olen;
|
||||
zs.next_in = in->ptr;
|
||||
zs.avail_in = ilen;
|
||||
zs.next_out = out->ptr;
|
||||
zs.avail_out = olen;
|
||||
|
||||
if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
|
||||
fido_log_debug("%s: inflate: %d", __func__, z);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
goto fail;
|
||||
}
|
||||
if (zs.avail_out != 0) {
|
||||
fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if ((z = inflateEnd(&zs)) != Z_OK) {
|
||||
fido_log_debug("%s: inflateEnd: %d", __func__, z);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
}
|
||||
if (r != FIDO_OK)
|
||||
fido_blob_reset(out);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* raw deflate */
|
||||
static int
|
||||
rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
|
||||
{
|
||||
z_stream zs;
|
||||
u_int ilen, olen;
|
||||
int r, z;
|
||||
|
||||
memset(&zs, 0, sizeof(zs));
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
|
||||
fido_log_debug("%s: in->len=%zu", __func__, in->len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
-MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
|
||||
fido_log_debug("%s: deflateInit2: %d", __func__, z);
|
||||
return FIDO_ERR_COMPRESS;
|
||||
}
|
||||
|
||||
olen = BOUND;
|
||||
if ((out->ptr = calloc(1, olen)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
out->len = olen;
|
||||
zs.next_in = in->ptr;
|
||||
zs.avail_in = ilen;
|
||||
zs.next_out = out->ptr;
|
||||
zs.avail_out = olen;
|
||||
|
||||
if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
|
||||
fido_log_debug("%s: inflate: %d", __func__, z);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
goto fail;
|
||||
}
|
||||
if (zs.avail_out >= out->len) {
|
||||
fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
|
||||
out->len);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
goto fail;
|
||||
}
|
||||
out->len -= zs.avail_out;
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if ((z = deflateEnd(&zs)) != Z_OK) {
|
||||
fido_log_debug("%s: deflateEnd: %d", __func__, z);
|
||||
r = FIDO_ERR_COMPRESS;
|
||||
}
|
||||
if (r != FIDO_OK)
|
||||
fido_blob_reset(out);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_compress(fido_blob_t *out, const fido_blob_t *in)
|
||||
{
|
||||
return rfc1951_deflate(out, in);
|
||||
}
|
||||
|
||||
int
|
||||
fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
|
||||
{
|
||||
if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
|
||||
return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
|
||||
return rfc1951_inflate(out, in, origsiz);
|
||||
}
|
||||
235
src/config.c
Normal file
235
src/config.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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 "fido.h"
|
||||
#include "fido/config.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#define CMD_ENABLE_ENTATTEST 0x01
|
||||
#define CMD_TOGGLE_ALWAYS_UV 0x02
|
||||
#define CMD_SET_PIN_MINLEN 0x03
|
||||
|
||||
static int
|
||||
config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac)
|
||||
{
|
||||
uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128];
|
||||
size_t cbor_len = 0;
|
||||
|
||||
memset(prefix, 0xff, sizeof(prefix));
|
||||
prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG;
|
||||
prefix[sizeof(prefix) - 1] = subcmd;
|
||||
|
||||
if (item != NULL) {
|
||||
if ((cbor_len = cbor_serialize(item, cbor, sizeof(cbor))) == 0) {
|
||||
fido_log_debug("%s: cbor_serialize", __func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((hmac->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
return -1;
|
||||
}
|
||||
memcpy(hmac->ptr, prefix, sizeof(prefix));
|
||||
memcpy(hmac->ptr + sizeof(prefix), cbor, cbor_len);
|
||||
hmac->len = cbor_len + sizeof(prefix);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[4];
|
||||
es256_pk_t *pk = NULL;
|
||||
fido_blob_t *ecdh = NULL, f, hmac;
|
||||
const uint8_t cmd = CTAP_CBOR_CONFIG;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(&hmac, 0, sizeof(hmac));
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
/* subCommand */
|
||||
if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* subCommandParams */
|
||||
if (paramc != 0 &&
|
||||
(argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) {
|
||||
fido_log_debug("%s: cbor_flatten_vector", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* pinProtocol, pinAuth */
|
||||
if (pin != NULL ||
|
||||
(fido_dev_supports_permissions(dev) && fido_dev_has_uv(dev))) {
|
||||
if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) {
|
||||
fido_log_debug("%s: config_prepare_hmac", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
|
||||
NULL, &argv[3], &argv[2], ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: cbor_add_uv_params", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* framing and transmission */
|
||||
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ecdh);
|
||||
free(f.ptr);
|
||||
free(hmac.ptr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin,
|
||||
ms)) != FIDO_OK)
|
||||
return r;
|
||||
|
||||
return fido_rx_cbor_status(dev, ms);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_enable_entattest(fido_dev_t *dev, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (config_enable_entattest_wait(dev, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin,
|
||||
ms)) != FIDO_OK)
|
||||
return r;
|
||||
|
||||
return (fido_rx_cbor_status(dev, ms));
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return config_toggle_always_uv_wait(dev, pin, &ms);
|
||||
}
|
||||
|
||||
static int
|
||||
config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force,
|
||||
const fido_str_array_t *rpid, const char *pin, int *ms)
|
||||
{
|
||||
cbor_item_t *argv[3];
|
||||
int r;
|
||||
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if ((rpid == NULL && len == 0 && !force) || len > UINT8_MAX) {
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) {
|
||||
fido_log_debug("%s: cbor_encode_uint8", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (rpid != NULL && (argv[1] = cbor_encode_str_array(rpid)) == NULL) {
|
||||
fido_log_debug("%s: cbor_encode_str_array", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (force && (argv[2] = cbor_build_bool(true)) == NULL) {
|
||||
fido_log_debug("%s: cbor_build_bool", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv),
|
||||
pin, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: config_tx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
config_pin_minlen(fido_dev_t *dev, size_t len, bool force,
|
||||
const fido_str_array_t *rpid, const char *pin, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = config_pin_minlen_tx(dev, len, force, rpid, pin,
|
||||
ms)) != FIDO_OK)
|
||||
return r;
|
||||
|
||||
return fido_rx_cbor_status(dev, ms);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return config_pin_minlen(dev, len, false, NULL, pin, &ms);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_force_pin_change(fido_dev_t *dev, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return config_pin_minlen(dev, 0, true, NULL, pin, &ms);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_pin_minlen_rpid(fido_dev_t *dev, const char * const *rpid,
|
||||
size_t n, const char *pin)
|
||||
{
|
||||
fido_str_array_t sa;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
if (fido_str_array_pack(&sa, rpid, n) < 0) {
|
||||
fido_log_debug("%s: fido_str_array_pack", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
r = config_pin_minlen(dev, 0, false, &sa, pin, &ms);
|
||||
fail:
|
||||
fido_str_array_free(&sa);
|
||||
|
||||
return r;
|
||||
}
|
||||
1316
src/cred.c
Normal file
1316
src/cred.c
Normal file
File diff suppressed because it is too large
Load Diff
834
src/credman.c
Normal file
834
src/credman.c
Normal file
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
* Copyright (c) 2019-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 <openssl/sha.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/credman.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#define CMD_CRED_METADATA 0x01
|
||||
#define CMD_RP_BEGIN 0x02
|
||||
#define CMD_RP_NEXT 0x03
|
||||
#define CMD_RK_BEGIN 0x04
|
||||
#define CMD_RK_NEXT 0x05
|
||||
#define CMD_DELETE_CRED 0x06
|
||||
#define CMD_UPDATE_CRED 0x07
|
||||
|
||||
static int
|
||||
credman_grow_array(void **ptr, size_t *n_alloc, const size_t *n_rx, size_t n,
|
||||
size_t size)
|
||||
{
|
||||
void *new_ptr;
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
if (n > UINT8_MAX) {
|
||||
fido_log_debug("%s: n > UINT8_MAX", __func__);
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (n < *n_alloc)
|
||||
return (0);
|
||||
|
||||
/* sanity check */
|
||||
if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
|
||||
fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
|
||||
*n_rx, *n_alloc);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
|
||||
return (-1);
|
||||
|
||||
*ptr = new_ptr;
|
||||
*n_alloc = n;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param,
|
||||
fido_blob_t *hmac_data)
|
||||
{
|
||||
cbor_item_t *param_cbor[3];
|
||||
const fido_cred_t *cred;
|
||||
size_t n;
|
||||
int ok = -1;
|
||||
|
||||
memset(¶m_cbor, 0, sizeof(param_cbor));
|
||||
|
||||
if (body == NULL)
|
||||
return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_RK_BEGIN:
|
||||
n = 1;
|
||||
if ((param_cbor[0] = fido_blob_encode(body)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case CMD_DELETE_CRED:
|
||||
n = 2;
|
||||
if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case CMD_UPDATE_CRED:
|
||||
n = 3;
|
||||
cred = body;
|
||||
param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id);
|
||||
param_cbor[2] = cbor_encode_user_entity(&cred->user);
|
||||
if (param_cbor[1] == NULL || param_cbor[2] == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
|
||||
fido_log_debug("%s: cbor_flatten_vector", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
|
||||
fido_log_debug("%s: cbor_build_frame", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
cbor_vector_free(param_cbor, nitems(param_cbor));
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
credman_get_cmd(const fido_dev_t *dev)
|
||||
{
|
||||
if (dev->flags & FIDO_DEV_CREDMAN)
|
||||
return (CTAP_CBOR_CRED_MGMT);
|
||||
|
||||
return (CTAP_CBOR_CRED_MGMT_PRE);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
|
||||
const char *rp_id, fido_opt_t uv, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
fido_blob_t hmac;
|
||||
es256_pk_t *pk = NULL;
|
||||
cbor_item_t *argv[4];
|
||||
const uint8_t cmd = credman_get_cmd(dev);
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(&hmac, 0, sizeof(hmac));
|
||||
memset(&argv, 0, sizeof(argv));
|
||||
|
||||
if (fido_dev_is_fido2(dev) == false) {
|
||||
fido_log_debug("%s: fido_dev_is_fido2", __func__);
|
||||
r = FIDO_ERR_INVALID_COMMAND;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* subCommand */
|
||||
if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* pinProtocol, pinAuth */
|
||||
if (pin != NULL || uv == FIDO_OPT_TRUE) {
|
||||
if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) {
|
||||
fido_log_debug("%s: credman_prepare_hmac", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
|
||||
rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: cbor_add_uv_params", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* framing and transmission */
|
||||
if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ecdh);
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
free(f.ptr);
|
||||
free(hmac.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_credman_metadata_t *metadata = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1:
|
||||
return (cbor_decode_uint64(val, &metadata->rk_existing));
|
||||
case 2:
|
||||
return (cbor_decode_uint64(val, &metadata->rk_remaining));
|
||||
default:
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
memset(metadata, 0, sizeof(*metadata));
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, metadata,
|
||||
credman_parse_metadata)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_metadata", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
|
||||
FIDO_OPT_TRUE, ms)) != FIDO_OK ||
|
||||
(r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
|
||||
const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (credman_get_metadata_wait(dev, metadata, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_cred_t *cred = arg;
|
||||
uint64_t prot;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 6:
|
||||
return (cbor_decode_user(val, &cred->user));
|
||||
case 7:
|
||||
return (cbor_decode_cred_id(val, &cred->attcred.id));
|
||||
case 8:
|
||||
if (cbor_decode_pubkey(val, &cred->attcred.type,
|
||||
&cred->attcred.pubkey) < 0)
|
||||
return (-1);
|
||||
cred->type = cred->attcred.type; /* XXX */
|
||||
return (0);
|
||||
case 10:
|
||||
if (cbor_decode_uint64(val, &prot) < 0 || prot > INT_MAX ||
|
||||
fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
|
||||
return (-1);
|
||||
return (0);
|
||||
case 11:
|
||||
return (fido_blob_decode(val, &cred->largeblob_key));
|
||||
default:
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
credman_reset_rk(fido_credman_rk_t *rk)
|
||||
{
|
||||
for (size_t i = 0; i < rk->n_alloc; i++) {
|
||||
fido_cred_reset_tx(&rk->ptr[i]);
|
||||
fido_cred_reset_rx(&rk->ptr[i]);
|
||||
}
|
||||
|
||||
free(rk->ptr);
|
||||
rk->ptr = NULL;
|
||||
memset(rk, 0, sizeof(*rk));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_credman_rk_t *rk = arg;
|
||||
uint64_t n;
|
||||
|
||||
/* totalCredentials */
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 9) {
|
||||
fido_log_debug("%s: cbor_type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
|
||||
(size_t)n, sizeof(*rk->ptr)) < 0) {
|
||||
fido_log_debug("%s: credman_grow_array", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
credman_reset_rk(rk);
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* adjust as needed */
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, rk,
|
||||
credman_parse_rk_count)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rk_count", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rk->n_alloc == 0) {
|
||||
fido_log_debug("%s: n_alloc=0", __func__);
|
||||
r = FIDO_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* parse the first rk */
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[0],
|
||||
credman_parse_rk)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rk", __func__);
|
||||
goto out;
|
||||
}
|
||||
rk->n_rx = 1;
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (rk->n_rx >= rk->n_alloc) {
|
||||
fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
|
||||
rk->n_alloc);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, &rk->ptr[rk->n_rx],
|
||||
credman_parse_rk)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rk", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
fido_blob_t rp_dgst;
|
||||
uint8_t dgst[SHA256_DIGEST_LENGTH];
|
||||
int r;
|
||||
|
||||
if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
rp_dgst.ptr = dgst;
|
||||
rp_dgst.len = sizeof(dgst);
|
||||
|
||||
if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
|
||||
FIDO_OPT_TRUE, ms)) != FIDO_OK ||
|
||||
(r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
while (rk->n_rx < rk->n_alloc) {
|
||||
if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL,
|
||||
FIDO_OPT_FALSE, ms)) != FIDO_OK ||
|
||||
(r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
rk->n_rx++;
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
|
||||
fido_credman_rk_t *rk, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
|
||||
size_t cred_id_len, const char *pin, int *ms)
|
||||
{
|
||||
fido_blob_t cred;
|
||||
int r;
|
||||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
|
||||
if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
|
||||
FIDO_OPT_TRUE, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
|
||||
goto fail;
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
free(cred.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
|
||||
size_t cred_id_len, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
struct fido_credman_single_rp *rp = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 3:
|
||||
return (cbor_decode_rp_entity(val, &rp->rp_entity));
|
||||
case 4:
|
||||
return (fido_blob_decode(val, &rp->rp_id_hash));
|
||||
default:
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
credman_reset_rp(fido_credman_rp_t *rp)
|
||||
{
|
||||
for (size_t i = 0; i < rp->n_alloc; i++) {
|
||||
free(rp->ptr[i].rp_entity.id);
|
||||
free(rp->ptr[i].rp_entity.name);
|
||||
rp->ptr[i].rp_entity.id = NULL;
|
||||
rp->ptr[i].rp_entity.name = NULL;
|
||||
fido_blob_reset(&rp->ptr[i].rp_id_hash);
|
||||
}
|
||||
|
||||
free(rp->ptr);
|
||||
rp->ptr = NULL;
|
||||
memset(rp, 0, sizeof(*rp));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_credman_rp_t *rp = arg;
|
||||
uint64_t n;
|
||||
|
||||
/* totalRPs */
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 5) {
|
||||
fido_log_debug("%s: cbor_type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
|
||||
(size_t)n, sizeof(*rp->ptr)) < 0) {
|
||||
fido_log_debug("%s: credman_grow_array", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
credman_reset_rp(rp);
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* adjust as needed */
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, rp,
|
||||
credman_parse_rp_count)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rp_count", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rp->n_alloc == 0) {
|
||||
fido_log_debug("%s: n_alloc=0", __func__);
|
||||
r = FIDO_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* parse the first rp */
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[0],
|
||||
credman_parse_rp)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rp", __func__);
|
||||
goto out;
|
||||
}
|
||||
rp->n_rx = 1;
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (rp->n_rx >= rp->n_alloc) {
|
||||
fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
|
||||
rp->n_alloc);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, &rp->ptr[rp->n_rx],
|
||||
credman_parse_rp)) != FIDO_OK) {
|
||||
fido_log_debug("%s: credman_parse_rp", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
|
||||
int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
|
||||
FIDO_OPT_TRUE, ms)) != FIDO_OK ||
|
||||
(r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
while (rp->n_rx < rp->n_alloc) {
|
||||
if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL,
|
||||
FIDO_OPT_FALSE, ms)) != FIDO_OK ||
|
||||
(r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
rp->n_rx++;
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (credman_get_rp_wait(dev, rp, pin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
|
||||
int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL,
|
||||
FIDO_OPT_TRUE, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (credman_set_dev_rk_wait(dev, cred, pin, &ms));
|
||||
}
|
||||
|
||||
fido_credman_rk_t *
|
||||
fido_credman_rk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_credman_rk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
fido_credman_rk_free(fido_credman_rk_t **rk_p)
|
||||
{
|
||||
fido_credman_rk_t *rk;
|
||||
|
||||
if (rk_p == NULL || (rk = *rk_p) == NULL)
|
||||
return;
|
||||
|
||||
credman_reset_rk(rk);
|
||||
free(rk);
|
||||
*rk_p = NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_credman_rk_count(const fido_credman_rk_t *rk)
|
||||
{
|
||||
return (rk->n_rx);
|
||||
}
|
||||
|
||||
const fido_cred_t *
|
||||
fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
|
||||
{
|
||||
if (idx >= rk->n_alloc)
|
||||
return (NULL);
|
||||
|
||||
return (&rk->ptr[idx]);
|
||||
}
|
||||
|
||||
fido_credman_metadata_t *
|
||||
fido_credman_metadata_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_credman_metadata_t)));
|
||||
}
|
||||
|
||||
void
|
||||
fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
|
||||
{
|
||||
fido_credman_metadata_t *metadata;
|
||||
|
||||
if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
|
||||
return;
|
||||
|
||||
free(metadata);
|
||||
*metadata_p = NULL;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
|
||||
{
|
||||
return (metadata->rk_existing);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
|
||||
{
|
||||
return (metadata->rk_remaining);
|
||||
}
|
||||
|
||||
fido_credman_rp_t *
|
||||
fido_credman_rp_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(fido_credman_rp_t)));
|
||||
}
|
||||
|
||||
void
|
||||
fido_credman_rp_free(fido_credman_rp_t **rp_p)
|
||||
{
|
||||
fido_credman_rp_t *rp;
|
||||
|
||||
if (rp_p == NULL || (rp = *rp_p) == NULL)
|
||||
return;
|
||||
|
||||
credman_reset_rp(rp);
|
||||
free(rp);
|
||||
*rp_p = NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_credman_rp_count(const fido_credman_rp_t *rp)
|
||||
{
|
||||
return (rp->n_rx);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
|
||||
{
|
||||
if (idx >= rp->n_alloc)
|
||||
return (NULL);
|
||||
|
||||
return (rp->ptr[idx].rp_entity.id);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
|
||||
{
|
||||
if (idx >= rp->n_alloc)
|
||||
return (NULL);
|
||||
|
||||
return (rp->ptr[idx].rp_entity.name);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
|
||||
{
|
||||
if (idx >= rp->n_alloc)
|
||||
return (0);
|
||||
|
||||
return (rp->ptr[idx].rp_id_hash.len);
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
|
||||
{
|
||||
if (idx >= rp->n_alloc)
|
||||
return (NULL);
|
||||
|
||||
return (rp->ptr[idx].rp_id_hash.ptr);
|
||||
}
|
||||
606
src/dev.c
Normal file
606
src/dev.c
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#ifndef TLS
|
||||
#define TLS
|
||||
#endif
|
||||
|
||||
static TLS bool disable_u2f_fallback;
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
static void
|
||||
set_random_report_len(fido_dev_t *dev)
|
||||
{
|
||||
dev->rx_len = CTAP_MIN_REPORT_LEN +
|
||||
uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
|
||||
dev->tx_len = CTAP_MIN_REPORT_LEN +
|
||||
uniform_random(CTAP_MAX_REPORT_LEN - CTAP_MIN_REPORT_LEN + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
|
||||
{
|
||||
char * const *ptr = fido_cbor_info_extensions_ptr(info);
|
||||
size_t len = fido_cbor_info_extensions_len(info);
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
if (strcmp(ptr[i], "credProtect") == 0)
|
||||
dev->flags |= FIDO_DEV_CRED_PROT;
|
||||
}
|
||||
|
||||
static void
|
||||
fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
|
||||
{
|
||||
char * const *ptr = fido_cbor_info_options_name_ptr(info);
|
||||
const bool *val = fido_cbor_info_options_value_ptr(info);
|
||||
size_t len = fido_cbor_info_options_len(info);
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
if (strcmp(ptr[i], "clientPin") == 0) {
|
||||
dev->flags |= val[i] ?
|
||||
FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
|
||||
} else if (strcmp(ptr[i], "credMgmt") == 0) {
|
||||
if (val[i])
|
||||
dev->flags |= FIDO_DEV_CREDMAN;
|
||||
} else if (strcmp(ptr[i], "credentialMgmtPreview") == 0) {
|
||||
if (val[i])
|
||||
dev->flags |= FIDO_DEV_CREDMAN_PRE;
|
||||
} else if (strcmp(ptr[i], "uv") == 0) {
|
||||
dev->flags |= val[i] ?
|
||||
FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
|
||||
} else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
|
||||
if (val[i])
|
||||
dev->flags |= FIDO_DEV_TOKEN_PERMS;
|
||||
} else if (strcmp(ptr[i], "bioEnroll") == 0) {
|
||||
dev->flags |= val[i] ?
|
||||
FIDO_DEV_BIO_SET : FIDO_DEV_BIO_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
|
||||
{
|
||||
const uint8_t *ptr = fido_cbor_info_protocols_ptr(info);
|
||||
size_t len = fido_cbor_info_protocols_len(info);
|
||||
|
||||
for (size_t i = 0; i < len; i++)
|
||||
switch (ptr[i]) {
|
||||
case CTAP_PIN_PROTOCOL1:
|
||||
dev->flags |= FIDO_DEV_PIN_PROTOCOL1;
|
||||
break;
|
||||
case CTAP_PIN_PROTOCOL2:
|
||||
dev->flags |= FIDO_DEV_PIN_PROTOCOL2;
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unknown protocol %u", __func__,
|
||||
ptr[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
|
||||
{
|
||||
fido_dev_set_extension_flags(dev, info);
|
||||
fido_dev_set_option_flags(dev, info);
|
||||
fido_dev_set_protocol_flags(dev, info);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (dev->io_handle != NULL) {
|
||||
fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (dev->io.open == NULL || dev->io.close == NULL) {
|
||||
fido_log_debug("%s: NULL open/close", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (dev->cid != CTAP_CID_BROADCAST) {
|
||||
fido_log_debug("%s: cid=0x%x", __func__, dev->cid);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) {
|
||||
fido_log_debug("%s: fido_get_random", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((dev->io_handle = dev->io.open(path)) == NULL) {
|
||||
fido_log_debug("%s: dev->io.open", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if (dev->io_own) {
|
||||
dev->rx_len = CTAP_MAX_REPORT_LEN;
|
||||
dev->tx_len = CTAP_MAX_REPORT_LEN;
|
||||
} else {
|
||||
dev->rx_len = fido_hid_report_in_len(dev->io_handle);
|
||||
dev->tx_len = fido_hid_report_out_len(dev->io_handle);
|
||||
}
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
set_random_report_len(dev);
|
||||
#endif
|
||||
|
||||
if (dev->rx_len < CTAP_MIN_REPORT_LEN ||
|
||||
dev->rx_len > CTAP_MAX_REPORT_LEN) {
|
||||
fido_log_debug("%s: invalid rx_len %zu", __func__, dev->rx_len);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dev->tx_len < CTAP_MIN_REPORT_LEN ||
|
||||
dev->tx_len > CTAP_MAX_REPORT_LEN) {
|
||||
fido_log_debug("%s: invalid tx_len %zu", __func__, dev->tx_len);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce),
|
||||
ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
fail:
|
||||
dev->io.close(dev->io_handle);
|
||||
dev->io_handle = NULL;
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_open_rx(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
fido_cbor_info_t *info = NULL;
|
||||
int reply_len;
|
||||
int r;
|
||||
|
||||
if ((reply_len = fido_rx(dev, CTAP_CMD_INIT, &dev->attr,
|
||||
sizeof(dev->attr), ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
dev->attr.nonce = dev->nonce;
|
||||
#endif
|
||||
|
||||
if ((size_t)reply_len != sizeof(dev->attr) ||
|
||||
dev->attr.nonce != dev->nonce) {
|
||||
fido_log_debug("%s: invalid nonce", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev->flags = 0;
|
||||
dev->cid = dev->attr.cid;
|
||||
|
||||
if (fido_dev_is_fido2(dev)) {
|
||||
if ((info = fido_cbor_info_new()) == NULL) {
|
||||
fido_log_debug("%s: fido_cbor_info_new", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((r = fido_dev_get_cbor_info_wait(dev, info,
|
||||
ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_cbor_info_wait: %d",
|
||||
__func__, r);
|
||||
if (disable_u2f_fallback)
|
||||
goto fail;
|
||||
fido_log_debug("%s: falling back to u2f", __func__);
|
||||
fido_dev_force_u2f(dev);
|
||||
} else {
|
||||
fido_dev_set_flags(dev, info);
|
||||
}
|
||||
}
|
||||
|
||||
if (fido_dev_is_fido2(dev) && info != NULL) {
|
||||
dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info);
|
||||
fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__,
|
||||
FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_cbor_info_free(&info);
|
||||
|
||||
if (r != FIDO_OK) {
|
||||
dev->io.close(dev->io_handle);
|
||||
dev->io_handle = NULL;
|
||||
}
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef USE_WINHELLO
|
||||
if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
|
||||
return (fido_winhello_open(dev));
|
||||
#endif
|
||||
if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK ||
|
||||
(r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
run_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen,
|
||||
const char *type, int (*manifest)(fido_dev_info_t *, size_t, size_t *))
|
||||
{
|
||||
size_t ndevs = 0;
|
||||
int r;
|
||||
|
||||
if (*olen >= ilen) {
|
||||
fido_log_debug("%s: skipping %s", __func__, type);
|
||||
return;
|
||||
}
|
||||
if ((r = manifest(devlist + *olen, ilen - *olen, &ndevs)) != FIDO_OK)
|
||||
fido_log_debug("%s: %s: 0x%x", __func__, type, r);
|
||||
fido_log_debug("%s: found %zu %s device%s", __func__, ndevs, type,
|
||||
ndevs == 1 ? "" : "s");
|
||||
*olen += ndevs;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
*olen = 0;
|
||||
|
||||
run_manifest(devlist, ilen, olen, "hid", fido_hid_manifest);
|
||||
#ifdef USE_NFC
|
||||
run_manifest(devlist, ilen, olen, "nfc", fido_nfc_manifest);
|
||||
#endif
|
||||
#ifdef USE_PCSC
|
||||
run_manifest(devlist, ilen, olen, "pcsc", fido_pcsc_manifest);
|
||||
#endif
|
||||
#ifdef USE_WINHELLO
|
||||
run_manifest(devlist, ilen, olen, "winhello", fido_winhello_manifest);
|
||||
#endif
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_open_with_info(fido_dev_t *dev)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
if (dev->path == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (fido_dev_open_wait(dev, dev->path, &ms));
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_open(fido_dev_t *dev, const char *path)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
#ifdef USE_NFC
|
||||
if (fido_is_nfc(path) && fido_dev_set_nfc(dev) < 0) {
|
||||
fido_log_debug("%s: fido_dev_set_nfc", __func__);
|
||||
return FIDO_ERR_INTERNAL;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_PCSC
|
||||
if (fido_is_pcsc(path) && fido_dev_set_pcsc(dev) < 0) {
|
||||
fido_log_debug("%s: fido_dev_set_pcsc", __func__);
|
||||
return FIDO_ERR_INTERNAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (fido_dev_open_wait(dev, path, &ms));
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_close(fido_dev_t *dev)
|
||||
{
|
||||
#ifdef USE_WINHELLO
|
||||
if (dev->flags & FIDO_DEV_WINHELLO)
|
||||
return (fido_winhello_close(dev));
|
||||
#endif
|
||||
if (dev->io_handle == NULL || dev->io.close == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
dev->io.close(dev->io_handle);
|
||||
dev->io_handle = NULL;
|
||||
dev->cid = CTAP_CID_BROADCAST;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
|
||||
{
|
||||
if (dev->io_handle == NULL || sigmask == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
#ifdef USE_NFC
|
||||
if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read)
|
||||
return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
|
||||
#endif
|
||||
if (dev->transport.rx == NULL && dev->io.read == fido_hid_read)
|
||||
return (fido_hid_set_sigmask(dev->io_handle, sigmask));
|
||||
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_cancel(fido_dev_t *dev)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
#ifdef USE_WINHELLO
|
||||
if (dev->flags & FIDO_DEV_WINHELLO)
|
||||
return (fido_winhello_cancel(dev));
|
||||
#endif
|
||||
if (fido_dev_is_fido2(dev) == false)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0)
|
||||
return (FIDO_ERR_TX);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
|
||||
{
|
||||
if (dev->io_handle != NULL) {
|
||||
fido_log_debug("%s: non-NULL handle", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (io == NULL || io->open == NULL || io->close == NULL ||
|
||||
io->read == NULL || io->write == NULL) {
|
||||
fido_log_debug("%s: NULL function", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
dev->io = *io;
|
||||
dev->io_own = true;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
|
||||
{
|
||||
if (dev->io_handle != NULL) {
|
||||
fido_log_debug("%s: non-NULL handle", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
dev->transport = *t;
|
||||
dev->io_own = true;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
fido_dev_io_handle(const fido_dev_t *dev)
|
||||
{
|
||||
|
||||
return (dev->io_handle);
|
||||
}
|
||||
|
||||
void
|
||||
fido_init(int flags)
|
||||
{
|
||||
if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
|
||||
fido_log_init();
|
||||
|
||||
disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
|
||||
}
|
||||
|
||||
fido_dev_t *
|
||||
fido_dev_new(void)
|
||||
{
|
||||
fido_dev_t *dev;
|
||||
|
||||
if ((dev = calloc(1, sizeof(*dev))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
dev->cid = CTAP_CID_BROADCAST;
|
||||
dev->timeout_ms = -1;
|
||||
dev->io = (fido_dev_io_t) {
|
||||
&fido_hid_open,
|
||||
&fido_hid_close,
|
||||
&fido_hid_read,
|
||||
&fido_hid_write,
|
||||
};
|
||||
|
||||
return (dev);
|
||||
}
|
||||
|
||||
fido_dev_t *
|
||||
fido_dev_new_with_info(const fido_dev_info_t *di)
|
||||
{
|
||||
fido_dev_t *dev;
|
||||
|
||||
if ((dev = calloc(1, sizeof(*dev))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
#if 0
|
||||
if (di->io.open == NULL || di->io.close == NULL ||
|
||||
di->io.read == NULL || di->io.write == NULL) {
|
||||
fido_log_debug("%s: NULL function", __func__);
|
||||
fido_dev_free(&dev);
|
||||
return (NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
dev->io = di->io;
|
||||
dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
|
||||
dev->transport = di->transport;
|
||||
dev->cid = CTAP_CID_BROADCAST;
|
||||
dev->timeout_ms = -1;
|
||||
|
||||
if ((dev->path = strdup(di->path)) == NULL) {
|
||||
fido_log_debug("%s: strdup", __func__);
|
||||
fido_dev_free(&dev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (dev);
|
||||
}
|
||||
|
||||
void
|
||||
fido_dev_free(fido_dev_t **dev_p)
|
||||
{
|
||||
fido_dev_t *dev;
|
||||
|
||||
if (dev_p == NULL || (dev = *dev_p) == NULL)
|
||||
return;
|
||||
|
||||
free(dev->path);
|
||||
free(dev);
|
||||
|
||||
*dev_p = NULL;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_protocol(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.protocol);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_major(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.major);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_minor(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.minor);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_build(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.build);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_flags(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.flags);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_is_fido2(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->attr.flags & FIDO_CAP_CBOR);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_is_winhello(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & FIDO_DEV_WINHELLO);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_supports_pin(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & (FIDO_DEV_PIN_SET|FIDO_DEV_PIN_UNSET));
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_has_pin(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & FIDO_DEV_PIN_SET);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_supports_cred_prot(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & FIDO_DEV_CRED_PROT);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_supports_credman(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & (FIDO_DEV_CREDMAN|FIDO_DEV_CREDMAN_PRE));
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_supports_uv(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_has_uv(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & FIDO_DEV_UV_SET);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_dev_supports_permissions(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->flags & FIDO_DEV_TOKEN_PERMS);
|
||||
}
|
||||
|
||||
void
|
||||
fido_dev_force_u2f(fido_dev_t *dev)
|
||||
{
|
||||
dev->attr.flags &= (uint8_t)~FIDO_CAP_CBOR;
|
||||
dev->flags = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_dev_force_fido2(fido_dev_t *dev)
|
||||
{
|
||||
dev->attr.flags |= FIDO_CAP_CBOR;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fido_dev_get_pin_protocol(const fido_dev_t *dev)
|
||||
{
|
||||
if (dev->flags & FIDO_DEV_PIN_PROTOCOL2)
|
||||
return (CTAP_PIN_PROTOCOL2);
|
||||
else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1)
|
||||
return (CTAP_PIN_PROTOCOL1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_dev_maxmsgsize(const fido_dev_t *dev)
|
||||
{
|
||||
return (dev->maxmsgsize);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_timeout(fido_dev_t *dev, int ms)
|
||||
{
|
||||
if (ms < -1)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
dev->timeout_ms = ms;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
27
src/diff_exports.sh
Normal file
27
src/diff_exports.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh -u
|
||||
|
||||
# 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
|
||||
|
||||
for f in export.gnu export.llvm export.msvc; do
|
||||
if [ ! -f "${f}" ]; then
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
TMPDIR="$(mktemp -d)"
|
||||
GNU="${TMPDIR}/gnu"
|
||||
LLVM="${TMPDIR}/llvm"
|
||||
MSVC="${TMPDIR}/msvc"
|
||||
|
||||
awk '/^[^*{}]+;$/' export.gnu | tr -d '\t;' | sort > "${GNU}"
|
||||
sed 's/^_//' export.llvm | sort > "${LLVM}"
|
||||
grep -v '^EXPORTS$' export.msvc | sort > "${MSVC}"
|
||||
diff -u "${GNU}" "${LLVM}" && diff -u "${MSVC}" "${LLVM}"
|
||||
ERROR=$?
|
||||
rm "${GNU}" "${LLVM}" "${MSVC}"
|
||||
rmdir "${TMPDIR}"
|
||||
|
||||
exit ${ERROR}
|
||||
208
src/ecdh.c
Normal file
208
src/ecdh.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 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/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
#if defined(LIBRESSL_VERSION_NUMBER)
|
||||
#include <openssl/hkdf.h>
|
||||
#else
|
||||
#include <openssl/kdf.h>
|
||||
#endif
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER)
|
||||
static int
|
||||
hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
|
||||
{
|
||||
const EVP_MD *md;
|
||||
uint8_t salt[32];
|
||||
|
||||
memset(salt, 0, sizeof(salt));
|
||||
if ((md = EVP_sha256()) == NULL ||
|
||||
HKDF(key, SHA256_DIGEST_LENGTH, md, secret->ptr, secret->len, salt,
|
||||
sizeof(salt), (const uint8_t *)info, strlen(info)) != 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int
|
||||
hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret)
|
||||
{
|
||||
const EVP_MD *const_md;
|
||||
EVP_MD *md = NULL;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
size_t keylen = SHA256_DIGEST_LENGTH;
|
||||
uint8_t salt[32];
|
||||
int ok = -1;
|
||||
|
||||
memset(salt, 0, sizeof(salt));
|
||||
if (secret->len > INT_MAX || strlen(info) > INT_MAX) {
|
||||
fido_log_debug("%s: invalid param", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((const_md = EVP_sha256()) == NULL ||
|
||||
(md = EVP_MD_meth_dup(const_md)) == NULL ||
|
||||
(ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) {
|
||||
fido_log_debug("%s: init", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (EVP_PKEY_derive_init(ctx) < 1 ||
|
||||
EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 ||
|
||||
EVP_PKEY_CTX_add1_hkdf_info(ctx, (void *)info, (int)strlen(info)) < 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_CTX", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (EVP_PKEY_derive(ctx, key, &keylen) < 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_derive", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (md != NULL)
|
||||
EVP_MD_meth_free(md);
|
||||
if (ctx != NULL)
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
return ok;
|
||||
}
|
||||
#endif /* defined(LIBRESSL_VERSION_NUMBER) */
|
||||
|
||||
static int
|
||||
kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret)
|
||||
{
|
||||
char hmac_info[] = "CTAP2 HMAC key"; /* const */
|
||||
char aes_info[] = "CTAP2 AES key"; /* const */
|
||||
|
||||
switch (prot) {
|
||||
case CTAP_PIN_PROTOCOL1:
|
||||
/* use sha256 on the resulting secret */
|
||||
key->len = SHA256_DIGEST_LENGTH;
|
||||
if ((key->ptr = calloc(1, key->len)) == NULL ||
|
||||
SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) {
|
||||
fido_log_debug("%s: SHA256", __func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case CTAP_PIN_PROTOCOL2:
|
||||
/* use two instances of hkdf-sha256 on the resulting secret */
|
||||
key->len = 2 * SHA256_DIGEST_LENGTH;
|
||||
if ((key->ptr = calloc(1, key->len)) == NULL ||
|
||||
hkdf_sha256(key->ptr, hmac_info, secret) < 0 ||
|
||||
hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, aes_info,
|
||||
secret) < 0) {
|
||||
fido_log_debug("%s: hkdf", __func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unknown pin protocol %u", __func__, prot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_ecdh(const fido_dev_t *dev, const es256_sk_t *sk, const es256_pk_t *pk,
|
||||
fido_blob_t **ecdh)
|
||||
{
|
||||
EVP_PKEY *pk_evp = NULL;
|
||||
EVP_PKEY *sk_evp = NULL;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
fido_blob_t *secret = NULL;
|
||||
int ok = -1;
|
||||
|
||||
*ecdh = NULL;
|
||||
if ((secret = fido_blob_new()) == NULL ||
|
||||
(*ecdh = fido_blob_new()) == NULL)
|
||||
goto fail;
|
||||
if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
|
||||
(sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
|
||||
fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
|
||||
EVP_PKEY_derive_init(ctx) <= 0 ||
|
||||
EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
|
||||
(secret->ptr = calloc(1, secret->len)) == NULL ||
|
||||
EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_derive", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
|
||||
fido_log_debug("%s: kdf", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (pk_evp != NULL)
|
||||
EVP_PKEY_free(pk_evp);
|
||||
if (sk_evp != NULL)
|
||||
EVP_PKEY_free(sk_evp);
|
||||
if (ctx != NULL)
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
if (ok < 0)
|
||||
fido_blob_free(ecdh);
|
||||
|
||||
fido_blob_free(&secret);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh, int *ms)
|
||||
{
|
||||
es256_sk_t *sk = NULL; /* our private key */
|
||||
es256_pk_t *ak = NULL; /* authenticator's public key */
|
||||
int r;
|
||||
|
||||
*pk = NULL;
|
||||
*ecdh = NULL;
|
||||
if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
|
||||
fido_log_debug("%s: es256_derive_pk", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((ak = es256_pk_new()) == NULL ||
|
||||
fido_dev_authkey(dev, ak, ms) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_authkey", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (do_ecdh(dev, sk, ak, ecdh) < 0) {
|
||||
fido_log_debug("%s: do_ecdh", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
es256_sk_free(&sk);
|
||||
es256_pk_free(&ak);
|
||||
|
||||
if (r != FIDO_OK) {
|
||||
es256_pk_free(pk);
|
||||
fido_blob_free(ecdh);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
232
src/eddsa.c
Normal file
232
src/eddsa.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 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/bn.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/eddsa.h"
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3070000f
|
||||
EVP_PKEY *
|
||||
EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
|
||||
size_t keylen)
|
||||
{
|
||||
(void)type;
|
||||
(void)e;
|
||||
(void)key;
|
||||
(void)keylen;
|
||||
|
||||
fido_log_debug("%s: unimplemented", __func__);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
|
||||
size_t *len)
|
||||
{
|
||||
(void)pkey;
|
||||
(void)pub;
|
||||
(void)len;
|
||||
|
||||
fido_log_debug("%s: unimplemented", __func__);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* LIBRESSL_VERSION_NUMBER */
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3040000f
|
||||
int
|
||||
EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
|
||||
const unsigned char *tbs, size_t tbslen)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)sigret;
|
||||
(void)siglen;
|
||||
(void)tbs;
|
||||
(void)tbslen;
|
||||
|
||||
fido_log_debug("%s: unimplemented", __func__);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* LIBRESSL_VERSION_NUMBER < 0x3040000f */
|
||||
|
||||
static int
|
||||
decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
|
||||
{
|
||||
if (cbor_isa_bytestring(item) == false ||
|
||||
cbor_bytestring_is_definite(item) == false ||
|
||||
cbor_bytestring_length(item) != xy_len) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(xy, cbor_bytestring_handle(item), xy_len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
eddsa_pk_t *k = arg;
|
||||
|
||||
if (cbor_isa_negint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8)
|
||||
return (0); /* ignore */
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* x coordinate */
|
||||
return (decode_coord(val, &k->x, sizeof(k->x)));
|
||||
}
|
||||
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
int
|
||||
eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k)
|
||||
{
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false ||
|
||||
cbor_map_iter(item, k, decode_pubkey_point) < 0) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
eddsa_pk_t *
|
||||
eddsa_pk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(eddsa_pk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
eddsa_pk_free(eddsa_pk_t **pkp)
|
||||
{
|
||||
eddsa_pk_t *pk;
|
||||
|
||||
if (pkp == NULL || (pk = *pkp) == NULL)
|
||||
return;
|
||||
|
||||
freezero(pk, sizeof(*pk));
|
||||
*pkp = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
|
||||
if (len < sizeof(*pk))
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
memcpy(pk, ptr, sizeof(*pk));
|
||||
|
||||
if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
|
||||
fido_log_debug("%s: eddsa_pk_to_EVP_PKEY", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
EVP_PKEY *
|
||||
eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k)
|
||||
{
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x,
|
||||
sizeof(k->x))) == NULL)
|
||||
fido_log_debug("%s: EVP_PKEY_new_raw_public_key", __func__);
|
||||
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int
|
||||
eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
|
||||
len != sizeof(pk->x))
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 ||
|
||||
len != sizeof(pk->x))
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
eddsa_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519) {
|
||||
fido_log_debug("%s: EVP_PKEY_base_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* EVP_DigestVerify needs ints */
|
||||
if (dgst->len > INT_MAX || sig->len > INT_MAX) {
|
||||
fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
|
||||
dgst->len, sig->len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((mdctx = EVP_MD_CTX_new()) == NULL) {
|
||||
fido_log_debug("%s: EVP_MD_CTX_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
|
||||
fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
|
||||
dgst->len) != 1) {
|
||||
fido_log_debug("%s: EVP_DigestVerify", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
eddsa_pk_verify_sig(const fido_blob_t *dgst, const eddsa_pk_t *pk,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
int ok = -1;
|
||||
|
||||
if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL ||
|
||||
eddsa_verify_sig(dgst, pkey, sig) < 0) {
|
||||
fido_log_debug("%s: eddsa_verify_sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
137
src/err.c
Normal file
137
src/err.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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/err.h"
|
||||
|
||||
const char *
|
||||
fido_strerr(int n)
|
||||
{
|
||||
switch (n) {
|
||||
case FIDO_ERR_SUCCESS:
|
||||
return "FIDO_ERR_SUCCESS";
|
||||
case FIDO_ERR_INVALID_COMMAND:
|
||||
return "FIDO_ERR_INVALID_COMMAND";
|
||||
case FIDO_ERR_INVALID_PARAMETER:
|
||||
return "FIDO_ERR_INVALID_PARAMETER";
|
||||
case FIDO_ERR_INVALID_LENGTH:
|
||||
return "FIDO_ERR_INVALID_LENGTH";
|
||||
case FIDO_ERR_INVALID_SEQ:
|
||||
return "FIDO_ERR_INVALID_SEQ";
|
||||
case FIDO_ERR_TIMEOUT:
|
||||
return "FIDO_ERR_TIMEOUT";
|
||||
case FIDO_ERR_CHANNEL_BUSY:
|
||||
return "FIDO_ERR_CHANNEL_BUSY";
|
||||
case FIDO_ERR_LOCK_REQUIRED:
|
||||
return "FIDO_ERR_LOCK_REQUIRED";
|
||||
case FIDO_ERR_INVALID_CHANNEL:
|
||||
return "FIDO_ERR_INVALID_CHANNEL";
|
||||
case FIDO_ERR_CBOR_UNEXPECTED_TYPE:
|
||||
return "FIDO_ERR_CBOR_UNEXPECTED_TYPE";
|
||||
case FIDO_ERR_INVALID_CBOR:
|
||||
return "FIDO_ERR_INVALID_CBOR";
|
||||
case FIDO_ERR_MISSING_PARAMETER:
|
||||
return "FIDO_ERR_MISSING_PARAMETER";
|
||||
case FIDO_ERR_LIMIT_EXCEEDED:
|
||||
return "FIDO_ERR_LIMIT_EXCEEDED";
|
||||
case FIDO_ERR_UNSUPPORTED_EXTENSION:
|
||||
return "FIDO_ERR_UNSUPPORTED_EXTENSION";
|
||||
case FIDO_ERR_FP_DATABASE_FULL:
|
||||
return "FIDO_ERR_FP_DATABASE_FULL";
|
||||
case FIDO_ERR_LARGEBLOB_STORAGE_FULL:
|
||||
return "FIDO_ERR_LARGEBLOB_STORAGE_FULL";
|
||||
case FIDO_ERR_CREDENTIAL_EXCLUDED:
|
||||
return "FIDO_ERR_CREDENTIAL_EXCLUDED";
|
||||
case FIDO_ERR_PROCESSING:
|
||||
return "FIDO_ERR_PROCESSING";
|
||||
case FIDO_ERR_INVALID_CREDENTIAL:
|
||||
return "FIDO_ERR_INVALID_CREDENTIAL";
|
||||
case FIDO_ERR_USER_ACTION_PENDING:
|
||||
return "FIDO_ERR_USER_ACTION_PENDING";
|
||||
case FIDO_ERR_OPERATION_PENDING:
|
||||
return "FIDO_ERR_OPERATION_PENDING";
|
||||
case FIDO_ERR_NO_OPERATIONS:
|
||||
return "FIDO_ERR_NO_OPERATIONS";
|
||||
case FIDO_ERR_UNSUPPORTED_ALGORITHM:
|
||||
return "FIDO_ERR_UNSUPPORTED_ALGORITHM";
|
||||
case FIDO_ERR_OPERATION_DENIED:
|
||||
return "FIDO_ERR_OPERATION_DENIED";
|
||||
case FIDO_ERR_KEY_STORE_FULL:
|
||||
return "FIDO_ERR_KEY_STORE_FULL";
|
||||
case FIDO_ERR_NOT_BUSY:
|
||||
return "FIDO_ERR_NOT_BUSY";
|
||||
case FIDO_ERR_NO_OPERATION_PENDING:
|
||||
return "FIDO_ERR_NO_OPERATION_PENDING";
|
||||
case FIDO_ERR_UNSUPPORTED_OPTION:
|
||||
return "FIDO_ERR_UNSUPPORTED_OPTION";
|
||||
case FIDO_ERR_INVALID_OPTION:
|
||||
return "FIDO_ERR_INVALID_OPTION";
|
||||
case FIDO_ERR_KEEPALIVE_CANCEL:
|
||||
return "FIDO_ERR_KEEPALIVE_CANCEL";
|
||||
case FIDO_ERR_NO_CREDENTIALS:
|
||||
return "FIDO_ERR_NO_CREDENTIALS";
|
||||
case FIDO_ERR_USER_ACTION_TIMEOUT:
|
||||
return "FIDO_ERR_USER_ACTION_TIMEOUT";
|
||||
case FIDO_ERR_NOT_ALLOWED:
|
||||
return "FIDO_ERR_NOT_ALLOWED";
|
||||
case FIDO_ERR_PIN_INVALID:
|
||||
return "FIDO_ERR_PIN_INVALID";
|
||||
case FIDO_ERR_PIN_BLOCKED:
|
||||
return "FIDO_ERR_PIN_BLOCKED";
|
||||
case FIDO_ERR_PIN_AUTH_INVALID:
|
||||
return "FIDO_ERR_PIN_AUTH_INVALID";
|
||||
case FIDO_ERR_PIN_AUTH_BLOCKED:
|
||||
return "FIDO_ERR_PIN_AUTH_BLOCKED";
|
||||
case FIDO_ERR_PIN_NOT_SET:
|
||||
return "FIDO_ERR_PIN_NOT_SET";
|
||||
case FIDO_ERR_PIN_REQUIRED:
|
||||
return "FIDO_ERR_PIN_REQUIRED";
|
||||
case FIDO_ERR_PIN_POLICY_VIOLATION:
|
||||
return "FIDO_ERR_PIN_POLICY_VIOLATION";
|
||||
case FIDO_ERR_PIN_TOKEN_EXPIRED:
|
||||
return "FIDO_ERR_PIN_TOKEN_EXPIRED";
|
||||
case FIDO_ERR_REQUEST_TOO_LARGE:
|
||||
return "FIDO_ERR_REQUEST_TOO_LARGE";
|
||||
case FIDO_ERR_ACTION_TIMEOUT:
|
||||
return "FIDO_ERR_ACTION_TIMEOUT";
|
||||
case FIDO_ERR_UP_REQUIRED:
|
||||
return "FIDO_ERR_UP_REQUIRED";
|
||||
case FIDO_ERR_UV_BLOCKED:
|
||||
return "FIDO_ERR_UV_BLOCKED";
|
||||
case FIDO_ERR_UV_INVALID:
|
||||
return "FIDO_ERR_UV_INVALID";
|
||||
case FIDO_ERR_UNAUTHORIZED_PERM:
|
||||
return "FIDO_ERR_UNAUTHORIZED_PERM";
|
||||
case FIDO_ERR_ERR_OTHER:
|
||||
return "FIDO_ERR_ERR_OTHER";
|
||||
case FIDO_ERR_SPEC_LAST:
|
||||
return "FIDO_ERR_SPEC_LAST";
|
||||
case FIDO_ERR_TX:
|
||||
return "FIDO_ERR_TX";
|
||||
case FIDO_ERR_RX:
|
||||
return "FIDO_ERR_RX";
|
||||
case FIDO_ERR_RX_NOT_CBOR:
|
||||
return "FIDO_ERR_RX_NOT_CBOR";
|
||||
case FIDO_ERR_RX_INVALID_CBOR:
|
||||
return "FIDO_ERR_RX_INVALID_CBOR";
|
||||
case FIDO_ERR_INVALID_PARAM:
|
||||
return "FIDO_ERR_INVALID_PARAM";
|
||||
case FIDO_ERR_INVALID_SIG:
|
||||
return "FIDO_ERR_INVALID_SIG";
|
||||
case FIDO_ERR_INVALID_ARGUMENT:
|
||||
return "FIDO_ERR_INVALID_ARGUMENT";
|
||||
case FIDO_ERR_USER_PRESENCE_REQUIRED:
|
||||
return "FIDO_ERR_USER_PRESENCE_REQUIRED";
|
||||
case FIDO_ERR_NOTFOUND:
|
||||
return "FIDO_ERR_NOTFOUND";
|
||||
case FIDO_ERR_COMPRESS:
|
||||
return "FIDO_ERR_COMPRESS";
|
||||
case FIDO_ERR_INTERNAL:
|
||||
return "FIDO_ERR_INTERNAL";
|
||||
default:
|
||||
return "FIDO_ERR_UNKNOWN";
|
||||
}
|
||||
}
|
||||
541
src/es256.c
Normal file
541
src/es256.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* 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 <openssl/bn.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000
|
||||
#define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x))
|
||||
#else
|
||||
#define get0_EC_KEY(x) EVP_PKEY_get0((x))
|
||||
#endif
|
||||
|
||||
static const int es256_nid = NID_X9_62_prime256v1;
|
||||
|
||||
static int
|
||||
decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
|
||||
{
|
||||
if (cbor_isa_bytestring(item) == false ||
|
||||
cbor_bytestring_is_definite(item) == false ||
|
||||
cbor_bytestring_length(item) != xy_len) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(xy, cbor_bytestring_handle(item), xy_len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
es256_pk_t *k = arg;
|
||||
|
||||
if (cbor_isa_negint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8)
|
||||
return (0); /* ignore */
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* x coordinate */
|
||||
return (decode_coord(val, &k->x, sizeof(k->x)));
|
||||
case 2: /* y coordinate */
|
||||
return (decode_coord(val, &k->y, sizeof(k->y)));
|
||||
}
|
||||
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_decode(const cbor_item_t *item, es256_pk_t *k)
|
||||
{
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false ||
|
||||
cbor_map_iter(item, k, decode_pubkey_point) < 0) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
cbor_item_t *
|
||||
es256_pk_encode(const es256_pk_t *pk, int ecdh)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
struct cbor_pair argv[5];
|
||||
int alg;
|
||||
int ok = -1;
|
||||
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if ((item = cbor_new_definite_map(5)) == NULL)
|
||||
goto fail;
|
||||
|
||||
/* kty */
|
||||
if ((argv[0].key = cbor_build_uint8(1)) == NULL ||
|
||||
(argv[0].value = cbor_build_uint8(2)) == NULL ||
|
||||
!cbor_map_add(item, argv[0]))
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* "The COSEAlgorithmIdentifier used is -25 (ECDH-ES +
|
||||
* HKDF-256) although this is NOT the algorithm actually
|
||||
* used. Setting this to a different value may result in
|
||||
* compatibility issues."
|
||||
*/
|
||||
if (ecdh)
|
||||
alg = COSE_ECDH_ES256;
|
||||
else
|
||||
alg = COSE_ES256;
|
||||
|
||||
/* alg */
|
||||
if ((argv[1].key = cbor_build_uint8(3)) == NULL ||
|
||||
(argv[1].value = cbor_build_negint8((uint8_t)(-alg - 1))) == NULL ||
|
||||
!cbor_map_add(item, argv[1]))
|
||||
goto fail;
|
||||
|
||||
/* crv */
|
||||
if ((argv[2].key = cbor_build_negint8(0)) == NULL ||
|
||||
(argv[2].value = cbor_build_uint8(1)) == NULL ||
|
||||
!cbor_map_add(item, argv[2]))
|
||||
goto fail;
|
||||
|
||||
/* x */
|
||||
if ((argv[3].key = cbor_build_negint8(1)) == NULL ||
|
||||
(argv[3].value = cbor_build_bytestring(pk->x,
|
||||
sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3]))
|
||||
goto fail;
|
||||
|
||||
/* y */
|
||||
if ((argv[4].key = cbor_build_negint8(2)) == NULL ||
|
||||
(argv[4].value = cbor_build_bytestring(pk->y,
|
||||
sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4]))
|
||||
goto fail;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ok < 0) {
|
||||
if (item != NULL) {
|
||||
cbor_decref(&item);
|
||||
item = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
if (argv[i].key)
|
||||
cbor_decref(&argv[i].key);
|
||||
if (argv[i].value)
|
||||
cbor_decref(&argv[i].value);
|
||||
}
|
||||
|
||||
return (item);
|
||||
}
|
||||
|
||||
es256_sk_t *
|
||||
es256_sk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(es256_sk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
es256_sk_free(es256_sk_t **skp)
|
||||
{
|
||||
es256_sk_t *sk;
|
||||
|
||||
if (skp == NULL || (sk = *skp) == NULL)
|
||||
return;
|
||||
|
||||
freezero(sk, sizeof(*sk));
|
||||
*skp = NULL;
|
||||
}
|
||||
|
||||
es256_pk_t *
|
||||
es256_pk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(es256_pk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
es256_pk_free(es256_pk_t **pkp)
|
||||
{
|
||||
es256_pk_t *pk;
|
||||
|
||||
if (pkp == NULL || (pk = *pkp) == NULL)
|
||||
return;
|
||||
|
||||
freezero(pk, sizeof(*pk));
|
||||
*pkp = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
EVP_PKEY *pkey;
|
||||
|
||||
if (len < sizeof(*pk))
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if (len == sizeof(*pk) + 1 && *p == 0x04)
|
||||
memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */
|
||||
else
|
||||
memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */
|
||||
|
||||
if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
|
||||
fido_log_debug("%s: es256_pk_to_EVP_PKEY", __func__);
|
||||
explicit_bzero(pk, sizeof(*pk));
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_set_x(es256_pk_t *pk, const unsigned char *x)
|
||||
{
|
||||
memcpy(pk->x, x, sizeof(pk->x));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_set_y(es256_pk_t *pk, const unsigned char *y)
|
||||
{
|
||||
memcpy(pk->y, y, sizeof(pk->y));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
es256_sk_create(es256_sk_t *key)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY_CTX *kctx = NULL;
|
||||
EVP_PKEY *p = NULL;
|
||||
EVP_PKEY *k = NULL;
|
||||
const EC_KEY *ec;
|
||||
const BIGNUM *d;
|
||||
int n;
|
||||
int ok = -1;
|
||||
|
||||
if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL ||
|
||||
EVP_PKEY_paramgen_init(pctx) <= 0 ||
|
||||
EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, es256_nid) <= 0 ||
|
||||
EVP_PKEY_paramgen(pctx, &p) <= 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_paramgen", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL ||
|
||||
EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_keygen", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL ||
|
||||
(d = EC_KEY_get0_private_key(ec)) == NULL ||
|
||||
(n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) ||
|
||||
(n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) {
|
||||
fido_log_debug("%s: EC_KEY_get0_private_key", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (p != NULL)
|
||||
EVP_PKEY_free(p);
|
||||
if (k != NULL)
|
||||
EVP_PKEY_free(k);
|
||||
if (pctx != NULL)
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
if (kctx != NULL)
|
||||
EVP_PKEY_CTX_free(kctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
EVP_PKEY *
|
||||
es256_pk_to_EVP_PKEY(const es256_pk_t *k)
|
||||
{
|
||||
BN_CTX *bnctx = NULL;
|
||||
EC_KEY *ec = NULL;
|
||||
EC_POINT *q = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
BIGNUM *x = NULL;
|
||||
BIGNUM *y = NULL;
|
||||
const EC_GROUP *g = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((bnctx = BN_CTX_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
BN_CTX_start(bnctx);
|
||||
|
||||
if ((x = BN_CTX_get(bnctx)) == NULL ||
|
||||
(y = BN_CTX_get(bnctx)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
|
||||
BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
|
||||
fido_log_debug("%s: BN_bin2bn", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
|
||||
(g = EC_KEY_get0_group(ec)) == NULL) {
|
||||
fido_log_debug("%s: EC_KEY init", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((q = EC_POINT_new(g)) == NULL ||
|
||||
EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
|
||||
EC_KEY_set_public_key(ec, q) == 0) {
|
||||
fido_log_debug("%s: EC_KEY_set_public_key", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pkey = EVP_PKEY_new()) == NULL ||
|
||||
EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ec = NULL; /* at this point, ec belongs to evp */
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (bnctx != NULL) {
|
||||
BN_CTX_end(bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
}
|
||||
|
||||
if (ec != NULL)
|
||||
EC_KEY_free(ec);
|
||||
if (q != NULL)
|
||||
EC_POINT_free(q);
|
||||
|
||||
if (ok < 0 && pkey != NULL) {
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
}
|
||||
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
|
||||
{
|
||||
BN_CTX *bnctx = NULL;
|
||||
BIGNUM *x = NULL;
|
||||
BIGNUM *y = NULL;
|
||||
const EC_POINT *q = NULL;
|
||||
EC_GROUP *g = NULL;
|
||||
size_t dx;
|
||||
size_t dy;
|
||||
int ok = FIDO_ERR_INTERNAL;
|
||||
int nx;
|
||||
int ny;
|
||||
|
||||
if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
|
||||
(g = EC_GROUP_new_by_curve_name(es256_nid)) == NULL ||
|
||||
(bnctx = BN_CTX_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
BN_CTX_start(bnctx);
|
||||
|
||||
if ((x = BN_CTX_get(bnctx)) == NULL ||
|
||||
(y = BN_CTX_get(bnctx)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (EC_POINT_is_on_curve(g, q, bnctx) != 1) {
|
||||
fido_log_debug("%s: EC_POINT_is_on_curve", __func__);
|
||||
ok = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
|
||||
(nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) ||
|
||||
(ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) {
|
||||
fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dx = sizeof(pk->x) - (size_t)nx;
|
||||
dy = sizeof(pk->y) - (size_t)ny;
|
||||
|
||||
if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) ||
|
||||
(ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) {
|
||||
fido_log_debug("%s: BN_bn2bin", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = FIDO_OK;
|
||||
fail:
|
||||
EC_GROUP_free(g);
|
||||
|
||||
if (bnctx != NULL) {
|
||||
BN_CTX_end(bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_from_EVP_PKEY(es256_pk_t *pk, const EVP_PKEY *pkey)
|
||||
{
|
||||
const EC_KEY *ec;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC ||
|
||||
(ec = get0_EC_KEY(pkey)) == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (es256_pk_from_EC_KEY(pk, ec));
|
||||
}
|
||||
|
||||
EVP_PKEY *
|
||||
es256_sk_to_EVP_PKEY(const es256_sk_t *k)
|
||||
{
|
||||
BN_CTX *bnctx = NULL;
|
||||
EC_KEY *ec = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
BIGNUM *d = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((bnctx = BN_CTX_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
BN_CTX_start(bnctx);
|
||||
|
||||
if ((d = BN_CTX_get(bnctx)) == NULL ||
|
||||
BN_bin2bn(k->d, sizeof(k->d), d) == NULL) {
|
||||
fido_log_debug("%s: BN_bin2bn", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
|
||||
EC_KEY_set_private_key(ec, d) == 0) {
|
||||
fido_log_debug("%s: EC_KEY_set_private_key", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pkey = EVP_PKEY_new()) == NULL ||
|
||||
EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ec = NULL; /* at this point, ec belongs to evp */
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (bnctx != NULL) {
|
||||
BN_CTX_end(bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
}
|
||||
|
||||
if (ec != NULL)
|
||||
EC_KEY_free(ec);
|
||||
|
||||
if (ok < 0 && pkey != NULL) {
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
}
|
||||
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int
|
||||
es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk)
|
||||
{
|
||||
BIGNUM *d = NULL;
|
||||
EC_KEY *ec = NULL;
|
||||
EC_POINT *q = NULL;
|
||||
const EC_GROUP *g = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL ||
|
||||
(ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
|
||||
(g = EC_KEY_get0_group(ec)) == NULL ||
|
||||
(q = EC_POINT_new(g)) == NULL) {
|
||||
fido_log_debug("%s: get", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 ||
|
||||
EC_KEY_set_public_key(ec, q) == 0 ||
|
||||
es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) {
|
||||
fido_log_debug("%s: set", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (d != NULL)
|
||||
BN_clear_free(d);
|
||||
if (q != NULL)
|
||||
EC_POINT_free(q);
|
||||
if (ec != NULL)
|
||||
EC_KEY_free(ec);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
es256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
|
||||
fido_log_debug("%s: EVP_PKEY_base_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
|
||||
EVP_PKEY_verify_init(pctx) != 1 ||
|
||||
EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
|
||||
dgst->len) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_verify", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
es256_pk_verify_sig(const fido_blob_t *dgst, const es256_pk_t *pk,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
int ok = -1;
|
||||
|
||||
if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
|
||||
es256_verify_sig(dgst, pkey, sig) < 0) {
|
||||
fido_log_debug("%s: es256_verify_sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
296
src/es384.c
Normal file
296
src/es384.c
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (c) 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 <openssl/bn.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/es384.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000
|
||||
#define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x))
|
||||
#else
|
||||
#define get0_EC_KEY(x) EVP_PKEY_get0((x))
|
||||
#endif
|
||||
|
||||
static int
|
||||
decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
|
||||
{
|
||||
if (cbor_isa_bytestring(item) == false ||
|
||||
cbor_bytestring_is_definite(item) == false ||
|
||||
cbor_bytestring_length(item) != xy_len) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(xy, cbor_bytestring_handle(item), xy_len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
es384_pk_t *k = arg;
|
||||
|
||||
if (cbor_isa_negint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8)
|
||||
return (0); /* ignore */
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* x coordinate */
|
||||
return (decode_coord(val, &k->x, sizeof(k->x)));
|
||||
case 2: /* y coordinate */
|
||||
return (decode_coord(val, &k->y, sizeof(k->y)));
|
||||
}
|
||||
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
int
|
||||
es384_pk_decode(const cbor_item_t *item, es384_pk_t *k)
|
||||
{
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false ||
|
||||
cbor_map_iter(item, k, decode_pubkey_point) < 0) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
es384_pk_t *
|
||||
es384_pk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(es384_pk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
es384_pk_free(es384_pk_t **pkp)
|
||||
{
|
||||
es384_pk_t *pk;
|
||||
|
||||
if (pkp == NULL || (pk = *pkp) == NULL)
|
||||
return;
|
||||
|
||||
freezero(pk, sizeof(*pk));
|
||||
*pkp = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
es384_pk_from_ptr(es384_pk_t *pk, const void *ptr, size_t len)
|
||||
{
|
||||
const uint8_t *p = ptr;
|
||||
EVP_PKEY *pkey;
|
||||
|
||||
if (len < sizeof(*pk))
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if (len == sizeof(*pk) + 1 && *p == 0x04)
|
||||
memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */
|
||||
else
|
||||
memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */
|
||||
|
||||
if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL) {
|
||||
fido_log_debug("%s: es384_pk_to_EVP_PKEY", __func__);
|
||||
explicit_bzero(pk, sizeof(*pk));
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
EVP_PKEY *
|
||||
es384_pk_to_EVP_PKEY(const es384_pk_t *k)
|
||||
{
|
||||
BN_CTX *bnctx = NULL;
|
||||
EC_KEY *ec = NULL;
|
||||
EC_POINT *q = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
BIGNUM *x = NULL;
|
||||
BIGNUM *y = NULL;
|
||||
const EC_GROUP *g = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((bnctx = BN_CTX_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
BN_CTX_start(bnctx);
|
||||
|
||||
if ((x = BN_CTX_get(bnctx)) == NULL ||
|
||||
(y = BN_CTX_get(bnctx)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
|
||||
BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
|
||||
fido_log_debug("%s: BN_bin2bn", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ec = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL ||
|
||||
(g = EC_KEY_get0_group(ec)) == NULL) {
|
||||
fido_log_debug("%s: EC_KEY init", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((q = EC_POINT_new(g)) == NULL ||
|
||||
EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
|
||||
EC_KEY_set_public_key(ec, q) == 0) {
|
||||
fido_log_debug("%s: EC_KEY_set_public_key", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pkey = EVP_PKEY_new()) == NULL ||
|
||||
EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ec = NULL; /* at this point, ec belongs to evp */
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (bnctx != NULL) {
|
||||
BN_CTX_end(bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
}
|
||||
|
||||
if (ec != NULL)
|
||||
EC_KEY_free(ec);
|
||||
if (q != NULL)
|
||||
EC_POINT_free(q);
|
||||
|
||||
if (ok < 0 && pkey != NULL) {
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
}
|
||||
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int
|
||||
es384_pk_from_EC_KEY(es384_pk_t *pk, const EC_KEY *ec)
|
||||
{
|
||||
BN_CTX *bnctx = NULL;
|
||||
BIGNUM *x = NULL;
|
||||
BIGNUM *y = NULL;
|
||||
const EC_POINT *q = NULL;
|
||||
EC_GROUP *g = NULL;
|
||||
size_t dx;
|
||||
size_t dy;
|
||||
int ok = FIDO_ERR_INTERNAL;
|
||||
int nx;
|
||||
int ny;
|
||||
|
||||
if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
|
||||
(g = EC_GROUP_new_by_curve_name(NID_secp384r1)) == NULL ||
|
||||
(bnctx = BN_CTX_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
BN_CTX_start(bnctx);
|
||||
|
||||
if ((x = BN_CTX_get(bnctx)) == NULL ||
|
||||
(y = BN_CTX_get(bnctx)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (EC_POINT_is_on_curve(g, q, bnctx) != 1) {
|
||||
fido_log_debug("%s: EC_POINT_is_on_curve", __func__);
|
||||
ok = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
|
||||
(nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) ||
|
||||
(ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) {
|
||||
fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dx = sizeof(pk->x) - (size_t)nx;
|
||||
dy = sizeof(pk->y) - (size_t)ny;
|
||||
|
||||
if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) ||
|
||||
(ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) {
|
||||
fido_log_debug("%s: BN_bn2bin", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = FIDO_OK;
|
||||
fail:
|
||||
EC_GROUP_free(g);
|
||||
|
||||
if (bnctx != NULL) {
|
||||
BN_CTX_end(bnctx);
|
||||
BN_CTX_free(bnctx);
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
es384_pk_from_EVP_PKEY(es384_pk_t *pk, const EVP_PKEY *pkey)
|
||||
{
|
||||
const EC_KEY *ec;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC ||
|
||||
(ec = get0_EC_KEY(pkey)) == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (es384_pk_from_EC_KEY(pk, ec));
|
||||
}
|
||||
|
||||
int
|
||||
es384_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
|
||||
fido_log_debug("%s: EVP_PKEY_base_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
|
||||
EVP_PKEY_verify_init(pctx) != 1 ||
|
||||
EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
|
||||
dgst->len) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_verify", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
es384_pk_verify_sig(const fido_blob_t *dgst, const es384_pk_t *pk,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
int ok = -1;
|
||||
|
||||
if ((pkey = es384_pk_to_EVP_PKEY(pk)) == NULL ||
|
||||
es384_verify_sig(dgst, pkey, sig) < 0) {
|
||||
fido_log_debug("%s: es384_verify_sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
272
src/export.gnu
Normal file
272
src/export.gnu
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
global:
|
||||
eddsa_pk_free;
|
||||
eddsa_pk_from_EVP_PKEY;
|
||||
eddsa_pk_from_ptr;
|
||||
eddsa_pk_new;
|
||||
eddsa_pk_to_EVP_PKEY;
|
||||
es256_pk_free;
|
||||
es256_pk_from_EC_KEY;
|
||||
es256_pk_from_EVP_PKEY;
|
||||
es256_pk_from_ptr;
|
||||
es256_pk_new;
|
||||
es256_pk_to_EVP_PKEY;
|
||||
es384_pk_free;
|
||||
es384_pk_from_EC_KEY;
|
||||
es384_pk_from_EVP_PKEY;
|
||||
es384_pk_from_ptr;
|
||||
es384_pk_new;
|
||||
es384_pk_to_EVP_PKEY;
|
||||
fido_assert_allow_cred;
|
||||
fido_assert_authdata_len;
|
||||
fido_assert_authdata_ptr;
|
||||
fido_assert_authdata_raw_len;
|
||||
fido_assert_authdata_raw_ptr;
|
||||
fido_assert_blob_len;
|
||||
fido_assert_blob_ptr;
|
||||
fido_assert_clientdata_hash_len;
|
||||
fido_assert_clientdata_hash_ptr;
|
||||
fido_assert_count;
|
||||
fido_assert_empty_allow_list;
|
||||
fido_assert_flags;
|
||||
fido_assert_free;
|
||||
fido_assert_hmac_secret_len;
|
||||
fido_assert_hmac_secret_ptr;
|
||||
fido_assert_id_len;
|
||||
fido_assert_id_ptr;
|
||||
fido_assert_largeblob_key_len;
|
||||
fido_assert_largeblob_key_ptr;
|
||||
fido_assert_new;
|
||||
fido_assert_rp_id;
|
||||
fido_assert_set_authdata;
|
||||
fido_assert_set_authdata_raw;
|
||||
fido_assert_set_clientdata;
|
||||
fido_assert_set_clientdata_hash;
|
||||
fido_assert_set_count;
|
||||
fido_assert_set_extensions;
|
||||
fido_assert_set_hmac_salt;
|
||||
fido_assert_set_hmac_secret;
|
||||
fido_assert_set_options;
|
||||
fido_assert_set_rp;
|
||||
fido_assert_set_sig;
|
||||
fido_assert_set_up;
|
||||
fido_assert_set_uv;
|
||||
fido_assert_set_winhello_appid;
|
||||
fido_assert_sigcount;
|
||||
fido_assert_sig_len;
|
||||
fido_assert_sig_ptr;
|
||||
fido_assert_user_display_name;
|
||||
fido_assert_user_icon;
|
||||
fido_assert_user_id_len;
|
||||
fido_assert_user_id_ptr;
|
||||
fido_assert_user_name;
|
||||
fido_assert_verify;
|
||||
fido_bio_dev_enroll_begin;
|
||||
fido_bio_dev_enroll_cancel;
|
||||
fido_bio_dev_enroll_continue;
|
||||
fido_bio_dev_enroll_remove;
|
||||
fido_bio_dev_get_info;
|
||||
fido_bio_dev_get_template_array;
|
||||
fido_bio_dev_set_template_name;
|
||||
fido_bio_enroll_free;
|
||||
fido_bio_enroll_last_status;
|
||||
fido_bio_enroll_new;
|
||||
fido_bio_enroll_remaining_samples;
|
||||
fido_bio_info_free;
|
||||
fido_bio_info_max_samples;
|
||||
fido_bio_info_new;
|
||||
fido_bio_info_type;
|
||||
fido_bio_template;
|
||||
fido_bio_template_array_count;
|
||||
fido_bio_template_array_free;
|
||||
fido_bio_template_array_new;
|
||||
fido_bio_template_free;
|
||||
fido_bio_template_id_len;
|
||||
fido_bio_template_id_ptr;
|
||||
fido_bio_template_name;
|
||||
fido_bio_template_new;
|
||||
fido_bio_template_set_id;
|
||||
fido_bio_template_set_name;
|
||||
fido_cbor_info_aaguid_len;
|
||||
fido_cbor_info_aaguid_ptr;
|
||||
fido_cbor_info_algorithm_cose;
|
||||
fido_cbor_info_algorithm_count;
|
||||
fido_cbor_info_algorithm_type;
|
||||
fido_cbor_info_certs_len;
|
||||
fido_cbor_info_certs_name_ptr;
|
||||
fido_cbor_info_certs_value_ptr;
|
||||
fido_cbor_info_extensions_len;
|
||||
fido_cbor_info_extensions_ptr;
|
||||
fido_cbor_info_free;
|
||||
fido_cbor_info_fwversion;
|
||||
fido_cbor_info_maxcredbloblen;
|
||||
fido_cbor_info_maxcredcntlst;
|
||||
fido_cbor_info_maxcredidlen;
|
||||
fido_cbor_info_maxlargeblob;
|
||||
fido_cbor_info_maxmsgsiz;
|
||||
fido_cbor_info_maxrpid_minpinlen;
|
||||
fido_cbor_info_minpinlen;
|
||||
fido_cbor_info_new;
|
||||
fido_cbor_info_new_pin_required;
|
||||
fido_cbor_info_options_len;
|
||||
fido_cbor_info_options_name_ptr;
|
||||
fido_cbor_info_options_value_ptr;
|
||||
fido_cbor_info_protocols_len;
|
||||
fido_cbor_info_protocols_ptr;
|
||||
fido_cbor_info_rk_remaining;
|
||||
fido_cbor_info_transports_len;
|
||||
fido_cbor_info_transports_ptr;
|
||||
fido_cbor_info_uv_attempts;
|
||||
fido_cbor_info_uv_modality;
|
||||
fido_cbor_info_versions_len;
|
||||
fido_cbor_info_versions_ptr;
|
||||
fido_cred_attstmt_len;
|
||||
fido_cred_attstmt_ptr;
|
||||
fido_cred_authdata_len;
|
||||
fido_cred_authdata_ptr;
|
||||
fido_cred_authdata_raw_len;
|
||||
fido_cred_authdata_raw_ptr;
|
||||
fido_cred_clientdata_hash_len;
|
||||
fido_cred_clientdata_hash_ptr;
|
||||
fido_cred_display_name;
|
||||
fido_cred_empty_exclude_list;
|
||||
fido_cred_exclude;
|
||||
fido_cred_flags;
|
||||
fido_cred_largeblob_key_len;
|
||||
fido_cred_largeblob_key_ptr;
|
||||
fido_cred_sigcount;
|
||||
fido_cred_fmt;
|
||||
fido_cred_free;
|
||||
fido_cred_id_len;
|
||||
fido_cred_id_ptr;
|
||||
fido_cred_aaguid_len;
|
||||
fido_cred_aaguid_ptr;
|
||||
fido_credman_del_dev_rk;
|
||||
fido_credman_get_dev_metadata;
|
||||
fido_credman_get_dev_rk;
|
||||
fido_credman_get_dev_rp;
|
||||
fido_credman_metadata_free;
|
||||
fido_credman_metadata_new;
|
||||
fido_credman_rk;
|
||||
fido_credman_rk_count;
|
||||
fido_credman_rk_existing;
|
||||
fido_credman_rk_free;
|
||||
fido_credman_rk_new;
|
||||
fido_credman_rk_remaining;
|
||||
fido_credman_rp_count;
|
||||
fido_credman_rp_free;
|
||||
fido_credman_rp_id;
|
||||
fido_credman_rp_id_hash_len;
|
||||
fido_credman_rp_id_hash_ptr;
|
||||
fido_credman_rp_name;
|
||||
fido_credman_rp_new;
|
||||
fido_credman_set_dev_rk;
|
||||
fido_cred_new;
|
||||
fido_cred_pin_minlen;
|
||||
fido_cred_prot;
|
||||
fido_cred_pubkey_len;
|
||||
fido_cred_pubkey_ptr;
|
||||
fido_cred_rp_id;
|
||||
fido_cred_rp_name;
|
||||
fido_cred_set_attstmt;
|
||||
fido_cred_set_attobj;
|
||||
fido_cred_set_authdata;
|
||||
fido_cred_set_authdata_raw;
|
||||
fido_cred_set_blob;
|
||||
fido_cred_set_clientdata;
|
||||
fido_cred_set_clientdata_hash;
|
||||
fido_cred_set_extensions;
|
||||
fido_cred_set_fmt;
|
||||
fido_cred_set_id;
|
||||
fido_cred_set_options;
|
||||
fido_cred_set_pin_minlen;
|
||||
fido_cred_set_prot;
|
||||
fido_cred_set_rk;
|
||||
fido_cred_set_rp;
|
||||
fido_cred_set_sig;
|
||||
fido_cred_set_type;
|
||||
fido_cred_set_user;
|
||||
fido_cred_set_uv;
|
||||
fido_cred_set_x509;
|
||||
fido_cred_sig_len;
|
||||
fido_cred_sig_ptr;
|
||||
fido_cred_type;
|
||||
fido_cred_user_id_len;
|
||||
fido_cred_user_id_ptr;
|
||||
fido_cred_user_name;
|
||||
fido_cred_verify;
|
||||
fido_cred_verify_self;
|
||||
fido_cred_x5c_len;
|
||||
fido_cred_x5c_list_count;
|
||||
fido_cred_x5c_list_len;
|
||||
fido_cred_x5c_list_ptr;
|
||||
fido_cred_x5c_ptr;
|
||||
fido_dev_build;
|
||||
fido_dev_cancel;
|
||||
fido_dev_close;
|
||||
fido_dev_enable_entattest;
|
||||
fido_dev_flags;
|
||||
fido_dev_force_fido2;
|
||||
fido_dev_force_pin_change;
|
||||
fido_dev_force_u2f;
|
||||
fido_dev_free;
|
||||
fido_dev_get_assert;
|
||||
fido_dev_get_cbor_info;
|
||||
fido_dev_get_retry_count;
|
||||
fido_dev_get_uv_retry_count;
|
||||
fido_dev_get_touch_begin;
|
||||
fido_dev_get_touch_status;
|
||||
fido_dev_has_pin;
|
||||
fido_dev_has_uv;
|
||||
fido_dev_info_free;
|
||||
fido_dev_info_manifest;
|
||||
fido_dev_info_manufacturer_string;
|
||||
fido_dev_info_new;
|
||||
fido_dev_info_path;
|
||||
fido_dev_info_product;
|
||||
fido_dev_info_product_string;
|
||||
fido_dev_info_ptr;
|
||||
fido_dev_info_set;
|
||||
fido_dev_info_vendor;
|
||||
fido_dev_io_handle;
|
||||
fido_dev_is_fido2;
|
||||
fido_dev_is_winhello;
|
||||
fido_dev_major;
|
||||
fido_dev_make_cred;
|
||||
fido_dev_minor;
|
||||
fido_dev_new;
|
||||
fido_dev_new_with_info;
|
||||
fido_dev_open;
|
||||
fido_dev_open_with_info;
|
||||
fido_dev_protocol;
|
||||
fido_dev_reset;
|
||||
fido_dev_set_io_functions;
|
||||
fido_dev_set_pin;
|
||||
fido_dev_set_pin_minlen;
|
||||
fido_dev_set_pin_minlen_rpid;
|
||||
fido_dev_set_sigmask;
|
||||
fido_dev_set_timeout;
|
||||
fido_dev_set_transport_functions;
|
||||
fido_dev_supports_cred_prot;
|
||||
fido_dev_supports_credman;
|
||||
fido_dev_supports_permissions;
|
||||
fido_dev_supports_pin;
|
||||
fido_dev_supports_uv;
|
||||
fido_dev_toggle_always_uv;
|
||||
fido_dev_largeblob_get;
|
||||
fido_dev_largeblob_get_array;
|
||||
fido_dev_largeblob_remove;
|
||||
fido_dev_largeblob_set;
|
||||
fido_dev_largeblob_set_array;
|
||||
fido_init;
|
||||
fido_set_log_handler;
|
||||
fido_strerr;
|
||||
rs256_pk_free;
|
||||
rs256_pk_from_ptr;
|
||||
rs256_pk_from_EVP_PKEY;
|
||||
rs256_pk_from_RSA;
|
||||
rs256_pk_new;
|
||||
rs256_pk_to_EVP_PKEY;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
267
src/export.llvm
Normal file
267
src/export.llvm
Normal file
@@ -0,0 +1,267 @@
|
||||
_eddsa_pk_free
|
||||
_eddsa_pk_from_EVP_PKEY
|
||||
_eddsa_pk_from_ptr
|
||||
_eddsa_pk_new
|
||||
_eddsa_pk_to_EVP_PKEY
|
||||
_es256_pk_free
|
||||
_es256_pk_from_EC_KEY
|
||||
_es256_pk_from_EVP_PKEY
|
||||
_es256_pk_from_ptr
|
||||
_es256_pk_new
|
||||
_es256_pk_to_EVP_PKEY
|
||||
_es384_pk_free
|
||||
_es384_pk_from_EC_KEY
|
||||
_es384_pk_from_EVP_PKEY
|
||||
_es384_pk_from_ptr
|
||||
_es384_pk_new
|
||||
_es384_pk_to_EVP_PKEY
|
||||
_fido_assert_allow_cred
|
||||
_fido_assert_authdata_len
|
||||
_fido_assert_authdata_ptr
|
||||
_fido_assert_authdata_raw_len
|
||||
_fido_assert_authdata_raw_ptr
|
||||
_fido_assert_blob_len
|
||||
_fido_assert_blob_ptr
|
||||
_fido_assert_clientdata_hash_len
|
||||
_fido_assert_clientdata_hash_ptr
|
||||
_fido_assert_count
|
||||
_fido_assert_empty_allow_list
|
||||
_fido_assert_flags
|
||||
_fido_assert_free
|
||||
_fido_assert_hmac_secret_len
|
||||
_fido_assert_hmac_secret_ptr
|
||||
_fido_assert_id_len
|
||||
_fido_assert_id_ptr
|
||||
_fido_assert_largeblob_key_len
|
||||
_fido_assert_largeblob_key_ptr
|
||||
_fido_assert_new
|
||||
_fido_assert_rp_id
|
||||
_fido_assert_set_authdata
|
||||
_fido_assert_set_authdata_raw
|
||||
_fido_assert_set_clientdata
|
||||
_fido_assert_set_clientdata_hash
|
||||
_fido_assert_set_count
|
||||
_fido_assert_set_extensions
|
||||
_fido_assert_set_hmac_salt
|
||||
_fido_assert_set_hmac_secret
|
||||
_fido_assert_set_options
|
||||
_fido_assert_set_rp
|
||||
_fido_assert_set_sig
|
||||
_fido_assert_set_up
|
||||
_fido_assert_set_uv
|
||||
_fido_assert_set_winhello_appid
|
||||
_fido_assert_sigcount
|
||||
_fido_assert_sig_len
|
||||
_fido_assert_sig_ptr
|
||||
_fido_assert_user_display_name
|
||||
_fido_assert_user_icon
|
||||
_fido_assert_user_id_len
|
||||
_fido_assert_user_id_ptr
|
||||
_fido_assert_user_name
|
||||
_fido_assert_verify
|
||||
_fido_bio_dev_enroll_begin
|
||||
_fido_bio_dev_enroll_cancel
|
||||
_fido_bio_dev_enroll_continue
|
||||
_fido_bio_dev_enroll_remove
|
||||
_fido_bio_dev_get_info
|
||||
_fido_bio_dev_get_template_array
|
||||
_fido_bio_dev_set_template_name
|
||||
_fido_bio_enroll_free
|
||||
_fido_bio_enroll_last_status
|
||||
_fido_bio_enroll_new
|
||||
_fido_bio_enroll_remaining_samples
|
||||
_fido_bio_info_free
|
||||
_fido_bio_info_max_samples
|
||||
_fido_bio_info_new
|
||||
_fido_bio_info_type
|
||||
_fido_bio_template
|
||||
_fido_bio_template_array_count
|
||||
_fido_bio_template_array_free
|
||||
_fido_bio_template_array_new
|
||||
_fido_bio_template_free
|
||||
_fido_bio_template_id_len
|
||||
_fido_bio_template_id_ptr
|
||||
_fido_bio_template_name
|
||||
_fido_bio_template_new
|
||||
_fido_bio_template_set_id
|
||||
_fido_bio_template_set_name
|
||||
_fido_cbor_info_aaguid_len
|
||||
_fido_cbor_info_aaguid_ptr
|
||||
_fido_cbor_info_algorithm_cose
|
||||
_fido_cbor_info_algorithm_count
|
||||
_fido_cbor_info_algorithm_type
|
||||
_fido_cbor_info_certs_len
|
||||
_fido_cbor_info_certs_name_ptr
|
||||
_fido_cbor_info_certs_value_ptr
|
||||
_fido_cbor_info_extensions_len
|
||||
_fido_cbor_info_extensions_ptr
|
||||
_fido_cbor_info_free
|
||||
_fido_cbor_info_fwversion
|
||||
_fido_cbor_info_maxcredbloblen
|
||||
_fido_cbor_info_maxcredcntlst
|
||||
_fido_cbor_info_maxcredidlen
|
||||
_fido_cbor_info_maxlargeblob
|
||||
_fido_cbor_info_maxmsgsiz
|
||||
_fido_cbor_info_maxrpid_minpinlen
|
||||
_fido_cbor_info_minpinlen
|
||||
_fido_cbor_info_new
|
||||
_fido_cbor_info_new_pin_required
|
||||
_fido_cbor_info_options_len
|
||||
_fido_cbor_info_options_name_ptr
|
||||
_fido_cbor_info_options_value_ptr
|
||||
_fido_cbor_info_protocols_len
|
||||
_fido_cbor_info_protocols_ptr
|
||||
_fido_cbor_info_rk_remaining
|
||||
_fido_cbor_info_transports_len
|
||||
_fido_cbor_info_transports_ptr
|
||||
_fido_cbor_info_uv_attempts
|
||||
_fido_cbor_info_uv_modality
|
||||
_fido_cbor_info_versions_len
|
||||
_fido_cbor_info_versions_ptr
|
||||
_fido_cred_attstmt_len
|
||||
_fido_cred_attstmt_ptr
|
||||
_fido_cred_authdata_len
|
||||
_fido_cred_authdata_ptr
|
||||
_fido_cred_authdata_raw_len
|
||||
_fido_cred_authdata_raw_ptr
|
||||
_fido_cred_clientdata_hash_len
|
||||
_fido_cred_clientdata_hash_ptr
|
||||
_fido_cred_display_name
|
||||
_fido_cred_empty_exclude_list
|
||||
_fido_cred_exclude
|
||||
_fido_cred_flags
|
||||
_fido_cred_largeblob_key_len
|
||||
_fido_cred_largeblob_key_ptr
|
||||
_fido_cred_sigcount
|
||||
_fido_cred_fmt
|
||||
_fido_cred_free
|
||||
_fido_cred_id_len
|
||||
_fido_cred_id_ptr
|
||||
_fido_cred_aaguid_len
|
||||
_fido_cred_aaguid_ptr
|
||||
_fido_credman_del_dev_rk
|
||||
_fido_credman_get_dev_metadata
|
||||
_fido_credman_get_dev_rk
|
||||
_fido_credman_get_dev_rp
|
||||
_fido_credman_metadata_free
|
||||
_fido_credman_metadata_new
|
||||
_fido_credman_rk
|
||||
_fido_credman_rk_count
|
||||
_fido_credman_rk_existing
|
||||
_fido_credman_rk_free
|
||||
_fido_credman_rk_new
|
||||
_fido_credman_rk_remaining
|
||||
_fido_credman_rp_count
|
||||
_fido_credman_rp_free
|
||||
_fido_credman_rp_id
|
||||
_fido_credman_rp_id_hash_len
|
||||
_fido_credman_rp_id_hash_ptr
|
||||
_fido_credman_rp_name
|
||||
_fido_credman_rp_new
|
||||
_fido_credman_set_dev_rk
|
||||
_fido_cred_new
|
||||
_fido_cred_pin_minlen
|
||||
_fido_cred_prot
|
||||
_fido_cred_pubkey_len
|
||||
_fido_cred_pubkey_ptr
|
||||
_fido_cred_rp_id
|
||||
_fido_cred_rp_name
|
||||
_fido_cred_set_attstmt
|
||||
_fido_cred_set_attobj
|
||||
_fido_cred_set_authdata
|
||||
_fido_cred_set_authdata_raw
|
||||
_fido_cred_set_blob
|
||||
_fido_cred_set_clientdata
|
||||
_fido_cred_set_clientdata_hash
|
||||
_fido_cred_set_extensions
|
||||
_fido_cred_set_fmt
|
||||
_fido_cred_set_id
|
||||
_fido_cred_set_options
|
||||
_fido_cred_set_pin_minlen
|
||||
_fido_cred_set_prot
|
||||
_fido_cred_set_rk
|
||||
_fido_cred_set_rp
|
||||
_fido_cred_set_sig
|
||||
_fido_cred_set_type
|
||||
_fido_cred_set_user
|
||||
_fido_cred_set_uv
|
||||
_fido_cred_set_x509
|
||||
_fido_cred_sig_len
|
||||
_fido_cred_sig_ptr
|
||||
_fido_cred_type
|
||||
_fido_cred_user_id_len
|
||||
_fido_cred_user_id_ptr
|
||||
_fido_cred_user_name
|
||||
_fido_cred_verify
|
||||
_fido_cred_verify_self
|
||||
_fido_cred_x5c_len
|
||||
_fido_cred_x5c_list_count
|
||||
_fido_cred_x5c_list_len
|
||||
_fido_cred_x5c_list_ptr
|
||||
_fido_cred_x5c_ptr
|
||||
_fido_dev_build
|
||||
_fido_dev_cancel
|
||||
_fido_dev_close
|
||||
_fido_dev_enable_entattest
|
||||
_fido_dev_flags
|
||||
_fido_dev_force_fido2
|
||||
_fido_dev_force_pin_change
|
||||
_fido_dev_force_u2f
|
||||
_fido_dev_free
|
||||
_fido_dev_get_assert
|
||||
_fido_dev_get_cbor_info
|
||||
_fido_dev_get_retry_count
|
||||
_fido_dev_get_uv_retry_count
|
||||
_fido_dev_get_touch_begin
|
||||
_fido_dev_get_touch_status
|
||||
_fido_dev_has_pin
|
||||
_fido_dev_has_uv
|
||||
_fido_dev_info_free
|
||||
_fido_dev_info_manifest
|
||||
_fido_dev_info_manufacturer_string
|
||||
_fido_dev_info_new
|
||||
_fido_dev_info_path
|
||||
_fido_dev_info_product
|
||||
_fido_dev_info_product_string
|
||||
_fido_dev_info_ptr
|
||||
_fido_dev_info_set
|
||||
_fido_dev_info_vendor
|
||||
_fido_dev_io_handle
|
||||
_fido_dev_is_fido2
|
||||
_fido_dev_is_winhello
|
||||
_fido_dev_major
|
||||
_fido_dev_make_cred
|
||||
_fido_dev_minor
|
||||
_fido_dev_new
|
||||
_fido_dev_new_with_info
|
||||
_fido_dev_open
|
||||
_fido_dev_open_with_info
|
||||
_fido_dev_protocol
|
||||
_fido_dev_reset
|
||||
_fido_dev_set_io_functions
|
||||
_fido_dev_set_pin
|
||||
_fido_dev_set_pin_minlen
|
||||
_fido_dev_set_pin_minlen_rpid
|
||||
_fido_dev_set_sigmask
|
||||
_fido_dev_set_timeout
|
||||
_fido_dev_set_transport_functions
|
||||
_fido_dev_supports_cred_prot
|
||||
_fido_dev_supports_credman
|
||||
_fido_dev_supports_permissions
|
||||
_fido_dev_supports_pin
|
||||
_fido_dev_supports_uv
|
||||
_fido_dev_toggle_always_uv
|
||||
_fido_dev_largeblob_get
|
||||
_fido_dev_largeblob_get_array
|
||||
_fido_dev_largeblob_remove
|
||||
_fido_dev_largeblob_set
|
||||
_fido_dev_largeblob_set_array
|
||||
_fido_init
|
||||
_fido_set_log_handler
|
||||
_fido_strerr
|
||||
_rs256_pk_free
|
||||
_rs256_pk_from_ptr
|
||||
_rs256_pk_from_EVP_PKEY
|
||||
_rs256_pk_from_RSA
|
||||
_rs256_pk_new
|
||||
_rs256_pk_to_EVP_PKEY
|
||||
268
src/export.msvc
Normal file
268
src/export.msvc
Normal file
@@ -0,0 +1,268 @@
|
||||
EXPORTS
|
||||
eddsa_pk_free
|
||||
eddsa_pk_from_EVP_PKEY
|
||||
eddsa_pk_from_ptr
|
||||
eddsa_pk_new
|
||||
eddsa_pk_to_EVP_PKEY
|
||||
es256_pk_free
|
||||
es256_pk_from_EC_KEY
|
||||
es256_pk_from_EVP_PKEY
|
||||
es256_pk_from_ptr
|
||||
es256_pk_new
|
||||
es256_pk_to_EVP_PKEY
|
||||
es384_pk_free
|
||||
es384_pk_from_EC_KEY
|
||||
es384_pk_from_EVP_PKEY
|
||||
es384_pk_from_ptr
|
||||
es384_pk_new
|
||||
es384_pk_to_EVP_PKEY
|
||||
fido_assert_allow_cred
|
||||
fido_assert_authdata_len
|
||||
fido_assert_authdata_ptr
|
||||
fido_assert_authdata_raw_len
|
||||
fido_assert_authdata_raw_ptr
|
||||
fido_assert_blob_len
|
||||
fido_assert_blob_ptr
|
||||
fido_assert_clientdata_hash_len
|
||||
fido_assert_clientdata_hash_ptr
|
||||
fido_assert_count
|
||||
fido_assert_empty_allow_list
|
||||
fido_assert_flags
|
||||
fido_assert_free
|
||||
fido_assert_hmac_secret_len
|
||||
fido_assert_hmac_secret_ptr
|
||||
fido_assert_id_len
|
||||
fido_assert_id_ptr
|
||||
fido_assert_largeblob_key_len
|
||||
fido_assert_largeblob_key_ptr
|
||||
fido_assert_new
|
||||
fido_assert_rp_id
|
||||
fido_assert_set_authdata
|
||||
fido_assert_set_authdata_raw
|
||||
fido_assert_set_clientdata
|
||||
fido_assert_set_clientdata_hash
|
||||
fido_assert_set_count
|
||||
fido_assert_set_extensions
|
||||
fido_assert_set_hmac_salt
|
||||
fido_assert_set_hmac_secret
|
||||
fido_assert_set_options
|
||||
fido_assert_set_rp
|
||||
fido_assert_set_sig
|
||||
fido_assert_set_up
|
||||
fido_assert_set_uv
|
||||
fido_assert_set_winhello_appid
|
||||
fido_assert_sigcount
|
||||
fido_assert_sig_len
|
||||
fido_assert_sig_ptr
|
||||
fido_assert_user_display_name
|
||||
fido_assert_user_icon
|
||||
fido_assert_user_id_len
|
||||
fido_assert_user_id_ptr
|
||||
fido_assert_user_name
|
||||
fido_assert_verify
|
||||
fido_bio_dev_enroll_begin
|
||||
fido_bio_dev_enroll_cancel
|
||||
fido_bio_dev_enroll_continue
|
||||
fido_bio_dev_enroll_remove
|
||||
fido_bio_dev_get_info
|
||||
fido_bio_dev_get_template_array
|
||||
fido_bio_dev_set_template_name
|
||||
fido_bio_enroll_free
|
||||
fido_bio_enroll_last_status
|
||||
fido_bio_enroll_new
|
||||
fido_bio_enroll_remaining_samples
|
||||
fido_bio_info_free
|
||||
fido_bio_info_max_samples
|
||||
fido_bio_info_new
|
||||
fido_bio_info_type
|
||||
fido_bio_template
|
||||
fido_bio_template_array_count
|
||||
fido_bio_template_array_free
|
||||
fido_bio_template_array_new
|
||||
fido_bio_template_free
|
||||
fido_bio_template_id_len
|
||||
fido_bio_template_id_ptr
|
||||
fido_bio_template_name
|
||||
fido_bio_template_new
|
||||
fido_bio_template_set_id
|
||||
fido_bio_template_set_name
|
||||
fido_cbor_info_aaguid_len
|
||||
fido_cbor_info_aaguid_ptr
|
||||
fido_cbor_info_algorithm_cose
|
||||
fido_cbor_info_algorithm_count
|
||||
fido_cbor_info_algorithm_type
|
||||
fido_cbor_info_certs_len
|
||||
fido_cbor_info_certs_name_ptr
|
||||
fido_cbor_info_certs_value_ptr
|
||||
fido_cbor_info_extensions_len
|
||||
fido_cbor_info_extensions_ptr
|
||||
fido_cbor_info_free
|
||||
fido_cbor_info_fwversion
|
||||
fido_cbor_info_maxcredbloblen
|
||||
fido_cbor_info_maxcredcntlst
|
||||
fido_cbor_info_maxcredidlen
|
||||
fido_cbor_info_maxlargeblob
|
||||
fido_cbor_info_maxmsgsiz
|
||||
fido_cbor_info_maxrpid_minpinlen
|
||||
fido_cbor_info_minpinlen
|
||||
fido_cbor_info_new
|
||||
fido_cbor_info_new_pin_required
|
||||
fido_cbor_info_options_len
|
||||
fido_cbor_info_options_name_ptr
|
||||
fido_cbor_info_options_value_ptr
|
||||
fido_cbor_info_protocols_len
|
||||
fido_cbor_info_protocols_ptr
|
||||
fido_cbor_info_rk_remaining
|
||||
fido_cbor_info_transports_len
|
||||
fido_cbor_info_transports_ptr
|
||||
fido_cbor_info_uv_attempts
|
||||
fido_cbor_info_uv_modality
|
||||
fido_cbor_info_versions_len
|
||||
fido_cbor_info_versions_ptr
|
||||
fido_cred_attstmt_len
|
||||
fido_cred_attstmt_ptr
|
||||
fido_cred_authdata_len
|
||||
fido_cred_authdata_ptr
|
||||
fido_cred_authdata_raw_len
|
||||
fido_cred_authdata_raw_ptr
|
||||
fido_cred_clientdata_hash_len
|
||||
fido_cred_clientdata_hash_ptr
|
||||
fido_cred_display_name
|
||||
fido_cred_empty_exclude_list
|
||||
fido_cred_exclude
|
||||
fido_cred_flags
|
||||
fido_cred_largeblob_key_len
|
||||
fido_cred_largeblob_key_ptr
|
||||
fido_cred_sigcount
|
||||
fido_cred_fmt
|
||||
fido_cred_free
|
||||
fido_cred_id_len
|
||||
fido_cred_id_ptr
|
||||
fido_cred_aaguid_len
|
||||
fido_cred_aaguid_ptr
|
||||
fido_credman_del_dev_rk
|
||||
fido_credman_get_dev_metadata
|
||||
fido_credman_get_dev_rk
|
||||
fido_credman_get_dev_rp
|
||||
fido_credman_metadata_free
|
||||
fido_credman_metadata_new
|
||||
fido_credman_rk
|
||||
fido_credman_rk_count
|
||||
fido_credman_rk_existing
|
||||
fido_credman_rk_free
|
||||
fido_credman_rk_new
|
||||
fido_credman_rk_remaining
|
||||
fido_credman_rp_count
|
||||
fido_credman_rp_free
|
||||
fido_credman_rp_id
|
||||
fido_credman_rp_id_hash_len
|
||||
fido_credman_rp_id_hash_ptr
|
||||
fido_credman_rp_name
|
||||
fido_credman_rp_new
|
||||
fido_credman_set_dev_rk
|
||||
fido_cred_new
|
||||
fido_cred_pin_minlen
|
||||
fido_cred_prot
|
||||
fido_cred_pubkey_len
|
||||
fido_cred_pubkey_ptr
|
||||
fido_cred_rp_id
|
||||
fido_cred_rp_name
|
||||
fido_cred_set_attstmt
|
||||
fido_cred_set_attobj
|
||||
fido_cred_set_authdata
|
||||
fido_cred_set_authdata_raw
|
||||
fido_cred_set_blob
|
||||
fido_cred_set_clientdata
|
||||
fido_cred_set_clientdata_hash
|
||||
fido_cred_set_extensions
|
||||
fido_cred_set_fmt
|
||||
fido_cred_set_id
|
||||
fido_cred_set_options
|
||||
fido_cred_set_pin_minlen
|
||||
fido_cred_set_prot
|
||||
fido_cred_set_rk
|
||||
fido_cred_set_rp
|
||||
fido_cred_set_sig
|
||||
fido_cred_set_type
|
||||
fido_cred_set_user
|
||||
fido_cred_set_uv
|
||||
fido_cred_set_x509
|
||||
fido_cred_sig_len
|
||||
fido_cred_sig_ptr
|
||||
fido_cred_type
|
||||
fido_cred_user_id_len
|
||||
fido_cred_user_id_ptr
|
||||
fido_cred_user_name
|
||||
fido_cred_verify
|
||||
fido_cred_verify_self
|
||||
fido_cred_x5c_len
|
||||
fido_cred_x5c_list_count
|
||||
fido_cred_x5c_list_len
|
||||
fido_cred_x5c_list_ptr
|
||||
fido_cred_x5c_ptr
|
||||
fido_dev_build
|
||||
fido_dev_cancel
|
||||
fido_dev_close
|
||||
fido_dev_enable_entattest
|
||||
fido_dev_flags
|
||||
fido_dev_force_fido2
|
||||
fido_dev_force_pin_change
|
||||
fido_dev_force_u2f
|
||||
fido_dev_free
|
||||
fido_dev_get_assert
|
||||
fido_dev_get_cbor_info
|
||||
fido_dev_get_retry_count
|
||||
fido_dev_get_uv_retry_count
|
||||
fido_dev_get_touch_begin
|
||||
fido_dev_get_touch_status
|
||||
fido_dev_has_pin
|
||||
fido_dev_has_uv
|
||||
fido_dev_info_free
|
||||
fido_dev_info_manifest
|
||||
fido_dev_info_manufacturer_string
|
||||
fido_dev_info_new
|
||||
fido_dev_info_path
|
||||
fido_dev_info_product
|
||||
fido_dev_info_product_string
|
||||
fido_dev_info_ptr
|
||||
fido_dev_info_set
|
||||
fido_dev_info_vendor
|
||||
fido_dev_io_handle
|
||||
fido_dev_is_fido2
|
||||
fido_dev_is_winhello
|
||||
fido_dev_major
|
||||
fido_dev_make_cred
|
||||
fido_dev_minor
|
||||
fido_dev_new
|
||||
fido_dev_new_with_info
|
||||
fido_dev_open
|
||||
fido_dev_open_with_info
|
||||
fido_dev_protocol
|
||||
fido_dev_reset
|
||||
fido_dev_set_io_functions
|
||||
fido_dev_set_pin
|
||||
fido_dev_set_pin_minlen
|
||||
fido_dev_set_pin_minlen_rpid
|
||||
fido_dev_set_sigmask
|
||||
fido_dev_set_timeout
|
||||
fido_dev_set_transport_functions
|
||||
fido_dev_supports_cred_prot
|
||||
fido_dev_supports_credman
|
||||
fido_dev_supports_permissions
|
||||
fido_dev_supports_pin
|
||||
fido_dev_supports_uv
|
||||
fido_dev_toggle_always_uv
|
||||
fido_dev_largeblob_get
|
||||
fido_dev_largeblob_get_array
|
||||
fido_dev_largeblob_remove
|
||||
fido_dev_largeblob_set
|
||||
fido_dev_largeblob_set_array
|
||||
fido_init
|
||||
fido_set_log_handler
|
||||
fido_strerr
|
||||
rs256_pk_free
|
||||
rs256_pk_from_ptr
|
||||
rs256_pk_from_EVP_PKEY
|
||||
rs256_pk_from_RSA
|
||||
rs256_pk_new
|
||||
rs256_pk_to_EVP_PKEY
|
||||
280
src/extern.h
Normal file
280
src/extern.h
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _EXTERN_H
|
||||
#define _EXTERN_H
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fido/types.h"
|
||||
#include "blob.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* aes256 */
|
||||
int aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *,
|
||||
const fido_blob_t *, fido_blob_t *);
|
||||
int aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *,
|
||||
const fido_blob_t *, fido_blob_t *);
|
||||
int aes256_gcm_dec(const fido_blob_t *, const fido_blob_t *,
|
||||
const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
|
||||
int aes256_gcm_enc(const fido_blob_t *, const fido_blob_t *,
|
||||
const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
|
||||
|
||||
/* cbor encoding functions */
|
||||
cbor_item_t *cbor_build_uint(const uint64_t);
|
||||
cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t);
|
||||
cbor_item_t *cbor_encode_assert_opt(fido_opt_t, fido_opt_t);
|
||||
cbor_item_t *cbor_encode_change_pin_auth(const fido_dev_t *,
|
||||
const fido_blob_t *, const fido_blob_t *, const fido_blob_t *);
|
||||
cbor_item_t *cbor_encode_cred_ext(const fido_cred_ext_t *, const fido_blob_t *);
|
||||
cbor_item_t *cbor_encode_assert_ext(fido_dev_t *,
|
||||
const fido_assert_ext_t *, const fido_blob_t *, const es256_pk_t *);
|
||||
cbor_item_t *cbor_encode_cred_opt(fido_opt_t, fido_opt_t);
|
||||
cbor_item_t *cbor_encode_pin_auth(const fido_dev_t *, const fido_blob_t *,
|
||||
const fido_blob_t *);
|
||||
cbor_item_t *cbor_encode_pin_opt(const fido_dev_t *);
|
||||
cbor_item_t *cbor_encode_pubkey(const fido_blob_t *);
|
||||
cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *);
|
||||
cbor_item_t *cbor_encode_pubkey_param(int);
|
||||
cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *);
|
||||
cbor_item_t *cbor_encode_str_array(const fido_str_array_t *);
|
||||
cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
|
||||
cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
|
||||
|
||||
/* cbor decoding functions */
|
||||
int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
|
||||
int cbor_decode_attobj(const cbor_item_t *, fido_cred_t *);
|
||||
int cbor_decode_bool(const cbor_item_t *, bool *);
|
||||
int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
|
||||
fido_authdata_t *, fido_attcred_t *, fido_cred_ext_t *);
|
||||
int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
|
||||
fido_authdata_t *, fido_assert_extattr_t *);
|
||||
int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *);
|
||||
int cbor_decode_fmt(const cbor_item_t *, char **);
|
||||
int cbor_decode_pubkey(const cbor_item_t *, int *, void *);
|
||||
int cbor_decode_rp_entity(const cbor_item_t *, fido_rp_t *);
|
||||
int cbor_decode_uint64(const cbor_item_t *, uint64_t *);
|
||||
int cbor_decode_user(const cbor_item_t *, fido_user_t *);
|
||||
int es256_pk_decode(const cbor_item_t *, es256_pk_t *);
|
||||
int es384_pk_decode(const cbor_item_t *, es384_pk_t *);
|
||||
int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *);
|
||||
int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *);
|
||||
|
||||
/* auxiliary cbor routines */
|
||||
int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t);
|
||||
int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *,
|
||||
size_t);
|
||||
int cbor_add_string(cbor_item_t *, const char *, const char *);
|
||||
int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
|
||||
void *));
|
||||
int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *);
|
||||
int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *);
|
||||
int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
|
||||
const cbor_item_t *, void *));
|
||||
int cbor_string_copy(const cbor_item_t *, char **);
|
||||
int cbor_parse_reply(const unsigned char *, size_t, void *,
|
||||
int(*)(const cbor_item_t *, const cbor_item_t *, void *));
|
||||
int cbor_add_uv_params(fido_dev_t *, uint8_t, const fido_blob_t *,
|
||||
const es256_pk_t *, const fido_blob_t *, const char *, const char *,
|
||||
cbor_item_t **, cbor_item_t **, int *);
|
||||
void cbor_vector_free(cbor_item_t **, size_t);
|
||||
int cbor_array_append(cbor_item_t **, cbor_item_t *);
|
||||
int cbor_array_drop(cbor_item_t **, size_t);
|
||||
|
||||
/* deflate */
|
||||
int fido_compress(fido_blob_t *, const fido_blob_t *);
|
||||
int fido_uncompress(fido_blob_t *, const fido_blob_t *, size_t);
|
||||
|
||||
#ifndef nitems
|
||||
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
|
||||
#endif
|
||||
|
||||
/* buf */
|
||||
int fido_buf_read(const unsigned char **, size_t *, void *, size_t);
|
||||
int fido_buf_write(unsigned char **, size_t *, const void *, size_t);
|
||||
|
||||
/* hid i/o */
|
||||
void *fido_hid_open(const char *);
|
||||
void fido_hid_close(void *);
|
||||
int fido_hid_read(void *, unsigned char *, size_t, int);
|
||||
int fido_hid_write(void *, const unsigned char *, size_t);
|
||||
int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
|
||||
int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
|
||||
int fido_hid_unix_open(const char *);
|
||||
int fido_hid_unix_wait(int, int, const fido_sigset_t *);
|
||||
int fido_hid_set_sigmask(void *, const fido_sigset_t *);
|
||||
size_t fido_hid_report_in_len(void *);
|
||||
size_t fido_hid_report_out_len(void *);
|
||||
|
||||
/* nfc i/o */
|
||||
bool fido_is_nfc(const char *);
|
||||
bool nfc_is_fido(const char *);
|
||||
void *fido_nfc_open(const char *);
|
||||
void fido_nfc_close(void *);
|
||||
int fido_nfc_read(void *, unsigned char *, size_t, int);
|
||||
int fido_nfc_write(void *, const unsigned char *, size_t);
|
||||
int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
|
||||
int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
|
||||
int fido_nfc_set_sigmask(void *, const fido_sigset_t *);
|
||||
int fido_dev_set_nfc(fido_dev_t *);
|
||||
|
||||
/* pcsc i/o */
|
||||
bool fido_is_pcsc(const char *);
|
||||
void *fido_pcsc_open(const char *);
|
||||
void fido_pcsc_close(void *);
|
||||
int fido_pcsc_read(void *, unsigned char *, size_t, int);
|
||||
int fido_pcsc_write(void *, const unsigned char *, size_t);
|
||||
int fido_pcsc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
|
||||
int fido_pcsc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
|
||||
int fido_dev_set_pcsc(fido_dev_t *);
|
||||
|
||||
/* windows hello */
|
||||
int fido_winhello_manifest(fido_dev_info_t *, size_t, size_t *);
|
||||
int fido_winhello_open(fido_dev_t *);
|
||||
int fido_winhello_close(fido_dev_t *);
|
||||
int fido_winhello_cancel(fido_dev_t *);
|
||||
int fido_winhello_get_assert(fido_dev_t *, fido_assert_t *, const char *, int);
|
||||
int fido_winhello_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
|
||||
int fido_winhello_make_cred(fido_dev_t *, fido_cred_t *, const char *, int);
|
||||
|
||||
/* generic i/o */
|
||||
int fido_rx_cbor_status(fido_dev_t *, int *);
|
||||
int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int *);
|
||||
int fido_tx(fido_dev_t *, uint8_t, const void *, size_t, int *);
|
||||
|
||||
/* log */
|
||||
#ifdef FIDO_NO_DIAGNOSTIC
|
||||
#define fido_log_init(...) do { /* nothing */ } while (0)
|
||||
#define fido_log_debug(...) do { /* nothing */ } while (0)
|
||||
#define fido_log_xxd(...) do { /* nothing */ } while (0)
|
||||
#define fido_log_error(...) do { /* nothing */ } while (0)
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
void fido_log_init(void);
|
||||
void fido_log_debug(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
void fido_log_xxd(const void *, size_t, const char *, ...)
|
||||
__attribute__((__format__ (printf, 3, 4)));
|
||||
void fido_log_error(int, const char *, ...)
|
||||
__attribute__((__format__ (printf, 2, 3)));
|
||||
#else
|
||||
void fido_log_init(void);
|
||||
void fido_log_debug(const char *, ...);
|
||||
void fido_log_xxd(const void *, size_t, const char *, ...);
|
||||
void fido_log_error(int, const char *, ...);
|
||||
#endif /* __GNUC__ */
|
||||
#endif /* FIDO_NO_DIAGNOSTIC */
|
||||
|
||||
/* u2f */
|
||||
int u2f_register(fido_dev_t *, fido_cred_t *, int *);
|
||||
int u2f_authenticate(fido_dev_t *, fido_assert_t *, int *);
|
||||
int u2f_get_touch_begin(fido_dev_t *, int *);
|
||||
int u2f_get_touch_status(fido_dev_t *, int *, int *);
|
||||
|
||||
/* unexposed fido ops */
|
||||
uint8_t fido_dev_get_pin_protocol(const fido_dev_t *);
|
||||
int fido_dev_authkey(fido_dev_t *, es256_pk_t *, int *);
|
||||
int fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int *);
|
||||
int fido_dev_get_uv_token(fido_dev_t *, uint8_t, const char *,
|
||||
const fido_blob_t *, const es256_pk_t *, const char *, fido_blob_t *,
|
||||
int *);
|
||||
uint64_t fido_dev_maxmsgsize(const fido_dev_t *);
|
||||
int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **, int *);
|
||||
|
||||
/* types */
|
||||
void fido_algo_array_free(fido_algo_array_t *);
|
||||
void fido_byte_array_free(fido_byte_array_t *);
|
||||
void fido_cert_array_free(fido_cert_array_t *);
|
||||
void fido_opt_array_free(fido_opt_array_t *);
|
||||
void fido_str_array_free(fido_str_array_t *);
|
||||
void fido_algo_free(fido_algo_t *);
|
||||
int fido_str_array_pack(fido_str_array_t *, const char * const *, size_t);
|
||||
|
||||
/* misc */
|
||||
void fido_assert_reset_rx(fido_assert_t *);
|
||||
void fido_assert_reset_tx(fido_assert_t *);
|
||||
void fido_cred_reset_rx(fido_cred_t *);
|
||||
void fido_cred_reset_tx(fido_cred_t *);
|
||||
void fido_cbor_info_reset(fido_cbor_info_t *);
|
||||
int fido_blob_serialise(fido_blob_t *, const cbor_item_t *);
|
||||
int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
|
||||
int fido_check_rp_id(const char *, const unsigned char *);
|
||||
int fido_get_random(void *, size_t);
|
||||
int fido_sha256(fido_blob_t *, const u_char *, size_t);
|
||||
int fido_time_now(struct timespec *);
|
||||
int fido_time_delta(const struct timespec *, int *);
|
||||
int fido_to_uint64(const char *, int, uint64_t *);
|
||||
|
||||
/* crypto */
|
||||
int es256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
|
||||
int es384_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
|
||||
int rs256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
|
||||
int eddsa_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
|
||||
int rs1_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
|
||||
int es256_pk_verify_sig(const fido_blob_t *, const es256_pk_t *,
|
||||
const fido_blob_t *);
|
||||
int es384_pk_verify_sig(const fido_blob_t *, const es384_pk_t *,
|
||||
const fido_blob_t *);
|
||||
int rs256_pk_verify_sig(const fido_blob_t *, const rs256_pk_t *,
|
||||
const fido_blob_t *);
|
||||
int eddsa_pk_verify_sig(const fido_blob_t *, const eddsa_pk_t *,
|
||||
const fido_blob_t *);
|
||||
int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *,
|
||||
const fido_blob_t *);
|
||||
int fido_get_signed_hash_tpm(fido_blob_t *, const fido_blob_t *,
|
||||
const fido_blob_t *, const fido_attstmt_t *, const fido_attcred_t *);
|
||||
|
||||
/* device manifest functions */
|
||||
int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *);
|
||||
int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *);
|
||||
int fido_pcsc_manifest(fido_dev_info_t *, size_t, size_t *);
|
||||
|
||||
/* fuzzing instrumentation */
|
||||
#ifdef FIDO_FUZZ
|
||||
uint32_t uniform_random(uint32_t);
|
||||
#endif
|
||||
|
||||
/* internal device capability flags */
|
||||
#define FIDO_DEV_PIN_SET 0x0001
|
||||
#define FIDO_DEV_PIN_UNSET 0x0002
|
||||
#define FIDO_DEV_CRED_PROT 0x0004
|
||||
#define FIDO_DEV_CREDMAN 0x0008
|
||||
#define FIDO_DEV_PIN_PROTOCOL1 0x0010
|
||||
#define FIDO_DEV_PIN_PROTOCOL2 0x0020
|
||||
#define FIDO_DEV_UV_SET 0x0040
|
||||
#define FIDO_DEV_UV_UNSET 0x0080
|
||||
#define FIDO_DEV_TOKEN_PERMS 0x0100
|
||||
#define FIDO_DEV_WINHELLO 0x0200
|
||||
#define FIDO_DEV_CREDMAN_PRE 0x0400
|
||||
#define FIDO_DEV_BIO_SET 0x0800
|
||||
#define FIDO_DEV_BIO_UNSET 0x1000
|
||||
|
||||
/* miscellanea */
|
||||
#define FIDO_DUMMY_CLIENTDATA ""
|
||||
#define FIDO_DUMMY_RP_ID "localhost"
|
||||
#define FIDO_DUMMY_USER_NAME "dummy"
|
||||
#define FIDO_DUMMY_USER_ID 1
|
||||
#define FIDO_WINHELLO_PATH "windows://hello"
|
||||
#define FIDO_NFC_PREFIX "nfc:"
|
||||
#define FIDO_PCSC_PREFIX "pcsc:"
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_EXTERN_H */
|
||||
21
src/fallthrough.h
Normal file
21
src/fallthrough.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
*/
|
||||
|
||||
#ifndef _FALLTHROUGH_H
|
||||
#define _FALLTHROUGH_H
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if __has_attribute(fallthrough)
|
||||
#define FALLTHROUGH __attribute__((fallthrough));
|
||||
#endif
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#ifndef FALLTHROUGH
|
||||
#define FALLTHROUGH /* FALLTHROUGH */
|
||||
#endif
|
||||
|
||||
#endif /* !_FALLTHROUGH_H */
|
||||
282
src/fido.h
Normal file
282
src/fido.h
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2022 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_H
|
||||
#define _FIDO_H
|
||||
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cbor.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "../openbsd-compat/openbsd-compat.h"
|
||||
#include "blob.h"
|
||||
#include "iso7816.h"
|
||||
#include "extern.h"
|
||||
#endif
|
||||
|
||||
#include "fido/err.h"
|
||||
#include "fido/param.h"
|
||||
#include "fido/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
fido_assert_t *fido_assert_new(void);
|
||||
fido_cred_t *fido_cred_new(void);
|
||||
fido_dev_t *fido_dev_new(void);
|
||||
fido_dev_t *fido_dev_new_with_info(const fido_dev_info_t *);
|
||||
fido_dev_info_t *fido_dev_info_new(size_t);
|
||||
fido_cbor_info_t *fido_cbor_info_new(void);
|
||||
void *fido_dev_io_handle(const fido_dev_t *);
|
||||
|
||||
void fido_assert_free(fido_assert_t **);
|
||||
void fido_cbor_info_free(fido_cbor_info_t **);
|
||||
void fido_cred_free(fido_cred_t **);
|
||||
void fido_dev_force_fido2(fido_dev_t *);
|
||||
void fido_dev_force_u2f(fido_dev_t *);
|
||||
void fido_dev_free(fido_dev_t **);
|
||||
void fido_dev_info_free(fido_dev_info_t **, size_t);
|
||||
|
||||
/* fido_init() flags. */
|
||||
#define FIDO_DEBUG 0x01
|
||||
#define FIDO_DISABLE_U2F_FALLBACK 0x02
|
||||
|
||||
void fido_init(int);
|
||||
void fido_set_log_handler(fido_log_handler_t *);
|
||||
|
||||
const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_authdata_raw_ptr(const fido_assert_t *,
|
||||
size_t);
|
||||
const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
|
||||
const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_largeblob_key_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
|
||||
const unsigned char *fido_assert_blob_ptr(const fido_assert_t *, size_t);
|
||||
|
||||
char **fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *);
|
||||
char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
|
||||
char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
|
||||
char **fido_cbor_info_transports_ptr(const fido_cbor_info_t *);
|
||||
char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *);
|
||||
const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *);
|
||||
const char *fido_assert_rp_id(const fido_assert_t *);
|
||||
const char *fido_assert_user_display_name(const fido_assert_t *, size_t);
|
||||
const char *fido_assert_user_icon(const fido_assert_t *, size_t);
|
||||
const char *fido_assert_user_name(const fido_assert_t *, size_t);
|
||||
const char *fido_cbor_info_algorithm_type(const fido_cbor_info_t *, size_t);
|
||||
const char *fido_cred_display_name(const fido_cred_t *);
|
||||
const char *fido_cred_fmt(const fido_cred_t *);
|
||||
const char *fido_cred_rp_id(const fido_cred_t *);
|
||||
const char *fido_cred_rp_name(const fido_cred_t *);
|
||||
const char *fido_cred_user_name(const fido_cred_t *);
|
||||
const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *);
|
||||
const char *fido_dev_info_path(const fido_dev_info_t *);
|
||||
const char *fido_dev_info_product_string(const fido_dev_info_t *);
|
||||
const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
|
||||
const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
|
||||
const uint64_t *fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *);
|
||||
const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
|
||||
const unsigned char *fido_cred_aaguid_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_attstmt_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_authdata_raw_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_largeblob_key_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
|
||||
const unsigned char *fido_cred_x5c_list_ptr(const fido_cred_t *, size_t);
|
||||
|
||||
int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t);
|
||||
int fido_assert_empty_allow_list(fido_assert_t *);
|
||||
int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *,
|
||||
size_t);
|
||||
int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *,
|
||||
size_t);
|
||||
int fido_assert_set_clientdata(fido_assert_t *, const unsigned char *, size_t);
|
||||
int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
|
||||
size_t);
|
||||
int fido_assert_set_count(fido_assert_t *, size_t);
|
||||
int fido_assert_set_extensions(fido_assert_t *, int);
|
||||
int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
|
||||
int fido_assert_set_hmac_secret(fido_assert_t *, size_t, const unsigned char *,
|
||||
size_t);
|
||||
int fido_assert_set_options(fido_assert_t *, bool, bool);
|
||||
int fido_assert_set_rp(fido_assert_t *, const char *);
|
||||
int fido_assert_set_up(fido_assert_t *, fido_opt_t);
|
||||
int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
|
||||
int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t);
|
||||
int fido_assert_set_winhello_appid(fido_assert_t *, const char *);
|
||||
int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
|
||||
int fido_cbor_info_algorithm_cose(const fido_cbor_info_t *, size_t);
|
||||
int fido_cred_empty_exclude_list(fido_cred_t *);
|
||||
int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_prot(const fido_cred_t *);
|
||||
int fido_cred_set_attstmt(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_attobj(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_blob(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_clientdata(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_extensions(fido_cred_t *, int);
|
||||
int fido_cred_set_fmt(fido_cred_t *, const char *);
|
||||
int fido_cred_set_id(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_options(fido_cred_t *, bool, bool);
|
||||
int fido_cred_set_pin_minlen(fido_cred_t *, size_t);
|
||||
int fido_cred_set_prot(fido_cred_t *, int);
|
||||
int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
|
||||
int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
|
||||
int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_set_type(fido_cred_t *, int);
|
||||
int fido_cred_set_uv(fido_cred_t *, fido_opt_t);
|
||||
int fido_cred_type(const fido_cred_t *);
|
||||
int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
|
||||
const char *, const char *, const char *);
|
||||
int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
|
||||
int fido_cred_verify(const fido_cred_t *);
|
||||
int fido_cred_verify_self(const fido_cred_t *);
|
||||
#ifdef _FIDO_SIGSET_DEFINED
|
||||
int fido_dev_set_sigmask(fido_dev_t *, const fido_sigset_t *);
|
||||
#endif
|
||||
int fido_dev_cancel(fido_dev_t *);
|
||||
int fido_dev_close(fido_dev_t *);
|
||||
int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
|
||||
int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
|
||||
int fido_dev_get_retry_count(fido_dev_t *, int *);
|
||||
int fido_dev_get_uv_retry_count(fido_dev_t *, int *);
|
||||
int fido_dev_get_touch_begin(fido_dev_t *);
|
||||
int fido_dev_get_touch_status(fido_dev_t *, int *, int);
|
||||
int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
|
||||
int fido_dev_info_set(fido_dev_info_t *, size_t, const char *, const char *,
|
||||
const char *, const fido_dev_io_t *, const fido_dev_transport_t *);
|
||||
int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
|
||||
int fido_dev_open_with_info(fido_dev_t *);
|
||||
int fido_dev_open(fido_dev_t *, const char *);
|
||||
int fido_dev_reset(fido_dev_t *);
|
||||
int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *);
|
||||
int fido_dev_set_pin(fido_dev_t *, const char *, const char *);
|
||||
int fido_dev_set_transport_functions(fido_dev_t *, const fido_dev_transport_t *);
|
||||
int fido_dev_set_timeout(fido_dev_t *, int);
|
||||
|
||||
size_t fido_assert_authdata_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_authdata_raw_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
|
||||
size_t fido_assert_count(const fido_assert_t *);
|
||||
size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_id_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_largeblob_key_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_sig_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_user_id_len(const fido_assert_t *, size_t);
|
||||
size_t fido_assert_blob_len(const fido_assert_t *, size_t);
|
||||
size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_algorithm_count(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_certs_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_transports_len(const fido_cbor_info_t *);
|
||||
size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
|
||||
size_t fido_cred_aaguid_len(const fido_cred_t *);
|
||||
size_t fido_cred_attstmt_len(const fido_cred_t *);
|
||||
size_t fido_cred_authdata_len(const fido_cred_t *);
|
||||
size_t fido_cred_authdata_raw_len(const fido_cred_t *);
|
||||
size_t fido_cred_clientdata_hash_len(const fido_cred_t *);
|
||||
size_t fido_cred_id_len(const fido_cred_t *);
|
||||
size_t fido_cred_largeblob_key_len(const fido_cred_t *);
|
||||
size_t fido_cred_pin_minlen(const fido_cred_t *);
|
||||
size_t fido_cred_pubkey_len(const fido_cred_t *);
|
||||
size_t fido_cred_sig_len(const fido_cred_t *);
|
||||
size_t fido_cred_user_id_len(const fido_cred_t *);
|
||||
size_t fido_cred_x5c_len(const fido_cred_t *);
|
||||
size_t fido_cred_x5c_list_count(const fido_cred_t *);
|
||||
size_t fido_cred_x5c_list_len(const fido_cred_t *, size_t);
|
||||
|
||||
uint8_t fido_assert_flags(const fido_assert_t *, size_t);
|
||||
uint32_t fido_assert_sigcount(const fido_assert_t *, size_t);
|
||||
uint8_t fido_cred_flags(const fido_cred_t *);
|
||||
uint32_t fido_cred_sigcount(const fido_cred_t *);
|
||||
uint8_t fido_dev_protocol(const fido_dev_t *);
|
||||
uint8_t fido_dev_major(const fido_dev_t *);
|
||||
uint8_t fido_dev_minor(const fido_dev_t *);
|
||||
uint8_t fido_dev_build(const fido_dev_t *);
|
||||
uint8_t fido_dev_flags(const fido_dev_t *);
|
||||
int16_t fido_dev_info_vendor(const fido_dev_info_t *);
|
||||
int16_t fido_dev_info_product(const fido_dev_info_t *);
|
||||
uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxlargeblob(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_minpinlen(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_uv_attempts(const fido_cbor_info_t *);
|
||||
uint64_t fido_cbor_info_uv_modality(const fido_cbor_info_t *);
|
||||
int64_t fido_cbor_info_rk_remaining(const fido_cbor_info_t *);
|
||||
|
||||
bool fido_dev_has_pin(const fido_dev_t *);
|
||||
bool fido_dev_has_uv(const fido_dev_t *);
|
||||
bool fido_dev_is_fido2(const fido_dev_t *);
|
||||
bool fido_dev_is_winhello(const fido_dev_t *);
|
||||
bool fido_dev_supports_credman(const fido_dev_t *);
|
||||
bool fido_dev_supports_cred_prot(const fido_dev_t *);
|
||||
bool fido_dev_supports_permissions(const fido_dev_t *);
|
||||
bool fido_dev_supports_pin(const fido_dev_t *);
|
||||
bool fido_dev_supports_uv(const fido_dev_t *);
|
||||
bool fido_cbor_info_new_pin_required(const fido_cbor_info_t *);
|
||||
|
||||
int fido_dev_largeblob_get(fido_dev_t *, const unsigned char *, size_t,
|
||||
unsigned char **, size_t *);
|
||||
int fido_dev_largeblob_set(fido_dev_t *, const unsigned char *, size_t,
|
||||
const unsigned char *, size_t, const char *);
|
||||
int fido_dev_largeblob_remove(fido_dev_t *, const unsigned char *, size_t,
|
||||
const char *);
|
||||
int fido_dev_largeblob_get_array(fido_dev_t *, unsigned char **, size_t *);
|
||||
int fido_dev_largeblob_set_array(fido_dev_t *, const unsigned char *, size_t,
|
||||
const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_H */
|
||||
133
src/fido/bio.h
Normal file
133
src/fido/bio.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_BIO_H
|
||||
#define _FIDO_BIO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "blob.h"
|
||||
#include "fido/err.h"
|
||||
#include "fido/param.h"
|
||||
#include "fido/types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#include <fido/err.h>
|
||||
#include <fido/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
struct fido_bio_template {
|
||||
fido_blob_t id;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct fido_bio_template_array {
|
||||
struct fido_bio_template *ptr;
|
||||
size_t n_alloc; /* number of allocated entries */
|
||||
size_t n_rx; /* number of populated entries */
|
||||
};
|
||||
|
||||
struct fido_bio_enroll {
|
||||
uint8_t remaining_samples;
|
||||
uint8_t last_status;
|
||||
fido_blob_t *token;
|
||||
};
|
||||
|
||||
struct fido_bio_info {
|
||||
uint8_t type;
|
||||
uint8_t max_samples;
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct fido_bio_template fido_bio_template_t;
|
||||
typedef struct fido_bio_template_array fido_bio_template_array_t;
|
||||
typedef struct fido_bio_enroll fido_bio_enroll_t;
|
||||
typedef struct fido_bio_info fido_bio_info_t;
|
||||
|
||||
#define FIDO_BIO_ENROLL_FP_GOOD 0x00
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
|
||||
#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
|
||||
#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
|
||||
#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
|
||||
#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
|
||||
#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
|
||||
#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
|
||||
#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
|
||||
|
||||
const char *fido_bio_template_name(const fido_bio_template_t *);
|
||||
const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *,
|
||||
size_t);
|
||||
const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *);
|
||||
fido_bio_enroll_t *fido_bio_enroll_new(void);
|
||||
fido_bio_info_t *fido_bio_info_new(void);
|
||||
fido_bio_template_array_t *fido_bio_template_array_new(void);
|
||||
fido_bio_template_t *fido_bio_template_new(void);
|
||||
int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *,
|
||||
fido_bio_enroll_t *, uint32_t, const char *);
|
||||
int fido_bio_dev_enroll_cancel(fido_dev_t *);
|
||||
int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *,
|
||||
fido_bio_enroll_t *, uint32_t);
|
||||
int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *,
|
||||
const char *);
|
||||
int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *);
|
||||
int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *,
|
||||
const char *);
|
||||
int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *,
|
||||
const char *);
|
||||
int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *,
|
||||
size_t);
|
||||
int fido_bio_template_set_name(fido_bio_template_t *, const char *);
|
||||
size_t fido_bio_template_array_count(const fido_bio_template_array_t *);
|
||||
size_t fido_bio_template_id_len(const fido_bio_template_t *);
|
||||
uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *);
|
||||
uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *);
|
||||
uint8_t fido_bio_info_max_samples(const fido_bio_info_t *);
|
||||
uint8_t fido_bio_info_type(const fido_bio_info_t *);
|
||||
void fido_bio_enroll_free(fido_bio_enroll_t **);
|
||||
void fido_bio_info_free(fido_bio_info_t **);
|
||||
void fido_bio_template_array_free(fido_bio_template_array_t **);
|
||||
void fido_bio_template_free(fido_bio_template_t **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_BIO_H */
|
||||
58
src/fido/config.h
Normal file
58
src/fido/config.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_CONFIG_H
|
||||
#define _FIDO_CONFIG_H
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "blob.h"
|
||||
#include "fido/err.h"
|
||||
#include "fido/param.h"
|
||||
#include "fido/types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#include <fido/err.h>
|
||||
#include <fido/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int fido_dev_enable_entattest(fido_dev_t *, const char *);
|
||||
int fido_dev_force_pin_change(fido_dev_t *, const char *);
|
||||
int fido_dev_toggle_always_uv(fido_dev_t *, const char *);
|
||||
int fido_dev_set_pin_minlen(fido_dev_t *, size_t, const char *);
|
||||
int fido_dev_set_pin_minlen_rpid(fido_dev_t *, const char * const *, size_t,
|
||||
const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_CONFIG_H */
|
||||
113
src/fido/credman.h
Normal file
113
src/fido/credman.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_CREDMAN_H
|
||||
#define _FIDO_CREDMAN_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "blob.h"
|
||||
#include "fido/err.h"
|
||||
#include "fido/param.h"
|
||||
#include "fido/types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#include <fido/err.h>
|
||||
#include <fido/param.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
struct fido_credman_metadata {
|
||||
uint64_t rk_existing;
|
||||
uint64_t rk_remaining;
|
||||
};
|
||||
|
||||
struct fido_credman_single_rp {
|
||||
fido_rp_t rp_entity;
|
||||
fido_blob_t rp_id_hash;
|
||||
};
|
||||
|
||||
struct fido_credman_rp {
|
||||
struct fido_credman_single_rp *ptr;
|
||||
size_t n_alloc; /* number of allocated entries */
|
||||
size_t n_rx; /* number of populated entries */
|
||||
};
|
||||
|
||||
struct fido_credman_rk {
|
||||
fido_cred_t *ptr;
|
||||
size_t n_alloc; /* number of allocated entries */
|
||||
size_t n_rx; /* number of populated entries */
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct fido_credman_metadata fido_credman_metadata_t;
|
||||
typedef struct fido_credman_rk fido_credman_rk_t;
|
||||
typedef struct fido_credman_rp fido_credman_rp_t;
|
||||
|
||||
const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t);
|
||||
const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t);
|
||||
|
||||
const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t);
|
||||
const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *,
|
||||
size_t);
|
||||
|
||||
fido_credman_metadata_t *fido_credman_metadata_new(void);
|
||||
fido_credman_rk_t *fido_credman_rk_new(void);
|
||||
fido_credman_rp_t *fido_credman_rp_new(void);
|
||||
|
||||
int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t,
|
||||
const char *);
|
||||
int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *,
|
||||
const char *);
|
||||
int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *,
|
||||
const char *);
|
||||
int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *);
|
||||
int fido_credman_set_dev_rk(fido_dev_t *, fido_cred_t *, const char *);
|
||||
|
||||
size_t fido_credman_rk_count(const fido_credman_rk_t *);
|
||||
size_t fido_credman_rp_count(const fido_credman_rp_t *);
|
||||
size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t);
|
||||
|
||||
uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *);
|
||||
uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *);
|
||||
|
||||
void fido_credman_metadata_free(fido_credman_metadata_t **);
|
||||
void fido_credman_rk_free(fido_credman_rk_t **);
|
||||
void fido_credman_rp_free(fido_credman_rp_t **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_CREDMAN_H */
|
||||
71
src/fido/eddsa.h
Normal file
71
src/fido/eddsa.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_EDDSA_H
|
||||
#define _FIDO_EDDSA_H
|
||||
|
||||
#include <openssl/ec.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
eddsa_pk_t *eddsa_pk_new(void);
|
||||
void eddsa_pk_free(eddsa_pk_t **);
|
||||
EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *);
|
||||
|
||||
int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *);
|
||||
int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t);
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3070000f
|
||||
#define EVP_PKEY_ED25519 EVP_PKEY_NONE
|
||||
int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *);
|
||||
EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *,
|
||||
size_t);
|
||||
int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t,
|
||||
const unsigned char *, size_t);
|
||||
#endif /* LIBRESSL_VERSION_NUMBER */
|
||||
|
||||
#endif /* _FIDO_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_EDDSA_H */
|
||||
106
src/fido/err.h
Normal file
106
src/fido/err.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_ERR_H
|
||||
#define _FIDO_ERR_H
|
||||
|
||||
#define FIDO_ERR_SUCCESS 0x00
|
||||
#define FIDO_ERR_INVALID_COMMAND 0x01
|
||||
#define FIDO_ERR_INVALID_PARAMETER 0x02
|
||||
#define FIDO_ERR_INVALID_LENGTH 0x03
|
||||
#define FIDO_ERR_INVALID_SEQ 0x04
|
||||
#define FIDO_ERR_TIMEOUT 0x05
|
||||
#define FIDO_ERR_CHANNEL_BUSY 0x06
|
||||
#define FIDO_ERR_LOCK_REQUIRED 0x0a
|
||||
#define FIDO_ERR_INVALID_CHANNEL 0x0b
|
||||
#define FIDO_ERR_CBOR_UNEXPECTED_TYPE 0x11
|
||||
#define FIDO_ERR_INVALID_CBOR 0x12
|
||||
#define FIDO_ERR_MISSING_PARAMETER 0x14
|
||||
#define FIDO_ERR_LIMIT_EXCEEDED 0x15
|
||||
#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
|
||||
#define FIDO_ERR_FP_DATABASE_FULL 0x17
|
||||
#define FIDO_ERR_LARGEBLOB_STORAGE_FULL 0x18
|
||||
#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
|
||||
#define FIDO_ERR_PROCESSING 0x21
|
||||
#define FIDO_ERR_INVALID_CREDENTIAL 0x22
|
||||
#define FIDO_ERR_USER_ACTION_PENDING 0x23
|
||||
#define FIDO_ERR_OPERATION_PENDING 0x24
|
||||
#define FIDO_ERR_NO_OPERATIONS 0x25
|
||||
#define FIDO_ERR_UNSUPPORTED_ALGORITHM 0x26
|
||||
#define FIDO_ERR_OPERATION_DENIED 0x27
|
||||
#define FIDO_ERR_KEY_STORE_FULL 0x28
|
||||
#define FIDO_ERR_NOT_BUSY 0x29
|
||||
#define FIDO_ERR_NO_OPERATION_PENDING 0x2a
|
||||
#define FIDO_ERR_UNSUPPORTED_OPTION 0x2b
|
||||
#define FIDO_ERR_INVALID_OPTION 0x2c
|
||||
#define FIDO_ERR_KEEPALIVE_CANCEL 0x2d
|
||||
#define FIDO_ERR_NO_CREDENTIALS 0x2e
|
||||
#define FIDO_ERR_USER_ACTION_TIMEOUT 0x2f
|
||||
#define FIDO_ERR_NOT_ALLOWED 0x30
|
||||
#define FIDO_ERR_PIN_INVALID 0x31
|
||||
#define FIDO_ERR_PIN_BLOCKED 0x32
|
||||
#define FIDO_ERR_PIN_AUTH_INVALID 0x33
|
||||
#define FIDO_ERR_PIN_AUTH_BLOCKED 0x34
|
||||
#define FIDO_ERR_PIN_NOT_SET 0x35
|
||||
#define FIDO_ERR_PIN_REQUIRED 0x36
|
||||
#define FIDO_ERR_PIN_POLICY_VIOLATION 0x37
|
||||
#define FIDO_ERR_PIN_TOKEN_EXPIRED 0x38
|
||||
#define FIDO_ERR_REQUEST_TOO_LARGE 0x39
|
||||
#define FIDO_ERR_ACTION_TIMEOUT 0x3a
|
||||
#define FIDO_ERR_UP_REQUIRED 0x3b
|
||||
#define FIDO_ERR_UV_BLOCKED 0x3c
|
||||
#define FIDO_ERR_UV_INVALID 0x3f
|
||||
#define FIDO_ERR_UNAUTHORIZED_PERM 0x40
|
||||
#define FIDO_ERR_ERR_OTHER 0x7f
|
||||
#define FIDO_ERR_SPEC_LAST 0xdf
|
||||
|
||||
/* defined internally */
|
||||
#define FIDO_OK FIDO_ERR_SUCCESS
|
||||
#define FIDO_ERR_TX -1
|
||||
#define FIDO_ERR_RX -2
|
||||
#define FIDO_ERR_RX_NOT_CBOR -3
|
||||
#define FIDO_ERR_RX_INVALID_CBOR -4
|
||||
#define FIDO_ERR_INVALID_PARAM -5
|
||||
#define FIDO_ERR_INVALID_SIG -6
|
||||
#define FIDO_ERR_INVALID_ARGUMENT -7
|
||||
#define FIDO_ERR_USER_PRESENCE_REQUIRED -8
|
||||
#define FIDO_ERR_INTERNAL -9
|
||||
#define FIDO_ERR_NOTFOUND -10
|
||||
#define FIDO_ERR_COMPRESS -11
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
const char *fido_strerr(int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* _FIDO_ERR_H */
|
||||
71
src/fido/es256.h
Normal file
71
src/fido/es256.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_ES256_H
|
||||
#define _FIDO_ES256_H
|
||||
|
||||
#include <openssl/ec.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
es256_pk_t *es256_pk_new(void);
|
||||
void es256_pk_free(es256_pk_t **);
|
||||
EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *);
|
||||
|
||||
int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *);
|
||||
int es256_pk_from_EVP_PKEY(es256_pk_t *, const EVP_PKEY *);
|
||||
int es256_pk_from_ptr(es256_pk_t *, const void *, size_t);
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
es256_sk_t *es256_sk_new(void);
|
||||
void es256_sk_free(es256_sk_t **);
|
||||
EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *);
|
||||
|
||||
int es256_derive_pk(const es256_sk_t *, es256_pk_t *);
|
||||
int es256_sk_create(es256_sk_t *);
|
||||
|
||||
int es256_pk_set_x(es256_pk_t *, const unsigned char *);
|
||||
int es256_pk_set_y(es256_pk_t *, const unsigned char *);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_ES256_H */
|
||||
59
src/fido/es384.h
Normal file
59
src/fido/es384.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_ES384_H
|
||||
#define _FIDO_ES384_H
|
||||
|
||||
#include <openssl/ec.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
es384_pk_t *es384_pk_new(void);
|
||||
void es384_pk_free(es384_pk_t **);
|
||||
EVP_PKEY *es384_pk_to_EVP_PKEY(const es384_pk_t *);
|
||||
|
||||
int es384_pk_from_EC_KEY(es384_pk_t *, const EC_KEY *);
|
||||
int es384_pk_from_EVP_PKEY(es384_pk_t *, const EVP_PKEY *);
|
||||
int es384_pk_from_ptr(es384_pk_t *, const void *, size_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_ES384_H */
|
||||
162
src/fido/param.h
Normal file
162
src/fido/param.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2022 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_PARAM_H
|
||||
#define _FIDO_PARAM_H
|
||||
|
||||
/* Authentication data flags. */
|
||||
#define CTAP_AUTHDATA_USER_PRESENT 0x01
|
||||
#define CTAP_AUTHDATA_USER_VERIFIED 0x04
|
||||
#define CTAP_AUTHDATA_ATT_CRED 0x40
|
||||
#define CTAP_AUTHDATA_EXT_DATA 0x80
|
||||
|
||||
/* CTAPHID command opcodes. */
|
||||
#define CTAP_CMD_PING 0x01
|
||||
#define CTAP_CMD_MSG 0x03
|
||||
#define CTAP_CMD_LOCK 0x04
|
||||
#define CTAP_CMD_INIT 0x06
|
||||
#define CTAP_CMD_WINK 0x08
|
||||
#define CTAP_CMD_CBOR 0x10
|
||||
#define CTAP_CMD_CANCEL 0x11
|
||||
#define CTAP_KEEPALIVE 0x3b
|
||||
#define CTAP_FRAME_INIT 0x80
|
||||
|
||||
/* CTAPHID CBOR command opcodes. */
|
||||
#define CTAP_CBOR_MAKECRED 0x01
|
||||
#define CTAP_CBOR_ASSERT 0x02
|
||||
#define CTAP_CBOR_GETINFO 0x04
|
||||
#define CTAP_CBOR_CLIENT_PIN 0x06
|
||||
#define CTAP_CBOR_RESET 0x07
|
||||
#define CTAP_CBOR_NEXT_ASSERT 0x08
|
||||
#define CTAP_CBOR_BIO_ENROLL 0x09
|
||||
#define CTAP_CBOR_CRED_MGMT 0x0a
|
||||
#define CTAP_CBOR_LARGEBLOB 0x0c
|
||||
#define CTAP_CBOR_CONFIG 0x0d
|
||||
#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
|
||||
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
|
||||
|
||||
/* Supported CTAP PIN/UV Auth Protocols. */
|
||||
#define CTAP_PIN_PROTOCOL1 1
|
||||
#define CTAP_PIN_PROTOCOL2 2
|
||||
|
||||
/* U2F command opcodes. */
|
||||
#define U2F_CMD_REGISTER 0x01
|
||||
#define U2F_CMD_AUTH 0x02
|
||||
|
||||
/* U2F command flags. */
|
||||
#define U2F_AUTH_SIGN 0x03
|
||||
#define U2F_AUTH_CHECK 0x07
|
||||
|
||||
/* ISO7816-4 status words. */
|
||||
#define SW1_MORE_DATA 0x61
|
||||
#define SW_CONDITIONS_NOT_SATISFIED 0x6985
|
||||
#define SW_WRONG_DATA 0x6a80
|
||||
#define SW_NO_ERROR 0x9000
|
||||
|
||||
/* HID Broadcast channel ID. */
|
||||
#define CTAP_CID_BROADCAST 0xffffffff
|
||||
|
||||
#define CTAP_INIT_HEADER_LEN 7
|
||||
#define CTAP_CONT_HEADER_LEN 5
|
||||
|
||||
/* Maximum length of a CTAP HID report in bytes. */
|
||||
#define CTAP_MAX_REPORT_LEN 64
|
||||
|
||||
/* Minimum length of a CTAP HID report in bytes. */
|
||||
#define CTAP_MIN_REPORT_LEN (CTAP_INIT_HEADER_LEN + 1)
|
||||
|
||||
/* Randomness device on UNIX-like platforms. */
|
||||
#ifndef FIDO_RANDOM_DEV
|
||||
#define FIDO_RANDOM_DEV "/dev/urandom"
|
||||
#endif
|
||||
|
||||
/* Maximum message size in bytes. */
|
||||
#ifndef FIDO_MAXMSG
|
||||
#define FIDO_MAXMSG 2048
|
||||
#endif
|
||||
|
||||
/* CTAP capability bits. */
|
||||
#define FIDO_CAP_WINK 0x01 /* if set, device supports CTAP_CMD_WINK */
|
||||
#define FIDO_CAP_CBOR 0x04 /* if set, device supports CTAP_CMD_CBOR */
|
||||
#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */
|
||||
|
||||
/* Supported COSE algorithms. */
|
||||
#define COSE_UNSPEC 0
|
||||
#define COSE_ES256 -7
|
||||
#define COSE_EDDSA -8
|
||||
#define COSE_ECDH_ES256 -25
|
||||
#define COSE_ES384 -35
|
||||
#define COSE_RS256 -257
|
||||
#define COSE_RS1 -65535
|
||||
|
||||
/* Supported COSE types. */
|
||||
#define COSE_KTY_OKP 1
|
||||
#define COSE_KTY_EC2 2
|
||||
#define COSE_KTY_RSA 3
|
||||
|
||||
/* Supported curves. */
|
||||
#define COSE_P256 1
|
||||
#define COSE_P384 2
|
||||
#define COSE_ED25519 6
|
||||
|
||||
/* Supported extensions. */
|
||||
#define FIDO_EXT_HMAC_SECRET 0x01
|
||||
#define FIDO_EXT_CRED_PROTECT 0x02
|
||||
#define FIDO_EXT_LARGEBLOB_KEY 0x04
|
||||
#define FIDO_EXT_CRED_BLOB 0x08
|
||||
#define FIDO_EXT_MINPINLEN 0x10
|
||||
|
||||
/* Supported credential protection policies. */
|
||||
#define FIDO_CRED_PROT_UV_OPTIONAL 0x01
|
||||
#define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0x02
|
||||
#define FIDO_CRED_PROT_UV_REQUIRED 0x03
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#define FIDO_EXT_ASSERT_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_LARGEBLOB_KEY| \
|
||||
FIDO_EXT_CRED_BLOB)
|
||||
#define FIDO_EXT_CRED_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_CRED_PROTECT| \
|
||||
FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB| \
|
||||
FIDO_EXT_MINPINLEN)
|
||||
#endif /* _FIDO_INTERNAL */
|
||||
|
||||
/* Recognised UV modes. */
|
||||
#define FIDO_UV_MODE_TUP 0x0001 /* internal test of user presence */
|
||||
#define FIDO_UV_MODE_FP 0x0002 /* internal fingerprint check */
|
||||
#define FIDO_UV_MODE_PIN 0x0004 /* internal pin check */
|
||||
#define FIDO_UV_MODE_VOICE 0x0008 /* internal voice recognition */
|
||||
#define FIDO_UV_MODE_FACE 0x0010 /* internal face recognition */
|
||||
#define FIDO_UV_MODE_LOCATION 0x0020 /* internal location check */
|
||||
#define FIDO_UV_MODE_EYE 0x0040 /* internal eyeprint check */
|
||||
#define FIDO_UV_MODE_DRAWN 0x0080 /* internal drawn pattern check */
|
||||
#define FIDO_UV_MODE_HAND 0x0100 /* internal handprint verification */
|
||||
#define FIDO_UV_MODE_NONE 0x0200 /* TUP/UV not required */
|
||||
#define FIDO_UV_MODE_ALL 0x0400 /* all supported UV modes required */
|
||||
#define FIDO_UV_MODE_EXT_PIN 0x0800 /* external pin verification */
|
||||
#define FIDO_UV_MODE_EXT_DRAWN 0x1000 /* external drawn pattern check */
|
||||
|
||||
#endif /* !_FIDO_PARAM_H */
|
||||
59
src/fido/rs256.h
Normal file
59
src/fido/rs256.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_RS256_H
|
||||
#define _FIDO_RS256_H
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "types.h"
|
||||
#else
|
||||
#include <fido.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
rs256_pk_t *rs256_pk_new(void);
|
||||
void rs256_pk_free(rs256_pk_t **);
|
||||
EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *);
|
||||
|
||||
int rs256_pk_from_EVP_PKEY(rs256_pk_t *, const EVP_PKEY *);
|
||||
int rs256_pk_from_RSA(rs256_pk_t *, const RSA *);
|
||||
int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_RS256_H */
|
||||
337
src/fido/types.h
Normal file
337
src/fido/types.h
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2022 Yubico AB. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_TYPES_H
|
||||
#define _FIDO_TYPES_H
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct fido_dev;
|
||||
|
||||
typedef void *fido_dev_io_open_t(const char *);
|
||||
typedef void fido_dev_io_close_t(void *);
|
||||
typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
|
||||
typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
|
||||
typedef int fido_dev_rx_t(struct fido_dev *, uint8_t, unsigned char *, size_t, int);
|
||||
typedef int fido_dev_tx_t(struct fido_dev *, uint8_t, const unsigned char *, size_t);
|
||||
|
||||
typedef struct fido_dev_io {
|
||||
fido_dev_io_open_t *open;
|
||||
fido_dev_io_close_t *close;
|
||||
fido_dev_io_read_t *read;
|
||||
fido_dev_io_write_t *write;
|
||||
} fido_dev_io_t;
|
||||
|
||||
typedef struct fido_dev_transport {
|
||||
fido_dev_rx_t *rx;
|
||||
fido_dev_tx_t *tx;
|
||||
} fido_dev_transport_t;
|
||||
|
||||
typedef enum {
|
||||
FIDO_OPT_OMIT = 0, /* use authenticator's default */
|
||||
FIDO_OPT_FALSE, /* explicitly set option to false */
|
||||
FIDO_OPT_TRUE, /* explicitly set option to true */
|
||||
} fido_opt_t;
|
||||
|
||||
typedef void fido_log_handler_t(const char *);
|
||||
|
||||
#undef _FIDO_SIGSET_DEFINED
|
||||
#define _FIDO_SIGSET_DEFINED
|
||||
#ifdef _WIN32
|
||||
typedef int fido_sigset_t;
|
||||
#elif defined(SIG_BLOCK)
|
||||
typedef sigset_t fido_sigset_t;
|
||||
#else
|
||||
#undef _FIDO_SIGSET_DEFINED
|
||||
#endif
|
||||
|
||||
#ifdef _FIDO_INTERNAL
|
||||
#include "packed.h"
|
||||
#include "blob.h"
|
||||
|
||||
/* COSE ES256 (ECDSA over P-256 with SHA-256) public key */
|
||||
typedef struct es256_pk {
|
||||
unsigned char x[32];
|
||||
unsigned char y[32];
|
||||
} es256_pk_t;
|
||||
|
||||
/* COSE ES256 (ECDSA over P-256 with SHA-256) (secret) key */
|
||||
typedef struct es256_sk {
|
||||
unsigned char d[32];
|
||||
} es256_sk_t;
|
||||
|
||||
/* COSE ES384 (ECDSA over P-384 with SHA-384) public key */
|
||||
typedef struct es384_pk {
|
||||
unsigned char x[48];
|
||||
unsigned char y[48];
|
||||
} es384_pk_t;
|
||||
|
||||
/* COSE RS256 (2048-bit RSA with PKCS1 padding and SHA-256) public key */
|
||||
typedef struct rs256_pk {
|
||||
unsigned char n[256];
|
||||
unsigned char e[3];
|
||||
} rs256_pk_t;
|
||||
|
||||
/* COSE EDDSA (ED25519) */
|
||||
typedef struct eddsa_pk {
|
||||
unsigned char x[32];
|
||||
} eddsa_pk_t;
|
||||
|
||||
PACKED_TYPE(fido_authdata_t,
|
||||
struct fido_authdata {
|
||||
unsigned char rp_id_hash[32]; /* sha256 of fido_rp.id */
|
||||
uint8_t flags; /* user present/verified */
|
||||
uint32_t sigcount; /* signature counter */
|
||||
/* actually longer */
|
||||
})
|
||||
|
||||
PACKED_TYPE(fido_attcred_raw_t,
|
||||
struct fido_attcred_raw {
|
||||
unsigned char aaguid[16]; /* credential's aaguid */
|
||||
uint16_t id_len; /* credential id length */
|
||||
uint8_t body[]; /* credential id + pubkey */
|
||||
})
|
||||
|
||||
typedef struct fido_attcred {
|
||||
unsigned char aaguid[16]; /* credential's aaguid */
|
||||
fido_blob_t id; /* credential id */
|
||||
int type; /* credential's cose algorithm */
|
||||
union { /* credential's public key */
|
||||
es256_pk_t es256;
|
||||
es384_pk_t es384;
|
||||
rs256_pk_t rs256;
|
||||
eddsa_pk_t eddsa;
|
||||
} pubkey;
|
||||
} fido_attcred_t;
|
||||
|
||||
typedef struct fido_attstmt {
|
||||
fido_blob_t certinfo; /* tpm attestation TPMS_ATTEST structure */
|
||||
fido_blob_t pubarea; /* tpm attestation TPMT_PUBLIC structure */
|
||||
fido_blob_t cbor; /* cbor-encoded attestation statement */
|
||||
fido_blob_array_t x5c; /* attestation certificate chain */
|
||||
fido_blob_t sig; /* attestation signature */
|
||||
int alg; /* attestation algorithm (cose) */
|
||||
} fido_attstmt_t;
|
||||
|
||||
typedef struct fido_rp {
|
||||
char *id; /* relying party id */
|
||||
char *name; /* relying party name */
|
||||
} fido_rp_t;
|
||||
|
||||
typedef struct fido_user {
|
||||
fido_blob_t id; /* required */
|
||||
char *icon; /* optional */
|
||||
char *name; /* optional */
|
||||
char *display_name; /* required */
|
||||
} fido_user_t;
|
||||
|
||||
typedef struct fido_cred_ext {
|
||||
int mask; /* enabled extensions */
|
||||
int prot; /* protection policy */
|
||||
size_t minpinlen; /* minimum pin length */
|
||||
} fido_cred_ext_t;
|
||||
|
||||
typedef struct fido_cred {
|
||||
fido_blob_t cd; /* client data */
|
||||
fido_blob_t cdh; /* client data hash */
|
||||
fido_rp_t rp; /* relying party */
|
||||
fido_user_t user; /* user entity */
|
||||
fido_blob_array_t excl; /* list of credential ids to exclude */
|
||||
fido_opt_t rk; /* resident key */
|
||||
fido_opt_t uv; /* user verification */
|
||||
fido_cred_ext_t ext; /* extensions */
|
||||
int type; /* cose algorithm */
|
||||
char *fmt; /* credential format */
|
||||
fido_cred_ext_t authdata_ext; /* decoded extensions */
|
||||
fido_blob_t authdata_cbor; /* cbor-encoded payload */
|
||||
fido_blob_t authdata_raw; /* cbor-decoded payload */
|
||||
fido_authdata_t authdata; /* decoded authdata payload */
|
||||
fido_attcred_t attcred; /* returned credential (key + id) */
|
||||
fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
|
||||
fido_blob_t largeblob_key; /* decoded large blob key */
|
||||
fido_blob_t blob; /* CTAP 2.1 credBlob */
|
||||
} fido_cred_t;
|
||||
|
||||
typedef struct fido_assert_extattr {
|
||||
int mask; /* decoded extensions */
|
||||
fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */
|
||||
fido_blob_t blob; /* decoded CTAP 2.1 credBlob */
|
||||
} fido_assert_extattr_t;
|
||||
|
||||
typedef struct _fido_assert_stmt {
|
||||
fido_blob_t id; /* credential id */
|
||||
fido_user_t user; /* user attributes */
|
||||
fido_blob_t hmac_secret; /* hmac secret */
|
||||
fido_assert_extattr_t authdata_ext; /* decoded extensions */
|
||||
fido_blob_t authdata_cbor; /* raw cbor payload */
|
||||
fido_blob_t authdata_raw; /* raw authdata */
|
||||
fido_authdata_t authdata; /* decoded authdata payload */
|
||||
fido_blob_t sig; /* signature of cdh + authdata */
|
||||
fido_blob_t largeblob_key; /* decoded large blob key */
|
||||
} fido_assert_stmt;
|
||||
|
||||
typedef struct fido_assert_ext {
|
||||
int mask; /* enabled extensions */
|
||||
fido_blob_t hmac_salt; /* optional hmac-secret salt */
|
||||
} fido_assert_ext_t;
|
||||
|
||||
typedef struct fido_assert {
|
||||
char *rp_id; /* relying party id */
|
||||
char *appid; /* winhello u2f appid */
|
||||
fido_blob_t cd; /* client data */
|
||||
fido_blob_t cdh; /* client data hash */
|
||||
fido_blob_array_t allow_list; /* list of allowed credentials */
|
||||
fido_opt_t up; /* user presence */
|
||||
fido_opt_t uv; /* user verification */
|
||||
fido_assert_ext_t ext; /* enabled extensions */
|
||||
fido_assert_stmt *stmt; /* array of expected assertions */
|
||||
size_t stmt_cnt; /* number of allocated assertions */
|
||||
size_t stmt_len; /* number of received assertions */
|
||||
} fido_assert_t;
|
||||
|
||||
typedef struct fido_opt_array {
|
||||
char **name;
|
||||
bool *value;
|
||||
size_t len;
|
||||
} fido_opt_array_t;
|
||||
|
||||
typedef struct fido_str_array {
|
||||
char **ptr;
|
||||
size_t len;
|
||||
} fido_str_array_t;
|
||||
|
||||
typedef struct fido_byte_array {
|
||||
uint8_t *ptr;
|
||||
size_t len;
|
||||
} fido_byte_array_t;
|
||||
|
||||
typedef struct fido_algo {
|
||||
char *type;
|
||||
int cose;
|
||||
} fido_algo_t;
|
||||
|
||||
typedef struct fido_algo_array {
|
||||
fido_algo_t *ptr;
|
||||
size_t len;
|
||||
} fido_algo_array_t;
|
||||
|
||||
typedef struct fido_cert_array {
|
||||
char **name;
|
||||
uint64_t *value;
|
||||
size_t len;
|
||||
} fido_cert_array_t;
|
||||
|
||||
typedef struct fido_cbor_info {
|
||||
fido_str_array_t versions; /* supported versions: fido2|u2f */
|
||||
fido_str_array_t extensions; /* list of supported extensions */
|
||||
fido_str_array_t transports; /* list of supported transports */
|
||||
unsigned char aaguid[16]; /* aaguid */
|
||||
fido_opt_array_t options; /* list of supported options */
|
||||
uint64_t maxmsgsiz; /* maximum message size */
|
||||
fido_byte_array_t protocols; /* supported pin protocols */
|
||||
fido_algo_array_t algorithms; /* list of supported algorithms */
|
||||
uint64_t maxcredcntlst; /* max credentials in list */
|
||||
uint64_t maxcredidlen; /* max credential ID length */
|
||||
uint64_t fwversion; /* firmware version */
|
||||
uint64_t maxcredbloblen; /* max credBlob length */
|
||||
uint64_t maxlargeblob; /* max largeBlob array length */
|
||||
uint64_t maxrpid_minlen; /* max rpid in set_pin_minlen_rpid */
|
||||
uint64_t minpinlen; /* min pin len enforced */
|
||||
uint64_t uv_attempts; /* platform uv attempts */
|
||||
uint64_t uv_modality; /* bitmask of supported uv types */
|
||||
int64_t rk_remaining; /* remaining resident credentials */
|
||||
bool new_pin_reqd; /* new pin required */
|
||||
fido_cert_array_t certs; /* associated certifications */
|
||||
} fido_cbor_info_t;
|
||||
|
||||
typedef struct fido_dev_info {
|
||||
char *path; /* device path */
|
||||
int16_t vendor_id; /* 2-byte vendor id */
|
||||
int16_t product_id; /* 2-byte product id */
|
||||
char *manufacturer; /* manufacturer string */
|
||||
char *product; /* product string */
|
||||
fido_dev_io_t io; /* i/o functions */
|
||||
fido_dev_transport_t transport; /* transport functions */
|
||||
} fido_dev_info_t;
|
||||
|
||||
PACKED_TYPE(fido_ctap_info_t,
|
||||
/* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */
|
||||
struct fido_ctap_info {
|
||||
uint64_t nonce; /* echoed nonce */
|
||||
uint32_t cid; /* channel id */
|
||||
uint8_t protocol; /* ctaphid protocol id */
|
||||
uint8_t major; /* major version number */
|
||||
uint8_t minor; /* minor version number */
|
||||
uint8_t build; /* build version number */
|
||||
uint8_t flags; /* capabilities flags; see FIDO_CAP_* */
|
||||
})
|
||||
|
||||
typedef struct fido_dev {
|
||||
uint64_t nonce; /* issued nonce */
|
||||
fido_ctap_info_t attr; /* device attributes */
|
||||
uint32_t cid; /* assigned channel id */
|
||||
char *path; /* device path */
|
||||
void *io_handle; /* abstract i/o handle */
|
||||
fido_dev_io_t io; /* i/o functions */
|
||||
bool io_own; /* device has own io/transport */
|
||||
size_t rx_len; /* length of HID input reports */
|
||||
size_t tx_len; /* length of HID output reports */
|
||||
int flags; /* internal flags; see FIDO_DEV_* */
|
||||
fido_dev_transport_t transport; /* transport functions */
|
||||
uint64_t maxmsgsize; /* max message size */
|
||||
int timeout_ms; /* read timeout in ms */
|
||||
} fido_dev_t;
|
||||
|
||||
#else
|
||||
typedef struct fido_assert fido_assert_t;
|
||||
typedef struct fido_cbor_info fido_cbor_info_t;
|
||||
typedef struct fido_cred fido_cred_t;
|
||||
typedef struct fido_dev fido_dev_t;
|
||||
typedef struct fido_dev_info fido_dev_info_t;
|
||||
typedef struct es256_pk es256_pk_t;
|
||||
typedef struct es256_sk es256_sk_t;
|
||||
typedef struct es384_pk es384_pk_t;
|
||||
typedef struct rs256_pk rs256_pk_t;
|
||||
typedef struct eddsa_pk eddsa_pk_t;
|
||||
#endif /* _FIDO_INTERNAL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_TYPES_H */
|
||||
222
src/hid.c
Normal file
222
src/hid.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int
|
||||
get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
|
||||
{
|
||||
*key = tag & 0xfc;
|
||||
if ((*key & 0xf0) == 0xf0) {
|
||||
fido_log_debug("%s: *key=0x%02x", __func__, *key);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*key_len = tag & 0x3;
|
||||
if (*key_len == 3) {
|
||||
*key_len = 4;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_key_val(const void *body, size_t key_len, uint32_t *val)
|
||||
{
|
||||
const uint8_t *ptr = body;
|
||||
|
||||
switch (key_len) {
|
||||
case 0:
|
||||
*val = 0;
|
||||
break;
|
||||
case 1:
|
||||
*val = ptr[0];
|
||||
break;
|
||||
case 2:
|
||||
*val = (uint32_t)((ptr[1] << 8) | ptr[0]);
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: key_len=%zu", __func__, key_len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_get_usage(const uint8_t *report_ptr, size_t report_len,
|
||||
uint32_t *usage_page)
|
||||
{
|
||||
const uint8_t *ptr = report_ptr;
|
||||
size_t len = report_len;
|
||||
|
||||
while (len > 0) {
|
||||
const uint8_t tag = ptr[0];
|
||||
ptr++;
|
||||
len--;
|
||||
|
||||
uint8_t key;
|
||||
size_t key_len;
|
||||
uint32_t key_val;
|
||||
|
||||
if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
|
||||
get_key_val(ptr, key_len, &key_val) < 0) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (key == 0x4) {
|
||||
*usage_page = key_val;
|
||||
}
|
||||
|
||||
ptr += key_len;
|
||||
len -= key_len;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_get_report_len(const uint8_t *report_ptr, size_t report_len,
|
||||
size_t *report_in_len, size_t *report_out_len)
|
||||
{
|
||||
const uint8_t *ptr = report_ptr;
|
||||
size_t len = report_len;
|
||||
uint32_t report_size = 0;
|
||||
|
||||
while (len > 0) {
|
||||
const uint8_t tag = ptr[0];
|
||||
ptr++;
|
||||
len--;
|
||||
|
||||
uint8_t key;
|
||||
size_t key_len;
|
||||
uint32_t key_val;
|
||||
|
||||
if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
|
||||
get_key_val(ptr, key_len, &key_val) < 0) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (key == 0x94) {
|
||||
report_size = key_val;
|
||||
} else if (key == 0x80) {
|
||||
*report_in_len = (size_t)report_size;
|
||||
} else if (key == 0x90) {
|
||||
*report_out_len = (size_t)report_size;
|
||||
}
|
||||
|
||||
ptr += key_len;
|
||||
len -= key_len;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
fido_dev_info_t *
|
||||
fido_dev_info_new(size_t n)
|
||||
{
|
||||
return (calloc(n, sizeof(fido_dev_info_t)));
|
||||
}
|
||||
|
||||
static void
|
||||
fido_dev_info_reset(fido_dev_info_t *di)
|
||||
{
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
memset(di, 0, sizeof(*di));
|
||||
}
|
||||
|
||||
void
|
||||
fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
|
||||
{
|
||||
fido_dev_info_t *devlist;
|
||||
|
||||
if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
fido_dev_info_reset(&devlist[i]);
|
||||
|
||||
free(devlist);
|
||||
|
||||
*devlist_p = NULL;
|
||||
}
|
||||
|
||||
const fido_dev_info_t *
|
||||
fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
|
||||
{
|
||||
return (&devlist[i]);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_info_set(fido_dev_info_t *devlist, size_t i,
|
||||
const char *path, const char *manufacturer, const char *product,
|
||||
const fido_dev_io_t *io, const fido_dev_transport_t *transport)
|
||||
{
|
||||
char *path_copy = NULL, *manu_copy = NULL, *prod_copy = NULL;
|
||||
int r;
|
||||
|
||||
if (path == NULL || manufacturer == NULL || product == NULL ||
|
||||
io == NULL) {
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((path_copy = strdup(path)) == NULL ||
|
||||
(manu_copy = strdup(manufacturer)) == NULL ||
|
||||
(prod_copy = strdup(product)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fido_dev_info_reset(&devlist[i]);
|
||||
devlist[i].path = path_copy;
|
||||
devlist[i].manufacturer = manu_copy;
|
||||
devlist[i].product = prod_copy;
|
||||
devlist[i].io = *io;
|
||||
if (transport)
|
||||
devlist[i].transport = *transport;
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
if (r != FIDO_OK) {
|
||||
free(prod_copy);
|
||||
free(manu_copy);
|
||||
free(path_copy);
|
||||
}
|
||||
return (r);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_dev_info_path(const fido_dev_info_t *di)
|
||||
{
|
||||
return (di->path);
|
||||
}
|
||||
|
||||
int16_t
|
||||
fido_dev_info_vendor(const fido_dev_info_t *di)
|
||||
{
|
||||
return (di->vendor_id);
|
||||
}
|
||||
|
||||
int16_t
|
||||
fido_dev_info_product(const fido_dev_info_t *di)
|
||||
{
|
||||
return (di->product_id);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_dev_info_manufacturer_string(const fido_dev_info_t *di)
|
||||
{
|
||||
return (di->manufacturer);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_dev_info_product_string(const fido_dev_info_t *di)
|
||||
{
|
||||
return (di->product);
|
||||
}
|
||||
337
src/hid_freebsd.c
Normal file
337
src/hid_freebsd.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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/param.h>
|
||||
|
||||
#include <dev/usb/usb_ioctl.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#if __FreeBSD_version >= 1300500
|
||||
#include <dev/hid/hidraw.h>
|
||||
#define USE_HIDRAW /* see usbhid(4) and hidraw(4) on FreeBSD 13+ */
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#if defined(__MidnightBSD__)
|
||||
#define UHID_VENDOR "MidnightBSD"
|
||||
#else
|
||||
#define UHID_VENDOR "FreeBSD"
|
||||
#endif
|
||||
|
||||
#define MAX_UHID 64
|
||||
|
||||
struct hid_freebsd {
|
||||
int fd;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
sigset_t sigmask;
|
||||
const sigset_t *sigmaskp;
|
||||
};
|
||||
|
||||
static bool
|
||||
is_fido(int fd)
|
||||
{
|
||||
char buf[64];
|
||||
struct usb_gen_descriptor ugd;
|
||||
uint32_t usage_page = 0;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
memset(&ugd, 0, sizeof(ugd));
|
||||
|
||||
ugd.ugd_report_type = UHID_FEATURE_REPORT;
|
||||
ugd.ugd_data = buf;
|
||||
ugd.ugd_maxlen = sizeof(buf);
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
return (false);
|
||||
}
|
||||
if (ugd.ugd_actlen > sizeof(buf) || fido_hid_get_usage(ugd.ugd_data,
|
||||
ugd.ugd_actlen, &usage_page) < 0) {
|
||||
fido_log_debug("%s: fido_hid_get_usage", __func__);
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (usage_page == 0xf1d0);
|
||||
}
|
||||
|
||||
#ifdef USE_HIDRAW
|
||||
static int
|
||||
copy_info_hidraw(fido_dev_info_t *di, const char *path)
|
||||
{
|
||||
int fd = -1;
|
||||
int ok = -1;
|
||||
struct usb_device_info udi;
|
||||
struct hidraw_devinfo devinfo;
|
||||
char rawname[129];
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
memset(&udi, 0, sizeof(udi));
|
||||
memset(&devinfo, 0, sizeof(devinfo));
|
||||
memset(rawname, 0, sizeof(rawname));
|
||||
|
||||
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
|
||||
goto fail;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
|
||||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRAWINFO), &devinfo) == -1 ||
|
||||
ioctl(fd, IOCTL_REQ(HIDIOCGRAWNAME(128)), rawname) == -1 ||
|
||||
(di->path = strdup(path)) == NULL ||
|
||||
(di->manufacturer = strdup(UHID_VENDOR)) == NULL ||
|
||||
(di->product = strdup(rawname)) == NULL)
|
||||
goto fail;
|
||||
di->vendor_id = devinfo.vendor;
|
||||
di->product_id = devinfo.product;
|
||||
} else {
|
||||
if ((di->path = strdup(path)) == NULL ||
|
||||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
|
||||
(di->product = strdup(udi.udi_product)) == NULL)
|
||||
goto fail;
|
||||
di->vendor_id = (int16_t)udi.udi_vendorNo;
|
||||
di->product_id = (int16_t)udi.udi_productNo;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1 && close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close %s", __func__, path);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
#endif /* USE_HIDRAW */
|
||||
|
||||
static int
|
||||
copy_info_uhid(fido_dev_info_t *di, const char *path)
|
||||
{
|
||||
int fd = -1;
|
||||
int ok = -1;
|
||||
struct usb_device_info udi;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
memset(&udi, 0, sizeof(udi));
|
||||
|
||||
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
|
||||
goto fail;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
strlcpy(udi.udi_vendor, UHID_VENDOR, sizeof(udi.udi_vendor));
|
||||
strlcpy(udi.udi_product, "uhid(4)", sizeof(udi.udi_product));
|
||||
udi.udi_vendorNo = 0x0b5d; /* stolen from PCI_VENDOR_OPENBSD */
|
||||
}
|
||||
|
||||
if ((di->path = strdup(path)) == NULL ||
|
||||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
|
||||
(di->product = strdup(udi.udi_product)) == NULL)
|
||||
goto fail;
|
||||
di->vendor_id = (int16_t)udi.udi_vendorNo;
|
||||
di->product_id = (int16_t)udi.udi_productNo;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1 && close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close %s", __func__, path);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
char path[64];
|
||||
size_t i;
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
|
||||
if (devlist == NULL || olen == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
*olen = 0;
|
||||
|
||||
#ifdef USE_HIDRAW
|
||||
for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
|
||||
snprintf(path, sizeof(path), "/dev/hidraw%zu", i);
|
||||
if (copy_info_hidraw(&devlist[*olen], path) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
++(*olen);
|
||||
}
|
||||
}
|
||||
/* hidraw(4) is preferred over uhid(4) */
|
||||
if (*olen != 0)
|
||||
return (FIDO_OK);
|
||||
#endif /* USE_HIDRAW */
|
||||
|
||||
for (i = 0; i < MAX_UHID && *olen < ilen; i++) {
|
||||
snprintf(path, sizeof(path), "/dev/uhid%zu", i);
|
||||
if (copy_info_uhid(&devlist[*olen], path) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
++(*olen);
|
||||
}
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
char buf[64];
|
||||
struct hid_freebsd *ctx;
|
||||
struct usb_gen_descriptor ugd;
|
||||
int r;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
memset(&ugd, 0, sizeof(ugd));
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((ctx->fd = fido_hid_unix_open(path)) == -1) {
|
||||
free(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ugd.ugd_report_type = UHID_FEATURE_REPORT;
|
||||
ugd.ugd_data = buf;
|
||||
ugd.ugd_maxlen = sizeof(buf);
|
||||
|
||||
/*
|
||||
* N.B. if ctx->fd is an hidraw(4) device, the ioctl() below puts it in
|
||||
* uhid(4) compat mode, which we need to keep fido_hid_write() as-is.
|
||||
*/
|
||||
if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ugd) == -1) ||
|
||||
ugd.ugd_actlen > sizeof(buf) ||
|
||||
fido_hid_get_report_len(ugd.ugd_data, ugd.ugd_actlen,
|
||||
&ctx->report_in_len, &ctx->report_out_len) < 0) {
|
||||
if (r == -1)
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
fido_log_debug("%s: using default report sizes", __func__);
|
||||
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
|
||||
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
|
||||
}
|
||||
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
|
||||
if (close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
|
||||
ctx->sigmask = *sigmask;
|
||||
ctx->sigmaskp = &ctx->sigmask;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
|
||||
fido_log_debug("%s: fd not ready", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = read(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_out_len + 1) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len - 1) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_freebsd *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
269
src/hid_hidapi.c
Normal file
269
src/hid_hidapi.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC. 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
|
||||
*/
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <hidapi.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
struct hid_hidapi {
|
||||
void *handle;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
};
|
||||
|
||||
static size_t
|
||||
fido_wcslen(const wchar_t *wcs)
|
||||
{
|
||||
size_t l = 0;
|
||||
while (*wcs++ != L'\0')
|
||||
l++;
|
||||
return l;
|
||||
}
|
||||
|
||||
static char *
|
||||
wcs_to_cs(const wchar_t *wcs)
|
||||
{
|
||||
char *cs;
|
||||
size_t i;
|
||||
|
||||
if (wcs == NULL || (cs = calloc(fido_wcslen(wcs) + 1, 1)) == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < fido_wcslen(wcs); i++) {
|
||||
if (wcs[i] >= 128) {
|
||||
/* give up on parsing non-ASCII text */
|
||||
free(cs);
|
||||
return strdup("hidapi device");
|
||||
}
|
||||
cs[i] = (char)wcs[i];
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, const struct hid_device_info *d)
|
||||
{
|
||||
memset(di, 0, sizeof(*di));
|
||||
|
||||
if (d->path != NULL)
|
||||
di->path = strdup(d->path);
|
||||
else
|
||||
di->path = strdup("");
|
||||
|
||||
if (d->manufacturer_string != NULL)
|
||||
di->manufacturer = wcs_to_cs(d->manufacturer_string);
|
||||
else
|
||||
di->manufacturer = strdup("");
|
||||
|
||||
if (d->product_string != NULL)
|
||||
di->product = wcs_to_cs(d->product_string);
|
||||
else
|
||||
di->product = strdup("");
|
||||
|
||||
if (di->path == NULL ||
|
||||
di->manufacturer == NULL ||
|
||||
di->product == NULL) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
return -1;
|
||||
}
|
||||
|
||||
di->product_id = (int16_t)d->product_id;
|
||||
di->vendor_id = (int16_t)d->vendor_id;
|
||||
di->io = (fido_dev_io_t) {
|
||||
&fido_hid_open,
|
||||
&fido_hid_close,
|
||||
&fido_hid_read,
|
||||
&fido_hid_write,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int
|
||||
get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
|
||||
{
|
||||
int fd;
|
||||
int s = -1;
|
||||
int ok = -1;
|
||||
|
||||
if ((fd = fido_hid_unix_open(path)) == -1) {
|
||||
fido_log_debug("%s: fido_hid_unix_open", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) < 0 || s < 0 ||
|
||||
(unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
|
||||
fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hrd->size = (unsigned)s;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) < 0) {
|
||||
fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_fido(const struct hid_device_info *hdi)
|
||||
{
|
||||
uint32_t usage_page = 0;
|
||||
struct hidraw_report_descriptor *hrd;
|
||||
|
||||
if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
|
||||
get_report_descriptor(hdi->path, hrd) < 0 ||
|
||||
fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
|
||||
usage_page = 0;
|
||||
|
||||
free(hrd);
|
||||
|
||||
return usage_page == 0xf1d0;
|
||||
}
|
||||
#elif defined(_WIN32) || defined(__APPLE__)
|
||||
static bool
|
||||
is_fido(const struct hid_device_info *hdi)
|
||||
{
|
||||
return hdi->usage_page == 0xf1d0;
|
||||
}
|
||||
#else
|
||||
static bool
|
||||
is_fido(const struct hid_device_info *hdi)
|
||||
{
|
||||
(void)hdi;
|
||||
fido_log_debug("%s: assuming FIDO HID", __func__);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_hidapi *ctx;
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((ctx->handle = hid_open_path(path)) == NULL) {
|
||||
free(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ctx->report_in_len = ctx->report_out_len = CTAP_MAX_REPORT_LEN;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_hidapi *ctx = handle;
|
||||
|
||||
hid_close(ctx->handle);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
(void)handle;
|
||||
(void)sigmask;
|
||||
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_hidapi *ctx = handle;
|
||||
|
||||
if (len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hid_read_timeout(ctx->handle, buf, len, ms);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_hidapi *ctx = handle;
|
||||
|
||||
if (len != ctx->report_out_len + 1) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hid_write(ctx->handle, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
struct hid_device_info *hdi;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return FIDO_OK; /* nothing to do */
|
||||
if (devlist == NULL)
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
if ((hdi = hid_enumerate(0, 0)) == NULL)
|
||||
return FIDO_OK; /* nothing to do */
|
||||
|
||||
for (struct hid_device_info *d = hdi; d != NULL; d = d->next) {
|
||||
if (is_fido(d) == false)
|
||||
continue;
|
||||
if (copy_info(&devlist[*olen], d) == 0) {
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hid_free_enumeration(hdi);
|
||||
|
||||
return FIDO_OK;
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_hidapi *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_hidapi *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
407
src/hid_linux.c
Normal file
407
src/hid_linux.c
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2024 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/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <libudev.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
struct hid_linux {
|
||||
int fd;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
sigset_t sigmask;
|
||||
const sigset_t *sigmaskp;
|
||||
};
|
||||
|
||||
static int
|
||||
get_report_descriptor(int fd, struct hidraw_report_descriptor *hrd)
|
||||
{
|
||||
int s = -1;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESCSIZE), &s) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl HIDIOCGRDESCSIZE", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
|
||||
fido_log_debug("%s: HIDIOCGRDESCSIZE %d", __func__, s);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
hrd->size = (unsigned)s;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(HIDIOCGRDESC), hrd) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl HIDIOCGRDESC", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_fido(const char *path)
|
||||
{
|
||||
int fd = -1;
|
||||
uint32_t usage_page = 0;
|
||||
struct hidraw_report_descriptor *hrd = NULL;
|
||||
|
||||
if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
|
||||
(fd = fido_hid_unix_open(path)) == -1)
|
||||
goto out;
|
||||
if (get_report_descriptor(fd, hrd) < 0 ||
|
||||
fido_hid_get_usage(hrd->value, hrd->size, &usage_page) < 0)
|
||||
usage_page = 0;
|
||||
|
||||
out:
|
||||
free(hrd);
|
||||
|
||||
if (fd != -1 && close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
return (usage_page == 0xf1d0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_uevent(const char *uevent, int *bus, int16_t *vendor_id,
|
||||
int16_t *product_id, char **hid_name)
|
||||
{
|
||||
char *cp;
|
||||
char *p;
|
||||
char *s;
|
||||
bool found_id = false;
|
||||
bool found_name = false;
|
||||
short unsigned int x;
|
||||
short unsigned int y;
|
||||
short unsigned int z;
|
||||
|
||||
if ((s = cp = strdup(uevent)) == NULL)
|
||||
return (-1);
|
||||
|
||||
while ((p = strsep(&cp, "\n")) != NULL && *p != '\0') {
|
||||
if (!found_id && strncmp(p, "HID_ID=", 7) == 0) {
|
||||
if (sscanf(p + 7, "%hx:%hx:%hx", &x, &y, &z) == 3) {
|
||||
*bus = (int)x;
|
||||
*vendor_id = (int16_t)y;
|
||||
*product_id = (int16_t)z;
|
||||
found_id = true;
|
||||
}
|
||||
} else if (!found_name && strncmp(p, "HID_NAME=", 9) == 0) {
|
||||
if ((*hid_name = strdup(p + 9)) != NULL)
|
||||
found_name = true;
|
||||
}
|
||||
}
|
||||
|
||||
free(s);
|
||||
|
||||
if (!found_name || !found_id)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_parent_attr(struct udev_device *dev, const char *subsystem,
|
||||
const char *devtype, const char *attr)
|
||||
{
|
||||
struct udev_device *parent;
|
||||
const char *value;
|
||||
|
||||
if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
|
||||
subsystem, devtype)) == NULL || (value =
|
||||
udev_device_get_sysattr_value(parent, attr)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
return (strdup(value));
|
||||
}
|
||||
|
||||
static char *
|
||||
get_usb_attr(struct udev_device *dev, const char *attr)
|
||||
{
|
||||
return (get_parent_attr(dev, "usb", "usb_device", attr));
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, struct udev *udev,
|
||||
struct udev_list_entry *udev_entry)
|
||||
{
|
||||
const char *name;
|
||||
const char *path;
|
||||
char *uevent = NULL;
|
||||
struct udev_device *dev = NULL;
|
||||
int bus = 0;
|
||||
char *hid_name = NULL;
|
||||
int ok = -1;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
|
||||
if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
|
||||
(dev = udev_device_new_from_syspath(udev, name)) == NULL ||
|
||||
(path = udev_device_get_devnode(dev)) == NULL ||
|
||||
is_fido(path) == 0)
|
||||
goto fail;
|
||||
|
||||
if ((uevent = get_parent_attr(dev, "hid", NULL, "uevent")) == NULL ||
|
||||
parse_uevent(uevent, &bus, &di->vendor_id, &di->product_id,
|
||||
&hid_name) < 0) {
|
||||
fido_log_debug("%s: uevent", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifndef FIDO_HID_ANY
|
||||
if (bus != BUS_USB) {
|
||||
fido_log_debug("%s: bus", __func__);
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
di->path = strdup(path);
|
||||
di->manufacturer = get_usb_attr(dev, "manufacturer");
|
||||
di->product = get_usb_attr(dev, "product");
|
||||
|
||||
if (di->manufacturer == NULL && di->product == NULL) {
|
||||
di->product = hid_name; /* fallback */
|
||||
hid_name = NULL;
|
||||
}
|
||||
if (di->manufacturer == NULL)
|
||||
di->manufacturer = strdup("");
|
||||
if (di->product == NULL)
|
||||
di->product = strdup("");
|
||||
if (di->path == NULL || di->manufacturer == NULL || di->product == NULL)
|
||||
goto fail;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (dev != NULL)
|
||||
udev_device_unref(dev);
|
||||
|
||||
free(uevent);
|
||||
free(hid_name);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
struct udev *udev = NULL;
|
||||
struct udev_enumerate *udev_enum = NULL;
|
||||
struct udev_list_entry *udev_list;
|
||||
struct udev_list_entry *udev_entry;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
|
||||
if (devlist == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if ((udev = udev_new()) == NULL ||
|
||||
(udev_enum = udev_enumerate_new(udev)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
|
||||
udev_enumerate_scan_devices(udev_enum) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
|
||||
r = FIDO_OK; /* zero hidraw devices */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
udev_list_entry_foreach(udev_entry, udev_list) {
|
||||
if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (udev_enum != NULL)
|
||||
udev_enumerate_unref(udev_enum);
|
||||
if (udev != NULL)
|
||||
udev_unref(udev);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_linux *ctx;
|
||||
struct hidraw_report_descriptor *hrd;
|
||||
struct timespec tv_pause;
|
||||
long interval_ms, retries = 0;
|
||||
bool looped;
|
||||
|
||||
retry:
|
||||
looped = false;
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
|
||||
(ctx->fd = fido_hid_unix_open(path)) == -1) {
|
||||
free(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
while (flock(ctx->fd, LOCK_EX|LOCK_NB) == -1) {
|
||||
if (errno != EWOULDBLOCK) {
|
||||
fido_log_error(errno, "%s: flock", __func__);
|
||||
fido_hid_close(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
looped = true;
|
||||
if (retries++ >= 20) {
|
||||
fido_log_debug("%s: flock timeout", __func__);
|
||||
fido_hid_close(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
interval_ms = retries * 100000000L;
|
||||
tv_pause.tv_sec = interval_ms / 1000000000L;
|
||||
tv_pause.tv_nsec = interval_ms % 1000000000L;
|
||||
if (nanosleep(&tv_pause, NULL) == -1) {
|
||||
fido_log_error(errno, "%s: nanosleep", __func__);
|
||||
fido_hid_close(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (looped) {
|
||||
fido_log_debug("%s: retrying", __func__);
|
||||
fido_hid_close(ctx);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if ((hrd = calloc(1, sizeof(*hrd))) == NULL ||
|
||||
get_report_descriptor(ctx->fd, hrd) < 0 ||
|
||||
fido_hid_get_report_len(hrd->value, hrd->size, &ctx->report_in_len,
|
||||
&ctx->report_out_len) < 0 || ctx->report_in_len == 0 ||
|
||||
ctx->report_out_len == 0) {
|
||||
fido_log_debug("%s: using default report sizes", __func__);
|
||||
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
|
||||
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
|
||||
}
|
||||
|
||||
free(hrd);
|
||||
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
|
||||
if (close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
|
||||
ctx->sigmask = *sigmask;
|
||||
ctx->sigmaskp = &ctx->sigmask;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
|
||||
fido_log_debug("%s: fd not ready", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = read(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_out_len + 1) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = write(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)r);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_linux *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
339
src/hid_netbsd.c
Normal file
339
src/hid_netbsd.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#define MAX_UHID 64
|
||||
|
||||
struct hid_netbsd {
|
||||
int fd;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
sigset_t sigmask;
|
||||
const sigset_t *sigmaskp;
|
||||
};
|
||||
|
||||
/* Hack to make this work with newer kernels even if /usr/include is old. */
|
||||
#if __NetBSD_Version__ < 901000000 /* 9.1 */
|
||||
#define USB_HID_GET_RAW _IOR('h', 1, int)
|
||||
#define USB_HID_SET_RAW _IOW('h', 2, int)
|
||||
#endif
|
||||
|
||||
static bool
|
||||
is_fido(int fd)
|
||||
{
|
||||
struct usb_ctl_report_desc ucrd;
|
||||
uint32_t usage_page = 0;
|
||||
int raw = 1;
|
||||
|
||||
memset(&ucrd, 0, sizeof(ucrd));
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ucrd) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (ucrd.ucrd_size < 0 ||
|
||||
(size_t)ucrd.ucrd_size > sizeof(ucrd.ucrd_data) ||
|
||||
fido_hid_get_usage(ucrd.ucrd_data, (size_t)ucrd.ucrd_size,
|
||||
&usage_page) < 0) {
|
||||
fido_log_debug("%s: fido_hid_get_usage", __func__);
|
||||
return (false);
|
||||
}
|
||||
|
||||
if (usage_page != 0xf1d0)
|
||||
return (false);
|
||||
|
||||
/*
|
||||
* This step is not strictly necessary -- NetBSD puts fido
|
||||
* devices into raw mode automatically by default, but in
|
||||
* principle that might change, and this serves as a test to
|
||||
* verify that we're running on a kernel with support for raw
|
||||
* mode at all so we don't get confused issuing writes that try
|
||||
* to set the report descriptor rather than transfer data on
|
||||
* the output interrupt pipe as we need.
|
||||
*/
|
||||
if (ioctl(fd, IOCTL_REQ(USB_HID_SET_RAW), &raw) == -1) {
|
||||
fido_log_error(errno, "%s: unable to set raw", __func__);
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, const char *path)
|
||||
{
|
||||
int fd = -1;
|
||||
int ok = -1;
|
||||
struct usb_device_info udi;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
memset(&udi, 0, sizeof(udi));
|
||||
|
||||
if ((fd = fido_hid_unix_open(path)) == -1 || is_fido(fd) == 0)
|
||||
goto fail;
|
||||
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((di->path = strdup(path)) == NULL ||
|
||||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
|
||||
(di->product = strdup(udi.udi_product)) == NULL)
|
||||
goto fail;
|
||||
|
||||
di->vendor_id = (int16_t)udi.udi_vendorNo;
|
||||
di->product_id = (int16_t)udi.udi_productNo;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1 && close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
char path[64];
|
||||
size_t i;
|
||||
|
||||
if (devlist == NULL || olen == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
|
||||
for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
|
||||
snprintf(path, sizeof(path), "/dev/uhid%zu", i);
|
||||
if (copy_info(&devlist[*olen], path) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
++(*olen);
|
||||
}
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Workaround for NetBSD (as of 201910) bug that loses
|
||||
* sync of DATA0/DATA1 sequence bit across uhid open/close.
|
||||
* Send pings until we get a response - early pings with incorrect
|
||||
* sequence bits will be ignored as duplicate packets by the device.
|
||||
*/
|
||||
static int
|
||||
terrible_ping_kludge(struct hid_netbsd *ctx)
|
||||
{
|
||||
u_char data[256];
|
||||
int i, n;
|
||||
struct pollfd pfd;
|
||||
|
||||
if (sizeof(data) < ctx->report_out_len + 1)
|
||||
return -1;
|
||||
for (i = 0; i < 4; i++) {
|
||||
memset(data, 0, sizeof(data));
|
||||
/* broadcast channel ID */
|
||||
data[1] = 0xff;
|
||||
data[2] = 0xff;
|
||||
data[3] = 0xff;
|
||||
data[4] = 0xff;
|
||||
/* Ping command */
|
||||
data[5] = 0x81;
|
||||
/* One byte ping only, Vasili */
|
||||
data[6] = 0;
|
||||
data[7] = 1;
|
||||
fido_log_debug("%s: send ping %d", __func__, i);
|
||||
if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1)
|
||||
return -1;
|
||||
fido_log_debug("%s: wait reply", __func__);
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = ctx->fd;
|
||||
pfd.events = POLLIN;
|
||||
if ((n = poll(&pfd, 1, 100)) == -1) {
|
||||
fido_log_error(errno, "%s: poll", __func__);
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
fido_log_debug("%s: timed out", __func__);
|
||||
continue;
|
||||
}
|
||||
if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1)
|
||||
return -1;
|
||||
/*
|
||||
* Ping isn't always supported on the broadcast channel,
|
||||
* so we might get an error, but we don't care - we're
|
||||
* synched now.
|
||||
*/
|
||||
fido_log_xxd(data, ctx->report_out_len, "%s: got reply",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
fido_log_debug("%s: no response", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_netbsd *ctx;
|
||||
struct usb_ctl_report_desc ucrd;
|
||||
int r;
|
||||
|
||||
memset(&ucrd, 0, sizeof(ucrd));
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
|
||||
(ctx->fd = fido_hid_unix_open(path)) == -1) {
|
||||
free(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((r = ioctl(ctx->fd, IOCTL_REQ(USB_GET_REPORT_DESC), &ucrd)) == -1 ||
|
||||
ucrd.ucrd_size < 0 ||
|
||||
(size_t)ucrd.ucrd_size > sizeof(ucrd.ucrd_data) ||
|
||||
fido_hid_get_report_len(ucrd.ucrd_data, (size_t)ucrd.ucrd_size,
|
||||
&ctx->report_in_len, &ctx->report_out_len) < 0) {
|
||||
if (r == -1)
|
||||
fido_log_error(errno, "%s: ioctl", __func__);
|
||||
fido_log_debug("%s: using default report sizes", __func__);
|
||||
ctx->report_in_len = CTAP_MAX_REPORT_LEN;
|
||||
ctx->report_out_len = CTAP_MAX_REPORT_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* NetBSD has a bug that causes it to lose
|
||||
* track of the DATA0/DATA1 sequence toggle across uhid device
|
||||
* open and close. This is a terrible hack to work around it.
|
||||
*/
|
||||
if (!is_fido(ctx->fd) || terrible_ping_kludge(ctx) != 0) {
|
||||
fido_hid_close(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
|
||||
if (close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
|
||||
ctx->sigmask = *sigmask;
|
||||
ctx->sigmaskp = &ctx->sigmask;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
|
||||
fido_log_debug("%s: fd not ready", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = read(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_error(errno, "%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_out_len + 1) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len - 1) {
|
||||
fido_log_error(errno, "%s: %zd != %zu", __func__, r, len - 1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_netbsd *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
280
src/hid_openbsd.c
Normal file
280
src/hid_openbsd.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Google LLC. 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/ioctl.h>
|
||||
#include <dev/usb/usb.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#define MAX_UHID 64
|
||||
|
||||
struct hid_openbsd {
|
||||
int fd;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
sigset_t sigmask;
|
||||
const sigset_t *sigmaskp;
|
||||
};
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, const char *path)
|
||||
{
|
||||
int fd = -1, ok = -1;
|
||||
struct usb_device_info udi;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
memset(&udi, 0, sizeof(udi));
|
||||
|
||||
if ((fd = fido_hid_unix_open(path)) == -1)
|
||||
goto fail;
|
||||
if (ioctl(fd, IOCTL_REQ(USB_GET_DEVICEINFO), &udi) == -1) {
|
||||
fido_log_error(errno, "%s: ioctl %s", __func__, path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x", __func__, path,
|
||||
udi.udi_bus, udi.udi_addr);
|
||||
fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"", __func__,
|
||||
path, udi.udi_vendor, udi.udi_product);
|
||||
fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, "
|
||||
"releaseNo = 0x%04x", __func__, path, udi.udi_productNo,
|
||||
udi.udi_vendorNo, udi.udi_releaseNo);
|
||||
|
||||
if ((di->path = strdup(path)) == NULL ||
|
||||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
|
||||
(di->product = strdup(udi.udi_product)) == NULL)
|
||||
goto fail;
|
||||
|
||||
di->vendor_id = (int16_t)udi.udi_vendorNo;
|
||||
di->product_id = (int16_t)udi.udi_productNo;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1 && close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close %s", __func__, path);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
size_t i;
|
||||
char path[64];
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
|
||||
if (devlist == NULL || olen == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
|
||||
snprintf(path, sizeof(path), "/dev/fido/%zu", i);
|
||||
if (copy_info(&devlist[*olen], path) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
++(*olen);
|
||||
}
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses
|
||||
* sync of DATA0/DATA1 sequence bit across uhid open/close.
|
||||
* Send pings until we get a response - early pings with incorrect
|
||||
* sequence bits will be ignored as duplicate packets by the device.
|
||||
*/
|
||||
static int
|
||||
terrible_ping_kludge(struct hid_openbsd *ctx)
|
||||
{
|
||||
u_char data[256];
|
||||
int i, n;
|
||||
struct pollfd pfd;
|
||||
|
||||
if (sizeof(data) < ctx->report_out_len + 1)
|
||||
return -1;
|
||||
for (i = 0; i < 4; i++) {
|
||||
memset(data, 0, sizeof(data));
|
||||
/* broadcast channel ID */
|
||||
data[1] = 0xff;
|
||||
data[2] = 0xff;
|
||||
data[3] = 0xff;
|
||||
data[4] = 0xff;
|
||||
/* Ping command */
|
||||
data[5] = 0x81;
|
||||
/* One byte ping only, Vasili */
|
||||
data[6] = 0;
|
||||
data[7] = 1;
|
||||
fido_log_debug("%s: send ping %d", __func__, i);
|
||||
if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1)
|
||||
return -1;
|
||||
fido_log_debug("%s: wait reply", __func__);
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = ctx->fd;
|
||||
pfd.events = POLLIN;
|
||||
if ((n = poll(&pfd, 1, 100)) == -1) {
|
||||
fido_log_error(errno, "%s: poll", __func__);
|
||||
return -1;
|
||||
} else if (n == 0) {
|
||||
fido_log_debug("%s: timed out", __func__);
|
||||
continue;
|
||||
}
|
||||
if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1)
|
||||
return -1;
|
||||
/*
|
||||
* Ping isn't always supported on the broadcast channel,
|
||||
* so we might get an error, but we don't care - we're
|
||||
* synched now.
|
||||
*/
|
||||
fido_log_xxd(data, ctx->report_out_len, "%s: got reply",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
fido_log_debug("%s: no response", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_openbsd *ret = NULL;
|
||||
|
||||
if ((ret = calloc(1, sizeof(*ret))) == NULL ||
|
||||
(ret->fd = fido_hid_unix_open(path)) == -1) {
|
||||
free(ret);
|
||||
return (NULL);
|
||||
}
|
||||
ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN;
|
||||
fido_log_debug("%s: inlen = %zu outlen = %zu", __func__,
|
||||
ret->report_in_len, ret->report_out_len);
|
||||
|
||||
/*
|
||||
* OpenBSD (as of 201910) has a bug that causes it to lose
|
||||
* track of the DATA0/DATA1 sequence toggle across uhid device
|
||||
* open and close. This is a terrible hack to work around it.
|
||||
*/
|
||||
if (terrible_ping_kludge(ret) != 0) {
|
||||
fido_hid_close(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
|
||||
|
||||
if (close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct hid_openbsd *ctx = handle;
|
||||
|
||||
ctx->sigmask = *sigmask;
|
||||
ctx->sigmaskp = &ctx->sigmask;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
|
||||
len, ctx->report_in_len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
|
||||
fido_log_debug("%s: fd not ready", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = read(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
|
||||
ssize_t r;
|
||||
|
||||
if (len != ctx->report_out_len + 1) {
|
||||
fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
|
||||
len, ctx->report_out_len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len - 1) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_openbsd *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_openbsd *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
611
src/hid_osx.c
Normal file
611
src/hid_osx.c
Normal file
@@ -0,0 +1,611 @@
|
||||
/*
|
||||
* Copyright (c) 2019-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 <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Availability.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 120000
|
||||
#define kIOMainPortDefault kIOMasterPortDefault
|
||||
#endif
|
||||
|
||||
#define IOREG "ioreg://"
|
||||
|
||||
struct hid_osx {
|
||||
IOHIDDeviceRef ref;
|
||||
CFStringRef loop_id;
|
||||
int report_pipe[2];
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
unsigned char report[CTAP_MAX_REPORT_LEN];
|
||||
};
|
||||
|
||||
static int
|
||||
get_int32(IOHIDDeviceRef dev, CFStringRef key, int32_t *v)
|
||||
{
|
||||
CFTypeRef ref;
|
||||
|
||||
if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
|
||||
CFGetTypeID(ref) != CFNumberGetTypeID()) {
|
||||
fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (CFNumberGetType(ref) != kCFNumberSInt32Type &&
|
||||
CFNumberGetType(ref) != kCFNumberSInt64Type) {
|
||||
fido_log_debug("%s: CFNumberGetType", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (CFNumberGetValue(ref, kCFNumberSInt32Type, v) == false) {
|
||||
fido_log_debug("%s: CFNumberGetValue", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
|
||||
{
|
||||
CFTypeRef ref;
|
||||
|
||||
memset(buf, 0, len);
|
||||
|
||||
if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
|
||||
CFGetTypeID(ref) != CFStringGetTypeID()) {
|
||||
fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (CFStringGetCString(ref, buf, (long)len,
|
||||
kCFStringEncodingUTF8) == false) {
|
||||
fido_log_debug("%s: CFStringGetCString", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_report_len(IOHIDDeviceRef dev, int dir, size_t *report_len)
|
||||
{
|
||||
CFStringRef key;
|
||||
int32_t v;
|
||||
|
||||
if (dir == 0)
|
||||
key = CFSTR(kIOHIDMaxInputReportSizeKey);
|
||||
else
|
||||
key = CFSTR(kIOHIDMaxOutputReportSizeKey);
|
||||
|
||||
if (get_int32(dev, key, &v) < 0) {
|
||||
fido_log_debug("%s: get_int32/%d", __func__, dir);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((*report_len = (size_t)v) > CTAP_MAX_REPORT_LEN) {
|
||||
fido_log_debug("%s: report_len=%zu", __func__, *report_len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id)
|
||||
{
|
||||
int32_t vendor;
|
||||
int32_t product;
|
||||
|
||||
if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 ||
|
||||
vendor > UINT16_MAX) {
|
||||
fido_log_debug("%s: get_int32 vendor", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (get_int32(dev, CFSTR(kIOHIDProductIDKey), &product) < 0 ||
|
||||
product > UINT16_MAX) {
|
||||
fido_log_debug("%s: get_int32 product", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*vendor_id = (int16_t)vendor;
|
||||
*product_id = (int16_t)product;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_str(IOHIDDeviceRef dev, char **manufacturer, char **product)
|
||||
{
|
||||
char buf[512];
|
||||
int ok = -1;
|
||||
|
||||
*manufacturer = NULL;
|
||||
*product = NULL;
|
||||
|
||||
if (get_utf8(dev, CFSTR(kIOHIDManufacturerKey), buf, sizeof(buf)) < 0)
|
||||
*manufacturer = strdup("");
|
||||
else
|
||||
*manufacturer = strdup(buf);
|
||||
|
||||
if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0)
|
||||
*product = strdup("");
|
||||
else
|
||||
*product = strdup(buf);
|
||||
|
||||
if (*manufacturer == NULL || *product == NULL) {
|
||||
fido_log_debug("%s: strdup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ok < 0) {
|
||||
free(*manufacturer);
|
||||
free(*product);
|
||||
*manufacturer = NULL;
|
||||
*product = NULL;
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_path(IOHIDDeviceRef dev)
|
||||
{
|
||||
io_service_t s;
|
||||
uint64_t id;
|
||||
char *path;
|
||||
|
||||
if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) {
|
||||
fido_log_debug("%s: IOHIDDeviceGetService", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (IORegistryEntryGetRegistryEntryID(s, &id) != KERN_SUCCESS) {
|
||||
fido_log_debug("%s: IORegistryEntryGetRegistryEntryID",
|
||||
__func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (asprintf(&path, "%s%llu", IOREG, (unsigned long long)id) == -1) {
|
||||
fido_log_error(errno, "%s: asprintf", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (path);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_fido(IOHIDDeviceRef dev)
|
||||
{
|
||||
char buf[32];
|
||||
uint32_t usage_page;
|
||||
|
||||
if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey),
|
||||
(int32_t *)&usage_page) < 0 || usage_page != 0xf1d0)
|
||||
return (false);
|
||||
|
||||
if (get_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)) < 0) {
|
||||
fido_log_debug("%s: get_utf8 transport", __func__);
|
||||
return (false);
|
||||
}
|
||||
|
||||
#ifndef FIDO_HID_ANY
|
||||
if (strcasecmp(buf, "usb") != 0) {
|
||||
fido_log_debug("%s: transport", __func__);
|
||||
return (false);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
|
||||
{
|
||||
memset(di, 0, sizeof(*di));
|
||||
|
||||
if (is_fido(dev) == false)
|
||||
return (-1);
|
||||
|
||||
if (get_id(dev, &di->vendor_id, &di->product_id) < 0 ||
|
||||
get_str(dev, &di->manufacturer, &di->product) < 0 ||
|
||||
(di->path = get_path(dev)) == NULL) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
IOHIDManagerRef manager = NULL;
|
||||
CFSetRef devset = NULL;
|
||||
size_t devcnt;
|
||||
CFIndex n;
|
||||
IOHIDDeviceRef *devs = NULL;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
|
||||
if (devlist == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if ((manager = IOHIDManagerCreate(kCFAllocatorDefault,
|
||||
kIOHIDManagerOptionNone)) == NULL) {
|
||||
fido_log_debug("%s: IOHIDManagerCreate", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
IOHIDManagerSetDeviceMatching(manager, NULL);
|
||||
|
||||
if ((devset = IOHIDManagerCopyDevices(manager)) == NULL) {
|
||||
fido_log_debug("%s: IOHIDManagerCopyDevices", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((n = CFSetGetCount(devset)) < 0) {
|
||||
fido_log_debug("%s: CFSetGetCount", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
devcnt = (size_t)n;
|
||||
|
||||
if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) {
|
||||
fido_log_debug("%s: calloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
CFSetGetValues(devset, (void *)devs);
|
||||
|
||||
for (size_t i = 0; i < devcnt; i++) {
|
||||
if (copy_info(&devlist[*olen], devs[i]) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (manager != NULL)
|
||||
CFRelease(manager);
|
||||
if (devset != NULL)
|
||||
CFRelease(devset);
|
||||
|
||||
free(devs);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static void
|
||||
report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
|
||||
uint32_t id, uint8_t *ptr, CFIndex len)
|
||||
{
|
||||
struct hid_osx *ctx = context;
|
||||
ssize_t r;
|
||||
|
||||
(void)dev;
|
||||
|
||||
if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
|
||||
id != 0 || len < 0 || (size_t)len != ctx->report_in_len) {
|
||||
fido_log_debug("%s: io error", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != (size_t)len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, (size_t)len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
removal_callback(void *context, IOReturn result, void *sender)
|
||||
{
|
||||
(void)context;
|
||||
(void)result;
|
||||
(void)sender;
|
||||
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}
|
||||
|
||||
static int
|
||||
set_nonblock(int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
fido_log_error(errno, "%s: fcntl F_GETFL", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
fido_log_error(errno, "%s: fcntl F_SETFL", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
disable_sigpipe(int fd)
|
||||
{
|
||||
int disabled = 1;
|
||||
|
||||
if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) {
|
||||
fido_log_error(errno, "%s: fcntl F_SETNOSIGPIPE", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static io_registry_entry_t
|
||||
get_ioreg_entry(const char *path)
|
||||
{
|
||||
uint64_t id;
|
||||
|
||||
if (strncmp(path, IOREG, strlen(IOREG)) != 0)
|
||||
return (IORegistryEntryFromPath(kIOMainPortDefault, path));
|
||||
|
||||
if (fido_to_uint64(path + strlen(IOREG), 10, &id) == -1) {
|
||||
fido_log_debug("%s: fido_to_uint64", __func__);
|
||||
return (MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
return (IOServiceGetMatchingService(kIOMainPortDefault,
|
||||
IORegistryEntryIDMatching(id)));
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_osx *ctx;
|
||||
io_registry_entry_t entry = MACH_PORT_NULL;
|
||||
char loop_id[32];
|
||||
int ok = -1;
|
||||
int r;
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
|
||||
fido_log_debug("%s: calloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ctx->report_pipe[0] = -1;
|
||||
ctx->report_pipe[1] = -1;
|
||||
|
||||
if (pipe(ctx->report_pipe) == -1) {
|
||||
fido_log_error(errno, "%s: pipe", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (set_nonblock(ctx->report_pipe[0]) < 0 ||
|
||||
set_nonblock(ctx->report_pipe[1]) < 0) {
|
||||
fido_log_debug("%s: set_nonblock", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (disable_sigpipe(ctx->report_pipe[1]) < 0) {
|
||||
fido_log_debug("%s: disable_sigpipe", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((entry = get_ioreg_entry(path)) == MACH_PORT_NULL) {
|
||||
fido_log_debug("%s: get_ioreg_entry: %s", __func__, path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ctx->ref = IOHIDDeviceCreate(kCFAllocatorDefault,
|
||||
entry)) == NULL) {
|
||||
fido_log_debug("%s: IOHIDDeviceCreate", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (get_report_len(ctx->ref, 0, &ctx->report_in_len) < 0 ||
|
||||
get_report_len(ctx->ref, 1, &ctx->report_out_len) < 0) {
|
||||
fido_log_debug("%s: get_report_len", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ctx->report_in_len > sizeof(ctx->report)) {
|
||||
fido_log_debug("%s: report_in_len=%zu", __func__,
|
||||
ctx->report_in_len);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (IOHIDDeviceOpen(ctx->ref,
|
||||
kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
|
||||
fido_log_debug("%s: IOHIDDeviceOpen", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p",
|
||||
(void *)ctx->ref)) < 0 || (size_t)r >= sizeof(loop_id)) {
|
||||
fido_log_debug("%s: snprintf", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ctx->loop_id = CFStringCreateWithCString(NULL, loop_id,
|
||||
kCFStringEncodingASCII)) == NULL) {
|
||||
fido_log_debug("%s: CFStringCreateWithCString", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
|
||||
(long)ctx->report_in_len, &report_callback, ctx);
|
||||
IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx);
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (entry != MACH_PORT_NULL)
|
||||
IOObjectRelease(entry);
|
||||
|
||||
if (ok < 0 && ctx != NULL) {
|
||||
if (ctx->ref != NULL)
|
||||
CFRelease(ctx->ref);
|
||||
if (ctx->loop_id != NULL)
|
||||
CFRelease(ctx->loop_id);
|
||||
if (ctx->report_pipe[0] != -1)
|
||||
close(ctx->report_pipe[0]);
|
||||
if (ctx->report_pipe[1] != -1)
|
||||
close(ctx->report_pipe[1]);
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_osx *ctx = handle;
|
||||
|
||||
IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
|
||||
(long)ctx->report_in_len, NULL, ctx);
|
||||
IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, ctx);
|
||||
|
||||
if (IOHIDDeviceClose(ctx->ref,
|
||||
kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess)
|
||||
fido_log_debug("%s: IOHIDDeviceClose", __func__);
|
||||
|
||||
CFRelease(ctx->ref);
|
||||
CFRelease(ctx->loop_id);
|
||||
|
||||
explicit_bzero(ctx->report, sizeof(ctx->report));
|
||||
close(ctx->report_pipe[0]);
|
||||
close(ctx->report_pipe[1]);
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
(void)handle;
|
||||
(void)sigmask;
|
||||
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_io_loop(struct hid_osx *ctx, int ms)
|
||||
{
|
||||
IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetCurrent(),
|
||||
ctx->loop_id);
|
||||
|
||||
if (ms == -1)
|
||||
ms = 5000; /* wait 5 seconds by default */
|
||||
|
||||
CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);
|
||||
|
||||
IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetCurrent(),
|
||||
ctx->loop_id);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_osx *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
explicit_bzero(buf, len);
|
||||
explicit_bzero(ctx->report, sizeof(ctx->report));
|
||||
|
||||
if (len != ctx->report_in_len || len > sizeof(ctx->report)) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* check for pending frame */
|
||||
if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
schedule_io_loop(ctx, ms);
|
||||
|
||||
if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_osx *ctx = handle;
|
||||
|
||||
if (len != ctx->report_out_len + 1 || len > LONG_MAX) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (IOHIDDeviceSetReport(ctx->ref, kIOHIDReportTypeOutput, 0, buf + 1,
|
||||
(long)(len - 1)) != kIOReturnSuccess) {
|
||||
fido_log_debug("%s: IOHIDDeviceSetReport", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_osx *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_osx *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len);
|
||||
}
|
||||
76
src/hid_unix.c
Normal file
76
src/hid_unix.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#define ppoll pollts
|
||||
#endif
|
||||
|
||||
int
|
||||
fido_hid_unix_open(const char *path)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
if ((fd = open(path, O_RDWR)) == -1) {
|
||||
if (errno != ENOENT && errno != ENXIO)
|
||||
fido_log_error(errno, "%s: open %s", __func__, path);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) == -1) {
|
||||
fido_log_error(errno, "%s: fstat %s", __func__, path);
|
||||
if (close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (S_ISCHR(st.st_mode) == 0) {
|
||||
fido_log_debug("%s: S_ISCHR %s", __func__, path);
|
||||
if (close(fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_unix_wait(int fd, int ms, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct pollfd pfd;
|
||||
int r;
|
||||
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.events = POLLIN;
|
||||
pfd.fd = fd;
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
return (0);
|
||||
#endif
|
||||
if (ms > -1) {
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
}
|
||||
|
||||
if ((r = ppoll(&pfd, 1, ms > -1 ? &ts : NULL, sigmask)) < 1) {
|
||||
if (r == -1)
|
||||
fido_log_error(errno, "%s: ppoll", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
571
src/hid_win.c
Normal file
571
src/hid_win.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* Copyright (c) 2019-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 <fcntl.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <initguid.h>
|
||||
#include <devpkey.h>
|
||||
#include <devpropdef.h>
|
||||
#include <hidclass.h>
|
||||
#include <hidsdi.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 6
|
||||
WINSETUPAPI WINBOOL WINAPI SetupDiGetDevicePropertyW(HDEVINFO,
|
||||
PSP_DEVINFO_DATA, const DEVPROPKEY *, DEVPROPTYPE *, PBYTE,
|
||||
DWORD, PDWORD, DWORD);
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) && __MINGW64_VERSION_MAJOR < 8
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97,
|
||||
0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8);
|
||||
#endif
|
||||
|
||||
struct hid_win {
|
||||
HANDLE dev;
|
||||
OVERLAPPED overlap;
|
||||
int report_pending;
|
||||
size_t report_in_len;
|
||||
size_t report_out_len;
|
||||
unsigned char report[1 + CTAP_MAX_REPORT_LEN];
|
||||
};
|
||||
|
||||
static bool
|
||||
is_fido(HANDLE dev)
|
||||
{
|
||||
PHIDP_PREPARSED_DATA data = NULL;
|
||||
HIDP_CAPS caps;
|
||||
int fido = 0;
|
||||
|
||||
if (HidD_GetPreparsedData(dev, &data) == false) {
|
||||
fido_log_debug("%s: HidD_GetPreparsedData", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
|
||||
fido_log_debug("%s: HidP_GetCaps", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fido = (uint16_t)caps.UsagePage == 0xf1d0;
|
||||
fail:
|
||||
if (data != NULL)
|
||||
HidD_FreePreparsedData(data);
|
||||
|
||||
return (fido);
|
||||
}
|
||||
|
||||
static int
|
||||
get_report_len(HANDLE dev, int dir, size_t *report_len)
|
||||
{
|
||||
PHIDP_PREPARSED_DATA data = NULL;
|
||||
HIDP_CAPS caps;
|
||||
USHORT v;
|
||||
int ok = -1;
|
||||
|
||||
if (HidD_GetPreparsedData(dev, &data) == false) {
|
||||
fido_log_debug("%s: HidD_GetPreparsedData/%d", __func__, dir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
|
||||
fido_log_debug("%s: HidP_GetCaps/%d", __func__, dir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dir == 0)
|
||||
v = caps.InputReportByteLength;
|
||||
else
|
||||
v = caps.OutputReportByteLength;
|
||||
|
||||
if ((*report_len = (size_t)v) == 0) {
|
||||
fido_log_debug("%s: report_len == 0", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (data != NULL)
|
||||
HidD_FreePreparsedData(data);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
get_id(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
|
||||
{
|
||||
HIDD_ATTRIBUTES attr;
|
||||
|
||||
attr.Size = sizeof(attr);
|
||||
|
||||
if (HidD_GetAttributes(dev, &attr) == false) {
|
||||
fido_log_debug("%s: HidD_GetAttributes", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*vendor_id = (int16_t)attr.VendorID;
|
||||
*product_id = (int16_t)attr.ProductID;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_manufacturer(HANDLE dev, char **manufacturer)
|
||||
{
|
||||
wchar_t buf[512];
|
||||
int utf8_len;
|
||||
int ok = -1;
|
||||
|
||||
*manufacturer = NULL;
|
||||
|
||||
if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
|
||||
fido_log_debug("%s: HidD_GetManufacturerString", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
|
||||
-1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
|
||||
fido_log_debug("%s: WideCharToMultiByte", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((*manufacturer = malloc((size_t)utf8_len)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
|
||||
*manufacturer, utf8_len, NULL, NULL) != utf8_len) {
|
||||
fido_log_debug("%s: WideCharToMultiByte", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ok < 0) {
|
||||
free(*manufacturer);
|
||||
*manufacturer = NULL;
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
get_product(HANDLE dev, char **product)
|
||||
{
|
||||
wchar_t buf[512];
|
||||
int utf8_len;
|
||||
int ok = -1;
|
||||
|
||||
*product = NULL;
|
||||
|
||||
if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
|
||||
fido_log_debug("%s: HidD_GetProductString", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
|
||||
-1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
|
||||
fido_log_debug("%s: WideCharToMultiByte", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((*product = malloc((size_t)utf8_len)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
|
||||
*product, utf8_len, NULL, NULL) != utf8_len) {
|
||||
fido_log_debug("%s: WideCharToMultiByte", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ok < 0) {
|
||||
free(*product);
|
||||
*product = NULL;
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_path(HDEVINFO devinfo, SP_DEVICE_INTERFACE_DATA *ifdata)
|
||||
{
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
|
||||
char *path = NULL;
|
||||
DWORD len = 0;
|
||||
|
||||
/*
|
||||
* "Get the required buffer size. Call SetupDiGetDeviceInterfaceDetail
|
||||
* with a NULL DeviceInterfaceDetailData pointer, a
|
||||
* DeviceInterfaceDetailDataSize of zero, and a valid RequiredSize
|
||||
* variable. In response to such a call, this function returns the
|
||||
* required buffer size at RequiredSize and fails with GetLastError
|
||||
* returning ERROR_INSUFFICIENT_BUFFER."
|
||||
*/
|
||||
if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, NULL, 0, &len,
|
||||
NULL) != false || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ifdetail = malloc(len)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ifdetail->cbSize = sizeof(*ifdetail);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetailA(devinfo, ifdata, ifdetail, len,
|
||||
NULL, NULL) == false) {
|
||||
fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((path = strdup(ifdetail->DevicePath)) == NULL) {
|
||||
fido_log_debug("%s: strdup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
free(ifdetail);
|
||||
|
||||
return (path);
|
||||
}
|
||||
|
||||
#ifndef FIDO_HID_ANY
|
||||
static bool
|
||||
hid_ok(HDEVINFO devinfo, DWORD idx)
|
||||
{
|
||||
SP_DEVINFO_DATA devinfo_data;
|
||||
wchar_t *parent = NULL;
|
||||
DWORD parent_type = DEVPROP_TYPE_STRING;
|
||||
DWORD len = 0;
|
||||
bool ok = false;
|
||||
|
||||
memset(&devinfo_data, 0, sizeof(devinfo_data));
|
||||
devinfo_data.cbSize = sizeof(devinfo_data);
|
||||
|
||||
if (SetupDiEnumDeviceInfo(devinfo, idx, &devinfo_data) == false) {
|
||||
fido_log_debug("%s: SetupDiEnumDeviceInfo", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
|
||||
&DEVPKEY_Device_Parent, &parent_type, NULL, 0, &len, 0) != false ||
|
||||
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
fido_log_debug("%s: SetupDiGetDevicePropertyW 1", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((parent = malloc(len)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (SetupDiGetDevicePropertyW(devinfo, &devinfo_data,
|
||||
&DEVPKEY_Device_Parent, &parent_type, (PBYTE)parent, len, NULL,
|
||||
0) == false) {
|
||||
fido_log_debug("%s: SetupDiGetDevicePropertyW 2", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = wcsncmp(parent, L"USB\\", 4) == 0;
|
||||
fail:
|
||||
free(parent);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, HDEVINFO devinfo, DWORD idx,
|
||||
SP_DEVICE_INTERFACE_DATA *ifdata)
|
||||
{
|
||||
HANDLE dev = INVALID_HANDLE_VALUE;
|
||||
int ok = -1;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
|
||||
if ((di->path = get_path(devinfo, ifdata)) == NULL) {
|
||||
fido_log_debug("%s: get_path", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fido_log_debug("%s: path=%s", __func__, di->path);
|
||||
|
||||
#ifndef FIDO_HID_ANY
|
||||
if (hid_ok(devinfo, idx) == false) {
|
||||
fido_log_debug("%s: hid_ok", __func__);
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev = CreateFileA(di->path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (dev == INVALID_HANDLE_VALUE) {
|
||||
fido_log_debug("%s: CreateFileA", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (is_fido(dev) == false) {
|
||||
fido_log_debug("%s: is_fido", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (get_id(dev, &di->vendor_id, &di->product_id) < 0) {
|
||||
fido_log_debug("%s: get_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (get_manufacturer(dev, &di->manufacturer) < 0) {
|
||||
fido_log_debug("%s: get_manufacturer", __func__);
|
||||
di->manufacturer = strdup("");
|
||||
}
|
||||
|
||||
if (get_product(dev, &di->product) < 0) {
|
||||
fido_log_debug("%s: get_product", __func__);
|
||||
di->product = strdup("");
|
||||
}
|
||||
|
||||
if (di->manufacturer == NULL || di->product == NULL) {
|
||||
fido_log_debug("%s: manufacturer/product", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (dev != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(dev);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
GUID hid_guid = GUID_DEVINTERFACE_HID;
|
||||
HDEVINFO devinfo = INVALID_HANDLE_VALUE;
|
||||
SP_DEVICE_INTERFACE_DATA ifdata;
|
||||
DWORD idx;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return (FIDO_OK); /* nothing to do */
|
||||
if (devlist == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
if ((devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
|
||||
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)) == INVALID_HANDLE_VALUE) {
|
||||
fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (idx = 0; SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid,
|
||||
idx, &ifdata) == true; idx++) {
|
||||
if (copy_info(&devlist[*olen], devinfo, idx, &ifdata) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_hid_open,
|
||||
fido_hid_close,
|
||||
fido_hid_read,
|
||||
fido_hid_write,
|
||||
};
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (devinfo != INVALID_HANDLE_VALUE)
|
||||
SetupDiDestroyDeviceInfoList(devinfo);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
void *
|
||||
fido_hid_open(const char *path)
|
||||
{
|
||||
struct hid_win *ctx;
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
ctx->dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED, NULL);
|
||||
|
||||
if (ctx->dev == INVALID_HANDLE_VALUE) {
|
||||
free(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((ctx->overlap.hEvent = CreateEventA(NULL, FALSE, FALSE,
|
||||
NULL)) == NULL) {
|
||||
fido_log_debug("%s: CreateEventA", __func__);
|
||||
fido_hid_close(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (get_report_len(ctx->dev, 0, &ctx->report_in_len) < 0 ||
|
||||
get_report_len(ctx->dev, 1, &ctx->report_out_len) < 0) {
|
||||
fido_log_debug("%s: get_report_len", __func__);
|
||||
fido_hid_close(ctx);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (ctx);
|
||||
}
|
||||
|
||||
void
|
||||
fido_hid_close(void *handle)
|
||||
{
|
||||
struct hid_win *ctx = handle;
|
||||
|
||||
if (ctx->overlap.hEvent != NULL) {
|
||||
if (ctx->report_pending) {
|
||||
fido_log_debug("%s: report_pending", __func__);
|
||||
if (CancelIoEx(ctx->dev, &ctx->overlap) == 0)
|
||||
fido_log_debug("%s CancelIoEx: 0x%lx",
|
||||
__func__, (u_long)GetLastError());
|
||||
}
|
||||
CloseHandle(ctx->overlap.hEvent);
|
||||
}
|
||||
|
||||
explicit_bzero(ctx->report, sizeof(ctx->report));
|
||||
CloseHandle(ctx->dev);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
(void)handle;
|
||||
(void)sigmask;
|
||||
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct hid_win *ctx = handle;
|
||||
DWORD n;
|
||||
|
||||
if (len != ctx->report_in_len - 1 || len > sizeof(ctx->report) - 1) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (ctx->report_pending == 0) {
|
||||
memset(&ctx->report, 0, sizeof(ctx->report));
|
||||
ResetEvent(ctx->overlap.hEvent);
|
||||
if (ReadFile(ctx->dev, ctx->report, (DWORD)(len + 1), &n,
|
||||
&ctx->overlap) == 0 && GetLastError() != ERROR_IO_PENDING) {
|
||||
CancelIo(ctx->dev);
|
||||
fido_log_debug("%s: ReadFile", __func__);
|
||||
return (-1);
|
||||
}
|
||||
ctx->report_pending = 1;
|
||||
}
|
||||
|
||||
if (ms > -1 && WaitForSingleObject(ctx->overlap.hEvent,
|
||||
(DWORD)ms) != WAIT_OBJECT_0)
|
||||
return (0);
|
||||
|
||||
ctx->report_pending = 0;
|
||||
|
||||
if (GetOverlappedResult(ctx->dev, &ctx->overlap, &n, TRUE) == 0) {
|
||||
fido_log_debug("%s: GetOverlappedResult", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n != len + 1) {
|
||||
fido_log_debug("%s: expected %zu, got %zu", __func__,
|
||||
len + 1, (size_t)n);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(buf, ctx->report + 1, len);
|
||||
explicit_bzero(ctx->report, sizeof(ctx->report));
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
int
|
||||
fido_hid_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct hid_win *ctx = handle;
|
||||
OVERLAPPED overlap;
|
||||
DWORD n;
|
||||
|
||||
memset(&overlap, 0, sizeof(overlap));
|
||||
|
||||
if (len != ctx->report_out_len) {
|
||||
fido_log_debug("%s: len %zu", __func__, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (WriteFile(ctx->dev, buf, (DWORD)len, NULL, &overlap) == 0 &&
|
||||
GetLastError() != ERROR_IO_PENDING) {
|
||||
fido_log_debug("%s: WriteFile", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (GetOverlappedResult(ctx->dev, &overlap, &n, TRUE) == 0) {
|
||||
fido_log_debug("%s: GetOverlappedResult", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n != len) {
|
||||
fido_log_debug("%s: expected %zu, got %zu", __func__, len,
|
||||
(size_t)n);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return ((int)len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_in_len(void *handle)
|
||||
{
|
||||
struct hid_win *ctx = handle;
|
||||
|
||||
return (ctx->report_in_len - 1);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_hid_report_out_len(void *handle)
|
||||
{
|
||||
struct hid_win *ctx = handle;
|
||||
|
||||
return (ctx->report_out_len - 1);
|
||||
}
|
||||
647
src/info.c
Normal file
647
src/info.c
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int
|
||||
decode_string(const cbor_item_t *item, void *arg)
|
||||
{
|
||||
fido_str_array_t *a = arg;
|
||||
const size_t i = a->len;
|
||||
|
||||
/* keep ptr[x] and len consistent */
|
||||
if (cbor_string_copy(item, &a->ptr[i]) < 0) {
|
||||
fido_log_debug("%s: cbor_string_copy", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
a->len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_string_array(const cbor_item_t *item, fido_str_array_t *v)
|
||||
{
|
||||
v->ptr = NULL;
|
||||
v->len = 0;
|
||||
|
||||
if (cbor_isa_array(item) == false ||
|
||||
cbor_array_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
v->ptr = calloc(cbor_array_size(item), sizeof(char *));
|
||||
if (v->ptr == NULL)
|
||||
return (-1);
|
||||
|
||||
if (cbor_array_iter(item, v, decode_string) < 0) {
|
||||
fido_log_debug("%s: decode_string", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
|
||||
{
|
||||
if (cbor_isa_bytestring(item) == false ||
|
||||
cbor_bytestring_is_definite(item) == false ||
|
||||
cbor_bytestring_length(item) != aaguid_len) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_opt_array_t *o = arg;
|
||||
const size_t i = o->len;
|
||||
|
||||
if (cbor_decode_bool(val, NULL) < 0) {
|
||||
fido_log_debug("%s: cbor_decode_bool", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_string_copy(key, &o->name[i]) < 0) {
|
||||
fido_log_debug("%s: cbor_string_copy", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
/* keep name/value and len consistent */
|
||||
o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
|
||||
o->len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_options(const cbor_item_t *item, fido_opt_array_t *o)
|
||||
{
|
||||
o->name = NULL;
|
||||
o->value = NULL;
|
||||
o->len = 0;
|
||||
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
o->name = calloc(cbor_map_size(item), sizeof(char *));
|
||||
o->value = calloc(cbor_map_size(item), sizeof(bool));
|
||||
if (o->name == NULL || o->value == NULL)
|
||||
return (-1);
|
||||
|
||||
return (cbor_map_iter(item, o, decode_option));
|
||||
}
|
||||
|
||||
static int
|
||||
decode_protocol(const cbor_item_t *item, void *arg)
|
||||
{
|
||||
fido_byte_array_t *p = arg;
|
||||
const size_t i = p->len;
|
||||
|
||||
if (cbor_isa_uint(item) == false ||
|
||||
cbor_int_get_width(item) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* keep ptr[x] and len consistent */
|
||||
p->ptr[i] = cbor_get_uint8(item);
|
||||
p->len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
|
||||
{
|
||||
p->ptr = NULL;
|
||||
p->len = 0;
|
||||
|
||||
if (cbor_isa_array(item) == false ||
|
||||
cbor_array_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
|
||||
if (p->ptr == NULL)
|
||||
return (-1);
|
||||
|
||||
if (cbor_array_iter(item, p, decode_protocol) < 0) {
|
||||
fido_log_debug("%s: decode_protocol", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
fido_algo_t *alg = arg;
|
||||
char *name = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (cbor_string_copy(key, &name) < 0) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
ok = 0; /* ignore */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strcmp(name, "alg")) {
|
||||
if (cbor_isa_negint(val) == false ||
|
||||
cbor_get_int(val) > INT_MAX || alg->cose != 0) {
|
||||
fido_log_debug("%s: alg", __func__);
|
||||
goto out;
|
||||
}
|
||||
alg->cose = -(int)cbor_get_int(val) - 1;
|
||||
} else if (!strcmp(name, "type")) {
|
||||
if (cbor_string_copy(val, &alg->type) < 0) {
|
||||
fido_log_debug("%s: type", __func__);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
out:
|
||||
free(name);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_algorithm(const cbor_item_t *item, void *arg)
|
||||
{
|
||||
fido_algo_array_t *aa = arg;
|
||||
const size_t i = aa->len;
|
||||
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&aa->ptr[i], 0, sizeof(aa->ptr[i]));
|
||||
|
||||
if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) {
|
||||
fido_log_debug("%s: decode_algorithm_entry", __func__);
|
||||
fido_algo_free(&aa->ptr[i]);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* keep ptr[x] and len consistent */
|
||||
aa->len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa)
|
||||
{
|
||||
aa->ptr = NULL;
|
||||
aa->len = 0;
|
||||
|
||||
if (cbor_isa_array(item) == false ||
|
||||
cbor_array_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t));
|
||||
if (aa->ptr == NULL)
|
||||
return (-1);
|
||||
|
||||
if (cbor_array_iter(item, aa, decode_algorithm) < 0) {
|
||||
fido_log_debug("%s: decode_algorithm", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_cert(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_cert_array_t *c = arg;
|
||||
const size_t i = c->len;
|
||||
|
||||
if (cbor_is_int(val) == false) {
|
||||
fido_log_debug("%s: cbor_is_int", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_string_copy(key, &c->name[i]) < 0) {
|
||||
fido_log_debug("%s: cbor_string_copy", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
/* keep name/value and len consistent */
|
||||
c->value[i] = cbor_get_int(val);
|
||||
c->len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_certs(const cbor_item_t *item, fido_cert_array_t *c)
|
||||
{
|
||||
c->name = NULL;
|
||||
c->value = NULL;
|
||||
c->len = 0;
|
||||
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
c->name = calloc(cbor_map_size(item), sizeof(char *));
|
||||
c->value = calloc(cbor_map_size(item), sizeof(uint64_t));
|
||||
if (c->name == NULL || c->value == NULL)
|
||||
return (-1);
|
||||
|
||||
return (cbor_map_iter(item, c, decode_cert));
|
||||
}
|
||||
|
||||
static int
|
||||
parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_cbor_info_t *ci = arg;
|
||||
uint64_t x;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* versions */
|
||||
return (decode_string_array(val, &ci->versions));
|
||||
case 2: /* extensions */
|
||||
return (decode_string_array(val, &ci->extensions));
|
||||
case 3: /* aaguid */
|
||||
return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
|
||||
case 4: /* options */
|
||||
return (decode_options(val, &ci->options));
|
||||
case 5: /* maxMsgSize */
|
||||
return (cbor_decode_uint64(val, &ci->maxmsgsiz));
|
||||
case 6: /* pinProtocols */
|
||||
return (decode_protocols(val, &ci->protocols));
|
||||
case 7: /* maxCredentialCountInList */
|
||||
return (cbor_decode_uint64(val, &ci->maxcredcntlst));
|
||||
case 8: /* maxCredentialIdLength */
|
||||
return (cbor_decode_uint64(val, &ci->maxcredidlen));
|
||||
case 9: /* transports */
|
||||
return (decode_string_array(val, &ci->transports));
|
||||
case 10: /* algorithms */
|
||||
return (decode_algorithms(val, &ci->algorithms));
|
||||
case 11: /* maxSerializedLargeBlobArray */
|
||||
return (cbor_decode_uint64(val, &ci->maxlargeblob));
|
||||
case 12: /* forcePINChange */
|
||||
return (cbor_decode_bool(val, &ci->new_pin_reqd));
|
||||
case 13: /* minPINLength */
|
||||
return (cbor_decode_uint64(val, &ci->minpinlen));
|
||||
case 14: /* fwVersion */
|
||||
return (cbor_decode_uint64(val, &ci->fwversion));
|
||||
case 15: /* maxCredBlobLen */
|
||||
return (cbor_decode_uint64(val, &ci->maxcredbloblen));
|
||||
case 16: /* maxRPIDsForSetMinPINLength */
|
||||
return (cbor_decode_uint64(val, &ci->maxrpid_minlen));
|
||||
case 17: /* preferredPlatformUvAttempts */
|
||||
return (cbor_decode_uint64(val, &ci->uv_attempts));
|
||||
case 18: /* uvModality */
|
||||
return (cbor_decode_uint64(val, &ci->uv_modality));
|
||||
case 19: /* certifications */
|
||||
return (decode_certs(val, &ci->certs));
|
||||
case 20: /* remainingDiscoverableCredentials */
|
||||
if (cbor_decode_uint64(val, &x) < 0 || x > INT64_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
ci->rk_remaining = (int64_t)x;
|
||||
return (0);
|
||||
default: /* ignore */
|
||||
fido_log_debug("%s: cbor type: 0x%02x", __func__, cbor_get_uint8(key));
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
|
||||
|
||||
fido_log_debug("%s: dev=%p", __func__, (void *)dev);
|
||||
|
||||
if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
return (FIDO_ERR_TX);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
|
||||
(void *)ci, *ms);
|
||||
|
||||
fido_cbor_info_reset(ci);
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = cbor_parse_reply(msg, (size_t)msglen, ci, parse_reply_element);
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef USE_WINHELLO
|
||||
if (dev->flags & FIDO_DEV_WINHELLO)
|
||||
return (fido_winhello_get_cbor_info(dev, ci));
|
||||
#endif
|
||||
if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK ||
|
||||
(r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
|
||||
}
|
||||
|
||||
/*
|
||||
* get/set functions for fido_cbor_info_t; always at the end of the file
|
||||
*/
|
||||
|
||||
fido_cbor_info_t *
|
||||
fido_cbor_info_new(void)
|
||||
{
|
||||
fido_cbor_info_t *ci;
|
||||
|
||||
if ((ci = calloc(1, sizeof(fido_cbor_info_t))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
fido_cbor_info_reset(ci);
|
||||
|
||||
return (ci);
|
||||
}
|
||||
|
||||
void
|
||||
fido_cbor_info_reset(fido_cbor_info_t *ci)
|
||||
{
|
||||
fido_str_array_free(&ci->versions);
|
||||
fido_str_array_free(&ci->extensions);
|
||||
fido_str_array_free(&ci->transports);
|
||||
fido_opt_array_free(&ci->options);
|
||||
fido_byte_array_free(&ci->protocols);
|
||||
fido_algo_array_free(&ci->algorithms);
|
||||
fido_cert_array_free(&ci->certs);
|
||||
ci->rk_remaining = -1;
|
||||
}
|
||||
|
||||
void
|
||||
fido_cbor_info_free(fido_cbor_info_t **ci_p)
|
||||
{
|
||||
fido_cbor_info_t *ci;
|
||||
|
||||
if (ci_p == NULL || (ci = *ci_p) == NULL)
|
||||
return;
|
||||
fido_cbor_info_reset(ci);
|
||||
free(ci);
|
||||
*ci_p = NULL;
|
||||
}
|
||||
|
||||
char **
|
||||
fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->versions.ptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->versions.len);
|
||||
}
|
||||
|
||||
char **
|
||||
fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->extensions.ptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->extensions.len);
|
||||
}
|
||||
|
||||
char **
|
||||
fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->transports.ptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->transports.len);
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->aaguid);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (sizeof(ci->aaguid));
|
||||
}
|
||||
|
||||
char **
|
||||
fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->options.name);
|
||||
}
|
||||
|
||||
const bool *
|
||||
fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->options.value);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_options_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->options.len);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxcredbloblen);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxmsgsiz);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxcredcntlst);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxcredidlen);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxlargeblob);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->fwversion);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_minpinlen(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->minpinlen);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_maxrpid_minpinlen(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->maxrpid_minlen);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_uv_attempts(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->uv_attempts);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
fido_cbor_info_uv_modality(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->uv_modality);
|
||||
}
|
||||
|
||||
int64_t
|
||||
fido_cbor_info_rk_remaining(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->rk_remaining);
|
||||
}
|
||||
|
||||
const uint8_t *
|
||||
fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->protocols.ptr);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->protocols.len);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->algorithms.len);
|
||||
}
|
||||
|
||||
const char *
|
||||
fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx)
|
||||
{
|
||||
if (idx >= ci->algorithms.len)
|
||||
return (NULL);
|
||||
|
||||
return (ci->algorithms.ptr[idx].type);
|
||||
}
|
||||
|
||||
int
|
||||
fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx)
|
||||
{
|
||||
if (idx >= ci->algorithms.len)
|
||||
return (0);
|
||||
|
||||
return (ci->algorithms.ptr[idx].cose);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_cbor_info_new_pin_required(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->new_pin_reqd);
|
||||
}
|
||||
|
||||
char **
|
||||
fido_cbor_info_certs_name_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->certs.name);
|
||||
}
|
||||
|
||||
const uint64_t *
|
||||
fido_cbor_info_certs_value_ptr(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->certs.value);
|
||||
}
|
||||
|
||||
size_t
|
||||
fido_cbor_info_certs_len(const fido_cbor_info_t *ci)
|
||||
{
|
||||
return (ci->certs.len);
|
||||
}
|
||||
356
src/io.c
Normal file
356
src/io.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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 "packed.h"
|
||||
|
||||
PACKED_TYPE(frame_t,
|
||||
struct frame {
|
||||
uint32_t cid; /* channel id */
|
||||
union {
|
||||
uint8_t type;
|
||||
struct {
|
||||
uint8_t cmd;
|
||||
uint8_t bcnth;
|
||||
uint8_t bcntl;
|
||||
uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_INIT_HEADER_LEN];
|
||||
} init;
|
||||
struct {
|
||||
uint8_t seq;
|
||||
uint8_t data[CTAP_MAX_REPORT_LEN - CTAP_CONT_HEADER_LEN];
|
||||
} cont;
|
||||
} body;
|
||||
})
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) > (y) ? (y) : (x))
|
||||
#endif
|
||||
|
||||
static int
|
||||
tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
int n;
|
||||
|
||||
if (fido_time_now(&ts) != 0)
|
||||
return (-1);
|
||||
|
||||
n = d->io.write(d->io_handle, pkt, len);
|
||||
|
||||
if (fido_time_delta(&ts, ms) != 0)
|
||||
return (-1);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
static int
|
||||
tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
|
||||
{
|
||||
struct frame *fp;
|
||||
unsigned char pkt[sizeof(*fp) + 1];
|
||||
const size_t len = d->tx_len + 1;
|
||||
int n;
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
fp = (struct frame *)(pkt + 1);
|
||||
fp->cid = d->cid;
|
||||
fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
|
||||
|
||||
if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
|
||||
(size_t)n != len)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
|
||||
{
|
||||
struct frame *fp;
|
||||
unsigned char pkt[sizeof(*fp) + 1];
|
||||
const size_t len = d->tx_len + 1;
|
||||
int n;
|
||||
|
||||
if (d->tx_len - CTAP_INIT_HEADER_LEN > sizeof(fp->body.init.data))
|
||||
return (0);
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
fp = (struct frame *)(pkt + 1);
|
||||
fp->cid = d->cid;
|
||||
fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
|
||||
fp->body.init.bcnth = (count >> 8) & 0xff;
|
||||
fp->body.init.bcntl = count & 0xff;
|
||||
count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
|
||||
memcpy(&fp->body.init.data, buf, count);
|
||||
|
||||
if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
|
||||
(size_t)n != len)
|
||||
return (0);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
static size_t
|
||||
tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
|
||||
{
|
||||
struct frame *fp;
|
||||
unsigned char pkt[sizeof(*fp) + 1];
|
||||
const size_t len = d->tx_len + 1;
|
||||
int n;
|
||||
|
||||
if (d->tx_len - CTAP_CONT_HEADER_LEN > sizeof(fp->body.cont.data))
|
||||
return (0);
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
fp = (struct frame *)(pkt + 1);
|
||||
fp->cid = d->cid;
|
||||
fp->body.cont.seq = seq;
|
||||
count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
|
||||
memcpy(&fp->body.cont.data, buf, count);
|
||||
|
||||
if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
|
||||
(size_t)n != len)
|
||||
return (0);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
static int
|
||||
tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
|
||||
{
|
||||
size_t n, sent;
|
||||
|
||||
if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
|
||||
fido_log_debug("%s: tx_preamble", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (uint8_t seq = 0; sent < count; sent += n) {
|
||||
if (seq & 0x80) {
|
||||
fido_log_debug("%s: seq & 0x80", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((n = tx_frame(d, seq++, buf + sent, count - sent,
|
||||
ms)) == 0) {
|
||||
fido_log_debug("%s: tx_frame", __func__);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
int n;
|
||||
|
||||
if (fido_time_now(&ts) != 0)
|
||||
return (-1);
|
||||
|
||||
n = d->transport.tx(d, cmd, buf, count);
|
||||
|
||||
if (fido_time_delta(&ts, ms) != 0)
|
||||
return (-1);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
|
||||
{
|
||||
fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
|
||||
fido_log_xxd(buf, count, "%s", __func__);
|
||||
|
||||
if (d->transport.tx != NULL)
|
||||
return (transport_tx(d, cmd, buf, count, ms));
|
||||
if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
|
||||
fido_log_debug("%s: invalid argument", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
|
||||
}
|
||||
|
||||
static int
|
||||
rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
int n;
|
||||
|
||||
memset(fp, 0, sizeof(*fp));
|
||||
|
||||
if (fido_time_now(&ts) != 0)
|
||||
return (-1);
|
||||
|
||||
if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
|
||||
(unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
|
||||
return (-1);
|
||||
|
||||
return (fido_time_delta(&ts, ms));
|
||||
}
|
||||
|
||||
static int
|
||||
rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
|
||||
{
|
||||
do {
|
||||
if (rx_frame(d, fp, ms) < 0)
|
||||
return (-1);
|
||||
#ifdef FIDO_FUZZ
|
||||
fp->cid = d->cid;
|
||||
#endif
|
||||
} while (fp->cid != d->cid || (fp->cid == d->cid &&
|
||||
fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
|
||||
|
||||
if (d->rx_len > sizeof(*fp))
|
||||
return (-1);
|
||||
|
||||
fido_log_xxd(fp, d->rx_len, "%s", __func__);
|
||||
#ifdef FIDO_FUZZ
|
||||
fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
|
||||
#endif
|
||||
|
||||
if (fp->cid != d->cid || fp->body.init.cmd != (CTAP_FRAME_INIT | cmd)) {
|
||||
fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
|
||||
__func__, fp->cid, d->cid, fp->body.init.cmd, cmd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
|
||||
{
|
||||
struct frame f;
|
||||
size_t r, payload_len, init_data_len, cont_data_len;
|
||||
|
||||
if (d->rx_len <= CTAP_INIT_HEADER_LEN ||
|
||||
d->rx_len <= CTAP_CONT_HEADER_LEN)
|
||||
return (-1);
|
||||
|
||||
init_data_len = d->rx_len - CTAP_INIT_HEADER_LEN;
|
||||
cont_data_len = d->rx_len - CTAP_CONT_HEADER_LEN;
|
||||
|
||||
if (init_data_len > sizeof(f.body.init.data) ||
|
||||
cont_data_len > sizeof(f.body.cont.data))
|
||||
return (-1);
|
||||
|
||||
if (rx_preamble(d, cmd, &f, ms) < 0) {
|
||||
fido_log_debug("%s: rx_preamble", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
payload_len = (size_t)((f.body.init.bcnth << 8) | f.body.init.bcntl);
|
||||
fido_log_debug("%s: payload_len=%zu", __func__, payload_len);
|
||||
|
||||
if (count < payload_len) {
|
||||
fido_log_debug("%s: count < payload_len", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (payload_len < init_data_len) {
|
||||
memcpy(buf, f.body.init.data, payload_len);
|
||||
return ((int)payload_len);
|
||||
}
|
||||
|
||||
memcpy(buf, f.body.init.data, init_data_len);
|
||||
r = init_data_len;
|
||||
|
||||
for (int seq = 0; r < payload_len; seq++) {
|
||||
if (rx_frame(d, &f, ms) < 0) {
|
||||
fido_log_debug("%s: rx_frame", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
fido_log_xxd(&f, d->rx_len, "%s", __func__);
|
||||
#ifdef FIDO_FUZZ
|
||||
f.cid = d->cid;
|
||||
f.body.cont.seq = (uint8_t)seq;
|
||||
#endif
|
||||
|
||||
if (f.cid != d->cid || f.body.cont.seq != seq) {
|
||||
fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
|
||||
__func__, f.cid, d->cid, f.body.cont.seq, seq);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (payload_len - r > cont_data_len) {
|
||||
memcpy(buf + r, f.body.cont.data, cont_data_len);
|
||||
r += cont_data_len;
|
||||
} else {
|
||||
memcpy(buf + r, f.body.cont.data, payload_len - r);
|
||||
r += payload_len - r; /* break */
|
||||
}
|
||||
}
|
||||
|
||||
return ((int)r);
|
||||
}
|
||||
|
||||
static int
|
||||
transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
int n;
|
||||
|
||||
if (fido_time_now(&ts) != 0)
|
||||
return (-1);
|
||||
|
||||
n = d->transport.rx(d, cmd, buf, count, *ms);
|
||||
|
||||
if (fido_time_delta(&ts, ms) != 0)
|
||||
return (-1);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
|
||||
{
|
||||
int n;
|
||||
|
||||
fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
|
||||
cmd, *ms);
|
||||
|
||||
if (d->transport.rx != NULL)
|
||||
return (transport_rx(d, cmd, buf, count, ms));
|
||||
if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
|
||||
fido_log_debug("%s: invalid argument", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((n = rx(d, cmd, buf, count, ms)) >= 0)
|
||||
fido_log_xxd(buf, (size_t)n, "%s", __func__);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
fido_rx_cbor_status(fido_dev_t *d, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(d, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0 ||
|
||||
(size_t)msglen < 1) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = msg[0];
|
||||
out:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
65
src/iso7816.c
Normal file
65
src/iso7816.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
iso7816_apdu_t *
|
||||
iso7816_new(uint8_t cla, uint8_t ins, uint8_t p1, uint16_t payload_len)
|
||||
{
|
||||
iso7816_apdu_t *apdu;
|
||||
size_t alloc_len;
|
||||
|
||||
alloc_len = sizeof(iso7816_apdu_t) + payload_len + 2; /* le1 le2 */
|
||||
if ((apdu = calloc(1, alloc_len)) == NULL)
|
||||
return NULL;
|
||||
apdu->alloc_len = alloc_len;
|
||||
apdu->payload_len = payload_len;
|
||||
apdu->payload_ptr = apdu->payload;
|
||||
apdu->header.cla = cla;
|
||||
apdu->header.ins = ins;
|
||||
apdu->header.p1 = p1;
|
||||
apdu->header.lc2 = (uint8_t)((payload_len >> 8) & 0xff);
|
||||
apdu->header.lc3 = (uint8_t)(payload_len & 0xff);
|
||||
|
||||
return apdu;
|
||||
}
|
||||
|
||||
void
|
||||
iso7816_free(iso7816_apdu_t **apdu_p)
|
||||
{
|
||||
iso7816_apdu_t *apdu;
|
||||
|
||||
if (apdu_p == NULL || (apdu = *apdu_p) == NULL)
|
||||
return;
|
||||
freezero(apdu, apdu->alloc_len);
|
||||
*apdu_p = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
|
||||
{
|
||||
if (cnt > apdu->payload_len || cnt > UINT16_MAX)
|
||||
return -1;
|
||||
memcpy(apdu->payload_ptr, buf, cnt);
|
||||
apdu->payload_ptr += cnt;
|
||||
apdu->payload_len = (uint16_t)(apdu->payload_len - cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const unsigned char *
|
||||
iso7816_ptr(const iso7816_apdu_t *apdu)
|
||||
{
|
||||
return (const unsigned char *)&apdu->header;
|
||||
}
|
||||
|
||||
size_t
|
||||
iso7816_len(const iso7816_apdu_t *apdu)
|
||||
{
|
||||
return apdu->alloc_len - offsetof(iso7816_apdu_t, header) -
|
||||
(sizeof(iso7816_apdu_t) - offsetof(iso7816_apdu_t, payload));
|
||||
}
|
||||
49
src/iso7816.h
Normal file
49
src/iso7816.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _ISO7816_H
|
||||
#define _ISO7816_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "packed.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
PACKED_TYPE(iso7816_header_t,
|
||||
struct iso7816_header {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t lc1;
|
||||
uint8_t lc2;
|
||||
uint8_t lc3;
|
||||
})
|
||||
|
||||
typedef struct iso7816_apdu {
|
||||
size_t alloc_len;
|
||||
uint16_t payload_len;
|
||||
uint8_t *payload_ptr;
|
||||
iso7816_header_t header;
|
||||
uint8_t payload[];
|
||||
} iso7816_apdu_t;
|
||||
|
||||
const unsigned char *iso7816_ptr(const iso7816_apdu_t *);
|
||||
int iso7816_add(iso7816_apdu_t *, const void *, size_t);
|
||||
iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint8_t, uint16_t);
|
||||
size_t iso7816_len(const iso7816_apdu_t *);
|
||||
void iso7816_free(iso7816_apdu_t **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_ISO7816_H */
|
||||
902
src/largeblob.c
Normal file
902
src/largeblob.c
Normal file
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
* 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 <openssl/sha.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#define LARGEBLOB_DIGEST_LENGTH 16
|
||||
#define LARGEBLOB_NONCE_LENGTH 12
|
||||
#define LARGEBLOB_TAG_LENGTH 16
|
||||
|
||||
typedef struct largeblob {
|
||||
size_t origsiz;
|
||||
fido_blob_t ciphertext;
|
||||
fido_blob_t nonce;
|
||||
} largeblob_t;
|
||||
|
||||
static largeblob_t *
|
||||
largeblob_new(void)
|
||||
{
|
||||
return calloc(1, sizeof(largeblob_t));
|
||||
}
|
||||
|
||||
static void
|
||||
largeblob_reset(largeblob_t *blob)
|
||||
{
|
||||
fido_blob_reset(&blob->ciphertext);
|
||||
fido_blob_reset(&blob->nonce);
|
||||
blob->origsiz = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
largeblob_free(largeblob_t **blob_ptr)
|
||||
{
|
||||
largeblob_t *blob;
|
||||
|
||||
if (blob_ptr == NULL || (blob = *blob_ptr) == NULL)
|
||||
return;
|
||||
largeblob_reset(blob);
|
||||
free(blob);
|
||||
*blob_ptr = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_aad(fido_blob_t *aad, uint64_t size)
|
||||
{
|
||||
uint8_t buf[4 + sizeof(uint64_t)];
|
||||
|
||||
buf[0] = 0x62; /* b */
|
||||
buf[1] = 0x6c; /* l */
|
||||
buf[2] = 0x6f; /* o */
|
||||
buf[3] = 0x62; /* b */
|
||||
size = htole64(size);
|
||||
memcpy(&buf[4], &size, sizeof(uint64_t));
|
||||
|
||||
return fido_blob_set(aad, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static fido_blob_t *
|
||||
largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key)
|
||||
{
|
||||
fido_blob_t *plaintext = NULL, *aad = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((plaintext = fido_blob_new()) == NULL ||
|
||||
(aad = fido_blob_new()) == NULL) {
|
||||
fido_log_debug("%s: fido_blob_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (largeblob_aad(aad, blob->origsiz) < 0) {
|
||||
fido_log_debug("%s: largeblob_aad", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext,
|
||||
plaintext) < 0) {
|
||||
fido_log_debug("%s: aes256_gcm_dec", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
fido_blob_free(&aad);
|
||||
|
||||
if (ok < 0)
|
||||
fido_blob_free(&plaintext);
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_get_nonce(largeblob_t *blob)
|
||||
{
|
||||
uint8_t buf[LARGEBLOB_NONCE_LENGTH];
|
||||
int ok = -1;
|
||||
|
||||
if (fido_get_random(buf, sizeof(buf)) < 0) {
|
||||
fido_log_debug("%s: fido_get_random", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
explicit_bzero(buf, sizeof(buf));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_seal(largeblob_t *blob, const fido_blob_t *body,
|
||||
const fido_blob_t *key)
|
||||
{
|
||||
fido_blob_t *plaintext = NULL, *aad = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((plaintext = fido_blob_new()) == NULL ||
|
||||
(aad = fido_blob_new()) == NULL) {
|
||||
fido_log_debug("%s: fido_blob_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (fido_compress(plaintext, body) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_compress", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (largeblob_aad(aad, body->len) < 0) {
|
||||
fido_log_debug("%s: largeblob_aad", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (largeblob_get_nonce(blob) < 0) {
|
||||
fido_log_debug("%s: largeblob_get_nonce", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext,
|
||||
&blob->ciphertext) < 0) {
|
||||
fido_log_debug("%s: aes256_gcm_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
blob->origsiz = body->len;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
fido_blob_free(&plaintext);
|
||||
fido_blob_free(&aad);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
cbor_item_t *argv[3];
|
||||
int r;
|
||||
|
||||
memset(argv, 0, sizeof(argv));
|
||||
memset(&f, 0, sizeof(f));
|
||||
|
||||
if ((argv[0] = cbor_build_uint(count)) == NULL ||
|
||||
(argv[2] = cbor_build_uint(offset)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
free(f.ptr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
|
||||
void *arg)
|
||||
{
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 1) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return 0; /* ignore */
|
||||
}
|
||||
|
||||
return fido_blob_decode(val, arg);
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen, r;
|
||||
|
||||
*chunk = NULL;
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
if ((*chunk = fido_blob_new()) == NULL) {
|
||||
fido_log_debug("%s: fido_blob_new", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, *chunk,
|
||||
parse_largeblob_reply)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_largeblob_reply", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
if (r != FIDO_OK)
|
||||
fido_blob_free(chunk);
|
||||
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static cbor_item_t *
|
||||
largeblob_array_load(const uint8_t *ptr, size_t len)
|
||||
{
|
||||
struct cbor_load_result cbor;
|
||||
cbor_item_t *item;
|
||||
|
||||
if (len < LARGEBLOB_DIGEST_LENGTH) {
|
||||
fido_log_debug("%s: len", __func__);
|
||||
return NULL;
|
||||
}
|
||||
len -= LARGEBLOB_DIGEST_LENGTH;
|
||||
if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
|
||||
fido_log_debug("%s: cbor_load", __func__);
|
||||
return NULL;
|
||||
}
|
||||
if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
cbor_decref(&item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_chunklen(fido_dev_t *dev)
|
||||
{
|
||||
uint64_t maxchunklen;
|
||||
|
||||
if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX)
|
||||
maxchunklen = SIZE_MAX;
|
||||
if (maxchunklen > FIDO_MAXMSG)
|
||||
maxchunklen = FIDO_MAXMSG;
|
||||
maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0;
|
||||
|
||||
return (size_t)maxchunklen;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
largeblob_t *blob = arg;
|
||||
uint64_t origsiz;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return 0; /* ignore */
|
||||
}
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 1: /* ciphertext */
|
||||
if (fido_blob_decode(val, &blob->ciphertext) < 0 ||
|
||||
blob->ciphertext.len < LARGEBLOB_TAG_LENGTH)
|
||||
return -1;
|
||||
return 0;
|
||||
case 2: /* nonce */
|
||||
if (fido_blob_decode(val, &blob->nonce) < 0 ||
|
||||
blob->nonce.len != LARGEBLOB_NONCE_LENGTH)
|
||||
return -1;
|
||||
return 0;
|
||||
case 3: /* origSize */
|
||||
if (!cbor_isa_uint(val) ||
|
||||
(origsiz = cbor_get_int(val)) > SIZE_MAX)
|
||||
return -1;
|
||||
blob->origsiz = (size_t)origsiz;
|
||||
return 0;
|
||||
default: /* ignore */
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_decode(largeblob_t *blob, const cbor_item_t *item)
|
||||
{
|
||||
if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) {
|
||||
fido_log_debug("%s: cbor_map_iter", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (fido_blob_is_empty(&blob->ciphertext) ||
|
||||
fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) {
|
||||
fido_log_debug("%s: incomplete blob", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cbor_item_t *
|
||||
largeblob_encode(const fido_blob_t *body, const fido_blob_t *key)
|
||||
{
|
||||
largeblob_t *blob;
|
||||
cbor_item_t *argv[3], *item = NULL;
|
||||
|
||||
memset(argv, 0, sizeof(argv));
|
||||
if ((blob = largeblob_new()) == NULL ||
|
||||
largeblob_seal(blob, body, key) < 0) {
|
||||
fido_log_debug("%s: largeblob_seal", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL ||
|
||||
(argv[1] = fido_blob_encode(&blob->nonce)) == NULL ||
|
||||
(argv[2] = cbor_build_uint(blob->origsiz)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
item = cbor_flatten_vector(argv, nitems(argv));
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
largeblob_free(&blob);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item,
|
||||
const fido_blob_t *key)
|
||||
{
|
||||
cbor_item_t **v;
|
||||
fido_blob_t *plaintext = NULL;
|
||||
largeblob_t blob;
|
||||
int r;
|
||||
|
||||
memset(&blob, 0, sizeof(blob));
|
||||
if (idx != NULL)
|
||||
*idx = 0;
|
||||
if ((v = cbor_array_handle(item)) == NULL)
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
for (size_t i = 0; i < cbor_array_size(item); i++) {
|
||||
if (largeblob_decode(&blob, v[i]) < 0 ||
|
||||
(plaintext = largeblob_decrypt(&blob, key)) == NULL) {
|
||||
fido_log_debug("%s: largeblob_decode", __func__);
|
||||
largeblob_reset(&blob);
|
||||
continue;
|
||||
}
|
||||
if (idx != NULL)
|
||||
*idx = i;
|
||||
break;
|
||||
}
|
||||
if (plaintext == NULL) {
|
||||
fido_log_debug("%s: not found", __func__);
|
||||
return FIDO_ERR_NOTFOUND;
|
||||
}
|
||||
if (out != NULL)
|
||||
r = fido_uncompress(out, plaintext, blob.origsiz);
|
||||
else
|
||||
r = FIDO_OK;
|
||||
|
||||
fido_blob_free(&plaintext);
|
||||
largeblob_reset(&blob);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data,
|
||||
size_t len)
|
||||
{
|
||||
u_char dgst[SHA256_DIGEST_LENGTH];
|
||||
|
||||
if (data == NULL || len == 0)
|
||||
return -1;
|
||||
if (SHA256(data, len, dgst) != dgst)
|
||||
return -1;
|
||||
memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_array_check(const fido_blob_t *array)
|
||||
{
|
||||
u_char expected_hash[LARGEBLOB_DIGEST_LENGTH];
|
||||
size_t body_len;
|
||||
|
||||
fido_log_xxd(array->ptr, array->len, __func__);
|
||||
if (array->len < sizeof(expected_hash)) {
|
||||
fido_log_debug("%s: len %zu", __func__, array->len);
|
||||
return -1;
|
||||
}
|
||||
body_len = array->len - sizeof(expected_hash);
|
||||
if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) {
|
||||
fido_log_debug("%s: largeblob_array_digest", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return timingsafe_bcmp(expected_hash, array->ptr + body_len,
|
||||
sizeof(expected_hash));
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
|
||||
{
|
||||
fido_blob_t *array, *chunk = NULL;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
*item = NULL;
|
||||
if ((n = get_chunklen(dev)) == 0)
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
if ((array = fido_blob_new()) == NULL)
|
||||
return FIDO_ERR_INTERNAL;
|
||||
do {
|
||||
fido_blob_free(&chunk);
|
||||
if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK ||
|
||||
(r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_wait %zu/%zu",
|
||||
__func__, array->len, n);
|
||||
goto fail;
|
||||
}
|
||||
if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) {
|
||||
fido_log_debug("%s: fido_blob_append", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
} while (chunk->len == n);
|
||||
|
||||
if (largeblob_array_check(array) != 0)
|
||||
*item = cbor_new_definite_array(0); /* per spec */
|
||||
else
|
||||
*item = largeblob_array_load(array->ptr, array->len);
|
||||
if (*item == NULL)
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
else
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&array);
|
||||
fido_blob_free(&chunk);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
|
||||
{
|
||||
uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH];
|
||||
uint32_t u32_offset;
|
||||
|
||||
if (data == NULL || len == 0) {
|
||||
fido_log_debug("%s: invalid data=%p, len=%zu", __func__,
|
||||
(const void *)data, len);
|
||||
return -1;
|
||||
}
|
||||
if (offset > UINT32_MAX) {
|
||||
fido_log_debug("%s: invalid offset=%zu", __func__, offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, 0xff, 32);
|
||||
buf[32] = CTAP_CBOR_LARGEBLOB;
|
||||
buf[33] = 0x00;
|
||||
u32_offset = htole32((uint32_t)offset);
|
||||
memcpy(&buf[34], &u32_offset, sizeof(uint32_t));
|
||||
if (SHA256(data, len, &buf[38]) != &buf[38]) {
|
||||
fido_log_debug("%s: SHA256", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fido_blob_set(hmac, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
|
||||
size_t chunk_len, size_t offset, size_t totalsiz, int *ms)
|
||||
{
|
||||
fido_blob_t *hmac = NULL, f;
|
||||
cbor_item_t *argv[6];
|
||||
int r;
|
||||
|
||||
memset(argv, 0, sizeof(argv));
|
||||
memset(&f, 0, sizeof(f));
|
||||
|
||||
if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL ||
|
||||
(argv[2] = cbor_build_uint(offset)) == NULL ||
|
||||
(offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (token != NULL) {
|
||||
if ((hmac = fido_blob_new()) == NULL ||
|
||||
prepare_hmac(offset, chunk, chunk_len, hmac) < 0 ||
|
||||
(argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL ||
|
||||
(argv[5] = cbor_encode_pin_opt(dev)) == NULL) {
|
||||
fido_log_debug("%s: cbor_encode_pin_auth", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
fido_blob_free(&hmac);
|
||||
free(f.ptr);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
|
||||
int *ms)
|
||||
{
|
||||
es256_pk_t *pk = NULL;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
int r;
|
||||
|
||||
if ((*token = fido_blob_new()) == NULL)
|
||||
return FIDO_ERR_INTERNAL;
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
|
||||
NULL, *token, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_get_uv_token", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (r != FIDO_OK)
|
||||
fido_blob_free(token);
|
||||
|
||||
fido_blob_free(&ecdh);
|
||||
es256_pk_free(&pk);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
|
||||
int *ms)
|
||||
{
|
||||
unsigned char dgst[SHA256_DIGEST_LENGTH];
|
||||
fido_blob_t cbor, *token = NULL;
|
||||
size_t chunklen, maxchunklen, totalsize;
|
||||
int r;
|
||||
|
||||
memset(&cbor, 0, sizeof(cbor));
|
||||
|
||||
if ((maxchunklen = get_chunklen(dev)) == 0) {
|
||||
fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
if ((fido_blob_serialise(&cbor, item)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_serialise", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if (cbor.len > SIZE_MAX - sizeof(dgst)) {
|
||||
fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
|
||||
fido_log_debug("%s: SHA256", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
|
||||
if (pin != NULL || fido_dev_supports_permissions(dev)) {
|
||||
if ((r = largeblob_get_uv_token(dev, pin, &token,
|
||||
ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_uv_token", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
|
||||
if ((chunklen = cbor.len - offset) > maxchunklen)
|
||||
chunklen = maxchunklen;
|
||||
if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
|
||||
chunklen, offset, totalsize, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: body", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
|
||||
totalsize, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: dgst", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&token);
|
||||
fido_blob_reset(&cbor);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
|
||||
const char *pin, int *ms)
|
||||
{
|
||||
cbor_item_t *array = NULL;
|
||||
size_t idx;
|
||||
int r;
|
||||
|
||||
if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_array", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
|
||||
case FIDO_OK:
|
||||
if (!cbor_array_replace(array, idx, item)) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case FIDO_ERR_NOTFOUND:
|
||||
if (cbor_array_append(&array, item) < 0) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: largeblob_array_lookup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_set_array", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (array != NULL)
|
||||
cbor_decref(&array);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
|
||||
int *ms)
|
||||
{
|
||||
cbor_item_t *array = NULL;
|
||||
size_t idx;
|
||||
int r;
|
||||
|
||||
if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_array", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_array_lookup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (cbor_array_drop(&array, idx) < 0) {
|
||||
fido_log_debug("%s: cbor_array_drop", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_set_array", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (array != NULL)
|
||||
cbor_decref(&array);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
|
||||
size_t key_len, unsigned char **blob_ptr, size_t *blob_len)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
fido_blob_t key, body;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&body, 0, sizeof(body));
|
||||
|
||||
if (key_len != 32) {
|
||||
fido_log_debug("%s: invalid key len %zu", __func__, key_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (blob_ptr == NULL || blob_len == NULL) {
|
||||
fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
|
||||
(const void *)blob_ptr, (const void *)blob_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
*blob_ptr = NULL;
|
||||
*blob_len = 0;
|
||||
if (fido_blob_set(&key, key_ptr, key_len) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
return FIDO_ERR_INTERNAL;
|
||||
}
|
||||
if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_array", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
|
||||
fido_log_debug("%s: largeblob_array_lookup", __func__);
|
||||
else {
|
||||
*blob_ptr = body.ptr;
|
||||
*blob_len = body.len;
|
||||
}
|
||||
fail:
|
||||
if (item != NULL)
|
||||
cbor_decref(&item);
|
||||
|
||||
fido_blob_reset(&key);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
|
||||
size_t key_len, const unsigned char *blob_ptr, size_t blob_len,
|
||||
const char *pin)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
fido_blob_t key, body;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
memset(&body, 0, sizeof(body));
|
||||
|
||||
if (key_len != 32) {
|
||||
fido_log_debug("%s: invalid key len %zu", __func__, key_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (blob_ptr == NULL || blob_len == 0) {
|
||||
fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
|
||||
(const void *)blob_ptr, blob_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
|
||||
fido_blob_set(&body, blob_ptr, blob_len) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((item = largeblob_encode(&body, &key)) == NULL) {
|
||||
fido_log_debug("%s: largeblob_encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
|
||||
fido_log_debug("%s: largeblob_add", __func__);
|
||||
fail:
|
||||
if (item != NULL)
|
||||
cbor_decref(&item);
|
||||
|
||||
fido_blob_reset(&key);
|
||||
fido_blob_reset(&body);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
|
||||
size_t key_len, const char *pin)
|
||||
{
|
||||
fido_blob_t key;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
memset(&key, 0, sizeof(key));
|
||||
|
||||
if (key_len != 32) {
|
||||
fido_log_debug("%s: invalid key len %zu", __func__, key_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (fido_blob_set(&key, key_ptr, key_len) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
return FIDO_ERR_INTERNAL;
|
||||
}
|
||||
if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
|
||||
fido_log_debug("%s: largeblob_drop", __func__);
|
||||
|
||||
fido_blob_reset(&key);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
|
||||
size_t *cbor_len)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
fido_blob_t cbor;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
memset(&cbor, 0, sizeof(cbor));
|
||||
|
||||
if (cbor_ptr == NULL || cbor_len == NULL) {
|
||||
fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
|
||||
(const void *)cbor_ptr, (const void *)cbor_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
*cbor_ptr = NULL;
|
||||
*cbor_len = 0;
|
||||
if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: largeblob_get_array", __func__);
|
||||
return r;
|
||||
}
|
||||
if (fido_blob_serialise(&cbor, item) < 0) {
|
||||
fido_log_debug("%s: fido_blob_serialise", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
} else {
|
||||
*cbor_ptr = cbor.ptr;
|
||||
*cbor_len = cbor.len;
|
||||
}
|
||||
|
||||
cbor_decref(&item);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
|
||||
size_t cbor_len, const char *pin)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
struct cbor_load_result cbor_result;
|
||||
int ms = dev->timeout_ms;
|
||||
int r;
|
||||
|
||||
if (cbor_ptr == NULL || cbor_len == 0) {
|
||||
fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
|
||||
(const void *)cbor_ptr, cbor_len);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
|
||||
fido_log_debug("%s: cbor_load", __func__);
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
|
||||
fido_log_debug("%s: largeblob_set_array", __func__);
|
||||
|
||||
cbor_decref(&item);
|
||||
|
||||
return r;
|
||||
}
|
||||
12
src/libfido2.pc.in
Normal file
12
src/libfido2.pc.in
Normal file
@@ -0,0 +1,12 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: A FIDO2 library
|
||||
URL: https://github.com/yubico/libfido2
|
||||
Version: @FIDO_VERSION@
|
||||
Requires: libcrypto
|
||||
Libs: -L${libdir} -lfido2
|
||||
Cflags: -I${includedir}
|
||||
122
src/log.c
Normal file
122
src/log.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 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
|
||||
*/
|
||||
|
||||
#undef _GNU_SOURCE /* XSI strerror_r() */
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#ifndef FIDO_NO_DIAGNOSTIC
|
||||
|
||||
#define XXDLEN 32
|
||||
#define XXDROW 128
|
||||
#define LINELEN 256
|
||||
|
||||
#ifndef TLS
|
||||
#define TLS
|
||||
#endif
|
||||
|
||||
static TLS int logging;
|
||||
static TLS fido_log_handler_t *log_handler;
|
||||
|
||||
static void
|
||||
log_on_stderr(const char *str)
|
||||
{
|
||||
fprintf(stderr, "%s", str);
|
||||
}
|
||||
|
||||
static void
|
||||
do_log(const char *suffix, const char *fmt, va_list args)
|
||||
{
|
||||
char line[LINELEN], body[LINELEN];
|
||||
|
||||
vsnprintf(body, sizeof(body), fmt, args);
|
||||
|
||||
if (suffix != NULL)
|
||||
snprintf(line, sizeof(line), "%.180s: %.70s\n", body, suffix);
|
||||
else
|
||||
snprintf(line, sizeof(line), "%.180s\n", body);
|
||||
|
||||
log_handler(line);
|
||||
}
|
||||
|
||||
void
|
||||
fido_log_init(void)
|
||||
{
|
||||
logging = 1;
|
||||
log_handler = log_on_stderr;
|
||||
}
|
||||
|
||||
void
|
||||
fido_log_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (!logging || log_handler == NULL)
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
do_log(NULL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
fido_log_xxd(const void *buf, size_t count, const char *fmt, ...)
|
||||
{
|
||||
const uint8_t *ptr = buf;
|
||||
char row[XXDROW], xxd[XXDLEN];
|
||||
va_list args;
|
||||
|
||||
if (!logging || log_handler == NULL)
|
||||
return;
|
||||
|
||||
snprintf(row, sizeof(row), "buf=%p, len=%zu", buf, count);
|
||||
va_start(args, fmt);
|
||||
do_log(row, fmt, args);
|
||||
va_end(args);
|
||||
*row = '\0';
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
*xxd = '\0';
|
||||
if (i % 16 == 0)
|
||||
snprintf(xxd, sizeof(xxd), "%04zu: %02x", i, *ptr++);
|
||||
else
|
||||
snprintf(xxd, sizeof(xxd), " %02x", *ptr++);
|
||||
strlcat(row, xxd, sizeof(row));
|
||||
if (i % 16 == 15 || i == count - 1) {
|
||||
fido_log_debug("%s", row);
|
||||
*row = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fido_log_error(int errnum, const char *fmt, ...)
|
||||
{
|
||||
char errstr[LINELEN];
|
||||
va_list args;
|
||||
|
||||
if (!logging || log_handler == NULL)
|
||||
return;
|
||||
if (strerror_r(errnum, errstr, sizeof(errstr)) != 0)
|
||||
snprintf(errstr, sizeof(errstr), "error %d", errnum);
|
||||
|
||||
va_start(args, fmt);
|
||||
do_log(errstr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
fido_set_log_handler(fido_log_handler_t *handler)
|
||||
{
|
||||
if (handler != NULL)
|
||||
log_handler = handler;
|
||||
}
|
||||
|
||||
#endif /* !FIDO_NO_DIAGNOSTIC */
|
||||
785
src/netlink.c
Normal file
785
src/netlink.c
Normal file
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* 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 <sys/socket.h>
|
||||
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/nfc.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "netlink.h"
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
static ssize_t (*fuzz_read)(int, void *, size_t);
|
||||
static ssize_t (*fuzz_write)(int, const void *, size_t);
|
||||
# define READ fuzz_read
|
||||
# define WRITE fuzz_write
|
||||
#else
|
||||
# define READ read
|
||||
# define WRITE write
|
||||
#endif
|
||||
|
||||
#ifndef SOL_NETLINK
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
#define NETLINK_POLL_MS 100
|
||||
|
||||
/* XXX avoid signed NLA_ALIGNTO */
|
||||
#undef NLA_HDRLEN
|
||||
#define NLA_HDRLEN NLMSG_ALIGN(sizeof(struct nlattr))
|
||||
|
||||
typedef struct nlmsgbuf {
|
||||
size_t siz; /* alloc size */
|
||||
size_t len; /* of payload */
|
||||
unsigned char *ptr; /* in payload */
|
||||
union {
|
||||
struct nlmsghdr nlmsg;
|
||||
char buf[NLMSG_HDRLEN]; /* align */
|
||||
} u;
|
||||
unsigned char payload[];
|
||||
} nlmsgbuf_t;
|
||||
|
||||
typedef struct genlmsgbuf {
|
||||
union {
|
||||
struct genlmsghdr genl;
|
||||
char buf[GENL_HDRLEN]; /* align */
|
||||
} u;
|
||||
} genlmsgbuf_t;
|
||||
|
||||
typedef struct nlamsgbuf {
|
||||
size_t siz; /* alloc size */
|
||||
size_t len; /* of payload */
|
||||
unsigned char *ptr; /* in payload */
|
||||
union {
|
||||
struct nlattr nla;
|
||||
char buf[NLA_HDRLEN]; /* align */
|
||||
} u;
|
||||
unsigned char payload[];
|
||||
} nlamsgbuf_t;
|
||||
|
||||
typedef struct nl_family {
|
||||
uint16_t id;
|
||||
uint32_t mcastgrp;
|
||||
} nl_family_t;
|
||||
|
||||
typedef struct nl_poll {
|
||||
uint32_t dev;
|
||||
unsigned int eventcnt;
|
||||
} nl_poll_t;
|
||||
|
||||
typedef struct nl_target {
|
||||
int found;
|
||||
uint32_t *value;
|
||||
} nl_target_t;
|
||||
|
||||
static const void *
|
||||
nlmsg_ptr(const nlmsgbuf_t *m)
|
||||
{
|
||||
return (&m->u.nlmsg);
|
||||
}
|
||||
|
||||
static size_t
|
||||
nlmsg_len(const nlmsgbuf_t *m)
|
||||
{
|
||||
return (m->u.nlmsg.nlmsg_len);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
nlmsg_type(const nlmsgbuf_t *m)
|
||||
{
|
||||
return (m->u.nlmsg.nlmsg_type);
|
||||
}
|
||||
|
||||
static nlmsgbuf_t *
|
||||
nlmsg_new(uint16_t type, uint16_t flags, size_t len)
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
size_t siz;
|
||||
|
||||
if (len > SIZE_MAX - sizeof(*m) ||
|
||||
(siz = sizeof(*m) + len) > UINT16_MAX ||
|
||||
(m = calloc(1, siz)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
m->siz = siz;
|
||||
m->len = len;
|
||||
m->ptr = m->payload;
|
||||
m->u.nlmsg.nlmsg_type = type;
|
||||
m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags;
|
||||
m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN;
|
||||
|
||||
return (m);
|
||||
}
|
||||
|
||||
static nlamsgbuf_t *
|
||||
nla_from_buf(const unsigned char **ptr, size_t *len)
|
||||
{
|
||||
nlamsgbuf_t h, *a;
|
||||
size_t nlalen, skip;
|
||||
|
||||
if (*len < sizeof(h.u))
|
||||
return (NULL);
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
memcpy(&h.u, *ptr, sizeof(h.u));
|
||||
|
||||
if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len ||
|
||||
nlalen - sizeof(h.u) > UINT16_MAX ||
|
||||
nlalen > SIZE_MAX - sizeof(*a) ||
|
||||
(skip = NLMSG_ALIGN(nlalen)) > *len ||
|
||||
(a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
memcpy(&a->u, *ptr, nlalen);
|
||||
a->siz = sizeof(*a) + nlalen - sizeof(h.u);
|
||||
a->ptr = a->payload;
|
||||
a->len = nlalen - sizeof(h.u);
|
||||
*ptr += skip;
|
||||
*len -= skip;
|
||||
|
||||
return (a);
|
||||
}
|
||||
|
||||
static nlamsgbuf_t *
|
||||
nla_getattr(nlamsgbuf_t *a)
|
||||
{
|
||||
return (nla_from_buf((void *)&a->ptr, &a->len));
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
nla_type(const nlamsgbuf_t *a)
|
||||
{
|
||||
return (a->u.nla.nla_type);
|
||||
}
|
||||
|
||||
static nlamsgbuf_t *
|
||||
nlmsg_getattr(nlmsgbuf_t *m)
|
||||
{
|
||||
return (nla_from_buf((void *)&m->ptr, &m->len));
|
||||
}
|
||||
|
||||
static int
|
||||
nla_read(nlamsgbuf_t *a, void *buf, size_t cnt)
|
||||
{
|
||||
if (cnt > a->u.nla.nla_len ||
|
||||
fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0)
|
||||
return (-1);
|
||||
|
||||
a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static nlmsgbuf_t *
|
||||
nlmsg_from_buf(const unsigned char **ptr, size_t *len)
|
||||
{
|
||||
nlmsgbuf_t h, *m;
|
||||
size_t msglen, skip;
|
||||
|
||||
if (*len < sizeof(h.u))
|
||||
return (NULL);
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
memcpy(&h.u, *ptr, sizeof(h.u));
|
||||
|
||||
if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len ||
|
||||
msglen - sizeof(h.u) > UINT16_MAX ||
|
||||
(skip = NLMSG_ALIGN(msglen)) > *len ||
|
||||
(m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
memcpy(&m->u, *ptr, msglen);
|
||||
*ptr += skip;
|
||||
*len -= skip;
|
||||
|
||||
return (m);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt)
|
||||
{
|
||||
if (cnt > m->u.nlmsg.nlmsg_len ||
|
||||
fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0)
|
||||
return (-1);
|
||||
|
||||
m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt)
|
||||
{
|
||||
if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len ||
|
||||
fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0)
|
||||
return (-1);
|
||||
|
||||
m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd)
|
||||
{
|
||||
genlmsgbuf_t g;
|
||||
|
||||
memset(&g, 0, sizeof(g));
|
||||
g.u.genl.cmd = cmd;
|
||||
g.u.genl.version = NFC_GENL_VERSION;
|
||||
|
||||
return (nlmsg_write(m, &g, sizeof(g)));
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd)
|
||||
{
|
||||
genlmsgbuf_t g;
|
||||
|
||||
memset(&g, 0, sizeof(g));
|
||||
|
||||
if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_get_status(nlmsgbuf_t *m)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (nlmsg_read(m, &status, sizeof(status)) < 0 || status == INT_MIN)
|
||||
return (-1);
|
||||
if (status < 0)
|
||||
status = -status;
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len)
|
||||
{
|
||||
int r;
|
||||
char *padding;
|
||||
size_t skip;
|
||||
nlamsgbuf_t a;
|
||||
|
||||
if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) ||
|
||||
skip < len || (padding = calloc(1, skip - len)) == NULL)
|
||||
return (-1);
|
||||
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.u.nla.nla_type = type;
|
||||
a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u));
|
||||
r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 ||
|
||||
nlmsg_write(m, ptr, len) < 0 ||
|
||||
nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0;
|
||||
|
||||
free(padding);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val)
|
||||
{
|
||||
return (nlmsg_setattr(m, type, &val, sizeof(val)));
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val)
|
||||
{
|
||||
return (nlmsg_setattr(m, type, &val, sizeof(val)));
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val)
|
||||
{
|
||||
return (nlmsg_setattr(m, type, val, strlen(val) + 1));
|
||||
}
|
||||
|
||||
static int
|
||||
nla_get_u16(nlamsgbuf_t *a, uint16_t *v)
|
||||
{
|
||||
return (nla_read(a, v, sizeof(*v)));
|
||||
}
|
||||
|
||||
static int
|
||||
nla_get_u32(nlamsgbuf_t *a, uint32_t *v)
|
||||
{
|
||||
return (nla_read(a, v, sizeof(*v)));
|
||||
}
|
||||
|
||||
static char *
|
||||
nla_get_str(nlamsgbuf_t *a)
|
||||
{
|
||||
size_t n;
|
||||
char *s = NULL;
|
||||
|
||||
if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' ||
|
||||
(s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) {
|
||||
free(s);
|
||||
return (NULL);
|
||||
}
|
||||
s[n - 1] = '\0';
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_tx(int fd, const nlmsgbuf_t *m)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
if ((r = WRITE(fd, nlmsg_ptr(m), nlmsg_len(m))) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if (r < 0 || (size_t)r != nlmsg_len(m)) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, nlmsg_len(m));
|
||||
return (-1);
|
||||
}
|
||||
fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m), "%s", __func__);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
if (len > SSIZE_MAX) {
|
||||
fido_log_debug("%s: len", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if (fido_hid_unix_wait(fd, ms, NULL) < 0) {
|
||||
fido_log_debug("%s: fido_hid_unix_wait", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((r = READ(fd, ptr, len)) == -1) {
|
||||
fido_log_error(errno, "%s: read %zd", __func__, r);
|
||||
return (-1);
|
||||
}
|
||||
fido_log_xxd(ptr, (size_t)r, "%s", __func__);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *))
|
||||
{
|
||||
nlamsgbuf_t *a;
|
||||
int r;
|
||||
|
||||
while ((a = nlmsg_getattr(m)) != NULL) {
|
||||
r = parser(a, arg);
|
||||
free(a);
|
||||
if (r < 0) {
|
||||
fido_log_debug("%s: parser", __func__);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *))
|
||||
{
|
||||
nlamsgbuf_t *a;
|
||||
int r;
|
||||
|
||||
while ((a = nla_getattr(g)) != NULL) {
|
||||
r = parser(a, arg);
|
||||
free(a);
|
||||
if (r < 0) {
|
||||
fido_log_debug("%s: parser", __func__);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nl_parse_reply(const uint8_t *blob, size_t blob_len, uint16_t msg_type,
|
||||
uint8_t genl_cmd, void *arg, int (*parser)(nlamsgbuf_t *, void *))
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
int r;
|
||||
|
||||
while (blob_len) {
|
||||
if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) {
|
||||
fido_log_debug("%s: nlmsg", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if (nlmsg_type(m) == NLMSG_ERROR) {
|
||||
r = nlmsg_get_status(m);
|
||||
free(m);
|
||||
return (r);
|
||||
}
|
||||
if (nlmsg_type(m) != msg_type ||
|
||||
nlmsg_get_genl(m, genl_cmd) < 0) {
|
||||
fido_log_debug("%s: skipping", __func__);
|
||||
free(m);
|
||||
continue;
|
||||
}
|
||||
if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) {
|
||||
fido_log_debug("%s: nlmsg_iter", __func__);
|
||||
free(m);
|
||||
return (-1);
|
||||
}
|
||||
free(m);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_mcastgrp(nlamsgbuf_t *a, void *arg)
|
||||
{
|
||||
nl_family_t *family = arg;
|
||||
char *name;
|
||||
|
||||
switch (nla_type(a)) {
|
||||
case CTRL_ATTR_MCAST_GRP_NAME:
|
||||
if ((name = nla_get_str(a)) == NULL ||
|
||||
strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) {
|
||||
free(name);
|
||||
return (-1); /* XXX skip? */
|
||||
}
|
||||
free(name);
|
||||
return (0);
|
||||
case CTRL_ATTR_MCAST_GRP_ID:
|
||||
if (family->mcastgrp)
|
||||
break;
|
||||
if (nla_get_u32(a, &family->mcastgrp) < 0) {
|
||||
fido_log_debug("%s: group", __func__);
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_mcastgrps(nlamsgbuf_t *a, void *arg)
|
||||
{
|
||||
return (nla_iter(a, arg, parse_mcastgrp));
|
||||
}
|
||||
|
||||
static int
|
||||
parse_family(nlamsgbuf_t *a, void *arg)
|
||||
{
|
||||
nl_family_t *family = arg;
|
||||
|
||||
switch (nla_type(a)) {
|
||||
case CTRL_ATTR_FAMILY_ID:
|
||||
if (family->id)
|
||||
break;
|
||||
if (nla_get_u16(a, &family->id) < 0) {
|
||||
fido_log_debug("%s: id", __func__);
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
case CTRL_ATTR_MCAST_GROUPS:
|
||||
return (nla_iter(a, family, parse_mcastgrps));
|
||||
}
|
||||
|
||||
fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp)
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
uint8_t reply[512];
|
||||
nl_family_t family;
|
||||
ssize_t r;
|
||||
int ok;
|
||||
|
||||
if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL ||
|
||||
nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 ||
|
||||
nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 ||
|
||||
nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 ||
|
||||
nlmsg_tx(fd, m) < 0) {
|
||||
free(m);
|
||||
return (-1);
|
||||
}
|
||||
free(m);
|
||||
memset(&family, 0, sizeof(family));
|
||||
if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) {
|
||||
fido_log_debug("%s: nlmsg_rx", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL,
|
||||
CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) {
|
||||
fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
|
||||
return (-1);
|
||||
}
|
||||
if (family.id == 0 || family.mcastgrp == 0) {
|
||||
fido_log_debug("%s: missing attr", __func__);
|
||||
return (-1);
|
||||
}
|
||||
*type = family.id;
|
||||
*mcastgrp = family.mcastgrp;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_target(nlamsgbuf_t *a, void *arg)
|
||||
{
|
||||
nl_target_t *t = arg;
|
||||
|
||||
if (t->found || nla_type(a) != NFC_ATTR_TARGET_INDEX) {
|
||||
fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
|
||||
return (0);
|
||||
}
|
||||
if (nla_get_u32(a, t->value) < 0) {
|
||||
fido_log_debug("%s: target", __func__);
|
||||
return (-1);
|
||||
}
|
||||
t->found = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev)
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
uint8_t reply[512];
|
||||
ssize_t r;
|
||||
int ok;
|
||||
|
||||
if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
|
||||
nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 ||
|
||||
nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
|
||||
nlmsg_tx(nl->fd, m) < 0) {
|
||||
free(m);
|
||||
return (-1);
|
||||
}
|
||||
free(m);
|
||||
if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
|
||||
fido_log_debug("%s: nlmsg_rx", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
|
||||
NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) {
|
||||
fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nl_nfc_poll(fido_nl_t *nl, uint32_t dev)
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
uint8_t reply[512];
|
||||
ssize_t r;
|
||||
int ok;
|
||||
|
||||
if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
|
||||
nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 ||
|
||||
nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
|
||||
nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 ||
|
||||
nlmsg_tx(nl->fd, m) < 0) {
|
||||
free(m);
|
||||
return (-1);
|
||||
}
|
||||
free(m);
|
||||
if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
|
||||
fido_log_debug("%s: nlmsg_rx", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
|
||||
NFC_CMD_START_POLL, NULL, NULL)) != 0) {
|
||||
fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms)
|
||||
{
|
||||
nlmsgbuf_t *m;
|
||||
nl_target_t t;
|
||||
uint8_t reply[512];
|
||||
ssize_t r;
|
||||
int ok;
|
||||
|
||||
if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL ||
|
||||
nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 ||
|
||||
nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
|
||||
nlmsg_tx(nl->fd, m) < 0) {
|
||||
free(m);
|
||||
return (-1);
|
||||
}
|
||||
free(m);
|
||||
if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) {
|
||||
fido_log_debug("%s: nlmsg_rx", __func__);
|
||||
return (-1);
|
||||
}
|
||||
memset(&t, 0, sizeof(t));
|
||||
t.value = target;
|
||||
if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
|
||||
NFC_CMD_GET_TARGET, &t, parse_target)) != 0) {
|
||||
fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
|
||||
return (-1);
|
||||
}
|
||||
if (!t.found) {
|
||||
fido_log_debug("%s: target not found", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_nfc_event(nlamsgbuf_t *a, void *arg)
|
||||
{
|
||||
nl_poll_t *ctx = arg;
|
||||
uint32_t dev;
|
||||
|
||||
if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) {
|
||||
fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
|
||||
return (0);
|
||||
}
|
||||
if (nla_get_u32(a, &dev) < 0) {
|
||||
fido_log_debug("%s: dev", __func__);
|
||||
return (-1);
|
||||
}
|
||||
if (dev == ctx->dev)
|
||||
ctx->eventcnt++;
|
||||
else
|
||||
fido_log_debug("%s: ignoring dev 0x%x", __func__, dev);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target)
|
||||
{
|
||||
uint8_t reply[512];
|
||||
nl_poll_t ctx;
|
||||
ssize_t r;
|
||||
int ok;
|
||||
|
||||
if (nl_nfc_poll(nl, dev) < 0) {
|
||||
fido_log_debug("%s: nl_nfc_poll", __func__);
|
||||
return (-1);
|
||||
}
|
||||
#ifndef FIDO_FUZZ
|
||||
if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||
&nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
|
||||
fido_log_error(errno, "%s: setsockopt add", __func__);
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
r = nlmsg_rx(nl->fd, reply, sizeof(reply), NETLINK_POLL_MS);
|
||||
#ifndef FIDO_FUZZ
|
||||
if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
|
||||
&nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
|
||||
fido_log_error(errno, "%s: setsockopt drop", __func__);
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
if (r < 0) {
|
||||
fido_log_debug("%s: nlmsg_rx", __func__);
|
||||
return (-1);
|
||||
}
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.dev = dev;
|
||||
if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
|
||||
NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) {
|
||||
fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
|
||||
return (-1);
|
||||
}
|
||||
if (ctx.eventcnt == 0) {
|
||||
fido_log_debug("%s: dev 0x%x not observed", __func__, dev);
|
||||
return (-1);
|
||||
}
|
||||
if (nl_dump_nfc_target(nl, dev, target, -1) < 0) {
|
||||
fido_log_debug("%s: nl_dump_nfc_target", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
fido_nl_free(fido_nl_t **nlp)
|
||||
{
|
||||
fido_nl_t *nl;
|
||||
|
||||
if (nlp == NULL || (nl = *nlp) == NULL)
|
||||
return;
|
||||
if (nl->fd != -1 && close(nl->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
|
||||
free(nl);
|
||||
*nlp = NULL;
|
||||
}
|
||||
|
||||
fido_nl_t *
|
||||
fido_nl_new(void)
|
||||
{
|
||||
fido_nl_t *nl;
|
||||
int ok = -1;
|
||||
|
||||
if ((nl = calloc(1, sizeof(*nl))) == NULL)
|
||||
return (NULL);
|
||||
if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
|
||||
NETLINK_GENERIC)) == -1) {
|
||||
fido_log_error(errno, "%s: socket", __func__);
|
||||
goto fail;
|
||||
}
|
||||
nl->saddr.nl_family = AF_NETLINK;
|
||||
if (bind(nl->fd, (struct sockaddr *)&nl->saddr,
|
||||
sizeof(nl->saddr)) == -1) {
|
||||
fido_log_error(errno, "%s: bind", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) {
|
||||
fido_log_debug("%s: nl_get_nfc_family", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (ok < 0)
|
||||
fido_nl_free(&nl);
|
||||
|
||||
return (nl);
|
||||
}
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
void
|
||||
set_netlink_io_functions(ssize_t (*read_f)(int, void *, size_t),
|
||||
ssize_t (*write_f)(int, const void *, size_t))
|
||||
{
|
||||
fuzz_read = read_f;
|
||||
fuzz_write = write_f;
|
||||
}
|
||||
#endif
|
||||
45
src/netlink.h
Normal file
45
src/netlink.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _FIDO_NETLINK_H
|
||||
#define _FIDO_NETLINK_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/nfc.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct fido_nl {
|
||||
int fd;
|
||||
uint16_t nfc_type;
|
||||
uint32_t nfc_mcastgrp;
|
||||
struct sockaddr_nl saddr;
|
||||
} fido_nl_t;
|
||||
|
||||
fido_nl_t *fido_nl_new(void);
|
||||
void fido_nl_free(struct fido_nl **);
|
||||
int fido_nl_power_nfc(struct fido_nl *, uint32_t);
|
||||
int fido_nl_get_nfc_target(struct fido_nl *, uint32_t , uint32_t *);
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
void set_netlink_io_functions(ssize_t (*)(int, void *, size_t),
|
||||
ssize_t (*)(int, const void *, size_t));
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* !_FIDO_NETLINK_H */
|
||||
350
src/nfc.c
Normal file
350
src/nfc.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/param.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
#define TX_CHUNK_SIZE 240
|
||||
|
||||
static const uint8_t aid[] = { 0xa0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01 };
|
||||
static const uint8_t v_u2f[] = { 'U', '2', 'F', '_', 'V', '2' };
|
||||
static const uint8_t v_fido[] = { 'F', 'I', 'D', 'O', '_', '2', '_', '0' };
|
||||
|
||||
static int
|
||||
tx_short_apdu(fido_dev_t *d, const iso7816_header_t *h, const uint8_t *payload,
|
||||
uint8_t payload_len, uint8_t cla_flags)
|
||||
{
|
||||
uint8_t apdu[5 + UINT8_MAX + 1];
|
||||
uint8_t sw[2];
|
||||
size_t apdu_len;
|
||||
int ok = -1;
|
||||
|
||||
memset(&apdu, 0, sizeof(apdu));
|
||||
apdu[0] = h->cla | cla_flags;
|
||||
apdu[1] = h->ins;
|
||||
apdu[2] = h->p1;
|
||||
apdu[3] = h->p2;
|
||||
apdu[4] = payload_len;
|
||||
memcpy(&apdu[5], payload, payload_len);
|
||||
apdu_len = (size_t)(5 + payload_len + 1);
|
||||
|
||||
if (d->io.write(d->io_handle, apdu, apdu_len) < 0) {
|
||||
fido_log_debug("%s: write", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cla_flags & 0x10) {
|
||||
if (d->io.read(d->io_handle, sw, sizeof(sw), -1) != 2) {
|
||||
fido_log_debug("%s: read", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((sw[0] << 8 | sw[1]) != SW_NO_ERROR) {
|
||||
fido_log_debug("%s: unexpected sw", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
explicit_bzero(apdu, sizeof(apdu));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
nfc_do_tx(fido_dev_t *d, const uint8_t *apdu_ptr, size_t apdu_len)
|
||||
{
|
||||
iso7816_header_t h;
|
||||
|
||||
if (fido_buf_read(&apdu_ptr, &apdu_len, &h, sizeof(h)) < 0) {
|
||||
fido_log_debug("%s: header", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (apdu_len < 2) {
|
||||
fido_log_debug("%s: apdu_len %zu", __func__, apdu_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
apdu_len -= 2; /* trim le1 le2 */
|
||||
|
||||
while (apdu_len > TX_CHUNK_SIZE) {
|
||||
if (tx_short_apdu(d, &h, apdu_ptr, TX_CHUNK_SIZE, 0x10) < 0) {
|
||||
fido_log_debug("%s: chain", __func__);
|
||||
return -1;
|
||||
}
|
||||
apdu_ptr += TX_CHUNK_SIZE;
|
||||
apdu_len -= TX_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
if (tx_short_apdu(d, &h, apdu_ptr, (uint8_t)apdu_len, 0) < 0) {
|
||||
fido_log_debug("%s: tx_short_apdu", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
const uint8_t *ptr;
|
||||
size_t len;
|
||||
int ok = -1;
|
||||
|
||||
switch (cmd) {
|
||||
case CTAP_CMD_INIT: /* select */
|
||||
if ((apdu = iso7816_new(0, 0xa4, 0x04, sizeof(aid))) == NULL ||
|
||||
iso7816_add(apdu, aid, sizeof(aid)) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case CTAP_CMD_CBOR: /* wrap cbor */
|
||||
if (count > UINT16_MAX || (apdu = iso7816_new(0x80, 0x10, 0x00,
|
||||
(uint16_t)count)) == NULL ||
|
||||
iso7816_add(apdu, buf, count) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case CTAP_CMD_MSG: /* already an apdu */
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: cmd=%02x", __func__, cmd);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (apdu != NULL) {
|
||||
ptr = iso7816_ptr(apdu);
|
||||
len = iso7816_len(apdu);
|
||||
} else {
|
||||
ptr = buf;
|
||||
len = count;
|
||||
}
|
||||
|
||||
if (nfc_do_tx(d, ptr, len) < 0) {
|
||||
fido_log_debug("%s: nfc_do_tx", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
rx_init(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
|
||||
{
|
||||
fido_ctap_info_t *attr = (fido_ctap_info_t *)buf;
|
||||
uint8_t f[64];
|
||||
int n;
|
||||
|
||||
if (count != sizeof(*attr)) {
|
||||
fido_log_debug("%s: count=%zu", __func__, count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(attr, 0, sizeof(*attr));
|
||||
|
||||
if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2 ||
|
||||
(f[n - 2] << 8 | f[n - 1]) != SW_NO_ERROR) {
|
||||
fido_log_debug("%s: read", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n -= 2;
|
||||
|
||||
if (n == sizeof(v_u2f) && memcmp(f, v_u2f, sizeof(v_u2f)) == 0)
|
||||
attr->flags = FIDO_CAP_CBOR;
|
||||
else if (n == sizeof(v_fido) && memcmp(f, v_fido, sizeof(v_fido)) == 0)
|
||||
attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG;
|
||||
else {
|
||||
fido_log_debug("%s: unknown version string", __func__);
|
||||
#ifdef FIDO_FUZZ
|
||||
attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
memcpy(&attr->nonce, &d->nonce, sizeof(attr->nonce)); /* XXX */
|
||||
|
||||
return (int)count;
|
||||
}
|
||||
|
||||
static int
|
||||
tx_get_response(fido_dev_t *d, uint8_t count)
|
||||
{
|
||||
uint8_t apdu[5];
|
||||
|
||||
memset(apdu, 0, sizeof(apdu));
|
||||
apdu[1] = 0xc0; /* GET_RESPONSE */
|
||||
apdu[4] = count;
|
||||
|
||||
if (d->io.write(d->io_handle, apdu, sizeof(apdu)) < 0) {
|
||||
fido_log_debug("%s: write", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rx_apdu(fido_dev_t *d, uint8_t sw[2], unsigned char **buf, size_t *count, int *ms)
|
||||
{
|
||||
uint8_t f[256 + 2];
|
||||
struct timespec ts;
|
||||
int n, ok = -1;
|
||||
|
||||
if (fido_time_now(&ts) != 0)
|
||||
goto fail;
|
||||
|
||||
if ((n = d->io.read(d->io_handle, f, sizeof(f), *ms)) < 2) {
|
||||
fido_log_debug("%s: read", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_time_delta(&ts, ms) != 0)
|
||||
goto fail;
|
||||
|
||||
if (fido_buf_write(buf, count, f, (size_t)(n - 2)) < 0) {
|
||||
fido_log_debug("%s: fido_buf_write", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(sw, f + n - 2, 2);
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
explicit_bzero(f, sizeof(f));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
rx_msg(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
|
||||
{
|
||||
uint8_t sw[2];
|
||||
const size_t bufsiz = count;
|
||||
|
||||
if (rx_apdu(d, sw, &buf, &count, &ms) < 0) {
|
||||
fido_log_debug("%s: preamble", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (sw[0] == SW1_MORE_DATA)
|
||||
if (tx_get_response(d, sw[1]) < 0 ||
|
||||
rx_apdu(d, sw, &buf, &count, &ms) < 0) {
|
||||
fido_log_debug("%s: chain", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fido_buf_write(&buf, &count, sw, sizeof(sw)) < 0) {
|
||||
fido_log_debug("%s: sw", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bufsiz - count > INT_MAX) {
|
||||
fido_log_debug("%s: bufsiz", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)(bufsiz - count);
|
||||
}
|
||||
|
||||
static int
|
||||
rx_cbor(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = rx_msg(d, buf, count, ms)) < 2)
|
||||
return -1;
|
||||
|
||||
return r - 2;
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
|
||||
{
|
||||
switch (cmd) {
|
||||
case CTAP_CMD_INIT:
|
||||
return rx_init(d, buf, count, ms);
|
||||
case CTAP_CMD_CBOR:
|
||||
return rx_cbor(d, buf, count, ms);
|
||||
case CTAP_CMD_MSG:
|
||||
return rx_msg(d, buf, count, ms);
|
||||
default:
|
||||
fido_log_debug("%s: cmd=%02x", __func__, cmd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nfc_is_fido(const char *path)
|
||||
{
|
||||
bool fido = false;
|
||||
fido_dev_t *d;
|
||||
int r;
|
||||
|
||||
if ((d = fido_dev_new()) == NULL) {
|
||||
fido_log_debug("%s: fido_dev_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
/* fido_dev_open selects the fido applet */
|
||||
if ((r = fido_dev_open(d, path)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_open: 0x%x", __func__, r);
|
||||
goto fail;
|
||||
}
|
||||
if ((r = fido_dev_close(d)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_close: 0x%x", __func__, r);
|
||||
goto fail;
|
||||
|
||||
}
|
||||
|
||||
fido = true;
|
||||
fail:
|
||||
fido_dev_free(&d);
|
||||
|
||||
return fido;
|
||||
}
|
||||
|
||||
#ifdef USE_NFC
|
||||
bool
|
||||
fido_is_nfc(const char *path)
|
||||
{
|
||||
return strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_nfc(fido_dev_t *d)
|
||||
{
|
||||
if (d->io_handle != NULL) {
|
||||
fido_log_debug("%s: device open", __func__);
|
||||
return -1;
|
||||
}
|
||||
d->io_own = true;
|
||||
d->io = (fido_dev_io_t) {
|
||||
fido_nfc_open,
|
||||
fido_nfc_close,
|
||||
fido_nfc_read,
|
||||
fido_nfc_write,
|
||||
};
|
||||
d->transport = (fido_dev_transport_t) {
|
||||
fido_nfc_rx,
|
||||
fido_nfc_tx,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* USE_NFC */
|
||||
356
src/nfc_linux.c
Normal file
356
src/nfc_linux.c
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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/uio.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/nfc.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <libudev.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/param.h"
|
||||
#include "netlink.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
struct nfc_linux {
|
||||
int fd;
|
||||
uint32_t dev;
|
||||
uint32_t target;
|
||||
sigset_t sigmask;
|
||||
const sigset_t *sigmaskp;
|
||||
struct fido_nl *nl;
|
||||
};
|
||||
|
||||
static char *
|
||||
get_parent_attr(struct udev_device *dev, const char *subsystem,
|
||||
const char *devtype, const char *attr)
|
||||
{
|
||||
struct udev_device *parent;
|
||||
const char *value;
|
||||
|
||||
if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
|
||||
subsystem, devtype)) == NULL || (value =
|
||||
udev_device_get_sysattr_value(parent, attr)) == NULL)
|
||||
return NULL;
|
||||
|
||||
return strdup(value);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_usb_attr(struct udev_device *dev, const char *attr)
|
||||
{
|
||||
return get_parent_attr(dev, "usb", "usb_device", attr);
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, struct udev *udev,
|
||||
struct udev_list_entry *udev_entry)
|
||||
{
|
||||
const char *name;
|
||||
char *str;
|
||||
struct udev_device *dev = NULL;
|
||||
uint64_t id;
|
||||
int ok = -1;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
|
||||
if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
|
||||
(dev = udev_device_new_from_syspath(udev, name)) == NULL)
|
||||
goto fail;
|
||||
if (asprintf(&di->path, "%s/%s", FIDO_NFC_PREFIX, name) == -1) {
|
||||
di->path = NULL;
|
||||
goto fail;
|
||||
}
|
||||
if (nfc_is_fido(di->path) == false) {
|
||||
fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path);
|
||||
goto fail;
|
||||
}
|
||||
if ((di->manufacturer = get_usb_attr(dev, "manufacturer")) == NULL)
|
||||
di->manufacturer = strdup("");
|
||||
if ((di->product = get_usb_attr(dev, "product")) == NULL)
|
||||
di->product = strdup("");
|
||||
if (di->manufacturer == NULL || di->product == NULL)
|
||||
goto fail;
|
||||
/* XXX assumes USB for vendor/product info */
|
||||
if ((str = get_usb_attr(dev, "idVendor")) != NULL &&
|
||||
fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX)
|
||||
di->vendor_id = (int16_t)id;
|
||||
free(str);
|
||||
if ((str = get_usb_attr(dev, "idProduct")) != NULL &&
|
||||
fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX)
|
||||
di->product_id = (int16_t)id;
|
||||
free(str);
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (dev != NULL)
|
||||
udev_device_unref(dev);
|
||||
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
sysnum_from_syspath(const char *path)
|
||||
{
|
||||
struct udev *udev = NULL;
|
||||
struct udev_device *dev = NULL;
|
||||
const char *str;
|
||||
uint64_t idx64;
|
||||
int idx = -1;
|
||||
|
||||
if ((udev = udev_new()) != NULL &&
|
||||
(dev = udev_device_new_from_syspath(udev, path)) != NULL &&
|
||||
(str = udev_device_get_sysnum(dev)) != NULL &&
|
||||
fido_to_uint64(str, 10, &idx64) == 0 && idx64 < INT_MAX)
|
||||
idx = (int)idx64;
|
||||
|
||||
if (dev != NULL)
|
||||
udev_device_unref(dev);
|
||||
if (udev != NULL)
|
||||
udev_unref(udev);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
struct udev *udev = NULL;
|
||||
struct udev_enumerate *udev_enum = NULL;
|
||||
struct udev_list_entry *udev_list;
|
||||
struct udev_list_entry *udev_entry;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return FIDO_OK;
|
||||
|
||||
if (devlist == NULL)
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
|
||||
if ((udev = udev_new()) == NULL ||
|
||||
(udev_enum = udev_enumerate_new(udev)) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (udev_enumerate_add_match_subsystem(udev_enum, "nfc") < 0 ||
|
||||
udev_enumerate_scan_devices(udev_enum) < 0)
|
||||
goto fail;
|
||||
|
||||
if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
|
||||
r = FIDO_OK; /* zero nfc devices */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
udev_list_entry_foreach(udev_entry, udev_list) {
|
||||
if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_nfc_open,
|
||||
fido_nfc_close,
|
||||
fido_nfc_read,
|
||||
fido_nfc_write,
|
||||
};
|
||||
devlist[*olen].transport = (fido_dev_transport_t) {
|
||||
fido_nfc_rx,
|
||||
fido_nfc_tx,
|
||||
};
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
if (udev_enum != NULL)
|
||||
udev_enumerate_unref(udev_enum);
|
||||
if (udev != NULL)
|
||||
udev_unref(udev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
nfc_target_connect(struct nfc_linux *ctx)
|
||||
{
|
||||
struct sockaddr_nfc sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_family = AF_NFC;
|
||||
sa.dev_idx = ctx->dev;
|
||||
sa.target_idx = ctx->target;
|
||||
sa.nfc_protocol = NFC_PROTO_ISO14443;
|
||||
|
||||
if ((ctx->fd = socket(AF_NFC, SOCK_SEQPACKET | SOCK_CLOEXEC,
|
||||
NFC_SOCKPROTO_RAW)) == -1) {
|
||||
fido_log_error(errno, "%s: socket", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (connect(ctx->fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
|
||||
fido_log_error(errno, "%s: connect", __func__);
|
||||
if (close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
ctx->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nfc_free(struct nfc_linux **ctx_p)
|
||||
{
|
||||
struct nfc_linux *ctx;
|
||||
|
||||
if (ctx_p == NULL || (ctx = *ctx_p) == NULL)
|
||||
return;
|
||||
if (ctx->fd != -1 && close(ctx->fd) == -1)
|
||||
fido_log_error(errno, "%s: close", __func__);
|
||||
if (ctx->nl != NULL)
|
||||
fido_nl_free(&ctx->nl);
|
||||
|
||||
free(ctx);
|
||||
*ctx_p = NULL;
|
||||
}
|
||||
|
||||
static struct nfc_linux *
|
||||
nfc_new(uint32_t dev)
|
||||
{
|
||||
struct nfc_linux *ctx;
|
||||
|
||||
if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
|
||||
(ctx->nl = fido_nl_new()) == NULL) {
|
||||
nfc_free(&ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->fd = -1;
|
||||
ctx->dev = dev;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void *
|
||||
fido_nfc_open(const char *path)
|
||||
{
|
||||
struct nfc_linux *ctx = NULL;
|
||||
int idx;
|
||||
|
||||
if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) != 0) {
|
||||
fido_log_debug("%s: bad prefix", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((idx = sysnum_from_syspath(path + strlen(FIDO_NFC_PREFIX))) < 0 ||
|
||||
(ctx = nfc_new((uint32_t)idx)) == NULL) {
|
||||
fido_log_debug("%s: nfc_new", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (fido_nl_power_nfc(ctx->nl, ctx->dev) < 0 ||
|
||||
fido_nl_get_nfc_target(ctx->nl, ctx->dev, &ctx->target) < 0 ||
|
||||
nfc_target_connect(ctx) < 0) {
|
||||
fido_log_debug("%s: netlink", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
fail:
|
||||
nfc_free(&ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
fido_nfc_close(void *handle)
|
||||
{
|
||||
struct nfc_linux *ctx = handle;
|
||||
|
||||
nfc_free(&ctx);
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_set_sigmask(void *handle, const fido_sigset_t *sigmask)
|
||||
{
|
||||
struct nfc_linux *ctx = handle;
|
||||
|
||||
ctx->sigmask = *sigmask;
|
||||
ctx->sigmaskp = &ctx->sigmask;
|
||||
|
||||
return FIDO_OK;
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct nfc_linux *ctx = handle;
|
||||
struct iovec iov[2];
|
||||
uint8_t preamble;
|
||||
ssize_t r;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
iov[0].iov_base = &preamble;
|
||||
iov[0].iov_len = sizeof(preamble);
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = len;
|
||||
|
||||
if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
|
||||
fido_log_debug("%s: fido_hid_unix_wait", __func__);
|
||||
return -1;
|
||||
}
|
||||
if ((r = readv(ctx->fd, iov, nitems(iov))) == -1) {
|
||||
fido_log_error(errno, "%s: read", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (r < 1) {
|
||||
fido_log_debug("%s: %zd < 1", __func__, r);
|
||||
return -1;
|
||||
}
|
||||
if (preamble != 0x00) {
|
||||
fido_log_debug("%s: preamble", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
r--;
|
||||
fido_log_xxd(buf, (size_t)r, "%s", __func__);
|
||||
|
||||
return (int)r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_nfc_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct nfc_linux *ctx = handle;
|
||||
ssize_t r;
|
||||
|
||||
fido_log_xxd(buf, len, "%s", __func__);
|
||||
|
||||
if (len > INT_MAX) {
|
||||
fido_log_debug("%s: len", __func__);
|
||||
return -1;
|
||||
}
|
||||
if ((r = write(ctx->fd, buf, len)) == -1) {
|
||||
fido_log_error(errno, "%s: write", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (r < 0 || (size_t)r != len) {
|
||||
fido_log_debug("%s: %zd != %zu", __func__, r, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)r;
|
||||
}
|
||||
23
src/packed.h
Normal file
23
src/packed.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _PACKED_H
|
||||
#define _PACKED_H
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define PACKED_TYPE(type, def) \
|
||||
typedef def __attribute__ ((__packed__)) type;
|
||||
#elif defined(_MSC_VER)
|
||||
#define PACKED_TYPE(type, def) \
|
||||
__pragma(pack(push, 1)) \
|
||||
typedef def type; \
|
||||
__pragma(pack(pop))
|
||||
#else
|
||||
#error "please provide a way to define packed types on your platform"
|
||||
#endif
|
||||
|
||||
#endif /* !_PACKED_H */
|
||||
394
src/pcsc.c
Normal file
394
src/pcsc.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Micro Focus or one of its affiliates.
|
||||
* Copyright (c) 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
|
||||
*/
|
||||
|
||||
#if __APPLE__
|
||||
#include <PCSC/wintypes.h>
|
||||
#include <PCSC/winscard.h>
|
||||
#else
|
||||
#include <winscard.h>
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/param.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
#define SCardConnect SCardConnectA
|
||||
#define SCardListReaders SCardListReadersA
|
||||
#endif
|
||||
|
||||
#ifndef SCARD_PROTOCOL_Tx
|
||||
#define SCARD_PROTOCOL_Tx SCARD_PROTOCOL_ANY
|
||||
#endif
|
||||
|
||||
#define BUFSIZE 1024 /* in bytes; passed to SCardListReaders() */
|
||||
#define APDULEN 264 /* 261 rounded up to the nearest multiple of 8 */
|
||||
#define READERS 8 /* maximum number of readers */
|
||||
|
||||
struct pcsc {
|
||||
SCARDCONTEXT ctx;
|
||||
SCARDHANDLE h;
|
||||
SCARD_IO_REQUEST req;
|
||||
uint8_t rx_buf[APDULEN];
|
||||
size_t rx_len;
|
||||
};
|
||||
|
||||
static LONG
|
||||
list_readers(SCARDCONTEXT ctx, char **buf)
|
||||
{
|
||||
LONG s;
|
||||
DWORD len;
|
||||
|
||||
len = BUFSIZE;
|
||||
if ((*buf = calloc(1, len)) == NULL)
|
||||
goto fail;
|
||||
if ((s = SCardListReaders(ctx, NULL, *buf, &len)) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: SCardListReaders 0x%lx", __func__, (long)s);
|
||||
goto fail;
|
||||
}
|
||||
/* sanity check "multi-string" */
|
||||
if (len > BUFSIZE || len < 2) {
|
||||
fido_log_debug("%s: bogus len=%u", __func__, (unsigned)len);
|
||||
goto fail;
|
||||
}
|
||||
if ((*buf)[len - 1] != 0 || (*buf)[len - 2] != '\0') {
|
||||
fido_log_debug("%s: bogus buf", __func__);
|
||||
goto fail;
|
||||
}
|
||||
return (LONG)SCARD_S_SUCCESS;
|
||||
fail:
|
||||
free(*buf);
|
||||
*buf = NULL;
|
||||
|
||||
return (LONG)SCARD_E_NO_READERS_AVAILABLE;
|
||||
}
|
||||
|
||||
static char *
|
||||
get_reader(SCARDCONTEXT ctx, const char *path)
|
||||
{
|
||||
char *reader = NULL, *buf = NULL;
|
||||
const char prefix[] = FIDO_PCSC_PREFIX "//slot";
|
||||
uint64_t n;
|
||||
|
||||
if (path == NULL)
|
||||
goto out;
|
||||
if (strncmp(path, prefix, strlen(prefix)) != 0 ||
|
||||
fido_to_uint64(path + strlen(prefix), 10, &n) < 0 ||
|
||||
n > READERS - 1) {
|
||||
fido_log_debug("%s: invalid path %s", __func__, path);
|
||||
goto out;
|
||||
}
|
||||
if (list_readers(ctx, &buf) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: list_readers", __func__);
|
||||
goto out;
|
||||
}
|
||||
for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
|
||||
if (n == 0) {
|
||||
reader = strdup(name);
|
||||
goto out;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
fido_log_debug("%s: failed to find reader %s", __func__, path);
|
||||
out:
|
||||
free(buf);
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
static int
|
||||
prepare_io_request(DWORD prot, SCARD_IO_REQUEST *req)
|
||||
{
|
||||
switch (prot) {
|
||||
case SCARD_PROTOCOL_T0:
|
||||
req->dwProtocol = SCARD_PCI_T0->dwProtocol;
|
||||
req->cbPciLength = SCARD_PCI_T0->cbPciLength;
|
||||
break;
|
||||
case SCARD_PROTOCOL_T1:
|
||||
req->dwProtocol = SCARD_PCI_T1->dwProtocol;
|
||||
req->cbPciLength = SCARD_PCI_T1->cbPciLength;
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unknown protocol %lu", __func__,
|
||||
(u_long)prot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_info(fido_dev_info_t *di, SCARDCONTEXT ctx, const char *reader, size_t idx)
|
||||
{
|
||||
SCARDHANDLE h = 0;
|
||||
SCARD_IO_REQUEST req;
|
||||
DWORD prot = 0;
|
||||
LONG s;
|
||||
int ok = -1;
|
||||
|
||||
memset(di, 0, sizeof(*di));
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
|
||||
goto fail;
|
||||
}
|
||||
if (prepare_io_request(prot, &req) < 0) {
|
||||
fido_log_debug("%s: prepare_io_request", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (asprintf(&di->path, "%s//slot%zu", FIDO_PCSC_PREFIX, idx) == -1) {
|
||||
di->path = NULL;
|
||||
fido_log_debug("%s: asprintf", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (nfc_is_fido(di->path) == false) {
|
||||
fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path);
|
||||
goto fail;
|
||||
}
|
||||
if ((di->manufacturer = strdup("PC/SC")) == NULL ||
|
||||
(di->product = strdup(reader)) == NULL)
|
||||
goto fail;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (h != 0)
|
||||
SCardDisconnect(h, SCARD_LEAVE_CARD);
|
||||
if (ok < 0) {
|
||||
free(di->path);
|
||||
free(di->manufacturer);
|
||||
free(di->product);
|
||||
explicit_bzero(di, sizeof(*di));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int
|
||||
fido_pcsc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
|
||||
{
|
||||
SCARDCONTEXT ctx = 0;
|
||||
char *buf = NULL;
|
||||
LONG s;
|
||||
size_t idx = 0;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
*olen = 0;
|
||||
|
||||
if (ilen == 0)
|
||||
return FIDO_OK;
|
||||
if (devlist == NULL)
|
||||
return FIDO_ERR_INVALID_ARGUMENT;
|
||||
|
||||
if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
|
||||
&ctx)) != SCARD_S_SUCCESS || ctx == 0) {
|
||||
fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
|
||||
(long)s);
|
||||
if (s == (LONG)SCARD_E_NO_SERVICE ||
|
||||
s == (LONG)SCARD_E_NO_SMARTCARD)
|
||||
r = FIDO_OK; /* suppress error */
|
||||
goto out;
|
||||
}
|
||||
if ((s = list_readers(ctx, &buf)) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: list_readers 0x%lx", __func__, (long)s);
|
||||
if (s == (LONG)SCARD_E_NO_READERS_AVAILABLE)
|
||||
r = FIDO_OK; /* suppress error */
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (const char *name = buf; *name != 0; name += strlen(name) + 1) {
|
||||
if (idx == READERS) {
|
||||
fido_log_debug("%s: stopping at %zu readers", __func__,
|
||||
idx);
|
||||
r = FIDO_OK;
|
||||
goto out;
|
||||
}
|
||||
if (copy_info(&devlist[*olen], ctx, name, idx++) == 0) {
|
||||
devlist[*olen].io = (fido_dev_io_t) {
|
||||
fido_pcsc_open,
|
||||
fido_pcsc_close,
|
||||
fido_pcsc_read,
|
||||
fido_pcsc_write,
|
||||
};
|
||||
devlist[*olen].transport = (fido_dev_transport_t) {
|
||||
fido_pcsc_rx,
|
||||
fido_pcsc_tx,
|
||||
};
|
||||
if (++(*olen) == ilen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
free(buf);
|
||||
if (ctx != 0)
|
||||
SCardReleaseContext(ctx);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void *
|
||||
fido_pcsc_open(const char *path)
|
||||
{
|
||||
char *reader = NULL;
|
||||
struct pcsc *dev = NULL;
|
||||
SCARDCONTEXT ctx = 0;
|
||||
SCARDHANDLE h = 0;
|
||||
SCARD_IO_REQUEST req;
|
||||
DWORD prot = 0;
|
||||
LONG s;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
if ((s = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
|
||||
&ctx)) != SCARD_S_SUCCESS || ctx == 0) {
|
||||
fido_log_debug("%s: SCardEstablishContext 0x%lx", __func__,
|
||||
(long)s);
|
||||
goto fail;
|
||||
|
||||
}
|
||||
if ((reader = get_reader(ctx, path)) == NULL) {
|
||||
fido_log_debug("%s: get_reader(%s)", __func__, path);
|
||||
goto fail;
|
||||
}
|
||||
if ((s = SCardConnect(ctx, reader, SCARD_SHARE_SHARED,
|
||||
SCARD_PROTOCOL_Tx, &h, &prot)) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: SCardConnect 0x%lx", __func__, (long)s);
|
||||
goto fail;
|
||||
}
|
||||
if (prepare_io_request(prot, &req) < 0) {
|
||||
fido_log_debug("%s: prepare_io_request", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if ((dev = calloc(1, sizeof(*dev))) == NULL)
|
||||
goto fail;
|
||||
|
||||
dev->ctx = ctx;
|
||||
dev->h = h;
|
||||
dev->req = req;
|
||||
ctx = 0;
|
||||
h = 0;
|
||||
fail:
|
||||
if (h != 0)
|
||||
SCardDisconnect(h, SCARD_LEAVE_CARD);
|
||||
if (ctx != 0)
|
||||
SCardReleaseContext(ctx);
|
||||
free(reader);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void
|
||||
fido_pcsc_close(void *handle)
|
||||
{
|
||||
struct pcsc *dev = handle;
|
||||
|
||||
if (dev->h != 0)
|
||||
SCardDisconnect(dev->h, SCARD_LEAVE_CARD);
|
||||
if (dev->ctx != 0)
|
||||
SCardReleaseContext(dev->ctx);
|
||||
|
||||
explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
|
||||
free(dev);
|
||||
}
|
||||
|
||||
int
|
||||
fido_pcsc_read(void *handle, unsigned char *buf, size_t len, int ms)
|
||||
{
|
||||
struct pcsc *dev = handle;
|
||||
int r;
|
||||
|
||||
(void)ms;
|
||||
if (dev->rx_len == 0 || dev->rx_len > len ||
|
||||
dev->rx_len > sizeof(dev->rx_buf)) {
|
||||
fido_log_debug("%s: rx_len", __func__);
|
||||
return -1;
|
||||
}
|
||||
fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: reading", __func__);
|
||||
memcpy(buf, dev->rx_buf, dev->rx_len);
|
||||
explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
|
||||
r = (int)dev->rx_len;
|
||||
dev->rx_len = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
fido_pcsc_write(void *handle, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct pcsc *dev = handle;
|
||||
DWORD n;
|
||||
LONG s;
|
||||
|
||||
if (len > INT_MAX) {
|
||||
fido_log_debug("%s: len", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
|
||||
dev->rx_len = 0;
|
||||
n = (DWORD)sizeof(dev->rx_buf);
|
||||
|
||||
fido_log_xxd(buf, len, "%s: writing", __func__);
|
||||
|
||||
if ((s = SCardTransmit(dev->h, &dev->req, buf, (DWORD)len, NULL,
|
||||
dev->rx_buf, &n)) != SCARD_S_SUCCESS) {
|
||||
fido_log_debug("%s: SCardTransmit 0x%lx", __func__, (long)s);
|
||||
explicit_bzero(dev->rx_buf, sizeof(dev->rx_buf));
|
||||
return -1;
|
||||
}
|
||||
dev->rx_len = (size_t)n;
|
||||
|
||||
fido_log_xxd(dev->rx_buf, dev->rx_len, "%s: read", __func__);
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int
|
||||
fido_pcsc_tx(fido_dev_t *d, uint8_t cmd, const u_char *buf, size_t count)
|
||||
{
|
||||
return fido_nfc_tx(d, cmd, buf, count);
|
||||
}
|
||||
|
||||
int
|
||||
fido_pcsc_rx(fido_dev_t *d, uint8_t cmd, u_char *buf, size_t count, int ms)
|
||||
{
|
||||
return fido_nfc_rx(d, cmd, buf, count, ms);
|
||||
}
|
||||
|
||||
bool
|
||||
fido_is_pcsc(const char *path)
|
||||
{
|
||||
return strncmp(path, FIDO_PCSC_PREFIX, strlen(FIDO_PCSC_PREFIX)) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_pcsc(fido_dev_t *d)
|
||||
{
|
||||
if (d->io_handle != NULL) {
|
||||
fido_log_debug("%s: device open", __func__);
|
||||
return -1;
|
||||
}
|
||||
d->io_own = true;
|
||||
d->io = (fido_dev_io_t) {
|
||||
fido_pcsc_open,
|
||||
fido_pcsc_close,
|
||||
fido_pcsc_read,
|
||||
fido_pcsc_write,
|
||||
};
|
||||
d->transport = (fido_dev_transport_t) {
|
||||
fido_pcsc_rx,
|
||||
fido_pcsc_tx,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
725
src/pin.c
Normal file
725
src/pin.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* 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 <openssl/sha.h>
|
||||
#include "fido.h"
|
||||
#include "fido/es256.h"
|
||||
|
||||
#define CTAP21_UV_TOKEN_PERM_MAKECRED 0x01
|
||||
#define CTAP21_UV_TOKEN_PERM_ASSERT 0x02
|
||||
#define CTAP21_UV_TOKEN_PERM_CRED_MGMT 0x04
|
||||
#define CTAP21_UV_TOKEN_PERM_BIO 0x08
|
||||
#define CTAP21_UV_TOKEN_PERM_LARGEBLOB 0x10
|
||||
#define CTAP21_UV_TOKEN_PERM_CONFIG 0x20
|
||||
|
||||
int
|
||||
fido_sha256(fido_blob_t *digest, const u_char *data, size_t data_len)
|
||||
{
|
||||
if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
|
||||
return (-1);
|
||||
|
||||
digest->len = SHA256_DIGEST_LENGTH;
|
||||
|
||||
if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
|
||||
fido_blob_reset(digest);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
pin_sha256_enc(const fido_dev_t *dev, const fido_blob_t *shared,
|
||||
const fido_blob_t *pin, fido_blob_t **out)
|
||||
{
|
||||
fido_blob_t *ph = NULL;
|
||||
int r;
|
||||
|
||||
if ((*out = fido_blob_new()) == NULL ||
|
||||
(ph = fido_blob_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_sha256(ph, pin->ptr, pin->len) < 0 || ph->len < 16) {
|
||||
fido_log_debug("%s: SHA256", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ph->len = 16; /* first 16 bytes */
|
||||
|
||||
if (aes256_cbc_enc(dev, shared, ph, *out) < 0) {
|
||||
fido_log_debug("%s: aes256_cbc_enc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&ph);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
pad64(const char *pin, fido_blob_t **ppin)
|
||||
{
|
||||
size_t pin_len;
|
||||
size_t ppin_len;
|
||||
|
||||
pin_len = strlen(pin);
|
||||
if (pin_len < 4 || pin_len > 63) {
|
||||
fido_log_debug("%s: invalid pin length", __func__);
|
||||
return (FIDO_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
|
||||
if ((*ppin = fido_blob_new()) == NULL)
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
|
||||
ppin_len = (pin_len + 63U) & ~63U;
|
||||
if (ppin_len < pin_len ||
|
||||
((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
|
||||
fido_blob_free(ppin);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
memcpy((*ppin)->ptr, pin, pin_len);
|
||||
(*ppin)->len = ppin_len;
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
pin_pad64_enc(const fido_dev_t *dev, const fido_blob_t *shared,
|
||||
const char *pin, fido_blob_t **out)
|
||||
{
|
||||
fido_blob_t *ppin = NULL;
|
||||
int r;
|
||||
|
||||
if ((r = pad64(pin, &ppin)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pad64", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((*out = fido_blob_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (aes256_cbc_enc(dev, shared, ppin, *out) < 0) {
|
||||
fido_log_debug("%s: aes256_cbc_enc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&ppin);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static cbor_item_t *
|
||||
encode_uv_permission(uint8_t cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case CTAP_CBOR_ASSERT:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_ASSERT));
|
||||
case CTAP_CBOR_BIO_ENROLL_PRE:
|
||||
case CTAP_CBOR_BIO_ENROLL:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_BIO));
|
||||
case CTAP_CBOR_CONFIG:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CONFIG));
|
||||
case CTAP_CBOR_MAKECRED:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_MAKECRED));
|
||||
case CTAP_CBOR_CRED_MGMT_PRE:
|
||||
case CTAP_CBOR_CRED_MGMT:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CRED_MGMT));
|
||||
case CTAP_CBOR_LARGEBLOB:
|
||||
return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_LARGEBLOB));
|
||||
default:
|
||||
fido_log_debug("%s: cmd 0x%02x", __func__, cmd);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
|
||||
const es256_pk_t *pk, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
fido_blob_t *p = NULL;
|
||||
fido_blob_t *phe = NULL;
|
||||
cbor_item_t *argv[6];
|
||||
int r;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if (pin == NULL) {
|
||||
fido_log_debug("%s: NULL pin", __func__);
|
||||
r = FIDO_ERR_PIN_REQUIRED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
|
||||
(const unsigned char *)pin, strlen(pin)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pin_sha256_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(5)) == NULL ||
|
||||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
|
||||
(argv[5] = fido_blob_encode(phe)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
fido_blob_free(&p);
|
||||
fido_blob_free(&phe);
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
|
||||
const es256_pk_t *pk, uint8_t cmd, const char *rpid, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
fido_blob_t *p = NULL;
|
||||
fido_blob_t *phe = NULL;
|
||||
cbor_item_t *argv[10];
|
||||
uint8_t subcmd;
|
||||
int r;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if (pin != NULL) {
|
||||
if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
|
||||
(const unsigned char *)pin, strlen(pin)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pin_sha256_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
subcmd = 9; /* getPinUvAuthTokenUsingPinWithPermissions */
|
||||
} else {
|
||||
if (fido_dev_has_uv(dev) == false) {
|
||||
fido_log_debug("%s: fido_dev_has_uv", __func__);
|
||||
r = FIDO_ERR_PIN_REQUIRED;
|
||||
goto fail;
|
||||
}
|
||||
subcmd = 6; /* getPinUvAuthTokenUsingUvWithPermissions */
|
||||
}
|
||||
|
||||
if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(subcmd)) == NULL ||
|
||||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
|
||||
(phe != NULL && (argv[5] = fido_blob_encode(phe)) == NULL) ||
|
||||
(argv[8] = encode_uv_permission(cmd)) == NULL ||
|
||||
(rpid != NULL && (argv[9] = cbor_build_string(rpid)) == NULL)) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
fido_blob_free(&p);
|
||||
fido_blob_free(&phe);
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_uv_token(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
fido_blob_t *token = arg;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != 2) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
return (fido_blob_decode(val, token));
|
||||
}
|
||||
|
||||
static int
|
||||
uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token,
|
||||
int *ms)
|
||||
{
|
||||
fido_blob_t *aes_token = NULL;
|
||||
unsigned char *msg = NULL;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
if ((aes_token = fido_blob_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, aes_token,
|
||||
parse_uv_token)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_uv_token", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (aes256_cbc_dec(dev, ecdh, aes_token, token) < 0) {
|
||||
fido_log_debug("%s: aes256_cbc_dec", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&aes_token);
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin,
|
||||
const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
|
||||
fido_blob_t *token, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ecdh == NULL || pk == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
if (fido_dev_supports_permissions(dev))
|
||||
r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid, ms);
|
||||
else
|
||||
r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms);
|
||||
if (r != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (uv_token_rx(dev, ecdh, token, ms));
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_uv_token(fido_dev_t *dev, uint8_t cmd, const char *pin,
|
||||
const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
|
||||
fido_blob_t *token, int *ms)
|
||||
{
|
||||
return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms));
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin,
|
||||
int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
fido_blob_t *ppine = NULL;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
fido_blob_t *opin = NULL;
|
||||
fido_blob_t *opinhe = NULL;
|
||||
cbor_item_t *argv[6];
|
||||
es256_pk_t *pk = NULL;
|
||||
int r;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
|
||||
(const unsigned char *)oldpin, strlen(oldpin)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* pad and encrypt new pin */
|
||||
if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pin_pad64_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* hash and encrypt old pin */
|
||||
if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pin_sha256_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(4)) == NULL ||
|
||||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
|
||||
(argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL ||
|
||||
(argv[4] = fido_blob_encode(ppine)) == NULL ||
|
||||
(argv[5] = fido_blob_encode(opinhe)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ppine);
|
||||
fido_blob_free(&ecdh);
|
||||
fido_blob_free(&opin);
|
||||
fido_blob_free(&opinhe);
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
fido_blob_t *ppine = NULL;
|
||||
fido_blob_t *ecdh = NULL;
|
||||
cbor_item_t *argv[5];
|
||||
es256_pk_t *pk = NULL;
|
||||
int r;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_do_ecdh", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
|
||||
fido_log_debug("%s: pin_pad64_enc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(3)) == NULL ||
|
||||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
|
||||
(argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL ||
|
||||
(argv[4] = fido_blob_encode(ppine)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
es256_pk_free(&pk);
|
||||
fido_blob_free(&ppine);
|
||||
fido_blob_free(&ecdh);
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
|
||||
int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (oldpin != NULL) {
|
||||
if ((r = fido_dev_change_pin_tx(dev, pin, oldpin,
|
||||
ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
|
||||
return (r);
|
||||
}
|
||||
} else {
|
||||
if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
|
||||
return (r);
|
||||
}
|
||||
}
|
||||
|
||||
if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_rx_cbor_status", __func__);
|
||||
return (r);
|
||||
}
|
||||
|
||||
if (dev->flags & FIDO_DEV_PIN_UNSET) {
|
||||
dev->flags &= ~FIDO_DEV_PIN_UNSET;
|
||||
dev->flags |= FIDO_DEV_PIN_SET;
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
parse_retry_count(const uint8_t keyval, const cbor_item_t *key,
|
||||
const cbor_item_t *val, void *arg)
|
||||
{
|
||||
int *retries = arg;
|
||||
uint64_t n;
|
||||
|
||||
if (cbor_isa_uint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8 ||
|
||||
cbor_get_uint8(key) != keyval) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
|
||||
fido_log_debug("%s: cbor_decode_uint64", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*retries = (int)n;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
return (parse_retry_count(3, key, val, arg));
|
||||
}
|
||||
|
||||
static int
|
||||
parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
return (parse_retry_count(5, key, val, arg));
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms)
|
||||
{
|
||||
fido_blob_t f;
|
||||
cbor_item_t *argv[2];
|
||||
int r;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
|
||||
(argv[1] = cbor_build_uint8(subcmd)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
|
||||
&f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
free(f.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
*retries = 0;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
|
||||
parse_pin_retry_count)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_pin_retry_count", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK ||
|
||||
(r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms));
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
|
||||
{
|
||||
unsigned char *msg;
|
||||
int msglen;
|
||||
int r;
|
||||
|
||||
*retries = 0;
|
||||
|
||||
if ((msg = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((msglen = fido_rx(dev, CTAP_CMD_CBOR, msg, FIDO_MAXMSG, ms)) < 0) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = cbor_parse_reply(msg, (size_t)msglen, retries,
|
||||
parse_uv_retry_count)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_uv_retry_count", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
freezero(msg, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK ||
|
||||
(r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms));
|
||||
}
|
||||
|
||||
int
|
||||
cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
|
||||
const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
|
||||
const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms)
|
||||
{
|
||||
fido_blob_t *token = NULL;
|
||||
int r;
|
||||
|
||||
if ((token = fido_blob_new()) == NULL) {
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
|
||||
token, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_dev_get_uv_token", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL ||
|
||||
(*opt = cbor_encode_pin_opt(dev)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_free(&token);
|
||||
|
||||
return (r);
|
||||
}
|
||||
83
src/random.c
Normal file
83
src/random.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_SYS_RANDOM_H
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
#include <winternl.h>
|
||||
#include <winerror.h>
|
||||
#include <stdio.h>
|
||||
#include <bcrypt.h>
|
||||
#include <sal.h>
|
||||
|
||||
int
|
||||
fido_get_random(void *buf, size_t len)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = BCryptGenRandom(NULL, buf, (ULONG)len,
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#elif defined(HAVE_ARC4RANDOM_BUF)
|
||||
int
|
||||
fido_get_random(void *buf, size_t len)
|
||||
{
|
||||
arc4random_buf(buf, len);
|
||||
return (0);
|
||||
}
|
||||
#elif defined(HAVE_GETRANDOM)
|
||||
int
|
||||
fido_get_random(void *buf, size_t len)
|
||||
{
|
||||
ssize_t r;
|
||||
|
||||
if ((r = getrandom(buf, len, 0)) < 0 || (size_t)r != len)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#elif defined(HAVE_DEV_URANDOM)
|
||||
int
|
||||
fido_get_random(void *buf, size_t len)
|
||||
{
|
||||
int fd = -1;
|
||||
int ok = -1;
|
||||
ssize_t r;
|
||||
|
||||
if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
|
||||
goto fail;
|
||||
if ((r = read(fd, buf, len)) < 0 || (size_t)r != len)
|
||||
goto fail;
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
#else
|
||||
#error "please provide an implementation of fido_get_random() for your platform"
|
||||
#endif /* _WIN32 */
|
||||
46
src/reset.c
Normal file
46
src/reset.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int
|
||||
fido_dev_reset_tx(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
const unsigned char cbor[] = { CTAP_CBOR_RESET };
|
||||
|
||||
if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
return (FIDO_ERR_TX);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
fido_dev_reset_wait(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = fido_dev_reset_tx(dev, ms)) != FIDO_OK ||
|
||||
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
|
||||
return (r);
|
||||
|
||||
if (dev->flags & FIDO_DEV_PIN_SET) {
|
||||
dev->flags &= ~FIDO_DEV_PIN_SET;
|
||||
dev->flags |= FIDO_DEV_PIN_UNSET;
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_reset(fido_dev_t *dev)
|
||||
{
|
||||
int ms = dev->timeout_ms;
|
||||
|
||||
return (fido_dev_reset_wait(dev, &ms));
|
||||
}
|
||||
65
src/rs1.c
Normal file
65
src/rs1.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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/rsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define PRAGMA(s) _Pragma(s)
|
||||
#else
|
||||
#define PRAGMA(s)
|
||||
#endif
|
||||
|
||||
static EVP_MD *
|
||||
rs1_get_EVP_MD(void)
|
||||
{
|
||||
PRAGMA("GCC diagnostic push")
|
||||
PRAGMA("GCC diagnostic ignored \"-Wcast-qual\"")
|
||||
return ((EVP_MD *)EVP_sha1());
|
||||
PRAGMA("GCC diagnostic pop")
|
||||
}
|
||||
|
||||
int
|
||||
rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
|
||||
fido_log_debug("%s: EVP_PKEY_base_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((md = rs1_get_EVP_MD()) == NULL) {
|
||||
fido_log_debug("%s: rs1_get_EVP_MD", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
|
||||
EVP_PKEY_verify_init(pctx) != 1 ||
|
||||
EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 ||
|
||||
EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_CTX", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
|
||||
dgst->len) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_verify", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
281
src/rs256.c
Normal file
281
src/rs256.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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 <openssl/bn.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/rs256.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000
|
||||
#define get0_RSA(x) EVP_PKEY_get0_RSA((x))
|
||||
#else
|
||||
#define get0_RSA(x) EVP_PKEY_get0((x))
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define PRAGMA(s) _Pragma(s)
|
||||
#else
|
||||
#define PRAGMA(s)
|
||||
#endif
|
||||
|
||||
static EVP_MD *
|
||||
rs256_get_EVP_MD(void)
|
||||
{
|
||||
PRAGMA("GCC diagnostic push")
|
||||
PRAGMA("GCC diagnostic ignored \"-Wcast-qual\"")
|
||||
return ((EVP_MD *)EVP_sha256());
|
||||
PRAGMA("GCC diagnostic pop")
|
||||
}
|
||||
|
||||
static int
|
||||
decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
|
||||
{
|
||||
if (cbor_isa_bytestring(item) == false ||
|
||||
cbor_bytestring_is_definite(item) == false ||
|
||||
cbor_bytestring_length(item) != len) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(ptr, cbor_bytestring_handle(item), len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
|
||||
{
|
||||
rs256_pk_t *k = arg;
|
||||
|
||||
if (cbor_isa_negint(key) == false ||
|
||||
cbor_int_get_width(key) != CBOR_INT_8)
|
||||
return (0); /* ignore */
|
||||
|
||||
switch (cbor_get_uint8(key)) {
|
||||
case 0: /* modulus */
|
||||
return (decode_bignum(val, &k->n, sizeof(k->n)));
|
||||
case 1: /* public exponent */
|
||||
return (decode_bignum(val, &k->e, sizeof(k->e)));
|
||||
}
|
||||
|
||||
return (0); /* ignore */
|
||||
}
|
||||
|
||||
int
|
||||
rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k)
|
||||
{
|
||||
if (cbor_isa_map(item) == false ||
|
||||
cbor_map_is_definite(item) == false ||
|
||||
cbor_map_iter(item, k, decode_rsa_pubkey) < 0) {
|
||||
fido_log_debug("%s: cbor type", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
rs256_pk_t *
|
||||
rs256_pk_new(void)
|
||||
{
|
||||
return (calloc(1, sizeof(rs256_pk_t)));
|
||||
}
|
||||
|
||||
void
|
||||
rs256_pk_free(rs256_pk_t **pkp)
|
||||
{
|
||||
rs256_pk_t *pk;
|
||||
|
||||
if (pkp == NULL || (pk = *pkp) == NULL)
|
||||
return;
|
||||
|
||||
freezero(pk, sizeof(*pk));
|
||||
*pkp = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
|
||||
if (len < sizeof(*pk))
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
memcpy(pk, ptr, sizeof(*pk));
|
||||
|
||||
if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
|
||||
fido_log_debug("%s: rs256_pk_to_EVP_PKEY", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
EVP_PKEY *
|
||||
rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
|
||||
{
|
||||
RSA *rsa = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
BIGNUM *n = NULL;
|
||||
BIGNUM *e = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((n = BN_new()) == NULL || (e = BN_new()) == NULL)
|
||||
goto fail;
|
||||
|
||||
if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL ||
|
||||
BN_bin2bn(k->e, sizeof(k->e), e) == NULL) {
|
||||
fido_log_debug("%s: BN_bin2bn", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) {
|
||||
fido_log_debug("%s: RSA_set0_key", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* at this point, n and e belong to rsa */
|
||||
n = NULL;
|
||||
e = NULL;
|
||||
|
||||
if (RSA_bits(rsa) != 2048) {
|
||||
fido_log_debug("%s: invalid key length", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pkey = EVP_PKEY_new()) == NULL ||
|
||||
EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
|
||||
fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rsa = NULL; /* at this point, rsa belongs to evp */
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (n != NULL)
|
||||
BN_free(n);
|
||||
if (e != NULL)
|
||||
BN_free(e);
|
||||
if (rsa != NULL)
|
||||
RSA_free(rsa);
|
||||
if (ok < 0 && pkey != NULL) {
|
||||
EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
}
|
||||
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int
|
||||
rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
|
||||
{
|
||||
const BIGNUM *n = NULL;
|
||||
const BIGNUM *e = NULL;
|
||||
const BIGNUM *d = NULL;
|
||||
int k;
|
||||
|
||||
if (RSA_bits(rsa) != 2048) {
|
||||
fido_log_debug("%s: invalid key length", __func__);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
RSA_get0_key(rsa, &n, &e, &d);
|
||||
|
||||
if (n == NULL || e == NULL) {
|
||||
fido_log_debug("%s: RSA_get0_key", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) ||
|
||||
(k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) {
|
||||
fido_log_debug("%s: invalid key", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) ||
|
||||
(k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) {
|
||||
fido_log_debug("%s: BN_bn2bin", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
rs256_pk_from_EVP_PKEY(rs256_pk_t *pk, const EVP_PKEY *pkey)
|
||||
{
|
||||
const RSA *rsa;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA ||
|
||||
(rsa = get0_RSA(pkey)) == NULL)
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
|
||||
return (rs256_pk_from_RSA(pk, rsa));
|
||||
}
|
||||
|
||||
int
|
||||
rs256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
|
||||
fido_log_debug("%s: EVP_PKEY_base_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((md = rs256_get_EVP_MD()) == NULL) {
|
||||
fido_log_debug("%s: rs256_get_EVP_MD", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
|
||||
EVP_PKEY_verify_init(pctx) != 1 ||
|
||||
EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 ||
|
||||
EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_CTX", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
|
||||
dgst->len) != 1) {
|
||||
fido_log_debug("%s: EVP_PKEY_verify", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
int
|
||||
rs256_pk_verify_sig(const fido_blob_t *dgst, const rs256_pk_t *pk,
|
||||
const fido_blob_t *sig)
|
||||
{
|
||||
EVP_PKEY *pkey;
|
||||
int ok = -1;
|
||||
|
||||
if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
|
||||
rs256_verify_sig(dgst, pkey, sig) < 0) {
|
||||
fido_log_debug("%s: rs256_verify_sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
75
src/time.c
Normal file
75
src/time.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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 <errno.h>
|
||||
#include "fido.h"
|
||||
|
||||
static int
|
||||
timespec_to_ms(const struct timespec *ts)
|
||||
{
|
||||
int64_t x, y;
|
||||
|
||||
if (ts->tv_sec < 0 || ts->tv_nsec < 0 ||
|
||||
ts->tv_nsec >= 1000000000LL)
|
||||
return -1;
|
||||
|
||||
if ((uint64_t)ts->tv_sec >= INT64_MAX / 1000LL)
|
||||
return -1;
|
||||
|
||||
x = ts->tv_sec * 1000LL;
|
||||
y = ts->tv_nsec / 1000000LL;
|
||||
|
||||
if (INT64_MAX - x < y || x + y > INT_MAX)
|
||||
return -1;
|
||||
|
||||
return (int)(x + y);
|
||||
}
|
||||
|
||||
int
|
||||
fido_time_now(struct timespec *ts_now)
|
||||
{
|
||||
if (clock_gettime(CLOCK_MONOTONIC, ts_now) != 0) {
|
||||
fido_log_error(errno, "%s: clock_gettime", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_time_delta(const struct timespec *ts_start, int *ms_remain)
|
||||
{
|
||||
struct timespec ts_end, ts_delta;
|
||||
int ms;
|
||||
|
||||
if (*ms_remain < 0)
|
||||
return 0;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts_end) != 0) {
|
||||
fido_log_error(errno, "%s: clock_gettime", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timespeccmp(&ts_end, ts_start, <)) {
|
||||
fido_log_debug("%s: timespeccmp", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
timespecsub(&ts_end, ts_start, &ts_delta);
|
||||
|
||||
if ((ms = timespec_to_ms(&ts_delta)) < 0) {
|
||||
fido_log_debug("%s: timespec_to_ms", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ms > *ms_remain)
|
||||
ms = *ms_remain;
|
||||
|
||||
*ms_remain -= ms;
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
src/touch.c
Normal file
109
src/touch.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 <openssl/sha.h>
|
||||
#include "fido.h"
|
||||
|
||||
int
|
||||
fido_dev_get_touch_begin(fido_dev_t *dev)
|
||||
{
|
||||
fido_blob_t f;
|
||||
cbor_item_t *argv[9];
|
||||
const char *clientdata = FIDO_DUMMY_CLIENTDATA;
|
||||
const uint8_t user_id = FIDO_DUMMY_USER_ID;
|
||||
unsigned char cdh[SHA256_DIGEST_LENGTH];
|
||||
fido_rp_t rp;
|
||||
fido_user_t user;
|
||||
int ms = dev->timeout_ms;
|
||||
int r = FIDO_ERR_INTERNAL;
|
||||
|
||||
memset(&f, 0, sizeof(f));
|
||||
memset(argv, 0, sizeof(argv));
|
||||
memset(cdh, 0, sizeof(cdh));
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
memset(&user, 0, sizeof(user));
|
||||
|
||||
if (fido_dev_is_fido2(dev) == false)
|
||||
return (u2f_get_touch_begin(dev, &ms));
|
||||
|
||||
if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
|
||||
(user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
|
||||
fido_log_debug("%s: strdup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
|
||||
(argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
|
||||
(argv[2] = cbor_encode_user_entity(&user)) == NULL ||
|
||||
(argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_dev_supports_pin(dev)) {
|
||||
if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
|
||||
(argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
|
||||
fido_log_debug("%s: cbor encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
|
||||
fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, &ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
cbor_vector_free(argv, nitems(argv));
|
||||
free(f.ptr);
|
||||
free(rp.id);
|
||||
free(user.name);
|
||||
free(user.id.ptr);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
|
||||
{
|
||||
int r;
|
||||
|
||||
*touched = 0;
|
||||
|
||||
if (fido_dev_is_fido2(dev) == false)
|
||||
return (u2f_get_touch_status(dev, touched, &ms));
|
||||
|
||||
switch ((r = fido_rx_cbor_status(dev, &ms))) {
|
||||
case FIDO_ERR_PIN_AUTH_INVALID:
|
||||
case FIDO_ERR_PIN_INVALID:
|
||||
case FIDO_ERR_PIN_NOT_SET:
|
||||
case FIDO_ERR_SUCCESS:
|
||||
*touched = 1;
|
||||
break;
|
||||
case FIDO_ERR_RX:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: fido_rx_cbor_status", __func__);
|
||||
return (r);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
391
src/tpm.c
Normal file
391
src/tpm.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Trusted Platform Module (TPM) 2.0 attestation support. Documentation
|
||||
* references are relative to revision 01.38 of the TPM 2.0 specification.
|
||||
*/
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "packed.h"
|
||||
#include "fido.h"
|
||||
|
||||
/* Part 1, 4.89: TPM_GENERATED_VALUE */
|
||||
#define TPM_MAGIC 0xff544347
|
||||
|
||||
/* Part 2, 6.3: TPM_ALG_ID */
|
||||
#define TPM_ALG_RSA 0x0001
|
||||
#define TPM_ALG_SHA256 0x000b
|
||||
#define TPM_ALG_NULL 0x0010
|
||||
#define TPM_ALG_ECC 0x0023
|
||||
|
||||
/* Part 2, 6.4: TPM_ECC_CURVE */
|
||||
#define TPM_ECC_P256 0x0003
|
||||
|
||||
/* Part 2, 6.9: TPM_ST_ATTEST_CERTIFY */
|
||||
#define TPM_ST_CERTIFY 0x8017
|
||||
|
||||
/* Part 2, 8.3: TPMA_OBJECT */
|
||||
#define TPMA_RESERVED 0xfff8f309 /* reserved bits; must be zero */
|
||||
#define TPMA_FIXED 0x00000002 /* object has fixed hierarchy */
|
||||
#define TPMA_CLEAR 0x00000004 /* object persists */
|
||||
#define TPMA_FIXED_P 0x00000010 /* object has fixed parent */
|
||||
#define TPMA_SENSITIVE 0x00000020 /* data originates within tpm */
|
||||
#define TPMA_SIGN 0x00040000 /* object may sign */
|
||||
|
||||
/* Part 2, 10.4.2: TPM2B_DIGEST */
|
||||
PACKED_TYPE(tpm_sha256_digest_t,
|
||||
struct tpm_sha256_digest {
|
||||
uint16_t size; /* sizeof(body) */
|
||||
uint8_t body[32];
|
||||
})
|
||||
|
||||
/* Part 2, 10.4.3: TPM2B_DATA */
|
||||
PACKED_TYPE(tpm_sha1_data_t,
|
||||
struct tpm_sha1_data {
|
||||
uint16_t size; /* sizeof(body) */
|
||||
uint8_t body[20];
|
||||
})
|
||||
|
||||
/* Part 2, 10.5.3: TPM2B_NAME */
|
||||
PACKED_TYPE(tpm_sha256_name_t,
|
||||
struct tpm_sha256_name {
|
||||
uint16_t size; /* sizeof(alg) + sizeof(body) */
|
||||
uint16_t alg; /* TPM_ALG_SHA256 */
|
||||
uint8_t body[32];
|
||||
})
|
||||
|
||||
/* Part 2, 10.11.1: TPMS_CLOCK_INFO */
|
||||
PACKED_TYPE(tpm_clock_info_t,
|
||||
struct tpm_clock_info {
|
||||
uint64_t timestamp_ms;
|
||||
uint32_t reset_count; /* obfuscated by tpm */
|
||||
uint32_t restart_count; /* obfuscated by tpm */
|
||||
uint8_t safe; /* 1 if timestamp_ms is current */
|
||||
})
|
||||
|
||||
/* Part 2, 10.12.8 TPMS_ATTEST */
|
||||
PACKED_TYPE(tpm_sha1_attest_t,
|
||||
struct tpm_sha1_attest {
|
||||
uint32_t magic; /* TPM_MAGIC */
|
||||
uint16_t type; /* TPM_ST_ATTEST_CERTIFY */
|
||||
tpm_sha256_name_t signer; /* full tpm path of signing key */
|
||||
tpm_sha1_data_t data; /* signed sha1 */
|
||||
tpm_clock_info_t clock;
|
||||
uint64_t fwversion; /* obfuscated by tpm */
|
||||
tpm_sha256_name_t name; /* sha256 of tpm_rs256_pubarea_t */
|
||||
tpm_sha256_name_t qual_name; /* full tpm path of attested key */
|
||||
})
|
||||
|
||||
/* Part 2, 11.2.4.5: TPM2B_PUBLIC_KEY_RSA */
|
||||
PACKED_TYPE(tpm_rs256_key_t,
|
||||
struct tpm_rs256_key {
|
||||
uint16_t size; /* sizeof(body) */
|
||||
uint8_t body[256];
|
||||
})
|
||||
|
||||
/* Part 2, 11.2.5.1: TPM2B_ECC_PARAMETER */
|
||||
PACKED_TYPE(tpm_es256_coord_t,
|
||||
struct tpm_es256_coord {
|
||||
uint16_t size; /* sizeof(body) */
|
||||
uint8_t body[32];
|
||||
})
|
||||
|
||||
/* Part 2, 11.2.5.2: TPMS_ECC_POINT */
|
||||
PACKED_TYPE(tpm_es256_point_t,
|
||||
struct tpm_es256_point {
|
||||
tpm_es256_coord_t x;
|
||||
tpm_es256_coord_t y;
|
||||
})
|
||||
|
||||
/* Part 2, 12.2.3.5: TPMS_RSA_PARMS */
|
||||
PACKED_TYPE(tpm_rs256_param_t,
|
||||
struct tpm_rs256_param {
|
||||
uint16_t symmetric; /* TPM_ALG_NULL */
|
||||
uint16_t scheme; /* TPM_ALG_NULL */
|
||||
uint16_t keybits; /* 2048 */
|
||||
uint32_t exponent; /* zero (meaning 2^16 + 1) */
|
||||
})
|
||||
|
||||
/* Part 2, 12.2.3.6: TPMS_ECC_PARMS */
|
||||
PACKED_TYPE(tpm_es256_param_t,
|
||||
struct tpm_es256_param {
|
||||
uint16_t symmetric; /* TPM_ALG_NULL */
|
||||
uint16_t scheme; /* TPM_ALG_NULL */
|
||||
uint16_t curve_id; /* TPM_ECC_P256 */
|
||||
uint16_t kdf; /* TPM_ALG_NULL */
|
||||
})
|
||||
|
||||
/* Part 2, 12.2.4: TPMT_PUBLIC */
|
||||
PACKED_TYPE(tpm_rs256_pubarea_t,
|
||||
struct tpm_rs256_pubarea {
|
||||
uint16_t alg; /* TPM_ALG_RSA */
|
||||
uint16_t hash; /* TPM_ALG_SHA256 */
|
||||
uint32_t attr;
|
||||
tpm_sha256_digest_t policy; /* must be present? */
|
||||
tpm_rs256_param_t param;
|
||||
tpm_rs256_key_t key;
|
||||
})
|
||||
|
||||
/* Part 2, 12.2.4: TPMT_PUBLIC */
|
||||
PACKED_TYPE(tpm_es256_pubarea_t,
|
||||
struct tpm_es256_pubarea {
|
||||
uint16_t alg; /* TPM_ALG_ECC */
|
||||
uint16_t hash; /* TPM_ALG_SHA256 */
|
||||
uint32_t attr;
|
||||
tpm_sha256_digest_t policy; /* must be present? */
|
||||
tpm_es256_param_t param;
|
||||
tpm_es256_point_t point;
|
||||
})
|
||||
|
||||
static int
|
||||
get_signed_sha1(tpm_sha1_data_t *dgst, const fido_blob_t *authdata,
|
||||
const fido_blob_t *clientdata)
|
||||
{
|
||||
const EVP_MD *md = NULL;
|
||||
EVP_MD_CTX *ctx = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if ((dgst->size = sizeof(dgst->body)) != SHA_DIGEST_LENGTH ||
|
||||
(md = EVP_sha1()) == NULL ||
|
||||
(ctx = EVP_MD_CTX_new()) == NULL ||
|
||||
EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
|
||||
EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 ||
|
||||
EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
|
||||
EVP_DigestFinal_ex(ctx, dgst->body, NULL) != 1) {
|
||||
fido_log_debug("%s: sha1", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
EVP_MD_CTX_free(ctx);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
get_signed_name(tpm_sha256_name_t *name, const fido_blob_t *pubarea)
|
||||
{
|
||||
name->alg = TPM_ALG_SHA256;
|
||||
name->size = sizeof(name->alg) + sizeof(name->body);
|
||||
if (sizeof(name->body) != SHA256_DIGEST_LENGTH ||
|
||||
SHA256(pubarea->ptr, pubarea->len, name->body) != name->body) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bswap_rs256_pubarea(tpm_rs256_pubarea_t *x)
|
||||
{
|
||||
x->alg = htobe16(x->alg);
|
||||
x->hash = htobe16(x->hash);
|
||||
x->attr = htobe32(x->attr);
|
||||
x->policy.size = htobe16(x->policy.size);
|
||||
x->param.symmetric = htobe16(x->param.symmetric);
|
||||
x->param.scheme = htobe16(x->param.scheme);
|
||||
x->param.keybits = htobe16(x->param.keybits);
|
||||
x->key.size = htobe16(x->key.size);
|
||||
}
|
||||
|
||||
static void
|
||||
bswap_es256_pubarea(tpm_es256_pubarea_t *x)
|
||||
{
|
||||
x->alg = htobe16(x->alg);
|
||||
x->hash = htobe16(x->hash);
|
||||
x->attr = htobe32(x->attr);
|
||||
x->policy.size = htobe16(x->policy.size);
|
||||
x->param.symmetric = htobe16(x->param.symmetric);
|
||||
x->param.scheme = htobe16(x->param.scheme);
|
||||
x->param.curve_id = htobe16(x->param.curve_id);
|
||||
x->param.kdf = htobe16(x->param.kdf);
|
||||
x->point.x.size = htobe16(x->point.x.size);
|
||||
x->point.y.size = htobe16(x->point.y.size);
|
||||
}
|
||||
|
||||
static void
|
||||
bswap_sha1_certinfo(tpm_sha1_attest_t *x)
|
||||
{
|
||||
x->magic = htobe32(x->magic);
|
||||
x->type = htobe16(x->type);
|
||||
x->signer.size = htobe16(x->signer.size);
|
||||
x->data.size = htobe16(x->data.size);
|
||||
x->name.alg = htobe16(x->name.alg);
|
||||
x->name.size = htobe16(x->name.size);
|
||||
}
|
||||
|
||||
static int
|
||||
check_rs256_pubarea(const fido_blob_t *buf, const rs256_pk_t *pk)
|
||||
{
|
||||
const tpm_rs256_pubarea_t *actual;
|
||||
tpm_rs256_pubarea_t expected;
|
||||
int ok;
|
||||
|
||||
if (buf->len != sizeof(*actual)) {
|
||||
fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
|
||||
return -1;
|
||||
}
|
||||
actual = (const void *)buf->ptr;
|
||||
|
||||
memset(&expected, 0, sizeof(expected));
|
||||
expected.alg = TPM_ALG_RSA;
|
||||
expected.hash = TPM_ALG_SHA256;
|
||||
expected.attr = be32toh(actual->attr);
|
||||
expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR);
|
||||
expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN);
|
||||
expected.policy = actual->policy;
|
||||
expected.policy.size = sizeof(expected.policy.body);
|
||||
expected.param.symmetric = TPM_ALG_NULL;
|
||||
expected.param.scheme = TPM_ALG_NULL;
|
||||
expected.param.keybits = 2048;
|
||||
expected.param.exponent = 0; /* meaning 2^16+1 */
|
||||
expected.key.size = sizeof(expected.key.body);
|
||||
memcpy(&expected.key.body, &pk->n, sizeof(expected.key.body));
|
||||
bswap_rs256_pubarea(&expected);
|
||||
|
||||
ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
|
||||
explicit_bzero(&expected, sizeof(expected));
|
||||
|
||||
return ok != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_es256_pubarea(const fido_blob_t *buf, const es256_pk_t *pk)
|
||||
{
|
||||
const tpm_es256_pubarea_t *actual;
|
||||
tpm_es256_pubarea_t expected;
|
||||
int ok;
|
||||
|
||||
if (buf->len != sizeof(*actual)) {
|
||||
fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
|
||||
return -1;
|
||||
}
|
||||
actual = (const void *)buf->ptr;
|
||||
|
||||
memset(&expected, 0, sizeof(expected));
|
||||
expected.alg = TPM_ALG_ECC;
|
||||
expected.hash = TPM_ALG_SHA256;
|
||||
expected.attr = be32toh(actual->attr);
|
||||
expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR);
|
||||
expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN);
|
||||
expected.policy = actual->policy;
|
||||
expected.policy.size = sizeof(expected.policy.body);
|
||||
expected.param.symmetric = TPM_ALG_NULL;
|
||||
expected.param.scheme = TPM_ALG_NULL; /* TCG Alg. Registry, 5.2.4 */
|
||||
expected.param.curve_id = TPM_ECC_P256;
|
||||
expected.param.kdf = TPM_ALG_NULL;
|
||||
expected.point.x.size = sizeof(expected.point.x.body);
|
||||
expected.point.y.size = sizeof(expected.point.y.body);
|
||||
memcpy(&expected.point.x.body, &pk->x, sizeof(expected.point.x.body));
|
||||
memcpy(&expected.point.y.body, &pk->y, sizeof(expected.point.y.body));
|
||||
bswap_es256_pubarea(&expected);
|
||||
|
||||
ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
|
||||
explicit_bzero(&expected, sizeof(expected));
|
||||
|
||||
return ok != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_sha1_certinfo(const fido_blob_t *buf, const fido_blob_t *clientdata_hash,
|
||||
const fido_blob_t *authdata_raw, const fido_blob_t *pubarea)
|
||||
{
|
||||
const tpm_sha1_attest_t *actual;
|
||||
tpm_sha1_attest_t expected;
|
||||
tpm_sha1_data_t signed_data;
|
||||
tpm_sha256_name_t signed_name;
|
||||
int ok = -1;
|
||||
|
||||
memset(&signed_data, 0, sizeof(signed_data));
|
||||
memset(&signed_name, 0, sizeof(signed_name));
|
||||
|
||||
if (get_signed_sha1(&signed_data, authdata_raw, clientdata_hash) < 0 ||
|
||||
get_signed_name(&signed_name, pubarea) < 0) {
|
||||
fido_log_debug("%s: get_signed_sha1/name", __func__);
|
||||
goto fail;
|
||||
}
|
||||
if (buf->len != sizeof(*actual)) {
|
||||
fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
|
||||
goto fail;
|
||||
}
|
||||
actual = (const void *)buf->ptr;
|
||||
|
||||
memset(&expected, 0, sizeof(expected));
|
||||
expected.magic = TPM_MAGIC;
|
||||
expected.type = TPM_ST_CERTIFY;
|
||||
expected.signer = actual->signer;
|
||||
expected.signer.size = sizeof(expected.signer.alg) +
|
||||
sizeof(expected.signer.body);
|
||||
expected.data = signed_data;
|
||||
expected.clock = actual->clock;
|
||||
expected.clock.safe = 1;
|
||||
expected.fwversion = actual->fwversion;
|
||||
expected.name = signed_name;
|
||||
expected.qual_name = actual->qual_name;
|
||||
bswap_sha1_certinfo(&expected);
|
||||
|
||||
ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
|
||||
fail:
|
||||
explicit_bzero(&expected, sizeof(expected));
|
||||
explicit_bzero(&signed_data, sizeof(signed_data));
|
||||
explicit_bzero(&signed_name, sizeof(signed_name));
|
||||
|
||||
return ok != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_get_signed_hash_tpm(fido_blob_t *dgst, const fido_blob_t *clientdata_hash,
|
||||
const fido_blob_t *authdata_raw, const fido_attstmt_t *attstmt,
|
||||
const fido_attcred_t *attcred)
|
||||
{
|
||||
const fido_blob_t *pubarea = &attstmt->pubarea;
|
||||
const fido_blob_t *certinfo = &attstmt->certinfo;
|
||||
|
||||
if (attstmt->alg != COSE_RS1) {
|
||||
fido_log_debug("%s: unsupported alg %d", __func__,
|
||||
attstmt->alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (attcred->type) {
|
||||
case COSE_ES256:
|
||||
if (check_es256_pubarea(pubarea, &attcred->pubkey.es256) < 0) {
|
||||
fido_log_debug("%s: check_es256_pubarea", __func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case COSE_RS256:
|
||||
if (check_rs256_pubarea(pubarea, &attcred->pubkey.rs256) < 0) {
|
||||
fido_log_debug("%s: check_rs256_pubarea", __func__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unsupported type %d", __func__,
|
||||
attcred->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (check_sha1_certinfo(certinfo, clientdata_hash, authdata_raw,
|
||||
pubarea) < 0) {
|
||||
fido_log_debug("%s: check_sha1_certinfo", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dgst->len < SHA_DIGEST_LENGTH ||
|
||||
SHA1(certinfo->ptr, certinfo->len, dgst->ptr) != dgst->ptr) {
|
||||
fido_log_debug("%s: sha1", __func__);
|
||||
return -1;
|
||||
}
|
||||
dgst->len = SHA_DIGEST_LENGTH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
91
src/types.c
Normal file
91
src/types.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
void
|
||||
fido_str_array_free(fido_str_array_t *sa)
|
||||
{
|
||||
for (size_t i = 0; i < sa->len; i++)
|
||||
free(sa->ptr[i]);
|
||||
|
||||
free(sa->ptr);
|
||||
sa->ptr = NULL;
|
||||
sa->len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_opt_array_free(fido_opt_array_t *oa)
|
||||
{
|
||||
for (size_t i = 0; i < oa->len; i++)
|
||||
free(oa->name[i]);
|
||||
|
||||
free(oa->name);
|
||||
free(oa->value);
|
||||
oa->name = NULL;
|
||||
oa->value = NULL;
|
||||
oa->len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_byte_array_free(fido_byte_array_t *ba)
|
||||
{
|
||||
free(ba->ptr);
|
||||
|
||||
ba->ptr = NULL;
|
||||
ba->len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_algo_free(fido_algo_t *a)
|
||||
{
|
||||
free(a->type);
|
||||
a->type = NULL;
|
||||
a->cose = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_algo_array_free(fido_algo_array_t *aa)
|
||||
{
|
||||
for (size_t i = 0; i < aa->len; i++)
|
||||
fido_algo_free(&aa->ptr[i]);
|
||||
|
||||
free(aa->ptr);
|
||||
aa->ptr = NULL;
|
||||
aa->len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fido_cert_array_free(fido_cert_array_t *ca)
|
||||
{
|
||||
for (size_t i = 0; i < ca->len; i++)
|
||||
free(ca->name[i]);
|
||||
|
||||
free(ca->name);
|
||||
free(ca->value);
|
||||
ca->name = NULL;
|
||||
ca->value = NULL;
|
||||
ca->len = 0;
|
||||
}
|
||||
|
||||
int
|
||||
fido_str_array_pack(fido_str_array_t *sa, const char * const *v, size_t n)
|
||||
{
|
||||
if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) {
|
||||
fido_log_debug("%s: calloc", __func__);
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if ((sa->ptr[i] = strdup(v[i])) == NULL) {
|
||||
fido_log_debug("%s: strdup", __func__);
|
||||
return -1;
|
||||
}
|
||||
sa->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
959
src/u2f.c
Normal file
959
src/u2f.c
Normal file
@@ -0,0 +1,959 @@
|
||||
/*
|
||||
* 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 <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "fido.h"
|
||||
#include "fido/es256.h"
|
||||
#include "fallthrough.h"
|
||||
|
||||
#define U2F_PACE_MS (100)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
static int
|
||||
usleep(unsigned int usec)
|
||||
{
|
||||
Sleep(usec / 1000);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
delay_ms(unsigned int ms, int *ms_remain)
|
||||
{
|
||||
if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
|
||||
ms = (unsigned int)*ms_remain;
|
||||
|
||||
if (ms > UINT_MAX / 1000) {
|
||||
fido_log_debug("%s: ms=%u", __func__, ms);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (usleep(ms * 1000) < 0) {
|
||||
fido_log_error(errno, "%s: usleep", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (*ms_remain > -1)
|
||||
*ms_remain -= (int)ms;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
|
||||
{
|
||||
sig->len = *len; /* consume the whole buffer */
|
||||
if ((sig->ptr = calloc(1, sig->len)) == NULL ||
|
||||
fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
|
||||
fido_log_debug("%s: fido_buf_read", __func__);
|
||||
fido_blob_reset(sig);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
|
||||
{
|
||||
X509 *cert = NULL;
|
||||
int ok = -1;
|
||||
|
||||
if (*len > LONG_MAX) {
|
||||
fido_log_debug("%s: invalid len %zu", __func__, *len);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* find out the certificate's length */
|
||||
const unsigned char *end = *buf;
|
||||
if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
|
||||
(x5c->len = (size_t)(end - *buf)) >= *len) {
|
||||
fido_log_debug("%s: d2i_X509", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read accordingly */
|
||||
if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
|
||||
fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
|
||||
fido_log_debug("%s: fido_buf_read", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (cert != NULL)
|
||||
X509_free(cert);
|
||||
|
||||
if (ok < 0)
|
||||
fido_blob_reset(x5c);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
|
||||
fido_blob_t *fake_cbor_ad)
|
||||
{
|
||||
fido_authdata_t ad;
|
||||
cbor_item_t *item = NULL;
|
||||
size_t alloc_len;
|
||||
|
||||
memset(&ad, 0, sizeof(ad));
|
||||
|
||||
if (SHA256((const void *)rp_id, strlen(rp_id),
|
||||
ad.rp_id_hash) != ad.rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ad.flags = flags; /* XXX translate? */
|
||||
ad.sigcount = sigcount;
|
||||
|
||||
if ((item = cbor_build_bytestring((const unsigned char *)&ad,
|
||||
sizeof(ad))) == NULL) {
|
||||
fido_log_debug("%s: cbor_build_bytestring", __func__);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fake_cbor_ad->ptr != NULL ||
|
||||
(fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
|
||||
&alloc_len)) == 0) {
|
||||
fido_log_debug("%s: cbor_serialize_alloc", __func__);
|
||||
cbor_decref(&item);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
cbor_decref(&item);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
|
||||
static int
|
||||
send_dummy_register(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
unsigned char *reply = NULL;
|
||||
unsigned char challenge[SHA256_DIGEST_LENGTH];
|
||||
unsigned char application[SHA256_DIGEST_LENGTH];
|
||||
int r;
|
||||
|
||||
/* dummy challenge & application */
|
||||
memset(&challenge, 0xff, sizeof(challenge));
|
||||
memset(&application, 0xff, sizeof(application));
|
||||
|
||||
if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
|
||||
SHA256_DIGEST_LENGTH)) == NULL ||
|
||||
iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
|
||||
iso7816_add(apdu, &application, sizeof(application)) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
do {
|
||||
if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
|
||||
iso7816_len(apdu), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) < 2) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
if (delay_ms(U2F_PACE_MS, ms) != 0) {
|
||||
fido_log_debug("%s: delay_ms", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
|
||||
int *found, int *ms)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
unsigned char *reply = NULL;
|
||||
unsigned char challenge[SHA256_DIGEST_LENGTH];
|
||||
unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
|
||||
uint8_t key_id_len;
|
||||
int r;
|
||||
|
||||
if (key_id->len > UINT8_MAX || rp_id == NULL) {
|
||||
fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
|
||||
key_id->len, (const void *)rp_id);
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&challenge, 0xff, sizeof(challenge));
|
||||
memset(&rp_id_hash, 0, sizeof(rp_id_hash));
|
||||
|
||||
if (SHA256((const void *)rp_id, strlen(rp_id),
|
||||
rp_id_hash) != rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
key_id_len = (uint8_t)key_id->len;
|
||||
|
||||
if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
|
||||
SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
|
||||
iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
|
||||
iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
|
||||
iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
|
||||
iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
|
||||
iso7816_len(apdu), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
if (fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG, ms) != 2) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch ((reply[0] << 8) | reply[1]) {
|
||||
case SW_CONDITIONS_NOT_SATISFIED:
|
||||
*found = 1; /* key exists */
|
||||
break;
|
||||
case SW_WRONG_DATA:
|
||||
*found = 0; /* key does not exist */
|
||||
break;
|
||||
default:
|
||||
/* unexpected sw */
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
|
||||
const unsigned char *reply, size_t len)
|
||||
{
|
||||
uint8_t flags;
|
||||
uint32_t sigcount;
|
||||
|
||||
if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
|
||||
fido_log_debug("%s: unexpected sw", __func__);
|
||||
return (FIDO_ERR_RX);
|
||||
}
|
||||
|
||||
len -= 2;
|
||||
|
||||
if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
|
||||
fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
|
||||
fido_log_debug("%s: fido_buf_read", __func__);
|
||||
return (FIDO_ERR_RX);
|
||||
}
|
||||
|
||||
if (sig_get(sig, &reply, &len) < 0) {
|
||||
fido_log_debug("%s: sig_get", __func__);
|
||||
return (FIDO_ERR_RX);
|
||||
}
|
||||
|
||||
if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
|
||||
fido_log_debug("%s; authdata_fake", __func__);
|
||||
return (FIDO_ERR_RX);
|
||||
}
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
|
||||
const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
unsigned char *reply = NULL;
|
||||
unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
|
||||
int reply_len;
|
||||
uint8_t key_id_len;
|
||||
int r;
|
||||
|
||||
#ifdef FIDO_FUZZ
|
||||
*ms = 0; /* XXX */
|
||||
#endif
|
||||
|
||||
if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
|
||||
rp_id == NULL) {
|
||||
r = FIDO_ERR_INVALID_ARGUMENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&rp_id_hash, 0, sizeof(rp_id_hash));
|
||||
|
||||
if (SHA256((const void *)rp_id, strlen(rp_id),
|
||||
rp_id_hash) != rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
key_id_len = (uint8_t)key_id->len;
|
||||
|
||||
if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
|
||||
SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
|
||||
iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
|
||||
iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
|
||||
iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
|
||||
iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
do {
|
||||
if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
|
||||
iso7816_len(apdu), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
|
||||
FIDO_MAXMSG, ms)) < 2) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
if (delay_ms(U2F_PACE_MS, ms) != 0) {
|
||||
fido_log_debug("%s: delay_ms", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
|
||||
|
||||
if ((r = parse_auth_reply(sig, ad, rp_id, reply,
|
||||
(size_t)reply_len)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_auth_reply", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
|
||||
fido_blob_t *cbor_blob)
|
||||
{
|
||||
es256_pk_t *pk = NULL;
|
||||
cbor_item_t *pk_cbor = NULL;
|
||||
size_t alloc_len;
|
||||
int ok = -1;
|
||||
|
||||
/* only handle uncompressed points */
|
||||
if (ec_point_len != 65 || ec_point[0] != 0x04) {
|
||||
fido_log_debug("%s: unexpected format", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pk = es256_pk_new()) == NULL ||
|
||||
es256_pk_set_x(pk, &ec_point[1]) < 0 ||
|
||||
es256_pk_set_y(pk, &ec_point[33]) < 0) {
|
||||
fido_log_debug("%s: es256_pk_set", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
|
||||
fido_log_debug("%s: es256_pk_encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
|
||||
&alloc_len)) != 77) {
|
||||
fido_log_debug("%s: cbor_serialize_alloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
es256_pk_free(&pk);
|
||||
|
||||
if (pk_cbor)
|
||||
cbor_decref(&pk_cbor);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
|
||||
const fido_blob_t *sig, fido_blob_t *out)
|
||||
{
|
||||
cbor_item_t *item = NULL;
|
||||
cbor_item_t *x5c_cbor = NULL;
|
||||
const uint8_t alg_cbor = (uint8_t)(-cose_alg - 1);
|
||||
struct cbor_pair kv[3];
|
||||
size_t alloc_len;
|
||||
int ok = -1;
|
||||
|
||||
memset(&kv, 0, sizeof(kv));
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if ((item = cbor_new_definite_map(3)) == NULL) {
|
||||
fido_log_debug("%s: cbor_new_definite_map", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((kv[0].key = cbor_build_string("alg")) == NULL ||
|
||||
(kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
|
||||
!cbor_map_add(item, kv[0])) {
|
||||
fido_log_debug("%s: alg", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((kv[1].key = cbor_build_string("sig")) == NULL ||
|
||||
(kv[1].value = fido_blob_encode(sig)) == NULL ||
|
||||
!cbor_map_add(item, kv[1])) {
|
||||
fido_log_debug("%s: sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
|
||||
(kv[2].value = cbor_new_definite_array(1)) == NULL ||
|
||||
(x5c_cbor = fido_blob_encode(x5c)) == NULL ||
|
||||
!cbor_array_push(kv[2].value, x5c_cbor) ||
|
||||
!cbor_map_add(item, kv[2])) {
|
||||
fido_log_debug("%s: x5c", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((out->len = cbor_serialize_alloc(item, &out->ptr,
|
||||
&alloc_len)) == 0) {
|
||||
fido_log_debug("%s: cbor_serialize_alloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (item != NULL)
|
||||
cbor_decref(&item);
|
||||
if (x5c_cbor != NULL)
|
||||
cbor_decref(&x5c_cbor);
|
||||
|
||||
for (size_t i = 0; i < nitems(kv); i++) {
|
||||
if (kv[i].key)
|
||||
cbor_decref(&kv[i].key);
|
||||
if (kv[i].value)
|
||||
cbor_decref(&kv[i].value);
|
||||
}
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
|
||||
const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
|
||||
{
|
||||
fido_authdata_t authdata;
|
||||
fido_attcred_raw_t attcred_raw;
|
||||
fido_blob_t pk_blob;
|
||||
fido_blob_t authdata_blob;
|
||||
cbor_item_t *authdata_cbor = NULL;
|
||||
unsigned char *ptr;
|
||||
size_t len;
|
||||
size_t alloc_len;
|
||||
int ok = -1;
|
||||
|
||||
memset(&pk_blob, 0, sizeof(pk_blob));
|
||||
memset(&authdata, 0, sizeof(authdata));
|
||||
memset(&authdata_blob, 0, sizeof(authdata_blob));
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (rp_id == NULL) {
|
||||
fido_log_debug("%s: NULL rp_id", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
|
||||
fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (SHA256((const void *)rp_id, strlen(rp_id),
|
||||
authdata.rp_id_hash) != authdata.rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
|
||||
authdata.sigcount = 0;
|
||||
|
||||
memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
|
||||
attcred_raw.id_len = htobe16(kh_len);
|
||||
|
||||
len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
|
||||
kh_len + pk_blob.len;
|
||||
ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
|
||||
|
||||
fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
|
||||
|
||||
if (authdata_blob.ptr == NULL)
|
||||
goto fail;
|
||||
|
||||
if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
|
||||
fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
|
||||
fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
|
||||
fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
|
||||
fido_log_debug("%s: fido_buf_write", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
|
||||
fido_log_debug("%s: fido_blob_encode", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
|
||||
&alloc_len)) == 0) {
|
||||
fido_log_debug("%s: cbor_serialize_alloc", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok = 0;
|
||||
fail:
|
||||
if (authdata_cbor)
|
||||
cbor_decref(&authdata_cbor);
|
||||
|
||||
fido_blob_reset(&pk_blob);
|
||||
fido_blob_reset(&authdata_blob);
|
||||
|
||||
return (ok);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
|
||||
{
|
||||
fido_blob_t x5c;
|
||||
fido_blob_t sig;
|
||||
fido_blob_t ad;
|
||||
fido_blob_t stmt;
|
||||
uint8_t dummy;
|
||||
uint8_t pubkey[65];
|
||||
uint8_t kh_len = 0;
|
||||
uint8_t *kh = NULL;
|
||||
int r;
|
||||
|
||||
memset(&x5c, 0, sizeof(x5c));
|
||||
memset(&sig, 0, sizeof(sig));
|
||||
memset(&ad, 0, sizeof(ad));
|
||||
memset(&stmt, 0, sizeof(stmt));
|
||||
r = FIDO_ERR_RX;
|
||||
|
||||
/* status word */
|
||||
if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
|
||||
fido_log_debug("%s: unexpected sw", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
len -= 2;
|
||||
|
||||
/* reserved byte */
|
||||
if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
|
||||
dummy != 0x05) {
|
||||
fido_log_debug("%s: reserved byte", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* pubkey + key handle */
|
||||
if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
|
||||
fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
|
||||
(kh = calloc(1, kh_len)) == NULL ||
|
||||
fido_buf_read(&reply, &len, kh, kh_len) < 0) {
|
||||
fido_log_debug("%s: fido_buf_read", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* x5c + sig */
|
||||
if (x5c_get(&x5c, &reply, &len) < 0 ||
|
||||
sig_get(&sig, &reply, &len) < 0) {
|
||||
fido_log_debug("%s: x5c || sig", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* attstmt */
|
||||
if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
|
||||
fido_log_debug("%s: encode_cred_attstmt", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* authdata */
|
||||
if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
|
||||
sizeof(pubkey), &ad) < 0) {
|
||||
fido_log_debug("%s: encode_cred_authdata", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
|
||||
fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
|
||||
fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_cred_set", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
freezero(kh, kh_len);
|
||||
fido_blob_reset(&x5c);
|
||||
fido_blob_reset(&sig);
|
||||
fido_blob_reset(&ad);
|
||||
fido_blob_reset(&stmt);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
|
||||
unsigned char *reply = NULL;
|
||||
int reply_len;
|
||||
int found;
|
||||
int r;
|
||||
|
||||
if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
|
||||
fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
|
||||
cred->uv);
|
||||
return (FIDO_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
|
||||
if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
|
||||
cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
|
||||
fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
|
||||
cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
|
||||
return (FIDO_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cred->excl.len; i++) {
|
||||
if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
|
||||
&found, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: key_lookup", __func__);
|
||||
return (r);
|
||||
}
|
||||
if (found) {
|
||||
if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: send_dummy_register",
|
||||
__func__);
|
||||
return (r);
|
||||
}
|
||||
return (FIDO_ERR_CREDENTIAL_EXCLUDED);
|
||||
}
|
||||
}
|
||||
|
||||
memset(&rp_id_hash, 0, sizeof(rp_id_hash));
|
||||
|
||||
if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
|
||||
rp_id_hash) != rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
|
||||
SHA256_DIGEST_LENGTH)) == NULL ||
|
||||
iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
|
||||
iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
do {
|
||||
if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
|
||||
iso7816_len(apdu), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply,
|
||||
FIDO_MAXMSG, ms)) < 2) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
if (delay_ms(U2F_PACE_MS, ms) != 0) {
|
||||
fido_log_debug("%s: delay_ms", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto fail;
|
||||
}
|
||||
} while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
|
||||
|
||||
if ((r = parse_register_reply(cred, reply,
|
||||
(size_t)reply_len)) != FIDO_OK) {
|
||||
fido_log_debug("%s: parse_register_reply", __func__);
|
||||
goto fail;
|
||||
}
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
static int
|
||||
u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
|
||||
fido_assert_t *fa, size_t idx, int *ms)
|
||||
{
|
||||
fido_blob_t sig;
|
||||
fido_blob_t ad;
|
||||
int found;
|
||||
int r;
|
||||
|
||||
memset(&sig, 0, sizeof(sig));
|
||||
memset(&ad, 0, sizeof(ad));
|
||||
|
||||
if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: key_lookup", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
fido_log_debug("%s: not found", __func__);
|
||||
r = FIDO_ERR_CREDENTIAL_EXCLUDED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0) {
|
||||
fido_log_debug("%s: fido_blob_set", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fa->up == FIDO_OPT_FALSE) {
|
||||
fido_log_debug("%s: checking for key existence only", __func__);
|
||||
r = FIDO_ERR_USER_PRESENCE_REQUIRED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
|
||||
ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: do_auth", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
|
||||
fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_assert_set", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
fido_blob_reset(&sig);
|
||||
fido_blob_reset(&ad);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
|
||||
{
|
||||
size_t nfound = 0;
|
||||
size_t nauth_ok = 0;
|
||||
int r;
|
||||
|
||||
if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
|
||||
fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
|
||||
(void *)fa->allow_list.ptr);
|
||||
return (FIDO_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
|
||||
if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
|
||||
fido_log_debug("%s: fido_assert_set_count", __func__);
|
||||
return (r);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fa->allow_list.len; i++) {
|
||||
switch ((r = u2f_authenticate_single(dev,
|
||||
&fa->allow_list.ptr[i], fa, nfound, ms))) {
|
||||
case FIDO_OK:
|
||||
nauth_ok++;
|
||||
FALLTHROUGH
|
||||
case FIDO_ERR_USER_PRESENCE_REQUIRED:
|
||||
nfound++;
|
||||
break;
|
||||
default:
|
||||
if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
|
||||
fido_log_debug("%s: u2f_authenticate_single",
|
||||
__func__);
|
||||
return (r);
|
||||
}
|
||||
/* ignore credentials that don't exist */
|
||||
}
|
||||
}
|
||||
|
||||
fa->stmt_len = nfound;
|
||||
|
||||
if (nfound == 0)
|
||||
return (FIDO_ERR_NO_CREDENTIALS);
|
||||
if (nauth_ok == 0)
|
||||
return (FIDO_ERR_USER_PRESENCE_REQUIRED);
|
||||
|
||||
return (FIDO_OK);
|
||||
}
|
||||
|
||||
int
|
||||
u2f_get_touch_begin(fido_dev_t *dev, int *ms)
|
||||
{
|
||||
iso7816_apdu_t *apdu = NULL;
|
||||
const char *clientdata = FIDO_DUMMY_CLIENTDATA;
|
||||
const char *rp_id = FIDO_DUMMY_RP_ID;
|
||||
unsigned char *reply = NULL;
|
||||
unsigned char clientdata_hash[SHA256_DIGEST_LENGTH];
|
||||
unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
|
||||
int r;
|
||||
|
||||
memset(&clientdata_hash, 0, sizeof(clientdata_hash));
|
||||
memset(&rp_id_hash, 0, sizeof(rp_id_hash));
|
||||
|
||||
if (SHA256((const void *)clientdata, strlen(clientdata),
|
||||
clientdata_hash) != clientdata_hash || SHA256((const void *)rp_id,
|
||||
strlen(rp_id), rp_id_hash) != rp_id_hash) {
|
||||
fido_log_debug("%s: sha256", __func__);
|
||||
return (FIDO_ERR_INTERNAL);
|
||||
}
|
||||
|
||||
if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
|
||||
SHA256_DIGEST_LENGTH)) == NULL ||
|
||||
iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
|
||||
iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
|
||||
fido_log_debug("%s: iso7816", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dev->attr.flags & FIDO_CAP_WINK) {
|
||||
fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
|
||||
fido_rx(dev, CTAP_CMD_WINK, reply, FIDO_MAXMSG, ms);
|
||||
}
|
||||
|
||||
if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
|
||||
iso7816_len(apdu), ms) < 0) {
|
||||
fido_log_debug("%s: fido_tx", __func__);
|
||||
r = FIDO_ERR_TX;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
fail:
|
||||
iso7816_free(&apdu);
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
|
||||
{
|
||||
unsigned char *reply;
|
||||
int reply_len;
|
||||
int r;
|
||||
|
||||
if ((reply = malloc(FIDO_MAXMSG)) == NULL) {
|
||||
fido_log_debug("%s: malloc", __func__);
|
||||
r = FIDO_ERR_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, reply, FIDO_MAXMSG,
|
||||
ms)) < 2) {
|
||||
fido_log_debug("%s: fido_rx", __func__);
|
||||
r = FIDO_OK; /* ignore */
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
|
||||
case SW_CONDITIONS_NOT_SATISFIED:
|
||||
if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
|
||||
fido_log_debug("%s: u2f_get_touch_begin", __func__);
|
||||
goto out;
|
||||
}
|
||||
*touched = 0;
|
||||
break;
|
||||
case SW_NO_ERROR:
|
||||
*touched = 1;
|
||||
break;
|
||||
default:
|
||||
fido_log_debug("%s: unexpected sw", __func__);
|
||||
r = FIDO_ERR_RX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = FIDO_OK;
|
||||
out:
|
||||
freezero(reply, FIDO_MAXMSG);
|
||||
|
||||
return (r);
|
||||
}
|
||||
31
src/util.c
Normal file
31
src/util.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 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 <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fido.h"
|
||||
|
||||
int
|
||||
fido_to_uint64(const char *str, int base, uint64_t *out)
|
||||
{
|
||||
char *ep;
|
||||
unsigned long long ull;
|
||||
|
||||
errno = 0;
|
||||
ull = strtoull(str, &ep, base);
|
||||
if (str == ep || *ep != '\0')
|
||||
return -1;
|
||||
else if (ull == ULLONG_MAX && errno == ERANGE)
|
||||
return -1;
|
||||
else if (ull > UINT64_MAX)
|
||||
return -1;
|
||||
*out = (uint64_t)ull;
|
||||
|
||||
return 0;
|
||||
}
|
||||
1149
src/webauthn.h
Normal file
1149
src/webauthn.h
Normal file
File diff suppressed because it is too large
Load Diff
1029
src/winhello.c
Normal file
1029
src/winhello.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user