From 75073d0a8478441cc97a6efa10b566c5fb1dac81 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Fri, 17 Apr 2020 20:57:17 +0100 Subject: New upstream version 1.4.0 --- src/CMakeLists.txt | 12 ++- src/assert.c | 31 ++++--- src/authkey.c | 10 +-- src/bio.c | 32 ++++---- src/blob.h | 11 +++ src/cbor.c | 108 ++++++++++++++++++------ src/cred.c | 122 ++++++++++++--------------- src/credman.c | 34 ++++---- src/dev.c | 223 ++++++++++++++++++++++++++++++++++++++++++++------ src/eddsa.c | 8 ++ src/err.c | 24 +++--- src/es256.c | 54 ++++++++---- src/export.gnu | 5 ++ src/export.llvm | 5 ++ src/export.msvc | 5 ++ src/extern.h | 32 +++++++- src/fido.h | 53 +++++------- src/fido/bio.h | 16 ++++ src/fido/credman.h | 16 ++++ src/fido/eddsa.h | 14 ++++ src/fido/err.h | 8 ++ src/fido/es256.h | 14 ++++ src/fido/param.h | 11 +++ src/fido/rs256.h | 14 ++++ src/fido/types.h | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hid_hidapi.c | 138 +++++++++++++++++++++++++++++++ src/hid_linux.c | 14 +++- src/hid_openbsd.c | 8 +- src/hid_osx.c | 17 +++- src/hid_win.c | 8 +- src/info.c | 21 +++-- src/io.c | 202 +++++++++++++++++++++++++-------------------- src/iso7816.h | 11 +++ src/log.c | 73 ++++++++++++----- src/pin.c | 126 ++++++++++++++++++++++++---- src/reset.c | 5 +- src/types.h | 171 -------------------------------------- src/u2f.c | 69 +++++++++------- 38 files changed, 1377 insertions(+), 583 deletions(-) create mode 100644 src/fido/types.h create mode 100644 src/hid_hidapi.c delete mode 100644 src/types.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 926e7f2..3cf62e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,11 +31,14 @@ list(APPEND FIDO_SOURCES ) if(FUZZ) + list(APPEND FIDO_SOURCES ../fuzz/prng.c) list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c) list(APPEND FIDO_SOURCES ../fuzz/wrap.c) endif() -if(WIN32) +if(USE_HIDAPI) + list(APPEND COMPAT_SOURCES hid_hidapi.c) +elseif(WIN32) list(APPEND COMPAT_SOURCES hid_win.c) elseif(APPLE) list(APPEND COMPAT_SOURCES hid_osx.c) @@ -43,6 +46,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") list(APPEND COMPAT_SOURCES hid_linux.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") list(APPEND COMPAT_SOURCES hid_openbsd.c) +else() + message(FATAL_ERROR "please define a hid backend for your platform") endif() list(APPEND COMPAT_SOURCES @@ -50,13 +55,14 @@ list(APPEND COMPAT_SOURCES ../openbsd-compat/explicit_bzero.c ../openbsd-compat/explicit_bzero_win32.c ../openbsd-compat/recallocarray.c + ../openbsd-compat/strlcat.c ../openbsd-compat/timingsafe_bcmp.c ) # static library add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES}) target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} - ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) + ${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES}) if(WIN32) if (MINGW) target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid) @@ -74,7 +80,7 @@ install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # dynamic library add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES}) target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES} - ${UDEV_LIBRARIES} ${BASE_LIBRARIES}) + ${UDEV_LIBRARIES} ${BASE_LIBRARIES} ${HIDAPI_LIBRARIES}) if(WIN32) if (MINGW) target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt diff --git a/src/assert.c b/src/assert.c index a21b308..b71d00e 100644 --- a/src/assert.c +++ b/src/assert.c @@ -152,8 +152,8 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert, } /* frame and transmit */ - if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, 7, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, nitems(argv), &f) < 0 || + fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -170,14 +170,14 @@ fail: static int fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; fido_assert_reset_rx(assert); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -211,10 +211,9 @@ fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms) static int fido_get_next_assert_tx(fido_dev_t *dev) { - const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT }; - if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { fido_log_debug("%s: fido_tx", __func__); return (FIDO_ERR_TX); } @@ -225,12 +224,12 @@ fido_get_next_assert_tx(fido_dev_t *dev) static int fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -363,8 +362,8 @@ check_extensions(int authdata_ext, int ext) return (0); } -static int -get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata, +int +fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor) { cbor_item_t *item = NULL; @@ -534,7 +533,7 @@ int fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, const void *pk) { - unsigned char buf[1024]; + unsigned char buf[1024]; /* XXX */ fido_blob_t dgst; const fido_assert_stmt *stmt = NULL; int ok = -1; @@ -579,9 +578,9 @@ fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg, goto out; } - if (get_signed_hash(cose_alg, &dgst, &assert->cdh, + if (fido_get_signed_hash(cose_alg, &dgst, &assert->cdh, &stmt->authdata_cbor) < 0) { - fido_log_debug("%s: get_signed_hash", __func__); + fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } diff --git a/src/authkey.c b/src/authkey.c index 9de37f1..83c2564 100644 --- a/src/authkey.c +++ b/src/authkey.c @@ -43,8 +43,8 @@ fido_dev_authkey_tx(fido_dev_t *dev) } /* frame and transmit */ - if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -61,8 +61,7 @@ fail: static int fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev, @@ -70,7 +69,8 @@ fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms) memset(authkey, 0, sizeof(*authkey)); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } diff --git a/src/bio.c b/src/bio.c index 74814b9..c1032d8 100644 --- a/src/bio.c +++ b/src/bio.c @@ -109,8 +109,8 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc, } /* framing and transmission */ - if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -235,14 +235,14 @@ bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val, static int bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; bio_reset_template_array(ta); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -382,8 +382,7 @@ static int bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, fido_bio_enroll_t *e, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; @@ -392,7 +391,8 @@ bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, e->remaining_samples = 0; e->last_status = 0; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -407,7 +407,7 @@ bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t, fido_log_debug("%s: bio_parse_template_id", __func__); return (r); } - + return (FIDO_OK); } @@ -482,15 +482,15 @@ fail: static int bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; e->remaining_samples = 0; e->last_status = 0; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -500,7 +500,7 @@ bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms) fido_log_debug("%s: bio_parse_enroll_status", __func__); return (r); } - + return (FIDO_OK); } @@ -643,14 +643,14 @@ bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg) static int bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; bio_reset_info(i); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } diff --git a/src/blob.h b/src/blob.h index 24fdc23..9e98d03 100644 --- a/src/blob.h +++ b/src/blob.h @@ -7,6 +7,13 @@ #ifndef _BLOB_H #define _BLOB_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + typedef struct fido_blob { unsigned char *ptr; size_t len; @@ -25,4 +32,8 @@ int fido_blob_set(fido_blob_t *, const unsigned char *, size_t); void fido_blob_free(fido_blob_t **); void fido_free_blob_array(fido_blob_array_t *); +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + #endif /* !_BLOB_H */ diff --git a/src/cbor.c b/src/cbor.c index 3e03592..3928325 100644 --- a/src/cbor.c +++ b/src/cbor.c @@ -313,6 +313,35 @@ fail: return (ok); } +static int +cbor_add_uint8(cbor_item_t *item, const char *key, uint8_t value) +{ + struct cbor_pair pair; + int ok = -1; + + memset(&pair, 0, sizeof(pair)); + + if ((pair.key = cbor_build_string(key)) == NULL || + (pair.value = cbor_build_uint8(value)) == NULL) { + fido_log_debug("%s: cbor_build", __func__); + goto fail; + } + + if (!cbor_map_add(item, pair)) { + fido_log_debug("%s: cbor_map_add", __func__); + goto fail; + } + + ok = 0; +fail: + if (pair.key) + cbor_decref(&pair.key); + if (pair.value) + cbor_decref(&pair.value); + + return (ok); +} + static int cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg) { @@ -535,19 +564,29 @@ fail: } cbor_item_t * -cbor_encode_extensions(int ext) +cbor_encode_extensions(const fido_cred_ext_t *ext) { cbor_item_t *item = NULL; + size_t size = 0; - if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET) - return (NULL); - - if ((item = cbor_new_definite_map(1)) == NULL) + if (ext->mask & FIDO_EXT_HMAC_SECRET) + size++; + if (ext->mask & FIDO_EXT_CRED_PROTECT) + size++; + if (size == 0 || (item = cbor_new_definite_map(size)) == NULL) return (NULL); - if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { - cbor_decref(&item); - return (NULL); + if (ext->mask & FIDO_EXT_HMAC_SECRET) { + if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) { + cbor_decref(&item); + return (NULL); + } + } + if (ext->mask & FIDO_EXT_CRED_PROTECT) { + if (cbor_add_uint8(item, "credProtect", ext->prot) < 0) { + cbor_decref(&item); + return (NULL); + } } return (item); @@ -1082,26 +1121,35 @@ fail: static int decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg) { - int *authdata_ext = arg; - char *type = NULL; - int ok = -1; + fido_cred_ext_t *authdata_ext = arg; + char *type = NULL; + int ok = -1; - if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) { + if (cbor_string_copy(key, &type) < 0) { fido_log_debug("%s: cbor type", __func__); ok = 0; /* ignore */ goto out; } - if (cbor_isa_float_ctrl(val) == false || - cbor_float_get_width(val) != CBOR_FLOAT_0 || - cbor_is_bool(val) == false || *authdata_ext != 0) { - fido_log_debug("%s: cbor type", __func__); - goto out; + if (strcmp(type, "hmac-secret") == 0) { + if (cbor_isa_float_ctrl(val) == false || + cbor_float_get_width(val) != CBOR_FLOAT_0 || + cbor_is_bool(val) == false) { + fido_log_debug("%s: cbor type", __func__); + goto out; + } + if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) + authdata_ext->mask |= FIDO_EXT_HMAC_SECRET; + } else if (strcmp(type, "credProtect") == 0) { + if (cbor_isa_uint(val) == false || + cbor_int_get_width(val) != CBOR_INT_8) { + fido_log_debug("%s: cbor type", __func__); + goto out; + } + authdata_ext->mask |= FIDO_EXT_CRED_PROTECT; + authdata_ext->prot = cbor_get_uint8(val); } - if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE) - *authdata_ext |= FIDO_EXT_HMAC_SECRET; - ok = 0; out: free(type); @@ -1110,7 +1158,8 @@ out: } static int -decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) +decode_extensions(const unsigned char **buf, size_t *len, + fido_cred_ext_t *authdata_ext) { cbor_item_t *item = NULL; struct cbor_load_result cbor; @@ -1118,8 +1167,9 @@ decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, *len); + fido_log_xxd(*buf, *len); - *authdata_ext = 0; + memset(authdata_ext, 0, sizeof(*authdata_ext)); if ((item = cbor_load(*buf, *len, &cbor)) == NULL) { fido_log_debug("%s: cbor_load", __func__); @@ -1129,7 +1179,6 @@ decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext) if (cbor_isa_map(item) == false || cbor_map_is_definite(item) == false || - cbor_map_size(item) != 1 || cbor_map_iter(item, authdata_ext, decode_extension) < 0) { fido_log_debug("%s: cbor type", __func__); goto fail; @@ -1204,7 +1253,7 @@ fail: int cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg, fido_blob_t *authdata_cbor, fido_authdata_t *authdata, - fido_attcred_t *attcred, int *authdata_ext) + fido_attcred_t *attcred, fido_cred_ext_t *authdata_ext) { const unsigned char *buf = NULL; size_t len; @@ -1227,6 +1276,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg, len = cbor_bytestring_length(item); fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len); + fido_log_xxd(buf, len); if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) { fido_log_debug("%s: fido_buf_read", __func__); @@ -1316,6 +1366,7 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) { fido_attstmt_t *attstmt = arg; char *name = NULL; + int cose_alg = 0; int ok = -1; if (cbor_string_copy(key, &name) < 0) { @@ -1326,11 +1377,16 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg) if (!strcmp(name, "alg")) { if (cbor_isa_negint(val) == false || - cbor_int_get_width(val) != CBOR_INT_8 || - cbor_get_uint8(val) != -COSE_ES256 - 1) { + cbor_get_int(val) > UINT16_MAX) { fido_log_debug("%s: alg", __func__); goto out; } + if ((cose_alg = -(int)cbor_get_int(val) - 1) != COSE_ES256 && + cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) { + fido_log_debug("%s: unsupported cose_alg=%d", __func__, + cose_alg); + goto out; + } } else if (!strcmp(name, "sig")) { if (cbor_bytestring_copy(val, &attstmt->sig.ptr, &attstmt->sig.len) < 0) { diff --git a/src/cred.c b/src/cred.c index c4e1edb..4ecbba8 100644 --- a/src/cred.c +++ b/src/cred.c @@ -76,8 +76,8 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin) } /* extensions */ - if (cred->ext) - if ((argv[5] = cbor_encode_extensions(cred->ext)) == NULL) { + if (cred->ext.mask) + if ((argv[5] = cbor_encode_extensions(&cred->ext)) == NULL) { fido_log_debug("%s: cbor_encode_extensions", __func__); r = FIDO_ERR_INTERNAL; goto fail; @@ -106,8 +106,8 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin) } /* framing and transmission */ - if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, 9, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 || + fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -126,14 +126,14 @@ fail: static int fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; fido_cred_reset_rx(cred); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -170,7 +170,8 @@ int fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) { if (fido_dev_is_fido2(dev) == false) { - if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext != 0) + if (pin != NULL || cred->rk == FIDO_OPT_TRUE || + cred->ext.mask != 0) return (FIDO_ERR_UNSUPPORTED_OPTION); return (u2f_register(dev, cred, -1)); } @@ -179,15 +180,9 @@ fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) } static int -check_extensions(int authdata_ext, int ext) +check_extensions(const fido_cred_ext_t *authdata_ext, const fido_cred_ext_t *ext) { - if (authdata_ext != ext) { - fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__, - authdata_ext, ext); - return (-1); - } - - return (0); + return (timingsafe_bcmp(authdata_ext, ext, sizeof(*authdata_ext))); } int @@ -207,48 +202,6 @@ fido_check_rp_id(const char *id, const unsigned char *obtained_hash) SHA256_DIGEST_LENGTH)); } -static int -get_signed_hash_packed(fido_blob_t *dgst, const fido_blob_t *clientdata, - const fido_blob_t *authdata_cbor) -{ - cbor_item_t *item = NULL; - unsigned char *authdata_ptr = NULL; - size_t authdata_len; - struct cbor_load_result cbor; - SHA256_CTX ctx; - int ok = -1; - - if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len, - &cbor)) == NULL) { - fido_log_debug("%s: cbor_load", __func__); - goto fail; - } - - if (cbor_isa_bytestring(item) == false || - cbor_bytestring_is_definite(item) == false) { - fido_log_debug("%s: cbor type", __func__); - goto fail; - } - - authdata_ptr = cbor_bytestring_handle(item); - authdata_len = cbor_bytestring_length(item); - - if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 || - SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 || - SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 || - SHA256_Final(dgst->ptr, &ctx) == 0) { - fido_log_debug("%s: sha256", __func__); - goto fail; - } - - ok = 0; -fail: - if (item != NULL) - cbor_decref(&item); - - return (ok); -} - static int get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id, size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id, @@ -356,16 +309,16 @@ fido_cred_verify(const fido_cred_t *cred) goto out; } - if (check_extensions(cred->authdata_ext, cred->ext) < 0) { + if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { fido_log_debug("%s: check_extensions", __func__); r = FIDO_ERR_INVALID_PARAM; goto out; } if (!strcmp(cred->fmt, "packed")) { - if (get_signed_hash_packed(&dgst, &cred->cdh, + if (fido_get_signed_hash(COSE_ES256, &dgst, &cred->cdh, &cred->authdata_cbor) < 0) { - fido_log_debug("%s: get_signed_hash_packed", __func__); + fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } @@ -395,7 +348,7 @@ out: int fido_cred_verify_self(const fido_cred_t *cred) { - unsigned char buf[SHA256_DIGEST_LENGTH]; + unsigned char buf[1024]; /* XXX */ fido_blob_t dgst; int ok = -1; int r; @@ -431,16 +384,16 @@ fido_cred_verify_self(const fido_cred_t *cred) goto out; } - if (check_extensions(cred->authdata_ext, cred->ext) < 0) { + if (check_extensions(&cred->authdata_ext, &cred->ext) != 0) { fido_log_debug("%s: check_extensions", __func__); r = FIDO_ERR_INVALID_PARAM; goto out; } if (!strcmp(cred->fmt, "packed")) { - if (get_signed_hash_packed(&dgst, &cred->cdh, + if (fido_get_signed_hash(cred->attcred.type, &dgst, &cred->cdh, &cred->authdata_cbor) < 0) { - fido_log_debug("%s: get_signed_hash_packed", __func__); + fido_log_debug("%s: fido_get_signed_hash", __func__); r = FIDO_ERR_INTERNAL; goto out; } @@ -519,9 +472,9 @@ fido_cred_reset_tx(fido_cred_t *cred) memset(&cred->rp, 0, sizeof(cred->rp)); memset(&cred->user, 0, sizeof(cred->user)); memset(&cred->excl, 0, sizeof(cred->excl)); + memset(&cred->ext, 0, sizeof(cred->ext)); cred->type = 0; - cred->ext = 0; cred->rk = FIDO_OPT_OMIT; cred->uv = FIDO_OPT_OMIT; } @@ -810,10 +763,14 @@ fail: int fido_cred_set_extensions(fido_cred_t *cred, int ext) { - if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET) - return (FIDO_ERR_INVALID_ARGUMENT); - - cred->ext = ext; + if (ext == 0) + cred->ext.mask = 0; + else { + if (ext != FIDO_EXT_HMAC_SECRET && + ext != FIDO_EXT_CRED_PROTECT) + return (FIDO_ERR_INVALID_ARGUMENT); + cred->ext.mask |= ext; + } return (FIDO_OK); } @@ -843,6 +800,25 @@ fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv) return (FIDO_OK); } +int +fido_cred_set_prot(fido_cred_t *cred, int prot) +{ + if (prot == 0) { + cred->ext.mask &= ~FIDO_EXT_CRED_PROTECT; + cred->ext.prot = 0; + } else { + if (prot != FIDO_CRED_PROT_UV_OPTIONAL && + prot != FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID && + prot != FIDO_CRED_PROT_UV_REQUIRED) + return (FIDO_ERR_INVALID_ARGUMENT); + + cred->ext.mask |= FIDO_EXT_CRED_PROTECT; + cred->ext.prot = prot; + } + + return (FIDO_OK); +} + int fido_cred_set_fmt(fido_cred_t *cred, const char *fmt) { @@ -991,6 +967,12 @@ fido_cred_id_len(const fido_cred_t *cred) return (cred->attcred.id.len); } +int +fido_cred_prot(const fido_cred_t *cred) +{ + return (cred->ext.prot); +} + const char * fido_cred_fmt(const fido_cred_t *cred) { diff --git a/src/credman.c b/src/credman.c index 76327e5..a382185 100644 --- a/src/credman.c +++ b/src/credman.c @@ -137,8 +137,8 @@ credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param, } /* framing and transmission */ - if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, 4, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -181,14 +181,14 @@ credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, static int credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[512]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; memset(metadata, 0, sizeof(*metadata)); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -300,14 +300,14 @@ credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, static int credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; credman_reset_rk(rk); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -339,12 +339,12 @@ credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) static int credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -514,14 +514,14 @@ credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, static int credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; credman_reset_rp(rp); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -553,12 +553,12 @@ credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) static int credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } diff --git a/src/dev.c b/src/dev.c index d0efac7..51b9935 100644 --- a/src/dev.c +++ b/src/dev.c @@ -6,6 +6,9 @@ #include #include +#ifdef HAVE_SYS_RANDOM_H +#include +#endif #include #include @@ -39,7 +42,22 @@ obtain_nonce(uint64_t *nonce) return (0); } -#elif defined(HAS_DEV_URANDOM) +#elif defined(HAVE_ARC4RANDOM_BUF) +static int +obtain_nonce(uint64_t *nonce) +{ + arc4random_buf(nonce, sizeof(*nonce)); + return (0); +} +#elif defined(HAVE_GETRANDOM) +static int +obtain_nonce(uint64_t *nonce) +{ + if (getrandom(nonce, sizeof(*nonce), 0) < 0) + return (-1); + return (0); +} +#elif defined(HAVE_DEV_URANDOM) static int obtain_nonce(uint64_t *nonce) { @@ -64,10 +82,34 @@ fail: #error "please provide an implementation of obtain_nonce() for your platform" #endif /* _WIN32 */ +#ifndef TLS +#define TLS +#endif + +typedef struct dev_manifest_func_node { + dev_manifest_func_t manifest_func; + struct dev_manifest_func_node *next; +} dev_manifest_func_node_t; + +static TLS dev_manifest_func_node_t *manifest_funcs = NULL; + +static void +find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr, + dev_manifest_func_node_t **prev) +{ + *prev = NULL; + *curr = manifest_funcs; + + while (*curr != NULL && (*curr)->manifest_func != f) { + *prev = *curr; + *curr = (*curr)->next; + } +} + static int fido_dev_open_tx(fido_dev_t *dev, const char *path) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT; + const uint8_t cmd = CTAP_CMD_INIT; if (dev->io_handle != NULL) { fido_log_debug("%s: handle=%p", __func__, dev->io_handle); @@ -102,11 +144,14 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path) static int fido_dev_open_rx(fido_dev_t *dev, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT; - int n; + fido_cbor_info_t *info = NULL; + int reply_len; + int r; - if ((n = fido_rx(dev, cmd, &dev->attr, sizeof(dev->attr), ms)) < 0) { + 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; } @@ -114,19 +159,42 @@ fido_dev_open_rx(fido_dev_t *dev, int ms) dev->attr.nonce = dev->nonce; #endif - if ((size_t)n != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) { + 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->cid = dev->attr.cid; - return (FIDO_OK); + 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 (fido_dev_get_cbor_info_wait(dev, info, ms) != FIDO_OK) { + fido_log_debug("%s: falling back to u2f", __func__); + fido_dev_force_u2f(dev); + } + } + + if (fido_dev_is_fido2(dev) && info != NULL) { + fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__, + FIDO_MAXMSG, (unsigned long)fido_cbor_info_maxmsgsiz(info)); + } + + r = FIDO_OK; fail: - dev->io.close(dev->io_handle); - dev->io_handle = NULL; + fido_cbor_info_free(&info); + + if (r != FIDO_OK) { + dev->io.close(dev->io_handle); + dev->io_handle = NULL; + } - return (FIDO_ERR_RX); + return (r); } static int @@ -141,6 +209,79 @@ fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms) return (FIDO_OK); } +int +fido_dev_register_manifest_func(const dev_manifest_func_t f) +{ + dev_manifest_func_node_t *prev, *curr, *n; + + find_manifest_func_node(f, &curr, &prev); + if (curr != NULL) + return (FIDO_OK); + + if ((n = calloc(1, sizeof(*n))) == NULL) { + fido_log_debug("%s: calloc", __func__); + return (FIDO_ERR_INTERNAL); + } + + n->manifest_func = f; + n->next = manifest_funcs; + manifest_funcs = n; + + return (FIDO_OK); +} + +void +fido_dev_unregister_manifest_func(const dev_manifest_func_t f) +{ + dev_manifest_func_node_t *prev, *curr; + + find_manifest_func_node(f, &curr, &prev); + if (curr == NULL) + return; + if (prev != NULL) + prev->next = curr->next; + else + manifest_funcs = curr->next; + + free(curr); +} + +int +fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + dev_manifest_func_node_t *curr = NULL; + dev_manifest_func_t m_func; + size_t curr_olen; + int r; + + *olen = 0; + + if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK) + return (FIDO_ERR_INTERNAL); + + for (curr = manifest_funcs; curr != NULL; curr = curr->next) { + curr_olen = 0; + m_func = curr->manifest_func; + r = m_func(devlist + *olen, ilen - *olen, &curr_olen); + if (r != FIDO_OK) + return (r); + *olen += curr_olen; + if (*olen == ilen) + break; + } + + return (FIDO_OK); +} + +int +fido_dev_open_with_info(fido_dev_t *dev) +{ + if (dev->path == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + return (fido_dev_open_wait(dev, dev->path, -1)); +} + int fido_dev_open(fido_dev_t *dev, const char *path) { @@ -162,7 +303,7 @@ fido_dev_close(fido_dev_t *dev) int fido_dev_cancel(fido_dev_t *dev) { - if (fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CANCEL, NULL, 0) < 0) + if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0) < 0) return (FIDO_ERR_TX); return (FIDO_OK); @@ -172,7 +313,7 @@ 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: NULL handle", __func__); + fido_log_debug("%s: non-NULL handle", __func__); return (FIDO_ERR_INVALID_ARGUMENT); } @@ -182,10 +323,20 @@ fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io) return (FIDO_ERR_INVALID_ARGUMENT); } - dev->io.open = io->open; - dev->io.close = io->close; - dev->io.read = io->read; - dev->io.write = io->write; + dev->io = *io; + + 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; return (FIDO_OK); } @@ -200,21 +351,44 @@ fido_init(int flags) fido_dev_t * fido_dev_new(void) { - fido_dev_t *dev; - fido_dev_io_t io; + fido_dev_t *dev; if ((dev = calloc(1, sizeof(*dev))) == NULL) return (NULL); dev->cid = CTAP_CID_BROADCAST; + 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); + + dev->cid = CTAP_CID_BROADCAST; + + 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); + } - io.open = fido_hid_open; - io.close = fido_hid_close; - io.read = fido_hid_read; - io.write = fido_hid_write; + dev->io = di->io; + dev->transport = di->transport; - if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) { - fido_log_debug("%s: fido_dev_set_io_functions", __func__); + if ((dev->path = strdup(di->path)) == NULL) { + fido_log_debug("%s: strdup", __func__); fido_dev_free(&dev); return (NULL); } @@ -230,6 +404,7 @@ fido_dev_free(fido_dev_t **dev_p) if (dev_p == NULL || (dev = *dev_p) == NULL) return; + free(dev->path); free(dev); *dev_p = NULL; diff --git a/src/eddsa.c b/src/eddsa.c index 92a0222..44a5563 100644 --- a/src/eddsa.c +++ b/src/eddsa.c @@ -23,6 +23,8 @@ EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key, (void)key; (void)keylen; + fido_log_debug("%s: unimplemented", __func__); + return (NULL); } @@ -34,6 +36,8 @@ EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub, (void)pub; (void)len; + fido_log_debug("%s: unimplemented", __func__); + return (0); } @@ -47,6 +51,8 @@ EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, (void)tbs; (void)tbslen; + fido_log_debug("%s: unimplemented", __func__); + return (0); } #endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */ @@ -55,6 +61,8 @@ EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, EVP_MD_CTX * EVP_MD_CTX_new(void) { + fido_log_debug("%s: unimplemented", __func__); + return (NULL); } diff --git a/src/err.c b/src/err.c index 5d3efd4..6261bfc 100644 --- a/src/err.c +++ b/src/err.c @@ -29,7 +29,7 @@ fido_strerr(int n) case FIDO_ERR_INVALID_CHANNEL: return "FIDO_ERR_INVALID_CHANNEL"; case FIDO_ERR_CBOR_UNEXPECTED_TYPE: - return "FIDO_ERR_UNEXPECTED_TYPE"; + return "FIDO_ERR_CBOR_UNEXPECTED_TYPE"; case FIDO_ERR_INVALID_CBOR: return "FIDO_ERR_INVALID_CBOR"; case FIDO_ERR_MISSING_PARAMETER: @@ -45,7 +45,7 @@ fido_strerr(int n) case FIDO_ERR_INVALID_CREDENTIAL: return "FIDO_ERR_INVALID_CREDENTIAL"; case FIDO_ERR_USER_ACTION_PENDING: - return "FIDO_ERR_ACTION_PENDING"; + return "FIDO_ERR_USER_ACTION_PENDING"; case FIDO_ERR_OPERATION_PENDING: return "FIDO_ERR_OPERATION_PENDING"; case FIDO_ERR_NO_OPERATIONS: @@ -55,11 +55,11 @@ fido_strerr(int n) case FIDO_ERR_OPERATION_DENIED: return "FIDO_ERR_OPERATION_DENIED"; case FIDO_ERR_KEY_STORE_FULL: - return "FIDO_ERR_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_OPERATION_PENDING"; + return "FIDO_ERR_NO_OPERATION_PENDING"; case FIDO_ERR_UNSUPPORTED_OPTION: return "FIDO_ERR_UNSUPPORTED_OPTION"; case FIDO_ERR_INVALID_OPTION: @@ -69,7 +69,7 @@ fido_strerr(int n) case FIDO_ERR_NO_CREDENTIALS: return "FIDO_ERR_NO_CREDENTIALS"; case FIDO_ERR_USER_ACTION_TIMEOUT: - return "FIDO_ERR_ACTION_TIMEOUT"; + return "FIDO_ERR_USER_ACTION_TIMEOUT"; case FIDO_ERR_NOT_ALLOWED: return "FIDO_ERR_NOT_ALLOWED"; case FIDO_ERR_PIN_INVALID: @@ -77,25 +77,25 @@ fido_strerr(int n) case FIDO_ERR_PIN_BLOCKED: return "FIDO_ERR_PIN_BLOCKED"; case FIDO_ERR_PIN_AUTH_INVALID: - return "FIDO_ERR_AUTH_INVALID"; + return "FIDO_ERR_PIN_AUTH_INVALID"; case FIDO_ERR_PIN_AUTH_BLOCKED: - return "FIDO_ERR_AUTH_BLOCKED"; + return "FIDO_ERR_PIN_AUTH_BLOCKED"; case FIDO_ERR_PIN_NOT_SET: - return "FIDO_ERR_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_POLICY_VIOLATION"; + return "FIDO_ERR_PIN_POLICY_VIOLATION"; case FIDO_ERR_PIN_TOKEN_EXPIRED: - return "FIDO_ERR_TOKEN_EXPIRED"; + return "FIDO_ERR_PIN_TOKEN_EXPIRED"; case FIDO_ERR_REQUEST_TOO_LARGE: - return "FIDO_ERR_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_ERR_OTHER: - return "FIDO_ERR_OTHER"; + return "FIDO_ERR_ERR_OTHER"; case FIDO_ERR_SPEC_LAST: return "FIDO_ERR_SPEC_LAST"; case FIDO_ERR_TX: diff --git a/src/es256.c b/src/es256.c index c8fd9f4..020ecaa 100644 --- a/src/es256.c +++ b/src/es256.c @@ -176,10 +176,15 @@ es256_pk_free(es256_pk_t **pkp) int es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len) { + const uint8_t *p = ptr; + if (len < sizeof(*pk)) return (FIDO_ERR_INVALID_ARGUMENT); - memcpy(pk, ptr, sizeof(*pk)); + if (len == sizeof(*pk) + 1 && *p == 0x04) + memcpy(pk, ++p, sizeof(*pk)); /* uncompressed format */ + else + memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */ return (FIDO_OK); } @@ -262,8 +267,12 @@ es256_pk_to_EVP_PKEY(const es256_pk_t *k) const int nid = NID_X9_62_prime256v1; int ok = -1; - if ((bnctx = BN_CTX_new()) == NULL || - (x = BN_CTX_get(bnctx)) == NULL || + 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; @@ -296,12 +305,16 @@ es256_pk_to_EVP_PKEY(const es256_pk_t *k) ok = 0; fail: - if (bnctx != NULL) + 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; @@ -313,7 +326,7 @@ fail: int es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) { - BN_CTX *ctx = NULL; + BN_CTX *bnctx = NULL; BIGNUM *x = NULL; BIGNUM *y = NULL; const EC_POINT *q = NULL; @@ -322,15 +335,17 @@ es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) int n; if ((q = EC_KEY_get0_public_key(ec)) == NULL || - (g = EC_KEY_get0_group(ec)) == NULL) + (g = EC_KEY_get0_group(ec)) == NULL || + (bnctx = BN_CTX_new()) == NULL) goto fail; - if ((ctx = BN_CTX_new()) == NULL || - (x = BN_CTX_get(ctx)) == NULL || - (y = BN_CTX_get(ctx)) == NULL) + BN_CTX_start(bnctx); + + if ((x = BN_CTX_get(bnctx)) == NULL || + (y = BN_CTX_get(bnctx)) == NULL) goto fail; - if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 || + if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 || (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) || (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) { fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp", @@ -346,8 +361,10 @@ es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec) ok = FIDO_OK; fail: - if (ctx != NULL) - BN_CTX_free(ctx); + if (bnctx != NULL) { + BN_CTX_end(bnctx); + BN_CTX_free(bnctx); + } return (ok); } @@ -362,7 +379,12 @@ es256_sk_to_EVP_PKEY(const es256_sk_t *k) const int nid = NID_X9_62_prime256v1; int ok = -1; - if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL || + 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; @@ -384,10 +406,14 @@ es256_sk_to_EVP_PKEY(const es256_sk_t *k) ok = 0; fail: - if (bnctx != NULL) + 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; diff --git a/src/export.gnu b/src/export.gnu index f111e33..cbfa69f 100644 --- a/src/export.gnu +++ b/src/export.gnu @@ -76,6 +76,7 @@ fido_cbor_info_extensions_ptr; fido_cbor_info_free; fido_cbor_info_maxmsgsiz; + fido_cbor_info_fwversion; fido_cbor_info_new; fido_cbor_info_options_len; fido_cbor_info_options_name_ptr; @@ -115,6 +116,7 @@ fido_credman_rp_name; fido_credman_rp_new; fido_cred_new; + fido_cred_prot; fido_cred_pubkey_len; fido_cred_pubkey_ptr; fido_cred_rp_id; @@ -125,6 +127,7 @@ fido_cred_set_extensions; fido_cred_set_fmt; fido_cred_set_options; + fido_cred_set_prot; fido_cred_set_rk; fido_cred_set_rp; fido_cred_set_sig; @@ -171,7 +174,9 @@ fido_dev_reset; fido_dev_set_io_functions; fido_dev_set_pin; + fido_dev_set_transport_functions; fido_init; + fido_set_log_handler; fido_strerr; rs256_pk_free; rs256_pk_from_ptr; diff --git a/src/export.llvm b/src/export.llvm index ef99a26..abde2e9 100644 --- a/src/export.llvm +++ b/src/export.llvm @@ -74,6 +74,7 @@ _fido_cbor_info_extensions_len _fido_cbor_info_extensions_ptr _fido_cbor_info_free _fido_cbor_info_maxmsgsiz +_fido_cbor_info_fwversion _fido_cbor_info_new _fido_cbor_info_options_len _fido_cbor_info_options_name_ptr @@ -113,6 +114,7 @@ _fido_credman_rp_id_hash_ptr _fido_credman_rp_name _fido_credman_rp_new _fido_cred_new +_fido_cred_prot _fido_cred_pubkey_len _fido_cred_pubkey_ptr _fido_cred_rp_id @@ -123,6 +125,7 @@ _fido_cred_set_clientdata_hash _fido_cred_set_extensions _fido_cred_set_fmt _fido_cred_set_options +_fido_cred_set_prot _fido_cred_set_rk _fido_cred_set_rp _fido_cred_set_sig @@ -169,7 +172,9 @@ _fido_dev_protocol _fido_dev_reset _fido_dev_set_io_functions _fido_dev_set_pin +_fido_dev_set_transport_functions _fido_init +_fido_set_log_handler _fido_strerr _rs256_pk_free _rs256_pk_from_ptr diff --git a/src/export.msvc b/src/export.msvc index ff5425a..06ec69a 100644 --- a/src/export.msvc +++ b/src/export.msvc @@ -75,6 +75,7 @@ fido_cbor_info_extensions_len fido_cbor_info_extensions_ptr fido_cbor_info_free fido_cbor_info_maxmsgsiz +fido_cbor_info_fwversion fido_cbor_info_new fido_cbor_info_options_len fido_cbor_info_options_name_ptr @@ -114,6 +115,7 @@ fido_credman_rp_id_hash_ptr fido_credman_rp_name fido_credman_rp_new fido_cred_new +fido_cred_prot fido_cred_pubkey_len fido_cred_pubkey_ptr fido_cred_rp_id @@ -124,6 +126,7 @@ fido_cred_set_clientdata_hash fido_cred_set_extensions fido_cred_set_fmt fido_cred_set_options +fido_cred_set_prot fido_cred_set_rk fido_cred_set_rp fido_cred_set_sig @@ -170,7 +173,9 @@ fido_dev_protocol fido_dev_reset fido_dev_set_io_functions fido_dev_set_pin +fido_dev_set_transport_functions fido_init +fido_set_log_handler fido_strerr rs256_pk_free rs256_pk_from_ptr diff --git a/src/extern.h b/src/extern.h index c35af58..fc0a49d 100644 --- a/src/extern.h +++ b/src/extern.h @@ -7,6 +7,15 @@ #ifndef _EXTERN_H #define _EXTERN_H +#include + +#include "fido/types.h" +#include "blob.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + /* aes256 */ int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *); @@ -16,7 +25,7 @@ cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t); cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t); cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *, const fido_blob_t *, const fido_blob_t *); -cbor_item_t *cbor_encode_extensions(int); +cbor_item_t *cbor_encode_extensions(const fido_cred_ext_t *); cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *, const es256_pk_t *, const fido_blob_t *); cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t); @@ -35,7 +44,7 @@ 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_cred_authdata(const cbor_item_t *, int, fido_blob_t *, - fido_authdata_t *, fido_attcred_t *, int *); + 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 *, int *, fido_blob_t *); int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *); @@ -77,8 +86,8 @@ 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_read(void *, unsigned char *, size_t, int); +int fido_hid_write(void *, const unsigned char *, size_t); /* generic i/o */ int fido_rx_cbor_status(fido_dev_t *, int); @@ -109,6 +118,7 @@ int u2f_authenticate(fido_dev_t *, fido_assert_t *, int); /* unexposed fido ops */ int fido_dev_authkey(fido_dev_t *, es256_pk_t *); +int fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int); int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *, const es256_pk_t *, fido_blob_t *); int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **); @@ -128,5 +138,19 @@ int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *, const fido_blob_t *); int fido_verify_sig_eddsa(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 *); + +/* hid device manifest */ +int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *); + +/* device manifest registration */ +typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *); +int fido_dev_register_manifest_func(const dev_manifest_func_t); +void fido_dev_unregister_manifest_func(const dev_manifest_func_t); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ #endif /* !_EXTERN_H */ diff --git a/src/fido.h b/src/fido.h index f85a41a..e41de89 100644 --- a/src/fido.h +++ b/src/fido.h @@ -14,24 +14,6 @@ #include #include -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 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 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; - #ifdef _FIDO_INTERNAL #include #include @@ -39,28 +21,21 @@ typedef enum { #include "blob.h" #include "../openbsd-compat/openbsd-compat.h" #include "iso7816.h" -#include "types.h" #include "extern.h" #endif #include "fido/err.h" #include "fido/param.h" +#include "fido/types.h" -#ifndef _FIDO_INTERNAL -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 rs256_pk rs256_pk_t; -typedef struct eddsa_pk eddsa_pk_t; -#endif +#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); @@ -76,6 +51,7 @@ void fido_dev_info_free(fido_dev_info_t **, size_t); #define FIDO_DEBUG 0x01 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_clientdata_hash_ptr(const fido_assert_t *); @@ -121,19 +97,23 @@ int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *, 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_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__)); +int fido_assert_set_options(fido_assert_t *, bool, bool) + __attribute__((__deprecated__("use fido_assert_set_up/fido_assert_set_uv"))); 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_verify(const fido_assert_t *, size_t, int, const void *); int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t); +int fido_cred_prot(const fido_cred_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_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_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__)); +int fido_cred_set_options(fido_cred_t *, bool, bool) + __attribute__((__deprecated__("use fido_cred_set_rk/fido_cred_set_uv"))); +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); @@ -152,10 +132,12 @@ 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_info_manifest(fido_dev_info_t *, size_t, size_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 *); size_t fido_assert_authdata_len(const fido_assert_t *, size_t); size_t fido_assert_clientdata_hash_len(const fido_assert_t *); @@ -178,7 +160,7 @@ size_t fido_cred_sig_len(const fido_cred_t *); size_t fido_cred_x5c_len(const fido_cred_t *); uint8_t fido_assert_flags(const fido_assert_t *, size_t); -uint32_t fido_assert_sigcount(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 *); uint8_t fido_dev_protocol(const fido_dev_t *); uint8_t fido_dev_major(const fido_dev_t *); @@ -188,7 +170,12 @@ 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_maxmsgsiz(const fido_cbor_info_t *); +uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *); bool fido_dev_is_fido2(const fido_dev_t *); +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + #endif /* !_FIDO_H */ diff --git a/src/fido/bio.h b/src/fido/bio.h index 31dffe4..afe9ca4 100644 --- a/src/fido/bio.h +++ b/src/fido/bio.h @@ -10,8 +10,20 @@ #include #include +#ifdef _FIDO_INTERNAL +#include "blob.h" #include "fido/err.h" #include "fido/param.h" +#include "fido/types.h" +#else +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ #ifdef _FIDO_INTERNAL struct fido_bio_template { @@ -92,4 +104,8 @@ 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 */ diff --git a/src/fido/credman.h b/src/fido/credman.h index 1c7cafe..eaffd65 100644 --- a/src/fido/credman.h +++ b/src/fido/credman.h @@ -10,8 +10,20 @@ #include #include +#ifdef _FIDO_INTERNAL +#include "blob.h" #include "fido/err.h" #include "fido/param.h" +#include "fido/types.h" +#else +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ #ifdef _FIDO_INTERNAL struct fido_credman_metadata { @@ -71,4 +83,8 @@ 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 */ diff --git a/src/fido/eddsa.h b/src/fido/eddsa.h index 9de272d..4a81017 100644 --- a/src/fido/eddsa.h +++ b/src/fido/eddsa.h @@ -12,6 +12,16 @@ #include #include +#ifdef _FIDO_INTERNAL +#include "types.h" +#else +#include +#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 *); @@ -37,4 +47,8 @@ void EVP_MD_CTX_free(EVP_MD_CTX *); #endif /* _FIDO_INTERNAL */ +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + #endif /* !_FIDO_EDDSA_H */ diff --git a/src/fido/err.h b/src/fido/err.h index 11f52bc..d7453fc 100644 --- a/src/fido/err.h +++ b/src/fido/err.h @@ -64,6 +64,14 @@ #define FIDO_ERR_USER_PRESENCE_REQUIRED -8 #define FIDO_ERR_INTERNAL -9 +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + const char *fido_strerr(int); +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + #endif /* _FIDO_ERR_H */ diff --git a/src/fido/es256.h b/src/fido/es256.h index d3d13dd..80f4db3 100644 --- a/src/fido/es256.h +++ b/src/fido/es256.h @@ -12,6 +12,16 @@ #include #include +#ifdef _FIDO_INTERNAL +#include "types.h" +#else +#include +#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 *); @@ -31,4 +41,8 @@ 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 */ diff --git a/src/fido/param.h b/src/fido/param.h index 9e12ac6..7d3c0cc 100644 --- a/src/fido/param.h +++ b/src/fido/param.h @@ -58,6 +58,11 @@ #define FIDO_RANDOM_DEV "/dev/urandom" #endif +/* Maximum message size in bytes. */ +#ifndef FIDO_MAXMSG +#define FIDO_MAXMSG 1200 +#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 */ @@ -80,5 +85,11 @@ /* Supported extensions. */ #define FIDO_EXT_HMAC_SECRET 0x01 +#define FIDO_EXT_CRED_PROTECT 0x02 + +/* 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 #endif /* !_FIDO_PARAM_H */ diff --git a/src/fido/rs256.h b/src/fido/rs256.h index d2fa162..2b08d59 100644 --- a/src/fido/rs256.h +++ b/src/fido/rs256.h @@ -12,6 +12,16 @@ #include #include +#ifdef _FIDO_INTERNAL +#include "types.h" +#else +#include +#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 *); @@ -19,4 +29,8 @@ EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *); 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 */ diff --git a/src/fido/types.h b/src/fido/types.h new file mode 100644 index 0000000..5df5e36 --- /dev/null +++ b/src/fido/types.h @@ -0,0 +1,235 @@ +/* + * 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. + */ + +#ifndef _FIDO_TYPES_H +#define _FIDO_TYPES_H + +#include +#include + +#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 *); + +#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 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; + rs256_pk_t rs256; + eddsa_pk_t eddsa; + } pubkey; +} fido_attcred_t; + +typedef struct fido_attstmt { + fido_blob_t x5c; /* attestation certificate */ + fido_blob_t sig; /* attestation signature */ +} 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 */ +} fido_cred_ext_t; + +typedef struct fido_cred { + 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; /* raw cbor 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_cred_t; + +typedef struct _fido_assert_stmt { + fido_blob_t id; /* credential id */ + fido_user_t user; /* user attributes */ + fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */ + fido_blob_t hmac_secret; /* hmac secret */ + int authdata_ext; /* decoded extensions */ + fido_blob_t authdata_cbor; /* raw cbor payload */ + fido_authdata_t authdata; /* decoded authdata payload */ + fido_blob_t sig; /* signature of cdh + authdata */ +} fido_assert_stmt; + +typedef struct fido_assert { + char *rp_id; /* relying party id */ + fido_blob_t cdh; /* client data hash */ + fido_blob_t hmac_salt; /* optional hmac-secret salt */ + fido_blob_array_t allow_list; /* list of allowed credentials */ + fido_opt_t up; /* user presence */ + fido_opt_t uv; /* user verification */ + int 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_cbor_info { + fido_str_array_t versions; /* supported versions: fido2|u2f */ + fido_str_array_t extensions; /* list of supported extensions */ + 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 */ + uint64_t fwversion; /* firmware version */ +} 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 */ + fido_dev_transport_t transport; /* transport functions */ +} 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 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 */ diff --git a/src/hid_hidapi.c b/src/hid_hidapi.c new file mode 100644 index 0000000..915621f --- /dev/null +++ b/src/hid_hidapi.c @@ -0,0 +1,138 @@ +/* + * 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. + */ + +#include + +#include +#include +#include + +#include "fido.h" + +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); + return -1; + } + + di->product_id = d->product_id; + di->vendor_id = d->vendor_id; + di->io = (fido_dev_io_t) { + &fido_hid_open, + &fido_hid_close, + &fido_hid_read, + &fido_hid_write, + }; + + return 0; +} + +void * +fido_hid_open(const char *path) +{ + return hid_open_path(path); +} + +void +fido_hid_close(void *hid_dev_handle) +{ + hid_close(hid_dev_handle); +} + +int +fido_hid_read(void *hid_dev_handle, unsigned char *buf, size_t len, int ms) +{ + return hid_read_timeout(hid_dev_handle, buf, len, ms); +} + +int +fido_hid_write(void *hid_dev_handle, const unsigned char *buf, size_t len) +{ + return hid_write(hid_dev_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 defined(_WIN32) || defined(__APPLE__) + if (d->usage_page != 0xf1d0) + continue; +#endif + if (copy_info(&devlist[*olen], d) == 0) { + if (++(*olen) == ilen) + break; + } + } + + hid_free_enumeration(hdi); + + return FIDO_OK; +} diff --git a/src/hid_linux.c b/src/hid_linux.c index c7cabc9..99c5afb 100644 --- a/src/hid_linux.c +++ b/src/hid_linux.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "fido.h" @@ -98,7 +99,6 @@ get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page, static int get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) { - int r; int s = -1; int fd; int ok = -1; @@ -108,7 +108,7 @@ get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) return (-1); } - if ((r = ioctl(fd, HIDIOCGRDESCSIZE, &s)) < 0 || s < 0 || + if (ioctl(fd, HIDIOCGRDESCSIZE, &s) < 0 || s < 0 || (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) { fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__); goto fail; @@ -116,7 +116,7 @@ get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd) hrd->size = s; - if ((r = ioctl(fd, HIDIOCGRDESC, hrd)) < 0) { + if (ioctl(fd, HIDIOCGRDESC, hrd) < 0) { fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__); goto fail; } @@ -240,7 +240,7 @@ fail: } int -fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { struct udev *udev = NULL; struct udev_enumerate *udev_enum = NULL; @@ -267,6 +267,12 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 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; } diff --git a/src/hid_openbsd.c b/src/hid_openbsd.c index 8b92bd6..2b31dba 100644 --- a/src/hid_openbsd.c +++ b/src/hid_openbsd.c @@ -29,7 +29,7 @@ struct hid_openbsd { }; int -fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { size_t i; char path[64]; @@ -101,6 +101,12 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) di = &devlist[*olen]; memset(di, 0, sizeof(*di)); + di->io = (fido_dev_io_t) { + fido_hid_open, + fido_hid_close, + fido_hid_read, + fido_hid_write, + }; if ((di->path = strdup(path)) == NULL || (di->manufacturer = strdup(udi.udi_vendor)) == NULL || (di->product = strdup(udi.udi_product)) == NULL) { diff --git a/src/hid_osx.c b/src/hid_osx.c index b705b43..5c40747 100644 --- a/src/hid_osx.c +++ b/src/hid_osx.c @@ -197,7 +197,7 @@ copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev) } int -fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { IOHIDManagerRef manager = NULL; CFSetRef devset = NULL; @@ -240,6 +240,12 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) for (CFIndex 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; } @@ -378,15 +384,18 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms) IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(), dev->loop_id); - do - r = CFRunLoopRunInMode(dev->loop_id, 0.003, true); - while (r != kCFRunLoopRunHandledSource); + r = CFRunLoopRunInMode(dev->loop_id, 0.3, true); IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL); IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL); IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(), dev->loop_id); + if (r != kCFRunLoopRunHandledSource) { + fido_log_debug("%s: CFRunLoopRunInMode=%d", __func__, (int)r); + return (-1); + } + return (REPORT_LEN - 1); } diff --git a/src/hid_win.c b/src/hid_win.c index 6d93778..f970589 100644 --- a/src/hid_win.c +++ b/src/hid_win.c @@ -172,7 +172,7 @@ fail: } int -fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +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; @@ -234,6 +234,12 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) } if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) { + devlist[*olen].io = (fido_dev_io_t) { + fido_hid_open, + fido_hid_close, + fido_hid_read, + fido_hid_write, + }; if (++(*olen) == ilen) break; } diff --git a/src/info.c b/src/info.c index e896503..8e256fa 100644 --- a/src/info.c +++ b/src/info.c @@ -217,6 +217,8 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) return (cbor_decode_uint64(val, &ci->maxmsgsiz)); case 6: /* pinProtocols */ return (decode_protocols(val, &ci->protocols)); + case 14: /* fwVersion */ + return (cbor_decode_uint64(val, &ci->fwversion)); default: /* ignore */ fido_log_debug("%s: cbor type", __func__); return (0); @@ -226,12 +228,11 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg) static int fido_dev_get_cbor_info_tx(fido_dev_t *dev) { - const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + const unsigned char cbor[] = { CTAP_CBOR_GETINFO }; fido_log_debug("%s: dev=%p", __func__, (void *)dev); - if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { fido_log_debug("%s: fido_tx", __func__); return (FIDO_ERR_TX); } @@ -242,8 +243,7 @@ fido_dev_get_cbor_info_tx(fido_dev_t *dev) static int fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[512]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev, @@ -251,7 +251,8 @@ fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) memset(ci, 0, sizeof(*ci)); - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } @@ -260,7 +261,7 @@ fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) parse_reply_element)); } -static int +int fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms) { int r; @@ -397,6 +398,12 @@ fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci) return (ci->maxmsgsiz); } +uint64_t +fido_cbor_info_fwversion(const fido_cbor_info_t *ci) +{ + return (ci->fwversion); +} + const uint8_t * fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci) { diff --git a/src/io.c b/src/io.c index aa88720..af2f49a 100644 --- a/src/io.c +++ b/src/io.c @@ -33,25 +33,40 @@ struct frame { #define MIN(x, y) ((x) > (y) ? (y) : (x)) #endif -static size_t -tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +static int +tx_empty(fido_dev_t *d, uint8_t cmd) { struct frame *fp; - unsigned char pkt[sizeof(*fp) + 1]; - int n; + unsigned char pkt[sizeof(*fp) + 1]; + int n; - if (d->io.write == NULL || (cmd & 0x80) == 0) - return (0); + memset(&pkt, 0, sizeof(pkt)); + fp = (struct frame *)(pkt + 1); + fp->cid = d->cid; + fp->body.init.cmd = CTAP_FRAME_INIT | cmd; + + n = d->io.write(d->io_handle, pkt, sizeof(pkt)); + if (n < 0 || (size_t)n != sizeof(pkt)) + return (-1); + + return (0); +} + +static size_t +tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +{ + struct frame *fp; + unsigned char pkt[sizeof(*fp) + 1]; + int n; memset(&pkt, 0, sizeof(pkt)); fp = (struct frame *)(pkt + 1); fp->cid = d->cid; - fp->body.init.cmd = 0x80 | cmd; + fp->body.init.cmd = CTAP_FRAME_INIT | cmd; fp->body.init.bcnth = (count >> 8) & 0xff; fp->body.init.bcntl = count & 0xff; count = MIN(count, sizeof(fp->body.init.data)); - if (count) - memcpy(&fp->body.init.data, buf, count); + memcpy(&fp->body.init.data, buf, count); n = d->io.write(d->io_handle, pkt, sizeof(pkt)); if (n < 0 || (size_t)n != sizeof(pkt)) @@ -61,19 +76,16 @@ tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) } static size_t -tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count) +tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count) { struct frame *fp; unsigned char pkt[sizeof(*fp) + 1]; int n; - if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX) - return (0); - memset(&pkt, 0, sizeof(pkt)); fp = (struct frame *)(pkt + 1); fp->cid = d->cid; - fp->body.cont.seq = (uint8_t)seq; + fp->body.cont.seq = seq; count = MIN(count, sizeof(fp->body.cont.data)); memcpy(&fp->body.cont.data, buf, count); @@ -84,52 +96,56 @@ tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count) return (count); } -int -fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +static int +tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count) { - int seq = 0; - size_t sent; - - fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__, - (void *)d, cmd, buf, count); - fido_log_xxd(buf, count); - - if (d->io_handle == NULL || count > UINT16_MAX) { - fido_log_debug("%s: invalid argument (%p, %zu)", __func__, - d->io_handle, count); - return (-1); - } + size_t n, sent; if ((sent = tx_preamble(d, cmd, buf, count)) == 0) { fido_log_debug("%s: tx_preamble", __func__); return (-1); } - while (sent < count) { + for (uint8_t seq = 0; sent < count; sent += n) { if (seq & 0x80) { fido_log_debug("%s: seq & 0x80", __func__); return (-1); } - const uint8_t *p = (const uint8_t *)buf + sent; - size_t n = tx_frame(d, seq++, p, count - sent); - if (n == 0) { + if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) { fido_log_debug("%s: tx_frame", __func__); return (-1); } - sent += n; } return (0); } +int +fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count) +{ + fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__, + (void *)d, cmd, (const void *)buf, count); + fido_log_xxd(buf, count); + + if (d->transport.tx != NULL) + return (d->transport.tx(d, cmd, buf, count)); + + if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) { + fido_log_debug("%s: invalid argument", __func__); + return (-1); + } + + if (count == 0) + return (tx_empty(d, cmd)); + + return (tx(d, cmd, buf, count)); +} + static int rx_frame(fido_dev_t *d, struct frame *fp, int ms) { int n; - if (d->io.read == NULL) - return (-1); - n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms); if (n < 0 || (size_t)n != sizeof(*fp)) return (-1); @@ -138,7 +154,7 @@ rx_frame(fido_dev_t *d, struct frame *fp, int ms) } static int -rx_preamble(fido_dev_t *d, struct frame *fp, int ms) +rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms) { do { if (rx_frame(d, fp, ms) < 0) @@ -149,66 +165,57 @@ rx_preamble(fido_dev_t *d, struct frame *fp, int ms) } while (fp->cid == d->cid && fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)); + fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp); + fido_log_xxd(fp, sizeof(*fp)); + +#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); } -int -fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms) +static int +rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms) { - struct frame f; - uint16_t r; - uint16_t flen; - int seq; - - if (d->io_handle == NULL || (cmd & 0x80) == 0) { - fido_log_debug("%s: invalid argument (%p, 0x%02x)", __func__, - d->io_handle, cmd); - return (-1); - } + struct frame f; + uint16_t r, payload_len; - if (rx_preamble(d, &f, ms) < 0) { + if (rx_preamble(d, cmd, &f, ms) < 0) { fido_log_debug("%s: rx_preamble", __func__); return (-1); } - fido_log_debug("%s: initiation frame at %p, len %zu", __func__, - (void *)&f, sizeof(f)); - fido_log_xxd(&f, sizeof(f)); + payload_len = (f.body.init.bcnth << 8) | f.body.init.bcntl; + fido_log_debug("%s: payload_len=%zu", __func__, (size_t)payload_len); -#ifdef FIDO_FUZZ - f.cid = d->cid; - f.body.init.cmd = cmd; -#endif - - if (f.cid != d->cid || f.body.init.cmd != cmd) { - fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)", - __func__, f.cid, d->cid, f.body.init.cmd, cmd); + if (count < (size_t)payload_len) { + fido_log_debug("%s: count < payload_len", __func__); return (-1); } - flen = (f.body.init.bcnth << 8) | f.body.init.bcntl; - if (count < (size_t)flen) { - fido_log_debug("%s: count < flen (%zu, %zu)", __func__, count, - (size_t)flen); - return (-1); - } - if (flen < sizeof(f.body.init.data)) { - memcpy(buf, f.body.init.data, flen); - return (flen); + if (payload_len < sizeof(f.body.init.data)) { + memcpy(buf, f.body.init.data, payload_len); + return (payload_len); } memcpy(buf, f.body.init.data, sizeof(f.body.init.data)); r = sizeof(f.body.init.data); - seq = 0; - while ((size_t)r < flen) { + for (int seq = 0; (size_t)r < payload_len; seq++) { if (rx_frame(d, &f, ms) < 0) { fido_log_debug("%s: rx_frame", __func__); return (-1); } - fido_log_debug("%s: continuation frame at %p, len %zu", - __func__, (void *)&f, sizeof(f)); + fido_log_debug("%s: continuation frame at %p", __func__, + (void *)&f); fido_log_xxd(&f, sizeof(f)); #ifdef FIDO_FUZZ @@ -216,38 +223,57 @@ fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms) f.body.cont.seq = seq; #endif - if (f.cid != d->cid || f.body.cont.seq != seq++) { + 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); } - uint8_t *p = (uint8_t *)buf + r; - - if ((size_t)(flen - r) > sizeof(f.body.cont.data)) { - memcpy(p, f.body.cont.data, sizeof(f.body.cont.data)); + if ((size_t)(payload_len - r) > sizeof(f.body.cont.data)) { + memcpy(buf + r, f.body.cont.data, + sizeof(f.body.cont.data)); r += sizeof(f.body.cont.data); } else { - memcpy(p, f.body.cont.data, flen - r); - r += (flen - r); /* break */ + memcpy(buf + r, f.body.cont.data, payload_len - r); + r += (payload_len - r); /* break */ } } - fido_log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r); - fido_log_xxd(buf, r); - return (r); } +int +fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms) +{ + int n; + + fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu, ms=%d", + __func__, (void *)d, cmd, (const void *)buf, count, ms); + + if (d->transport.rx != NULL) + return (d->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_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n); + fido_log_xxd(buf, n); + } + + return (n); +} + int fido_rx_cbor_status(fido_dev_t *d, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; - if ((reply_len = fido_rx(d, cmd, &reply, sizeof(reply), ms)) < 0 || - (size_t)reply_len < 1) { + if ((reply_len = fido_rx(d, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0 || (size_t)reply_len < 1) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } diff --git a/src/iso7816.h b/src/iso7816.h index 426cd97..563243f 100644 --- a/src/iso7816.h +++ b/src/iso7816.h @@ -7,8 +7,15 @@ #ifndef _ISO7816_H #define _ISO7816_H +#include +#include + #include "packed.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + PACKED_TYPE(iso7816_header_t, struct iso7816_header { uint8_t cla; @@ -35,4 +42,8 @@ iso7816_apdu_t *iso7816_new(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 */ diff --git a/src/log.c b/src/log.c index 982bdb7..d6f0934 100644 --- a/src/log.c +++ b/src/log.c @@ -7,57 +7,86 @@ #include #include #include +#include + #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); +} void fido_log_init(void) { logging = 1; + log_handler = log_on_stderr; } void -fido_log_xxd(const void *buf, size_t count) +fido_log_debug(const char *fmt, ...) { - const uint8_t *ptr = buf; - size_t i; + char line[LINELEN]; + va_list ap; + int r; - if (!logging) + if (!logging || log_handler == NULL) return; - fprintf(stderr, " "); - - for (i = 0; i < count; i++) { - fprintf(stderr, "%02x ", *ptr++); - if ((i + 1) % 16 == 0 && i + 1 < count) - fprintf(stderr, "\n "); - } - - fprintf(stderr, "\n"); - fflush(stderr); + va_start(ap, fmt); + r = vsnprintf(line, sizeof(line) - 1, fmt, ap); + va_end(ap); + if (r < 0 || (size_t)r >= sizeof(line) - 1) + return; + strlcat(line, "\n", sizeof(line)); + log_handler(line); } void -fido_log_debug(const char *fmt, ...) +fido_log_xxd(const void *buf, size_t count) { - va_list ap; + const uint8_t *ptr = buf; + char row[XXDROW]; + char xxd[XXDLEN]; - if (!logging) + if (!logging || log_handler == NULL || count == 0) return; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); + *row = '\0'; - fprintf(stderr, "\n"); - fflush(stderr); + 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_set_log_handler(fido_log_handler_t *handler) +{ + if (handler != NULL) + log_handler = handler; } #endif /* !FIDO_NO_DIAGNOSTIC */ diff --git a/src/pin.c b/src/pin.c index 1ed555c..36acbe4 100644 --- a/src/pin.c +++ b/src/pin.c @@ -5,6 +5,7 @@ */ #include + #include "fido.h" #include "fido/es256.h" @@ -23,6 +24,14 @@ parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg) return (fido_blob_decode(val, token)); } +#ifdef FIDO_UVTOKEN +static int +parse_uvtoken(const cbor_item_t *key, const cbor_item_t *val, void *arg) +{ + return (parse_pintoken(key, val, arg)); +} +#endif /* FIDO_UVTOKEN */ + static int fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk) @@ -51,8 +60,8 @@ fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin, goto fail; } - if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -67,13 +76,47 @@ fail: return (r); } +#ifdef FIDO_UVTOKEN +static int +fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk) +{ + fido_blob_t f; + cbor_item_t *argv[3]; + 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(6)) == NULL || + (argv[2] = es256_pk_encode(pk, 0)) == 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) < 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); +} +#endif /* FIDO_UVTOKEN */ + static int fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; fido_blob_t *aes_token = NULL; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; @@ -82,7 +125,8 @@ fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, goto fail; } - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; @@ -107,15 +151,69 @@ fail: return (r); } +#ifdef FIDO_UVTOKEN +static int +fido_dev_get_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 reply[FIDO_MAXMSG]; + int reply_len; + int r; + + if ((aes_token = fido_blob_new()) == NULL) { + r = FIDO_ERR_INTERNAL; + goto fail; + } + + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { + fido_log_debug("%s: fido_rx", __func__); + r = FIDO_ERR_RX; + goto fail; + } + + if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token, + parse_uvtoken)) != FIDO_OK) { + fido_log_debug("%s: parse_uvtoken", __func__); + goto fail; + } + + if (aes256_cbc_dec(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); + + return (r); +} +#endif /* FIDO_UVTOKEN */ + static int fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms) { int r; +#ifdef FIDO_UVTOKEN + if (getenv("FIDO_UVTOKEN") != NULL) { + if ((r = fido_dev_get_uv_token_tx(dev, pk)) != FIDO_OK || + (r = fido_dev_get_uv_token_rx(dev, ecdh, token, ms)) != FIDO_OK) + return (r); + } else { + if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK || + (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK) + return (r); + } +#else if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK || (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK) return (r); +#endif return (FIDO_OK); } @@ -196,8 +294,8 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin) goto fail; } - if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -249,8 +347,8 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin) goto fail; } - if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -338,8 +436,8 @@ fido_dev_get_retry_count_tx(fido_dev_t *dev) goto fail; } - if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 || - fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) { + if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv), + &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; @@ -356,14 +454,14 @@ fail: static int fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; - unsigned char reply[512]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int r; *retries = 0; - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) { + if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), + ms)) < 0) { fido_log_debug("%s: fido_rx", __func__); return (FIDO_ERR_RX); } diff --git a/src/reset.c b/src/reset.c index 4b2c88a..ebda1cd 100644 --- a/src/reset.c +++ b/src/reset.c @@ -10,10 +10,9 @@ static int fido_dev_reset_tx(fido_dev_t *dev) { - const unsigned char cbor[] = { CTAP_CBOR_RESET }; - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR; + const unsigned char cbor[] = { CTAP_CBOR_RESET }; - if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) { + if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) { fido_log_debug("%s: fido_tx", __func__); return (FIDO_ERR_TX); } diff --git a/src/types.h b/src/types.h deleted file mode 100644 index 42ed1b7..0000000 --- a/src/types.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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. - */ - -#ifndef _TYPES_H -#define _TYPES_H - -#include "packed.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 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; - rs256_pk_t rs256; - eddsa_pk_t eddsa; - } pubkey; -} fido_attcred_t; - -typedef struct fido_attstmt { - fido_blob_t x5c; /* attestation certificate */ - fido_blob_t sig; /* attestation signature */ -} 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 { - 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 */ - int ext; /* enabled extensions */ - int type; /* cose algorithm */ - char *fmt; /* credential format */ - int authdata_ext; /* decoded extensions */ - fido_blob_t authdata_cbor; /* raw cbor 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_cred_t; - -typedef struct _fido_assert_stmt { - fido_blob_t id; /* credential id */ - fido_user_t user; /* user attributes */ - fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */ - fido_blob_t hmac_secret; /* hmac secret */ - int authdata_ext; /* decoded extensions */ - fido_blob_t authdata_cbor; /* raw cbor payload */ - fido_authdata_t authdata; /* decoded authdata payload */ - fido_blob_t sig; /* signature of cdh + authdata */ -} fido_assert_stmt; - -typedef struct fido_assert { - char *rp_id; /* relying party id */ - fido_blob_t cdh; /* client data hash */ - fido_blob_t hmac_salt; /* optional hmac-secret salt */ - fido_blob_array_t allow_list; /* list of allowed credentials */ - fido_opt_t up; /* user presence */ - fido_opt_t uv; /* user verification */ - int 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_cbor_info { - fido_str_array_t versions; /* supported versions: fido2|u2f */ - fido_str_array_t extensions; /* list of supported extensions */ - 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_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_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 */ - void *io_handle; /* abstract i/o handle */ - fido_dev_io_t io; /* i/o functions & data */ -} fido_dev_t; - -#endif /* !_TYPES_H */ diff --git a/src/u2f.c b/src/u2f.c index 82b289f..19a959d 100644 --- a/src/u2f.c +++ b/src/u2f.c @@ -125,11 +125,10 @@ authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount, static int send_dummy_register(fido_dev_t *dev, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; iso7816_apdu_t *apdu = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char application[SHA256_DIGEST_LENGTH]; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int r; #ifdef FIDO_FUZZ @@ -150,13 +149,13 @@ send_dummy_register(fido_dev_t *dev, int ms) } do { - if (fido_tx(dev, cmd, iso7816_ptr(apdu), + if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } - if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) < 2) { + if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; @@ -179,11 +178,10 @@ static int key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, int *found, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; iso7816_apdu_t *apdu = NULL; unsigned char challenge[SHA256_DIGEST_LENGTH]; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; - unsigned char reply[8]; + unsigned char reply[FIDO_MAXMSG]; uint8_t key_id_len; int r; @@ -217,12 +215,13 @@ key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, goto fail; } - if (fido_tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) { + if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), + iso7816_len(apdu)) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } - if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) != 2) { + if (fido_rx(dev, CTAP_CMD_MSG, &reply, sizeof(reply), ms) != 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; @@ -285,10 +284,9 @@ 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) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; iso7816_apdu_t *apdu = NULL; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; - unsigned char reply[128]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; uint8_t key_id_len; int r; @@ -326,14 +324,14 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id, } do { - if (fido_tx(dev, cmd, iso7816_ptr(apdu), + if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), - ms)) < 2) { + if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, + sizeof(reply), ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; @@ -575,10 +573,9 @@ fail: int u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms) { - const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG; iso7816_apdu_t *apdu = NULL; unsigned char rp_id_hash[SHA256_DIGEST_LENGTH]; - unsigned char reply[2048]; + unsigned char reply[FIDO_MAXMSG]; int reply_len; int found; int r; @@ -634,14 +631,14 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms) } do { - if (fido_tx(dev, cmd, iso7816_ptr(apdu), + if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) { fido_log_debug("%s: fido_tx", __func__); r = FIDO_ERR_TX; goto fail; } - if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), - ms)) < 2) { + if ((reply_len = fido_rx(dev, CTAP_CMD_MSG, &reply, + sizeof(reply), ms)) < 2) { fido_log_debug("%s: fido_rx", __func__); r = FIDO_ERR_RX; goto fail; @@ -687,6 +684,12 @@ u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id, 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; @@ -699,8 +702,7 @@ u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id, goto fail; } - if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0 || - fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK || + 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; @@ -724,6 +726,7 @@ fail: int u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms) { + int nfound = 0; int nauth_ok = 0; int r; @@ -739,20 +742,30 @@ u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms) } for (size_t i = 0; i < fa->allow_list.len; i++) { - if ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i], - fa, nauth_ok, ms)) == FIDO_OK) { + switch ((r = u2f_authenticate_single(dev, + &fa->allow_list.ptr[i], fa, nfound, ms))) { + case FIDO_OK: nauth_ok++; - } else if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) { - fido_log_debug("%s: u2f_authenticate_single", __func__); - return (r); + /* 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 */ } - /* ignore credentials that don't exist */ } - fa->stmt_len = nauth_ok; + fa->stmt_len = nfound; - if (nauth_ok == 0) + if (nfound == 0) return (FIDO_ERR_NO_CREDENTIALS); + if (nauth_ok == 0) + return (FIDO_ERR_USER_PRESENCE_REQUIRED); return (FIDO_OK); } -- cgit v1.2.3