From 78a1be9a7d58f369729fc4f86c9305775378b133 Mon Sep 17 00:00:00 2001 From: lihuanhuan Date: Mon, 30 Dec 2024 10:56:23 +0800 Subject: [PATCH] Support FIDO2 credential storage && write private key in factory mode. (#221) * Add command to write private key in factory mode. * Support FIDO2 credential storage. --- common/protob/messages-management.proto | 8 ++ common/protob/messages.proto | 1 + core/embed/bootloader/main.c | 3 + core/embed/bootloader/messages.c | 27 +++++ core/embed/bootloader/messages.h | 2 + core/embed/bootloader/protob/messages.options | 1 + core/embed/bootloader/protob/messages.pb.c | 3 + core/embed/bootloader/protob/messages.pb.h | 17 +++ core/embed/bootloader/protob/messages.proto | 9 ++ .../extmod/modtrezorconfig/modtrezorconfig.c | 41 +++++++ .../modtrezorcrypto-nist256p1.h | 46 +++++--- .../modtrezorcrypto-se-thd89.h | 18 ++++ core/embed/trezorhal/se_thd89.c | 101 ++++++++++++++++++ core/embed/trezorhal/se_thd89.h | 23 ++++ .../generated/trezorcrypto/nist256p1.pyi | 3 +- .../mocks/generated/trezorcrypto/se_thd89.pyi | 8 ++ core/src/all_modules.py | 2 + core/src/apps/webauthn/fido2.py | 12 +-- core/src/apps/webauthn/fido_seed.py | 18 ++++ .../src/apps/webauthn/resident_credentials.py | 5 + core/src/apps/workflow_handlers.py | 14 +-- core/src/storage/resident_credentials.py | 20 ++-- core/src/trezor/enums/MessageType.py | 1 + core/src/trezor/enums/__init__.py | 1 + core/src/trezor/messages.py | 14 +++ core/src/trezor/ui/layouts/lvgl/webauthn.py | 2 +- python/src/trezorlib/messages.py | 15 +++ 27 files changed, 376 insertions(+), 39 deletions(-) create mode 100644 core/src/apps/webauthn/fido_seed.py diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 690aa4112..e05b8f878 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -643,6 +643,14 @@ message DeviceInfo { optional string pre_firmware = 6; } +/** + * Request: Read SE private key + * @end + */ +message WriteSEPrivateKey { + required bytes private_key = 1; +} + /** * Request: Read SE public key * @end diff --git a/common/protob/messages.proto b/common/protob/messages.proto index 62f6e20ac..64c0ca5f3 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -593,4 +593,5 @@ enum MessageType { MessageType_FileInfoList = 10024 [(wire_out) = true]; MessageType_OnekeyGetFeatures = 10025 [(wire_in) = true]; MessageType_OnekeyFeatures = 10026 [(wire_out) = true]; + MessageType_WriteSEPrivateKey = 10027 [(wire_in) = true, (wire_bootloader) = true]; } diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index e62bc8044..3cb0c7ea9 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -527,6 +527,9 @@ secbool bootloader_usb_loop_factory(const vendor_header* const vhdr, case MSG_NAME_TO_ID(GetDeviceInfo): // GetDeviceInfo process_msg_GetDeviceInfo(USB_IFACE_NUM, msg_size, buf); break; + case MSG_NAME_TO_ID(WriteSEPrivateKey): // WriteSEPrivateKey + process_msg_WriteSEPrivateKey(USB_IFACE_NUM, msg_size, buf); + break; case MSG_NAME_TO_ID(ReadSEPublicKey): // ReadSEPublicKey process_msg_ReadSEPublicKey(USB_IFACE_NUM, msg_size, buf); break; diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 2ff32a070..59d0a9676 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -339,6 +339,7 @@ static void send_msg_features(uint8_t iface_num, init_state |= device_serial_set() ? 1 : 0; init_state |= se_has_cerrificate() ? (1 << 2) : 0; MSG_SEND_ASSIGN_VALUE(initstates, init_state); + MSG_SEND_ASSIGN_VALUE(onekey_device_type, OneKeyDeviceType_PRO); } else { MSG_SEND_ASSIGN_STRING(vendor, "onekey.so"); @@ -671,6 +672,25 @@ void process_msg_GetDeviceInfo(uint8_t iface_num, uint32_t msg_size, MSG_SEND(DeviceInfo); } +void process_msg_WriteSEPrivateKey(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf) { + MSG_RECV_INIT(WriteSEPrivateKey); + MSG_RECV(WriteSEPrivateKey); + + if (msg_recv.private_key.size != 32) { + send_failure(iface_num, FailureType_Failure_ProcessError, + "Private key size invalid"); + return; + } + + if (se_set_private_key_extern(msg_recv.private_key.bytes)) { + send_success(iface_num, "Write private key success"); + } else { + send_failure(iface_num, FailureType_Failure_ProcessError, + "Write private key failed"); + } +} + void process_msg_ReadSEPublicKey(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { uint8_t pubkey[64] = {0}; @@ -728,6 +748,13 @@ void process_msg_SESignMessage(uint8_t iface_num, uint32_t msg_size, MSG_SEND_INIT(SEMessageSignature); + if (se_sign_message_with_write_key((uint8_t *)msg_recv.message.bytes, + msg_recv.message.size, sign)) { + MSG_SEND_ASSIGN_REQUIRED_BYTES(signature, sign, 64); + MSG_SEND(SEMessageSignature); + return; + } + if (se_sign_message((uint8_t *)msg_recv.message.bytes, msg_recv.message.size, sign)) { MSG_SEND_ASSIGN_REQUIRED_BYTES(signature, sign, 64); diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index 24f094593..c0dc4bf7f 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -68,6 +68,8 @@ void process_msg_DeviceInfoSettings(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); void process_msg_GetDeviceInfo(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); +void process_msg_WriteSEPrivateKey(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf); void process_msg_ReadSEPublicKey(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); void process_msg_WriteSEPublicCert(uint8_t iface_num, uint32_t msg_size, diff --git a/core/embed/bootloader/protob/messages.options b/core/embed/bootloader/protob/messages.options index 4122ab475..1cfe410fc 100644 --- a/core/embed/bootloader/protob/messages.options +++ b/core/embed/bootloader/protob/messages.options @@ -107,6 +107,7 @@ DeviceInfo.NFT_voucher max_size:32 DeviceInfo.cpu_info max_size:16 DeviceInfo.pre_firmware max_size:16 +WriteSEPrivateKey.private_key max_size:32 SEPublicKey.public_key max_size:64 WriteSEPublicCert.public_cert max_size:416 SEPublicCert.public_cert max_size:416 diff --git a/core/embed/bootloader/protob/messages.pb.c b/core/embed/bootloader/protob/messages.pb.c index 3dc725389..024fd529d 100644 --- a/core/embed/bootloader/protob/messages.pb.c +++ b/core/embed/bootloader/protob/messages.pb.c @@ -99,6 +99,9 @@ PB_BIND(GetDeviceInfo, GetDeviceInfo, AUTO) PB_BIND(DeviceInfo, DeviceInfo, AUTO) +PB_BIND(WriteSEPrivateKey, WriteSEPrivateKey, AUTO) + + PB_BIND(ReadSEPublicKey, ReadSEPublicKey, AUTO) diff --git a/core/embed/bootloader/protob/messages.pb.h b/core/embed/bootloader/protob/messages.pb.h index 24295a20c..788d32cbf 100644 --- a/core/embed/bootloader/protob/messages.pb.h +++ b/core/embed/bootloader/protob/messages.pb.h @@ -36,6 +36,7 @@ typedef enum _MessageType { MessageType_MessageType_SEMessageSignature = 10013, MessageType_MessageType_OnekeyGetFeatures = 10025, MessageType_MessageType_OnekeyFeatures = 10026, + MessageType_MessageType_WriteSEPrivateKey = 10027, MessageType_MessageType_Reboot = 30000, MessageType_MessageType_FirmwareUpdateEmmc = 30001, MessageType_MessageType_EmmcFixPermission = 30100, @@ -487,6 +488,11 @@ typedef struct _Success { char message[256]; } Success; +typedef PB_BYTES_ARRAY_T(32) WriteSEPrivateKey_private_key_t; +typedef struct _WriteSEPrivateKey { + WriteSEPrivateKey_private_key_t private_key; +} WriteSEPrivateKey; + typedef PB_BYTES_ARRAY_T(416) WriteSEPublicCert_public_cert_t; typedef struct _WriteSEPublicCert { WriteSEPublicCert_public_cert_t public_cert; @@ -573,6 +579,7 @@ extern "C" { #define DeviceInfoSettings_init_default {false, "", false, "", false, ""} #define GetDeviceInfo_init_default {0} #define DeviceInfo_init_default {false, "", false, "", false, "", false, {0, {0}}, false, "", false, ""} +#define WriteSEPrivateKey_init_default {{0, {0}}} #define ReadSEPublicKey_init_default {0} #define SEPublicKey_init_default {{0, {0}}} #define WriteSEPublicCert_init_default {{0, {0}}} @@ -611,6 +618,7 @@ extern "C" { #define DeviceInfoSettings_init_zero {false, "", false, "", false, ""} #define GetDeviceInfo_init_zero {0} #define DeviceInfo_init_zero {false, "", false, "", false, "", false, {0, {0}}, false, "", false, ""} +#define WriteSEPrivateKey_init_zero {{0, {0}}} #define ReadSEPublicKey_init_zero {0} #define SEPublicKey_init_zero {{0, {0}}} #define WriteSEPublicCert_init_zero {{0, {0}}} @@ -769,6 +777,7 @@ extern "C" { #define SEPublicKey_public_key_tag 1 #define SESignMessage_message_tag 1 #define Success_message_tag 1 +#define WriteSEPrivateKey_private_key_tag 1 #define WriteSEPublicCert_public_cert_tag 1 #define EmmcFileRead_file_tag 1 #define EmmcFileRead_ui_percentage_tag 2 @@ -1062,6 +1071,11 @@ X(a, STATIC, OPTIONAL, STRING, pre_firmware, 6) #define DeviceInfo_CALLBACK NULL #define DeviceInfo_DEFAULT NULL +#define WriteSEPrivateKey_FIELDLIST(X, a) \ +X(a, STATIC, REQUIRED, BYTES, private_key, 1) +#define WriteSEPrivateKey_CALLBACK NULL +#define WriteSEPrivateKey_DEFAULT NULL + #define ReadSEPublicKey_FIELDLIST(X, a) \ #define ReadSEPublicKey_CALLBACK NULL @@ -1128,6 +1142,7 @@ extern const pb_msgdesc_t EmmcDirRemove_msg; extern const pb_msgdesc_t DeviceInfoSettings_msg; extern const pb_msgdesc_t GetDeviceInfo_msg; extern const pb_msgdesc_t DeviceInfo_msg; +extern const pb_msgdesc_t WriteSEPrivateKey_msg; extern const pb_msgdesc_t ReadSEPublicKey_msg; extern const pb_msgdesc_t SEPublicKey_msg; extern const pb_msgdesc_t WriteSEPublicCert_msg; @@ -1168,6 +1183,7 @@ extern const pb_msgdesc_t SEMessageSignature_msg; #define DeviceInfoSettings_fields &DeviceInfoSettings_msg #define GetDeviceInfo_fields &GetDeviceInfo_msg #define DeviceInfo_fields &DeviceInfo_msg +#define WriteSEPrivateKey_fields &WriteSEPrivateKey_msg #define ReadSEPublicKey_fields &ReadSEPublicKey_msg #define SEPublicKey_fields &SEPublicKey_msg #define WriteSEPublicCert_fields &WriteSEPublicCert_msg @@ -1214,6 +1230,7 @@ extern const pb_msgdesc_t SEMessageSignature_msg; #define SESignMessage_size 1027 #define Success_size 258 #define WipeDevice_size 0 +#define WriteSEPrivateKey_size 34 #define WriteSEPublicCert_size 419 #ifdef __cplusplus diff --git a/core/embed/bootloader/protob/messages.proto b/core/embed/bootloader/protob/messages.proto index 5758102d3..b94ccf60f 100644 --- a/core/embed/bootloader/protob/messages.proto +++ b/core/embed/bootloader/protob/messages.proto @@ -31,6 +31,7 @@ enum MessageType { MessageType_SEMessageSignature = 10013; MessageType_OnekeyGetFeatures = 10025; MessageType_OnekeyFeatures = 10026; + MessageType_WriteSEPrivateKey = 10027; MessageType_Reboot = 30000; MessageType_FirmwareUpdateEmmc = 30001; @@ -525,6 +526,14 @@ message DeviceInfo { optional string pre_firmware = 6; } +/** + * Request: Read SE private key + * @end + */ +message WriteSEPrivateKey { + required bytes private_key = 1; +} + /** * Request: Read SE public key * @end diff --git a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c index 745206487..45ed28bd2 100644 --- a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -393,6 +393,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_get_val_len_obj, 2, /// value fails. /// """ STATIC mp_obj_t mod_trezorconfig_get(size_t n_args, const mp_obj_t *args) { + uint8_t app = trezor_obj_get_uint8(args[0]); + // webauthn resident credentials, FIDO2 + if (app == 4) { + uint32_t index = trezor_obj_get_uint(args[1]); + uint16_t len = sizeof(CTAP_credential_id_storage) - + FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN; + CTAP_credential_id_storage cred_id = {0}; + + if (!se_get_fido2_resident_credentials(index, cred_id.rp_id_hash, &len)) { + return mp_const_none; + } + return mp_obj_new_bytes(cred_id.rp_id_hash, len); + } + uint32_t key = trezor_obj_get_uint(args[1]); bool is_private = key & (1 << 31); @@ -435,6 +449,23 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_get_obj, 2, 3, /// Sets a value of given key for given app. /// """ STATIC mp_obj_t mod_trezorconfig_set(size_t n_args, const mp_obj_t *args) { + uint8_t app = trezor_obj_get_uint8(args[0]); + // webauthn resident credentials, FIDO2 + if (app == 4) { + uint32_t index = trezor_obj_get_uint(args[1]); + + mp_buffer_info_t cred_id; + mp_get_buffer_raise(args[2], &cred_id, MP_BUFFER_READ); + if (cred_id.len > sizeof(CTAP_credential_id_storage) - + FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN) { + mp_raise_msg(&mp_type_RuntimeError, "Credential ID too long"); + } + if (!se_set_fido2_resident_credentials(index, cred_id.buf, cred_id.len)) { + mp_raise_msg(&mp_type_RuntimeError, "Could not save value"); + } + return mp_const_none; + } + uint32_t key = trezor_obj_get_uint(args[1]); bool is_private = key & (1 << 31); secbool (*writer)(uint16_t, const void *, uint16_t) = @@ -468,6 +499,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_set_obj, 3, 4, /// Deletes the given key of the given app. /// """ STATIC mp_obj_t mod_trezorconfig_delete(size_t n_args, const mp_obj_t *args) { + uint8_t app = trezor_obj_get_uint8(args[0]); + // webauthn resident credentials, FIDO2 + if (app == 4) { + uint32_t index = trezor_obj_get_uint(args[1]); + if (!se_delete_fido2_resident_credentials(index)) { + mp_raise_msg(&mp_type_RuntimeError, "Could not delete value"); + } + return mp_const_true; + } + uint32_t key = trezor_obj_get_uint(args[1]); bool is_private = key & (1 << 31); secbool (*writer)(uint16_t, const void *, uint16_t) = diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h index 81cf3c302..d092dc237 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h @@ -203,16 +203,18 @@ STATIC mp_obj_t mod_trezorcrypto_nist256p1_verify_recover(mp_obj_t signature, STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nist256p1_verify_recover_obj, mod_trezorcrypto_nist256p1_verify_recover); -/// def multiply(secret_key: bytes, public_key: bytes) -> bytes: +/// def multiply(secret_key: bytes, public_key: bytes, inner_secret_key: bool = +/// False) -> bytes: /// """ /// Multiplies point defined by public_key with scalar defined by /// secret_key. Useful for ECDH. /// """ -STATIC mp_obj_t mod_trezorcrypto_nist256p1_multiply(mp_obj_t secret_key, - mp_obj_t public_key) { +STATIC mp_obj_t mod_trezorcrypto_nist256p1_multiply(size_t n_args, + const mp_obj_t *args) { mp_buffer_info_t sk = {0}, pk = {0}; - mp_get_buffer_raise(secret_key, &sk, MP_BUFFER_READ); - mp_get_buffer_raise(public_key, &pk, MP_BUFFER_READ); + mp_get_buffer_raise(args[0], &sk, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &pk, MP_BUFFER_READ); + bool inner_secret_key = (n_args == 3 && args[2] == mp_const_true); if (sk.len != 32) { mp_raise_ValueError("Invalid length of secret key"); } @@ -222,19 +224,28 @@ STATIC mp_obj_t mod_trezorcrypto_nist256p1_multiply(mp_obj_t secret_key, vstr_t out = {0}; vstr_init_len(&out, 65); #if USE_THD89 - uint8_t pubkey[65] = {0}; - if (pk.len == 33) { - if (!ecdsa_uncompress_pubkey(&nist256p1, pk.buf, pubkey)) { - mp_raise_ValueError("Invalid public key"); + if (inner_secret_key) { + if (0 != ecdh_multiply(&nist256p1, (const uint8_t *)sk.buf, + (const uint8_t *)pk.buf, (uint8_t *)out.buf)) { + vstr_clear(&out); + mp_raise_ValueError("Multiply failed"); } } else { - memcpy(pubkey, pk.buf, 65); - } - if (0 != se_get_shared_key(NIST256P1_NAME, (const uint8_t *)pubkey, - (uint8_t *)out.buf)) { - vstr_clear(&out); - mp_raise_ValueError("Multiply failed"); + uint8_t pubkey[65] = {0}; + if (pk.len == 33) { + if (!ecdsa_uncompress_pubkey(&nist256p1, pk.buf, pubkey)) { + mp_raise_ValueError("Invalid public key"); + } + } else { + memcpy(pubkey, pk.buf, 65); + } + if (0 != se_get_shared_key(NIST256P1_NAME, (const uint8_t *)pubkey, + (uint8_t *)out.buf)) { + vstr_clear(&out); + mp_raise_ValueError("Multiply failed"); + } } + #else if (0 != ecdh_multiply(&nist256p1, (const uint8_t *)sk.buf, (const uint8_t *)pk.buf, (uint8_t *)out.buf)) { @@ -244,8 +255,9 @@ STATIC mp_obj_t mod_trezorcrypto_nist256p1_multiply(mp_obj_t secret_key, #endif return mp_obj_new_str_from_vstr(&mp_type_bytes, &out); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_nist256p1_multiply_obj, - mod_trezorcrypto_nist256p1_multiply); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorcrypto_nist256p1_multiply_obj, 2, 3, + mod_trezorcrypto_nist256p1_multiply); STATIC const mp_rom_map_elem_t mod_trezorcrypto_nist256p1_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_nist256p1)}, diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h index efeb36dd9..b1814921f 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h @@ -926,6 +926,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1( mod_trezorcrypto_se_thd89_fido_att_sign_digest_obj, mod_trezorcrypto_se_thd89_fido_att_sign_digest); +/// def fido_delete_all_credentials() -> None: +/// """ +/// Delete all FIDO2 credentials. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_delete_all_credentials(void) { + se_delete_all_fido2_credentials(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0( + mod_trezorcrypto_se_thd89_fido_delete_all_credentials_obj, + mod_trezorcrypto_se_thd89_fido_delete_all_credentials); + +/// FIDO2_CRED_COUNT_MAX: int + STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_se_thd89)}, {MP_ROM_QSTR(MP_QSTR_check), @@ -994,6 +1008,10 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = { MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_sign_digest_obj)}, {MP_ROM_QSTR(MP_QSTR_fido_att_sign_digest), MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_att_sign_digest_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_delete_all_credentials), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_delete_all_credentials_obj)}, + {MP_ROM_QSTR(MP_QSTR_FIDO2_CRED_COUNT_MAX), + MP_ROM_INT(FIDO2_RESIDENT_CREDENTIALS_COUNT)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_se_thd89_globals, mod_trezorcrypto_se_thd89_globals_table); diff --git a/core/embed/trezorhal/se_thd89.c b/core/embed/trezorhal/se_thd89.c index e5e26f181..fa5f458df 100644 --- a/core/embed/trezorhal/se_thd89.c +++ b/core/embed/trezorhal/se_thd89.c @@ -1034,6 +1034,29 @@ secbool se_sign_message(uint8_t *msg, uint32_t msg_len, uint8_t *signature) { return thd89_transmit(sign, sizeof(sign), signature, &signature_len); } +secbool se_sign_message_with_write_key(uint8_t *msg, uint32_t msg_len, + uint8_t *signature) { + uint8_t sign[37] = {0x00, 0xF5, 0x00, 0x04, 0x20}; + uint16_t signature_len = 64; + + SHA256_CTX ctx = {0}; + uint8_t result[32] = {0}; + + sha256_Init(&ctx); + sha256_Update(&ctx, msg, msg_len); + sha256_Final(&ctx, result); + + memcpy(sign + 5, result, 32); + return thd89_transmit(sign, sizeof(sign), signature, &signature_len); +} + +secbool se_set_private_key_extern(uint8_t key[32]) { + uint8_t set_key[37] = {0x00, 0xF6, 0x00, 0x03, 0x20}; + uint16_t resp_len = 0; + memcpy(set_key + 5, key, 32); + return thd89_transmit(set_key, sizeof(set_key), NULL, &resp_len); +} + secbool se_set_session_key_ex(uint8_t addr, const uint8_t *session_key) { uint8_t cmd[32] = {0x00, 0xF6, 0x00, 0x02, 0x10}; uint16_t resp_len = 0; @@ -2296,3 +2319,81 @@ secbool se_fido_att_sign_digest(const uint8_t *hash, uint8_t *sig) { memcpy(sig, resp, resp_len); return sectrue; } + +secbool se_get_fido2_data(uint16_t offset, uint8_t *dest, uint16_t len) { + uint8_t cmd[4] = {0}; + uint16_t recv_len = len; + cmd[0] = (offset >> 8) & 0xFF; + cmd[1] = offset & 0xFF; + cmd[2] = (len >> 8) & 0xFF; + cmd[3] = len & 0xFF; + if (!se_transmit_mac(SE_INS_READ_DATA, 0x00, 0x03, cmd, sizeof(cmd), dest, + &recv_len)) { + return secfalse; + } + return sectrue; +} + +secbool se_set_fido2_data(uint16_t offset, const uint8_t *src, uint16_t len) { + uint8_t cmd[4] = {0}; + cmd[0] = (offset >> 8) & 0xFF; + cmd[1] = offset & 0xFF; + cmd[2] = (len >> 8) & 0xFF; + cmd[3] = len & 0xFF; + memcpy(APDU_DATA, cmd, 4); + memcpy(APDU_DATA + 4, src, len); + if (!se_transmit_mac(SE_INS_WRITE_DATA, 0x00, 0x03, APDU_DATA, 4 + len, NULL, + NULL)) { + return secfalse; + } + return sectrue; +} + +secbool se_get_fido2_resident_credentials(uint32_t index, uint8_t *dest, + uint16_t *dst_len) { + if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) return secfalse; + uint8_t buffer[FIDO2_RESIDENT_CREDENTIALS_SIZE]; + CTAP_credential_id_storage *cred_id = (CTAP_credential_id_storage *)buffer; + if (!se_get_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE, buffer, 6)) { + return secfalse; + } + if (memcmp(cred_id->credential_id_flag, FIDO2_RESIDENT_CREDENTIALS_FLAGS, + 4) != 0) { + return secfalse; + } + if (*dst_len < cred_id->credential_length) { + return secfalse; + } + if (!se_get_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE + 6, + buffer + 6, cred_id->credential_length)) { + return secfalse; + } + *dst_len = cred_id->credential_length; + memcpy(dest, cred_id->rp_id_hash, *dst_len); + return sectrue; +} + +secbool se_set_fido2_resident_credentials(uint32_t index, const uint8_t *src, + uint16_t len) { + if (index >= FIDO2_RESIDENT_CREDENTIALS_COUNT) return secfalse; + if (len > (FIDO2_RESIDENT_CREDENTIALS_SIZE - 6)) return secfalse; + CTAP_credential_id_storage cred_id = {0}; + memcpy(cred_id.credential_id_flag, FIDO2_RESIDENT_CREDENTIALS_FLAGS, 4); + cred_id.credential_length = len; + memcpy(cred_id.rp_id_hash, src, len); + return se_set_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE, + (uint8_t *)&cred_id, 6 + len); +} + +secbool se_delete_fido2_resident_credentials(uint32_t index) { + uint8_t buffer[FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN] = {0xff}; + return se_set_fido2_data(index * FIDO2_RESIDENT_CREDENTIALS_SIZE, buffer, + FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN); +} + +secbool se_delete_all_fido2_credentials(void) { + if (!se_transmit_mac(SE_INS_WRITE_DATA, 0x00, 0x04, NULL, 0, NULL, NULL)) { + return secfalse; + } + return sectrue; +} diff --git a/core/embed/trezorhal/se_thd89.h b/core/embed/trezorhal/se_thd89.h index 5cd6dd798..1a59d330d 100644 --- a/core/embed/trezorhal/se_thd89.h +++ b/core/embed/trezorhal/se_thd89.h @@ -12,6 +12,20 @@ #define MAX_AUTHORIZATION_LEN 128 +#define FIDO2_RESIDENT_CREDENTIALS_SIZE (512) +#define FIDO2_RESIDENT_CREDENTIALS_COUNT (60) +#define FIDO2_RESIDENT_CREDENTIALS_FLAGS "\x66\x69\x64\x6F" // "fido" +#define FIDO2_RESIDENT_CREDENTIALS_HEADER_LEN (6) +typedef struct { + uint8_t credential_id_flag[4]; + uint16_t credential_length; + uint8_t rp_id_hash[32]; + uint8_t credential_id[474]; +} __attribute__((packed)) CTAP_credential_id_storage; +_Static_assert(sizeof(CTAP_credential_id_storage) == + FIDO2_RESIDENT_CREDENTIALS_SIZE, + "CTAP_credential_id_storage size must be flash page size"); + typedef secbool (*UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress, const char *message); void se_set_ui_callback(UI_WAIT_CALLBACK callback); @@ -95,6 +109,9 @@ secbool se_write_certificate(const uint8_t *cert, uint16_t cert_len); secbool se_read_certificate(uint8_t *cert, uint16_t *cert_len); secbool se_has_cerrificate(void); secbool se_sign_message(uint8_t *msg, uint32_t msg_len, uint8_t *signature); +secbool se_sign_message_with_write_key(uint8_t *msg, uint32_t msg_len, + uint8_t *signature); +secbool se_set_private_key_extern(uint8_t key[32]); secbool se_set_session_key_ex(uint8_t addr, const uint8_t *session_key); secbool se_set_session_key(const uint8_t *session_key); @@ -186,4 +203,10 @@ secbool se_derive_fido_keys(HDNode *out, const char *curve, uint32_t *fingerprint); secbool se_fido_hdnode_sign_digest(const uint8_t *hash, uint8_t *sig); secbool se_fido_att_sign_digest(const uint8_t *hash, uint8_t *sig); +secbool se_get_fido2_resident_credentials(uint32_t index, uint8_t *dest, + uint16_t *dst_len); +secbool se_set_fido2_resident_credentials(uint32_t index, const uint8_t *src, + uint16_t len); +secbool se_delete_fido2_resident_credentials(uint32_t index); +secbool se_delete_all_fido2_credentials(void); #endif diff --git a/core/mocks/generated/trezorcrypto/nist256p1.pyi b/core/mocks/generated/trezorcrypto/nist256p1.pyi index 996a4c41f..e1c2c5bea 100644 --- a/core/mocks/generated/trezorcrypto/nist256p1.pyi +++ b/core/mocks/generated/trezorcrypto/nist256p1.pyi @@ -41,7 +41,8 @@ def verify_recover(signature: bytes, digest: bytes) -> bytes: # extmod/modtrezorcrypto/modtrezorcrypto-nist256p1.h -def multiply(secret_key: bytes, public_key: bytes) -> bytes: +def multiply(secret_key: bytes, public_key: bytes, inner_secret_key: bool = +False) -> bytes: """ Multiplies point defined by public_key with scalar defined by secret_key. Useful for ECDH. diff --git a/core/mocks/generated/trezorcrypto/se_thd89.pyi b/core/mocks/generated/trezorcrypto/se_thd89.pyi index fbba16375..e902311d5 100644 --- a/core/mocks/generated/trezorcrypto/se_thd89.pyi +++ b/core/mocks/generated/trezorcrypto/se_thd89.pyi @@ -281,3 +281,11 @@ def fido_att_sign_digest( ) -> bytes: """ """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_delete_all_credentials() -> None: + """ + Delete all FIDO2 credentials. + """ +FIDO2_CRED_COUNT_MAX: int diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 69cf9a818..d2565f9e0 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -1409,6 +1409,8 @@ import apps.webauthn.credential apps.webauthn.fido2 import apps.webauthn.fido2 + apps.webauthn.fido_seed + import apps.webauthn.fido_seed apps.webauthn.knownapps import apps.webauthn.knownapps apps.webauthn.list_resident_credentials diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py index 204cd381f..b25588f6c 100644 --- a/core/src/apps/webauthn/fido2.py +++ b/core/src/apps/webauthn/fido2.py @@ -172,11 +172,11 @@ _FIDO_ATT_PRIV_KEY = b"q&\xac+\xf6D\xdca\x86\xad\x83\xef\x1f\xcd\xf1*W\xb5\xcf\xa2\x00\x0b\x8a\xd0'\xe9V\xe8T\xc5\n\x8b" _FIDO_ATT_CERT = b"0\x82\x01\xcd0\x82\x01s\xa0\x03\x02\x01\x02\x02\x04\x03E`\xc40\n\x06\x08*\x86H\xce=\x04\x03\x020.1,0*\x06\x03U\x04\x03\x0c#Trezor FIDO Root CA Serial 841513560 \x17\r200406100417Z\x18\x0f20500406100417Z0x1\x0b0\t\x06\x03U\x04\x06\x13\x02CZ1\x1c0\x1a\x06\x03U\x04\n\x0c\x13SatoshiLabs, s.r.o.1\"0 \x06\x03U\x04\x0b\x0c\x19Authenticator Attestation1'0%\x06\x03U\x04\x03\x0c\x1eTrezor FIDO EE Serial 548784040Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04\xd9\x18\xbd\xfa\x8aT\xac\x92\xe9\r\xa9\x1f\xcaz\xa2dT\xc0\xd1s61M\xde\x83\xa5K\x86\xb5\xdfN\xf0Re\x9a\x1do\xfc\xb7F\x7f\x1a\xcd\xdb\x8a3\x08\x0b^\xed\x91\x89\x13\xf4C\xa5&\x1b\xc7{h`o\xc1\xa33010!\x06\x0b+\x06\x01\x04\x01\x82\xe5\x1c\x01\x01\x04\x04\x12\x04\x10\xd6\xd0\xbd\xc3b\xee\xc4\xdb\xde\x8dzenJD\x870\x0c\x06\x03U\x1d\x13\x01\x01\xff\x04\x020\x000\n\x06\x08*\x86H\xce=\x04\x03\x02\x03H\x000E\x02 \x0b\xce\xc4R\xc3\n\x11'\xe5\xd5\xf5\xfc\xf5\xd6Wy\x11+\xe50\xad\x9d-TXJ\xbeE\x86\xda\x93\xc6\x02!\x00\xaf\xca=\xcf\xd8A\xb0\xadz\x9e$}\x0ff\xf4L,\x83\xf9T\xab\x95O\x896\xc15\x08\x7fX\xf1\x95" else: - _FIDO_ATT_CERT = b"0\x82\x02\x960\x82\x02=\xa0\x03\x02\x01\x02\x02\x08]X\xed\x96\xdf\xc3H\xf50\n\x06\x08*\x86H\xce=\x04\x03\x020\x81\x971\x0b0\t\x06\x03U\x04\x06\x13\x02CN1\x100\x0e\x06\x03U\x04\x08\x13\x07BEIJING1\x100\x0e\x06\x03U\x04\x07\x13\x07HAIDIAN1\x1f0\x1d\x06\x03U\x04\n\x13\x16ONEKEY GLOBAL CO., LTD1\x0f0\r\x06\x03U\x04\x0b\x13\x06ONEKEY1\x140\x12\x06\x03U\x04\x03\x13\x0bONEKEY ROOT1\x1c0\x1a\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\rdev@onekey.so0\x1e\x17\r231107035200Z\x17\r331107035100Z0\x81\x961\x0b0\t\x06\x03U\x04\x06\x13\x02CN1\x100\x0e\x06\x03U\x04\x08\x13\x07BEIJING1\x100\x0e\x06\x03U\x04\x07\x13\x07HAIDIAN1\x1f0\x1d\x06\x03U\x04\n\x13\x16ONEKEY GLOBAL CO., LTD1\x0f0\r\x06\x03U\x04\x0b\x13\x06ONEKEY1\x130\x11\x06\x03U\x04\x03\x13\nONEKEY U2F1\x1c0\x1a\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\rdev@onekey.so0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04 \xc4\xc2\xca(6f\xb2\xd7\xa0|%\xb7,_\xc3\xac\xfe\xb4\x9cd\xb0'\xc1\x84\xa3\xea\x10\xe8\xd0=H\xa4\xa4\x12l=\xbc\xc6\x1f\x9fT\xda\xb5\xde0\x85\xb70\x9f(*\xc7c\xafl\x0b\xf2\xfa\xa23\x88\x0fu\xa3r0p0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14>,B\xa5\x9e\x85Q?\x9e\xda\xc8\xcft\xc3\x95?W\x93\xda\xb60\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x01\x060\x11\x06\t`\x86H\x01\x86\xf8B\x01\x01\x04\x04\x03\x02\x00\x070\x1e\x06\t`\x86H\x01\x86\xf8B\x01\r\x04\x11\x16\x0fxca certificate0\n\x06\x08*\x86H\xce=\x04\x03\x02\x03G\x000D\x02 M\xf5/\xaf\xbb\xf1fM\xbc\xf3\xe0\xd5\n\xd2\xc3'\xf5@\xaaU\x8d\xbcZBzT\xd8\xf5Y\xd4Tu\x02 @(S\xdf\x1d\xf1\xfdr\xe8_\xa7\xc0l+\xff+\xd2\xb1\x9a\xa1\x85\xf6\x08 '\xbb\xa9\xf78r\x97`" + _FIDO_ATT_CERT = b"\x30\x82\x02\x65\x30\x82\x02\x0C\xA0\x03\x02\x01\x02\x02\x08\x2F\x1F\xAB\x58\x0B\xEB\xE5\xF0\x30\x0A\x06\x08\x2A\x86\x48\xCE\x3D\x04\x03\x02\x30\x81\x97\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x10\x30\x0E\x06\x03\x55\x04\x08\x13\x07\x42\x45\x49\x4A\x49\x4E\x47\x31\x10\x30\x0E\x06\x03\x55\x04\x07\x13\x07\x48\x41\x49\x44\x49\x41\x4E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0A\x13\x16\x4F\x4E\x45\x4B\x45\x59\x20\x47\x4C\x4F\x42\x41\x4C\x20\x43\x4F\x2E\x2C\x20\x4C\x54\x44\x31\x0F\x30\x0D\x06\x03\x55\x04\x0B\x13\x06\x4F\x4E\x45\x4B\x45\x59\x31\x14\x30\x12\x06\x03\x55\x04\x03\x13\x0B\x4F\x4E\x45\x4B\x45\x59\x20\x52\x4F\x4F\x54\x31\x1C\x30\x1A\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01\x16\x0D\x64\x65\x76\x40\x6F\x6E\x65\x6B\x65\x79\x2E\x73\x6F\x30\x1E\x17\x0D\x32\x34\x31\x30\x31\x38\x30\x32\x30\x37\x30\x30\x5A\x17\x0D\x32\x39\x31\x30\x31\x38\x30\x32\x30\x37\x30\x30\x5A\x30\x81\xAA\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x4E\x31\x10\x30\x0E\x06\x03\x55\x04\x08\x13\x07\x42\x45\x49\x4A\x49\x4E\x47\x31\x10\x30\x0E\x06\x03\x55\x04\x07\x13\x07\x48\x41\x49\x44\x49\x41\x4E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0A\x13\x16\x4F\x4E\x45\x4B\x45\x59\x20\x47\x4C\x4F\x42\x41\x4C\x20\x43\x4F\x2E\x2C\x20\x4C\x54\x44\x31\x22\x30\x20\x06\x03\x55\x04\x0B\x13\x19\x41\x75\x74\x68\x65\x6E\x74\x69\x63\x61\x74\x6F\x72\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6F\x6E\x31\x14\x30\x12\x06\x03\x55\x04\x03\x13\x0B\x4F\x4E\x45\x4B\x45\x59\x20\x46\x49\x44\x4F\x31\x1C\x30\x1A\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01\x16\x0D\x64\x65\x76\x40\x6F\x6E\x65\x6B\x65\x79\x2E\x73\x6F\x30\x59\x30\x13\x06\x07\x2A\x86\x48\xCE\x3D\x02\x01\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07\x03\x42\x00\x04\x20\xC4\xC2\xCA\x28\x36\x66\xB2\xD7\xA0\x7C\x25\xB7\x2C\x5F\xC3\xAC\xFE\xB4\x9C\x64\xB0\x27\xC1\x84\xA3\xEA\x10\xE8\xD0\x3D\x48\xA4\xA4\x12\x6C\x3D\xBC\xC6\x1F\x9F\x54\xDA\xB5\xDE\x30\x85\xB7\x30\x9F\x28\x2A\xC7\x63\xAF\x6C\x0B\xF2\xFA\xA2\x33\x88\x0F\x75\xA3\x2D\x30\x2B\x30\x09\x06\x03\x55\x1D\x13\x04\x02\x30\x00\x30\x1E\x06\x09\x60\x86\x48\x01\x86\xF8\x42\x01\x0D\x04\x11\x16\x0F\x78\x63\x61\x20\x63\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x30\x0A\x06\x08\x2A\x86\x48\xCE\x3D\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x2F\x73\x6A\x80\xBC\x4F\x38\x0D\xDE\x21\xC1\x35\x40\x59\x09\x8E\x4C\x81\x9D\x3E\xA9\x6A\x51\x2F\xB3\x54\xEE\xEF\x5B\x84\xA5\xF9\x02\x20\x04\xD8\x37\x35\x88\x76\xED\x71\x02\x84\x82\x6E\x26\x3A\xB8\x8F\x82\xA4\xF8\xD4\x2E\x15\x94\xB6\xE2\x7E\xDD\xC3\x7D\xE1\xA5\x63" _BOGUS_APPID_CHROME = b"A" * 32 _BOGUS_APPID_FIREFOX = b"\0" * 32 _BOGUS_APPIDS = (_BOGUS_APPID_CHROME, _BOGUS_APPID_FIREFOX) -_AAGUID = b"\x69\xe7\xc3\x6f\xf2\xf6\x9e\x0d\x07\xa6\xbc\xc2\x43\x26\x2e\x6b" # First 16 bytes of SHA-256("OneKey Pro FIDO2") +_AAGUID = b"\x70\xe7\xc3\x6f\xf2\xf6\x9e\x0d\x07\xa6\xbc\xc2\x43\x26\x2e\x6b" # First 16 bytes of SHA-256("OneKey Pro FIDO2") # authentication control byte _AUTH_ENFORCE = const(0x03) # enforce user presence and sign @@ -205,7 +205,7 @@ # FIDO2 configuration. _ALLOW_FIDO2 = True -_ALLOW_RESIDENT_CREDENTIALS = False +_ALLOW_RESIDENT_CREDENTIALS = storage.device.get_se01_version() >= "1.1.5" _ALLOW_WINK = False # The default attestation type to use in MakeCredential responses. If false, then basic attestation will be used by default. @@ -851,7 +851,6 @@ async def on_confirm(self) -> None: response_data = cbor_make_credential_sign( self._client_data_hash, self._cred, self._user_verification ) - cmd = Cmd(self.cid, _CMD_CBOR, bytes([_ERR_NONE]) + response_data) if self._resident: send_cmd_sync( @@ -1000,7 +999,8 @@ async def confirm_dialog(self) -> bool: return await confirm_webauthn_reset() async def on_confirm(self) -> None: - storage.resident_credentials.delete_all() + if _ALLOW_RESIDENT_CREDENTIALS: + storage.resident_credentials.delete_all() cmd = Cmd(self.cid, _CMD_CBOR, bytes([_ERR_NONE])) await send_cmd(cmd, self.iface) self.finished = True @@ -1812,7 +1812,7 @@ def cbor_get_assertion_hmac_secret(cred: Credential, hmac_secret: dict) -> bytes raise CborError(_ERR_INVALID_LEN) # Compute the ECDH shared secret. - ecdh_result = nist256p1.multiply(KEY_AGREEMENT_PRIVKEY, b"\04" + x + y) + ecdh_result = nist256p1.multiply(KEY_AGREEMENT_PRIVKEY, b"\04" + x + y, True) shared_secret = hashlib.sha256(ecdh_result[1:33]).digest() # Check the authentication tag and decrypt the salt. diff --git a/core/src/apps/webauthn/fido_seed.py b/core/src/apps/webauthn/fido_seed.py new file mode 100644 index 000000000..19f5fcb7c --- /dev/null +++ b/core/src/apps/webauthn/fido_seed.py @@ -0,0 +1,18 @@ +def ensure_fido_seed(func): + def wrapper(*args, **kwargs): + from trezor.crypto import se_thd89 + from utime import sleep_ms + + while True: + try: + ret = se_thd89.fido_seed() + if ret: + break + else: + sleep_ms(100) + continue + except Exception: + raise Exception("Failed to generate seed.") + return func(*args, **kwargs) + + return wrapper diff --git a/core/src/apps/webauthn/resident_credentials.py b/core/src/apps/webauthn/resident_credentials.py index eda12d86c..946e4b1ba 100644 --- a/core/src/apps/webauthn/resident_credentials.py +++ b/core/src/apps/webauthn/resident_credentials.py @@ -5,6 +5,7 @@ from storage.resident_credentials import MAX_RESIDENT_CREDENTIALS from .credential import Fido2Credential +from .fido_seed import ensure_fido_seed RP_ID_HASH_LENGTH = const(32) @@ -17,6 +18,7 @@ def _credential_from_data(index: int, data: bytes) -> Fido2Credential: return cred +@ensure_fido_seed def find_all() -> Iterator[Fido2Credential]: for index in range(MAX_RESIDENT_CREDENTIALS): data = storage.resident_credentials.get(index) @@ -24,6 +26,7 @@ def find_all() -> Iterator[Fido2Credential]: yield _credential_from_data(index, data) +@ensure_fido_seed def find_by_rp_id_hash(rp_id_hash: bytes) -> Iterator[Fido2Credential]: for index in range(MAX_RESIDENT_CREDENTIALS): data = storage.resident_credentials.get(index) @@ -39,6 +42,7 @@ def find_by_rp_id_hash(rp_id_hash: bytes) -> Iterator[Fido2Credential]: yield _credential_from_data(index, data) +@ensure_fido_seed def get_resident_credential(index: int) -> Fido2Credential | None: if not 0 <= index < MAX_RESIDENT_CREDENTIALS: return None @@ -50,6 +54,7 @@ def get_resident_credential(index: int) -> Fido2Credential | None: return _credential_from_data(index, data) +@ensure_fido_seed def store_resident_credential(cred: Fido2Credential) -> bool: slot = None for index in range(MAX_RESIDENT_CREDENTIALS): diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index 3fb7665dd..490bd3cae 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -115,13 +115,13 @@ def find_message_handler_module(msg_type: int) -> str: if msg_type == MessageType.GetNextU2FCounter: return "apps.management.get_next_u2f_counter" - # # webauthn - # if msg_type == MessageType.WebAuthnListResidentCredentials: - # return "apps.webauthn.list_resident_credentials" - # if msg_type == MessageType.WebAuthnAddResidentCredential: - # return "apps.webauthn.add_resident_credential" - # if msg_type == MessageType.WebAuthnRemoveResidentCredential: - # return "apps.webauthn.remove_resident_credential" + # webauthn + if msg_type == MessageType.WebAuthnListResidentCredentials: + return "apps.webauthn.list_resident_credentials" + if msg_type == MessageType.WebAuthnAddResidentCredential: + return "apps.webauthn.add_resident_credential" + if msg_type == MessageType.WebAuthnRemoveResidentCredential: + return "apps.webauthn.remove_resident_credential" # ethereum if msg_type == MessageType.EthereumGetAddress: diff --git a/core/src/storage/resident_credentials.py b/core/src/storage/resident_credentials.py index 885391824..23e52aa6c 100644 --- a/core/src/storage/resident_credentials.py +++ b/core/src/storage/resident_credentials.py @@ -1,10 +1,13 @@ -from micropython import const - from storage import common +from trezor import utils +from trezor.crypto import se_thd89 -_RESIDENT_CREDENTIAL_START_KEY = const(1) - -MAX_RESIDENT_CREDENTIALS = const(100) +if utils.USE_THD89: + MAX_RESIDENT_CREDENTIALS = se_thd89.FIDO2_CRED_COUNT_MAX + _RESIDENT_CREDENTIAL_START_KEY = 0 +else: + _RESIDENT_CREDENTIAL_START_KEY = 1 + MAX_RESIDENT_CREDENTIALS = 100 def get(index: int) -> bytes | None: @@ -29,5 +32,8 @@ def delete(index: int) -> None: def delete_all() -> None: - for i in range(MAX_RESIDENT_CREDENTIALS): - common.delete(common.APP_WEBAUTHN, i + _RESIDENT_CREDENTIAL_START_KEY) + if utils.USE_THD89: + se_thd89.fido_delete_all_credentials() + else: + for i in range(MAX_RESIDENT_CREDENTIALS): + common.delete(common.APP_WEBAUTHN, i + _RESIDENT_CREDENTIAL_START_KEY) diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 6008dc807..691935c55 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -413,3 +413,4 @@ FileInfoList = 10024 OnekeyGetFeatures = 10025 OnekeyFeatures = 10026 + WriteSEPrivateKey = 10027 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index 86a347dd1..927ca9663 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -431,6 +431,7 @@ class MessageType(IntEnum): FileInfoList = 10024 OnekeyGetFeatures = 10025 OnekeyFeatures = 10026 + WriteSEPrivateKey = 10027 class FailureType(IntEnum): UnexpectedMessage = 1 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 0131b1c30..76633e03b 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -3638,6 +3638,20 @@ def __init__( def is_type_of(cls, msg: Any) -> TypeGuard["DeviceInfo"]: return isinstance(msg, cls) + class WriteSEPrivateKey(protobuf.MessageType): + private_key: "bytes" + + def __init__( + self, + *, + private_key: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["WriteSEPrivateKey"]: + return isinstance(msg, cls) + class ReadSEPublicKey(protobuf.MessageType): @classmethod diff --git a/core/src/trezor/ui/layouts/lvgl/webauthn.py b/core/src/trezor/ui/layouts/lvgl/webauthn.py index 35ec858df..cb8b33983 100644 --- a/core/src/trezor/ui/layouts/lvgl/webauthn.py +++ b/core/src/trezor/ui/layouts/lvgl/webauthn.py @@ -45,4 +45,4 @@ async def confirm_webauthn_reset() -> bool: _(i18n_keys.BUTTON__CANCEL), icon_path="A:/res/warning.png", ) - return await screen.request() + return bool(await screen.request()) diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 19674ce54..957de1e5d 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -439,6 +439,7 @@ class MessageType(IntEnum): FileInfoList = 10024 OnekeyGetFeatures = 10025 OnekeyFeatures = 10026 + WriteSEPrivateKey = 10027 class FailureType(IntEnum): @@ -5152,6 +5153,20 @@ def __init__( self.pre_firmware = pre_firmware +class WriteSEPrivateKey(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 10027 + FIELDS = { + 1: protobuf.Field("private_key", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + private_key: "bytes", + ) -> None: + self.private_key = private_key + + class ReadSEPublicKey(protobuf.MessageType): MESSAGE_WIRE_TYPE = 10004