diff --git a/.gitmodules b/.gitmodules index 6e06e69..852c02c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "pico-hsm-sdk"] - path = pico-hsm-sdk - url = ../pico-hsm-sdk +[submodule "pico-keys-sdk"] + path = pico-keys-sdk + url = https://github.com/polhenarejos/pico-keys-sdk diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eee727..a5f60bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ set(SOURCES ${SOURCES} endif() set(USB_ITF_HID 1) -include(pico-hsm-sdk/pico_hsm_sdk_import.cmake) +include(pico-keys-sdk/pico_keys_sdk_import.cmake) set(INCLUDES ${INCLUDES} ${CMAKE_CURRENT_LIST_DIR}/src/fido @@ -147,5 +147,5 @@ target_compile_options(pico_fido PUBLIC endif (APPLE) else() pico_add_extra_outputs(pico_fido) -target_link_libraries(pico_fido PRIVATE pico_hsm_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board) +target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board) endif() diff --git a/build_pico_fido.sh b/build_pico_fido.sh index d5a8743..e3f8434 100755 --- a/build_pico_fido.sh +++ b/build_pico_fido.sh @@ -1,7 +1,7 @@ #!/bin/bash VERSION_MAJOR="5" -VERSION_MINOR="6-eddsa1" +VERSION_MINOR="8-eddsa1" rm -rf release/* cd build_release diff --git a/pico-hsm-sdk b/pico-hsm-sdk deleted file mode 160000 index 5ec98c8..0000000 --- a/pico-hsm-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ec98c84aa8aa8aec4dda6d609fcca1d57d1eb3a diff --git a/pico-keys-sdk b/pico-keys-sdk new file mode 160000 index 0000000..f0687c1 --- /dev/null +++ b/pico-keys-sdk @@ -0,0 +1 @@ +Subproject commit f0687c1ef392c2bcb293ea554f1dd8b784484922 diff --git a/src/fido/cbor.c b/src/fido/cbor.c index 394c29e..ba95179 100644 --- a/src/fido/cbor.c +++ b/src/fido/cbor.c @@ -25,6 +25,7 @@ #include "apdu.h" #include "management.h" #include "ctap2_cbor.h" +#include "version.h" const bool _btrue = true, _bfalse = false; @@ -40,6 +41,8 @@ int cbor_config(const uint8_t *data, size_t len); int cbor_vendor(const uint8_t *data, size_t len); int cbor_large_blobs(const uint8_t *data, size_t len); +extern int cmd_read_config(); + const uint8_t aaguid[16] = { 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2") @@ -91,6 +94,12 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) { else if (cmd == CTAP_VENDOR_CBOR) { return cbor_vendor(data, len); } + else if (cmd == 0xC2) { + if (cmd_read_config() == 0x9000) { + res_APDU_size -= 1; + return 0; + } + } } return CTAP1_ERR_INVALID_CMD; } diff --git a/src/fido/cbor_client_pin.c b/src/fido/cbor_client_pin.c index f47c09e..c7c13d8 100644 --- a/src/fido/cbor_client_pin.c +++ b/src/fido/cbor_client_pin.c @@ -31,7 +31,7 @@ #include "files.h" #include "random.h" #include "crypto_utils.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" uint32_t usage_timer = 0, initial_usage_time_limit = 0; @@ -181,12 +181,12 @@ int resetPinUvAuthToken() { int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) { if (protocol == 1) { memcpy(out, in, in_len); - return aes_encrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len); + return aes_encrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len); } else if (protocol == 2) { random_gen(NULL, out, IV_SIZE); memcpy(out + IV_SIZE, in, in_len); - return aes_encrypt(key + 32, out, 32 * 8, HSM_AES_MODE_CBC, out + IV_SIZE, in_len); + return aes_encrypt(key + 32, out, 32 * 8, PICO_KEYS_AES_MODE_CBC, out + IV_SIZE, in_len); } return -1; @@ -195,11 +195,11 @@ int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_l int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) { if (protocol == 1) { memcpy(out, in, in_len); - return aes_decrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len); + return aes_decrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len); } else if (protocol == 2) { memcpy(out, in + IV_SIZE, in_len); - return aes_decrypt(key + 32, in, 32 * 8, HSM_AES_MODE_CBC, out, in_len - IV_SIZE); + return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE); } return -1; diff --git a/src/fido/cbor_config.c b/src/fido/cbor_config.c index 88562d8..4026cc7 100644 --- a/src/fido/cbor_config.c +++ b/src/fido/cbor_config.c @@ -22,7 +22,7 @@ #include "files.h" #include "apdu.h" #include "credential.h" -#include "hsm.h" +#include "pico_keys.h" #include "random.h" #include "mbedtls/ecdh.h" #include "mbedtls/chachapoly.h" @@ -64,7 +64,7 @@ int cbor_config(const uint8_t *data, size_t len) { raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1); CBOR_PARSE_MAP_START(_f1, 2) { - if (subcommand == 0xff) { + if (subcommand == 0x7f) { CBOR_FIELD_GET_UINT(subpara, 2); if (subpara == 0x01) { CBOR_FIELD_GET_UINT(vendorCommandId, 2); @@ -134,7 +134,7 @@ int cbor_config(const uint8_t *data, size_t len) { CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID); } - if (subcommand == 0xff) { + if (subcommand == 0x7f) { if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) { if (!file_has_data(ef_keydev_enc)) { CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED); diff --git a/src/fido/cbor_cred_mgmt.c b/src/fido/cbor_cred_mgmt.c index 7f5bf3f..68e95a8 100644 --- a/src/fido/cbor_cred_mgmt.c +++ b/src/fido/cbor_cred_mgmt.c @@ -22,7 +22,7 @@ #include "files.h" #include "apdu.h" #include "credential.h" -#include "hsm.h" +#include "pico_keys.h" uint8_t rp_counter = 1; uint8_t rp_total = 0; diff --git a/src/fido/cbor_get_assertion.c b/src/fido/cbor_get_assertion.c index c3f51ee..5d1b067 100644 --- a/src/fido/cbor_get_assertion.c +++ b/src/fido/cbor_get_assertion.c @@ -24,7 +24,7 @@ #include "fido.h" #include "files.h" #include "crypto_utils.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "cbor_make_credential.h" #include "credential.h" diff --git a/src/fido/cbor_large_blobs.c b/src/fido/cbor_large_blobs.c index 4948457..c3ebd70 100644 --- a/src/fido/cbor_large_blobs.c +++ b/src/fido/cbor_large_blobs.c @@ -21,7 +21,7 @@ #include "hid/ctap_hid.h" #include "files.h" #include "apdu.h" -#include "hsm.h" +#include "pico_keys.h" #include "mbedtls/sha256.h" static uint64_t expectedLength = 0, expectedNextOffset = 0; diff --git a/src/fido/cbor_make_credential.c b/src/fido/cbor_make_credential.c index 37b7b45..d35da3c 100644 --- a/src/fido/cbor_make_credential.c +++ b/src/fido/cbor_make_credential.c @@ -25,7 +25,7 @@ #include "credential.h" #include "mbedtls/sha256.h" #include "random.h" -#include "hsm.h" +#include "pico_keys.h" int cbor_make_credential(const uint8_t *data, size_t len) { CborParser parser; diff --git a/src/fido/cbor_vendor.c b/src/fido/cbor_vendor.c index f76de3c..afe939b 100644 --- a/src/fido/cbor_vendor.c +++ b/src/fido/cbor_vendor.c @@ -21,7 +21,7 @@ #include "hid/ctap_hid.h" #include "files.h" #include "apdu.h" -#include "hsm.h" +#include "pico_keys.h" #include "random.h" #include "mbedtls/ecdh.h" #include "mbedtls/chachapoly.h" diff --git a/src/fido/cmd_authenticate.c b/src/fido/cmd_authenticate.c index 3bea9e6..6f458d6 100644 --- a/src/fido/cmd_authenticate.c +++ b/src/fido/cmd_authenticate.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "ctap.h" #include "random.h" diff --git a/src/fido/cmd_register.c b/src/fido/cmd_register.c index 837cb90..7962719 100644 --- a/src/fido/cmd_register.c +++ b/src/fido/cmd_register.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "ctap.h" #include "random.h" @@ -32,18 +32,17 @@ const uint8_t u2f_aid[] = { int u2f_unload(); int u2f_process_apdu(); -app_t *u2f_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, u2f_aid + 1, MIN(aid_len, u2f_aid[0])) && cap_supported(CAP_U2F)) { - a->aid = u2f_aid; +int u2f_select(app_t *a) { + if (cap_supported(CAP_U2F)) { a->process_apdu = u2f_process_apdu; a->unload = u2f_unload; - return a; + return CCID_OK; } - return NULL; + return CCID_ERR_FILE_NOT_FOUND; } void __attribute__((constructor)) u2f_ctor() { - register_app(u2f_select); + register_app(u2f_select, u2f_aid); } int u2f_unload() { diff --git a/src/fido/cmd_version.c b/src/fido/cmd_version.c index 6a3b132..7b0ff74 100644 --- a/src/fido/cmd_version.c +++ b/src/fido/cmd_version.c @@ -16,7 +16,7 @@ */ #include "apdu.h" -#include "hsm.h" +#include "pico_keys.h" int cmd_version() { memcpy(res_APDU, "U2F_V2", strlen("U2F_V2")); diff --git a/src/fido/credential.c b/src/fido/credential.c index 7e766c6..b43388b 100644 --- a/src/fido/credential.c +++ b/src/fido/credential.c @@ -26,7 +26,7 @@ #include "ctap.h" #include "random.h" #include "files.h" -#include "hsm.h" +#include "pico_keys.h" int credential_derive_chacha_key(uint8_t *outk); diff --git a/src/fido/fido.c b/src/fido/fido.c index dfedaee..2ce4650 100644 --- a/src/fido/fido.c +++ b/src/fido/fido.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "ctap.h" #include "files.h" @@ -33,6 +33,7 @@ #include #include "management.h" #include "ctap_hid.h" +#include "version.h" int fido_process_apdu(); int fido_unload(); @@ -42,7 +43,7 @@ pinUvAuthToken_t paut = { 0 }; uint8_t keydev_dec[32]; bool has_keydev_dec = false; -const uint8_t fido_aid[] = { +const uint8_t _fido_aid[] = { 8, 0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01 }; @@ -53,21 +54,44 @@ const uint8_t atr_fido[] = { 0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40 }; -app_t *fido_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, fido_aid + 1, MIN(aid_len, fido_aid[0])) && cap_supported(CAP_FIDO2)) { - a->aid = fido_aid; +uint8_t fido_get_version_major() { + return PICO_FIDO_VERSION_MAJOR; +} +uint8_t fido_get_version_minor() { + return PICO_FIDO_VERSION_MINOR; +} + +int fido_select(app_t *a) { + if (cap_supported(CAP_FIDO2)) { a->process_apdu = fido_process_apdu; a->unload = fido_unload; - return a; + return CCID_OK; } - return NULL; + return CCID_ERR_FILE_NOT_FOUND; } +extern uint8_t (*get_version_major)(); +extern uint8_t (*get_version_minor)(); +extern const uint8_t *fido_aid; +extern void (*init_fido_cb)(); +extern void (*cbor_thread_func)(); +extern int (*cbor_process_cb)(uint8_t, const uint8_t *, size_t); +extern void cbor_thread(); +extern int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len); + void __attribute__((constructor)) fido_ctor() { #if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION) ccid_atr = atr_fido; #endif - register_app(fido_select); + get_version_major = fido_get_version_major; + get_version_minor = fido_get_version_minor; + fido_aid = _fido_aid; + init_fido_cb = init_fido; +#ifndef ENABLE_EMULATION + cbor_thread_func = cbor_thread; +#endif + cbor_process_cb = cbor_process; + register_app(fido_select, fido_aid); } int fido_unload() { @@ -389,8 +413,10 @@ void scan_all() { scan_files(); } +extern void init_otp(); void init_fido() { scan_all(); + init_otp(); } bool wait_button_pressed() { diff --git a/src/fido/management.c b/src/fido/management.c index f2f8df7..83cbd43 100644 --- a/src/fido/management.c +++ b/src/fido/management.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "version.h" #include "files.h" @@ -31,22 +31,20 @@ const uint8_t man_aid[] = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; extern void scan_all(); -app_t *man_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, man_aid + 1, MIN(aid_len, man_aid[0]))) { - a->aid = man_aid; - a->process_apdu = man_process_apdu; - a->unload = man_unload; - sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR); - res_APDU_size = strlen((char *) res_APDU); - apdu.ne = res_APDU_size; - scan_all(); - return a; - } - return NULL; +extern void init_otp(); +int man_select(app_t *a) { + a->process_apdu = man_process_apdu; + a->unload = man_unload; + sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR); + res_APDU_size = strlen((char *) res_APDU); + apdu.ne = res_APDU_size; + scan_all(); + init_otp(); + return CCID_OK; } void __attribute__((constructor)) man_ctor() { - register_app(man_select); + register_app(man_select, man_aid); } int man_unload() { diff --git a/src/fido/oath.c b/src/fido/oath.c index e3d25bc..cfeb390 100644 --- a/src/fido/oath.c +++ b/src/fido/oath.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "files.h" #include "random.h" @@ -68,9 +68,8 @@ const uint8_t oath_aid[] = { 0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01 }; -app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, oath_aid + 1, MIN(aid_len, oath_aid[0])) && cap_supported(CAP_OATH)) { - a->aid = oath_aid; +int oath_select(app_t *a) { + if (cap_supported(CAP_OATH)) { a->process_apdu = oath_process_apdu; a->unload = oath_unload; res_APDU_size = 0; @@ -82,10 +81,10 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { res_APDU[res_APDU_size++] = TAG_NAME; res_APDU[res_APDU_size++] = 8; #ifndef ENABLE_EMULATION - pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size)); - res_APDU_size += 8; + pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size)); + res_APDU_size += 8; #else - memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8; + memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8; #endif if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) { random_gen(NULL, challenge, sizeof(challenge)); @@ -105,13 +104,13 @@ app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { res_APDU[res_APDU_size++] = 1; res_APDU[res_APDU_size++] = ALG_HMAC_SHA1; apdu.ne = res_APDU_size; - return a; + return CCID_OK; } - return NULL; + return CCID_ERR_FILE_NOT_FOUND; } void __attribute__((constructor)) oath_ctor() { - register_app(oath_select); + register_app(oath_select, oath_aid); } int oath_unload() { @@ -455,6 +454,7 @@ int cmd_calculate_all() { if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) { return SW_INCORRECT_PARAMS(); } + res_APDU_size = 0; for (int i = 0; i < MAX_OATH_CRED; i++) { file_t *ef = search_dynamic_file(EF_OATH_CRED + i); if (file_has_data(ef)) { diff --git a/src/fido/otp.c b/src/fido/otp.c index a38c287..e37e779 100644 --- a/src/fido/otp.c +++ b/src/fido/otp.c @@ -16,7 +16,7 @@ */ #include "fido.h" -#include "hsm.h" +#include "pico_keys.h" #include "apdu.h" #include "files.h" #include "random.h" @@ -111,14 +111,20 @@ uint16_t otp_status(); int otp_process_apdu(); int otp_unload(); +#ifndef ENABLE_EMULATION +extern int (*hid_set_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t); +extern uint16_t (*hid_get_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t); +int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t); +uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t); +#endif + const uint8_t otp_aid[] = { 7, 0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; -app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { - if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0])) && cap_supported(CAP_OTP)) { - a->aid = otp_aid; +int otp_select(app_t *a) { + if (cap_supported(CAP_OTP)) { a->process_apdu = otp_process_apdu; a->unload = otp_unload; if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) || @@ -132,9 +138,9 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) { memmove(res_APDU, res_APDU + 1, 6); res_APDU_size = 6; apdu.ne = res_APDU_size; - return a; + return CCID_OK; } - return NULL; + return CCID_ERR_FILE_NOT_FOUND; } uint8_t modhex_tab[] = @@ -176,6 +182,22 @@ extern int calculate_oath(uint8_t truncate, size_t key_len, const uint8_t *chal, size_t chal_len); + +uint16_t calculate_crc(const uint8_t *data, size_t data_len) { + uint16_t crc = 0xFFFF; + for (size_t idx = 0; idx < data_len; idx++) { + crc ^= data[idx]; + for (uint8_t i = 0; i < 8; i++) { + uint16_t j = crc & 0x1; + crc >>= 1; + if (j == 1) { + crc ^= 0x8408; + } + } + } + return crc & 0xFFFF; +} + #ifndef ENABLE_EMULATION static uint8_t session_counter[2] = { 0 }; #endif @@ -244,10 +266,11 @@ int otp_button_pressed(uint8_t slot) { } } else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) { - if (otp_config->cfg_flags & SHORT_TICKET) { - otp_config->fixed_size /= 2; + uint8_t fixed_size = FIXED_SIZE + UID_SIZE + KEY_SIZE; + if (otp_config->cfg_flags & SHORT_TICKET) { // Not clear which is the purpose of SHORT_TICKET + //fixed_size /= 2; } - add_keyboard_buffer(otp_config->fixed_data, otp_config->fixed_size, false); + add_keyboard_buffer(otp_config->fixed_data, fixed_size, false); if (otp_config->tkt_flags & APPEND_CR) { append_keyboard_buffer((const uint8_t *) "\x28", 1); } @@ -308,8 +331,12 @@ int otp_button_pressed(uint8_t slot) { } void __attribute__((constructor)) otp_ctor() { - register_app(otp_select); + register_app(otp_select, otp_aid); button_pressed_cb = otp_button_pressed; +#ifndef ENABLE_EMULATION + hid_set_report_cb = otp_hid_set_report_cb; + hid_get_report_cb = otp_hid_get_report_cb; +#endif } int otp_unload() { @@ -489,3 +516,110 @@ int otp_process_apdu() { } return SW_INS_NOT_SUPPORTED(); } + +#ifndef ENABLE_EMULATION + +uint8_t otp_frame_rx[70] = {0}; +uint8_t otp_frame_tx[70] = {0}; +uint8_t otp_exp_seq = 0, otp_curr_seq = 0; +uint8_t otp_header[4] = {0}; + +extern uint16_t *get_send_buffer_size(uint8_t itf); + +int otp_send_frame(uint8_t *frame, size_t frame_len) { + uint16_t crc = calculate_crc(frame, frame_len); + frame[frame_len] = ~crc & 0xff; + frame[frame_len + 1] = ~crc >> 8; + frame_len += 2; + *get_send_buffer_size(ITF_KEYBOARD) = frame_len; + otp_exp_seq = (frame_len / 7); + if (frame_len % 7) { + otp_exp_seq++; + } + otp_curr_seq = 0; + return 0; +} + +int otp_hid_set_report_cb(uint8_t itf, + uint8_t report_id, + hid_report_type_t report_type, + uint8_t const *buffer, + uint16_t bufsize) +{ + if (report_type == 3) { + DEBUG_PAYLOAD(buffer, bufsize); + if (itf == ITF_KEYBOARD && buffer[7] == 0xFF) { // reset + *get_send_buffer_size(ITF_KEYBOARD) = 0; + otp_curr_seq = otp_exp_seq = 0; + memset(otp_frame_tx, 0, sizeof(otp_frame_tx)); + } + else if (buffer[7] & 0x80) { // a frame + uint8_t rseq = buffer[7] & 0x1F; + if (rseq < 10) { + if (rseq == 0) { + memset(otp_frame_rx, 0, sizeof(otp_frame_rx)); + } + memcpy(otp_frame_rx + rseq * 7, buffer, 7); + if (rseq == 9) { + DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx)); + uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = (otp_frame_rx[66] << 8 | otp_frame_rx[65]); + uint8_t slot_id = otp_frame_rx[64]; + if (residual_crc == rcrc) { + apdu.data = otp_frame_rx; + apdu.nc = 64; + apdu.rdata = otp_frame_tx; + apdu.header[0] = 0; + apdu.header[1] = 0x01; + apdu.header[2] = slot_id; + apdu.header[3] = 0; + int ret = otp_process_apdu(); + if (ret == 0x9000 && res_APDU_size > 0) { + otp_send_frame(apdu.rdata, apdu.rlen); + } + } + else { + printf("[OTP] Bad CRC!\n"); + } + } + } + } + return 1; + } + return 0; +} + +uint16_t otp_hid_get_report_cb(uint8_t itf, + uint8_t report_id, + hid_report_type_t report_type, + uint8_t *buffer, + uint16_t reqlen) { + // TODO not Implemented + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + uint16_t send_buffer_size = *get_send_buffer_size(ITF_KEYBOARD); + if (send_buffer_size > 0) { + uint8_t seq = otp_curr_seq++; + memset(buffer, 0, 8); + memcpy(buffer, otp_frame_tx + 7 * seq, MIN(7, send_buffer_size)); + buffer[7] = 0x40 | seq; + DEBUG_DATA(buffer, 8); + *get_send_buffer_size(ITF_KEYBOARD) -= MIN(7, send_buffer_size); + } + else if (otp_curr_seq == otp_exp_seq && otp_exp_seq > 0) { + memset(buffer, 0, 7); + buffer[7] = 0x40; + DEBUG_DATA(buffer,8); + otp_curr_seq = otp_exp_seq = 0; + } + else { + otp_status(); + memcpy(buffer, res_APDU, 7); + } + + return reqlen; +} + +#endif diff --git a/src/fido/version.h b/src/fido/version.h index f226d38..789f039 100644 --- a/src/fido/version.h +++ b/src/fido/version.h @@ -18,7 +18,7 @@ #ifndef __VERSION_H_ #define __VERSION_H_ -#define PICO_FIDO_VERSION 0x0507 +#define PICO_FIDO_VERSION 0x0508 #define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff) #define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff) diff --git a/tools/pico-fido-tool.py b/tools/pico-fido-tool.py index 9971387..89d1615 100644 --- a/tools/pico-fido-tool.py +++ b/tools/pico-fido-tool.py @@ -23,7 +23,6 @@ import sys import argparse import platform from binascii import hexlify -from words import words from threading import Event from typing import Mapping, Any, Optional, Callable import struct @@ -33,7 +32,7 @@ from enum import IntEnum, unique try: from fido2.ctap2.config import Config - from fido2.ctap2 import Ctap2 + from fido2.ctap2 import Ctap2, ClientPin, PinProtocolV2 from fido2.hid import CtapHidDevice, CTAPHID from fido2.utils import bytes2int, int2bytes from fido2 import cbor @@ -58,14 +57,6 @@ except: from enum import IntEnum from binascii import hexlify -if (platform.system() == 'Windows' or platform.system() == 'Linux'): - from secure_key import windows as skey -elif (platform.system() == 'Darwin'): - from secure_key import macos as skey -else: - print('ERROR: platform not supported') - sys.exit(-1) - def get_pki_data(url, data=None, method='GET'): user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; ' 'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7' @@ -90,6 +81,7 @@ class VendorConfig(Config): class CMD(IntEnum): CONFIG_AUT_ENABLE = 0x03e43f56b34285e2 CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9 + CONFIG_VENDOR_PROTOTYPE = 0x7f class RESP(IntEnum): KEY_AGREEMENT = 0x01 @@ -99,7 +91,7 @@ class VendorConfig(Config): def enable_device_aut(self, ct): self._call( - Config.CMD.VENDOR_PROTOTYPE, + VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE, { VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_ENABLE, VendorConfig.PARAM.VENDOR_AUT_CT: ct @@ -108,7 +100,7 @@ class VendorConfig(Config): def disable_device_aut(self): self._call( - Config.CMD.VENDOR_PROTOTYPE, + VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE, { VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_DISABLE }, @@ -230,7 +222,7 @@ class Vendor: self.__key_enc = None self.__iv = None - self.vcfg = VendorConfig(ctap) + self.vcfg = VendorConfig(ctap, pin_uv_protocol=pin_uv_protocol, pin_uv_token=pin_uv_token) def _call(self, cmd, sub_cmd, params=None): if params: @@ -252,6 +244,14 @@ class Vendor: return self.ctap.vendor(cmd, sub_cmd, params, pin_uv_protocol, pin_uv_param) def backup_save(self, filename): + if (platform.system() == 'Windows' or platform.system() == 'Linux'): + from secure_key import windows as skey + elif (platform.system() == 'Darwin'): + from secure_key import macos as skey + else: + print('ERROR: platform not supported') + sys.exit(-1) + from words import words ret = self._call( Vendor.CMD.VENDOR_BACKUP, Vendor.SUBCMD.ENABLE, @@ -270,6 +270,14 @@ class Vendor: print(f'{(c+1):02d} - {words[coef]}') def backup_load(self, filename): + if (platform.system() == 'Windows' or platform.system() == 'Linux'): + from secure_key import windows as skey + elif (platform.system() == 'Darwin'): + from secure_key import macos as skey + else: + print('ERROR: platform not supported') + sys.exit(-1) + from words import words d = 0 if (d == 0): for c in range(24): @@ -349,6 +357,13 @@ class Vendor: ) def _get_key_device(self): + if (platform.system() == 'Windows' or platform.system() == 'Linux'): + from secure_key import windows as skey + elif (platform.system() == 'Darwin'): + from secure_key import macos as skey + else: + print('ERROR: platform not supported') + sys.exit(-1) return skey.get_secure_key() def get_skey(self): @@ -381,6 +396,7 @@ class Vendor: def parse_args(): parser = argparse.ArgumentParser() subparser = parser.add_subparsers(title="commands", dest="command") + parser.add_argument('-p','--pin', help='Specify the PIN of the device.', required=True) parser_secure = subparser.add_parser('secure', help='Manages security of Pico Fido.') parser_secure.add_argument('subcommand', choices=['enable', 'disable', 'unlock'], help='Enables, disables or unlocks the security.') @@ -426,15 +442,17 @@ def attestation(vdr, args): vdr.upload_ea(cert.public_bytes(Encoding.DER)) def main(args): - print('Pico Fido Tool v1.4') + print('Pico Fido Tool v1.6') print('Author: Pol Henarejos') print('Report bugs to https://github.com/polhenarejos/pico-fido/issues') print('') print('') dev = next(CtapHidDevice.list_devices(), None) - - vdr = Vendor(Ctap2Vendor(dev)) + ctap = Ctap2Vendor(dev) + client_pin = ClientPin(ctap) + token = client_pin.get_pin_token(args.pin, permissions=ClientPin.PERMISSION.AUTHENTICATOR_CFG) + vdr = Vendor(ctap, pin_uv_protocol=PinProtocolV2(), pin_uv_token=token) if (args.command == 'secure'): secure(vdr, args) diff --git a/tools/secure_key/macos.py b/tools/secure_key/macos.py index 381ee21..1ccc1a4 100644 --- a/tools/secure_key/macos.py +++ b/tools/secure_key/macos.py @@ -51,7 +51,9 @@ def get_secure_key(): try: backend = get_backend(False) key = backend.get_password(DOMAIN, USERNAME)[0] - except keyring.errors.KeyringError: + if (key is None): + raise TypeError + except (keyring.errors.KeyringError, TypeError): try: key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault except keyring.errors.PasswordSetError: diff --git a/tools/secure_key/windows.py b/tools/secure_key/windows.py new file mode 100644 index 0000000..844190a --- /dev/null +++ b/tools/secure_key/windows.py @@ -0,0 +1,44 @@ +import sys + +DOMAIN = "PicoKeys.com" +USERNAME = "Pico-Fido" + +try: + import keyring +except: + print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyring`') + sys.exit(-1) + +try: + from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key + from cryptography.hazmat.primitives.asymmetric import ec +except: + print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`') + sys.exit(-1) + + + +def generate_secure_key(): + pkey = ec.generate_private_key(ec.SECP256R1()) + set_secure_key(pkey) + return keyring.get_password(DOMAIN, USERNAME) + +def get_d(key): + return load_pem_private_key(key, password=None).private_numbers().private_value.to_bytes(32, 'big') + +def set_secure_key(pk): + try: + keyring.delete_password(DOMAIN, USERNAME) + except: + pass + keyring.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()).decode()) + +def get_secure_key(): + key = None + try: + key = keyring.get_password(DOMAIN, USERNAME) + if (key is None): + raise TypeError + except (keyring.errors.KeyringError, TypeError): + key = generate_secure_key() + return get_d(key.encode())