From 7c60aea84140888d74aaa6608bc30aeb75e6e9eb Mon Sep 17 00:00:00 2001 From: loatheb Date: Tue, 27 Aug 2024 16:21:29 +0800 Subject: [PATCH] feat: lite support & monero support (#159) * feat: added lite card support (#138) * Support OneKey lite card * Monero coin supports SE signing. (#113) * feat(core): add support for psbt sign by qr (#139) * feat(core): add support for psbt sign and address verify and get multi accounts * fix(core): optimize Near transfer amount display * fix(core): optimize boot ui * fix(core): fix ada message sign * fix(core): optimize polkadot code base * fix(core): optimize onboarding guide && update FCC Id * fix: Lite Card UI Issues and Mnemonic Word Count Validation (#141) Fix Lite Card UI Text Display and Mnemonic Count Validation issue * fix: update pro lite card i18n (#143) * Optimize SDRAM and PN532 reset timing. (#144) * Verify firmware data in SDRAM. * Reconfigure SDRAM-related GPIOs in the firmware. * Add SDRAM test item to aging test. * Optimize PN532 reset timing. * chore(core): bump firmware version to 4.10.0 (#140) * fix(core): fix ethereum sign issue by qr * fix(core): fix polkadot transfer amount decode issue * bump firmware version to 4.10.0 * ci(core): pin nix version to avoid some issue * Update USB recognition method, wireless charging && fix storage bug. (#142) * Use USB3320C register status to detect USB connection. * Check battery temperature when enabling wireless charging. * Fix bug where data storage exceeds space. * Support FIDO * Verify firmware data in SDRAM. * Keep screen in reset state when turned off. * Add SDRAM test item to aging test. * Optimize PN532 reset timing. * bump firmware version to 4.10.0 --------- Co-authored-by: loatheb * chore: rebuild lokalise i18n * chore: fix conflict * fix: conflict issue * fix: conflict * fix: remove workflow qr update * fix: remove unused utils import * fix: isort issue --------- Co-authored-by: guowei0105 <155709160+guowei0105@users.noreply.github.com> Co-authored-by: lihuanhuan Co-authored-by: Ritchie <32901980+somebodyLi@users.noreply.github.com> --- core/SConscript.bootloader | 1 + core/SConscript.firmware | 9 + core/embed/bootloader/main.c | 44 +- core/embed/bootloader/messages.c | 6 +- .../modtrezorcrypto/modtrezorcrypto-bip32.h | 41 ++ .../modtrezorcrypto-se-thd89.h | 332 ++++++++++++ .../extmod/modtrezorio/modtrezorio-nfc.h | 135 ++--- .../extmod/modtrezorio/modtrezorio-poll.h | 31 +- .../extmod/modtrezorio/modtrezorio-usb.h | 20 +- core/embed/extmod/modtrezorio/modtrezorio.c | 2 +- core/embed/extmod/modtrezorui/mipi_lcd.c | 19 +- .../extmod/modtrezorutils/modtrezorutils.c | 24 +- core/embed/firmware/main.c | 3 + core/embed/firmware/version.h | 4 +- core/embed/lite_card/cmac.c | 286 ++++++++++ core/embed/lite_card/cmac.h | 7 + core/embed/lite_card/lite_card.c | 250 +++++++++ core/embed/lite_card/lite_card.h | 12 + core/embed/lite_card/scp03.c | 82 +++ core/embed/lite_card/scp03.h | 21 + core/embed/lite_card/scp11.c | 488 +++++++++++++++++ core/embed/lite_card/scp11.h | 91 ++++ core/embed/lite_card/tag.h | 40 ++ core/embed/lite_card/tlv.c | 41 ++ core/embed/lite_card/tlv.h | 26 + core/embed/pn532/pn532.c | 312 ++++++++++- core/embed/pn532/pn532.h | 62 ++- core/embed/pn532/pn532_frame.c | 320 ----------- core/embed/pn532/pn532_frame.h | 14 - core/embed/pn532/pn532_functions.c | 395 -------------- core/embed/pn532/pn532_functions.h | 75 --- core/embed/pn532/pn532_hal.c | 118 ---- core/embed/pn532/pn532_hal.h | 23 - core/embed/pn532/pn532_interface.c | 29 - core/embed/pn532/pn532_interface.h | 24 - core/embed/pn532/pn532_spi.c | 42 ++ core/embed/pn532/pn532_spi.h | 17 + core/embed/pn532/pn532_stub.c | 44 ++ core/embed/pn532/pn532_stub.h | 17 + core/embed/trezorhal/device.c | 13 +- core/embed/trezorhal/low_power.c | 99 +++- core/embed/trezorhal/low_power.h | 2 +- core/embed/trezorhal/nfc.c | 492 ++--------------- core/embed/trezorhal/nfc.h | 19 +- core/embed/trezorhal/se_thd89.c | 282 +++++++++- core/embed/trezorhal/se_thd89.h | 27 + core/embed/trezorhal/spi.c | 2 +- core/mocks/generated/trezorcrypto/bip32.pyi | 6 + .../mocks/generated/trezorcrypto/se_thd89.pyi | 99 ++++ core/mocks/generated/trezorio/__init__.pyi | 38 +- core/mocks/generated/trezorio/nfc.pyi | 22 + core/mocks/generated/trezorutils.pyi | 6 +- core/src/all_modules.py | 2 + core/src/apps/common/seed.py | 35 +- .../management/recovery_device/__init__.py | 15 +- .../management/recovery_device/homescreen.py | 52 +- .../apps/management/recovery_device/layout.py | 9 +- core/src/apps/monero/misc.py | 43 +- core/src/apps/monero/xmr/credentials.py | 23 + core/src/apps/monero/xmr/monero.py | 22 +- .../ur_registry/chains/bitcoin/transaction.py | 15 - .../hardware_requests/verify_address.py | 1 - core/src/apps/webauthn/credential.py | 127 +++-- core/src/apps/webauthn/fido2.py | 83 ++- core/src/apps/workflow_handlers.py | 30 +- core/src/main.py | 7 +- core/src/session.py | 29 +- core/src/storage/device.py | 2 + core/src/trezor/lvglui/i18n/keys.py | 4 + core/src/trezor/lvglui/i18n/locales/de.py | 2 + core/src/trezor/lvglui/i18n/locales/en.py | 2 + core/src/trezor/lvglui/i18n/locales/es.py | 2 + core/src/trezor/lvglui/i18n/locales/fr.py | 2 + core/src/trezor/lvglui/i18n/locales/it.py | 2 + core/src/trezor/lvglui/i18n/locales/ja.py | 2 + core/src/trezor/lvglui/i18n/locales/ko.py | 2 + core/src/trezor/lvglui/i18n/locales/ru.py | 2 + core/src/trezor/lvglui/i18n/locales/zh_cn.py | 2 + core/src/trezor/lvglui/i18n/locales/zh_hk.py | 2 + .../lvglui/res/nfc-icon-transfering.png | Bin 2602 -> 2852 bytes core/src/trezor/lvglui/res/nfc-start.png | Bin 8779 -> 12842 bytes core/src/trezor/lvglui/scrs/common.py | 1 + core/src/trezor/lvglui/scrs/homescreen.py | 116 ++-- core/src/trezor/lvglui/scrs/initscreen.py | 89 +++- core/src/trezor/lvglui/scrs/nfc.py | 504 +++++++++++++++++- core/src/trezor/lvglui/scrs/pinscreen.py | 74 +++ core/src/trezor/lvglui/scrs/template.py | 1 + core/src/trezor/lvglui/scrs/wipe_device.py | 57 ++ core/src/trezor/qr.py | 4 +- core/src/trezor/uart.py | 119 +++-- core/src/trezor/ui/layouts/lvgl/__init__.py | 118 +--- core/src/trezor/ui/layouts/lvgl/lite.py | 404 ++++++++++++++ core/src/trezor/utils.py | 24 +- core/src/trezor/wire/__init__.py | 9 - core/src/usb.py | 3 +- tools/style.c.exclude | 3 +- 96 files changed, 4565 insertions(+), 2094 deletions(-) create mode 100644 core/embed/lite_card/cmac.c create mode 100644 core/embed/lite_card/cmac.h create mode 100644 core/embed/lite_card/lite_card.c create mode 100644 core/embed/lite_card/lite_card.h create mode 100644 core/embed/lite_card/scp03.c create mode 100644 core/embed/lite_card/scp03.h create mode 100644 core/embed/lite_card/scp11.c create mode 100644 core/embed/lite_card/scp11.h create mode 100644 core/embed/lite_card/tag.h create mode 100644 core/embed/lite_card/tlv.c create mode 100644 core/embed/lite_card/tlv.h delete mode 100644 core/embed/pn532/pn532_frame.c delete mode 100644 core/embed/pn532/pn532_frame.h delete mode 100644 core/embed/pn532/pn532_functions.c delete mode 100644 core/embed/pn532/pn532_functions.h delete mode 100644 core/embed/pn532/pn532_hal.c delete mode 100644 core/embed/pn532/pn532_hal.h delete mode 100644 core/embed/pn532/pn532_interface.c delete mode 100644 core/embed/pn532/pn532_interface.h create mode 100644 core/embed/pn532/pn532_spi.c create mode 100644 core/embed/pn532/pn532_spi.h create mode 100644 core/embed/pn532/pn532_stub.c create mode 100644 core/embed/pn532/pn532_stub.h create mode 100644 core/mocks/generated/trezorio/nfc.pyi mode change 100644 => 100755 core/src/trezor/lvglui/res/nfc-icon-transfering.png create mode 100644 core/src/trezor/ui/layouts/lvgl/lite.py diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 3c7f9dba8..72ae85143 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -199,6 +199,7 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/usbd_msc_scsi.c', 'embed/trezorhal/usbd_msc_storage.c', 'embed/trezorhal/usbd_desc.c', + 'embed/trezorhal/usbd_ulpi.c', 'embed/trezorhal/trans_fifo.c', 'embed/trezorhal/i2c.c', 'embed/trezorhal/spi.c', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 8dadf4cf0..8bb5f18cf 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -188,6 +188,7 @@ if TREZOR_MODEL in ('T',): 'embed/trezorhal/fatfs/diskio.c', ] SOURCE_MOD.extend(Glob('embed/pn532/*.c')) + SOURCE_MOD.extend(Glob('embed/lite_card/*.c')) # modtrezorui CPPPATH_MOD += [ @@ -434,6 +435,7 @@ if PRODUCTION_MODEL == 'H': 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_jpeg.c', 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rtc.c', 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_rtc_ex.c', + 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_lptim.c', 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_fmc.c', 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_sdmmc.c', 'vendor/micropython/lib/stm32lib/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_usb.c', @@ -478,6 +480,7 @@ if PRODUCTION_MODEL == 'H': 'embed/trezorhal/usbd_ctlreq.c', 'embed/trezorhal/usbd_ioreq.c', 'embed/trezorhal/usbd_desc.c', + 'embed/trezorhal/usbd_ulpi.c', 'embed/trezorhal/thd89.c', 'embed/trezorhal/se_thd89.c', 'embed/trezorhal/trans_fifo.c', @@ -571,6 +574,10 @@ SOURCE_DEBUG_UTILS.extend(Glob('embed/debug_utils/*.c')) SOURCE_PN532_WRAPPER = [] SOURCE_PN532_WRAPPER.extend(Glob('embed/pn532/*.c')) +# lite card wrapper +SOURCE_LITE_CARD_WRAPPER = [] +SOURCE_LITE_CARD_WRAPPER.extend(Glob('embed/lite_card/*.c')) + # fp_sensor wrapper SOURCE_FP_SENSOR_WRAPPER = [] SOURCE_FP_SENSOR_WRAPPER.extend(Glob('embed/fp_sensor_wrapper/*.c')) @@ -705,6 +712,7 @@ if PRODUCTION_MODEL == 'H': 'embed/zbar/qrcode', 'embed/zbar/decoder', 'embed/bytewords/', + 'embed/lite_card', ] + CPPPATH_MOD, CPPDEFINES=[ 'TREZOR_MODEL_'+TREZOR_MODEL, @@ -833,6 +841,7 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/common.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/reset.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/recovery.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/lite.py')) if EVERYTHING: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/altcoin.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/ui/layouts/webauthn.py')) diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 0f3538b96..51e7b4091 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -246,32 +246,30 @@ static void usb_init_all(secbool usb21_landing) { static void usb_switch(void) { static bool usb_opened = false; - - if (!ble_charging_state()) { - ble_cmd_req(BLE_PWR, BLE_PWR_CHARGING); - return; - } - - if(ble_get_charge_type() != 0) - { - if((ble_get_charge_type() == CHARGE_BY_USB) && (!usb_opened)) - { - usb_start(); - usb_opened = true; + static uint32_t counter0 = 0, counter1 = 0; + + if (usb_3320_host_connected()) { + counter0++; + counter1 = 0; + if (counter0 > 5) { + counter0 = 0; + if (!usb_opened) { + usb_start(); + usb_opened = true; + } } - else if (usb_opened) - { - usb_stop(); - usb_opened = false; + + } else { + counter0 = 0; + counter1++; + if (counter1 > 5) { + counter1 = 0; + if (usb_opened) { + usb_stop(); + usb_opened = false; + } } } - else - { - // default enable usb if no power status obtained - usb_start(); - usb_opened = true; - } - } static void charge_switch(void) { diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 42f1b30e4..32ca32aac 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -355,7 +355,8 @@ static void send_msg_features(uint8_t iface_num, MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len); const char *ver_str = format_ver("%d.%d.%d", hdr->onekey_version); MSG_SEND_ASSIGN_STRING_LEN(onekey_version, ver_str, strlen(ver_str)); - MSG_SEND_ASSIGN_STRING_LEN(onekey_firmware_version, ver_str, strlen(ver_str)); + MSG_SEND_ASSIGN_STRING_LEN(onekey_firmware_version, ver_str, + strlen(ver_str)); } else { MSG_SEND_ASSIGN_VALUE(firmware_present, false); @@ -425,7 +426,8 @@ static void send_msg_features_ex(uint8_t iface_num, if (vhdr && hdr) { const char *ver_str = format_ver("%d.%d.%d", hdr->onekey_version); - MSG_SEND_ASSIGN_STRING_LEN(onekey_firmware_version, ver_str, strlen(ver_str)); + MSG_SEND_ASSIGN_STRING_LEN(onekey_firmware_version, ver_str, + strlen(ver_str)); uint8_t *fimware_hash = get_firmware_hash(); MSG_SEND_ASSIGN_BYTES(onekey_firmware_hash, fimware_hash, 32); diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h index 0fd7bb99a..a72a931b0 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-bip32.h @@ -230,6 +230,45 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_derive_path(mp_obj_t self, STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_derive_path_obj, mod_trezorcrypto_HDNode_derive_path); +/// def derive_fido_path(self, path: Sequence[int]) -> None: +/// """ +/// Go through a list of indexes and iteratively derive a child node in +/// place. +/// """ +STATIC mp_obj_t mod_trezorcrypto_HDNode_derive_fido_path(mp_obj_t self, + mp_obj_t path) { + mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self); + + // get path objects and length + size_t plen = 0; + mp_obj_t *pitems = NULL; + mp_obj_get_array(path, &plen, &pitems); + if (plen > 32) { + mp_raise_ValueError("Path cannot be longer than 32 indexes"); + } + +#if USE_THD89 + uint32_t address_n[plen]; + + for (uint32_t pi = 0; pi < plen; pi++) { + address_n[pi] = trezor_obj_get_uint(pitems[pi]); + } + const char *curve = o->hdnode.curve->curve_name; + if (!se_derive_fido_keys(&o->hdnode, curve, address_n, plen, + &o->fingerprint)) { + o->fingerprint = 0; + memzero(&o->hdnode, sizeof(o->hdnode)); + mp_raise_ValueError("Failed to derive path"); + } +#else + + mp_raise_ValueError("Not implemented"); +#endif + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_HDNode_derive_fido_path_obj, + mod_trezorcrypto_HDNode_derive_fido_path); + /// def serialize_public(self, version: int) -> str: /// """ /// Serialize the public info from HD node to base58 string. @@ -537,6 +576,8 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_HDNode_locals_dict_table[] = { MP_ROM_PTR(&mod_trezorcrypto_HDNode_nem_encrypt_obj)}, {MP_ROM_QSTR(MP_QSTR_ethereum_pubkeyhash), MP_ROM_PTR(&mod_trezorcrypto_HDNode_ethereum_pubkeyhash_obj)}, + {MP_ROM_QSTR(MP_QSTR_derive_fido_path), + MP_ROM_PTR(&mod_trezorcrypto_HDNode_derive_fido_path_obj)}, #endif }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_HDNode_locals_dict, diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h index 35b49e275..efeb36dd9 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h @@ -501,6 +501,21 @@ STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_node(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_se_thd89_slip21_node_obj, mod_trezorcrypto_se_thd89_slip21_node); +/// def slip21_fido_node() -> bytes: +/// """ +/// Returns slip21 fido node, seed without passphrase. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_slip21_fido_node(void) { + vstr_t vstr = {0}; + vstr_init_len(&vstr, 64); + if (se_slip21_fido_node((uint8_t *)vstr.buf) != 0) { + mp_raise_ValueError("slip21 fido node failed"); + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_se_thd89_slip21_fido_node_obj, + mod_trezorcrypto_se_thd89_slip21_fido_node); + /// def authorization_set( /// authorization_type: int, /// authorization: bytes, @@ -616,6 +631,301 @@ STATIC mp_obj_t mod_trezorcrypto_se_thd89_sign_message(mp_obj_t msg) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_se_thd89_sign_message_obj, mod_trezorcrypto_se_thd89_sign_message); +/// def derive_xmr( +/// path: Sequence[int] +/// digest: bytes, +/// ) -> tuple[bytes, bytes]: +STATIC mp_obj_t mod_trezorcrypto_se_thd89_derive_xmr(mp_obj_t path) { + // get path objects and length + size_t plen = 0; + mp_obj_t *pitems = NULL; + mp_obj_get_array(path, &plen, &pitems); + if (plen > 32) { + mp_raise_ValueError("Path cannot be longer than 32 indexes"); + } + + uint32_t address_n[plen]; + + for (uint32_t pi = 0; pi < plen; pi++) { + address_n[pi] = trezor_obj_get_uint(pitems[pi]); + } + + uint8_t pubkey[32], prikey_hash[32]; + if (!se_derive_xmr_key("ed25519", address_n, plen, pubkey, prikey_hash)) { + mp_raise_ValueError("Failed to derive path"); + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + tuple->items[0] = mp_obj_new_bytes(pubkey, 32); + tuple->items[1] = mp_obj_new_bytes(prikey_hash, 32); + + return MP_OBJ_FROM_PTR(tuple); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_se_thd89_derive_xmr_obj, + mod_trezorcrypto_se_thd89_derive_xmr); + +/// def derive_xmr_privare( +/// deriv: bytes +/// index: int, +/// ) -> bytes: +/// """ +/// base + H_s(derivation || varint(output_index)) +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_derive_xmr_private(mp_obj_t deriv, + mp_obj_t index) { + mp_buffer_info_t pub_key = {0}; + mp_get_buffer_raise(deriv, &pub_key, MP_BUFFER_READ); + + uint32_t idx = mp_obj_get_int(index); + + uint8_t out_pri[32]; + if (!se_derive_xmr_private_key(pub_key.buf, idx, out_pri)) { + mp_raise_ValueError("Failed to derive private key"); + } + + return mp_obj_new_str_copy(&mp_type_bytes, (const uint8_t *)out_pri, 32); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_se_thd89_derive_xmr_private_obj, + mod_trezorcrypto_se_thd89_derive_xmr_private); + +/// def xmr_get_tx_key( +/// rand: bytes +/// hash: bytes, +/// ) -> bytes: +/// """ +/// base + H_s(derivation || varint(output_index)) +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_xmr_get_tx_key(mp_obj_t rand, + mp_obj_t hash) { + mp_buffer_info_t rand_r = {0}; + mp_get_buffer_raise(rand, &rand_r, MP_BUFFER_READ); + + mp_buffer_info_t hash_h = {0}; + mp_get_buffer_raise(hash, &hash_h, MP_BUFFER_READ); + + uint8_t out_pri[32]; + if (!se_xmr_get_tx_key(rand_r.buf, hash_h.buf, out_pri)) { + mp_raise_ValueError("Failed to get tx key"); + } + + return mp_obj_new_str_copy(&mp_type_bytes, (const uint8_t *)out_pri, 32); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_se_thd89_xmr_get_tx_key_obj, + mod_trezorcrypto_se_thd89_xmr_get_tx_key); + +/// def fido_seed( +/// callback: Callable[[int, int], None] | None = None, +/// ) -> bool: +/// """ +/// Generate seed from mnemonic without passphrase. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_seed(size_t n_args, + const mp_obj_t *args) { + if (n_args > 0) { + // generate with a progress callback + ui_wait_callback = args[0]; + // se_set_ui_callback(ui_wait_callback); + ui_wait_callback = mp_const_none; + } else { + // generate without callback + // se_set_ui_callback(NULL); + } + uint8_t percent; + if (!se_gen_fido_seed(&percent)) { + mp_raise_msg(&mp_type_RuntimeError, "Failed to generate seed."); + } + if (percent != 100) { + return mp_const_false; + } + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorcrypto_se_thd89_fido_seed_obj, 0, 1, + mod_trezorcrypto_se_thd89_fido_seed); + +/// def fido_u2f_register( +/// app_id: bytes, +/// challenge: bytes, +/// ) -> tuple[bytes, bytes, bytes]: +/// """ +/// U2F Register. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_u2f_register( + mp_obj_t app_id, mp_obj_t challenge) { + mp_buffer_info_t app_id_b = {0}; + mp_get_buffer_raise(app_id, &app_id_b, MP_BUFFER_READ); + + mp_buffer_info_t challenge_b = {0}; + mp_get_buffer_raise(challenge, &challenge_b, MP_BUFFER_READ); + + if (app_id_b.len != 32 || challenge_b.len != 32) { + mp_raise_ValueError("Invalid length of app_id or challenge"); + } + uint8_t key_handle[64], public_key[65], sign[64]; + if (!se_u2f_register(app_id_b.buf, challenge_b.buf, key_handle, public_key, + sign)) { + mp_raise_ValueError("Failed to register"); + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); + tuple->items[0] = mp_obj_new_bytes(key_handle, 64); + tuple->items[1] = mp_obj_new_bytes(public_key, 65); + tuple->items[2] = mp_obj_new_bytes(sign, 64); + return MP_OBJ_FROM_PTR(tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_se_thd89_fido_u2f_register_obj, + mod_trezorcrypto_se_thd89_fido_u2f_register); + +/// def u2f_gen_handle_and_node( +/// app_id: bytes, +/// ) -> tuple[bytes, HDNode]: +/// """ +/// U2F generate handle and HDNode. +/// """ +STATIC mp_obj_t +mod_trezorcrypto_se_thd89_u2f_gen_handle_and_node(mp_obj_t app_id) { + mp_buffer_info_t app_id_b = {0}; + mp_get_buffer_raise(app_id, &app_id_b, MP_BUFFER_READ); + + if (app_id_b.len != 32) { + mp_raise_ValueError("Invalid length of app_id"); + } + uint8_t key_handle[64]; + HDNode hdnode = {0}; + if (!se_u2f_gen_handle_and_node(app_id_b.buf, key_handle, &hdnode)) { + mp_raise_ValueError("Failed to generate handle and node"); + } + + mp_obj_HDNode_t *o = m_new_obj_with_finaliser(mp_obj_HDNode_t); + o->base.type = &mod_trezorcrypto_HDNode_type; + o->hdnode = hdnode; + o->fingerprint = 0; + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + tuple->items[0] = mp_obj_new_bytes(key_handle, 64); + tuple->items[1] = MP_OBJ_FROM_PTR(o); + + return MP_OBJ_FROM_PTR(tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1( + mod_trezorcrypto_se_thd89_u2f_gen_handle_and_node_obj, + mod_trezorcrypto_se_thd89_u2f_gen_handle_and_node); + +/// def fido_u2f_validate( +/// app_id: bytes, +/// key_handle: bytes, +/// ) -> bool: +/// """ +/// U2F Validate Handle. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_u2f_validate( + mp_obj_t app_id, mp_obj_t key_handle) { + mp_buffer_info_t app_id_b = {0}; + mp_get_buffer_raise(app_id, &app_id_b, MP_BUFFER_READ); + + mp_buffer_info_t key_handle_b = {0}; + mp_get_buffer_raise(key_handle, &key_handle_b, MP_BUFFER_READ); + + if (app_id_b.len != 32 || key_handle_b.len != 64) { + mp_raise_ValueError("Invalid length of app_id or key_handle"); + } + + if (!se_u2f_validate_handle(app_id_b.buf, key_handle_b.buf)) { + return mp_const_false; + } + return mp_const_true; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2( + mod_trezorcrypto_se_thd89_fido_u2f_validate_obj, + mod_trezorcrypto_se_thd89_fido_u2f_validate); + +/// def fido_u2f_authenticate( +/// app_id: bytes, +/// key_handle: bytes, +/// challenge: bytes, +/// ) -> tuple[int, bytes]: +/// """ +/// U2F Authenticate. +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_u2f_authenticate( + mp_obj_t app_id, mp_obj_t key_handle, mp_obj_t challenge) { + mp_buffer_info_t app_id_b = {0}; + mp_get_buffer_raise(app_id, &app_id_b, MP_BUFFER_READ); + + mp_buffer_info_t key_handle_b = {0}; + mp_get_buffer_raise(key_handle, &key_handle_b, MP_BUFFER_READ); + + mp_buffer_info_t challenge_b = {0}; + mp_get_buffer_raise(challenge, &challenge_b, MP_BUFFER_READ); + + if (app_id_b.len != 32 || challenge_b.len != 32 || key_handle_b.len != 64) { + mp_raise_ValueError("Invalid length of app_id or challenge"); + } + uint8_t sign[64]; + uint32_t u2f_counter; + if (!se_u2f_authenticate(app_id_b.buf, key_handle_b.buf, challenge_b.buf, + (uint8_t *)&u2f_counter, sign)) { + mp_raise_ValueError("Failed to authenticate"); + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + u2f_counter = ((u2f_counter >> 24) & 0xff) | ((u2f_counter >> 8) & 0xff00) | + ((u2f_counter << 8) & 0xff0000) | + ((u2f_counter << 24) & 0xff000000); + tuple->items[0] = mp_obj_new_int(u2f_counter); + tuple->items[1] = mp_obj_new_bytes(sign, 64); + return MP_OBJ_FROM_PTR(tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3( + mod_trezorcrypto_se_thd89_fido_u2f_authenticate_obj, + mod_trezorcrypto_se_thd89_fido_u2f_authenticate); + +/// def fido_sign_digest( +/// digest: bytes, +/// ) -> bytes: +/// """ +/// """ +STATIC mp_obj_t mod_trezorcrypto_se_thd89_fido_sign_digest(mp_obj_t digest) { + mp_buffer_info_t dig = {0}; + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); + + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); + } + uint8_t sign[64]; + if (!se_fido_hdnode_sign_digest((const uint8_t *)dig.buf, sign)) { + mp_raise_ValueError("Signing failed"); + } + return mp_obj_new_bytes(sign, 64); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_se_thd89_fido_sign_digest_obj, + mod_trezorcrypto_se_thd89_fido_sign_digest); + +/// def fido_att_sign_digest( +/// digest: bytes, +/// ) -> bytes: +/// """ +/// """ +STATIC mp_obj_t +mod_trezorcrypto_se_thd89_fido_att_sign_digest(mp_obj_t digest) { + mp_buffer_info_t dig = {0}; + mp_get_buffer_raise(digest, &dig, MP_BUFFER_READ); + + if (dig.len != 32) { + mp_raise_ValueError("Invalid length of digest"); + } + uint8_t sign[64]; + if (!se_fido_att_sign_digest((const uint8_t *)dig.buf, sign)) { + mp_raise_ValueError("Signing failed"); + } + return mp_obj_new_bytes(sign, 64); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1( + mod_trezorcrypto_se_thd89_fido_att_sign_digest_obj, + mod_trezorcrypto_se_thd89_fido_att_sign_digest); + 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), @@ -650,6 +960,8 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = { MP_ROM_PTR(&mod_trezorcrypto_se_thd89_aes256_decrypt_obj)}, {MP_ROM_QSTR(MP_QSTR_slip21_node), MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_node_obj)}, + {MP_ROM_QSTR(MP_QSTR_slip21_fido_node), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_slip21_fido_node_obj)}, {MP_ROM_QSTR(MP_QSTR_authorization_set), MP_ROM_PTR(&mod_trezorcrypto_se_thd89_authorization_set_obj)}, {MP_ROM_QSTR(MP_QSTR_authorization_get_type), @@ -662,6 +974,26 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_se_thd89_globals_table[] = { MP_ROM_PTR(&mod_trezorcrypto_se_thd89_read_certificate_obj)}, {MP_ROM_QSTR(MP_QSTR_sign_message), MP_ROM_PTR(&mod_trezorcrypto_se_thd89_sign_message_obj)}, + {MP_ROM_QSTR(MP_QSTR_derive_xmr), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_derive_xmr_obj)}, + {MP_ROM_QSTR(MP_QSTR_derive_xmr_private), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_derive_xmr_private_obj)}, + {MP_ROM_QSTR(MP_QSTR_xmr_get_tx_key), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_xmr_get_tx_key_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_seed), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_seed_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_u2f_register), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_u2f_register_obj)}, + {MP_ROM_QSTR(MP_QSTR_u2f_gen_handle_and_node), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_u2f_gen_handle_and_node_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_u2f_authenticate), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_u2f_authenticate_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_u2f_validate), + MP_ROM_PTR(&mod_trezorcrypto_se_thd89_fido_u2f_validate_obj)}, + {MP_ROM_QSTR(MP_QSTR_fido_sign_digest), + 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)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_se_thd89_globals, mod_trezorcrypto_se_thd89_globals_table); diff --git a/core/embed/extmod/modtrezorio/modtrezorio-nfc.h b/core/embed/extmod/modtrezorio/modtrezorio-nfc.h index 8e4a32855..2da349c9b 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-nfc.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-nfc.h @@ -1,117 +1,80 @@ #include "embed/extmod/trezorobj.h" + +#include "lite_card.h" #include "nfc.h" -/// package: trezorio.__init__ +/// package: trezorio.nfc -/// class NFC: -/// """ -/// """ -typedef struct _mp_obj_NFC_t { - mp_obj_base_t base; -} mp_obj_NFC_t; - -/// def __init__( -/// self, -/// ) -> None: -/// """ -/// """ -STATIC mp_obj_t mod_trezorio_NFC_make_new(const mp_obj_type_t* type, - size_t n_args, size_t n_kw, - const mp_obj_t* args) { - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - mp_obj_NFC_t* o = m_new_obj(mp_obj_NFC_t); - o->base.type = type; - - return MP_OBJ_FROM_PTR(o); -} - -/// def pwr_ctrl(self, on_off: bool) -> int: +/// def pwr_ctrl(on_off: bool) -> bool: /// """ /// Control NFC power. /// """ -STATIC mp_obj_t mod_trezorio_NFC_pwr_ctrl(mp_obj_t self, mp_obj_t on_off) { - return mp_obj_new_int(nfc_pwr_ctl(mp_obj_is_true(on_off))); +STATIC mp_obj_t mod_trezorio_NFC_pwr_ctrl(mp_obj_t on_off) { + return nfc_pwr_ctl(mp_obj_is_true(on_off)) ? mp_const_true : mp_const_false; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_NFC_pwr_ctrl_obj, +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_NFC_pwr_ctrl_obj, mod_trezorio_NFC_pwr_ctrl); -/// def wait_card(self, timeout_ms: int) -> int: +/// def poll_card() -> bool: /// """ -/// Wait for card with timeout. +/// Poll card. /// """ -STATIC mp_obj_t mod_trezorio_NFC_wait_card(mp_obj_t self, mp_obj_t timeout_ms) { - return mp_obj_new_int(nfc_wait_card(mp_obj_get_int(timeout_ms))); +STATIC mp_obj_t mod_trezorio_NFC_poll_card(void) { + return nfc_poll_card() ? mp_const_true : mp_const_false; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_NFC_wait_card_obj, - mod_trezorio_NFC_wait_card); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_NFC_poll_card_obj, + mod_trezorio_NFC_poll_card); -/// def send_recv(self, send: bytearray) -> Tuple[int, bytearray]: +/// def send_recv(apdu: bytes, safe: bool = False) -> tuple[bytes, bytes]: /// """ /// Send receive data through NFC. /// """ -STATIC mp_obj_t mod_trezorio_NFC_send_recv(mp_obj_t self, mp_obj_t send) { - mp_buffer_info_t buf_capdu = {0}; - mp_get_buffer_raise(send, &buf_capdu, MP_BUFFER_READ); - uint8_t buf_rapdu[PN532_InDataExchange_BUFF_SIZE]; - size_t len_rapdu = PN532_InDataExchange_BUFF_SIZE; +STATIC mp_obj_t mod_trezorio_NFC_send_recv(size_t n_args, + const mp_obj_t *args) { + bool safe = n_args > 1 && args[1] == mp_const_true; + mp_buffer_info_t apdu = {0}; + mp_get_buffer_raise(args[0], &apdu, MP_BUFFER_READ); - NFC_STATUS status = nfc_send_recv(buf_capdu.buf, (uint16_t)buf_capdu.len, - buf_rapdu, (uint16_t*)&len_rapdu); + if (apdu.len > 255) { + mp_raise_msg(&mp_type_ValueError, "APDU too long"); + } - mp_obj_t result[2]; - result[0] = mp_obj_new_int(status); - result[1] = mp_obj_new_bytes(buf_rapdu, len_rapdu); + uint8_t sw1sw2[2] = {0}; + uint8_t resp[256] = {0}; + uint16_t resp_len = sizeof(resp); - return mp_obj_new_tuple(2, result); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_NFC_send_recv_obj, - mod_trezorio_NFC_send_recv); + bool success = lite_card_apdu((uint8_t *)apdu.buf, apdu.len, resp, &resp_len, + sw1sw2, safe); -/// def send_recv_single_shot(self, send: bytearray, timeout_ms: int) -> -/// Tuple[int, bytearray]: -/// """ -/// Wait for card, then send receive data through NFC. -/// """ -STATIC mp_obj_t mod_trezorio_NFC_send_recv_single_shot(mp_obj_t self, - mp_obj_t send, - mp_obj_t timeout_ms) { - mp_buffer_info_t buf_capdu = {0}; - mp_get_buffer_raise(send, &buf_capdu, MP_BUFFER_READ); - uint8_t buf_rapdu[PN532_InDataExchange_BUFF_SIZE]; - size_t len_rapdu = PN532_InDataExchange_BUFF_SIZE; - - NFC_STATUS status = - nfc_send_recv_aio(buf_capdu.buf, (uint16_t)buf_capdu.len, buf_rapdu, - (uint16_t*)&len_rapdu, mp_obj_get_int(timeout_ms)); - - mp_obj_t result[2]; - result[0] = mp_obj_new_int(status); - result[1] = mp_obj_new_bytes(buf_rapdu, len_rapdu); - - return mp_obj_new_tuple(2, result); + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + + tuple->items[0] = mp_obj_new_str_copy(&mp_type_bytes, resp, resp_len); + + if (!success) { + sw1sw2[0] = 0x99; + sw1sw2[1] = 0x99; + } + + tuple->items[1] = mp_obj_new_str_copy(&mp_type_bytes, sw1sw2, 2); + + return MP_OBJ_FROM_PTR(tuple); } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_NFC_send_recv_single_shot_obj, - mod_trezorio_NFC_send_recv_single_shot); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_NFC_send_recv_obj, 1, 2, + mod_trezorio_NFC_send_recv); -// class attr -STATIC const mp_rom_map_elem_t mod_trezorio_NFC_locals_dict_table[] = { +STATIC const mp_rom_map_elem_t mod_trezorio_NFC_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_pwr_ctrl), MP_ROM_PTR(&mod_trezorio_NFC_pwr_ctrl_obj)}, - {MP_ROM_QSTR(MP_QSTR_wait_card), - MP_ROM_PTR(&mod_trezorio_NFC_wait_card_obj)}, + {MP_ROM_QSTR(MP_QSTR_poll_card), + MP_ROM_PTR(&mod_trezorio_NFC_poll_card_obj)}, {MP_ROM_QSTR(MP_QSTR_send_recv), MP_ROM_PTR(&mod_trezorio_NFC_send_recv_obj)}, - {MP_ROM_QSTR(MP_QSTR_send_recv_single_shot), - MP_ROM_PTR(&mod_trezorio_NFC_send_recv_single_shot_obj)}, }; -STATIC MP_DEFINE_CONST_DICT(mod_trezorio_NFC_locals_dict, - mod_trezorio_NFC_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(mod_trezorio_NFC_globals, + mod_trezorio_NFC_globals_table); -STATIC const mp_obj_type_t mod_trezorio_NFC_type = { - {&mp_type_type}, - .name = MP_QSTR_NFC, - .make_new = mod_trezorio_NFC_make_new, - .locals_dict = (void*)&mod_trezorio_NFC_locals_dict, +STATIC const mp_obj_module_t mod_trezorcrypto_NFC_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&mod_trezorio_NFC_globals, }; diff --git a/core/embed/extmod/modtrezorio/modtrezorio-poll.h b/core/embed/extmod/modtrezorio/modtrezorio-poll.h index cd262838c..4eed5276f 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-poll.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-poll.h @@ -157,6 +157,33 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, #endif else if (iface == USB_STATE_IFACE) { static bool usb_connect = false, usb_connect_bak = false; + static bool usb_open = false, usb_open_bak = false; + static int counter0 = 0, counter1 = 0; + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + + if (usb_3320_host_connected()) { + counter1 = 0; + counter0++; + if (counter0 > 5) { + usb_open = true; + counter0 = 0; + } + } else { + counter0 = 0; + counter1++; + if (counter1 > 5) { + usb_open = false; + counter1 = 0; + } + } + if (usb_open_bak != usb_open) { + usb_open_bak = usb_open; + tuple->items[0] = mp_const_none; + tuple->items[1] = mp_obj_new_bool(usb_open_bak); + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = MP_OBJ_FROM_PTR(tuple); + return mp_const_true; + } if (is_usb_connected() == sectrue) { usb_connect = true; } else { @@ -164,8 +191,10 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, } if (usb_connect_bak != usb_connect) { usb_connect_bak = usb_connect; + tuple->items[0] = mp_obj_new_bool(usb_connect_bak); + tuple->items[1] = mp_const_none; ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); - ret->items[1] = mp_obj_new_bool(usb_connect_bak); + ret->items[1] = MP_OBJ_FROM_PTR(tuple); return mp_const_true; } } else if (iface == FINGERPRINT_IFACE) { diff --git a/core/embed/extmod/modtrezorio/modtrezorio-usb.h b/core/embed/extmod/modtrezorio/modtrezorio-usb.h index 2b2269696..1c90cea2a 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-usb.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-usb.h @@ -224,7 +224,7 @@ STATIC mp_obj_t mod_trezorio_USB_open(mp_obj_t self, } // Start the USB stack - usb_start(); + // usb_start(); o->state = USB_OPENED; // If we found any VCP interfaces, use the last one for stdio, @@ -290,6 +290,22 @@ STATIC mp_obj_t mod_trezorio_USB_connected(mp_obj_t self) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB_connected_obj, mod_trezorio_USB_connected); +/// def connect_ctrl(self, state :bool) -> None: +/// """ +/// Control usb connect. +/// """ +STATIC mp_obj_t mod_trezorio_USB_connect_ctrl(mp_obj_t self, mp_obj_t state) { + if (state == mp_const_true) { + usb_start(); + } else { + usb_stop(); + } + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_USB_connect_ctrl_obj, + mod_trezorio_USB_connect_ctrl); + /// def state(self) -> int: /// """ /// Get USB state. @@ -311,6 +327,8 @@ STATIC const mp_rom_map_elem_t mod_trezorio_USB_locals_dict_table[] = { {MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&mod_trezorio_USB_connected_obj)}, {MP_ROM_QSTR(MP_QSTR_state), MP_ROM_PTR(&mod_trezorio_USB_state_obj)}, + {MP_ROM_QSTR(MP_QSTR_connect_ctrl), + MP_ROM_PTR(&mod_trezorio_USB_connect_ctrl_obj)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorio_USB_locals_dict, mod_trezorio_USB_locals_dict_table); diff --git a/core/embed/extmod/modtrezorio/modtrezorio.c b/core/embed/extmod/modtrezorio/modtrezorio.c index 50ba40703..cc8d54751 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio.c +++ b/core/embed/extmod/modtrezorio/modtrezorio.c @@ -107,7 +107,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_USB_STATE), MP_ROM_INT(USB_STATE_IFACE)}, {MP_ROM_QSTR(MP_QSTR_FINGERPRINT_STATE), MP_ROM_INT(FINGERPRINT_IFACE)}, {MP_ROM_QSTR(MP_QSTR_MOTOR), MP_ROM_PTR(&mod_trezorio_MOTOR_type)}, - {MP_ROM_QSTR(MP_QSTR_NFC), MP_ROM_PTR(&mod_trezorio_NFC_type)}, + {MP_ROM_QSTR(MP_QSTR_nfc), MP_ROM_PTR(&mod_trezorcrypto_NFC_module)}, #elif defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R {MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_INT(BUTTON_IFACE)}, {MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED), diff --git a/core/embed/extmod/modtrezorui/mipi_lcd.c b/core/embed/extmod/modtrezorui/mipi_lcd.c index 4c91b8ec6..b514e19ab 100644 --- a/core/embed/extmod/modtrezorui/mipi_lcd.c +++ b/core/embed/extmod/modtrezorui/mipi_lcd.c @@ -446,9 +446,9 @@ void st7701_init_sequence(void) { 0x13); // CND2BKxSEL select bank3 with cmd2 st7701_dsi(0xe8, 0x00, 0x0e); // ? st7701_dsi(0xff, 0x77, 0x01, 0x00, 0x00, - 0x00); // CND2BKxSEL select bank0 without cmd2 + 0x00); // CND2BKxSEL select bank0 without cmd2 st7701_dsi(MIPI_DCS_EXIT_SLEEP_MODE); // SLPOUT - HAL_Delay(120); // delay + HAL_Delay(120); // delay st7701_dsi(0xff, 0x77, 0x01, 0x00, 0x00, 0x13); // CND2BKxSEL select bank3 with cmd2 st7701_dsi(0xe8, 0x00, 0x0c); // ? @@ -462,7 +462,7 @@ void st7701_init_sequence(void) { st7701_dsi(MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2C); st7701_dsi(MIPI_DCS_SET_PIXEL_FORMAT, 0x50); st7701_dsi(MIPI_DCS_SET_DISPLAY_ON); // DISPON - HAL_Delay(20); // delay + HAL_Delay(20); // delay st7701_dsi(0xff, 0x77, 0x01, 0x00, 0x00, 0x10); // CND2BKxSEL select bank1 with cmd2 st7701_dsi(0xe5, 0x00, 0x00); // ? @@ -474,6 +474,19 @@ int display_backlight(int val) { #if TREZOR_MODEL == 1 val = 255; #endif + if (val == 0 && DISPLAY_BACKLIGHT != 0) { + // clock down + __HAL_DSI_DISABLE(&hlcd_dsi); + __HAL_LTDC_DISABLE(&hlcd_ltdc); + // lcd reset + HAL_GPIO_WritePin(LCD_RESET_GPIO_PORT, LCD_RESET_PIN, GPIO_PIN_RESET); + } else if (val != 0 && DISPLAY_BACKLIGHT == 0) { + HAL_GPIO_WritePin(LCD_RESET_GPIO_PORT, LCD_RESET_PIN, GPIO_PIN_SET); + HAL_Delay(30); + __HAL_DSI_ENABLE(&hlcd_dsi); + __HAL_LTDC_ENABLE(&hlcd_ltdc); + st7701_init_sequence(); + } if (DISPLAY_BACKLIGHT != val && val >= 0 && val <= 255) { DISPLAY_BACKLIGHT = val; TIM1->CCR1 = (LED_PWM_TIM_PERIOD - 1) * val / 255; diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 4df5dd43a..19caa9671 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -561,19 +561,27 @@ STATIC mp_obj_t mod_trezorutils_bytewords_decode(mp_obj_t type, STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorutils_bytewords_decode_obj, mod_trezorutils_bytewords_decode); -/// def enter_lowpower(restart: bool, seconds: int) -> None: +/// def enter_lowpower( +/// restart: bool, +/// seconds: int, +/// wake_up: bool = False +/// ) ->None: /// """ /// Enter lowpower mode. /// """ -STATIC mp_obj_t mod_trezorutils_enter_lowpower(mp_obj_t restart, - mp_obj_t seconds) { - int val_ms = mp_obj_get_int(seconds); - bool val_restart = mp_obj_is_true(restart); - enter_stop_mode(val_restart, val_ms / 1000); +STATIC mp_obj_t mod_trezorutils_enter_lowpower(size_t n_args, + const mp_obj_t *args) { + bool val_restart = mp_obj_is_true(args[0]); + int val_ms = mp_obj_get_int(args[1]); + + bool wake_up = n_args > 2 && args[2] == mp_const_true; + + enter_stop_mode(val_restart, val_ms / 1000, wake_up); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorutils_enter_lowpower_obj, - mod_trezorutils_enter_lowpower); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorutils_enter_lowpower_obj, + 2, 3, + mod_trezorutils_enter_lowpower); STATIC mp_obj_str_t mod_trezorutils_revision_obj = { {&mp_type_bytes}, 0, sizeof(SCM_REVISION) - 1, (const byte *)SCM_REVISION}; diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index 8b2e0036d..3f87a45c8 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -60,6 +60,7 @@ #include "fingerprint.h" #include "mipi_lcd.h" #include "motor.h" +#include "nfc.h" #include "qspi_flash.h" #include "se_thd89.h" #include "spi_legacy.h" @@ -173,6 +174,8 @@ int main(void) { timer_init(); fingerprint_init(); + nfc_init(); + device_test(false); device_burnin_test(false); diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index bb166e04b..2b221b78f 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -9,8 +9,8 @@ #define FIX_VERSION_BUILD 99 #define ONEKEY_VERSION_MAJOR 4 -#define ONEKEY_VERSION_MINOR 9 -#define ONEKEY_VERSION_PATCH 3 +#define ONEKEY_VERSION_MINOR 10 +#define ONEKEY_VERSION_PATCH 0 #define ONEKEY_VERSION_BUILD 0 #define _STR(X) #X diff --git a/core/embed/lite_card/cmac.c b/core/embed/lite_card/cmac.c new file mode 100644 index 000000000..12645bbce --- /dev/null +++ b/core/embed/lite_card/cmac.c @@ -0,0 +1,286 @@ +#include + +/******** SBOX Table *********/ +const unsigned char sbox_table[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, + 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, + 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, + 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, + 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, + 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, + 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, + 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, + 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, + 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, + 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, + 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, + 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, + 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, + 0xb0, 0x54, 0xbb, 0x16}; + +/* For CMAC Calculation */ +unsigned char const_Rb[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87}; + +unsigned char const_Zero[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +void xor_128(unsigned char *a, unsigned char *b, unsigned char *out) { + int i; + for (i = 0; i < 16; i++) { + out[i] = a[i] ^ b[i]; + } +} + +void xor_32(unsigned char *a, unsigned char *b, unsigned char *out) { + int i; + for (i = 0; i < 4; i++) { + out[i] = a[i] ^ b[i]; + } +} + +unsigned char sbox(unsigned char a) { return sbox_table[(int)a]; } + +void next_key(unsigned char *key, int round) { + unsigned char rcon; + unsigned char sbox_key[4]; + unsigned char rcon_table[12] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, + 0x40, 0x80, 0x1b, 0x36, 0x36, 0x36}; + + sbox_key[0] = sbox(key[13]); + sbox_key[1] = sbox(key[14]); + sbox_key[2] = sbox(key[15]); + sbox_key[3] = sbox(key[12]); + rcon = rcon_table[round]; + + xor_32(&key[0], sbox_key, &key[0]); + key[0] = key[0] ^ rcon; + + xor_32(&key[4], &key[0], &key[4]); + xor_32(&key[8], &key[4], &key[8]); + xor_32(&key[12], &key[8], &key[12]); +} + +void byte_sub(unsigned char *in, unsigned char *out) { + int i; + for (i = 0; i < 16; i++) { + out[i] = sbox(in[i]); + } +} + +void shift_row(unsigned char *in, unsigned char *out) { + out[0] = in[0]; + out[1] = in[5]; + out[2] = in[10]; + out[3] = in[15]; + out[4] = in[4]; + out[5] = in[9]; + out[6] = in[14]; + out[7] = in[3]; + out[8] = in[8]; + out[9] = in[13]; + out[10] = in[2]; + out[11] = in[7]; + out[12] = in[12]; + out[13] = in[1]; + out[14] = in[6]; + out[15] = in[11]; +} + +void mix_column(unsigned char *in, unsigned char *out) { + int i; + unsigned char add1b[4]; + unsigned char add1bf7[4]; + unsigned char rotl[4]; + unsigned char swap_halfs[4]; + unsigned char andf7[4]; + unsigned char rotr[4]; + unsigned char temp[4]; + unsigned char tempb[4]; + + for (i = 0; i < 4; i++) { + if ((in[i] & 0x80) == 0x80) + add1b[i] = 0x1b; + else + add1b[i] = 0x00; + } + + swap_halfs[0] = in[2]; /* Swap halfs */ + swap_halfs[1] = in[3]; + swap_halfs[2] = in[0]; + swap_halfs[3] = in[1]; + + rotl[0] = in[3]; /* Rotate left 8 bits */ + rotl[1] = in[0]; + rotl[2] = in[1]; + rotl[3] = in[2]; + + andf7[0] = in[0] & 0x7f; + andf7[1] = in[1] & 0x7f; + andf7[2] = in[2] & 0x7f; + andf7[3] = in[3] & 0x7f; + + for (i = 3; i > 0; i--) /* logical shift left 1 bit */ + { + andf7[i] = andf7[i] << 1; + if ((andf7[i - 1] & 0x80) == 0x80) { + andf7[i] = (andf7[i] | 0x01); + } + } + andf7[0] = andf7[0] << 1; + andf7[0] = andf7[0] & 0xfe; + + xor_32(add1b, andf7, add1bf7); + + xor_32(in, add1bf7, rotr); + + temp[0] = rotr[0]; /* Rotate right 8 bits */ + rotr[0] = rotr[1]; + rotr[1] = rotr[2]; + rotr[2] = rotr[3]; + rotr[3] = temp[0]; + xor_32(add1bf7, rotr, temp); + xor_32(swap_halfs, rotl, tempb); + xor_32(temp, tempb, out); +} + +/****************************************/ +/* AES__128() */ +/* Performs a 128 bit AES encrypt with */ +/* 128 bit data. */ +/****************************************/ +void AES__128(unsigned char *key, unsigned char *data, + unsigned char *ciphertext) { + int round; + int i; + unsigned char intermediatea[16]; + unsigned char intermediateb[16]; + unsigned char round_key[16]; + + for (i = 0; i < 16; i++) round_key[i] = key[i]; + + for (round = 0; round < 11; round++) { + if (round == 0) { + xor_128(round_key, data, ciphertext); + next_key(round_key, round); + } + + else if (round == 10) { + byte_sub(ciphertext, intermediatea); + shift_row(intermediatea, intermediateb); + xor_128(intermediateb, round_key, ciphertext); + } else /* 1 - 9 */ + { + byte_sub(ciphertext, intermediatea); + shift_row(intermediatea, intermediateb); + mix_column(&intermediateb[0], &intermediatea[0]); + mix_column(&intermediateb[4], &intermediatea[4]); + mix_column(&intermediateb[8], &intermediatea[8]); + mix_column(&intermediateb[12], &intermediatea[12]); + xor_128(intermediatea, round_key, ciphertext); + next_key(round_key, round); + } + } +} + +/* CMAC-AES Generation Function */ +void leftshift_onebit(unsigned char *input, unsigned char *output) { + int i; + unsigned char overflow = 0; + + for (i = 15; i >= 0; i--) { + output[i] = input[i] << 1; + output[i] |= overflow; + overflow = (input[i] & 0x80) ? 1 : 0; + } + return; +} + +void generate_subkey(unsigned char *key, unsigned char *K1, unsigned char *K2) { + unsigned char L[16]; + unsigned char Z[16]; + unsigned char tmp[16]; + int i; + + for (i = 0; i < 16; i++) Z[i] = 0; + + AES__128(key, Z, L); + + if ((L[0] & 0x80) == 0) { /* If MSB(L) = 0, then K1 = L << 1 */ + leftshift_onebit(L, K1); + } else { /* Else K1 = ( L << 1 ) (+) Rb */ + leftshift_onebit(L, tmp); + xor_128(tmp, const_Rb, K1); + } + + if ((K1[0] & 0x80) == 0) { + leftshift_onebit(K1, K2); + } else { + leftshift_onebit(K1, tmp); + xor_128(tmp, const_Rb, K2); + } + return; +} + +void padding(unsigned char *lastb, unsigned char *pad, int length) { + int j; + + /* original last block */ + for (j = 0; j < 16; j++) { + if (j < length) { + pad[j] = lastb[j]; + } else if (j == length) { + pad[j] = 0x80; + } else { + pad[j] = 0x00; + } + } +} + +void AES128_CMAC(unsigned char *key, unsigned char *input, int length, + unsigned char *mac) { + unsigned char X[16], Y[16], M_last[16], padded[16]; + unsigned char K1[16], K2[16]; + int n, i, flag; + generate_subkey(key, K1, K2); + + n = (length + 15) / 16; /* n is number of rounds */ + if (n == 0) { + n = 1; + flag = 0; + } else { + if ((length % 16) == 0) { /* last block is a complete block */ + flag = 1; + } else { /* last block is not complete block */ + flag = 0; + } + } + + if (flag) { /* last block is complete block */ + xor_128(&input[16 * (n - 1)], K1, M_last); + } else { + padding(&input[16 * (n - 1)], padded, length % 16); + xor_128(padded, K2, M_last); + } + + for (i = 0; i < 16; i++) X[i] = 0; + + for (i = 0; i < n - 1; i++) { + xor_128(X, &input[16 * i], Y); /* Y := Mi (+) X */ + AES__128(key, Y, X); /* X := AES-128(KEY, Y); */ + } + + xor_128(X, M_last, Y); + AES__128(key, Y, X); + + for (i = 0; i < 16; i++) { + mac[i] = X[i]; + } +} diff --git a/core/embed/lite_card/cmac.h b/core/embed/lite_card/cmac.h new file mode 100644 index 000000000..b6b90aed4 --- /dev/null +++ b/core/embed/lite_card/cmac.h @@ -0,0 +1,7 @@ +#ifndef __CMAC_H__ +#define __CMAC_H__ + +void AES128_CMAC(unsigned char *key, unsigned char *input, int length, + unsigned char *mac); + +#endif diff --git a/core/embed/lite_card/lite_card.c b/core/embed/lite_card/lite_card.c new file mode 100644 index 000000000..bdb187a13 --- /dev/null +++ b/core/embed/lite_card/lite_card.c @@ -0,0 +1,250 @@ +#include + +#include "aes/aes.h" +#include "cmac.h" +#include "lite_card.h" +#include "nfc.h" +#include "scp03.h" +#include "scp11.h" + +static scp11_context scp11_ctx = {0}; +static scp03_context scp03_ctx = {0}; + +static bool lite_card_get_sd_certificate(uint8_t* cert, uint16_t* cert_len) { + uint8_t apdu_get_sd_certificates[] = {0x80, 0xca, 0xbf, 0x21, 0x06, 0xa6, + 0x04, 0x83, 0x02, 0x15, 0x18}; + uint8_t sw1sw2[2] = {0}; + if (!nfc_send_recv(apdu_get_sd_certificates, sizeof(apdu_get_sd_certificates), + cert, cert_len, sw1sw2)) { + return false; + } + if (sw1sw2[0] != 0x90 || sw1sw2[1] != 0x00) { + return false; + } + + return true; +} + +static bool lite_card_send_device_certificate(uint8_t* cert, uint8_t cert_len) { + uint8_t apdu_send_device_cert[256] = {0x80, 0x2a, 0x18, 0x10, 0x00}; + uint8_t resp[2] = {0}; + uint16_t resp_len = sizeof(resp); + uint8_t sw1sw2[2] = {0}; + apdu_send_device_cert[4] = cert_len; + memcpy(apdu_send_device_cert + 5, cert, cert_len); + if (!nfc_send_recv(apdu_send_device_cert, cert_len + 5, resp, &resp_len, + sw1sw2)) { + return false; + } + if (sw1sw2[0] != 0x90 || sw1sw2[1] != 0x00) { + return false; + } + return true; +} + +static bool lite_card_mutual_authentication(scp11_context* scp11_ctx) { + uint8_t apdu_mutual_auth[256] = {0x80, 0x82, 0x18, 0x15, 0x00}; + uint8_t sw1sw2[2] = {0}; + apdu_mutual_auth[4] = 251; + scp11_get_mutual_auth_data(apdu_mutual_auth + 5, &apdu_mutual_auth[4], + *scp11_ctx); + if (!nfc_send_recv(apdu_mutual_auth, apdu_mutual_auth[4] + 5, + scp11_ctx->response_msg.data, + &scp11_ctx->response_msg.data_len, sw1sw2)) { + return false; + } + if (sw1sw2[0] != 0x90 || sw1sw2[1] != 0x00) { + return false; + } + return true; +} + +bool lite_card_send_safeapdu(uint8_t* apdu, uint16_t apdu_len, + uint8_t* response, uint16_t* response_len, + uint8_t* sw1sw2) { + uint8_t mac_data[256 + 16], data_buffer[256], enc_data[256]; + uint16_t enc_data_len = 0, buffer_len = sizeof(data_buffer); + uint8_t mac[16]; + memcpy(data_buffer, apdu, apdu_len); + + if (apdu_len == 4) { + data_buffer[4] = 0; + apdu_len = 5; + } + + // encrypt data + scp03_generate_icv(scp11_ctx.session_key.s_enc, &scp03_ctx, true); + scp03_encrypt(scp11_ctx.session_key.s_enc, scp03_ctx.icv, data_buffer + 5, + apdu_len - 5, enc_data, &enc_data_len); + + // gen cmac + // cla + data_buffer[0] |= 4; + // enc_data_len + mac_len + data_buffer[4] = enc_data_len + 8; + + memcpy(mac_data, scp03_ctx.mac_chain_value, + sizeof(scp03_ctx.mac_chain_value)); + memcpy(mac_data + 16, data_buffer, 5); + memcpy(mac_data + 21, enc_data, enc_data_len); + + AES128_CMAC(scp11_ctx.session_key.s_mac, mac_data, enc_data_len + 5 + 16, + mac); + + memcpy(data_buffer + 5, enc_data, enc_data_len); + memcpy(data_buffer + enc_data_len + 5, mac, 8); + + memcpy(scp03_ctx.mac_chain_value, mac, sizeof(scp03_ctx.mac_chain_value)); + + if (!nfc_send_recv(data_buffer, enc_data_len + 5 + 8, data_buffer, + &buffer_len, sw1sw2)) { + return false; + } + + if (buffer_len == 0) { + if (sw1sw2[0] == 0x90 && sw1sw2[1] == 0x00) { + return false; + } + if (sw1sw2[0] == 0x63) { + return false; + } + return true; + } + + // enc data len + mac len + if (buffer_len < AES_BLOCK_SIZE + SCP03_MAC_SIZE) { + return false; + } + + if ((buffer_len - SCP03_MAC_SIZE) % AES_BLOCK_SIZE != 0) { + return false; + } + + // verify mac + memcpy(mac_data, scp03_ctx.mac_chain_value, + sizeof(scp03_ctx.mac_chain_value)); + memcpy(mac_data + 16, data_buffer, buffer_len - 8); + memcpy(mac_data + 16 + buffer_len - 8, sw1sw2, 2); + AES128_CMAC(scp11_ctx.session_key.s_rmac, mac_data, buffer_len + 8 + 2, mac); + + if (memcmp(mac, data_buffer + buffer_len - 8, 8) != 0) { + return false; + } + + buffer_len -= 8; + + // decrypt data + scp03_generate_icv(scp11_ctx.session_key.s_enc, &scp03_ctx, false); + if (!scp03_decrypt(scp11_ctx.session_key.s_enc, scp03_ctx.icv, data_buffer, + buffer_len, enc_data, &buffer_len)) { + return false; + } + memcpy(response, enc_data, buffer_len); + *response_len = buffer_len; + return true; +} + +bool lite_card_open_secure_channel(void) { + if (scp11_get_secure_channel_status(&scp11_ctx)) { + return true; + } + scp11_init(&scp11_ctx); + + scp11_ctx.sd_cert.raw_len = sizeof(scp11_ctx.sd_cert.raw); + if (!lite_card_get_sd_certificate(scp11_ctx.sd_cert.raw, + &scp11_ctx.sd_cert.raw_len)) { + return false; + } + if (!scp11_certificate_parse_and_verify(scp11_ctx.sd_cert.raw, + scp11_ctx.sd_cert.raw_len, + &scp11_ctx.sd_cert)) { + return false; + } + if (!lite_card_send_device_certificate(scp11_ctx.oce_cert.raw, + scp11_ctx.oce_cert.raw_len)) { + return false; + } + if (!lite_card_mutual_authentication(&scp11_ctx)) { + return false; + } + + if (!scp11_open_secure_channel(&scp11_ctx)) { + return false; + } + + scp03_init(&scp03_ctx, scp11_ctx.response_msg.lv_GPC_TLV_MA_RECEIPT.value); + + return true; +} + +bool lite_card_apdu(uint8_t* apdu, uint16_t apdu_len, uint8_t* response, + uint16_t* response_len, uint8_t* sw1sw2, bool safe) { + if (memcmp(apdu, "\x00\xa4\x04\x00", 4) == 0) { + scp11_close_secure_channel(&scp11_ctx); + } + + if (safe) { + if (!lite_card_open_secure_channel()) { + return false; + } + return lite_card_send_safeapdu(apdu, apdu_len, response, response_len, + sw1sw2); + } + return nfc_send_recv(apdu, apdu_len, response, response_len, sw1sw2); +} + +bool lite_card_safe_apdu_test(void) { + uint8_t select_mf[] = {0x00, 0xa4, 0x04, 0x00}; + uint8_t select_old_aid[] = {0x00, 0xa4, 0x04, 0x00, 0x08, 0xD1, 0x56, + 0x00, 0x01, 0x32, 0x83, 0x40, 0x01}; + uint8_t reset_card[] = {0x80, 0xcb, 0x80, 0x00, 0x05, + 0xdf, 0xfe, 0x02, 0x82, 0x05}; + uint8_t check_pin[] = {0x80, 0xcb, 0x80, 0x00, 0x05, + 0xdf, 0xff, 0x02, 0x81, 0x05}; + uint8_t set_pin[] = {0x80, 0xcb, 0x80, 0x00, 0x0e, 0xdf, 0xfe, + 0x0b, 0x82, 0x04, 0x08, 0x00, 0x06, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36}; + uint8_t verify_pin[] = {0x80, 0x20, 0x00, 0x00, 0x07, 0x06, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36}; + uint8_t resp[256]; + uint16_t resp_len = sizeof(resp); + uint8_t sw1sw2[2] = {0}; + + lite_card_apdu(select_mf, 4, resp, &resp_len, sw1sw2, false); + + resp_len = sizeof(resp); + if (!lite_card_apdu(reset_card, sizeof(reset_card), resp, &resp_len, sw1sw2, + true)) { + return false; + } + resp_len = sizeof(resp); + if (!lite_card_apdu(check_pin, sizeof(check_pin), resp, &resp_len, sw1sw2, + true)) { + return false; + } + + resp_len = sizeof(resp); + if (!lite_card_apdu(set_pin, sizeof(set_pin), resp, &resp_len, sw1sw2, + true)) { + return false; + } + + resp_len = sizeof(resp); + if (!lite_card_apdu(check_pin, sizeof(check_pin), resp, &resp_len, sw1sw2, + true)) { + return false; + } + if (resp[0] != 0) { + return false; + } + + lite_card_apdu(select_old_aid, sizeof(select_old_aid), resp, &resp_len, + sw1sw2, false); + + resp_len = sizeof(resp); + if (!lite_card_apdu(verify_pin, sizeof(verify_pin), resp, &resp_len, sw1sw2, + true)) { + return false; + } + return true; +} diff --git a/core/embed/lite_card/lite_card.h b/core/embed/lite_card/lite_card.h new file mode 100644 index 000000000..f405dee68 --- /dev/null +++ b/core/embed/lite_card/lite_card.h @@ -0,0 +1,12 @@ +#ifndef _LITE_CARD_H_ +#define _LITE_CARD_H_ + +#include +#include + +#define CARD_AID_MAX_LEN 32 + +bool lite_card_select_aid(uint8_t* aid, uint8_t aid_len); +bool lite_card_apdu(uint8_t* apdu, uint16_t apdu_len, uint8_t* response, + uint16_t* response_len, uint8_t* sw1sw2, bool safe); +#endif diff --git a/core/embed/lite_card/scp03.c b/core/embed/lite_card/scp03.c new file mode 100644 index 000000000..298c5dda3 --- /dev/null +++ b/core/embed/lite_card/scp03.c @@ -0,0 +1,82 @@ + +#include + +#include "aes/aes.h" +#include "scp03.h" + +#define SWAP_UINT32(x) \ + (((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | \ + (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000) + +void scp03_generate_icv(uint8_t *aes_key, scp03_context *scp03_ctx, bool send) { + aes_encrypt_ctx ctxe; + uint8_t iv[AES_BLOCK_SIZE] = {0}; + uint8_t counter[AES_BLOCK_SIZE] = {0}; + uint32_t counter_temp; + + aes_init(); + + if (send) { + counter_temp = SWAP_UINT32(scp03_ctx->counter); + memcpy(counter + 12, &counter_temp, 4); + } else { + scp03_ctx->counter++; + counter_temp = SWAP_UINT32(scp03_ctx->counter); + memcpy(counter + 12, &counter_temp, 4); + counter[0] = 0x80; + } + aes_encrypt_key128(aes_key, &ctxe); + + aes_cbc_encrypt(counter, scp03_ctx->icv, AES_BLOCK_SIZE, iv, &ctxe); + + return; +} + +void scp03_encrypt(uint8_t *aes_key, uint8_t *icv, uint8_t *data, + uint16_t data_len, uint8_t *output, uint16_t *output_len) { + aes_encrypt_ctx ctxe; + + uint8_t pad_len = AES_BLOCK_SIZE - (data_len % AES_BLOCK_SIZE); + memset(data + data_len, 0, pad_len); + data[data_len] = 0x80; + + data_len += pad_len; + + aes_encrypt_key128(aes_key, &ctxe); + + aes_cbc_encrypt(data, output, data_len, icv, &ctxe); + + *output_len = data_len; + + return; +} + +bool scp03_decrypt(uint8_t *aes_key, uint8_t *icv, uint8_t *data, + uint16_t data_len, uint8_t *output, uint16_t *output_len) { + aes_decrypt_ctx ctxd; + + aes_decrypt_key128(aes_key, &ctxd); + + aes_cbc_decrypt(data, output, data_len, icv, &ctxd); + + for (uint8_t i = 0; i < 16; i++) { + if (output[data_len - 1 - i] == 0x80) { + *output_len = data_len - i - 1; + return true; + } else if (output[data_len - 1 - i] != 0) { + *output_len = 0; + return false; + } + } + + return false; +} + +void scp03_init(scp03_context *scp03_ctx, uint8_t *mac_chain_value) { + memset(scp03_ctx, 0, sizeof(scp03_context)); + + scp03_ctx->counter = 1; + + memcpy(scp03_ctx->mac_chain_value, mac_chain_value, + sizeof(scp03_ctx->mac_chain_value)); +} \ No newline at end of file diff --git a/core/embed/lite_card/scp03.h b/core/embed/lite_card/scp03.h new file mode 100644 index 000000000..9a27e17de --- /dev/null +++ b/core/embed/lite_card/scp03.h @@ -0,0 +1,21 @@ +#ifndef _SCP03_H_ +#define _SCP03_H_ + +#include +#include + +#define SCP03_MAC_SIZE 8 + +typedef struct { + uint32_t counter; + uint8_t icv[16]; + uint8_t mac_chain_value[16]; +} scp03_context; +void scp03_generate_icv(uint8_t *aes_key, scp03_context *scp03_ctx, bool send); +void scp03_encrypt(uint8_t *aes_key, uint8_t *icv, uint8_t *data, + uint16_t data_len, uint8_t *output, uint16_t *output_len); +bool scp03_decrypt(uint8_t *aes_key, uint8_t *icv, uint8_t *data, + uint16_t data_len, uint8_t *output, uint16_t *output_len); +void scp03_init(scp03_context *scp03_ctx, uint8_t *mac_chain_value); + +#endif diff --git a/core/embed/lite_card/scp11.c b/core/embed/lite_card/scp11.c new file mode 100644 index 000000000..ec65a69bf --- /dev/null +++ b/core/embed/lite_card/scp11.c @@ -0,0 +1,488 @@ + +#include + +#include "cmac.h" +#include "ecdsa.h" +#include "nist256p1.h" +#include "rand.h" +#include "scp11.h" +#include "sha2.h" + +#include "se_thd89.h" + +const static uint8_t sd_cert_verify_pubkey[65] = { + "\x04\x21\x46\xE7\x94\x1C\x2E\xBE\xBD\xC4\x7A\x1B\xFA\x52\x9A\x81\x5F\x2C" + "\x3C\x55\x75\x78\x62\xC3\x78\x23\x60\x21\xD4\x99\xC3\x2E\xD5\x2F\x93\x54" + "\xB7\x0D\x81\x38\xAD\x52\x74\x76\xB7\x26\x21\x2B\x97\xD6\x78\x77\xD5\x5F" + "\x45\x9C\xB7\xE2\xD5\xF8\x5E\xF9\xD7\x93\x02"}; + +const static uint8_t device_certificate[] = { + "\x7F\x21\x81\xDB\x93\x10\x43\x45\x52\x54\x5F\x4F\x43\x45\x5F\x45\x43\x4B" + "\x41\x30\x30\x31\x42\x0D\x6A\x75\x62\x69\x74\x65\x72\x77\x61\x6C\x6C\x65" + "\x74\x5F\x20\x0D\x6A\x75\x62\x69\x74\x65\x72\x77\x61\x6C\x6C\x65\x74\x95" + "\x02\x00\x80\x5F\x25\x04\x20\x20\x05\x25\x5F\x24\x04\x20\x25\x05\x24\x53" + "\x00\xBF\x20\x00\x7F\x49\x46\xB0\x41\x04\x08\xCC\xB4\x9E\xB9\x10\x57\x28" + "\x75\x72\xE6\x87\x06\xF3\xCB\x4C\x27\xCE\x19\xAD\x94\xC4\x0B\x2A\x37\xC5" + "\x94\xE5\x1B\xC0\x9E\xAD\x96\x34\x94\x66\x30\x6C\x58\x63\xF6\xE8\xBE\xB3" + "\xF0\xEA\x99\x71\x18\x48\x16\x32\x01\xBF\xE8\xC7\x88\x43\x3D\x45\x81\x64" + "\x69\xE5\xF0\x01\x00\x5F\x37\x47\x30\x45\x02\x21\x00\x87\x9E\xEB\x7E\xE0" + "\x96\x2B\x44\xBD\x3D\x87\x01\x16\x1A\x26\x34\x77\xCC\x2F\x08\xD7\x68\x1A" + "\xF8\x54\x6F\xBC\x17\xEB\x3E\x99\x65\x02\x20\x16\x00\xFA\x7A\x74\x1B\x0E" + "\xFE\x7C\x14\x3D\x73\x71\x3E\x80\x31\xAF\xBB\x3F\x1C\x0B\x6D\x69\x04\x80" + "\x20\xD2\x73\xE4\x8A\xAF\x5E"}; + +// const scp11_shared_info_data scp11_shared_info_data_default = { +// .scp_id_param = {0x11, 0x07}, +// .key_usage = 0x3c, +// .key_type = 0x88, +// .key_length = 0x10, +// //"pro-lite" +// .host_id = {0x70,0x72,0x6F,0x2D,0x6C,0x69,0x74,0x65} +// }; + +const uint8_t shared_info_buffer[] = {GPC_TLV_SHAREDINFO_SCP_ID_PARAM, + 0x02, + 0x11, + 0x07, + GPC_TLV_SHAREDINFO_KEYUSAGE, + 0x01, + 0x3c, + GPC_TLV_SHAREDINFO_KEYTYPE, + 0x01, + 0x88, + GPC_TLV_SHAREDINFO_KEYLENGTH, + 0x01, + 0x10, + GPC_TLV_SHAREDINFO_HOSTID, + 0x08, + 0x70, + 0x72, + 0x6F, + 0x2D, + 0x6C, + 0x69, + 0x74, + 0x65}; + +const uint8_t shared_info_data_buffer[] = {0x3c, 0x88, 0x10, 0x08, 0x70, 0x72, + 0x6F, 0x2D, 0x6C, 0x69, 0x74, 0x65}; + +#define SET_CERT_TLV_FIELD(cert, tag, val, length) \ + do { \ + cert->lv_##tag.value = val; \ + cert->lv_##tag.len = length; \ + } while (0) + +#define INIT_TLV_FIELD(tlv, tag_value, buf, buf_len) \ + do { \ + tlv.tag = tag_value; \ + tlv.length = buf_len; \ + tlv.value = buf; \ + } while (0) + +bool scp11_get_pubkey(uint8_t *input, uint8_t inpue_len, uint8_t *pubkey) { + uint8_t *p = input; + uint8_t *end = input + inpue_len; + uint16_t tag; + uint16_t value_len = 0; + uint16_t offset = 0; + + if (inpue_len == 0) { + return false; + } + + while (p < end) { + if (!tlv_parse_tag(p, end, &tag, &offset)) { + return false; + } + p += offset; + + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + + p += offset; + + switch (tag) { + case GPC_TLV_PK_Q: + if (value_len != 65) return false; + memcpy(pubkey, p, value_len); + break; + case GPC_TLV_PK_PARAM: + break; + default: + break; + } + p += value_len; + } + + return true; +} + +static bool scp11_get_signature(uint8_t *input, uint8_t inpue_len, + uint8_t *signature) { + uint8_t *p = input; + uint8_t *end = input + inpue_len; + uint16_t value_len = 0; + uint16_t offset = 0; + + if (inpue_len == 0) { + return false; + } + + // der signature + if (*p != 0x30) { + return false; + } + p++; + + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + + p += offset; + + for (int i = 0; i < 2; i++) { + if (*p != 0x02) { + return false; + } + p++; + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + p += offset; + + if (value_len == 33 && *p == 0x00) { + p++; + value_len--; + } + + if (value_len != 32) { + return false; + } + memcpy(signature + i * 32, p, value_len); + p += value_len; + } + + return true; +} + +bool scp11_certificate_parse_and_verify(uint8_t *cert_raw, uint16_t cert_len, + scp11_certificate *cert) { + // Parse certificate + uint16_t tag; + uint8_t tag_len, len_len; + uint16_t value_len = 0; + uint16_t offset = 0; + + if (cert_len == 0) { + return false; + } + + uint8_t *p = cert_raw; + uint8_t *end = cert_raw + cert_len; + + SHA256_CTX ctx = {0}; + uint8_t digest[32] = {0}, signature[64] = {0}; + sha256_Init(&ctx); + + while (p < end) { + if (!tlv_parse_tag(p, end, &tag, &offset)) { + return false; + } + p += offset; + tag_len = offset; + + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + + p += offset; + len_len = offset; + + bool is_nested = tag_len == 1 ? tag & 0x20 : (tag >> 8) & 0x20; + + if (is_nested) { + switch (tag) { + case GPC_TLV_SCP11CRT_ENTITY: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_ENTITY, p, value_len); + break; + case GPC_TLV_SCP11CRT_PUBKEY: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_PUBKEY, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + p += value_len; + break; + case GPC_TLV_SCP11CRT_BF_RESTR: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_BF_RESTR, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + p += value_len; + break; + default: + break; + } + + continue; + } + + switch (tag) { + case GPC_TLV_SCP11CRT_SN: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_SN, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_CAKLOCID: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_CAKLOCID, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_SUBJECTID: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_SUBJECTID, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_KEYUSAGE: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_KEYUSAGE, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_EFFEDATE: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_EFFEDATE, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_EXPEDATE: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_EXPEDATE, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_DISC_53: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_DISC_53, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_DISC_73: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_DISC_73, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_BF_RESTR: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_BF_RESTR, p, value_len); + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + case GPC_TLV_SCP11CRT_SIGNATURE: + SET_CERT_TLV_FIELD(cert, GPC_TLV_SCP11CRT_SIGNATURE, p, value_len); + break; + default: + sha256_Update(&ctx, p - tag_len - len_len, + tag_len + len_len + value_len); + break; + } + p += value_len; + } + // Verify certificate + sha256_Final(&ctx, digest); + + if (!scp11_get_signature(cert->lv_GPC_TLV_SCP11CRT_SIGNATURE.value, + cert->lv_GPC_TLV_SCP11CRT_SIGNATURE.len, + signature)) { + return false; + } + + return ecdsa_verify_digest(&nist256p1, sd_cert_verify_pubkey, signature, + digest) == 0; +} + +bool scp11_get_mutual_auth_data(uint8_t *data, uint8_t *data_len, + scp11_context scp11_ctx) { + uint8_t *p = data; + + uint8_t len = sizeof(shared_info_buffer) + 2 + 68; + + if (*data_len < len) { + return false; + } + + *p++ = GPC_TLV_MA_CR; + *p++ = scp11_ctx.shared_info.len; + memcpy(p, scp11_ctx.shared_info.value, scp11_ctx.shared_info.len); + p += scp11_ctx.shared_info.len; + *p++ = GPC_TLV_MA_PK >> 8; + *p++ = GPC_TLV_MA_PK & 0xff; + *p++ = 0x41; + memcpy(p, scp11_ctx.mutual_auth.oce_temp_public_key, 65); + p += 65; + + *data_len = len; + + return true; +} + +bool scp11_parse_response_msg(scp11_response_msg *resp_msg) { + uint8_t *p = resp_msg->data; + uint8_t *end = resp_msg->data + resp_msg->data_len; + uint16_t tag, value_len, offset; + + if (!tlv_parse_tag(p, end, &tag, &offset)) { + return false; + } + + if (tag != GPC_TLV_MA_PK) { + return false; + } + p += offset; + + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + p += offset; + + resp_msg->lv_GPC_TLV_MA_PK.value = p; + resp_msg->lv_GPC_TLV_MA_PK.len = value_len; + + p += value_len; + + if (!tlv_parse_tag(p, end, &tag, &offset)) { + return false; + } + p += offset; + + if (tag != GPC_TLV_MA_RECEIPT) { + return false; + } + + if (!tlv_parse_length(p, end, &value_len, &offset)) { + return false; + } + p += offset; + + if (value_len != 16) { + return false; + } + + resp_msg->lv_GPC_TLV_MA_RECEIPT.value = p; + resp_msg->lv_GPC_TLV_MA_RECEIPT.len = value_len; + + return true; +} + +bool scp11_open_secure_channel(scp11_context *scp11_ctx) { + uint8_t shsss_key[65], shses_key[65]; + uint8_t z[40] = {0}; + + uint8_t counter[4] = {0x00, 0x00, 0x00, 0x01}; + + if (!scp11_parse_response_msg(&scp11_ctx->response_msg)) { + return false; + } + + scp11_get_pubkey(scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_PUBKEY.value, + scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_PUBKEY.len, + scp11_ctx->mutual_auth.sd_public_key); + + // use se + if (0 != + se_lite_card_ecdh(scp11_ctx->mutual_auth.sd_public_key + 1, shsss_key)) { + return false; + } + // if (0 != ecdh_multiply(&nist256p1, scp11_ctx->mutual_auth.oce_private_key, + // scp11_ctx->mutual_auth.sd_public_key, shsss_key)) { + // return false; + // } + + if (0 != ecdh_multiply(&nist256p1, + scp11_ctx->mutual_auth.oce_temp_private_key, + scp11_ctx->mutual_auth.sd_public_key, shses_key)) { + return false; + } + + sha1_Raw(shses_key + 1, 32, z); + sha1_Raw(shsss_key + 1, 32, z + 20); + + SHA256_CTX ctx = {0}; + uint8_t digest[32]; + + sha256_Init(&ctx); + sha256_Update(&ctx, z, 40); + sha256_Update(&ctx, counter, 4); + sha256_Update(&ctx, shared_info_data_buffer, sizeof(shared_info_data_buffer)); + sha256_Update(&ctx, scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_CAKLOCID.value, + scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_CAKLOCID.len); + sha256_Final(&ctx, digest); + + memcpy(scp11_ctx->session_key.key_dek, digest, 16); + memcpy(scp11_ctx->session_key.s_enc, digest + 16, 16); + + counter[3] = 0x02; + sha256_Init(&ctx); + sha256_Update(&ctx, z, 40); + sha256_Update(&ctx, counter, 4); + sha256_Update(&ctx, shared_info_data_buffer, sizeof(shared_info_data_buffer)); + sha256_Update(&ctx, scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_CAKLOCID.value, + scp11_ctx->sd_cert.lv_GPC_TLV_SCP11CRT_CAKLOCID.len); + sha256_Final(&ctx, digest); + + memcpy(scp11_ctx->session_key.s_mac, digest, 16); + memcpy(scp11_ctx->session_key.s_rmac, digest + 16, 16); + + uint8_t mac_data[255], mac[16]; + uint8_t mac_data_len = sizeof(mac_data); + + scp11_get_mutual_auth_data(mac_data, &mac_data_len, *scp11_ctx); + memcpy(mac_data + mac_data_len, scp11_ctx->response_msg.data, 68); + mac_data_len += 68; + + AES128_CMAC(scp11_ctx->session_key.key_dek, mac_data, mac_data_len, mac); + + if (memcmp(mac, scp11_ctx->response_msg.lv_GPC_TLV_MA_RECEIPT.value, 16) != + 0) { + return false; + } + + scp11_ctx->is_secure_channel_opened = true; + + return true; +} + +void scp11_init(scp11_context *scp11_ctx) { + memset(scp11_ctx, 0, sizeof(scp11_ctx)); + + scp11_ctx->shared_info.value = (uint8_t *)shared_info_buffer; + scp11_ctx->shared_info.len = sizeof(shared_info_buffer); + + memcpy(scp11_ctx->oce_cert.raw, device_certificate, + sizeof(device_certificate) - 1); + scp11_ctx->oce_cert.raw_len = sizeof(device_certificate) - 1; + + // private key stored in SE + // memcpy(scp11_ctx->mutual_auth.oce_private_key, device_private_key, 32); + + scp11_ctx->response_msg.data_len = sizeof(scp11_ctx->response_msg.data); + + if (!scp11_certificate_parse_and_verify(scp11_ctx->oce_cert.raw, + scp11_ctx->oce_cert.raw_len, + &scp11_ctx->oce_cert)) { + return; + } + + scp11_get_pubkey(scp11_ctx->oce_cert.lv_GPC_TLV_SCP11CRT_PUBKEY.value, + scp11_ctx->oce_cert.lv_GPC_TLV_SCP11CRT_PUBKEY.len, + scp11_ctx->mutual_auth.oce_public_key); + + random_buffer(scp11_ctx->mutual_auth.oce_temp_private_key, + sizeof(scp11_ctx->mutual_auth.oce_temp_private_key)); + + ecdsa_get_public_key65(&nist256p1, + scp11_ctx->mutual_auth.oce_temp_private_key, + scp11_ctx->mutual_auth.oce_temp_public_key); +} + +void scp11_close_secure_channel(scp11_context *scp11_ctx) { + scp11_ctx->is_secure_channel_opened = false; +} + +bool scp11_get_secure_channel_status(scp11_context *scp11_ctx) { + return scp11_ctx->is_secure_channel_opened; +} diff --git a/core/embed/lite_card/scp11.h b/core/embed/lite_card/scp11.h new file mode 100644 index 000000000..ac8b0bb36 --- /dev/null +++ b/core/embed/lite_card/scp11.h @@ -0,0 +1,91 @@ +#ifndef _SCP_11_H_ +#define _SCP_11_H_ + +#include "tag.h" +#include "tlv.h" + +#define SCP11_MAX_CERTIFICATE_SIZE 256 +#define SPC11_SESSION_KEY_SIZE 16 + +typedef struct { + TLV_FIELD(GPC_TLV_SCP11CRT_ENTITY); + TLV_FIELD(GPC_TLV_SCP11CRT_SN); + TLV_FIELD(GPC_TLV_SCP11CRT_CAKLOCID); + TLV_FIELD(GPC_TLV_SCP11CRT_SUBJECTID); + TLV_FIELD(GPC_TLV_SCP11CRT_KEYUSAGE); + TLV_FIELD(GPC_TLV_SCP11CRT_EFFEDATE); + TLV_FIELD(GPC_TLV_SCP11CRT_EXPEDATE); + TLV_FIELD(GPC_TLV_SCP11CRT_DISC_53); + TLV_FIELD(GPC_TLV_SCP11CRT_DISC_73); + TLV_FIELD(GPC_TLV_SCP11CRT_BF_RESTR); + TLV_FIELD(GPC_TLV_SCP11CRT_PUBKEY); + TLV_FIELD(GPC_TLV_SCP11CRT_SIGNATURE); + + uint8_t raw[SCP11_MAX_CERTIFICATE_SIZE]; + uint16_t raw_len; +} scp11_certificate; + +// typedef struct { +// TLV_VAR(GPC_TLV_SHAREDINFO_SCP_ID_PARAM); +// TLV_VAR(GPC_TLV_SHAREDINFO_KEYUSAGE); +// TLV_VAR(GPC_TLV_SHAREDINFO_KEYTYPE); +// TLV_VAR(GPC_TLV_SHAREDINFO_KEYLENGTH); +// TLV_VAR(GPC_TLV_SHAREDINFO_HOSTID); +// } scp11_shared_info; + +// typedef struct { +// uint8_t scp_id_param[2]; +// uint8_t key_usage; +// uint8_t key_type; +// uint8_t key_length; +// uint8_t host_id[8]; +// } scp11_shared_info_data; + +typedef struct { + uint8_t len; + uint8_t *value; +} scp11_shared_info; + +typedef struct { + uint8_t key_dek[SPC11_SESSION_KEY_SIZE]; + uint8_t s_enc[SPC11_SESSION_KEY_SIZE]; + uint8_t s_mac[SPC11_SESSION_KEY_SIZE]; + uint8_t s_rmac[SPC11_SESSION_KEY_SIZE]; + uint8_t s_dek[SPC11_SESSION_KEY_SIZE]; +} scp11_session_key; + +typedef struct { + uint8_t oce_private_key[32]; + uint8_t oce_public_key[65]; + uint8_t oce_temp_private_key[32]; + uint8_t oce_temp_public_key[65]; + uint8_t sd_public_key[65]; +} scp11_mutual_auth; + +typedef struct { + TLV_FIELD(GPC_TLV_MA_PK); + TLV_FIELD(GPC_TLV_MA_RECEIPT); + uint8_t data[128]; + uint16_t data_len; +} scp11_response_msg; + +typedef struct { + bool is_secure_channel_opened; + scp11_certificate sd_cert; + scp11_certificate oce_cert; + scp11_mutual_auth mutual_auth; + scp11_shared_info shared_info; + scp11_response_msg response_msg; + scp11_session_key session_key; +} scp11_context; + +bool scp11_certificate_parse_and_verify(uint8_t *cert_raw, uint16_t cert_len, + scp11_certificate *cert); +bool scp11_get_mutual_auth_data(uint8_t *data, uint8_t *data_len, + scp11_context scp11_ctx); +bool scp11_open_secure_channel(scp11_context *scp11_ctx); +void scp11_close_secure_channel(scp11_context *scp11_ctx); +bool scp11_get_secure_channel_status(scp11_context *scp11_ctx); +void scp11_init(scp11_context *scp11_ctx); + +#endif diff --git a/core/embed/lite_card/tag.h b/core/embed/lite_card/tag.h new file mode 100644 index 000000000..bfc7abfb3 --- /dev/null +++ b/core/embed/lite_card/tag.h @@ -0,0 +1,40 @@ +#ifndef _TLV_TAG_H_ +#define _TLV_TAG_H_ + +// SharedInfo +#define GPC_TLV_SHAREDINFO_SCP_ID_PARAM 0x90 +#define GPC_TLV_SHAREDINFO_KEYUSAGE 0x95 +#define GPC_TLV_SHAREDINFO_KEYTYPE 0x80 +#define GPC_TLV_SHAREDINFO_KEYLENGTH 0x81 +#define GPC_TLV_SHAREDINFO_HOSTID 0x84 + +// MUTUAL AUTHENTICATE Data Field +#define GPC_TLV_MA_CR 0xA6 +#define GPC_TLV_MA_PK 0x5F49 +#define GPC_TLV_MA_RECEIPT 0x86 + +// scp11 crt +#define GPC_TLV_SCP11CRT 0x7F +#define GPC_TLV_SCP11CRT_ENTITY 0x7F21 +#define GPC_TLV_SCP11CRT_PUBKEY 0x7F49 + +#define GPC_TLV_SCP11CRT_CAKLOCID 0x42 +#define GPC_TLV_SCP11CRT_DISC_53 0x53 +#define GPC_TLV_SCP11CRT_DISC_73 0x73 +#define GPC_TLV_SCP11CRT_SN 0x93 +#define GPC_TLV_SCP11CRT_KEYUSAGE 0x95 + +#define GPC_TLV_SCP11CRT_ITEM 0x5F +#define GPC_TLV_SCP11CRT_SUBJECTID 0x5F20 +#define GPC_TLV_SCP11CRT_EXPEDATE 0x5F24 +#define GPC_TLV_SCP11CRT_EFFEDATE 0x5F25 +#define GPC_TLV_SCP11CRT_SIGNATURE 0x5F37 + +#define GPC_TLV_SCP11CRT_BF 0xBF +#define GPC_TLV_SCP11CRT_BF_RESTR 0xBF20 +#define GPC_TLV_SCP11CRT_BF_ENTITY 0xBF21 + +#define GPC_TLV_PK_Q 0xB0 +#define GPC_TLV_PK_PARAM 0xF0 + +#endif diff --git a/core/embed/lite_card/tlv.c b/core/embed/lite_card/tlv.c new file mode 100644 index 000000000..723d781c8 --- /dev/null +++ b/core/embed/lite_card/tlv.c @@ -0,0 +1,41 @@ +#include "tlv.h" + +bool tlv_parse_tag(const uint8_t *data, uint8_t *end, uint16_t *tag, + uint16_t *offset) { + if ((*data & 0x1f) == 0x1f) { + *tag = (*data << 8) | *(data + 1); + *offset = 2; + } else { + *tag = *data; + *offset = 1; + } + + return true; +} + +bool tlv_parse_length(const uint8_t *data, uint8_t *end, uint16_t *length, + uint16_t *offset) { + *length = 0; + if (*data & 0x80) { + uint8_t len_bytes = *data & 0x7f; + if (len_bytes > 2) { + return false; + } + + for (uint8_t i = 0; i < len_bytes; i++) { + *length = (*length << 8) | *(data + 1 + i); + } + *offset = len_bytes + 1; + if (data + *offset + *length > end) { + return false; + } + } else { + *length = *data; + *offset = 1; + if (data + *offset + *length > end) { + return false; + } + } + + return true; +} diff --git a/core/embed/lite_card/tlv.h b/core/embed/lite_card/tlv.h new file mode 100644 index 000000000..262ae81a4 --- /dev/null +++ b/core/embed/lite_card/tlv.h @@ -0,0 +1,26 @@ +#ifndef _TLV_H_ +#define _TLV_H_ + +#include +#include + +typedef struct { + uint16_t tag; + uint16_t length; + uint8_t *value; +} tlv_t; + +typedef struct { + uint16_t len; + uint8_t *value; +} tlv_value_t; + +#define TLV_VAR(tag) tlv_t tlv_##tag +#define TLV_FIELD(tag) tlv_value_t lv_##tag + +bool tlv_parse_tag(const uint8_t *data, uint8_t *end, uint16_t *tag, + uint16_t *offset); +bool tlv_parse_length(const uint8_t *data, uint8_t *end, uint16_t *length, + uint16_t *offset); + +#endif diff --git a/core/embed/pn532/pn532.c b/core/embed/pn532/pn532.c index 2ebeed004..11313be0e 100644 --- a/core/embed/pn532/pn532.c +++ b/core/embed/pn532/pn532.c @@ -1,29 +1,297 @@ -// stdlib -#include -#include -// pn532 lib -#include "pn532_defines.h" -#include "pn532_interface.h" -#include "pn532_hal.h" -#include "pn532_frame.h" -#include "pn532_functions.h" -// own header +#include + #include "pn532.h" +#include "systick.h" + +static const uint8_t pn532_ack_frame[] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}; +static pn532_controller_t pn532_controller = {0}; + +void pn532_init(void) +{ + pn532_controller.spi_controller = get_spi_controller(); + pn532_controller.stub_controller = get_stub_controller(); + pn532_controller.delay_ms = dwt_delay_ms; + pn532_controller.stub_controller->init(); + pn532_controller.spi_controller->spi_init(); + + pn532_controller.stub_controller->chip_reset_ctl(false); + // pn532_controller.delay_ms(5); + // pn532_controller.stub_controller->chip_reset_ctl(true); + // pn532_controller.delay_ms(20); +} + +void pn532_power_ctl(bool on_off) +{ + if ( on_off ) + { + pn532_controller.stub_controller->chip_reset_ctl(false); + pn532_controller.delay_ms(5); + pn532_controller.stub_controller->chip_reset_ctl(true); + pn532_controller.delay_ms(20); + } + else + { + pn532_controller.stub_controller->chip_reset_ctl(false); + pn532_controller.delay_ms(5); + } +} + +static bool pn532_write_frame(pn532_controller_t* pn532, uint8_t cmd, uint8_t* params, uint8_t params_length) +{ + pn532_frame_t frame = {0}; + uint8_t spi_cmd = PN532_SPI_DATAWRITE; + + if ( params_length > PN532_FRAME_DATA_MAX_LENGTH - 1 ) + { + return false; + } + + frame.preamble = PN532_PREAMBLE; + frame.start_code1 = PN532_STARTCODE1; + frame.start_code2 = PN532_STARTCODE2; + frame.len = params_length + 2; + frame.lcs = ~frame.len + 1; + frame.tfi = PN532_HOSTTOPN532; + frame.data[0] = cmd; + memcpy(frame.data + 1, params, params_length); + frame.dcs = 0; + for ( uint16_t i = 0; i < frame.len; i++ ) + { + frame.dcs += frame.raw[5 + i]; + } + frame.dcs = ~frame.dcs + 1; + frame.data[params_length + 1] = frame.dcs; + frame.data[params_length + 2] = PN532_POSTAMBLE; + pn532->spi_controller->chip_select(true); + pn532->delay_ms(1); + pn532->spi_controller->write(&spi_cmd, 1); + + for ( uint16_t i = 0; i < frame.len + 7; i++ ) + { + pn532->spi_controller->write(frame.raw + i, 1); + } + // pn532->spi_controller->write(frame.raw, frame.len + 7); + pn532->spi_controller->chip_select(false); + pn532->delay_ms(1); + + return true; +} + +static bool pn532_get_status(pn532_controller_t* pn532) +{ + uint8_t status = 0; + uint8_t cmd = PN532_SPI_STATREAD; + pn532->spi_controller->chip_select(true); + pn532->delay_ms(1); + pn532->spi_controller->write(&cmd, 1); + pn532->spi_controller->read(&status, 1); + pn532->spi_controller->chip_select(false); + pn532->delay_ms(1); + return status == PN532_SPI_READY; +} + +static bool pn532_wait_ready(pn532_controller_t* pn532, int timeout_ms) +{ + while ( !pn532_get_status(pn532) ) + { + if ( timeout_ms <= 0 ) + { + return false; + } + pn532->delay_ms(10); + timeout_ms -= 10; + } + return true; +} + +static bool pn532_read_ack(pn532_controller_t* pn532) +{ + uint8_t cmd = PN532_SPI_DATAREAD; + uint8_t ack[6] = {0}; + pn532->spi_controller->chip_select(true); + pn532->spi_controller->write(&cmd, 1); + pn532->spi_controller->read(ack, sizeof(ack)); + pn532->spi_controller->chip_select(false); + return memcmp(ack, pn532_ack_frame, sizeof(ack)) == 0; +} + +static bool pn532_read_frame(pn532_controller_t* pn532, pn532_frame_t* frame) +{ + uint8_t cmd = PN532_SPI_DATAREAD; + uint8_t index = 0; + bool ret = false; + pn532->spi_controller->chip_select(true); + pn532->spi_controller->write(&cmd, 1); + pn532->spi_controller->read(frame->raw, 3); + if ( frame->preamble != PN532_PREAMBLE || frame->start_code1 != PN532_STARTCODE1 || + frame->start_code2 != PN532_STARTCODE2 ) + { + goto exit; + } + index = 3; + + pn532->spi_controller->read(frame->raw + index, 2); + index += 2; + + if ( frame->len > PN532_FRAME_DATA_MAX_LENGTH ) + { + goto exit; + } -static PN532 _pn532; -PN532* pn532 = NULL; + if ( frame->len != ((~frame->lcs + 1) & 0xff) ) + { + goto exit; + } -void PN532_LibrarySetup() + for ( uint16_t i = 0; i < frame->len + 2; i++ ) + { + pn532->spi_controller->read(frame->raw + index, 1); + index++; + } + + // pn532->spi_controller->read(frame->raw + index, frame->len + 2); + + if ( frame->tfi != PN532_PN532TOHOST ) + { + goto exit; + } + + uint8_t dcs = 0; + for ( uint16_t i = 0; i < frame->len + 2; i++ ) + { + dcs += frame->raw[5 + i]; + } + + if ( dcs != 0 ) + { + goto exit; + } + + ret = true; + +exit: + pn532->spi_controller->chip_select(false); + return ret; +} + +static bool +pn532_write_cmd_check_ack(pn532_controller_t* pn532, uint8_t cmd, uint8_t* params, uint8_t params_length) { - pn532 = &_pn532; + pn532_write_frame(pn532, cmd, params, params_length); + if ( !pn532_wait_ready(pn532, PN532_TIMEOUT_MS_NORMAL) ) + { + return false; + } + return pn532_read_ack(pn532); +} + +static bool +pn532_read_response(pn532_controller_t* pn532, uint8_t* resonse, uint16_t* resonse_len, int timeout_ms) +{ + pn532_frame_t frame = {0}; + if ( !pn532_wait_ready(pn532, timeout_ms) ) + { + return false; + } - pn532->PowerOn = power_on; - pn532->PowerOff = power_off; + if ( !pn532_read_frame(pn532, &frame) ) + { + return false; + } + if ( resonse != NULL ) + { + uint8_t len = *resonse_len > frame.len - 2 ? frame.len - 2 : *resonse_len; + memcpy(resonse, frame.data + 1, len); + *resonse_len = len; + } - // Miscellaneous - pn532->Diagnose = Diagnose; - pn532->GetFirmwareVersion = GetFirmwareVersion; - pn532->SAMConfiguration = SAMConfiguration; - pn532->InListPassiveTarget = InListPassiveTarget; - pn532->InDataExchange = InDataExchange; + return true; +} + +bool pn532_transceive( + uint8_t cmd, uint8_t* paras, uint8_t paras_length, uint8_t* response, uint16_t* response_length, + int timeout_ms +) +{ + if ( !pn532_write_cmd_check_ack(&pn532_controller, cmd, paras, paras_length) ) + { + return false; + } + return pn532_read_response(&pn532_controller, response, response_length, timeout_ms); +} + +bool pn532_getFirmwareVersion(uint8_t* response, uint16_t* response_length) +{ + return pn532_transceive( + PN532_COMMAND_GETFIRMWAREVERSION, NULL, 0, response, response_length, PN532_TIMEOUT_MS_NORMAL + ); +} + +bool pn532_SAMConfiguration(void) +{ + uint8_t params[] = {0x01, 0x14, 0x01}; + return pn532_transceive( + PN532_COMMAND_SAMCONFIGURATION, params, sizeof(params), NULL, NULL, PN532_TIMEOUT_MS_NORMAL + ); +} + +bool pn532_inListPassiveTarget(void) +{ + uint8_t params[] = {0x01, PN532_InListPassiveTarget_BrTy_106k_typeA}; + uint8_t response[PN532_FRAME_DATA_MAX_LENGTH] = {0}; + uint16_t response_length = sizeof(response); + if ( !pn532_transceive( + PN532_COMMAND_INLISTPASSIVETARGET, params, sizeof(params), response, &response_length, + PN532_TIMEOUT_MS_NORMAL_PASSSIVETARGET + ) ) + { + return false; + } + if ( response[0] != 0x01 ) + { + return false; + } + return true; +} + +bool pn532_inDataExchange( + uint8_t* send_data, uint8_t send_data_length, uint8_t* response, uint16_t* response_length +) +{ + uint8_t buffer[PN532_FRAME_DATA_MAX_LENGTH] = {0}; + uint16_t recv_len = sizeof(buffer); + if ( send_data_length > PN532_FRAME_DATA_MAX_LENGTH - 1 ) + { + return false; + } + buffer[0] = 0x01; + memcpy(buffer + 1, send_data, send_data_length); + if ( !pn532_transceive( + PN532_COMMAND_INDATAEXCHANGE, buffer, send_data_length + 1, buffer, &recv_len, + PN532_TIMEOUT_MS_DATA_EXCHANGE + ) ) + { + return false; + } + if ( buffer[0] != 0x00 ) + { + return false; + } + memmove(response, buffer + 1, recv_len - 1); + *response_length = recv_len - 1; + return true; +} + +bool pn532_tgGetStatus(uint8_t* status) +{ + uint8_t response[PN532_FRAME_DATA_MAX_LENGTH] = {0}; + uint16_t response_length = sizeof(response); + if ( !pn532_transceive( + PN532_COMMAND_TGGETTARGETSTATUS, NULL, 0, response, &response_length, PN532_TIMEOUT_MS_NORMAL + ) ) + { + return false; + } + *status = response[0]; + return true; } diff --git a/core/embed/pn532/pn532.h b/core/embed/pn532/pn532.h index 828339e44..b16c5c66b 100644 --- a/core/embed/pn532/pn532.h +++ b/core/embed/pn532/pn532.h @@ -1,30 +1,48 @@ -#ifndef _PN532_ -#define _PN532_ - -#include -#include +#ifndef _PN532_H_ +#define _PN532_H_ +#include "pn532_spi.h" +#include "pn532_stub.h" #include "pn532_defines.h" -#include "pn532_interface.h" +#define PN532_FRAME_DATA_MAX_LENGTH 255 +#define PN532_TIMEOUT_MS_NORMAL 200 +#define PN532_TIMEOUT_MS_DATA_EXCHANGE 1800 +#define PN532_TIMEOUT_MS_NORMAL_PASSSIVETARGET 30 + +typedef struct +{ + void (*delay_ms)(uint32_t timeout); + pn532_stub_t* stub_controller; + pn532_spi_t* spi_controller; +} pn532_controller_t; -typedef struct _PN532 +typedef union { - void (*PowerOn)(void); - void (*PowerOff)(void); + struct __attribute__((packed)) + { + uint8_t preamble; + uint8_t start_code1; + uint8_t start_code2; + uint8_t len; + uint8_t lcs; + uint8_t tfi; + uint8_t data[PN532_FRAME_DATA_MAX_LENGTH - 1]; + uint8_t dcs; + uint8_t postamble; + }; + uint8_t raw[PN532_FRAME_DATA_MAX_LENGTH + 7]; - // Miscellaneous - bool (*Diagnose)(PN532_DIAG, uint8_t*, uint8_t); - bool (*GetFirmwareVersion)(PN532_FW_VER*); - bool (*SAMConfiguration)(PN532_SAM_MODE, uint8_t, bool); - bool (*InListPassiveTarget - )(PN532_InListPassiveTarget_Params params, PN532_InListPassiveTarget_Results* results); - bool (*InDataExchange - )(uint8_t Tg, uint8_t* DataOut, uint16_t DataOut_len, uint8_t* Status, uint8_t* DataIn, - uint16_t* DataIn_len); -} PN532; +} pn532_frame_t; -extern PN532* pn532; -void PN532_LibrarySetup(); +void pn532_init(void); +void pn532_power_ctl(bool on_off); +bool pn532_getFirmwareVersion(uint8_t* response, uint16_t* response_length); +bool pn532_SAMConfiguration(void); +bool pn532_inListPassiveTarget(void); +bool pn532_inDataExchange( + uint8_t* send_data, uint8_t send_data_length, uint8_t* response, uint16_t* response_length +); +bool pn532_tgGetStatus(uint8_t* status); -#endif //_PN532_ \ No newline at end of file +#endif diff --git a/core/embed/pn532/pn532_frame.c b/core/embed/pn532/pn532_frame.c deleted file mode 100644 index ce1e07596..000000000 --- a/core/embed/pn532/pn532_frame.c +++ /dev/null @@ -1,320 +0,0 @@ -#ifndef _PN532_INTERNAL_ -#define _PN532_INTERNAL_ -// stdlib -#include -#include -#include -// pn532 lib -#include "pn532_defines.h" -#include "pn532_interface.h" -#include "pn532_hal.h" -// own header -#include "pn532_frame.h" - -#ifndef PN532_SUPPORT_EXTENDED_INFO_FRAME - #pragma message "Note: PN532 Extended Information Frame Disabled" -#else - #pragma message "Note: PN532 Extended Information Frame Enabled" -#endif - -// Frame Container -PN532_FRAME* PackFrame(PN532_FRAME_TYPE type, uint8_t* buff, uint8_t len) -{ - PN532_FRAME* frame = (PN532_FRAME*)malloc(sizeof(PN532_FRAME) + sizeof(uint8_t) * len); - if ( frame != NULL ) - { - frame->type = type; - frame->length = len; - if ( buff != NULL ) - { - memcpy(frame->data, buff, len); - // memcpy(&frame->data[0], buff, len); // or this? - } - } - return frame; -} - -void DiscardFrame(PN532_FRAME* frame) -{ - if ( frame != NULL ) - { - free(frame); - frame = NULL; - } -} - -// Frame RW - -bool WriteFrame(PN532_FRAME* frame) -{ -#ifdef PN532_DEBUG - pn532_interface->log("%s\n", __func__); -#endif - - uint8_t frame_buffer[PN532_FRAME_MAX_LENGTH]; - uint16_t frame_raw_size = 0; - // uint16_t frame_data_len = 0; - // uint8_t frame_data_checksum = 0xff; - - memset(frame_buffer, PN532_FRAME_MAX_LENGTH, 0x00); - - // check type and data length - if ( frame->type == PN532_FRAME_NORMAL_INFO || frame->type == PN532_FRAME_EXTENDED_INFO ) - { - // check direction (TFI) - if ( frame->data[0] != PN532_HOSTTOPN532 ) - { - pn532_interface->log("Frame direction mark invalid!\n"); - return false; - } - - // check length sanity - if ( (frame->type == PN532_FRAME_NORMAL_INFO && frame->length > 0x00ff) || - (frame->type == PN532_FRAME_EXTENDED_INFO && frame->length > 0xffff) ) - { - pn532_interface->log("Frame data length overflow for type!\n"); - return false; - } - } - else if ( frame->type == PN532_FRAME_ACK || frame->type == PN532_FRAME_NACK ) - { - // check length sanity - if ( frame->length != 0 ) - { - pn532_interface->log("Frame data length overflow for type!\n"); - return false; - } - } - else if ( frame->type == PN532_FRAME_ERROR ) - { - // send error frame to PN532 do not make sense - pn532_interface->log("Frame type invalid!\n"); - return false; - } - else - { - pn532_interface->log("Frame type invalid!\n"); - return false; - } - - // make raw frame - - frame_buffer[frame_raw_size] = PN532_PREAMBLE; - frame_raw_size += sizeof(uint8_t); - - frame_buffer[frame_raw_size] = PN532_STARTCODE1; - frame_raw_size += sizeof(uint8_t); - frame_buffer[frame_raw_size] = PN532_STARTCODE2; - frame_raw_size += sizeof(uint8_t); - - if ( frame->type == PN532_FRAME_NORMAL_INFO || frame->type == PN532_FRAME_EXTENDED_INFO ) - { - // TFI treat as part of data for all following logics - if ( frame->type == PN532_FRAME_NORMAL_INFO ) - { - // length - frame_buffer[frame_raw_size] = 0xff & frame->length; - frame_raw_size += sizeof(uint8_t); - // length checksum - frame_buffer[frame_raw_size] = 0xff & (~frame->length + 1); - frame_raw_size += sizeof(uint8_t); - } - if ( frame->type == PN532_FRAME_EXTENDED_INFO ) - { - // length padding - frame_buffer[frame_raw_size] = 0xff; - frame_buffer[frame_raw_size + 1] = 0xff; - frame_raw_size += sizeof(uint16_t); - // length - frame_buffer[frame_raw_size] = (frame->length >> 8); - frame_buffer[frame_raw_size + 1] = (frame->length & 0x00ff); - frame_raw_size += sizeof(uint16_t); - // length checksum - frame_buffer[frame_raw_size] = 0xff & (~((frame->length >> 8) + (frame->length & 0x00ff)) + 1); - frame_raw_size += sizeof(uint8_t); - } - - // data - memcpy(&frame_buffer[frame_raw_size], frame->data, frame->length); - frame_raw_size += frame->length; - - // data checksum - frame_buffer[frame_raw_size] = 0; - for ( uint8_t i = 0; i < frame->length; i++ ) - { - frame_buffer[frame_raw_size] += frame->data[i]; - } - frame_buffer[frame_raw_size] = ~frame_buffer[frame_raw_size] + 1; - frame_raw_size += sizeof(uint8_t); - } - else if ( frame->type == PN532_FRAME_ACK ) - { - frame_buffer[frame_raw_size] = 0x00; - frame_buffer[frame_raw_size + 1] = 0xff; - frame_raw_size += sizeof(uint16_t); - } - else if ( frame->type == PN532_FRAME_NACK ) - { - frame_buffer[frame_raw_size] = 0xff; - frame_buffer[frame_raw_size + 1] = 0x00; - frame_raw_size += sizeof(uint16_t); - } - - frame_buffer[frame_raw_size] = PN532_POSTAMBLE; - frame_raw_size += sizeof(uint8_t); - - // hal read (until timeout) - if ( !write_data(frame_buffer, frame_raw_size) ) - { - pn532_interface->log("Write data failed!\n"); - return false; - } - - return true; -} - -bool ReadFrame(PN532_FRAME** frame) -{ -#ifdef PN532_DEBUG - pn532_interface->log("%s\n", __func__); -#endif - - uint8_t frame_buffer[PN532_FRAME_MAX_LENGTH]; - uint8_t* p_frame = frame_buffer; - uint16_t frame_data_len = 0; - uint8_t frame_data_checksum = 0xff; - PN532_FRAME_TYPE frame_type; - - memset(frame_buffer, PN532_FRAME_MAX_LENGTH, 0x00); - - // hal read (until timeout) - if ( !read_data(frame_buffer, sizeof(frame_buffer)) ) - { - pn532_interface->log("Read data failed!\n"); - return false; - } - - // find frame header - while ( true ) - { - if ( p_frame >= frame_buffer + sizeof(frame_buffer) ) - { - pn532_interface->log("Frame header not found!\n"); - return false; - } - - if ( p_frame[0] == 0x00 && p_frame[1] == 0xff ) - { - p_frame += 2; // skip 0x00ff - break; - } - else - { - p_frame++; - } - } - - // check type and data length - if ( ((p_frame[0] + p_frame[1]) & 0xff) == 0 ) - { - frame_type = PN532_FRAME_NORMAL_INFO; - frame_data_len = p_frame[0]; - p_frame += 2; // skip length and length checksum - } - else if ( (p_frame[0] == 0xff) && (p_frame[1] == 0xff) && (((p_frame[2] + p_frame[3] + p_frame[4]) & 0xffff) == 0) ) - { - frame_type = PN532_FRAME_EXTENDED_INFO; - frame_data_len = (p_frame[2] << 8) + p_frame[3]; - p_frame += 5; // skip length and length checksum - } - else if ( (p_frame[0] == 0x00) && (p_frame[1] == 0xff) ) - { - frame_type = PN532_FRAME_ACK; - frame_data_len = 0; - } - else if ( (p_frame[0] == 0xff) && (p_frame[1] == 0x00) ) - { - frame_type = PN532_FRAME_NACK; - frame_data_len = 0; - } - else if ( (p_frame[0] == 0x01) && (p_frame[1] == 0xff) ) - { - frame_type = PN532_FRAME_ERROR; - frame_data_len = p_frame[0]; // always be 1 - p_frame += 2; // skip length and length checksum - } - else - { - pn532_interface->log("Frame corrupted or invalid!\n"); - return false; - } - - // process by type - switch ( frame_type ) - { - case PN532_FRAME_NORMAL_INFO: - case PN532_FRAME_EXTENDED_INFO: - // check direction (TFI) - if ( p_frame[0] != PN532_PN532TOHOST ) - { - pn532_interface->log("Frame direction mark invalid!\n"); - return false; - } - // verify data with checksum - frame_data_checksum = 0; - for ( uint8_t i = 0; i < frame_data_len; i++ ) - { - frame_data_checksum += p_frame[i]; - } - frame_data_checksum += p_frame[frame_data_len]; // the actual checksum from raw frame - if ( frame_data_checksum != 0 ) - { - pn532_interface->log("Frame data or checksum corrupted\n"); - return false; - } - // pack frame - *frame = PackFrame(frame_type, p_frame, frame_data_len); - return true; - // break; - - case PN532_FRAME_ACK: - case PN532_FRAME_NACK: - *frame = PackFrame(frame_type, NULL, frame_data_len); - return true; - // break; - - case PN532_FRAME_ERROR: - pn532_interface->log("Error frame recived\n"); - // verify data with checksum - if ( ((p_frame[0] + p_frame[1]) & 0xff) != 0 ) - { - pn532_interface->log("Frame data or checksum corrupted\n"); - return false; - } - pn532_interface->log("Error code: %00X\n", p_frame[0]); - *frame = PackFrame(frame_type, p_frame, frame_data_len); - return true; - // break; - - default: - break; - } - - // #ifdef PN532_DEBUG - // pn532_interface->log("Frame length: %u\n", len); - - // size_t byte_str_len = (len) * 3 + 1; - // char str[byte_str_len]; - // size_t processed = 0; - // memzero(str, byte_str_len); - // if ( buffer_to_hex_string(buff, len, str, byte_str_len, &processed) ) - // pn532_interface->log("Frame data: %s\n", str); - // else - // pn532_interface->log("Frame data: convert failed\n"); - // #endif - - // should never reach here - return false; -} - -#endif // _PN532_INTERNAL_ \ No newline at end of file diff --git a/core/embed/pn532/pn532_frame.h b/core/embed/pn532/pn532_frame.h deleted file mode 100644 index 206427f88..000000000 --- a/core/embed/pn532/pn532_frame.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _PN532_FRAME_ -#define _PN532_FRAME_ - -#include -#include - -#include "pn532_defines.h" - -PN532_FRAME* PackFrame(PN532_FRAME_TYPE type, uint8_t* buff, uint8_t len); -void DiscardFrame(PN532_FRAME* frame); -bool WriteFrame(PN532_FRAME* frame); -bool ReadFrame(PN532_FRAME** frame); - -#endif // _PN532_FRAME_ \ No newline at end of file diff --git a/core/embed/pn532/pn532_functions.c b/core/embed/pn532/pn532_functions.c deleted file mode 100644 index ac156dc10..000000000 --- a/core/embed/pn532/pn532_functions.c +++ /dev/null @@ -1,395 +0,0 @@ - -// stdlib -#include -#include -#include -// pn532 lib -#include "pn532_defines.h" -#include "pn532_interface.h" -#include "pn532_hal.h" -#include "pn532_frame.h" -// own header -#include "pn532_functions.h" - -bool ReadACK() -{ - bool result = false; - PN532_FRAME* frame = NULL; - if ( ReadFrame(&frame) ) - { - result = frame->type == PN532_FRAME_ACK; - } - DiscardFrame(frame); - return result; -} -bool WriteACK() -{ - bool result = false; - PN532_FRAME* frame = PackFrame(PN532_FRAME_ACK, NULL, 0); - if ( frame != NULL ) - { - result = WriteFrame(frame); - } - DiscardFrame(frame); - return result; -} -bool WriteNACK() -{ - bool result = false; - PN532_FRAME* frame = PackFrame(PN532_FRAME_NACK, NULL, 0); - if ( frame != NULL ) - { - result = WriteFrame(frame); - } - DiscardFrame(frame); - return result; -} -void WakeUp() -{ - pn532_interface->chip_sel_ctl(false); - pn532_interface->delay_ms(2); - - // do not pull up, send cmd instead, according to FIG.49 - // pn532_interface->chip_sel_ctl(true); - - SAMConfiguration_Default(); -} -bool ExecCommand_ADV( - bool use_extended_frame, uint8_t command, uint8_t* params, uint16_t params_length, uint8_t* response, - uint16_t* response_length, uint32_t timeout_ms -) -{ - uint8_t - buff[use_extended_frame ? PN532_FRAME_EXTENDED_INFO_MAX_LENGTH : PN532_FRAME_NORMAL_INFO_MAX_LENGTH]; - uint8_t* p_buff = buff; - - p_buff[0] = PN532_HOSTTOPN532; - p_buff++; - - p_buff[0] = command; - p_buff++; - - memcpy(p_buff, params, params_length); - p_buff += params_length; - - PN532_FRAME* frame_req = PackFrame( - use_extended_frame ? PN532_FRAME_EXTENDED_INFO : PN532_FRAME_NORMAL_INFO, buff, p_buff - buff - ); - if ( !WriteFrame(frame_req) ) - { - return false; - } - DiscardFrame(frame_req); - - if ( !wait_ready(timeout_ms) ) - { - return false; - } - - if ( !ReadACK() ) - { - return false; - } - - if ( !wait_ready(timeout_ms) ) - { - return false; - } - - PN532_FRAME* frame_resp = NULL; - if ( !ReadFrame(&frame_resp) ) - { - return false; - } - if ( !(frame_resp->data[0] == PN532_PN532TOHOST && frame_resp->data[1] == (command + 0x01)) ) - { - return false; - } - if ( frame_resp->length - 2 > *response_length ) - { - return false; - } - *response_length = frame_resp->length - 2; - // memcpy(response, &frame_resp->data[2], frame_resp->length - 2); - memcpy(response, frame_resp->data + 2, frame_resp->length - 2); - DiscardFrame(frame_req); - - if ( !WriteACK() ) // optional, abort if anything still on going - { - return false; - } - - return true; -} - -// Miscellaneous -bool Diagnose(PN532_DIAG diag, uint8_t* diag_params, uint8_t diag_params_len) -{ - uint8_t buff[PN532_DIAG_BUFF_SIZE]; - uint8_t* p_buff = NULL; - uint16_t response_size = PN532_DIAG_BUFF_SIZE; - - // prepare request - p_buff = buff; - - p_buff[0] = diag; - p_buff++; - - memcpy(p_buff, diag_params, diag_params_len); - p_buff += diag_params_len; - - // exec - if ( !ExecCommand( - PN532_COMMAND_DIAGNOSE, buff, p_buff - buff, buff, &response_size, PN532_COMMAND_TIMEOUT - ) ) - return false; - - // parse result - p_buff = buff; - switch ( diag ) - { - // case PN532_DIAG_CommunicationLineTest: - // break; - - case PN532_DIAG_ROMTest: - case PN532_DIAG_RAMTest: - return p_buff[0] == 0x00; - - // case PN532_DIAG_PollingTestToTarget: - // break; - // case PN532_DIAG_EchoBackTest: - // break; - // case PN532_DIAG_AttentionRequestTest: - // break; - // case PN532_DIAG_SelfAntenaTest: - // break; - - default: - return false; - break; - } -} - -bool GetFirmwareVersion(PN532_FW_VER* fw_ver) -{ - uint8_t buff[PN532_FW_VER_BUFF_SIZE]; - uint8_t* p_buff = NULL; - uint16_t response_size = PN532_FW_VER_BUFF_SIZE; - - // prepare request - p_buff = buff; - // command has no param - - // exec - if ( !ExecCommand( - PN532_COMMAND_GETFIRMWAREVERSION, buff, p_buff - buff, buff, &response_size, - PN532_COMMAND_TIMEOUT - ) ) - return false; - - // parse result - p_buff = buff; - memcpy(fw_ver, p_buff, 4); - - return true; -} - -bool GetGeneralStatus(); - -bool ReadRegister(); -bool WriteRegister(); - -bool ReadGPIO(); -bool WriteGPIO(); - -bool SetSerialBaudRate(); -bool SetParameters(); - -bool SAMConfiguration(PN532_SAM_MODE mode, uint8_t timeout, bool use_irq) -{ - uint8_t buff[PN532_SAM_CONFIG_BUFF_SIZE]; - uint8_t* p_buff = NULL; - uint16_t response_size = PN532_SAM_CONFIG_BUFF_SIZE; - - // prepare request - p_buff = buff; - p_buff[0] = mode; - p_buff++; - p_buff[0] = timeout; - p_buff++; - p_buff[0] = use_irq ? 0x01 : 0x00; - p_buff++; - - // exec - if ( !ExecCommand( - PN532_COMMAND_SAMCONFIGURATION, buff, p_buff - buff, buff, &response_size, PN532_COMMAND_TIMEOUT - ) ) - return false; - - // parse result - // nothing to do - - return true; -} - -bool PowerDown(); - -// RF communication -bool RFConfiguration(); -bool RFRegulationTest(); - -// Initiator -bool InJumpForDEP(); -bool InJumpForPSL(); -bool InListPassiveTarget(PN532_InListPassiveTarget_Params params, PN532_InListPassiveTarget_Results* results) -{ - uint8_t buff[PN532_InListPassiveTarget_BUFF_SIZE]; - uint8_t* p_buff = NULL; - uint16_t response_size = PN532_InListPassiveTarget_BUFF_SIZE; - - // sanity check - if ( (params.MaxTg <= 0) || (params.MaxTg > 2) || - ((params.BrTy == PN532_InListPassiveTarget_BrTy_106k_Jewel) && (params.MaxTg > 1)) ) - return false; - - // prepare request - p_buff = buff; - p_buff[0] = params.MaxTg; - p_buff++; - p_buff[0] = params.BrTy; - p_buff++; - - switch ( params.BrTy ) - { - case PN532_InListPassiveTarget_BrTy_106k_typeA: - // InitiatorData optional - if ( params.InitiatorData_len > 0 ) - { - memcpy(p_buff, params.InitiatorData, params.InitiatorData_len); - p_buff += params.InitiatorData_len; - } - break; - - case PN532_InListPassiveTarget_BrTy_212k: - // InitiatorData required - if ( params.InitiatorData_len > 0 ) - { - memcpy(p_buff, params.InitiatorData, params.InitiatorData_len); - p_buff += params.InitiatorData_len; - } - else - { - return false; - } - break; - - case PN532_InListPassiveTarget_BrTy_424k: - // InitiatorData required - if ( params.InitiatorData_len > 0 ) - { - memcpy(p_buff, params.InitiatorData, params.InitiatorData_len); - p_buff += params.InitiatorData_len; - } - else - { - return false; - } - break; - - case PN532_InListPassiveTarget_BrTy_106k_typeB: - // InitiatorData required - if ( params.InitiatorData_len > 0 ) - { - memcpy(p_buff, params.InitiatorData, params.InitiatorData_len); - p_buff += params.InitiatorData_len; - } - else - { - return false; - } - break; - - case PN532_InListPassiveTarget_BrTy_106k_Jewel: - // InitiatorData forbidden - if ( params.InitiatorData_len != 0 ) - { - return false; - } - break; - - default: - return false; - break; - } - - // exec - if ( !ExecCommand( - PN532_COMMAND_INLISTPASSIVETARGET, buff, p_buff - buff, buff, &response_size, - PN532_COMMAND_TIMEOUT - ) ) - return false; - - // parse result - p_buff = buff; - - results->BrTy = params.BrTy; - - results->NbTg = p_buff[0]; - p_buff++; - - memcpy(results->TargetData, p_buff, PN532_InListPassiveTarget_DATA_BUFF_SIZE - (p_buff - buff)); - - return true; -} -bool InATR(); -bool InPSL(); -bool InDataExchange( - uint8_t Tg, uint8_t* DataOut, uint16_t DataOut_len, uint8_t* Status, uint8_t* DataIn, uint16_t* DataIn_len -) -{ - uint8_t buff[PN532_InDataExchange_BUFF_SIZE]; - uint8_t* p_buff = NULL; - uint16_t response_size = PN532_InDataExchange_BUFF_SIZE; - - // prepare request - p_buff = buff; - - p_buff[0] = Tg; - p_buff++; - - memcpy(p_buff, DataOut, DataOut_len); - p_buff += DataOut_len; - - // exec - if ( !ExecCommand( - PN532_COMMAND_INDATAEXCHANGE, buff, p_buff - buff, buff, &response_size, PN532_COMMAND_TIMEOUT - ) ) - return false; - - // parse result - p_buff = buff; - - *Status = p_buff[0]; - p_buff++; - - memset(DataIn, 0x00, *DataIn_len); - memcpy(DataIn, p_buff, response_size); - *DataIn_len = response_size; - - return true; -} -bool InCommunicateThru(); -bool InDeselect(); -bool InRelease(); -bool InSelect(); -bool InAutoPoll(); - -// Target -bool TgInitAsTarget(); -bool TgSetGeneralBytes(); -bool TgGetData(); -bool TgSetData(); -bool TgSetMetaData(); -bool TgGetInitiatorCommand(); -bool TgResponseToInitiator(); -bool TgGetTargetStatus(); \ No newline at end of file diff --git a/core/embed/pn532/pn532_functions.h b/core/embed/pn532/pn532_functions.h deleted file mode 100644 index 04ba62f3b..000000000 --- a/core/embed/pn532/pn532_functions.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef _PN532_FUNCTIONS_H_ -#define _PN532_FUNCTIONS_H_ - -#include -#include - -#include "pn532_defines.h" - -bool ReadACK(); -bool WriteACK(); -bool WriteNACK(); -void WakeUp(); -bool ExecCommand_ADV( - bool use_extended_frame, uint8_t command, uint8_t* params, uint16_t params_length, uint8_t* response, - uint16_t* response_length, uint32_t timeout_ms -); -#ifndef PN532_SUPPORT_EXTENDED_INFO_FRAME - #define ExecCommand(_command, _params, _params_length, _response, _response_length, _timeout_ms) \ - ExecCommand_ADV(false, _command, _params, _params_length, _response, _response_length, _timeout_ms) -#else - #define ExecCommand(_command, _params, _params_length, _response, _response_length, _timeout_ms) \ - ExecCommand_ADV(true, _command, _params, _params_length, _response, _response_length, _timeout_ms) -#endif - -// Miscellaneous -bool Diagnose(PN532_DIAG diag, uint8_t* params, uint8_t param_cout); - -bool GetFirmwareVersion(PN532_FW_VER* fw_ver); - -bool GetGeneralStatus(); - -bool ReadRegister(); -bool WriteRegister(); - -bool ReadGPIO(); -bool WriteGPIO(); - -bool SetSerialBaudRate(); -bool SetParameters(); - -bool SAMConfiguration(PN532_SAM_MODE mode, uint8_t timeout, bool use_irq); -#define SAMConfiguration_Default() SAMConfiguration(PN532_SAM_Normal, 0, true) - -bool PowerDown(); - -// RF communication -bool RFConfiguration(); -bool RFRegulationTest(); - -// Initiator -bool InJumpForDEP(); -bool InJumpForPSL(); -bool InListPassiveTarget(PN532_InListPassiveTarget_Params params, PN532_InListPassiveTarget_Results* results); -bool InATR(); -bool InPSL(); -bool InDataExchange( - uint8_t Tg, uint8_t* DataOut, uint16_t DataOut_len, uint8_t* Status, uint8_t* DataIn, uint16_t* DataIn_len -); -bool InCommunicateThru(); -bool InDeselect(); -bool InRelease(); -bool InSelect(); -bool InAutoPoll(); - -// Target -bool TgInitAsTarget(); -bool TgSetGeneralBytes(); -bool TgGetData(); -bool TgSetData(); -bool TgSetMetaData(); -bool TgGetInitiatorCommand(); -bool TgResponseToInitiator(); -bool TgGetTargetStatus(); - -#endif // _PN532_FUNCTIONS_H_ \ No newline at end of file diff --git a/core/embed/pn532/pn532_hal.c b/core/embed/pn532/pn532_hal.c deleted file mode 100644 index 03d7c9711..000000000 --- a/core/embed/pn532/pn532_hal.c +++ /dev/null @@ -1,118 +0,0 @@ -// stdlib -#include -#include -#include -// pn532 lib -#include "pn532_defines.h" -#include "pn532_interface.h" -// own header -#include "pn532_hal.h" - -void power_on() -{ - // bus init - pn532_interface->bus_init(); - - // reset - pn532_interface->reset_ctl(false); - pn532_interface->delay_ms(5); - pn532_interface->reset_ctl(true); - pn532_interface->delay_ms(20); -} - -void power_off() -{ - // hold reset down, hard power down - pn532_interface->reset_ctl(false); - - // bus deinit - pn532_interface->bus_deinit(); -} - -bool bus_rw(uint8_t* w_data, uint16_t w_count, uint8_t* r_data, uint16_t r_count) -{ - bool result = pn532_interface->rw(w_data, w_count, r_data, r_count); - - if ( !result ) - { - pn532_interface->log("bus_rw failed"); - } - - return result; -} - -uint8_t get_status() -{ - uint8_t req = PN532_SPI_STATREAD; - uint8_t resp; - bus_rw(&req, 1, &resp, 1); - return resp; -} - -bool get_irq() -{ - return !pn532_interface->irq_read(); // inverted as irq is active LOW -} - -bool wait_irq(uint32_t timeout_ms) -{ - uint32_t delay_ms_step = timeout_ms / 10; - - while ( !get_irq() ) - { - // check in this way to avoid overflow - if ( timeout_ms < delay_ms_step ) - { - return false; - } - - pn532_interface->delay_ms(delay_ms_step); - - timeout_ms -= delay_ms_step; - } - - return true; -} - -bool is_ready() -{ - return (get_status() & PN532_SPI_READY) == PN532_SPI_READY; -} - -bool wait_ready(uint32_t timeout_ms) -{ - uint32_t delay_ms_step = timeout_ms / 10; - - while ( !is_ready() ) - { - // check in this way to avoid overflow - if ( timeout_ms < delay_ms_step ) - { - // pn532_interface->log("wait_ready timeout"); - return false; - } - - pn532_interface->delay_ms(delay_ms_step); - - timeout_ms -= delay_ms_step; - } - - return true; -} - -bool read_data(uint8_t* buff, uint16_t len) -{ - uint8_t req = PN532_SPI_DATAREAD; - uint8_t* resp = buff; - return bus_rw(&req, 1, resp, len); -} - -bool write_data(uint8_t* buff, uint16_t len) -{ - uint8_t req[len + 1]; - req[0] = PN532_SPI_DATAWRITE; - memcpy(&req[1], buff, len); - - uint8_t* resp = NULL; - return bus_rw(req, len + 1, resp, 0); -} \ No newline at end of file diff --git a/core/embed/pn532/pn532_hal.h b/core/embed/pn532/pn532_hal.h deleted file mode 100644 index 361d8aafe..000000000 --- a/core/embed/pn532/pn532_hal.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _PN532_HAL_ -#define _PN532_HAL_ - -#include -#include - -void power_on(); -void power_off(); - -bool bus_rw(uint8_t* w_data, uint16_t w_count, uint8_t* r_data, uint16_t r_count); - -uint8_t get_status(); -bool get_irq(); - -bool is_ready(); -bool wait_ready(uint32_t timeout_ms); - -bool hal_frame_wrapper_spi(uint8_t* buff, uint8_t buff_len, uint8_t data_len, PN532_FRAME_TYPE_SPI type); - -bool read_data(uint8_t* buff, uint16_t len); -bool write_data(uint8_t* buff, uint16_t len); - -#endif // _PN532_HAL_ \ No newline at end of file diff --git a/core/embed/pn532/pn532_interface.c b/core/embed/pn532/pn532_interface.c deleted file mode 100644 index 3ee47ab72..000000000 --- a/core/embed/pn532/pn532_interface.c +++ /dev/null @@ -1,29 +0,0 @@ -// stdlib -#include -#include -// pn532 lib -#include "pn532_defines.h" -// own header -#include "pn532_interface.h" - -PN532_INTERFACE* pn532_interface = NULL; - -bool PN532_InterfaceSetup(PN532_INTERFACE* interface) -{ - - if ( interface == NULL ) - { - return false; - } - - // all functions must be filled - if ( interface->bus_init == NULL || interface->bus_deinit == NULL || interface->reset_ctl == NULL || - interface->chip_sel_ctl == NULL || interface->irq_read == NULL || interface->rw == NULL || - interface->delay_ms == NULL || interface->log == NULL ) - { - return false; - } - - pn532_interface = interface; - return true; -} \ No newline at end of file diff --git a/core/embed/pn532/pn532_interface.h b/core/embed/pn532/pn532_interface.h deleted file mode 100644 index e363c868a..000000000 --- a/core/embed/pn532/pn532_interface.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _PN532_INTERFACE_ -#define _PN532_INTERFACE_ - -#include -#include - -#include "pn532_defines.h" - -typedef struct _PN532_INTERFACE -{ - bool (*bus_init)(void); - bool (*bus_deinit)(void); - void (*reset_ctl)(bool); - void (*chip_sel_ctl)(bool); - bool (*irq_read)(void); - bool (*rw)(uint8_t* w_data, uint16_t w_count, uint8_t* r_data, uint16_t r_count); - void (*delay_ms)(uint16_t timeout); - void (*log)(const char* fmt, ...); -} PN532_INTERFACE; - -extern PN532_INTERFACE* pn532_interface; -bool PN532_InterfaceSetup(PN532_INTERFACE* interface); - -#endif //_PN532_INTERFACE_ \ No newline at end of file diff --git a/core/embed/pn532/pn532_spi.c b/core/embed/pn532/pn532_spi.c new file mode 100644 index 000000000..ef00aed11 --- /dev/null +++ b/core/embed/pn532/pn532_spi.c @@ -0,0 +1,42 @@ +#include "pn532_spi.h" + +static SPI_HandleTypeDef* spi_handle_nfc; + +void pn532_spi_init(void) +{ + spi_init_by_device(SPI_NFC); + spi_handle_nfc = &spi_handles[spi_find_channel_by_device(SPI_NFC)]; +} + +void pn532_spi_deinit(void) +{ + spi_deinit_by_device(SPI_NFC); +} + +void pn532_spi_write(uint8_t* buf, uint32_t size) +{ + HAL_SPI_Transmit(spi_handle_nfc, buf, size, 1000); +} + +void pn532_spi_read(uint8_t* buf, uint32_t size) +{ + HAL_SPI_Receive(spi_handle_nfc, buf, size, 1000); +} + +void pn532_spi_chip_select(bool enable) +{ + HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_4, enable ? GPIO_PIN_RESET : GPIO_PIN_SET); +} + +static pn532_spi_t pn532_spi_st = { + .spi_init = pn532_spi_init, + .spi_deinit = pn532_spi_deinit, + .chip_select = pn532_spi_chip_select, + .write = pn532_spi_write, + .read = pn532_spi_read, +}; + +pn532_spi_t* get_spi_controller(void) +{ + return &pn532_spi_st; +} diff --git a/core/embed/pn532/pn532_spi.h b/core/embed/pn532/pn532_spi.h new file mode 100644 index 000000000..bd769fdd6 --- /dev/null +++ b/core/embed/pn532/pn532_spi.h @@ -0,0 +1,17 @@ +#ifndef _PN532_SPI_ +#define _PN532_SPI_ + +#include "spi.h" + +typedef struct +{ + void (*spi_init)(void); + void (*spi_deinit)(void); + void (*chip_select)(bool enable); + void (*write)(uint8_t* buf, uint32_t size); + void (*read)(uint8_t* buf, uint32_t size); +} pn532_spi_t; + +pn532_spi_t* get_spi_controller(void); + +#endif diff --git a/core/embed/pn532/pn532_stub.c b/core/embed/pn532/pn532_stub.c new file mode 100644 index 000000000..078b504f4 --- /dev/null +++ b/core/embed/pn532/pn532_stub.c @@ -0,0 +1,44 @@ +#include STM32_HAL_H + +#include "pn532_stub.h" + +static void pn532_stub_init(void) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOJ_CLK_ENABLE(); + + // IRQ NFC_IRQ PC4 + GPIO_InitStruct.Pin = GPIO_PIN_4; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = 0; // ignored + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + // RSTPDn NFC_RST PD5 + GPIO_InitStruct.Pin = GPIO_PIN_5; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = 0; // ignored + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_RESET); +} + +static void pn532_stub_reset_ctl(bool enable) +{ + HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, enable ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + +pn532_stub_t pn532_stub_st = { + .init = pn532_stub_init, + .chip_reset_ctl = pn532_stub_reset_ctl, +}; + +pn532_stub_t* get_stub_controller(void) +{ + return &pn532_stub_st; +} diff --git a/core/embed/pn532/pn532_stub.h b/core/embed/pn532/pn532_stub.h new file mode 100644 index 000000000..fcef521c3 --- /dev/null +++ b/core/embed/pn532/pn532_stub.h @@ -0,0 +1,17 @@ +#ifndef _PN532_STUB_H_ +#define _PN532_STUB_H_ + +#include +#include + +typedef struct +{ + + void (*init)(void); + void (*chip_reset_ctl)(bool enable); + +} pn532_stub_t; + +pn532_stub_t* get_stub_controller(void); + +#endif // _PN532_STUB_H_ diff --git a/core/embed/trezorhal/device.c b/core/embed/trezorhal/device.c index aee71ef48..5ad7ae156 100644 --- a/core/embed/trezorhal/device.c +++ b/core/embed/trezorhal/device.c @@ -577,9 +577,8 @@ static bool _nfc_test() { nfc_init(); nfc_pwr_ctl(true); while (1) { - if (nfc_poll_card() == NFC_STATUS_OPERACTION_SUCCESS) { - if (nfc_select_aid((uint8_t *)"\xD1\x56\x00\x01\x32\x83\x40\x01", 8) == - NFC_STATUS_OPERACTION_SUCCESS) { + if (nfc_poll_card()) { + if (nfc_select_aid((uint8_t *)"\xD1\x56\x00\x01\x32\x83\x40\x01", 8)) { nfc_pwr_ctl(false); return true; } @@ -587,7 +586,7 @@ static bool _nfc_test() { if (nfc_select_aid( (uint8_t *)"\x6f\x6e\x65\x6b\x65\x79\x2e\x62\x61\x63\x6b\x75\x70\x01", - 14) == NFC_STATUS_OPERACTION_SUCCESS) { + 14)) { nfc_pwr_ctl(false); return true; } @@ -985,14 +984,14 @@ void device_burnin_test(bool force) { nfc_pwr_ctl(true); HAL_TIM_Base_Stop(&TimHandle); while (1) { - if (nfc_poll_card() == NFC_STATUS_OPERACTION_SUCCESS) { + if (nfc_poll_card()) { if (nfc_select_aid((uint8_t *)"\xD1\x56\x00\x01\x32\x83\x40\x01", - 8) == NFC_STATUS_OPERACTION_SUCCESS) { + 8)) { break; } else if (nfc_select_aid( (uint8_t *)"\x6f\x6e\x65\x6b\x65\x79\x2e\x62" "\x61\x63\x6b\x75\x70\x01", - 14) == NFC_STATUS_OPERACTION_SUCCESS) { + 14)) { break; } } diff --git a/core/embed/trezorhal/low_power.c b/core/embed/trezorhal/low_power.c index f20d10c83..89dc6df80 100644 --- a/core/embed/trezorhal/low_power.c +++ b/core/embed/trezorhal/low_power.c @@ -16,12 +16,65 @@ static RTC_HandleTypeDef RTCHandle; #define RTC_ASYNCH_PREDIV 0x7F #define RTC_SYNCH_PREDIV 0xF9 /* 32Khz/128 - 1 */ +#define LPTIMER_PERIOD 10000 /* 10000ms */ static bool rtc_inited = false; static bool wakeup_by_rtc = false; #define RTC_MAX_TIMEOUT 0xFFFF +LPTIM_HandleTypeDef LptimHandle; + +void lptim_init(void) { + static bool lptim_inited = false; + + if (lptim_inited) { + return; + } + RCC_OscInitTypeDef RCC_OscInitStruct; + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + + // /* Enable LSI clock */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI; + RCC_OscInitStruct.LSIState = RCC_LSI_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + HAL_RCC_OscConfig(&RCC_OscInitStruct); + + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1; + RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI; + HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct); + + __HAL_RCC_LPTIM1_CLK_ENABLE(); + + LptimHandle.Instance = LPTIM1; + + LptimHandle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; + LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV128; + LptimHandle.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE; + LptimHandle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL; + LptimHandle.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE; + + HAL_LPTIM_Init(&LptimHandle); + HAL_NVIC_SetPriority(LPTIM1_IRQn, 1, 0); + HAL_NVIC_EnableIRQ(LPTIM1_IRQn); + lptim_inited = true; +} + +// MAX timeout is 128/32Khz = 4ms * 0xFFFF = 262s +void lptim_set_period(uint32_t timeout_ms) { + lptim_init(); + HAL_LPTIM_TimeOut_Stop_IT(&LptimHandle); + if (timeout_ms) { + HAL_LPTIM_TimeOut_Start_IT(&LptimHandle, 0, + timeout_ms > 4 ? timeout_ms / 4 : 1); + } +} + +void LPTIM1_IRQHandler(void) { + HAL_LPTIM_IRQHandler(&LptimHandle); + HAL_LPTIM_TimeOut_Stop_IT(&LptimHandle); +} + void rtc_init(void) { if (rtc_inited) { return; @@ -93,13 +146,13 @@ void RTC_WKUP_IRQHandler(void) { wakeup_by_rtc = true; } -void enter_stop_mode(bool restart, uint32_t shutdown_seconds) { +void enter_stop_mode(bool restart, uint32_t shutdown_seconds, bool wake_up) { static uint32_t seconds = 0; static uint32_t rtc_period = 0; if (is_usb_connected()) { return; } - if (restart) { + if (restart && shutdown_seconds) { seconds = shutdown_seconds; } camera_power_off(); @@ -108,29 +161,39 @@ void enter_stop_mode(bool restart, uint32_t shutdown_seconds) { usart_enable_stop_wup(); lcd_refresh_suspend(); sdram_set_self_refresh(); - while (seconds) { - // previous rtc timeout - if (wakeup_by_rtc) { + if (seconds > 0) { + while (seconds) { + // previous rtc timeout + if (wakeup_by_rtc) { + seconds -= rtc_period; + if (seconds == 0) { + ble_power_off(); + } + restart = true; + } + if (!restart) { + restart = true; + } else { + rtc_period = seconds > RTC_MAX_TIMEOUT ? RTC_MAX_TIMEOUT : seconds; + rtc_set_period(rtc_period); + } + if (wake_up) { + lptim_set_period(LPTIMER_PERIOD); + } + HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); + if (!wakeup_by_rtc) { + break; + } seconds -= rtc_period; if (seconds == 0) { ble_power_off(); } - restart = true; } - if (!restart) { - restart = true; - } else { - rtc_period = seconds > RTC_MAX_TIMEOUT ? RTC_MAX_TIMEOUT : seconds; - rtc_set_period(rtc_period); + } else { + if (wake_up) { + lptim_set_period(LPTIMER_PERIOD); } HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); - if (!wakeup_by_rtc) { - break; - } - seconds -= rtc_period; - if (seconds == 0) { - ble_power_off(); - } } sdram_set_normal_mode(); lcd_refresh_resume(); diff --git a/core/embed/trezorhal/low_power.h b/core/embed/trezorhal/low_power.h index aa338943f..781173acf 100644 --- a/core/embed/trezorhal/low_power.h +++ b/core/embed/trezorhal/low_power.h @@ -3,7 +3,7 @@ #include -void enter_stop_mode(bool restart, uint32_t shutdown_seconds); +void enter_stop_mode(bool restart, uint32_t shutdown_seconds, bool wake_up); void rtc_init(void); void rtc_disable(void); void rtc_set_period(uint32_t period); diff --git a/core/embed/trezorhal/nfc.c b/core/embed/trezorhal/nfc.c index e3fa98ae3..a84d3290e 100644 --- a/core/embed/trezorhal/nfc.c +++ b/core/embed/trezorhal/nfc.c @@ -1,510 +1,78 @@ +#include #include "display.h" +#include "pn532.h" #include "nfc.h" -#include "debug_utils.h" -static const uint32_t NFC_SPI_TIMEOUT = 0x0000ffff; -static bool nfc_powered_on = false; -static PN532_INTERFACE _pn532_interface; // the actuall handle - -static void nfc_lowlevel_gpio_init(void) -{ - GPIO_InitTypeDef GPIO_InitStruct = {0}; - - __HAL_RCC_GPIOC_CLK_ENABLE(); - __HAL_RCC_GPIOJ_CLK_ENABLE(); - - // IRQ NFC_IRQ PC4 - GPIO_InitStruct.Pin = GPIO_PIN_4; - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = 0; // ignored - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - // RSTPDn NFC_RST PD5 - GPIO_InitStruct.Pin = GPIO_PIN_5; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_PULLUP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = 0; // ignored - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - - HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_SET); -} - -static void nfc_lowlevel_reset_ctl(bool ctl) -{ - HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, (ctl ? GPIO_PIN_SET : GPIO_PIN_RESET)); -} - -static void nfc_lowlevel_chip_sel_ctl(bool ctl) -{ - HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_4, (ctl ? GPIO_PIN_SET : GPIO_PIN_RESET)); -} - -static bool nfc_lowlevel_irq_get() -{ - return HAL_GPIO_ReadPin(GPIOJ, GPIO_PIN_4) == GPIO_PIN_SET; -} - -static bool nfc_lowlevel_spi_rw(uint8_t* w_data, uint16_t w_count, uint8_t* r_data, uint16_t r_count) -{ - bool result = true; - - nfc_lowlevel_chip_sel_ctl(false); - HAL_Delay(1); - - if ( w_data != NULL ) - if ( HAL_SPI_Transmit(&spi_handle_nfc, w_data, w_count, NFC_SPI_TIMEOUT) != HAL_OK ) - result = false; - if ( r_data != NULL ) - if ( HAL_SPI_Receive(&spi_handle_nfc, r_data, r_count, NFC_SPI_TIMEOUT) != HAL_OK ) - { - if ( spi_handle_nfc.ErrorCode != HAL_SPI_ERROR_TIMEOUT ) - { - result = false; - } - } - - HAL_Delay(1); - nfc_lowlevel_chip_sel_ctl(true); - - return result; -} - -static void nfc_lowlevel_delay_ms(uint16_t timeout) -{ - HAL_Delay(timeout); -} - -// use display_printf instead! -// static void nfc_lowlevel_log(const char *fmt, ...) -// { -// va_list va; -// va_start(va, fmt); -// char buf[256] = {0}; -// int len = vsnprintf(buf, sizeof(buf), fmt, va); -// display_print(buf, len); -// va_end(va); -// } - -static bool nfc_lowlevel_init() -{ - return spi_init_by_device(SPI_NFC); -} - -static bool nfc_lowlevel_deinit() -{ - return spi_deinit_by_device(SPI_NFC); -} +static bool nfc_powered = false; void nfc_init(void) { - nfc_lowlevel_gpio_init(); - - _pn532_interface.bus_init = nfc_lowlevel_init; - _pn532_interface.bus_deinit = nfc_lowlevel_deinit; - _pn532_interface.reset_ctl = nfc_lowlevel_reset_ctl; - _pn532_interface.chip_sel_ctl = nfc_lowlevel_chip_sel_ctl; - _pn532_interface.irq_read = nfc_lowlevel_irq_get; - _pn532_interface.rw = nfc_lowlevel_spi_rw; - _pn532_interface.delay_ms = nfc_lowlevel_delay_ms; - _pn532_interface.log = display_printf; - - if ( !PN532_InterfaceSetup(&_pn532_interface) ) - { - // this should never happen as long as above filled all members of - // pn532_interface - display_printf("PN532_InterfaceSetup failed!"); - return; - } - - PN532_LibrarySetup(); + pn532_init(); } -NFC_STATUS nfc_pwr_ctl(bool on_off) +bool nfc_pwr_ctl(bool on_off) { - // sanity check - if ( pn532 == NULL ) + if ( on_off == nfc_powered ) { - return NFC_STATUS_NOT_INITIALIZED; + return true; } - - NFC_STATUS result = NFC_STATUS_UNDEFINED_ERROR; - - // switch nfc + pn532_power_ctl(on_off); if ( on_off ) { - pn532->PowerOn(); - result = - (pn532->SAMConfiguration(PN532_SAM_Normal, 0x14, true) ? NFC_STATUS_OPERACTION_SUCCESS - : NFC_STATUS_OPERACTION_FAILED); - } - else - { - pn532->PowerOff(); - result = NFC_STATUS_OPERACTION_SUCCESS; + return pn532_SAMConfiguration(); } - - // update state - if ( result == NFC_STATUS_OPERACTION_SUCCESS ) - { - nfc_powered_on = on_off; - } - - return result; + return true; } -NFC_STATUS nfc_wait_card(uint32_t timeout_ms) -{ - // sanity check - if ( pn532 == NULL || !nfc_powered_on ) - { - return NFC_STATUS_NOT_INITIALIZED; - } - if ( timeout_ms < NFC_TIMEOUT_COUNT_STEP_MS ) - { - return NFC_STATUS_INVALID_USAGE; - } - - NFC_STATUS result = NFC_STATUS_UNDEFINED_ERROR; - - // InListPassiveTarget - PN532_InListPassiveTarget_Params ILPT_params = { - .MaxTg = 1, - .BrTy = PN532_InListPassiveTarget_BrTy_106k_typeA, - .InitiatorData_len = 0, - }; - PN532_InListPassiveTarget_Results ILPT_result = {0}; - - uint32_t time_passed_ms = 0; - while ( true ) - { - // detect card - if ( pn532->InListPassiveTarget(ILPT_params, &ILPT_result) ) - { - // detected - // only allow a single card - if ( ILPT_result.NbTg == 1 ) - { - result = NFC_STATUS_OPERACTION_SUCCESS; - } - else - { - result = NFC_STATUS_OPERACTION_FAILED; - } - break; - } - - if ( time_passed_ms < timeout_ms ) - { - hal_delay(NFC_TIMEOUT_COUNT_STEP_MS); - time_passed_ms += NFC_TIMEOUT_COUNT_STEP_MS; - continue; - } - else - { - result = NFC_STATUS_OPERACTION_TIMEOUT; - break; - } - } - - return result; -} - -NFC_STATUS nfc_send_recv(uint8_t* send, uint16_t sendLength, uint8_t* response, uint16_t* responseLength) -{ - // sanity check - if ( pn532 == NULL || !nfc_powered_on ) - { - return NFC_STATUS_NOT_INITIALIZED; - } - if ( sendLength > PN532_InDataExchange_BUFF_SIZE ) - { - return NFC_STATUS_INVALID_USAGE; - } - - NFC_STATUS result = NFC_STATUS_UNDEFINED_ERROR; - - // InDataExchange - uint8_t InDataExchange_status = 0xff; - - if ( pn532->InDataExchange(1, send, sendLength, &InDataExchange_status, response, responseLength) ) - { - if ( InDataExchange_status == 0x00 ) - { - result = NFC_STATUS_OPERACTION_SUCCESS; - } - else - { - result = NFC_STATUS_OPERACTION_FAILED; - } - } - - return result; -} - -NFC_STATUS nfc_send_recv_aio( - uint8_t* send, uint16_t sendLength, uint8_t* response, uint16_t* responseLength, uint32_t timeout_ms +bool nfc_send_recv( + uint8_t* send, uint16_t send_len, uint8_t* response, uint16_t* response_len, uint8_t* sw1sw2 ) { - // sanity check - if ( pn532 == NULL || !nfc_powered_on ) + if ( !pn532_inDataExchange(send, send_len, response, response_len) ) { - return NFC_STATUS_NOT_INITIALIZED; + return false; } - if ( sendLength > PN532_InDataExchange_BUFF_SIZE || timeout_ms < NFC_TIMEOUT_COUNT_STEP_MS ) - { - return NFC_STATUS_INVALID_USAGE; - } - - NFC_STATUS result = NFC_STATUS_UNDEFINED_ERROR; - // InListPassiveTarget - PN532_InListPassiveTarget_Params ILPT_params = { - .MaxTg = 1, - .BrTy = PN532_InListPassiveTarget_BrTy_106k_typeA, - .InitiatorData_len = 0, - }; - PN532_InListPassiveTarget_Results ILPT_result = {0}; - // InDataExchange - uint8_t InDataExchange_status = 0xff; - - uint32_t time_passed_ms = 0; - while ( true ) + if ( sw1sw2 != NULL ) { - // detect card - if ( pn532->InListPassiveTarget(ILPT_params, &ILPT_result) ) - { - // found card - - if ( ILPT_result.NbTg == 1 ) - { - - if ( pn532->InDataExchange( - 1, send, sendLength, &InDataExchange_status, response, responseLength - ) ) - { - if ( InDataExchange_status == 0x00 ) - { - result = NFC_STATUS_OPERACTION_SUCCESS; - } - else - { - result = NFC_STATUS_OPERACTION_FAILED; - } - } - } - else - { - result = NFC_STATUS_INVALID_USAGE; - } - break; - } - - if ( time_passed_ms < timeout_ms ) - { - hal_delay(NFC_TIMEOUT_COUNT_STEP_MS); - time_passed_ms += NFC_TIMEOUT_COUNT_STEP_MS; - continue; - } - else - { - result = NFC_STATUS_OPERACTION_TIMEOUT; - break; - } + sw1sw2[0] = response[*response_len - 2]; + sw1sw2[1] = response[*response_len - 1]; } - return result; + *response_len -= 2; + return true; } -NFC_STATUS nfc_poll_card(void) +bool nfc_poll_card(void) { - // sanity check - if ( pn532 == NULL || !nfc_powered_on ) - { - return NFC_STATUS_NOT_INITIALIZED; - } - - NFC_STATUS result = NFC_STATUS_UNDEFINED_ERROR; - - // InListPassiveTarget - PN532_InListPassiveTarget_Params ILPT_params = { - .MaxTg = 1, - .BrTy = PN532_InListPassiveTarget_BrTy_106k_typeA, - .InitiatorData_len = 0, - }; - PN532_InListPassiveTarget_Results ILPT_result = {0}; - - // detect card - if ( pn532->InListPassiveTarget(ILPT_params, &ILPT_result) ) - { - // detected - // only allow a single card - if ( ILPT_result.NbTg == 1 ) - { - result = NFC_STATUS_OPERACTION_SUCCESS; - } - else - { - result = NFC_STATUS_OPERACTION_FAILED; - } - } - - return result; + return pn532_inListPassiveTarget(); } -NFC_STATUS nfc_select_aid(uint8_t* aid, uint8_t aid_len) +bool nfc_select_aid(uint8_t* aid, uint8_t aid_len) { uint8_t apdu_select[64] = {0x00, 0xA4, 0x04, 0x00, 0x00}; uint8_t response[64] = {0}; uint16_t response_len = 64; + uint8_t sw1sw2[2] = {0}; apdu_select[4] = aid_len; memcpy(apdu_select + 5, aid, aid_len); - NFC_STATUS result = nfc_send_recv(apdu_select, aid_len + 5, response, &response_len); - - if ( result == NFC_STATUS_OPERACTION_SUCCESS ) + if ( nfc_send_recv(apdu_select, aid_len + 5, response, &response_len, sw1sw2) ) { - if ( response[0] == 0x90 && response[1] == 0x00 ) + if ( sw1sw2[0] == 0x90 && sw1sw2[1] == 0x00 ) { - result = NFC_STATUS_OPERACTION_SUCCESS; + return true; } else { - result = NFC_STATUS_OPERACTION_FAILED; + return false; } } - return result; + return false; } -void nfc_test() +NFC_STATUS nfc_get_status(uint8_t* status) { - display_printf("TouchPro Demo Mode\n"); - display_printf("======================\n\n"); - - display_printf("NFC PN532 Library Init..."); - nfc_init(); - display_printf("Done\n"); - - pn532->PowerOn(); - - PN532_FW_VER fw_ver; - display_printf("NFC PN532 Get FW Ver..."); - if ( !pn532->GetFirmwareVersion(&fw_ver) ) - { - display_printf("Fail\n"); - while ( true ) - ; // die here - } - display_printf("Success\n"); - display_printf( - "IC:0x%02X, Ver:0x%02X, Rev:0x%02X, Support:0x%02X \n", fw_ver.IC, fw_ver.Ver, fw_ver.Rev, - fw_ver.Support - ); - - display_printf("NFC PN532 Config..."); - if ( !pn532->SAMConfiguration(PN532_SAM_Normal, 0x14, true) ) - { - display_printf("Fail\n"); - while ( true ) - ; // die here - } - display_printf("Success\n"); - - // card cmd define - uint8_t capdu_getSN[] = {0x80, 0xcb, 0x80, 0x00, 0x05, 0xdf, 0xff, 0x02, 0x81, 0x01}; - // uint8_t capdu_getBackupState[] = {0x80, 0x6a, 0x00, 0x00}; - // uint8_t capdu_getPINState[] = {0x80, 0xcb, 0x80, 0x00, 0x05, 0xdf, 0xff, - // 0x02, 0x81, 0x05}; - - // InListPassiveTarget - PN532_InListPassiveTarget_Params ILPT_params = { - .MaxTg = 1, - .BrTy = PN532_InListPassiveTarget_BrTy_106k_typeA, - .InitiatorData_len = 0, - }; - PN532_InListPassiveTarget_Results ILPT_result = {0}; - - // InDataExchange - uint8_t InDataExchange_status = 0xff; - uint8_t buf_rapdu[PN532_InDataExchange_BUFF_SIZE]; - uint16_t len_rapdu = PN532_InDataExchange_BUFF_SIZE; - - while ( true ) - { - // detect card - display_printf("Checking for card..."); - if ( pn532->InListPassiveTarget(ILPT_params, &ILPT_result) && ILPT_result.NbTg == 1 ) - { - display_printf("Detected\n"); - if ( pn532->InDataExchange( - 1, capdu_getSN, sizeof(capdu_getSN), &InDataExchange_status, buf_rapdu, &len_rapdu - ) ) - { - display_printf("DataExchanging..."); - if ( InDataExchange_status == 0x00 ) - { - display_printf("Success\n"); - display_printf("CardSN: %s\n", (char*)buf_rapdu); - // print_buffer_Wait(buf_rapdu, len_rapdu); - } - else - { - display_printf("Fail\n"); - } - } - // break; - } - else - { - display_printf("LS Timeout\n"); - } - - hal_delay(300); - } - - while ( true ) - ; -} - -void nfc_test_v2() -{ - display_printf("TouchPro Demo Mode\n"); - display_printf("======================\n\n"); - - display_printf("NFC PN532 Library Init..."); - nfc_init(); - display_printf("Done\n"); - - uint8_t capdu_getSN[] = {0x80, 0xcb, 0x80, 0x00, 0x05, 0xdf, 0xff, 0x02, 0x81, 0x01}; - // uint8_t capdu_getBackupState[] = {0x80, 0x6a, 0x00, 0x00}; - // uint8_t capdu_getPINState[] = {0x80, 0xcb, 0x80, 0x00, 0x05, 0xdf, 0xff, - // 0x02, 0x81, 0x05}; - - uint8_t buf_rapdu[PN532_InDataExchange_BUFF_SIZE]; - uint16_t len_rapdu = PN532_InDataExchange_BUFF_SIZE; - - display_printf("Gettings Card SN..."); - nfc_pwr_ctl(true); - NFC_STATUS status = nfc_send_recv_aio(capdu_getSN, sizeof(capdu_getSN), buf_rapdu, &len_rapdu, 1000); - nfc_pwr_ctl(false); - - // print result - if ( status == NFC_STATUS_OPERACTION_SUCCESS ) - { - display_printf("Success\n"); - display_printf("CardSN: %s\n", (char*)buf_rapdu); - print_buffer(buf_rapdu, len_rapdu); - } - else - { - display_printf("Fail\n"); - display_printf("NFC_STATUS: %d\n", (int)status); - } - - // die here - while ( true ) - ; + return pn532_tgGetStatus(status) ? NFC_STATUS_OPERACTION_SUCCESS : NFC_STATUS_OPERACTION_FAILED; } diff --git a/core/embed/trezorhal/nfc.h b/core/embed/trezorhal/nfc.h index e1be00cbd..d7bcecfea 100644 --- a/core/embed/trezorhal/nfc.h +++ b/core/embed/trezorhal/nfc.h @@ -4,8 +4,6 @@ #include "pn532.h" #include "spi.h" -#define spi_handle_nfc spi_handles[spi_find_channel_by_device(SPI_NFC)] - #define NFC_TIMEOUT_COUNT_STEP_MS 100 typedef enum @@ -19,17 +17,10 @@ typedef enum } NFC_STATUS; void nfc_init(void); -NFC_STATUS nfc_pwr_ctl(bool on_off); -NFC_STATUS nfc_wait_card(uint32_t timeout_ms); -NFC_STATUS nfc_send_recv(uint8_t* send, uint16_t sendLength, uint8_t* response, uint16_t* responseLength); -NFC_STATUS nfc_send_recv_aio( - uint8_t* send, uint16_t sendLength, uint8_t* response, uint16_t* responseLength, uint32_t timeout_ms +bool nfc_pwr_ctl(bool on_off); +bool nfc_send_recv( + uint8_t* send, uint16_t send_len, uint8_t* response, uint16_t* response_len, uint8_t* sw1sw2 ); - -NFC_STATUS nfc_poll_card(void); -NFC_STATUS nfc_select_aid(uint8_t* aid, uint8_t aid_len); - -void nfc_test(); -void nfc_test_v2(); - +bool nfc_poll_card(void); +bool nfc_select_aid(uint8_t* aid, uint8_t aid_len); #endif // __NFC_H__ diff --git a/core/embed/trezorhal/se_thd89.c b/core/embed/trezorhal/se_thd89.c index 1ed9913ce..e5e26f181 100644 --- a/core/embed/trezorhal/se_thd89.c +++ b/core/embed/trezorhal/se_thd89.c @@ -34,6 +34,21 @@ #define SE_INS_HASHR 0xED #define SE_INS_HASHRAM 0xEE #define SE_INS_FINGERPRINT 0xEF +#define SE_INS_FIDO 0xF9 + +typedef enum { + SE_FIDO_GEN_SEED = 0x00, + SE_FIDO_U2F_REGISTER, + SE_FIDO_U2F_GEN_HANDLE, + SE_FIDO_U2F_VALIDATE_HANDLE, + SE_FIDO_U2F_AUTHENTICATE, + SE_FIDO_GET_COUNTER, + SE_FIDO_NEXT_COUNTER, + SE_FIDO_SET_COUNTER, + SE_FIDO_DERIVE_NODE, + SE_FIDO_NODE_SIGN, + SE_FIDO_ATT_SIGN, +} SE_FIDO_P2; #define SE_PIN_RETRY_MAX 10 @@ -498,6 +513,68 @@ secbool se_derive_keys(HDNode *out, const char *curve, return sectrue; } +secbool se_derive_xmr_key(const char *curve, const uint32_t *address_n, + size_t address_n_count, uint8_t *pubkey, + uint8_t *prikey_hash) { + uint8_t resp[64]; + uint16_t resp_len = sizeof(resp); + + uint8_t len = strlen(curve); + APDU_DATA[0] = len; + memcpy(APDU_DATA + 1, curve, len); + len += 1; + + memcpy(APDU_DATA + len, (uint8_t *)address_n, address_n_count * 4); + len += address_n_count * 4; + + if (!se_transmit_mac(SE_INS_DERIVE, 0x00, 0x01, APDU_DATA, len, resp, + &resp_len)) { + return secfalse; + } + memcpy((void *)pubkey, resp, 32); + memcpy((void *)prikey_hash, resp + 32, 32); + + return sectrue; +} + +secbool se_derive_xmr_private_key(const uint8_t *pubkey, const uint32_t index, + uint8_t *prikey) { + uint8_t resp[32]; + uint16_t resp_len = sizeof(resp); + + uint8_t data[32 + 4]; + + memcpy(data, pubkey, 32); + memcpy(data + 32, &index, 4); + + if (!se_transmit_mac(SE_INS_DERIVE, 0x00, 0x02, data, sizeof(data), resp, + &resp_len)) { + return secfalse; + } + memcpy(prikey, resp, 32); + + return sectrue; +} + +secbool se_xmr_get_tx_key(const uint8_t *rand, const uint8_t *hash, + uint8_t *out) { + uint8_t resp[32]; + uint16_t resp_len = sizeof(resp); + + uint8_t data[32 + 32]; + + memcpy(data, rand, 32); + memcpy(data + 32, hash, 32); + + if (!se_transmit_mac(SE_INS_DERIVE, 0x00, 0x03, data, sizeof(data), resp, + &resp_len)) { + return secfalse; + } + memcpy(out, resp, 32); + + return sectrue; +} + static secbool _se_reset_storage(void) { uint8_t rand[16]; @@ -1215,9 +1292,27 @@ secbool se_getSecsta(void) { return secfalse; } -secbool se_set_u2f_counter(uint32_t u2fcounter) { return sectrue; } +secbool se_set_u2f_counter(uint32_t u2fcounter) { + uint8_t cmd[9] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_SET_COUNTER, 0x04}; + uint16_t recv_len = 0; + + memcpy(cmd + 5, &u2fcounter, 4); + + if (!thd89_transmit(cmd, sizeof(cmd), NULL, &recv_len)) { + return secfalse; + } + + return sectrue; +} -secbool se_get_u2f_counter(uint32_t *u2fcounter) { return sectrue; } +secbool se_get_u2f_counter(uint32_t *u2fcounter) { + uint8_t cmd[5] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_NEXT_COUNTER, 0x00}; + uint16_t recv_len = 4; + if (!thd89_transmit(cmd, sizeof(cmd), (uint8_t *)u2fcounter, &recv_len)) { + return secfalse; + } + return sectrue; +} secbool se_set_mnemonic(const char *mnemonic, uint16_t len) { return se_transmit_mac(0xE2, 0x00, 0x00, (uint8_t *)mnemonic, len, NULL, @@ -1702,6 +1797,18 @@ int se_curve25519_ecdh(const uint8_t *publickey, uint8_t *sessionkey) { return 0; } +int se_lite_card_ecdh(const uint8_t *publickey, uint8_t *sessionkey) { + uint8_t resp[128]; + uint16_t resp_len = sizeof(resp); + + if (!se_transmit_mac(SE_INS_ECDH, 0x00, 0x02, (uint8_t *)publickey, 64, resp, + &resp_len)) { + return -1; + } + memcpy(sessionkey, resp, resp_len); + return 0; +} + int se_get_shared_key(const char *curve, const uint8_t *peer_public_key, uint8_t *session_key) { if (strcmp(curve, NIST256P1_NAME) == 0 || @@ -1852,6 +1959,15 @@ int se_slip21_node(uint8_t *data) { } return 0; } +// seed without passphrase +int se_slip21_fido_node(uint8_t *data) { + uint16_t resp_len = 64; + + if (!se_transmit_mac(0xEB, 0x00, 0x01, NULL, 0, data, &resp_len)) { + return -1; + } + return 0; +} secbool se_authorization_set(const uint32_t authorization_type, const uint8_t *authorization, @@ -2018,3 +2134,165 @@ secbool se_fp_read(uint16_t offset, void *val_dest, uint16_t len, uint8_t index, } return sectrue; } + +secbool se_gen_fido_seed(uint8_t *percent) { + uint8_t cmd[5] = {0x00, 0xf9, 0x00, 0x00, 0x00}; + uint16_t recv_len = 0; + uint16_t sw1sw2; + + if (!thd89_transmit(cmd, sizeof(cmd), NULL, &recv_len)) { + sw1sw2 = thd89_last_error(); + if ((sw1sw2 & 0xff00) == 0x6c00) { + *percent = sw1sw2 & 0xff; + if (ui_callback) { + ui_callback(0, *percent * 10, NULL); + } + return sectrue; + } + return secfalse; + } + *percent = 100; + return sectrue; +} + +secbool se_u2f_register(const uint8_t app_id[32], const uint8_t challenge[32], + uint8_t key_handle[64], uint8_t pub_key[65], + uint8_t sign[64]) { + uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_REGISTER}; + uint8_t recv[256]; + uint16_t recv_len = sizeof(recv); + memcpy(cmd + 5, app_id, 32); + memcpy(cmd + 5 + 32, challenge, 32); + + cmd[4] = 64; + if (!thd89_transmit(cmd, 5 + 64, (uint8_t *)recv, &recv_len)) { + return secfalse; + } + + // key_handle 64 public key 65 sign 64 + if (recv_len != 193) { + return secfalse; + } + memcpy(key_handle, recv, 64); + memcpy(pub_key, recv + 64, 65); + memcpy(sign, recv + 64 + 65, 64); + return sectrue; +} + +secbool se_u2f_gen_handle_and_node(const uint8_t app_id[32], + uint8_t key_handle[64], HDNode *out) { + uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_GEN_HANDLE}; + uint8_t recv[256]; + uint16_t recv_len = sizeof(recv); + memcpy(cmd + 5, app_id, 32); + + cmd[4] = 32; + if (!thd89_transmit(cmd, 5 + 32, (uint8_t *)recv, &recv_len)) { + return secfalse; + } + + // key_handle 64 ,fingerprint 4 + HDNode - 4(out->curve) + if (recv_len != 64 + sizeof(HDNode)) { + return secfalse; + } + memcpy(key_handle, recv, 64); + memcpy((void *)out, recv + 64 + 4, sizeof(HDNode) - 4); + out->curve = get_curve_by_name("nist256p1"); + return sectrue; +} + +secbool se_u2f_validate_handle(const uint8_t app_id[32], + const uint8_t key_handle[64]) { + uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_VALIDATE_HANDLE}; + + memcpy(cmd + 5, app_id, 32); + memcpy(cmd + 5 + 32, key_handle, 64); + + cmd[4] = 32 + 64; + if (!thd89_transmit(cmd, 5 + 96, NULL, NULL)) { + return secfalse; + } + return sectrue; +} + +secbool se_u2f_authenticate(const uint8_t app_id[32], + const uint8_t key_handle[64], + const uint8_t challenge[32], uint8_t *u2f_counter, + uint8_t sign[64]) { + uint8_t cmd[256] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_U2F_AUTHENTICATE}; + uint8_t recv[128]; + uint16_t recv_len = sizeof(recv); + memcpy(cmd + 5, app_id, 32); + memcpy(cmd + 5 + 32, key_handle, 64); + memcpy(cmd + 5 + 32 + 64, challenge, 32); + + cmd[4] = 128; + if (!thd89_transmit(cmd, 5 + 128, (uint8_t *)recv, &recv_len)) { + return secfalse; + } + + // counter 4 sign 64 + if (recv_len != 68) { + return secfalse; + } + memcpy(u2f_counter, recv, 4); + memcpy(sign, recv + 4, 64); + return sectrue; +} + +secbool se_derive_fido_keys(HDNode *out, const char *curve, + const uint32_t *address_n, size_t address_n_count, + uint32_t *fingerprint) { + uint8_t cmd[128] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_DERIVE_NODE}; + uint8_t resp[256]; + uint16_t resp_len = sizeof(resp); + + uint8_t len = strlen(curve); + cmd[5] = len; + memcpy(cmd + 6, curve, len); + len += 1; + + memcpy(cmd + 5 + len, (uint8_t *)address_n, address_n_count * 4); + len += address_n_count * 4; + + cmd[4] = len; + + if (!thd89_transmit(cmd, 5 + len, (uint8_t *)resp, &resp_len)) { + return secfalse; + } + out->curve = get_curve_by_name(curve); + if (fingerprint) { + memcpy(fingerprint, resp, 4); + } + memcpy((void *)out, resp + 4, sizeof(HDNode) - 4); + + return sectrue; +} + +secbool se_fido_hdnode_sign_digest(const uint8_t *hash, uint8_t *sig) { + uint8_t cmd[37] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_NODE_SIGN, 0x20}; + uint8_t resp[64]; + uint16_t resp_len = sizeof(resp); + + memcpy(cmd + 5, hash, 32); + + if (!thd89_transmit(cmd, 37, (uint8_t *)resp, &resp_len)) { + return secfalse; + } + memcpy(sig, resp, resp_len); + return sectrue; +} + +secbool se_fido_att_sign_digest(const uint8_t *hash, uint8_t *sig) { + uint8_t cmd[37] = {0x00, SE_INS_FIDO, 0x00, SE_FIDO_ATT_SIGN, 0x20}; + uint8_t resp[64]; + uint16_t resp_len = sizeof(resp); + + memcpy(cmd + 5, hash, 32); + + if (!thd89_transmit(cmd, 37, (uint8_t *)resp, &resp_len)) { + return secfalse; + } + memcpy(sig, resp, resp_len); + return sectrue; +} diff --git a/core/embed/trezorhal/se_thd89.h b/core/embed/trezorhal/se_thd89.h index d81f5cd52..5cd6dd798 100644 --- a/core/embed/trezorhal/se_thd89.h +++ b/core/embed/trezorhal/se_thd89.h @@ -110,6 +110,13 @@ secbool se_gen_session_seed(const char *passphrase, bool cardano); secbool se_derive_keys(HDNode *out, const char *curve, const uint32_t *address_n, size_t address_n_count, uint32_t *fingerprint); +secbool se_derive_xmr_key(const char *curve, const uint32_t *address_n, + size_t address_n_count, uint8_t *pubkey, + uint8_t *prikey_hash); +secbool se_derive_xmr_private_key(const uint8_t *pubkey, const uint32_t index, + uint8_t *prikey); +secbool se_xmr_get_tx_key(const uint8_t *rand, const uint8_t *hash, + uint8_t *out); secbool se_node_sign_digest(const uint8_t *hash, uint8_t *sig, uint8_t *by); int se_ecdsa_sign_digest(const uint8_t curve, const uint8_t canonical, const uint8_t *digest, uint8_t *sig, uint8_t *pby); @@ -141,6 +148,7 @@ int se_nem_aes256_decrypt(const uint8_t *ed25519_public_key, const uint8_t *iv, const uint8_t *salt, uint8_t *payload, uint16_t size, uint8_t *out); int se_slip21_node(uint8_t *data); +int se_slip21_fido_node(uint8_t *data); secbool se_authorization_set(const uint32_t authorization_type, const uint8_t *authorization, @@ -159,4 +167,23 @@ secbool se_fp_write(uint16_t offset, const void *val_dest, uint16_t len, secbool se_fp_read(uint16_t offset, void *val_dest, uint16_t len, uint8_t index, uint8_t total); +int se_lite_card_ecdh(const uint8_t *publickey, uint8_t *sessionkey); + +secbool se_gen_fido_seed(uint8_t *percent); +secbool se_u2f_register(const uint8_t app_id[32], const uint8_t challenge[32], + uint8_t key_handle[64], uint8_t pub_key[65], + uint8_t sign[64]); +secbool se_u2f_gen_handle_and_node(const uint8_t app_id[32], + uint8_t key_handle[64], HDNode *out); +secbool se_u2f_validate_handle(const uint8_t app_id[32], + const uint8_t key_handle[64]); +secbool se_u2f_authenticate(const uint8_t app_id[32], + const uint8_t key_handle[64], + const uint8_t challenge[32], uint8_t *u2f_counter, + uint8_t sign[64]); +secbool se_derive_fido_keys(HDNode *out, const char *curve, + const uint32_t *address_n, size_t address_n_count, + 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); #endif diff --git a/core/embed/trezorhal/spi.c b/core/embed/trezorhal/spi.c index e9c75efd2..b79b6a66c 100644 --- a/core/embed/trezorhal/spi.c +++ b/core/embed/trezorhal/spi.c @@ -213,7 +213,7 @@ bool SPI_6_INIT() spi_handles[SPI_6].Init.CLKPolarity = SPI_POLARITY_LOW; spi_handles[SPI_6].Init.CLKPhase = SPI_PHASE_1EDGE; spi_handles[SPI_6].Init.NSS = SPI_NSS_SOFT; - spi_handles[SPI_6].Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; + spi_handles[SPI_6].Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; spi_handles[SPI_6].Init.FirstBit = SPI_FIRSTBIT_LSB; spi_handles[SPI_6].Init.TIMode = SPI_TIMODE_DISABLE; spi_handles[SPI_6].Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; diff --git a/core/mocks/generated/trezorcrypto/bip32.pyi b/core/mocks/generated/trezorcrypto/bip32.pyi index eab0eaaad..542d8fa8f 100644 --- a/core/mocks/generated/trezorcrypto/bip32.pyi +++ b/core/mocks/generated/trezorcrypto/bip32.pyi @@ -31,6 +31,12 @@ class HDNode: place. """ + def derive_fido_path(self, path: Sequence[int]) -> None: + """ + Go through a list of indexes and iteratively derive a child node in + place. + """ + def serialize_public(self, version: int) -> str: """ Serialize the public info from HD node to base58 string. diff --git a/core/mocks/generated/trezorcrypto/se_thd89.pyi b/core/mocks/generated/trezorcrypto/se_thd89.pyi index 01d4208d3..fbba16375 100644 --- a/core/mocks/generated/trezorcrypto/se_thd89.pyi +++ b/core/mocks/generated/trezorcrypto/se_thd89.pyi @@ -135,6 +135,13 @@ def slip21_node() -> bytes: """ +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def slip21_fido_node() -> bytes: + """ + Returns slip21 fido node, seed without passphrase. + """ + + # extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h def authorization_set( authorization_type: int, @@ -182,3 +189,95 @@ def sign_message(msg: bytes) -> bytes: """ Sign message. """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def derive_xmr( + path: Sequence[int] + digest: bytes, +) -> tuple[bytes, bytes]: + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def derive_xmr_privare( + deriv: bytes + index: int, +) -> bytes: + """ + base + H_s(derivation || varint(output_index)) + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def xmr_get_tx_key( + rand: bytes + hash: bytes, +) -> bytes: + """ + base + H_s(derivation || varint(output_index)) + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_seed( + callback: Callable[[int, int], None] | None = None, +) -> bool: + """ + Generate seed from mnemonic without passphrase. + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_u2f_register( + app_id: bytes, + challenge: bytes, +) -> tuple[bytes, bytes, bytes]: + """ + U2F Register. + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def u2f_gen_handle_and_node( + app_id: bytes, +) -> tuple[bytes, HDNode]: + """ + U2F generate handle and HDNode. + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_u2f_validate( + app_id: bytes, + key_handle: bytes, +) -> bool: + """ + U2F Validate Handle. + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_u2f_authenticate( + app_id: bytes, + key_handle: bytes, + challenge: bytes, +) -> tuple[int, bytes]: + """ + U2F Authenticate. + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_sign_digest( + digest: bytes, +) -> bytes: + """ + """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-se-thd89.h +def fido_att_sign_digest( + digest: bytes, +) -> bytes: + """ + """ diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi index 490786ef3..69a92ade0 100644 --- a/core/mocks/generated/trezorio/__init__.pyi +++ b/core/mocks/generated/trezorio/__init__.pyi @@ -129,39 +129,6 @@ class MOTOR: """ -# extmod/modtrezorio/modtrezorio-nfc.h -class NFC: - """ - """ - - def __init__( - self, - ) -> None: - """ - """ - - def pwr_ctrl(self, on_off: bool) -> int: - """ - Control NFC power. - """ - - def wait_card(self, timeout_ms: int) -> int: - """ - Wait for card with timeout. - """ - - def send_recv(self, send: bytearray) -> Tuple[int, bytearray]: - """ - Send receive data through NFC. - """ - - def send_recv_single_shot(self, send: bytearray, timeout_ms: int) -> - Tuple[int, bytearray]: - """ - Wait for card, then send receive data through NFC. - """ - - # extmod/modtrezorio/modtrezorio-poll.h def poll(ifaces: Iterable[int], list_ref: list, timeout_ms: int) -> bool: """ @@ -258,6 +225,11 @@ class USB: Get USB connect state. """ + def connect_ctrl(self, state :bool) -> None: + """ + Control usb connect. + """ + def state(self) -> int: """ Get USB state. diff --git a/core/mocks/generated/trezorio/nfc.pyi b/core/mocks/generated/trezorio/nfc.pyi new file mode 100644 index 000000000..c5420603e --- /dev/null +++ b/core/mocks/generated/trezorio/nfc.pyi @@ -0,0 +1,22 @@ +from typing import * + + +# extmod/modtrezorio/modtrezorio-nfc.h +def pwr_ctrl(on_off: bool) -> bool: + """ + Control NFC power. + """ + + +# extmod/modtrezorio/modtrezorio-nfc.h +def poll_card() -> bool: + """ + Poll card. + """ + + +# extmod/modtrezorio/modtrezorio-nfc.h +def send_recv(apdu: bytes, safe: bool = False) -> tuple[bytes, bytes]: + """ + Send receive data through NFC. + """ diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index 45e2c88bf..256464184 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -198,7 +198,11 @@ def bytewords_decode( # extmod/modtrezorutils/modtrezorutils.c -def enter_lowpower(restart: bool, seconds: int) -> None: +def enter_lowpower( +restart: bool, +seconds: int, +wake_up: bool = False +) ->None: """ Enter lowpower mode. """ diff --git a/core/src/all_modules.py b/core/src/all_modules.py index 9ea8a19bd..369890c6f 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -347,6 +347,8 @@ import trezor.ui.layouts.lvgl.altcoin trezor.ui.layouts.lvgl.common import trezor.ui.layouts.lvgl.common +trezor.ui.layouts.lvgl.lite +import trezor.ui.layouts.lvgl.lite trezor.ui.layouts.lvgl.recovery import trezor.ui.layouts.lvgl.recovery trezor.ui.layouts.lvgl.reset diff --git a/core/src/apps/common/seed.py b/core/src/apps/common/seed.py index 6897307b5..83207ab40 100644 --- a/core/src/apps/common/seed.py +++ b/core/src/apps/common/seed.py @@ -19,9 +19,12 @@ class Slip21Node: def __init__(self, seed: bytes | None = None, data: bytes | None = None) -> None: if utils.USE_THD89: - from trezor.crypto import se_thd89 + if data is not None: + self.data = data + else: + from trezor.crypto import se_thd89 - self.data = se_thd89.slip21_node() + self.data = se_thd89.slip21_node() else: assert seed is None or data is None, "Specify exactly one of: seed, data" if data is not None: @@ -125,10 +128,34 @@ def derive_node_without_passphrase( return node +def derive_fido_node_with_se( + path: Bip32Path, curve_name: str = "nist256p1" +) -> bip32.HDNode: + from trezor.crypto import se_thd89 + + se_thd89.fido_seed() + node = bip32.HDNode( + depth=0, + fingerprint=0, + child_num=0, + chain_code=bytearray(32), + public_key=bytearray(33), + curve_name=curve_name, + ) + node.derive_fido_path(path) + return node + + def derive_slip21_node_without_passphrase(path: Slip21Path) -> Slip21Node: - seed = _get_seed_without_passphrase() - node = Slip21Node(seed) + if utils.USE_THD89: + from trezor.crypto import se_thd89 + + node = Slip21Node(data=se_thd89.slip21_fido_node()) + else: + seed = _get_seed_without_passphrase() + node = Slip21Node(seed) node.derive_path(path) + return node diff --git a/core/src/apps/management/recovery_device/__init__.py b/core/src/apps/management/recovery_device/__init__.py index 0bdabbf8d..92fa1f504 100644 --- a/core/src/apps/management/recovery_device/__init__.py +++ b/core/src/apps/management/recovery_device/__init__.py @@ -27,13 +27,16 @@ DRY_RUN_ALLOWED_FIELDS = ("dry_run", "word_count", "enforce_wordlist", "type") -async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success: +async def recovery_device( + ctx: wire.Context, msg: RecoveryDevice, type: str = "phrase" +) -> Success: """ Recover BIP39/SLIP39 seed into empty device. Recovery is also possible with replugged Trezor. We call this process Persistence. User starts the process here using the RecoveryDevice msg and then they can unplug the device anytime and continue without a computer. """ + _validate(msg) utils.mark_initialization_processing() if not msg.dry_run: @@ -47,12 +50,11 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success: if msg.language is not None: storage.device.set_language(msg.language) i18n_refresh() - # if storage.recovery.is_in_progress(): - # return await recovery_process(ctx) + # if storage.recovery.is_in_progress(): + # return await recovery_process(ctx) try: - await _continue_dialog(ctx, msg) - + # await _continue_dialog(ctx, msg) if isinstance(ctx, wire.DummyContext): utils.play_dead() @@ -66,6 +68,7 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success: newpin = None if not msg.dry_run: + # set up pin if requested if msg.pin_protection: newpin = await request_pin_confirm(ctx, allow_cancel=False) @@ -81,7 +84,7 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success: storage.recovery.set_in_progress(True) storage.recovery.set_dry_run(bool(msg.dry_run)) # workflow.set_default(recovery_homescreen) - result = await recovery_process(ctx) + result = await recovery_process(ctx, type) except BaseException as e: raise e else: diff --git a/core/src/apps/management/recovery_device/homescreen.py b/core/src/apps/management/recovery_device/homescreen.py index b31b177a6..fa90f09c2 100644 --- a/core/src/apps/management/recovery_device/homescreen.py +++ b/core/src/apps/management/recovery_device/homescreen.py @@ -29,10 +29,17 @@ async def recovery_homescreen() -> None: await recovery_process(ctx) -async def recovery_process(ctx: wire.GenericContext) -> Success: - wire.AVOID_RESTARTING_FOR = (MessageType.Initialize, MessageType.GetFeatures) +async def recovery_process(ctx: wire.GenericContext, type: str = "phrase") -> Success: + wire.AVOID_RESTARTING_FOR = ( + MessageType.Initialize, + MessageType.GetFeatures, + ) try: - return await _continue_recovery_process(ctx) + if type == "phrase": + return await _continue_recovery_process(ctx) + else: + return await _continue_recovery_process_lite(ctx) + except recover.RecoveryAborted: dry_run = storage.recovery.is_dry_run() if dry_run: @@ -43,7 +50,44 @@ async def recovery_process(ctx: wire.GenericContext) -> Success: raise wire.ActionCancelled -async def _continue_recovery_process(ctx: wire.GenericContext) -> Success: +async def _continue_recovery_process_lite( + ctx: wire.GenericContext, +) -> Success: + + from trezor.ui.layouts.lvgl.lite import backup_with_lite_import + + words_length = 0 + + secret = await backup_with_lite_import(ctx) + + if secret == 0: + raise RuntimeError("secret is zero") + if isinstance(secret, str): + words_length = len(secret.split()) + + is_slip39 = backup_types.is_slip39_word_count(words_length) + + if is_slip39: + secret, share = recover.process_slip39(words=str(secret)) + else: + secret = recover.process_bip39(words=str(secret)) + share = None + + if is_slip39 and share is None: + raise RuntimeError("SLIP-39 share should not be None") + + backup_type = backup_types.infer_backup_type(is_slip39, share) + + if secret is None: + raise ValueError("Secret cannot be None") + result = await _finish_recovery(ctx, secret, backup_type) + + return result + + +async def _continue_recovery_process( + ctx: wire.GenericContext, +) -> Success: # gather the current recovery state from storage dry_run = storage.recovery.is_dry_run() word_count, backup_type = recover.load_slip39_state() diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index cd9656b17..0ebdf2190 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -7,12 +7,12 @@ from trezor.lvglui.lv_colors import lv_colors from trezor.ui.layouts import ( backup_with_keytag, - backup_with_lite, confirm_action, show_success, show_warning, ) from trezor.ui.layouts.common import button_request +from trezor.ui.layouts.lvgl.lite import backup_with_lite from trezor.ui.layouts.lvgl.recovery import ( # noqa: F401 continue_recovery, request_word, @@ -119,13 +119,12 @@ async def show_dry_run_result( button=_(i18n_keys.BUTTON__CONTINUE), header=_(i18n_keys.TITLE__CORRECT), ) - if not is_slip39 and not __debug__: + # if not is_slip39 and not __debug__: + if not is_slip39: if utils.is_backup_with_lite_1st(): await backup_with_lite(ctx, mnemonics, recovery_check=True) - await backup_with_keytag(ctx, mnemonics, recovery_check=True) else: await backup_with_keytag(ctx, mnemonics, recovery_check=True) - await backup_with_lite(ctx, mnemonics, recovery_check=True) else: if is_slip39: @@ -200,7 +199,7 @@ async def homescreen_dialog( info_func: Callable | None = None, ) -> None: while True: - if await continue_recovery(ctx, button_label, text, subtext, info_func): + if await continue_recovery(ctx, button_label, text, subtext, info_func): # 继续恢复 # go forward in the recovery process break # user has chosen to abort, confirm the choice diff --git a/core/src/apps/monero/misc.py b/core/src/apps/monero/misc.py index 83b8ce1ab..751981e78 100644 --- a/core/src/apps/monero/misc.py +++ b/core/src/apps/monero/misc.py @@ -13,16 +13,31 @@ def get_creds( keychain: Keychain, address_n: Bip32Path, network_type: MoneroNetworkType ) -> AccountCreds: - from apps.monero.xmr import monero + from apps.monero.xmr import crypto_helpers, monero from apps.monero.xmr.credentials import AccountCreds + from trezor import utils - node = keychain.derive(address_n) + if utils.USE_THD89: + from trezor.crypto import se_thd89 - key_seed = node.private_key() - spend_sec, _, view_sec, _ = monero.generate_monero_keys(key_seed) + fake_spend_sec = b"\x00" * 32 + pubkey, hash = se_thd89.derive_xmr(address_n) + spend_sec = crypto_helpers.decodeint(fake_spend_sec) + spend_pub = crypto_helpers.decodepoint(pubkey) - creds = AccountCreds.new_wallet(view_sec, spend_sec, network_type) - return creds + view_sec, view_pub = monero.generate_keys(crypto_helpers.decodeint(hash)) + creds = AccountCreds.new_wallet_ex( + view_sec, spend_sec, view_pub, spend_pub, network_type + ) + return creds + else: + node = keychain.derive(address_n) + + key_seed = node.private_key() + spend_sec, _, view_sec, _ = monero.generate_monero_keys(key_seed) + + creds = AccountCreds.new_wallet(view_sec, spend_sec, network_type) + return creds def compute_tx_key( @@ -32,11 +47,19 @@ def compute_tx_key( rand_mult_num: Scalar, ) -> bytes: from apps.monero.xmr import crypto, crypto_helpers + from trezor import utils - rand_inp = crypto.sc_add_into(None, spend_key_private, rand_mult_num) - passwd = crypto_helpers.keccak_2hash( - crypto_helpers.encodeint(rand_inp) + tx_prefix_hash - ) + if utils.USE_THD89: + from trezor.crypto import se_thd89 + + passwd = se_thd89.xmr_get_tx_key( + crypto_helpers.encodeint(rand_mult_num), tx_prefix_hash + ) + else: + rand_inp = crypto.sc_add_into(None, spend_key_private, rand_mult_num) + passwd = crypto_helpers.keccak_2hash( + crypto_helpers.encodeint(rand_inp) + tx_prefix_hash + ) tx_key = crypto_helpers.compute_hmac(salt, passwd) return tx_key diff --git a/core/src/apps/monero/xmr/credentials.py b/core/src/apps/monero/xmr/credentials.py index fc2039737..9f1c80a04 100644 --- a/core/src/apps/monero/xmr/credentials.py +++ b/core/src/apps/monero/xmr/credentials.py @@ -48,3 +48,26 @@ def new_wallet( address=addr, network_type=network_type, ) + + @classmethod + def new_wallet_ex( + cls, + priv_view_key: crypto.Scalar, + priv_spend_key: crypto.Scalar, + pub_view_key: crypto.Point, + pub_spend_key: crypto.Point, + network_type: MoneroNetworkType = MoneroNetworkType.MAINNET, + ) -> "AccountCreds": + addr = encode_addr( + net_version(network_type), + crypto_helpers.encodepoint(pub_spend_key), + crypto_helpers.encodepoint(pub_view_key), + ) + return cls( + view_key_private=priv_view_key, + spend_key_private=priv_spend_key, + view_key_public=pub_view_key, + spend_key_public=pub_spend_key, + address=addr, + network_type=network_type, + ) diff --git a/core/src/apps/monero/xmr/monero.py b/core/src/apps/monero/xmr/monero.py index e8d214eab..4a1b6d0d1 100644 --- a/core/src/apps/monero/xmr/monero.py +++ b/core/src/apps/monero/xmr/monero.py @@ -143,13 +143,23 @@ def generate_tx_spend_and_key_image( :param received_index: subaddress index this payment was received to :return: """ - if crypto.sc_iszero(ack.spend_key_private): - raise ValueError("Watch-only wallet not supported") + from trezor import utils - # derive secret key with subaddress - step 1: original CN derivation - scalar_step1 = crypto_helpers.derive_secret_key( - recv_derivation, real_output_index, ack.spend_key_private - ) + if utils.USE_THD89: + from trezor.crypto import se_thd89 + + recv_derivation = crypto_helpers.encodepoint(recv_derivation) + # derive secret key with subaddress - step 1: original CN derivation + scalar_step1 = se_thd89.derive_xmr_private(recv_derivation, real_output_index) + scalar_step1 = crypto_helpers.decodeint(scalar_step1) + else: + if crypto.sc_iszero(ack.spend_key_private): + raise ValueError("Watch-only wallet not supported") + + # derive secret key with subaddress - step 1: original CN derivation + scalar_step1 = crypto_helpers.derive_secret_key( + recv_derivation, real_output_index, ack.spend_key_private + ) # step 2: add Hs(SubAddr || a || index_major || index_minor) subaddr_sk = None diff --git a/core/src/apps/ur_registry/chains/bitcoin/transaction.py b/core/src/apps/ur_registry/chains/bitcoin/transaction.py index 4bd52a99a..43e1c2454 100644 --- a/core/src/apps/ur_registry/chains/bitcoin/transaction.py +++ b/core/src/apps/ur_registry/chains/bitcoin/transaction.py @@ -244,8 +244,6 @@ async def run(self): wire.QR_CONTEXT, btc_pubkey_msg ) master_fp = resp.root_fingerprint.to_bytes(4, "big") - # if __debug__: - # master_fp = b'\x92\xbdh}' # pyright: on # Do multiple passes for multisig passes = 1 @@ -336,9 +334,6 @@ async def run(self): # Find key to sign with found = False # Whether we have found a key to sign with - # found_in_sigs = ( - # False # Whether we have found one of our keys in the signatures - # ) our_keys = 0 # path_last_ours = None # The path of the last key that is ours. We will use this if we need to ignore this input because it is already signed. if txinputtype.script_type in ECDSA_SCRIPT_TYPES: @@ -349,7 +344,6 @@ async def run(self): if ( key in psbt_in.partial_sigs ): # This key already has a signature - # found_in_sigs = True continue if ( not found @@ -371,7 +365,6 @@ async def run(self): "Key fingerprint does not match master key" ) elif txinputtype.script_type in SCHNORR_SCRIPT_TYPES: - # found_in_sigs = len(psbt_in.tap_key_sig) > 0 for key, (_, origin) in psbt_in.tap_bip32_paths.items(): # Assume key path signing if ( @@ -399,14 +392,6 @@ async def run(self): if our_keys > passes: passes = our_keys - if not found: # None of our keys were in hd_keypaths or in partial_sigs - # This input is not one of ours - raise Exception("Invalid input params") - # elif not found and found_in_sigs: - # # All of our keys are in partial_sigs, pick the first key that is ours, sign with it, - # # and ignore whatever signature is produced for this input - # raise Exception("Invalid input params") - # append to inputs inputs.append(txinputtype) self.inputs = inputs diff --git a/core/src/apps/ur_registry/chains/hardware_requests/verify_address.py b/core/src/apps/ur_registry/chains/hardware_requests/verify_address.py index 9f50721e5..b734add20 100644 --- a/core/src/apps/ur_registry/chains/hardware_requests/verify_address.py +++ b/core/src/apps/ur_registry/chains/hardware_requests/verify_address.py @@ -44,7 +44,6 @@ async def run(self): else: raise ValueError("Invalid chain") # assert address.address is not None, "Address should not be None" - # print(f"Address: {params["address"]}") # if address.address.lower() != params["address"].lower(): # if __debug__: # print(f"Address mismatch: {address.address} != {params['address']}") diff --git a/core/src/apps/webauthn/credential.py b/core/src/apps/webauthn/credential.py index 1bb627a7e..2f4153c41 100644 --- a/core/src/apps/webauthn/credential.py +++ b/core/src/apps/webauthn/credential.py @@ -5,7 +5,7 @@ import storage.device from trezor import log, utils -from trezor.crypto import bip32, chacha20poly1305, der, hashlib, hmac, random +from trezor.crypto import bip32, chacha20poly1305, der, hashlib, hmac, random, se_thd89 from trezor.crypto.curve import ed25519, nist256p1 from apps.common import cbor, seed @@ -59,6 +59,9 @@ def __init__(self) -> None: self.rp_id: str = "" self.rp_id_hash: bytes = b"" self.user_id: bytes | None = None + self.pub_key: bytes = b"" + self.signature: bytes = b"" + self.u2f_counter: int = 0 def __lt__(self, other: "Credential") -> bool: raise NotImplementedError @@ -82,8 +85,12 @@ def _u2f_sign(self, data: Iterable[bytes]) -> bytes: dig = hashlib.sha256() for segment in data: dig.update(segment) - sig = nist256p1.sign(self._private_key(), dig.digest(), False) - return der.encode_seq((sig[1:33], sig[33:])) + if utils.USE_THD89: + sig = se_thd89.fido_sign_digest(dig.digest()) + return der.encode_seq((sig[0:32], sig[32:])) + else: + sig = nist256p1.sign(self._private_key(), dig.digest(), False) + return der.encode_seq((sig[1:33], sig[33:])) def bogus_signature(self) -> bytes: raise NotImplementedError @@ -171,7 +178,6 @@ def from_cred_id( ) -> "Fido2Credential": if len(cred_id) < CRED_ID_MIN_LENGTH or cred_id[0:4] != _CRED_ID_VERSION: raise ValueError # invalid length or version - key = seed.derive_slip21_node_without_passphrase( [b"SLIP-0022", cred_id[0:4], b"Encryption key"] ).key() @@ -191,6 +197,7 @@ def from_cred_id( ctx = chacha20poly1305(key, iv) ctx.auth(rp_id_hash) data = ctx.decrypt(ciphertext) + if not utils.consteq(ctx.finish(), tag): raise ValueError # inauthentic ciphertext @@ -284,12 +291,25 @@ def _private_key(self) -> bytes: path = [HARDENED | 10022, HARDENED | int.from_bytes(self.id[:4], "big")] + [ HARDENED | i for i in ustruct.unpack(">4L", self.id[-16:]) ] - node = seed.derive_node_without_passphrase(path, _CURVE_NAME[self.curve]) + if utils.USE_THD89: + node = seed.derive_fido_node_with_se(path, _CURVE_NAME[self.curve]) + else: + node = seed.derive_node_without_passphrase(path, _CURVE_NAME[self.curve]) return node.private_key() def public_key(self) -> bytes: if self.curve == common.COSE_CURVE_P256: - pubkey = nist256p1.publickey(self._private_key(), False) + if utils.USE_THD89: + path = [ + HARDENED | 10022, + HARDENED | int.from_bytes(self.id[:4], "big"), + ] + [HARDENED | i for i in ustruct.unpack(">4L", self.id[-16:])] + node = seed.derive_fido_node_with_se(path, _CURVE_NAME[self.curve]) + pubkey = se_thd89.uncompress_pubkey( + _CURVE_NAME[self.curve], node.public_key() + ) + else: + pubkey = nist256p1.publickey(self._private_key(), False) return cbor.encode( { common.COSE_KEY_ALG: self.algorithm, @@ -316,6 +336,8 @@ def sign(self, data: Iterable[bytes]) -> bytes: common.COSE_ALG_ES256, common.COSE_CURVE_P256, ): + if utils.USE_THD89: + self._private_key() return self._u2f_sign(data) elif (self.algorithm, self.curve) == ( common.COSE_ALG_EDDSA, @@ -378,7 +400,12 @@ def _private_key(self) -> bytes: return self.node.private_key() def public_key(self) -> bytes: - return nist256p1.publickey(self._private_key(), False) + if utils.USE_THD89: + if self.node is None: + return b"" + return se_thd89.uncompress_pubkey("nist256p1", self.node.public_key()) + else: + return nist256p1.publickey(self._private_key(), False) def sign(self, data: Iterable[bytes]) -> bytes: return self._u2f_sign(data) @@ -387,21 +414,24 @@ def bogus_signature(self) -> bytes: return der.encode_seq((b"\x0a" * 32, b"\x0a" * 32)) def generate_key_handle(self) -> None: - # derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' - path = [HARDENED | random.uniform(0x8000_0000) for _ in range(0, 8)] - nodepath = [_U2F_KEY_PATH] + path + if utils.USE_THD89: + self.id, self.node = se_thd89.u2f_gen_handle_and_node(self.rp_id_hash) + else: + # derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' + path = [HARDENED | random.uniform(0x8000_0000) for _ in range(0, 8)] + nodepath = [_U2F_KEY_PATH] + path - # prepare signing key from random path, compute decompressed public key - self.node = seed.derive_node_without_passphrase(nodepath, "nist256p1") + # prepare signing key from random path, compute decompressed public key + self.node = seed.derive_node_without_passphrase(nodepath, "nist256p1") - # first half of keyhandle is keypath - keypath = ustruct.pack("<8L", *path) + # first half of keyhandle is keypath + keypath = ustruct.pack("<8L", *path) - # second half of keyhandle is a hmac of rp_id_hash and keypath - mac = hmac(hmac.SHA256, self.node.private_key(), self.rp_id_hash) - mac.update(keypath) + # second half of keyhandle is a hmac of rp_id_hash and keypath + mac = hmac(hmac.SHA256, self.node.private_key(), self.rp_id_hash) + mac.update(keypath) - self.id = keypath + mac.digest() + self.id = keypath + mac.digest() def app_name(self) -> str: from . import knownapps @@ -419,22 +449,51 @@ def from_key_handle(key_handle: bytes, rp_id_hash: bytes) -> "U2fCredential": if len(key_handle) != _KEY_HANDLE_LENGTH: raise ValueError # key length mismatch - # check the keyHandle and generate the signing key - node = U2fCredential._node_from_key_handle(rp_id_hash, key_handle, "<8L") - if node is None: - # prior to firmware version 2.0.8, keypath was serialized in a - # big-endian manner, instead of little endian, like in trezor-mcu. - # try to parse it as big-endian now and check the HMAC. - node = U2fCredential._node_from_key_handle(rp_id_hash, key_handle, ">8L") - if node is None: - # specific error logged in msg_authenticate_genkey - raise ValueError # failed to parse key handle in either direction - - cred = U2fCredential() - cred.id = key_handle - cred.rp_id_hash = rp_id_hash - cred.node = node - return cred + if utils.USE_THD89: + cred = U2fCredential._node_from_handle_by_se(key_handle, rp_id_hash) + return cred + else: + # check the keyHandle and generate the signing key + node = U2fCredential._node_from_key_handle(rp_id_hash, key_handle, "<8L") + if node is None: + # prior to firmware version 2.0.8, keypath was serialized in a + # big-endian manner, instead of little endian, like in trezor-mcu. + # try to parse it as big-endian now and check the HMAC. + node = U2fCredential._node_from_key_handle( + rp_id_hash, key_handle, ">8L" + ) + if node is None: + # specific error logged in msg_authenticate_genkey + raise ValueError # failed to parse key handle in either direction + + cred = U2fCredential() + cred.id = key_handle + cred.rp_id_hash = rp_id_hash + cred.node = node + return cred + + @staticmethod + def _node_from_handle_by_se( + key_handle: bytes, rp_id_hash: bytes + ) -> "U2fCredential": + if len(key_handle) != _KEY_HANDLE_LENGTH: + raise ValueError # key length mismatch + try: + if se_thd89.fido_u2f_validate(rp_id_hash, key_handle): + cred = U2fCredential() + cred.id = key_handle + cred.rp_id_hash = rp_id_hash + # unpack the keypath from the first half of keyhandle + keypath = key_handle[:32] + path = ustruct.unpack("<8L", keypath) + nodepath = [_U2F_KEY_PATH] + list(path) + node = seed.derive_fido_node_with_se(nodepath, "nist256p1") + cred.node = node + return cred + else: + raise ValueError + except Exception: + raise ValueError @staticmethod def _node_from_key_handle( diff --git a/core/src/apps/webauthn/fido2.py b/core/src/apps/webauthn/fido2.py index 796c36888..e967c07de 100644 --- a/core/src/apps/webauthn/fido2.py +++ b/core/src/apps/webauthn/fido2.py @@ -8,7 +8,7 @@ import storage.resident_credentials from storage.fido2 import KEY_AGREEMENT_PRIVKEY, KEY_AGREEMENT_PUBKEY from trezor import config, io, log, loop, ui, utils, workflow -from trezor.crypto import aes, der, hashlib, hmac, random +from trezor.crypto import aes, der, hashlib, hmac, random, se_thd89 from trezor.crypto.curve import nist256p1 from trezor.lvglui.i18n import gettext as _, keys as i18n_keys from trezor.ui.components.common.confirm import Pageable @@ -168,12 +168,15 @@ # register response _U2F_REGISTER_ID = const(0x05) # version 2 registration identifier -_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" +if utils.EMULATOR: + _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`" _BOGUS_APPID_CHROME = b"A" * 32 _BOGUS_APPID_FIREFOX = b"\0" * 32 _BOGUS_APPIDS = (_BOGUS_APPID_CHROME, _BOGUS_APPID_FIREFOX) -_AAGUID = b"\xd6\xd0\xbd\xc3b\xee\xc4\xdb\xde\x8dzenJD\x87" # First 16 bytes of SHA-256("TREZOR 2") +_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") # authentication control byte _AUTH_ENFORCE = const(0x03) # enforce user presence and sign @@ -202,7 +205,7 @@ # FIDO2 configuration. _ALLOW_FIDO2 = True -_ALLOW_RESIDENT_CREDENTIALS = True +_ALLOW_RESIDENT_CREDENTIALS = False _ALLOW_WINK = False # The default attestation type to use in MakeCredential responses. If false, then basic attestation will be used by default. @@ -587,6 +590,26 @@ async def verify_user(keepalive_callback: KeepaliveCallback) -> bool: return ret +async def se_gen_seed(keepalive_callback: KeepaliveCallback) -> bool: + from utime import sleep_ms + + if storage.device._FIDO_SEED_GEN is True: + return True + + while True: + try: + ret = se_thd89.fido_seed() + if ret: + storage.device._FIDO_SEED_GEN = True + return True + else: + keepalive_callback() + sleep_ms(100) + continue + except Exception: + return False + + class State: def __init__(self, cid: int, iface: io.HID) -> None: self.cid = cid @@ -710,6 +733,28 @@ def __eq__(self, other: object) -> bool: return isinstance(other, U2fUnlock) +class U2fSeed(State): + def timeout_ms(self) -> int: + return _U2F_CONFIRM_TIMEOUT_MS + + async def confirm_dialog(self) -> bool: + while True: + try: + ret = se_thd89.fido_seed() + if ret: + storage.device._FIDO_SEED_GEN = True + set_homescreen() + return True + else: + await loop.sleep(100) + continue + except Exception: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, U2fSeed) + + class Fido2State(State): def __init__(self, cid: int, iface: io.HID) -> None: super().__init__(cid, iface) @@ -756,6 +801,9 @@ async def confirm_dialog(self) -> bool | "State": if not await verify_user(KeepaliveCallback(self.cid, self.iface)): return False + if not await se_gen_seed(KeepaliveCallback(self.cid, self.iface)): + return False + set_homescreen() resp = self.process_func(self.req, self.dialog_mgr) if isinstance(resp, State): @@ -1200,6 +1248,11 @@ def msg_register(req: Msg, dialog_mgr: DialogManager) -> Cmd: # doesn't seem to violate the protocol and at least stops Chrome from polling. return cmd_error(req.cid, _ERR_CHANNEL_BUSY) + if not storage.device._FIDO_SEED_GEN: + new_state: State = U2fSeed(req.cid, dialog_mgr.iface) + dialog_mgr.set_state(new_state) + return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED) + # check length of input data if len(req.data) != 64: if __debug__: @@ -1210,6 +1263,7 @@ def msg_register(req: Msg, dialog_mgr: DialogManager) -> Cmd: chal = req.data[:32] cred = U2fCredential() cred.rp_id_hash = req.data[32:] + cred.generate_key_handle() # check equality with last request @@ -1245,13 +1299,15 @@ def basic_attestation_sign(data: Iterable[bytes]) -> bytes: for segment in data: dig.update(segment) if utils.USE_THD89: - raise NotImplementedError + sig = se_thd89.fido_att_sign_digest(dig.digest()) + return der.encode_seq((sig[:32], sig[32:])) else: sig = nist256p1.sign(_FIDO_ATT_PRIV_KEY, dig.digest(), False) - return der.encode_seq((sig[1:33], sig[33:])) + return der.encode_seq((sig[1:33], sig[33:])) def msg_register_sign(challenge: bytes, cred: U2fCredential) -> bytes: + pubkey = cred.public_key() sig = basic_attestation_sign((b"\x00", cred.rp_id_hash, challenge, cred.id, pubkey)) @@ -1289,6 +1345,11 @@ def msg_authenticate(req: Msg, dialog_mgr: DialogManager) -> Cmd: log.warning(__name__, "_SW_WRONG_LENGTH req.data") return msg_error(req.cid, _SW_WRONG_LENGTH) + if not storage.device._FIDO_SEED_GEN: + new_state: State = U2fSeed(req.cid, dialog_mgr.iface) + dialog_mgr.set_state(new_state) + return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED) + # check keyHandleLen khlen = req.data[_REQ_CMD_AUTHENTICATE_KHLEN] auth = overlay_struct(bytearray(req.data), req_cmd_authenticate(khlen)) @@ -1492,10 +1553,10 @@ def cbor_make_credential_process(req: Cmd, dialog_mgr: DialogManager) -> State | cred.algorithm = alg cred.curve = common.COSE_CURVE_P256 break - elif alg == common.COSE_ALG_EDDSA: - cred.algorithm = alg - cred.curve = common.COSE_CURVE_ED25519 - break + # elif alg == common.COSE_ALG_EDDSA: + # cred.algorithm = alg + # cred.curve = common.COSE_CURVE_ED25519 + # break else: return cbor_error(req.cid, _ERR_UNSUPPORTED_ALGORITHM) diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index f09da012f..36807732c 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -159,21 +159,21 @@ def find_message_handler_module(msg_type: int) -> str: if msg_type == MessageType.EthereumSignTypedHashOneKey: return "apps.ethereum.onekey.sign_typed_data_hash" - # # monero - # if msg_type == MessageType.MoneroGetAddress: - # return "apps.monero.get_address" - # if msg_type == MessageType.MoneroGetWatchKey: - # return "apps.monero.get_watch_only" - # if msg_type == MessageType.MoneroTransactionInitRequest: - # return "apps.monero.sign_tx" - # if msg_type == MessageType.MoneroKeyImageExportInitRequest: - # return "apps.monero.key_image_sync" - # if msg_type == MessageType.MoneroGetTxKeyRequest: - # return "apps.monero.get_tx_keys" - # if msg_type == MessageType.MoneroLiveRefreshStartRequest: - # return "apps.monero.live_refresh" - # if __debug__ and msg_type == MessageType.DebugMoneroDiagRequest: - # return "apps.monero.diag" + # monero + if msg_type == MessageType.MoneroGetAddress: + return "apps.monero.get_address" + if msg_type == MessageType.MoneroGetWatchKey: + return "apps.monero.get_watch_only" + if msg_type == MessageType.MoneroTransactionInitRequest: + return "apps.monero.sign_tx" + if msg_type == MessageType.MoneroKeyImageExportInitRequest: + return "apps.monero.key_image_sync" + if msg_type == MessageType.MoneroGetTxKeyRequest: + return "apps.monero.get_tx_keys" + if msg_type == MessageType.MoneroLiveRefreshStartRequest: + return "apps.monero.live_refresh" + if __debug__ and msg_type == MessageType.DebugMoneroDiagRequest: + return "apps.monero.diag" # nem if msg_type == MessageType.NEMGetAddress: diff --git a/core/src/main.py b/core/src/main.py index e75a7b0f4..511b4fe43 100644 --- a/core/src/main.py +++ b/core/src/main.py @@ -48,12 +48,9 @@ import usb # noqa: F401 # start the USB -# import storage.device -# if not storage.device.is_airgap_mode(): -# usb.bus.open(storage.device.get_device_id()) - import storage.device -storage.device.get_device_id() +usb.bus.open(storage.device.get_device_id()) + # initialize the status bar StatusBar.get_instance() diff --git a/core/src/session.py b/core/src/session.py index bba53eb74..6b8e58f7d 100644 --- a/core/src/session.py +++ b/core/src/session.py @@ -1,15 +1,13 @@ -import storage.device -from trezor import loop, utils +from trezor import log, loop, utils from trezor.lvglui import lvgl_tick from trezor.qr import handle_qr_task from trezor.uart import ( - ctrl_wireless_charge, - disconnect_ble, fetch_all, handle_ble_info, handle_fingerprint, handle_uart, handle_usb_state, + stop_mode, ) from trezor.ui import display @@ -25,34 +23,22 @@ if __debug__: import apps.debug - from trezor import log apps.debug.boot() -def stop_mode(reset_timer: bool = False): - ctrl_wireless_charge(True) - disconnect_ble() - utils.enter_lowpower(reset_timer, storage.device.get_autoshutdown_delay_ms()) - - async def handle_stop_mode(): while True: # leave enough time for usb to be detected await loop.sleep(200) - if display.backlight(): # screen is on - return - stop_mode(False) + if display.backlight() == 0: + stop_mode(False) # if the screen is off, enter low power mode after reloop -if display.backlight() == 0: - stop_mode(True) -else: - if utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_START: - utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_CHARGING - apps.base.screen_off_if_possible() +# if display.backlight() == 0: +# stop_mode(True) # run main event loop and specify which screen is the default apps.base.set_homescreen() @@ -70,6 +56,7 @@ async def handle_stop_mode(): loop.schedule(handle_qr_task()) loop.schedule(handle_stop_mode()) + utils.set_up() if utils.show_app_guide(): from trezor.ui.layouts import show_onekey_app_guide @@ -79,4 +66,4 @@ async def handle_stop_mode(): loop.run() if __debug__: - log.debug(__name__, "Restarting main loop") # type: ignore["log" is possibly unbound] + log.debug(__name__, "Restarting main loop") diff --git a/core/src/storage/device.py b/core/src/storage/device.py index 3d0ee20a5..4f5b95b4f 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -84,6 +84,8 @@ _DEVICE_ID_VALUE: str | None = None _TREZOR_COMPATIBLE_VALUE: bool | None = None _NEEDS_BACKUP_VALUE: bool | None = None +_FIDO_SEED_GEN = False + if utils.USE_THD89: import uctypes diff --git a/core/src/trezor/lvglui/i18n/keys.py b/core/src/trezor/lvglui/i18n/keys.py index 08f3bf102..cd456c316 100644 --- a/core/src/trezor/lvglui/i18n/keys.py +++ b/core/src/trezor/lvglui/i18n/keys.py @@ -1763,4 +1763,8 @@ CONTENT__WALLET_MISMATCH_DESC = 804 # Non-standard message signature. CONTENT__NON_STANDARD_MESSAGE_SIGNATURE = 805 +# Send Tokens +TITLE__SEND_TOKENS = 806 +# The following transaction output contains contract data: +CONTENT__FOLLOWING_TRANSACTION_CONTAINS_CONTRACT = 807 # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/de.py b/core/src/trezor/lvglui/i18n/locales/de.py index 098b8d951..e8a685120 100644 --- a/core/src/trezor/lvglui/i18n/locales/de.py +++ b/core/src/trezor/lvglui/i18n/locales/de.py @@ -806,5 +806,7 @@ "Unstimmigkeit der Geldbörse", "Ihre in der App ausgewählte Wallet stimmt nicht mit der Hardware-Wallet überein. Bitte überprüfen Sie dies und versuchen Sie es erneut.", "Nicht standardmäßige Nachrichtensignatur.", + "Token senden", + "Die folgende Transaktionsausgabe enthält Vertragsdaten:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/en.py b/core/src/trezor/lvglui/i18n/locales/en.py index 9a33fbd15..64c6f312f 100644 --- a/core/src/trezor/lvglui/i18n/locales/en.py +++ b/core/src/trezor/lvglui/i18n/locales/en.py @@ -806,5 +806,7 @@ "Wallet Mismatch", "Your selected wallet in the app does not match the hardware wallet. Please check and try again.", "Non-standard message signature.", + "Send Tokens", + "The following transaction output contains contract data:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/es.py b/core/src/trezor/lvglui/i18n/locales/es.py index 9b6208ebc..385a25b5a 100644 --- a/core/src/trezor/lvglui/i18n/locales/es.py +++ b/core/src/trezor/lvglui/i18n/locales/es.py @@ -806,5 +806,7 @@ "Incompatibilidad de billetera", "Tu billetera seleccionada en la aplicación no coincide con la billetera de hardware. Por favor, verifica y vuelve a intentarlo.", "Firma de mensaje no estándar.", + "Enviar tokens", + "La siguiente salida de transacción contiene datos del contrato:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/fr.py b/core/src/trezor/lvglui/i18n/locales/fr.py index 93fafde6f..be6855b3b 100644 --- a/core/src/trezor/lvglui/i18n/locales/fr.py +++ b/core/src/trezor/lvglui/i18n/locales/fr.py @@ -806,5 +806,7 @@ "Incompatibilité de portefeuille", "Votre portefeuille sélectionné dans l'application ne correspond pas au portefeuille matériel. Veuillez vérifier et réessayer.", "Signature de message non standard.", + "Envoyer des jetons", + "La sortie de transaction suivante contient des données de contrat :", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/it.py b/core/src/trezor/lvglui/i18n/locales/it.py index 2fce21075..da31c6d14 100644 --- a/core/src/trezor/lvglui/i18n/locales/it.py +++ b/core/src/trezor/lvglui/i18n/locales/it.py @@ -806,5 +806,7 @@ "Incompatibilità del portafoglio", "Il portafoglio selezionato nell'app non corrisponde al portafoglio hardware. Si prega di controllare e riprovare.", "Firma del messaggio non standard.", + "Invia token", + "Il seguente output di transazione contiene i dati del contratto:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ja.py b/core/src/trezor/lvglui/i18n/locales/ja.py index 764d98159..f63603fe8 100644 --- a/core/src/trezor/lvglui/i18n/locales/ja.py +++ b/core/src/trezor/lvglui/i18n/locales/ja.py @@ -806,5 +806,7 @@ "ウォレットの不一致", "アプリ内の選択されたウォレットがハードウェアウォレットと一致していません。確認してもう一度お試しください。", "非標準のメッセージ署名。", + "トークンを送信", + "次のトランザクション出力には契約データが含まれています。", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ko.py b/core/src/trezor/lvglui/i18n/locales/ko.py index 4bc97eb2c..27b339e0d 100644 --- a/core/src/trezor/lvglui/i18n/locales/ko.py +++ b/core/src/trezor/lvglui/i18n/locales/ko.py @@ -806,5 +806,7 @@ "지갑 불일치", "앱에서 선택한 지갑이 하드웨어 지갑과 일치하지 않습니다. 확인 후 다시 시도해 주세요.", "비표준 메시지 서명.", + "토큰 보내기", + "다음 거래 출력에는 계약 데이터가 포함되어 있습니다.", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/ru.py b/core/src/trezor/lvglui/i18n/locales/ru.py index 58b2ddde8..3882f9c59 100644 --- a/core/src/trezor/lvglui/i18n/locales/ru.py +++ b/core/src/trezor/lvglui/i18n/locales/ru.py @@ -806,5 +806,7 @@ "Несоответствие кошелька", "Выбранный вами кошелек в приложении не совпадает с аппаратным кошельком. Пожалуйста, проверьте и попробуйте снова.", "Нестандартная подпись сообщения.", + "Отправить токены", + "Следующий вывод транзакции содержит данные контракта:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/zh_cn.py b/core/src/trezor/lvglui/i18n/locales/zh_cn.py index 717e2d67a..fa41b1009 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_cn.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_cn.py @@ -806,5 +806,7 @@ "钱包不匹配", "硬件钱包和 app 中选择的钱包不匹配,请检查后再试。", "非标准格式消息签名。", + "发送代币", + "以下交易输出包含合约数据:", ] # fmt: on diff --git a/core/src/trezor/lvglui/i18n/locales/zh_hk.py b/core/src/trezor/lvglui/i18n/locales/zh_hk.py index da45d7592..9ed38866f 100644 --- a/core/src/trezor/lvglui/i18n/locales/zh_hk.py +++ b/core/src/trezor/lvglui/i18n/locales/zh_hk.py @@ -806,5 +806,7 @@ "錢包不匹配", "硬體錢包和 app 中選擇的錢包不匹配,請檢查後再試。", "非標準格式訊息簽名。", + "發送代幣", + "以下交易輸出包含合約資料:", ] # fmt: on diff --git a/core/src/trezor/lvglui/res/nfc-icon-transfering.png b/core/src/trezor/lvglui/res/nfc-icon-transfering.png old mode 100644 new mode 100755 index 80b2cb8b4175663ec233f7aa3dd71e3983894f4d..30a34e09ba63397c8bee7ad2797654e22289ef9e GIT binary patch delta 2830 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 diff --git a/core/src/trezor/lvglui/res/nfc-start.png b/core/src/trezor/lvglui/res/nfc-start.png index 002e09c0cace183378eed48eaa1e3143c608b321..9fe21804ff0fc23f89483bd725c9893ae5b11466 100644 GIT binary patch literal 12842 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 TNeBP615i`ed|a+*5%PZkCKZJ> literal 8779 zcmbVy`8(8K{Pr-`F^0yz3^R=EOJm>740+qjmM!bZE?c%D>ma2!St3g@6p>wovc=d# zSxQs}St6oEANl$|*YgKF*K@Ax{P4Q(`)sdsopWB-IVa^7He#xVVr= zBpeP;PEKZGVlp!`b9HrPU|unzBHe!e%p-+}hzL(lPaz>8#(QD{Pt;OVQ?0D5R8&+H6clW2 zZ7~?kX;19!?EwJb;^v0L2|!rsqoSf9NCrBxFciy*s?`!oQKHvJK-AcDbaeP~mBd{X zWOU?j-@Y9i8w-ixgBT-tA1E;d@IwTd&RQtYd2&N9a*0GKLL8BzA&PhI+|e6%M*AvA z5#{AnUI@>J0tTPXZ7c71KEVF~KIFz@tXJ0hv_Sk*bM zy`$}AcNi2){_Sh>eZs*fXeCnVxoAC;S*LSbTVn(NTmknk<(kN%o9BP}wavlfd%a;k zT{E*W?x}nKt~9i02j7+7-^#bJzr*CXq-~ZnAouO=f?rPY;ow4Vd{3Kcxl7D|Wli9S zqQLsA_oI8!#j-+o-8Rz{h03y4tHUxTTlqGrm;d<8Yre8?y`y-?_2g$d&Lab;kcK0Evs=%_=0T)PvCur^~vC; zIM@c^T;B}a3?z*qgA1cwAb#f8v`t*-nTF%oZ|_;>$DUwrk{Pk~W9~@fVv2@4_|RJz z;=>iW6TgGHpWYim3A@ZO|I^~t=Y(v6=c)tM^RY=&*zcV|BvO(PIU%n@d3=Slq0ZTb zF&)MK0eW7F9dxw2{T7?3q2g(M1t=CD(ByVsta2+L(S6vwwG9S3;`mzB&Q`0av{mwp zv$8WR{z>QsHH_24eLu$$m_M0_$f1WV-96I!JKTa=80xqc*L7j@Eds zPxm?F-x+G&M9u?YgQ4OLja@!zvSeMlXRK(*e)7&W>A$O^_()g0qUJ|-LEgCdD~vv! zOdY)Q^X<&-STTVwAJugZ=TM1xhvZmM znm9!ydP-0ncIUb#mzJn_uV#<`0&G9o-T!fwzJ8&pfm5!w&~wN_k%JiquKKh{K9wBR z%e@QbO&e(F(Y*PFvoO>2Inho@#8i@*|TWT>LMO))^<<5h;Yl?hFh&|A7?pQ;N-@$#!?o|6waT~e8$1`7y89XC!-Jd3h%l>wq+Z9%>$k@qIenN#tJ6w( zYUjl9n{_sPE#IumRVSXnCbl5-0jS`W!|>SScm56ti<^Uywr)v!7sZ>HIwV>VNrvbb z`Kuacf>Vze`Lc{;ZDx}PTza&Y)NRcCb3Xn~V=+Ek?;PvjK8v2)2~sF>vf;$m#@1f( zzSF-?w*S(DK0t@wX|n%2NE$w{I-cz{Pe~Rek&vn`0pNqp6-|x!1T>q}HOM&@fR))P z1Ded4Sv{L5A%=c@i~4MO>!2=fCIBMsOi7?MU3=%04PjuYPN{u}_r0j$vK1mO&ox%& z(o!_KP(T}w=eaxfd+u#)m5u@g*I{c%tuVebxl&b ziQB`j&|CfT=eOv}8bf}&rU?ye6W1`iW#{ohSCK!wE}4)0YgI^nb;$@;|C&+2>)tHO z-wQ?J9ZiAr+KSdY_9=Uh1m61k=%iKE|Go~Db|i)3tG?^!0OC3D|E^|lm-y`&Gj2RM za=U)5j*H)>T4IZl0-?jx`}iX1di&B2A&uuNe{>ew-#T$N#BC*RR0qfZ1TwVVKqffP z7h-!Lj`&*4``Gw%3YfL}$KvW*9Rg1}eqW@((wosnD?M;`jbgFOIN4tuj|VGv;_b>5 zEhuYWjI)uP?QiMco%6~Nst^7pzt2IMz3(0>!hhdoxQ~tMYE?c_prw^1jlG;#o1l>* z=*cdAVVC?*T6Hg>LdV)7-T?98l0!R)7BHa7FphCv;HPUK_a+5=Jv%!1K+H%Q0(*}C zn8$eG_@IAZjCkaYYyx67cCK`XU^Oe=HVy&y$u?x}Wb>+E?jMW6jXwq43 z?!mFbY~_Gzhs5^+yvs~6wN81~pJq6I4;T~Rwg!i==71|Hdm%Tkj6KKe{Q`#FvI^*q z=KE~huUM2Qi(4gJg=$o=$vZ?Y8H~Wf+u;eE{i{#IudJ39-{{bP^*m{8lZVnpzgGPi zN|;7;FEKSZ?HOx4{_z?5WtD@1;7dzlWVP}z3vFlbDIU}BE2u_@D*cg-G}ZM*7@7KT zjMWyuWb5*=xXd8^YxQl}(AZ&Z^g-l*?YFe05F-NV^gi|o0XXONFpG)fkIGNojB1|V zxDR7PTC1X&Z5wV9th97OPgroWw)P^wfSAtMb1ntMi9U1cGSk3IM7A_+KR-zNHdT}w zg~P!Z)kjk~6vKWIDgAW_)+XKS1R?a#`%cTl)F(OacoEUE8xPFSidIF2?Qa0ygxWoU zyui?EW<23$aPqYkA#B%u;~NpUKSzJQO-xKY_37-akBomm^h5F9J@Mfb5JkrW=cK?t z`W5cO6f4>DYt_N;p6PEN56?ZH`&uO&6}7hgZGBjM-8zlOTagN5WUWY2W4SL17T^La zc}h2?(&~3p@3VwWlrFmcKU=*co4W7F;~l6&2xmN z|4GiY&x5=w6vJw94&)__P6Ye9hhZui`?BssN{k-8Xt2huW{289Un;6XA%4^g54OkP z%hrc;lyL6%cMn-yl+<4QJsjA*V>UYyT#2X_gY0kE^aI!Abpo1L`ms!d;tNt`4!j8EOw7dX%sd?|NIloAQ?>mN05v{pC!s@0sPX$- z4zo6bO`={)yb_hgANsA|F@_OzUye7h;<%2Ny3dl4N=s*HGq>YFH8R>7|cz z%{*`6?w1@Ow0oRbC${kOJM`p%gg+m~dU^Vom%RBh+y!;4HrS8l9Klrj5#n?pp1u_Obe(qR!;4N2)WJ<9L1_7 zUAk>KIpnn=&uk`VYrLXHm)a$sQSs}RdRnbevzM1|)A9Jq0{t=T=0SSOtm)elj-qJLGRcX^ zH|zdn)$ZS4eG@Hz;z$xrI_%L&UGlPU=Zt0ZS<3F}jg9i7hPeKEV{9VkH9>Z{yN9i0 zaFe`ZqRe7%aqjrZn)$9p^U;6#lp&|VG0+oT@i$yN&aBmGrE00#LPC5ynzCx%U9ms@ z>w&KNT?e;SP^@v8Q^4An2|<#&N1 zqu}TCPUB9#)S`bkRo;X>arHHs(>Xr;DzlSH)y`4d4I#FPFrseN!+2gUP1U&d4ei&< zG47b4aRK0ZMBdsPK}@+F>*Tp~dHG@6{0xQf@iV5LjLTMymnv){lnJ8pMi6B#JM*^GNNPd*)3(; zZo#8TcFBP#mQ6e9mDpl9h4|pcKsffD)}@Po!;c^oP%pnpUIOU)c^zJJd_ zt*;D}6#%sT?^3vdnrl}s{;fWspfh=#^U$B$ES%{{hm%e|+yahtz`1T&ZzoeQWO?;- zqxn12)(K5ws}*?FtdJKn9A%IE=v7#g=U-w(YqT52^UAcz;LAU@{`{;gU~FBdoTWqa za6{|o;^g7et+&5kHH7;1mr zo&OG6T;9EKoQ_3l{+So0PFBjXB~S#gnY9FTwQq?Lh5AnfA1~|e2%#9WS|8D5={JJ? zQ6i2r$#RyhTpZJl4oSJXe`-vPw(`V`s^}ey{f$|3c(elVQm7v}{NGrJNs9X6d8@99 zFym;xvR3`baN62U-OQpJ6h_D5CRGH*%-}<3Kz~kej z8#W^ip7CdGr-?(Eq_TBKY=ZuqJNh)f)_4keF!EzaIhjVDzPc6aHAaCMg zaLZEif5S^9J8J9Vze18@;f*b}b(R%}?8m+GSaqiSkxFZZ{ zRoh~-wW++eCt*zoR7T}XpdV#gYPI&6t+&s9p0M--=C7O;$^N%VbbalQ;z56SuVNJr zA6~u-x98jLBDNxk`Dun9#%$%a+NC$d9Jf>u3$u5;UB2=s2_yeCEI0;c*wR^VT*4}& z8!L=CSotLNO=h+35Ziu!=V6#I$|1Uz{fcdN-IAjF<7fPtJNZCfD#AAt)xH#7wAk-2 z9A>=Aos<%ueISku=FoJhzs8*qxGoD_%M=N#g-)kQ|9ANY$J0=tvP@5npnx05I^a7S z^CnN9PD}!wr&m_Ys5zK*5I}1Z3$oh!G< zJznAn%@X324D&*rwJ|-iEJPyFoOi^>)Ii2<6EOJFx#@@T0G5J8tT#qSY3RLSKoB^f zhSOPzred@;&=Q!>h9$=In$fo>HpfpL-QDP$@{+UR0(CV>OOi2~0nu z{HVdI*GTBz>;qi-F(Wp1zVXhSHHfN>pqyfyVWLE>wosLV#BKM=du$n7!s48&M8V8r z?zHW-fgFFgbfyBc+Rz&S;8 zOE;c`?O9C}D`9lUO3_fX)eM28M@S$s;e z?v>J?E3QJcm{H+o%-%krFV>wqm<1UwLQm%DgL235SSOJT%yG-0rFob*sQ}fCAv%Z% zCASoS=$L^21E6<>-Z1(ia50jr+mq+1&t^4aK&}>@9OBN7P&MksVb=o7X_MwuUne^V z?q3~;3(-3cxUO)^8VAcE` z>Tr&x{7!M_2@=U4RG~)wUEYxt@z^Ji;My9k5zM+_TtW|{(j`T3Qo574J%85Vs4Ki` z;Ufn06`v1=jj;z~7!d=7eOd^6)6?b}IQGhcpHqZ~2&sWd;BnWxZXZMpFec@ThQc?n zycP)zBS>)4@n%p~ z=RY^I6T0S#IQ)Xhi;lE!JbeS{myX7s;J}xO{2S*fX|sSdRjSNVQQcQh`=w6M-+L#s zAjT6boNWTQA(8ukW3hg7OiQh2iqbvj|!Rnlt_7C zyzhMe--a+M^v6Z|X?Fle?Q!_|W;}EiOw^pv-0)B)+4)3nuMSf7dW7u}(ylu=5(;#E z+m!ib@sCtZ+IFEma@vDhgVDYIn*TSUJ&vB*v+YB4f;-Y9+aw+xYT?ot!1AjiN?2(U zzkaND$FqkCEv{X+$0=|}c}m%0{&v5oy)=dY0T&sY4Bs|{cJp~XLt2}Ey5t3vWtJep z-$nV^tX%HzK+n1Db68f2*Rg2Ur>{<8Fh)vy(30zMZ06~4X1?UfYdKc%34ln)R%v@u zVVK@?2{+G0jGIWI8Nj|UzFhl}M*F*%=cy5%S)ZflB1Bnob3ly>#i~C|`mVGMUwJD& z{dpfiliIzi&b+)DjZ+#|)Kw9Fnv&vBG2Ej%(+66E8aoC5ej6l}!?E0Bv%N~CLU3v3 z1yKS*&dd|-NtK!T-{Oy?P~%x^np>j$YDH zgI8b|==6kp{F~@}$z5h#W!Nuig0adWlwwnLOkgoN-1}H*nu#^_9tHfr1my(bSRQ_E z@2@=DZ;f?>Yp_2fAnU6K)(-i_8R^S}(vs(?_R=&qA`ZeQ{*U&x+x;_BEP!$24@+TF zPwxGkum0<1sMAH6Q-x*e?vO)t)hn+|>aPc|KQBNg@$Ml6%Yj3Z>_I6ZUYhnir(W%a zvrVzh=Sc%5rDJ)`sT%b(3h0LDpe;ZXqF(jYSIO>@$4bBAaWgWJ0PIgC_<8fHf5+m- zF=vw>3XV3ZrOsW3~QaL}be>arT zprAmeRFUwdw>eAAgdOq?k~z7wAWr202AaRAm{9{OKLvBoEqSd+%aWMtGM6sc5$vgx z_4Dc4|l@(s>uZ zFrnq)i%NEr3Oi~E*b&BkhawzyYkbP=B--De8vnt8^*kaYX7rY{W9l}u0Qed-`|*-7 zwMd|W5Sfe>eGM`uZp!ict2>{ve%+^&@kndEbekYcQm4{oa^sHHR^*SL`tmdh(UKm~ zN$bP3Q+y=V&Dv@~T{L?R=k%xdg21osj;nt}VGDFQ#X$zQ_}ce2UQ^PYmwEOp(aHoU zpZSS`EJ>d_pOmZMBrm7ycah4Ma+}X-$l#$*OzI6?KvW1C^XTzu@0c`O(cYuZ|3a1n zFHnO)y>KEj$w!y=8Qemq{#tuH?5@so8saw!rTPPRe)(k#3`3~Qy4ItE+&Y;hY=YRP+Bkk zT9(Os3dl@!(m)a?=F*~i48GJ7MA!=H@(oUdz7$~pe5ww2PNHELkg^$dImJ6w2bqI9 zpF>nodiM_PVT9pGQ9*D8B?y0?lX8APT@Er6CPKOx!@7uCKE7tP;1E75%2}+zd?xzZ z-|4ywJj_8DiYe4rv{@4ihdHu@eOf@nreEO^I_Ne>%%}lk(P;pmHC90vvMN;GDNQ;< z&1Xg<_Xo#wyh$5s<1+#u!uzdmiK*|3c$A-Fa{HJvzaBcVaYFV@iL~Fa1>+c+p_fjUg<^Z zZ7}kMBZD!fzm9uf-{9!at#sdOwFWtawWO9A(itw@68c68c)~rstdrzSj15Mc8sGO$ zRC!K!rVjIXUG^#A9~Yp#_=SHKY)-CKgh`WyeeOP)1 z2Yk89?&(bY8F&UH@FsN~Oo+cwrF<3%-Kbj^z*u~csH+K}l7$$mzz&SXXvWh`!6u*N zPuw_3N=bVsfxGfqEv%4z8iBInZIX)dwgWV2LKQ#2HiMt1_qr6}{az|`KprGZ8a@UI z)PNx!H}us+;RUo_YAzlzJrv5m;e@R`earcmq;oNKwWx{0Z4L1QtG@}6K8ULxXDA5Z z+$6PDvcKstNYhTpIx~p7g3KncKd&%R2T8_pP!m`o%SPJZvOMkE1`A{mpC5b(#2rVg z{7UBYe`(DQ2}+*l#&OY{k9|Q`3tPG>Ww7!CW(GY}f62>zd{t{nc+#SVXk@sXP-PSi zbKD%w67UJ0k%}^mU?@(M(#4^ZU0$^K1AD+_WiZVCpjj8EDoqjr_~-7h>=@`j zea=}0y@w*Wcd|z8{&h^-*&%jDc%<0Z0|D2_|yb@}-ao>uL;;?V< zDuRqHdFaA`45Cg9{yGSFAkVO2{$mmR?1gzQbX3R3GVRO+!@n;y*4Z_EhShl!v+E|6j^XN-LGU%AO0A@CW^3SX+p}A(8 zcb-XL&VA>nNfo>uZOSB+@0vec5sM+jHWTs_Re}d!%#56yaZdM6m4mWQ&Kz}DRIQ`h zfKgeJG+@5H=lp^1DB!^_z~7qc3EnSZ%%2c~ZCt=Sa>b0|i-Mi>Y?)y}qE>#6n9L>n z^}XhAFRQ+*O5|aJ|nm%{SOvBv>pHe diff --git a/core/src/trezor/lvglui/scrs/common.py b/core/src/trezor/lvglui/scrs/common.py index 5cb4f79d8..16b43d2e4 100644 --- a/core/src/trezor/lvglui/scrs/common.py +++ b/core/src/trezor/lvglui/scrs/common.py @@ -349,6 +349,7 @@ async def request(self) -> Any: ) except Result as result: # Result exception was raised, this means this layout is complete. + value = result.value return value diff --git a/core/src/trezor/lvglui/scrs/homescreen.py b/core/src/trezor/lvglui/scrs/homescreen.py index 19a46a0ab..6b0bf9407 100644 --- a/core/src/trezor/lvglui/scrs/homescreen.py +++ b/core/src/trezor/lvglui/scrs/homescreen.py @@ -821,6 +821,12 @@ def __init__(self, prev_scr=None): left_img_src="A:/res/about.png", has_next=False, ) + self.nfc_test_import = ListItemBtn( + self.container, + "NFC test import", + left_img_src="A:/res/about.png", + has_next=False, + ) self.general = ListItemBtn( self.container, _(i18n_keys.ITEM__GENERAL), @@ -927,10 +933,15 @@ def on_click(self, event_obj): if target == self.anim_test: AnimationSettings(self) if target == self.nfc_test: - from trezor.ui.layouts import backup_with_lite + from trezor.ui.layouts.lvgl.lite import backup_with_lite from trezor import wire workflow.spawn(backup_with_lite(wire.DUMMY_CONTEXT, b"")) + if target == self.nfc_test_import: + from trezor.ui.layouts.lvgl.lite import backup_with_lite_import + from trezor import wire + + workflow.spawn(backup_with_lite_import(wire.DUMMY_CONTEXT)) def _load_scr(self, scr: "Screen", back: bool = False) -> None: lv.scr_load(scr) @@ -1189,12 +1200,11 @@ def __init__(self, prev_scr=None): self.onekey = ListItemBtn( self.container, _(i18n_keys.ITEM__ONEKEY_WALLET), - # _(i18n_keys.CONTENT__BTC_AND_EVM_COMPATIBLE_NETWORKS), - _(i18n_keys.CONTENT__COMING_SOON), + "BTC·EVM", left_img_src="A:/res/ok-logo-48.png", ) self.onekey.text_layout_vertical(pad_top=17, pad_ver=20) - self.onekey.disable() + # self.onekey.disable() # self.onekey.add_flag(lv.obj.FLAG.HIDDEN) self.okx = ListItemBtn( @@ -1240,11 +1250,11 @@ def on_click(self, event_obj): ) return ConnectWallet( + _(i18n_keys.ITEM__ONEKEY_WALLET), + "Ethereum, Polygon, Avalanche, Base and other EVM networks.", None, - None, - None, - encoder=encoder, - subtitle=_(i18n_keys.CONTENT__OPEN_ONEKEY_SCAN_THE_QRCODE), + "A:/res/ok-logo-96.png", + encoder, ) elif target == self.mm: qr_data = ( @@ -1304,7 +1314,7 @@ def __init__(self, prev_scr=None): left_img_src="A:/res/icon-lite-48.png", ) # hide lite backup for now - self.lite.add_flag(lv.obj.FLAG.HIDDEN) + # self.lite.add_flag(lv.obj.FLAG.HIDDEN) self.keytag = ListItemBtn( self.container, @@ -1377,50 +1387,57 @@ def __init__( ) self.qr.align_to(self.subtitle, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40) - if wallet_name and support_chains: - self.panel = lv.obj(self.content_area) - self.panel.set_size(456, lv.SIZE.CONTENT) - self.panel.add_style( - StyleWrapper() - .bg_color(lv_colors.ONEKEY_GRAY_3) - .bg_opa() - .radius(40) - .border_width(0) - .pad_hor(24) - .pad_ver(12) - .text_color(lv_colors.WHITE), - 0, - ) - self.label_top = lv.label(self.panel) - self.label_top.set_text(_(i18n_keys.LIST_KEY__SUPPORTED_CHAINS)) - self.label_top.add_style( - StyleWrapper().text_font(font_GeistSemiBold26).pad_ver(4).pad_hor(0), 0 - ) - self.label_top.align(lv.ALIGN.TOP_LEFT, 0, 0) - self.line = lv.line(self.panel) - self.line.set_size(408, 1) - self.line.add_style( - StyleWrapper().bg_color(lv_colors.ONEKEY_GRAY_2).bg_opa(), 0 - ) - self.line.align_to(self.label_top, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 9) - self.label_bottom = lv.label(self.panel) - self.label_bottom.set_width(408) - self.label_bottom.add_style( - StyleWrapper().text_font(font_GeistRegular26).pad_ver(12).pad_hor(0), 0 - ) - # self.content_area.clear_flag(lv.obj.FLAG.SCROLL_ELASTIC) - # self.content_area.clear_flag(lv.obj.FLAG.SCROLL_MOMENTUM) - self.content_area.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) - self.label_bottom.set_long_mode(lv.label.LONG.WRAP) - self.label_bottom.set_text(support_chains) - self.label_bottom.align_to(self.line, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 0) - self.panel.align_to(self.qr, lv.ALIGN.OUT_BOTTOM_MID, 0, 32) + self.panel = lv.obj(self.content_area) + self.panel.set_size(456, lv.SIZE.CONTENT) + self.panel.add_style( + StyleWrapper() + .bg_color(lv_colors.ONEKEY_GRAY_3) + .bg_opa() + .radius(40) + .border_width(0) + .pad_hor(24) + .pad_ver(12) + .text_color(lv_colors.WHITE), + 0, + ) + self.label_top = lv.label(self.panel) + self.label_top.set_text(_(i18n_keys.LIST_KEY__SUPPORTED_CHAINS)) + self.label_top.add_style( + StyleWrapper().text_font(font_GeistSemiBold26).pad_ver(4).pad_hor(0), 0 + ) + self.label_top.align(lv.ALIGN.TOP_LEFT, 0, 0) + self.line = lv.line(self.panel) + self.line.set_size(408, 1) + self.line.add_style( + StyleWrapper().bg_color(lv_colors.ONEKEY_GRAY_2).bg_opa(), 0 + ) + self.line.align_to(self.label_top, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 9) + self.label_bottom = lv.label(self.panel) + self.label_bottom.set_width(408) + self.label_bottom.add_style( + StyleWrapper().text_font(font_GeistRegular26).pad_ver(12).pad_hor(0), 0 + ) + self.scrolling = False + self.content_area.clear_flag(lv.obj.FLAG.SCROLL_ELASTIC) + self.content_area.clear_flag(lv.obj.FLAG.SCROLL_MOMENTUM) + self.content_area.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) + self.label_bottom.set_long_mode(lv.label.LONG.WRAP) + self.label_bottom.set_text(support_chains) + self.label_bottom.align_to(self.line, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 0) + self.panel.align_to(self.qr, lv.ALIGN.OUT_BOTTOM_MID, 0, 32) self.nav_back.add_event_cb(self.on_nav_back, lv.EVENT.CLICKED, None) self.add_event_cb(self.on_nav_back, lv.EVENT.GESTURE, None) - + self.add_event_cb(self.on_scroll_begin, lv.EVENT.SCROLL_BEGIN, None) + self.add_event_cb(self.on_scroll_end, lv.EVENT.SCROLL_END, None) if encoder is not None: workflow.spawn(self.update_qr()) + def on_scroll_begin(self, event_obj): + self.scrolling = True + + def on_scroll_end(self, event_obj): + self.scrolling = False + def on_nav_back(self, event_obj): code = event_obj.code target = event_obj.get_target() @@ -1446,6 +1463,9 @@ async def update_qr(self): if stop_single in racer.finished: self.destroy() return + if self.scrolling: + await loop.sleep(5000) + continue assert self.encoder is not None qr_data = self.encoder.next_part() self.qr.update(qr_data, len(qr_data)) diff --git a/core/src/trezor/lvglui/scrs/initscreen.py b/core/src/trezor/lvglui/scrs/initscreen.py index a1eac1da9..44df805fe 100644 --- a/core/src/trezor/lvglui/scrs/initscreen.py +++ b/core/src/trezor/lvglui/scrs/initscreen.py @@ -1,6 +1,11 @@ -from trezor import utils +from trezor import utils, workflow from trezor.langs import langs_keys, langs_values from trezor.lvglui.i18n import gettext as _, i18n_refresh, keys as i18n_keys +from trezor.messages import RecoveryDevice, ResetDevice +from trezor.wire import DUMMY_CONTEXT + +from apps.management.recovery_device import recovery_device +from apps.management.reset_device import reset_device from .common import FullSizeWindow, Screen, lv # noqa: F401,F403,F405 from .components.container import ContainerFlexCol @@ -67,10 +72,6 @@ def eventhandler(self, event_obj): if utils.lcd_resume(): return if target == self.btn_yes: - from trezor import workflow - from trezor.wire import DUMMY_CONTEXT - from apps.management.reset_device import reset_device - from trezor.messages import ResetDevice # pyright: off workflow.spawn( @@ -81,26 +82,74 @@ def eventhandler(self, event_obj): language=language, pin_protection=True, ), - ) + ), ) elif target == self.btn_no: - from apps.management.recovery_device import recovery_device - from trezor.messages import RecoveryDevice - from trezor import workflow - from trezor.wire import DUMMY_CONTEXT - workflow.spawn( - recovery_device( - DUMMY_CONTEXT, - RecoveryDevice( - enforce_wordlist=True, - language=language, - pin_protection=True, - ), - ) - ) # pyright: on + SelectImportType() + elif target == self.nav_back.nav_btn: pass else: return self.destroy(100) + + +class SelectImportType(FullSizeWindow): + def __init__(self): + super().__init__( + _(i18n_keys.TITLE__IMPORT_WALLET), + _(i18n_keys.CONTENT__SELECT_THE_WAY_YOU_WANT_TO_IMPORT), + anim_dir=0, + ) + self.add_nav_back() + optional_str = _(i18n_keys.TITLE__RECOVERY_PHRASE) + "\n" + "OneKey Lite" + self.choices = RadioTrigger(self, optional_str) + self.add_event_cb(self.on_ready, lv.EVENT.READY, None) + self.add_event_cb(self.on_back, lv.EVENT.CLICKED, None) + self.add_event_cb(self.on_nav_back, lv.EVENT.GESTURE, None) + + def on_nav_back(self, event_obj): + code = event_obj.code + if code == lv.EVENT.GESTURE: + _dir = lv.indev_get_act().get_gesture_dir() + if _dir == lv.DIR.RIGHT: + lv.event_send(self.nav_back.nav_btn, lv.EVENT.CLICKED, None) + + def on_ready(self, event_obj): + code = event_obj.code + if code == lv.EVENT.CLICKED: + if utils.lcd_resume(): + return + self.show_dismiss_anim() + selected_index = self.choices.get_selected_index() + if selected_index == 0: + workflow.spawn( + recovery_device( + DUMMY_CONTEXT, + RecoveryDevice( + enforce_wordlist=True, + language=language, + pin_protection=True, + ), + "phrase", + ) + ) + elif selected_index == 1: + workflow.spawn( + recovery_device( + DUMMY_CONTEXT, + RecoveryDevice( + enforce_wordlist=True, + language=language, + pin_protection=True, + ), + "lite", + ) + ) + + def on_back(self, event_obj): + target = event_obj.get_target() + if target == self.nav_back.nav_btn: + self.channel.publish(0) + self.show_dismiss_anim() diff --git a/core/src/trezor/lvglui/scrs/nfc.py b/core/src/trezor/lvglui/scrs/nfc.py index bac7f1c6a..31de6ffdc 100644 --- a/core/src/trezor/lvglui/scrs/nfc.py +++ b/core/src/trezor/lvglui/scrs/nfc.py @@ -1,12 +1,476 @@ +from trezorio import nfc + +from trezor import io, loop +from trezor.crypto import bip39 + from ..i18n import gettext as _, keys as i18n_keys from . import lv from .common import FullSizeWindow +LITE_CARD_ERROR_REPONSE = -1 +LITE_CARD_SUCCESS_REPONSE = 0 +LITE_CARD_ERROR = 1 +LITE_CARD_FIND = 2 +LITE_CARD_CONNECT_FAILURE = 3 +LITE_CARD_NOT_SAME = 4 +LITE_CARD_HAS_BEEN_RESET = 5 +LITE_CARD_PIN_ERROR = 6 +LITE_CARD_NO_BACKUP = 8 +LITE_CARD_UNSUPPORTED_WORD_COUNT = 9 +LITE_CARD_DISCONNECT = 99 +LITE_CARD_OPERATE_SUCCESS = 2 +LITE_CARD_SUCCESS_STATUS = b"\x90\x00" +LITE_CARD_DISCONECT_STATUS = b"\x99\x99" + +CMD_GET_SERIAL_NUMBER = b"\x80\xcb\x80\x00\x05\xdf\xff\x02\x81\x01" +CMD_OLD_APPLET = b"\x00\xa4\x04\x00\x08\xD1\x56\x00\x01\x32\x83\x40\x01" +CMD_NEW_APPLET = ( + b"\x00\xa4\x04\x00\x0E\x6F\x6E\x65\x6B\x65\x79\x2E\x62\x61\x63\x6B\x75\x70\x01" +) +CMD_GET_PIN_RETRY_COUNT = b"\x80\xcb\x80\x00\x05\xdf\xff\x02\x81\x02" +CMD_RESET_CARD = b"\x80\xcb\x80\x00\x05\xdf\xfe\x02\x82\x05" +CMD_GET_PIN_STATUS = b"\x80\xcb\x80\x00\x05\xdf\xff\x02\x81\x05" +CMD_SELECT_PRIMARY_SAFETY = b"\x00\xa4\x04\x00" +CMD_EXPORT_DATA = b"\x80\x4b\x00\x00" +CMD_BACKUP_DATA = b"\x80\x3b\x00\x00" +CMD_SETUP_NEW_PIN = b"\x80\xcb\x80\x00\x0e\xdf\xfe\x0b\x82\x04\x08\x00\x06" +CMD_GET_BACKUP_STATUS = b"\x80\x6A\x00\x00" +CMD_VERIFY_PIN = b"\x80\x20\x00\x00\x07\x06" + + +async def handle_sw1sw2_connect_error(self): + self.channel.publish(LITE_CARD_CONNECT_FAILURE) + await loop.sleep(180) + self.clean() + self.destroy() + + +def perform_ticks(motor_ctl, num_ticks): + for _ticks in range(num_ticks): + motor_ctl.tick() + + +async def handle_cleanup(self, data): + self.channel.publish(data) + await loop.sleep(180) + self.clean() + self.destroy() + + +async def run_card_search(self): + while self.searching: + await loop.sleep(1000) + if nfc.poll_card(): + MOTOR_CTL = io.MOTOR() + perform_ticks(MOTOR_CTL, 80) + self.searching = False + self.channel.publish(LITE_CARD_FIND) + self.clean() + self.destroy() + return + else: + pass + + +async def get_card_num(self): + card_num = None + card_num, sw1sw2 = nfc.send_recv(CMD_GET_SERIAL_NUMBER) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return card_num, LITE_CARD_ERROR_REPONSE + elif sw1sw2 == LITE_CARD_SUCCESS_STATUS: + pass + else: + _, sw1sw2 = nfc.send_recv(CMD_SELECT_PRIMARY_SAFETY) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return card_num, LITE_CARD_ERROR_REPONSE + card_num, sw1sw2 = nfc.send_recv(CMD_GET_SERIAL_NUMBER) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return card_num, LITE_CARD_ERROR_REPONSE + + if isinstance(card_num, bytes): + card_num_str = card_num.decode("utf-8") + else: + card_num_str = str(card_num) + + return card_num_str, LITE_CARD_SUCCESS_REPONSE + + +def get_card_type(card_num_str): + card_type = None + if len(card_num_str) >= 5: + if card_num_str[4] == "T" and card_num_str[5] == "2": + card_type = "OLD" + elif card_num_str[4] == "B" and card_num_str[5] == "A": + card_type = "NEW" + return card_type + + +async def check_card_data(self): + + card_num, status = await get_card_num(self) + if status == LITE_CARD_ERROR_REPONSE: + return + pinresp, pinsw1sw2 = nfc.send_recv(CMD_GET_PIN_STATUS) + if pinsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + if pinsw1sw2 == LITE_CARD_SUCCESS_STATUS and pinresp == b"\x02": + if card_num is not None: + data = "2" + str(card_num) + await handle_cleanup(self, data) + return + elif pinsw1sw2 == LITE_CARD_SUCCESS_STATUS: + if card_num is not None: + data = "3" + str(card_num) + await handle_cleanup(self, data) + return + else: + await handle_cleanup(self, LITE_CARD_ERROR_REPONSE) + return + + +async def check_best_try_restcard(self): + numresp, numsw1sw2 = nfc.send_recv(CMD_GET_PIN_RETRY_COUNT) + if numsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return LITE_CARD_ERROR_REPONSE + + if numresp in [b"\x01", b"\x00"] and numsw1sw2 == LITE_CARD_SUCCESS_STATUS: + + _, restsw1sw2 = nfc.send_recv(CMD_RESET_CARD, True) + if restsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return LITE_CARD_ERROR_REPONSE + await handle_cleanup(self, LITE_CARD_HAS_BEEN_RESET) + return LITE_CARD_ERROR_REPONSE + return LITE_CARD_SUCCESS_REPONSE + + +async def start_import_pin_mnemonicmphrase(self, pin): + + card_num, status = await get_card_num(self) + if status == LITE_CARD_ERROR_REPONSE: + return + card_type = get_card_type(card_num) + + if card_type == "OLD": + _, sw1sw2 = nfc.send_recv(CMD_SELECT_PRIMARY_SAFETY) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + elif card_type == "NEW": + pass + else: + return LITE_CARD_CONNECT_FAILURE + + if card_type == "OLD": + status = await check_best_try_restcard(self) + if status == LITE_CARD_ERROR_REPONSE: + return + + if card_type == "NEW": + resp, sw1sw2 = nfc.send_recv(CMD_NEW_APPLET) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + if card_type == "NEW": + status = await check_best_try_restcard(self) + if status == LITE_CARD_ERROR_REPONSE: + return + + pinresp, pinsw1sw2 = nfc.send_recv(CMD_GET_PIN_STATUS) + if pinsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + if pinsw1sw2 == LITE_CARD_SUCCESS_STATUS and pinresp == b"\x02": + await handle_cleanup(self, LITE_CARD_NO_BACKUP) + return + if card_type == "NEW": + resp, sw1sw2 = nfc.send_recv(CMD_GET_BACKUP_STATUS) + state = resp[0] + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + elif (state & 0x02) != 0x02: + await handle_cleanup(self, LITE_CARD_NO_BACKUP) + return + if card_type == "OLD": + resp, sw1sw2 = nfc.send_recv(CMD_OLD_APPLET) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + pin_bytes = "".join(pin).encode() + command_data = CMD_VERIFY_PIN + pin_bytes + resp, sw1sw2 = nfc.send_recv(command_data, True) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + if sw1sw2[0] == 0x63 and (sw1sw2[1] & 0xF0) == 0xC0: + retry_count = sw1sw2[1] & 0x0F + print(f"PIN verification failed. Remaining retries: {retry_count}") + pin_status = f"63C{retry_count:X}" + await handle_cleanup(self, pin_status) + return + + if card_type == "OLD": + exportresp, exportsw1sw2 = nfc.send_recv(CMD_EXPORT_DATA) + else: + exportresp, exportsw1sw2 = nfc.send_recv(CMD_EXPORT_DATA, True) + if exportsw1sw2 == LITE_CARD_DISCONECT_STATUS or len(exportresp) < 8: + await handle_sw1sw2_connect_error(self) + return + decoder = MnemonicEncoder() + encoded_mnemonic_str, _, _ = decoder.parse_card_data(exportresp) + decoded_mnemonics = decoder.decode_mnemonics(encoded_mnemonic_str) + word_count = len(decoded_mnemonics.split()) + if word_count in [15, 21]: + await handle_cleanup(self, LITE_CARD_UNSUPPORTED_WORD_COUNT) + return + await handle_cleanup(self, decoded_mnemonics) + + +async def start_check_pin_mnemonicmphrase(self, pin, mnemonic, card_num): + + card_num_again, status = await get_card_num(self) + if status == LITE_CARD_ERROR_REPONSE: + return + card_type = get_card_type(card_num) + + if card_type == "OLD": + _, sw1sw2 = nfc.send_recv(CMD_SELECT_PRIMARY_SAFETY) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + elif card_type == "NEW": + pass + else: + return LITE_CARD_CONNECT_FAILURE + + if card_num != card_num_again: + await handle_cleanup(self, LITE_CARD_NOT_SAME) + return + + if card_type == "OLD": + status = await check_best_try_restcard(self) + if status == LITE_CARD_ERROR_REPONSE: + return + + if card_type == "OLD": + _, sw1sw2 = nfc.send_recv(CMD_OLD_APPLET) + else: + _, sw1sw2 = nfc.send_recv(CMD_NEW_APPLET) + + if card_type == "NEW": + status = await check_best_try_restcard(self) + if status == LITE_CARD_ERROR_REPONSE: + return + + # verify pin + pin_bytes = "".join(pin).encode() + command_data = CMD_VERIFY_PIN + pin_bytes + _, sw1sw2 = nfc.send_recv(command_data, True) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + if sw1sw2[0] == 0x63 and (sw1sw2[1] & 0xF0) == 0xC0: + retry_count = sw1sw2[1] & 0x0F + print(f"PIN verification failed. Remaining retries: {retry_count}") + pin_status = f"63C{retry_count:X}" + self.stop_animation() + await handle_cleanup(self, pin_status) + return retry_count + + if card_type == "NEW": + command_data = CMD_SETUP_NEW_PIN + pin_bytes + _, sw1sw2 = nfc.send_recv(command_data, True) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + # verify pin + pin_bytes = "".join(pin).encode() + command_data = CMD_VERIFY_PIN + pin_bytes + _, sw1sw2 = nfc.send_recv(command_data, True) + + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + encoder = MnemonicEncoder() + encoded_mnemonic_str = encoder.encode_mnemonics(mnemonic) + version = "01" + lang = "00" + meta = "ffff" + version + lang + payload = encoded_mnemonic_str + meta + payload_bytes = bytes( + int(payload[i : i + 2], 16) for i in range(0, len(payload), 2) + ) + + seed_length = len(payload_bytes) + lc = seed_length.to_bytes(1, "big") + apdu_command = CMD_BACKUP_DATA + lc + payload_bytes + if card_type == "OLD": + _, importsw1sw2 = nfc.send_recv(apdu_command) + else: + _, importsw1sw2 = nfc.send_recv(apdu_command, True) + if importsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + if importsw1sw2 == LITE_CARD_SUCCESS_STATUS: + await handle_cleanup(self, LITE_CARD_OPERATE_SUCCESS) + return + self.channel.publish(LITE_CARD_CONNECT_FAILURE) + await loop.sleep(180) + self.clean() + self.destroy() + return + + +async def start_set_pin_mnemonicmphrase(self, pin, mnemonic, card_num): + + card_num_again, status = await get_card_num(self) + if status == LITE_CARD_ERROR_REPONSE: + return + card_type = get_card_type(card_num_again) + if card_type == "OLD": + _, restsw1sw2 = nfc.send_recv(CMD_RESET_CARD, True) + if restsw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + if card_num != card_num_again: + await handle_cleanup(self, LITE_CARD_NOT_SAME) + return + + # reset card + if card_type == "OLD": + _, sw1sw2 = nfc.send_recv(CMD_RESET_CARD, True) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + # select app + if card_type == "NEW": + _, sw1sw2 = nfc.send_recv(CMD_NEW_APPLET) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + # set pin + pin_bytes = "".join(pin).encode() + command_data = CMD_SETUP_NEW_PIN + pin_bytes + + _, sw1sw2 = nfc.send_recv(command_data, True) + if sw1sw2 == LITE_CARD_DISCONECT_STATUS: + await handle_sw1sw2_connect_error(self) + return + + if card_type == "OLD": + _, sw1sw2 = nfc.send_recv(CMD_OLD_APPLET) + + # verify pin + pin_bytes = "".join(pin).encode() + command_data = CMD_VERIFY_PIN + pin_bytes + + restsw1sw2, sw1sw2 = nfc.send_recv(command_data, True) + + encoder = MnemonicEncoder() + encoded_mnemonic_str = encoder.encode_mnemonics(mnemonic) + version = "01" + lang = "00" + meta = "ffff" + version + lang + payload = encoded_mnemonic_str + meta + payload_bytes = bytes( + int(payload[i : i + 2], 16) for i in range(0, len(payload), 2) + ) + seed_length = len(payload_bytes) + lc = seed_length.to_bytes(1, "big") + apdu_command = CMD_BACKUP_DATA + lc + payload_bytes + if card_type == "OLD": + _, importsw1sw2 = nfc.send_recv(apdu_command) + else: + _, importsw1sw2 = nfc.send_recv(apdu_command, True) + + if importsw1sw2 == LITE_CARD_DISCONECT_STATUS: + self.stop_animation() + await handle_sw1sw2_connect_error(self) + return + if importsw1sw2 == LITE_CARD_SUCCESS_STATUS: + await handle_cleanup(self, LITE_CARD_OPERATE_SUCCESS) + return + await handle_cleanup(self, LITE_CARD_CONNECT_FAILURE) + return + + +class MnemonicEncoder: + def encode_mnemonics(self, seed): + n = 2048 + words = seed.split() + i = 0 + while words: + w = words.pop() + k = bip39.find(w) + i = i * n + k + result_str = str(i) + if len(result_str) % 2 != 0: + result_str = "0" + result_str + return result_str + + def int_to_hex_str(self, num): + """Convert an integer to a hexadecimal string.""" + hex_str = hex(num)[2:] # Convert to hex and remove the '0x' prefix + if len(hex_str) % 2: # Make sure the length is even + hex_str = "0" + hex_str + return hex_str + + def fromhex(self, hex_str): + """Convert a hex string to a byte array.""" + return bytes(int(hex_str[i : i + 2], 16) for i in range(0, len(hex_str), 2)) + + def bytes_to_hex_str(self, byte_data): + return "".join(f"{byte:02x}" for byte in byte_data) + + def decode_mnemonics(self, encoded_mnemonic_str): + n = 2048 + encoded_int = int(encoded_mnemonic_str, 10) + words = [] + + while encoded_int > 0: + index = int(encoded_int % n) + encoded_int = encoded_int // n + words.append(bip39.get_word(index)) + # v1 fix + fix_fill_count = 0 + supported_mnemonic_length = [12, 15, 18, 21, 24] + for length in supported_mnemonic_length: + if len(words) == length: + break + if len(words) < length: + fix_fill_count = length - len(words) + break + words.extend([bip39.get_word(0)] * fix_fill_count) + + return " ".join(words) + + def parse_card_data(self, data): + + encoded_mnemonic_bytes = data[:-4] + version_bytes = data[-4:-3] + lang_bytes = data[-3:-2] + encoded_mnemonic_str = self.bytes_to_hex_str(encoded_mnemonic_bytes) + version = self.bytes_to_hex_str(version_bytes) + lang = self.bytes_to_hex_str(lang_bytes) + + return encoded_mnemonic_str, version, lang + class SearchDeviceScreen(FullSizeWindow): def __init__(self): super().__init__( - _(i18n_keys.TITLE__SEARCHING), + _(i18n_keys.TITLE__CONNECTING), _(i18n_keys.CONTENT__KEEP_LITE_DEVICE_TOGETHER_BACKUP_COMPLETE), cancel_text=_(i18n_keys.BUTTON__CANCEL), anim_dir=0, @@ -14,11 +478,9 @@ def __init__(self): self.img_bg = lv.img(self.content_area) self.img_bg.set_src("A:/res/nfc-bg.png") self.img_bg.align_to(self.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 125) - self.img_searching = lv.img(self.content_area) self.img_searching.set_src("A:/res/nfc-icon-searching.png") self.img_searching.align_to(self.img_bg, lv.ALIGN.CENTER, 0, 0) - self.anim = lv.anim_t() self.anim.init() self.anim.set_var(self.img_bg) @@ -28,6 +490,8 @@ def __init__(self): self.anim.set_path_cb(lv.anim_t.path_linear) self.anim.set_custom_exec_cb(lambda _a, val: self.set_angle(val)) lv.anim_t.start(self.anim) + self.searching = True + loop.schedule(run_card_search(self)) def set_angle(self, angle): try: @@ -44,26 +508,30 @@ def __init__(self): cancel_text=_(i18n_keys.BUTTON__CANCEL), anim_dir=0, ) - self.img_bg = lv.img(self.content_area) - self.img_bg.set_src("A:/res/nfc-bg.png") - self.img_bg.align_to(self.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 125) - self.img_searching = lv.img(self.content_area) self.img_searching.set_src("A:/res/nfc-icon-transfering.png") - self.img_searching.align_to(self.img_bg, lv.ALIGN.CENTER, 0, 0) - - self.anim = lv.anim_t() - self.anim.init() - self.anim.set_var(self.img_bg) - self.anim.set_values(0, 3600) - self.anim.set_time(1000) - self.anim.set_repeat_count(0xFFFF) # infinite - self.anim.set_path_cb(lv.anim_t.path_ease_in_out) - self.anim.set_custom_exec_cb(lambda _a, val: self.set_angle(val)) - lv.anim_t.start(self.anim) + self.img_searching.align_to(self.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 237) + self.searching = True def set_angle(self, angle): try: self.img_bg.set_angle(angle) except Exception: pass + + def check_card_data(self): + loop.schedule(check_card_data(self)) + + def stop_animation(self): + self.searching = False + + def set_pin_mnemonicmphrase(self, pin, card_num, mnemonics): + + loop.schedule(start_set_pin_mnemonicmphrase(self, pin, mnemonics, card_num)) + + def check_pin_mnemonicmphrase(self, pin, card_num, mnemonics): + + loop.schedule(start_check_pin_mnemonicmphrase(self, pin, mnemonics, card_num)) + + def import_pin_mnemonicmphrase(self, pin): + loop.schedule(start_import_pin_mnemonicmphrase(self, pin)) diff --git a/core/src/trezor/lvglui/scrs/pinscreen.py b/core/src/trezor/lvglui/scrs/pinscreen.py index 363c77113..e5f15dcc5 100644 --- a/core/src/trezor/lvglui/scrs/pinscreen.py +++ b/core/src/trezor/lvglui/scrs/pinscreen.py @@ -184,6 +184,80 @@ def on_event(self, event_obj): self.destroy() +class InputLitePinConfirm(FullSizeWindow): + def __init__(self, title): + super().__init__( + title=title, + subtitle=None, + anim_dir=0, + ) + self.title.add_style( + StyleWrapper() + .text_font(font_GeistSemiBold48) + .text_align_center() + .text_letter_space(0), + 0, + ) + self.title.align(lv.ALIGN.TOP_MID, 0, 24) + self.clear_flag(lv.obj.FLAG.SCROLLABLE) + self.keyboard = NumberKeyboard(self, max_len=6, min_len=6) + self.keyboard.add_event_cb(self.on_event, lv.EVENT.READY, None) + self.keyboard.add_event_cb(self.on_event, lv.EVENT.CANCEL, None) + self.keyboard.add_event_cb(self.on_event, lv.EVENT.VALUE_CHANGED, None) + self.input_result = None + + def on_event(self, event_obj): + code = event_obj.code + if code == lv.EVENT.VALUE_CHANGED: + utils.lcd_resume() + return + elif code == lv.EVENT.READY: + input = self.keyboard.ta.get_text() + if len(input) < 6: + return + self.input_result = input + self.channel.publish(self.input_result) + elif code == lv.EVENT.CANCEL: + self.channel.publish(0) + + self.clean() + self.destroy() + + +async def pin_mismatch(ctx) -> None: + from trezor.ui.layouts import show_warning + + await show_warning( + ctx=ctx, + br_type="pin_not_match", + header=_(i18n_keys.TITLE__NOT_MATCH), + content=_( + i18n_keys.CONTENT__THE_TWO_ONEKEY_LITE_USED_FOR_CONNECTION_ARE_NOT_THE_SAME + ), + icon="A:/res/danger.png", + btn_yes_bg_color=lv_colors.ONEKEY_BLACK, + ) + + +async def request_lite_pin(ctx, prompt: str) -> str: + pin_screen = InputLitePinConfirm(prompt) + pin = await ctx.wait(pin_screen.request()) + return pin + + +async def request_lite_pin_confirm(ctx) -> str: + while True: + pin1 = await request_lite_pin(ctx, _(i18n_keys.TITLE__ENTER_ONEKEY_LITE_PIN)) + if pin1 == 0: + return pin1 + pin2 = await request_lite_pin(ctx, _(i18n_keys.TITLE__CONFIRM_ONEKEY_LITE_PIN)) + if pin2 == 0: + return pin2 + if pin1 == pin2: + return pin1 + await pin_mismatch(ctx) + + class SetupComplete(FullSizeWindow): def __init__(self, subtitle=""): super().__init__( diff --git a/core/src/trezor/lvglui/scrs/template.py b/core/src/trezor/lvglui/scrs/template.py index 26382f029..59e0fbb8f 100644 --- a/core/src/trezor/lvglui/scrs/template.py +++ b/core/src/trezor/lvglui/scrs/template.py @@ -3174,6 +3174,7 @@ def __init__( title, subtitle, qr_code, + primary_color, encoder=None, ): super().__init__( diff --git a/core/src/trezor/lvglui/scrs/wipe_device.py b/core/src/trezor/lvglui/scrs/wipe_device.py index 1b8bc1356..b4df99659 100644 --- a/core/src/trezor/lvglui/scrs/wipe_device.py +++ b/core/src/trezor/lvglui/scrs/wipe_device.py @@ -121,3 +121,60 @@ def eventhandler(self, event_obj): from apps.base import set_homescreen set_homescreen() + + +class WipeLiteCardTips(FullSizeWindow): + def __init__(self): + title = _(i18n_keys.TITLE__CARD_CONTAINS_BACKUP) + subtitle = _(i18n_keys.TITLE__CARD_CONTAINS_BACKUP_DESC) + icon_path = "A:/res/warning.png" + super().__init__( + title, + subtitle, + _(i18n_keys.BUTTON__OVERWRITE), + _(i18n_keys.BUTTON__CANCEL), + icon_path=icon_path, + ) + self.container = ContainerFlexCol( + self.content_area, + self.subtitle, + padding_row=8, + clip_corner=False, + ) + self.item1 = ListItemWithLeadingCheckbox( + self.container, + _(i18n_keys.FORM__I_UNDERSTAND_THAT_THE_BACKUP_WILL_BE_OVERWRITTEN), + radius=40, + ) + self.slider_enable(False) + self.container.add_event_cb(self.on_value_changed, lv.EVENT.VALUE_CHANGED, None) + self.cb_cnt = 0 + + def slider_enable(self, enable: bool = True): + if enable: + self.btn_yes.add_flag(lv.obj.FLAG.CLICKABLE) + self.btn_yes.enable( + bg_color=lv_colors.ONEKEY_GREEN, text_color=lv_colors.BLACK + ) + + else: + self.btn_yes.clear_flag(lv.obj.FLAG.CLICKABLE) + self.btn_yes.disable( + bg_color=lv_colors.ONEKEY_BLACK_1, text_color=lv_colors.ONEKEY_GRAY + ) + + def on_value_changed(self, event_obj): + code = event_obj.code + target = event_obj.get_target() + if code == lv.EVENT.VALUE_CHANGED: + if target == self.item1.checkbox: + if target.get_state() & lv.STATE.CHECKED: + self.item1.enable_bg_color() + self.cb_cnt += 1 + else: + self.item1.enable_bg_color(False) + self.cb_cnt -= 1 + if self.cb_cnt == 1: + self.slider_enable() + elif self.cb_cnt < 1: + self.slider_enable(False) diff --git a/core/src/trezor/qr.py b/core/src/trezor/qr.py index db01d94d2..70578e53b 100644 --- a/core/src/trezor/qr.py +++ b/core/src/trezor/qr.py @@ -142,8 +142,8 @@ async def gen_request(self, ur: UR) -> bool: if registry_type not in [ "eth-sign-request", - # "onekey-app-call-device", - # "crypto-psbt", + "onekey-app-call-device", + "crypto-psbt", ]: if __debug__: print(f"unsupported type {registry_type}") diff --git a/core/src/trezor/uart.py b/core/src/trezor/uart.py index 68fd63124..f452acd7e 100644 --- a/core/src/trezor/uart.py +++ b/core/src/trezor/uart.py @@ -1,6 +1,5 @@ import ustruct from micropython import const -from typing import TYPE_CHECKING from storage import device from trezor import config, io, log, loop, motor, utils, workflow @@ -8,8 +7,8 @@ # from trezor.lvglui.scrs.charging import ChargingPromptScr from trezor.ui import display +from typing import TYPE_CHECKING -import usb from apps import base if TYPE_CHECKING: @@ -32,6 +31,7 @@ _CMD_BATTERY_STATUS = const(9) _CMD_SIDE_BUTTON_PRESS = const(10) _CMD_LED_BRIGHTNESS = const(12) +_CMD_BATTERY_INFO = const(13) _CMD_BLE_BUILD_ID = const(16) _CMD_BLE_HASH = const(17) CHARING_TYPE = 0 # 1 VIA USB / 2 VIA WIRELESS @@ -41,10 +41,12 @@ BLE_CTRL = io.BLE() FLASH_LED_BRIGHTNESS: int | None = None BUTTON_PRESSING = False +wireless_charge_screen_off_task = None async def handle_fingerprint(): from trezorio import fingerprint + from trezor.lvglui.scrs import fingerprints global BUTTON_PRESSING @@ -142,7 +144,13 @@ async def handle_usb_state(): try: utils.AIRGAP_MODE_CHANGED = False usb_state = loop.wait(io.USB_STATE) - state = await usb_state + state, enable = await usb_state + if enable is not None and not device.is_airgap_mode(): + import usb + + usb.bus.connect_ctrl(enable) + continue + utils.turn_on_lcd_if_possible() if state: # if display.backlight() == 0: @@ -248,7 +256,6 @@ async def process_push() -> None: res = ustruct.unpack(">B", value)[0] utils.BATTERY_CAP = res StatusBar.get_instance().set_battery_img(res, utils.CHARGING) - elif cmd == _CMD_SIDE_BUTTON_PRESS: # 1 short press 2 long press await _deal_button_press(value) @@ -261,6 +268,8 @@ async def process_push() -> None: elif cmd == _CMD_LED_BRIGHTNESS: # retrieve led brightness _retrieve_flashled_brightness(value) + elif cmd == _CMD_BATTERY_INFO: + _deal_battery_info(value) elif cmd == _CMD_BLE_BUILD_ID: _retrieve_ble_build_id(value) elif cmd == _CMD_BLE_HASH: @@ -307,10 +316,6 @@ async def _deal_button_press(value: bytes) -> None: await loop.race(safe_reloop(), loop.sleep(200)) workflow.spawn(utils.internal_reloop()) return - else: - if utils.CHARGE_WIRELESS_STATUS != utils.CHARGE_WIRELESS_STOP: - ctrl_charge_switch(True) - return else: utils.turn_on_lcd_if_possible() @@ -330,9 +335,7 @@ async def _deal_button_press(value: bytes) -> None: global BUTTON_PRESSING BUTTON_PRESSING = True if utils.is_collecting_fingerprint(): - from trezor.lvglui.scrs.fingerprints import ( - CollectFingerprintProgress, - ) + from trezor.lvglui.scrs.fingerprints import CollectFingerprintProgress if CollectFingerprintProgress.has_instance(): CollectFingerprintProgress.get_instance().prompt_tips() @@ -354,55 +357,38 @@ async def _deal_charging_state(value: bytes) -> None: CHARGE_START, _POWER_STATUS_CHARGING, ): - if utils.CHARGING: - return - utils.CHARGING = True StatusBar.get_instance().show_charging(True) if utils.BATTERY_CAP: - StatusBar.get_instance().set_battery_img(utils.BATTERY_CAP, utils.CHARGING) + StatusBar.get_instance().set_battery_img(utils.BATTERY_CAP, True) if CHARING_TYPE == CHARGE_BY_WIRELESS: - if utils.CHARGE_WIRELESS_STATUS != utils.CHARGE_WIRELESS_STOP: - return - utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_START + if utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_STOP: + utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_CHARGING + global wireless_charge_screen_off_task - if device.is_initialized(): - if utils.is_initialization_processing(): - return - utils.AUTO_POWER_OFF = True - from trezor.lvglui.scrs import fingerprints + wireless_charge_screen_off_task = utils.turn_off_lcd_delay() - if config.has_pin() and config.is_unlocked(): - if fingerprints.is_available(): - if fingerprints.is_unlocked(): - fingerprints.lock() - else: - config.lock() - await loop.race(safe_reloop(), loop.sleep(200)) - workflow.spawn(utils.internal_reloop()) + loop.schedule(wireless_charge_screen_off_task) + elif utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_CHARGING: + return else: - if utils.CHARGE_WIRELESS_STATUS != utils.CHARGE_WIRELESS_STOP: - utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_STOP + if utils.CHARGING: + return + utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_STOP ctrl_charge_switch(True) - if not device.is_airgap_mode() and usb.bus.state() == 0: - usb.bus = usb.init() - for iface in usb.active_iface: - usb.bus.add(iface) - usb.bus.open(device.get_device_id()) - + utils.CHARGING = True elif res in (_USB_STATUS_PLUG_OUT, _POWER_STATUS_CHARGING_FINISHED): - if utils.CHARGE_WIRELESS_STATUS != utils.CHARGE_WIRELESS_STOP: - utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_STOP - # if not utils.CHARGING: - # return utils.CHARGING = False - ctrl_charge_switch(True) + ctrl_charge_switch(False) StatusBar.get_instance().show_charging(False) StatusBar.get_instance().show_usb(False) if utils.BATTERY_CAP: - StatusBar.get_instance().set_battery_img(utils.BATTERY_CAP, utils.CHARGING) - if res == _USB_STATUS_PLUG_OUT: - if usb.bus.state() == 1: - usb.bus.close() + StatusBar.get_instance().set_battery_img(utils.BATTERY_CAP, False) + + if utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_CHARGING: + utils.CHARGE_WIRELESS_STATUS = utils.CHARGE_WIRELESS_STOP + global wireless_charge_screen_off_task + if wireless_charge_screen_off_task: + loop.close(wireless_charge_screen_off_task) utils.turn_on_lcd_if_possible() @@ -462,6 +448,14 @@ def _retrieve_flashled_brightness(value: bytes) -> None: utils.FLASH_LED_BRIGHTNESS = FLASH_LED_BRIGHTNESS +def _deal_battery_info(value: bytes) -> None: + res, val = ustruct.unpack(">BH", value) + if res == 4: + utils.BATTERY_TEMP = val + if val <= 38 and display.backlight() == 0: + ctrl_wireless_charge(True) + + def _retrieve_ble_name(value: bytes) -> None: if value != b"": utils.BLE_NAME = value.decode("utf-8") @@ -551,6 +545,11 @@ def fetch_ble_info(): BLE_CTRL.ctrl(0x83, b"\x06") +def fetch_battery_temperature(): + BLE_CTRL.ctrl(0x86, b"\x04") + # BLE_CTRL.ctrl(0x86, b"\x05") + + def ctrl_ble(enable: bool) -> None: """Request to open or close ble. @param enable: True to open, False to close @@ -649,3 +648,27 @@ def ctrl_wireless_charge(enable: bool) -> None: if utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_STOP: return ctrl_charge_switch(enable) + + +def get_wireless_charge_status() -> bool: + if utils.CHARGE_WIRELESS_STATUS == utils.CHARGE_WIRELESS_STOP: + return True + if utils.CHARGE_ENABLE: + return True + return False + + +def stop_mode(reset_timer: bool = False): + disconnect_ble() + + lp_timer_enable = False + wireless_charge = get_wireless_charge_status() + + if not wireless_charge: + lp_timer_enable = True + + utils.enter_lowpower( + reset_timer, device.get_autoshutdown_delay_ms(), lp_timer_enable + ) + if not wireless_charge: + fetch_battery_temperature() diff --git a/core/src/trezor/ui/layouts/lvgl/__init__.py b/core/src/trezor/ui/layouts/lvgl/__init__.py index d53deb590..3cbf9659e 100644 --- a/core/src/trezor/ui/layouts/lvgl/__init__.py +++ b/core/src/trezor/ui/layouts/lvgl/__init__.py @@ -28,6 +28,7 @@ "show_error_and_raise", "show_pubkey", "show_success", + "show_lite_card_exit", "show_xpub", "show_warning", "confirm_output", @@ -477,6 +478,30 @@ def show_success( ) +def show_lite_card_exit( + ctx: wire.GenericContext, + br_type: str, + content: str, + header: str = "Success", + subheader: str | None = None, + button: str = "Exit", + cacelbutton: str = "Cancel", +) -> Awaitable[None]: + + return _show_modal( + ctx, + br_type=br_type, + br_code=ButtonRequestType.Success, + header=header, + subheader=subheader, + content=content, + button_confirm=button, + button_cancel=cacelbutton, + icon=None, + icon_color=ui.GREEN, + ) + + async def show_error_no_interact(title: str, subtitle: str, cancel_text: str = ""): from trezor.lvglui.scrs.template import ErrorFeedback @@ -1885,96 +1910,6 @@ async def backup_with_keytag( break -async def backup_with_lite( - ctx: wire.GenericContext, mnemonics: bytes, recovery_check: bool = False -): - # TODO: Not support now - if not __debug__: - return - from trezor.lvglui.scrs.common import FullSizeWindow, lv - from trezor.lvglui.scrs.pinscreen import InputLitePin - - while True: - ask_screen = FullSizeWindow( - _(i18n_keys.TITLE__BACK_UP_WITH_LITE), - _(i18n_keys.TITLE__BACK_UP_WITH_LITE_DESC), - confirm_text=_(i18n_keys.BUTTON__BACKUP), - cancel_text=_(i18n_keys.BUTTON__NOT_NOW), - icon_path="A:/res/icon-lite.png", - anim_dir=0, - ) - ask_screen.btn_layout_ver() - if await ctx.wait(ask_screen.request()): - while True: - start_scr = FullSizeWindow( - _(i18n_keys.TITLE__GET_STARTED), - _(i18n_keys.CONTENT__PLACE_LITE_DEVICE_FIGURE_CLICK_CONTINUE), - confirm_text=_(i18n_keys.BUTTON__CONTINUE), - cancel_text=_(i18n_keys.BUTTON__BACK), - anim_dir=0, - ) - start_scr.img = lv.img(start_scr.content_area) - start_scr.img.set_src("A:/res/nfc-start.png") - start_scr.img.align_to( - start_scr.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 52 - ) - if await ctx.wait(start_scr.request()): - if __debug__: - print("Lite backup start") - pin = await ctx.wait(InputLitePin().request()) - if __debug__: - print(f"Got lite pin {pin}") - if not pin: - if __debug__: - print("Lite pin cancelled") - continue - - from trezor.lvglui.scrs.nfc import SearchDeviceScreen - - search_scr = SearchDeviceScreen() - # from trezorio import NFC - - # nfc_controler = NFC() - # res = nfc_controler.pwr_ctrl(True) - # print(f"nfc power on {res}") - # # res = nfc_controler.wait_card(1000) - # status, res = nfc_controler.send_recv_single_shot( - # b"\x80\xcb\x80\x00\x05\xdf\xff\x02\x81\x01", 1000 - # ) - # print(f"nfc wait card status {status} and result {res}") - # nfc_controler.send_recv(b'\x00\x00\x00\x00') - # nfc_controler.send_recv_single_shot(b'\x00\x00\x00\x00', 100) - # get serial number - # status, res = nfc_controler.send_recv_single_shot( - # b"\x80\xcb\x80\x00\x05\xdf\xff\x02\x81\x01", 100 - # ) - - # TODO: 1. start nfc - # TODO: 2. searching device - # TODO: 3. select primary security domain - # TODO: 4. select backup APP ...... - - if await ctx.wait(search_scr.request()): - break - - # finally show backup success - await show_success( - ctx, - "Lite backup success", - _(i18n_keys.TITLE__BACKUP_COMPLETED_DESC), - _(i18n_keys.TITLE__BACKUP_COMPLETED), - button=_(i18n_keys.BUTTON__CONTINUE), - ) - else: - if __debug__: - print("Lite backup cancelled") - break - else: - if __debug__: - print("Lite backup skipped") - break - - async def confirm_polkadot_balances( ctx: wire.GenericContext, chain_name: str, @@ -2123,9 +2058,10 @@ async def show_ur_response( screen = UrResponse( title, - _(i18n_keys.TITLE_CONFIRM_ADDRESS_DESC), + _(i18n_keys.CONTENT__RETUNRN_TO_THE_APP_AND_SCAN_THE_SIGNED_TX_QR_CODE_BELOW), qr_code=qr_code, encoder=encoder, + primary_color=ctx.primary_color, ) await ctx.wait(screen.request()) diff --git a/core/src/trezor/ui/layouts/lvgl/lite.py b/core/src/trezor/ui/layouts/lvgl/lite.py new file mode 100644 index 000000000..cc05da107 --- /dev/null +++ b/core/src/trezor/ui/layouts/lvgl/lite.py @@ -0,0 +1,404 @@ +from trezorio import nfc + +from trezor import wire +from trezor.lvglui.i18n import gettext as _, keys as i18n_keys +from trezor.lvglui.scrs.common import FullSizeWindow, lv +from trezor.lvglui.scrs.nfc import ( + LITE_CARD_CONNECT_FAILURE, + LITE_CARD_FIND, + LITE_CARD_HAS_BEEN_RESET, + LITE_CARD_NO_BACKUP, + LITE_CARD_NOT_SAME, + LITE_CARD_OPERATE_SUCCESS, + LITE_CARD_PIN_ERROR, + LITE_CARD_UNSUPPORTED_WORD_COUNT, + SearchDeviceScreen, + TransferDataScreen, +) +from trezor.lvglui.scrs.pinscreen import InputLitePin, request_lite_pin_confirm + +LITE_CARD_BUTTON_CONFIRM = 1 +LITE_CARD_BUTTON_CANCLE = 0 +LITE_CARD_BLANK = 2 +LITE_CARD_WITH_DATA = 3 + + +async def show_fullsize_window( + ctx: wire.GenericContext, + title, + content, + confirm_text, + cancel_text=None, + icon_path=None, +): + screen = FullSizeWindow( + title, + content, + confirm_text=confirm_text, + cancel_text=cancel_text, + icon_path=icon_path, + anim_dir=0, + ) + screen.btn_layout_ver() + if hasattr(screen, "subtitle"): + screen.subtitle.set_recolor(True) + result = await ctx.wait(screen.request()) + return result + + +async def show_start_screen(ctx: wire.GenericContext): + screen = FullSizeWindow( + _(i18n_keys.TITLE__GET_STARTED), + _(i18n_keys.CONTENT__PLACE_LITE_DEVICE_FIGURE_CLICK_CONTINUE), + confirm_text=_(i18n_keys.BUTTON__CONTINUE), + cancel_text=_(i18n_keys.BUTTON__BACK), + anim_dir=0, + ) + screen.img = lv.img(screen.content_area) + screen.img.set_src("A:/res/nfc-start.png") + screen.img.align_to(screen.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 52) + return await ctx.wait(screen.request()) + + +async def search_device(ctx: wire.GenericContext): + search_scr = SearchDeviceScreen() + return await ctx.wait(search_scr.request()) + + +async def input_pin(ctx: wire.GenericContext): + return await ctx.wait(InputLitePin().request()) + + +async def backup_with_lite( + ctx: wire.GenericContext, mnemonics: bytes, recovery_check: bool = False +): + async def handle_pin_setup(card_num, mnemonics): + pin = await request_lite_pin_confirm(ctx) + if pin and pin != LITE_CARD_BUTTON_CANCLE: + flag = await handle_second_placement(card_num, pin, mnemonics) + return flag + else: + pass + + async def handle_second_placement(card_num, pin, mnemonics): + while True: + start_scr_againc = FullSizeWindow( + _(i18n_keys.TITLE__CONNECT_AGAIN), + _(i18n_keys.CONTENT__KEEP_LITE_DEVICE_TOGETHER_BACKUP_COMPLETE), + confirm_text=_(i18n_keys.BUTTON__CONTINUE), + cancel_text=_(i18n_keys.BUTTON__BACK), + anim_dir=0, + ) + start_scr_againc.img = lv.img(start_scr_againc.content_area) + start_scr_againc.img.set_src("A:/res/nfc-start.png") + start_scr_againc.img.align_to( + start_scr_againc.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 52 + ) + again_flag = await ctx.wait(start_scr_againc.request()) + if again_flag == LITE_CARD_BUTTON_CONFIRM: + while True: + status_code = await search_device(ctx) + if status_code == LITE_CARD_FIND: + trash_scr = TransferDataScreen() + trash_scr.set_pin_mnemonicmphrase(pin, card_num, mnemonics) + final_status_code = await ctx.wait(trash_scr.request()) + if final_status_code == LITE_CARD_OPERATE_SUCCESS: + + from trezor.ui.layouts import show_success + + await show_success( + ctx, + _(i18n_keys.TITLE__BACK_UP_COMPLETE), + _(i18n_keys.TITLE__BACKUP_COMPLETED_DESC), + header=_(i18n_keys.TITLE__BACK_UP_COMPLETE), + button=_(i18n_keys.BUTTON__I_GOT_IT), + ) + return LITE_CARD_OPERATE_SUCCESS + elif final_status_code == LITE_CARD_NOT_SAME: + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__THE_TWO_ONEKEY_LITE_USED_FOR_CONNECTION_ARE_NOT_THE_SAME + ), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + return LITE_CARD_NOT_SAME + + elif final_status_code == LITE_CARD_CONNECT_FAILURE: + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__MAKE_SURE_THE_CARD_IS_CLOSE_TO_THE_UPPER_LEFT + ), + _(i18n_keys.BUTTON__TRY_AGAIN), + _(i18n_keys.BUTTON__BACK), + icon_path="A:/res/danger.png", + ) + if flag == LITE_CARD_BUTTON_CANCLE: + return LITE_CARD_CONNECT_FAILURE + elif flag == LITE_CARD_BUTTON_CONFIRM: + continue + + else: + break + else: + break + + async def handle_existing_data(card_num, mnemonics): + from trezor.lvglui.scrs.wipe_device import WipeLiteCardTips + + confirm_screen = WipeLiteCardTips() + confirm_screen_flag = await ctx.wait(confirm_screen.request()) + if confirm_screen_flag: + pin = await input_pin(ctx) + place_again_data_card = True + if pin: + while place_again_data_card: + start_scr_againc = FullSizeWindow( + _(i18n_keys.TITLE__CONNECT_AGAIN), + _(i18n_keys.CONTENT__KEEP_LITE_DEVICE_TOGETHER_BACKUP_COMPLETE), + confirm_text=_(i18n_keys.BUTTON__CONTINUE), + cancel_text=_(i18n_keys.BUTTON__BACK), + anim_dir=0, + ) + start_scr_againc.img = lv.img(start_scr_againc.content_area) + start_scr_againc.img.set_src("A:/res/nfc-start.png") + start_scr_againc.img.align_to( + start_scr_againc.subtitle, lv.ALIGN.OUT_BOTTOM_MID, 0, 52 + ) + again_flag = await ctx.wait(start_scr_againc.request()) + if again_flag == LITE_CARD_BUTTON_CONFIRM: + place_again_data_card_next = True + while place_again_data_card_next: + status_code = await search_device(ctx) + if status_code == LITE_CARD_FIND: + trash_scr = TransferDataScreen() + trash_scr.check_pin_mnemonicmphrase( + pin, card_num, mnemonics + ) + final_status_code = await ctx.wait(trash_scr.request()) + if final_status_code == LITE_CARD_OPERATE_SUCCESS: + from trezor.ui.layouts import show_success + + await show_success( + ctx, + _(i18n_keys.TITLE__BACK_UP_COMPLETE), + _(i18n_keys.TITLE__BACKUP_COMPLETED_DESC), + header=_(i18n_keys.TITLE__BACK_UP_COMPLETE), + button=_(i18n_keys.BUTTON__I_GOT_IT), + ) + return LITE_CARD_OPERATE_SUCCESS + # lite card disconnect + elif final_status_code == LITE_CARD_CONNECT_FAILURE: + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__MAKE_SURE_THE_CARD_IS_CLOSE_TO_THE_UPPER_LEFT + ), + _(i18n_keys.BUTTON__TRY_AGAIN), + _(i18n_keys.BUTTON__BACK), + icon_path="A:/res/danger.png", + ) + if flag == LITE_CARD_BUTTON_CANCLE: + return LITE_CARD_CONNECT_FAILURE + elif flag == LITE_CARD_BUTTON_CONFIRM: + continue + + elif final_status_code == LITE_CARD_NOT_SAME: + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__THE_TWO_ONEKEY_LITE_USED_FOR_CONNECTION_ARE_NOT_THE_SAME + ), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + return LITE_CARD_NOT_SAME + + elif final_status_code == LITE_CARD_HAS_BEEN_RESET: + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__LITE_HAS_BEEN_RESET), + _(i18n_keys.TITLE__LITE_HAS_BEEN_RESET_DESC), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + return LITE_CARD_HAS_BEEN_RESET + + elif str(final_status_code).startswith("63C"): + retry_count = int(str(final_status_code)[-1], 16) + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__LITE_PIN_ERROR), + _(i18n_keys.TITLE__LITE_PIN_ERROR_DESC).format( + f"#FF0000 {retry_count}#" + ), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + return LITE_CARD_PIN_ERROR + + else: + break + elif status_code == LITE_CARD_BUTTON_CANCLE: + place_again_data_card_next = False + else: + break + + nfc.pwr_ctrl(True) + first_placement = True + while first_placement: + start_flag = await show_start_screen(ctx) + if start_flag == LITE_CARD_BUTTON_CONFIRM: + status_code = await search_device(ctx) + if status_code == LITE_CARD_FIND: + trash_scr = TransferDataScreen() + trash_scr.check_card_data() + carddata = await ctx.wait(trash_scr.request()) + if carddata == LITE_CARD_CONNECT_FAILURE: + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__MAKE_SURE_THE_CARD_IS_CLOSE_TO_THE_UPPER_LEFT + ), + _(i18n_keys.BUTTON__TRY_AGAIN), + _(i18n_keys.BUTTON__BACK), + icon_path="A:/res/danger.png", + ) + if flag == LITE_CARD_BUTTON_CANCLE: + first_placement = False + elif flag == LITE_CARD_BUTTON_CONFIRM: + continue + + first_char = carddata[0] + first_char_num = int(first_char) + card_num = carddata[1:] + if first_char_num == LITE_CARD_BLANK: + flag = await handle_pin_setup(card_num, mnemonics) + elif first_char_num == LITE_CARD_WITH_DATA: + flag = await handle_existing_data(card_num, mnemonics) + if flag in [ + LITE_CARD_CONNECT_FAILURE, + LITE_CARD_NOT_SAME, + LITE_CARD_HAS_BEEN_RESET, + LITE_CARD_PIN_ERROR, + ]: + continue + elif flag == LITE_CARD_OPERATE_SUCCESS: + return LITE_CARD_OPERATE_SUCCESS + elif status_code == LITE_CARD_BUTTON_CANCLE: + continue + elif start_flag == LITE_CARD_BUTTON_CANCLE: + from trezor.ui.layouts import show_lite_card_exit + + try: + await ctx.wait( + show_lite_card_exit( + ctx, + _(i18n_keys.TITLE__EXIT_BACKUP_PROCESS), + _(i18n_keys.TITLE__EXIT_BACKUP_PROCESS_DESC), + _(i18n_keys.BUTTON__EXIT), + _(i18n_keys.BUTTON__CANCEL), + ) + ) + return + except wire.ActionCancelled: + continue + + +async def backup_with_lite_import(ctx: wire.GenericContext): + async def handle_wallet_recovery(pin): + trash_scr = TransferDataScreen() + trash_scr.import_pin_mnemonicmphrase(pin) + mnemonic_phrase = await ctx.wait(trash_scr.request()) + if mnemonic_phrase: + return mnemonic_phrase + + nfc.pwr_ctrl(True) + back_up_page = True + + while back_up_page: + start_flag = await show_start_screen(ctx) + if start_flag == LITE_CARD_BUTTON_CONFIRM: + pin = await input_pin(ctx) + if pin: + first_placement = True + while first_placement: + status_code = await search_device(ctx) + if status_code == LITE_CARD_FIND: + mnemonic_phrase = await handle_wallet_recovery(pin) + if mnemonic_phrase == LITE_CARD_CONNECT_FAILURE: + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__CONNECT_FAILED), + _( + i18n_keys.CONTENT__MAKE_SURE_THE_CARD_IS_CLOSE_TO_THE_UPPER_LEFT + ), + _(i18n_keys.BUTTON__TRY_AGAIN), + _(i18n_keys.BUTTON__BACK), + icon_path="A:/res/danger.png", + ) + if flag == LITE_CARD_BUTTON_CANCLE: + first_placement = False + elif flag == LITE_CARD_BUTTON_CONFIRM: + continue + elif mnemonic_phrase == LITE_CARD_NO_BACKUP: + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__NO_BACKUP_ON_THIS_CARD), + _(i18n_keys.TITLE__NO_BACKUP_ON_THIS_CARD_DESC), + _(i18n_keys.BUTTON__TRY_AGAIN), + _(i18n_keys.BUTTON__BACK), + icon_path="A:/res/danger.png", + ) + if flag == LITE_CARD_BUTTON_CANCLE: + first_placement = False + elif flag == LITE_CARD_BUTTON_CONFIRM: + continue + + elif mnemonic_phrase == LITE_CARD_HAS_BEEN_RESET: + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__LITE_HAS_BEEN_RESET), + _(i18n_keys.TITLE__LITE_HAS_BEEN_RESET_DESC), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + first_placement = False + elif mnemonic_phrase == LITE_CARD_UNSUPPORTED_WORD_COUNT: + await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__UNSUPPORTED_RECOVERY_PHRASE), + _(i18n_keys.TITLE__UNSUPPORTED_RECOVERY_PHRASE_DESC), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + first_placement = False + elif str(mnemonic_phrase).startswith("63C"): + retry_count = int(str(mnemonic_phrase)[-1], 16) + flag = await show_fullsize_window( + ctx, + _(i18n_keys.TITLE__LITE_PIN_ERROR), + _(i18n_keys.TITLE__LITE_PIN_ERROR_DESC).format( + f"#FF0000 {retry_count}#" + ), + _(i18n_keys.BUTTON__I_GOT_IT), + icon_path="A:/res/danger.png", + ) + first_placement = False + + elif mnemonic_phrase: + return mnemonic_phrase + elif status_code == LITE_CARD_BUTTON_CANCLE: + break + + if start_flag == LITE_CARD_BUTTON_CANCLE: + return LITE_CARD_BUTTON_CANCLE + + return LITE_CARD_BUTTON_CANCLE diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index 769625a60..8da48ab8f 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -63,6 +63,7 @@ def board_version() -> str: DISABLE_ANIMATION = 0 BLE_CONNECTED: bool | None = None BATTERY_CAP: int | None = None +BATTERY_TEMP = 0 SHORT_AUTO_LOCK_TIME_MS = 20 * 1000 DEFAULT_LABEL = "OneKey Pro" AUTO_POWER_OFF = False @@ -74,8 +75,7 @@ def board_version() -> str: _BACKUP_WITH_LITE_FIRST = False _COLOR_FLAG: str | None = None CHARGE_WIRELESS_STOP = const(0) -CHARGE_WIRELESS_START = const(1) -CHARGE_WIRELESS_CHARGING = const(2) +CHARGE_WIRELESS_CHARGING = const(1) CHARGE_WIRELESS_STATUS = CHARGE_WIRELESS_STOP CHARGE_ENABLE: bool | None = None CHARGING = False @@ -196,13 +196,22 @@ async def internal_reloop(): async def turn_off_lcd(): from trezor.ui import display from trezor import loop, wire + from storage import device if display.backlight(): global AUTO_POWER_OFF display.backlight(0) AUTO_POWER_OFF = True await wire.signal_ack() - loop.clear() + if device.is_initialized(): + loop.clear() + + +async def turn_off_lcd_delay(): + from trezor import loop + + await loop.sleep(3000) + await turn_off_lcd() def play_dead(): @@ -232,11 +241,7 @@ def disable_airgap_mode(): AIRGAP_MODE_CHANGED = True import usb - if usb.bus.state() == 0: - usb.bus = usb.init() - for iface in usb.active_iface: - usb.bus.add(iface) - usb.bus.open(device.get_device_id()) + usb.bus.connect_ctrl(True) def enable_airgap_mode(): @@ -252,8 +257,7 @@ def enable_airgap_mode(): AIRGAP_MODE_CHANGED = True import usb - if usb.bus.state() == 1: - usb.bus.close() + usb.bus.connect_ctrl(False) def show_app_guide(): diff --git a/core/src/trezor/wire/__init__.py b/core/src/trezor/wire/__init__.py index 86227fced..678114e74 100644 --- a/core/src/trezor/wire/__init__.py +++ b/core/src/trezor/wire/__init__.py @@ -428,15 +428,6 @@ async def _handle_single_message( from trezor.enums import MessageType if msg.type in [ - MessageType.MoneroGetAddress, - MessageType.MoneroGetWatchKey, - MessageType.MoneroTransactionInitRequest, - MessageType.MoneroKeyImageExportInitRequest, - MessageType.MoneroGetTxKeyRequest, - MessageType.MoneroLiveRefreshStartRequest, - ]: - await ctx.write(unsupported_yet("Monero")) - elif msg.type in [ MessageType.WebAuthnListResidentCredentials, MessageType.WebAuthnAddResidentCredential, MessageType.WebAuthnRemoveResidentCredential, diff --git a/core/src/usb.py b/core/src/usb.py index 15a97d10d..baf008d3c 100644 --- a/core/src/usb.py +++ b/core/src/usb.py @@ -40,8 +40,7 @@ def init() -> io.USB: _iface_iter = iter(range(5)) ENABLE_IFACE_DEBUG = __debug__ -# ENABLE_IFACE_WEBAUTHN = not utils.BITCOIN_ONLY -ENABLE_IFACE_WEBAUTHN = False # not yet implemented +ENABLE_IFACE_WEBAUTHN = not utils.BITCOIN_ONLY # ENABLE_IFACE_VCP = __debug__ ENABLE_IFACE_VCP = False diff --git a/tools/style.c.exclude b/tools/style.c.exclude index 3f28c2f5f..f9f2fe707 100644 --- a/tools/style.c.exclude +++ b/tools/style.c.exclude @@ -1 +1,2 @@ -^\./core/embed/quirc \ No newline at end of file +^\./core/embed/quirc +^\./core/embed/lite_card \ No newline at end of file