diff --git a/core/.changelog.d/3296.added b/core/.changelog.d/3296.added new file mode 100644 index 0000000000..b67fca3cb5 --- /dev/null +++ b/core/.changelog.d/3296.added @@ -0,0 +1 @@ +Integrate Optiga into PIN verification for Model R. diff --git a/core/embed/bootloader/.changelog.d/3122.changed b/core/embed/bootloader/.changelog.d/3122.changed deleted file mode 100644 index ba6f3d0e89..0000000000 --- a/core/embed/bootloader/.changelog.d/3122.changed +++ /dev/null @@ -1 +0,0 @@ -No longer erases seed when firmware is corrupted but firmware header is correct and signed. Added firmware corrupted info to bootloader screen. diff --git a/core/embed/bootloader/.changelog.d/3205.added b/core/embed/bootloader/.changelog.d/3205.added index fe4468f37e..7d7e7f98e5 100644 --- a/core/embed/bootloader/.changelog.d/3205.added +++ b/core/embed/bootloader/.changelog.d/3205.added @@ -1,2 +1 @@ Added firmware update without interaction. -Split builds of different parts to use simple util.s assembler, while FW+bootloader use interconnected ones. diff --git a/core/embed/bootloader/CHANGELOG.md b/core/embed/bootloader/CHANGELOG.md index fe576222b0..98da1f8020 100644 --- a/core/embed/bootloader/CHANGELOG.md +++ b/core/embed/bootloader/CHANGELOG.md @@ -4,8 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.1.3 [September 2023] + +### Changed +- Split builds of different parts to use simple util.s assembler, while FW+bootloader use interconnected ones. [#3205] +- No longer erases seed when firmware is corrupted but firmware header is correct and signed. Added firmware corrupted info to bootloader screen. [#3122] +- Correctly reinitialize Optiga SE when rebooting. [#3303] + + ## 2.1.2 [August 2023] +Internal only release for Model R prototypes. + ### Added - Added support for STM32F429I-DISC1 board [#2989] - Locked bootloader support: bootloader will disallow installation of unofficial firmware unless the Optiga pairing secret is erased. @@ -94,4 +104,7 @@ Internal only release for Model R prototypes. [#2955]: https://github.com/trezor/trezor-firmware/pull/2955 [#2989]: https://github.com/trezor/trezor-firmware/pull/2989 [#3048]: https://github.com/trezor/trezor-firmware/pull/3048 +[#3122]: https://github.com/trezor/trezor-firmware/pull/3122 +[#3205]: https://github.com/trezor/trezor-firmware/pull/3205 [#3222]: https://github.com/trezor/trezor-firmware/pull/3222 +[#3303]: https://github.com/trezor/trezor-firmware/pull/3303 diff --git a/core/embed/bootloader/version.h b/core/embed/bootloader/version.h index b3d996d9a0..541fd1bd20 100644 --- a/core/embed/bootloader/version.h +++ b/core/embed/bootloader/version.h @@ -1,6 +1,6 @@ #define VERSION_MAJOR 2 #define VERSION_MINOR 1 -#define VERSION_PATCH 3 +#define VERSION_PATCH 4 #define VERSION_BUILD 0 #define VERSION_UINT32 \ (VERSION_MAJOR | (VERSION_MINOR << 8) | (VERSION_PATCH << 16) | \ diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-random.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-random.h index ecfba7eb22..e85f05e6ae 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-random.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-random.h @@ -59,28 +59,13 @@ STATIC mp_obj_t mod_trezorcrypto_random_bytes(size_t n_args, vstr_init_len(&vstr, len); #if USE_OPTIGA if (n_args > 1 && mp_obj_is_true(args[1])) { - uint8_t *dest = (uint8_t *)vstr.buf; - if (!optiga_random_buffer(dest, len)) { + if (!optiga_random_buffer((uint8_t *)vstr.buf, len)) { vstr_clear(&vstr); mp_raise_msg(&mp_type_RuntimeError, "Failed to get randomness from Optiga."); } - uint8_t buffer[4] = {0}; - while (len > sizeof(buffer)) { - random_buffer(buffer, sizeof(buffer)); - for (int i = 0; i < sizeof(buffer); ++i) { - *dest ^= buffer[i]; - ++dest; - } - len -= sizeof(buffer); - } - - random_buffer(buffer, len); - for (int i = 0; i < len; ++i) { - *dest ^= buffer[i]; - ++dest; - } + random_xor((uint8_t *)vstr.buf, len); } else #endif { diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 83d24e021e..55281b5062 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -284,18 +284,38 @@ STATIC mp_obj_str_t mod_trezorutils_revision_obj = { STATIC mp_obj_str_t mod_trezorutils_model_name_obj = { {&mp_type_str}, 0, sizeof(MODEL_NAME) - 1, (const byte *)MODEL_NAME}; +STATIC mp_obj_str_t mod_trezorutils_full_name_obj = { + {&mp_type_str}, + 0, + sizeof(MODEL_FULL_NAME) - 1, + (const byte *)MODEL_FULL_NAME}; + /// SCM_REVISION: bytes +/// """Git commit hash of the firmware.""" /// VERSION_MAJOR: int +/// """Major version.""" /// VERSION_MINOR: int +/// """Minor version.""" /// VERSION_PATCH: int +/// """Patch version.""" /// USE_SD_CARD: bool +/// """Whether the hardware supports SD card.""" /// USE_BACKLIGHT: bool +/// """Whether the hardware supports backlight brightness control.""" /// USE_OPTIGA: bool +/// """Whether the hardware supports Optiga secure element.""" /// MODEL: str +/// """Model name.""" +/// MODEL_FULL_NAME: str +/// """Full name including Trezor prefix.""" /// INTERNAL_MODEL: str +/// """Internal model code.""" /// EMULATOR: bool +/// """Whether the firmware is running in the emulator.""" /// BITCOIN_ONLY: bool +/// """Whether the firmware is Bitcoin-only.""" /// UI_LAYOUT: str +/// """UI layout identifier ("tt" for model T, "tr" for models One and R).""" STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorutils)}, @@ -336,6 +356,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_USE_OPTIGA), mp_const_false}, #endif {MP_ROM_QSTR(MP_QSTR_MODEL), MP_ROM_PTR(&mod_trezorutils_model_name_obj)}, + {MP_ROM_QSTR(MP_QSTR_MODEL_FULL_NAME), + MP_ROM_PTR(&mod_trezorutils_full_name_obj)}, {MP_ROM_QSTR(MP_QSTR_INTERNAL_MODEL), MP_ROM_QSTR(MODEL_INTERNAL_NAME_QSTR)}, #ifdef TREZOR_EMULATOR diff --git a/core/embed/models/model_D001.h b/core/embed/models/model_D001.h index 1d7ddac433..5a97b71e6f 100644 --- a/core/embed/models/model_D001.h +++ b/core/embed/models/model_D001.h @@ -2,6 +2,7 @@ #define MODELS_MODEL_D001_H_ #define MODEL_NAME "T" +#define MODEL_FULL_NAME "Trezor Model T" #define MODEL_INTERNAL_NAME "D001" #define MODEL_INTERNAL_NAME_TOKEN T #define MODEL_INTERNAL_NAME_QSTR MP_QSTR_D001 diff --git a/core/embed/models/model_T1B1.h b/core/embed/models/model_T1B1.h index 36cf6f5918..b1f425584b 100644 --- a/core/embed/models/model_T1B1.h +++ b/core/embed/models/model_T1B1.h @@ -2,6 +2,7 @@ #define MODELS_MODEL_T1B1_H_ #define MODEL_NAME "1" +#define MODEL_FULL_NAME "Trezor Model One" #define MODEL_INTERNAL_NAME "T1B1" #define MODEL_INTERNAL_NAME_TOKEN T1B1 #define MODEL_INTERNAL_NAME_QSTR MP_QSTR_T1B1 diff --git a/core/embed/models/model_T2B1.h b/core/embed/models/model_T2B1.h index e04b349145..26bb9c2f55 100644 --- a/core/embed/models/model_T2B1.h +++ b/core/embed/models/model_T2B1.h @@ -2,6 +2,7 @@ #define MODELS_MODEL_T2B1_H_ #define MODEL_NAME "Safe 3" +#define MODEL_FULL_NAME "Trezor Safe 3" #define MODEL_INTERNAL_NAME "T2B1" #define MODEL_INTERNAL_NAME_TOKEN T2B1 #define MODEL_INTERNAL_NAME_QSTR MP_QSTR_T2B1 diff --git a/core/embed/models/model_T2T1.h b/core/embed/models/model_T2T1.h index 68b57d1908..bbc6964a2b 100644 --- a/core/embed/models/model_T2T1.h +++ b/core/embed/models/model_T2T1.h @@ -2,6 +2,7 @@ #define MODELS_MODEL_T2T1_H_ #define MODEL_NAME "T" +#define MODEL_FULL_NAME "Trezor Model T" #define MODEL_INTERNAL_NAME "T2T1" #define MODEL_INTERNAL_NAME_TOKEN T2T1 #define MODEL_INTERNAL_NAME_QSTR MP_QSTR_T2T1 diff --git a/core/embed/prodtest/optiga_prodtest.c b/core/embed/prodtest/optiga_prodtest.c index 924659a650..b27b76a6fd 100644 --- a/core/embed/prodtest/optiga_prodtest.c +++ b/core/embed/prodtest/optiga_prodtest.c @@ -112,8 +112,8 @@ void pair_optiga(void) { // Enable writing the pairing secret to OPTIGA. optiga_metadata metadata = {0}; - metadata.change = OPTIGA_ACCESS_ALWAYS; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; metadata.data_type = TYPE_PTFBIND; set_metadata(OID_KEY_PAIRING, &metadata); // Ignore result. @@ -169,29 +169,29 @@ void optiga_lock(void) { // Set metadata for device certificate. memzero(&metadata, sizeof(metadata)); - metadata.lcso = OPTIGA_LCS_OPERATIONAL; - metadata.change = OPTIGA_ACCESS_NEVER; - metadata.read = OPTIGA_ACCESS_ALWAYS; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; if (!set_metadata(OID_CERT_DEV, &metadata)) { return; } // Set metadata for FIDO attestation certificate. memzero(&metadata, sizeof(metadata)); - metadata.lcso = OPTIGA_LCS_OPERATIONAL; - metadata.change = OPTIGA_ACCESS_NEVER; - metadata.read = OPTIGA_ACCESS_ALWAYS; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; if (!set_metadata(OID_CERT_FIDO, &metadata)) { return; } // Set metadata for device private key. memzero(&metadata, sizeof(metadata)); - metadata.lcso = OPTIGA_LCS_OPERATIONAL; - metadata.change = OPTIGA_ACCESS_NEVER; - metadata.read = OPTIGA_ACCESS_NEVER; + metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_NEVER; metadata.execute = ACCESS_PAIRED; metadata.key_usage = KEY_USE_SIGN; if (!set_metadata(OID_KEY_DEV, &metadata)) { @@ -200,9 +200,9 @@ void optiga_lock(void) { // Set metadata for FIDO attestation private key. memzero(&metadata, sizeof(metadata)); - metadata.lcso = OPTIGA_LCS_OPERATIONAL; - metadata.change = OPTIGA_ACCESS_NEVER; - metadata.read = OPTIGA_ACCESS_NEVER; + metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_NEVER; metadata.execute = ACCESS_PAIRED; metadata.key_usage = KEY_USE_SIGN; if (!set_metadata(OID_KEY_FIDO, &metadata)) { @@ -211,10 +211,10 @@ void optiga_lock(void) { // Set metadata for pairing key. memzero(&metadata, sizeof(metadata)); - metadata.lcso = OPTIGA_LCS_OPERATIONAL; - metadata.change = OPTIGA_ACCESS_NEVER; - metadata.read = OPTIGA_ACCESS_NEVER; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; metadata.data_type = TYPE_PTFBIND; if (!set_metadata(OID_KEY_PAIRING, &metadata)) { return; @@ -230,7 +230,7 @@ optiga_locked_status get_optiga_locked_status(void) { OID_KEY_FIDO, OID_KEY_PAIRING}; optiga_metadata locked_metadata = {0}; - locked_metadata.lcso = OPTIGA_LCS_OPERATIONAL; + locked_metadata.lcso = OPTIGA_META_LCS_OPERATIONAL; for (size_t i = 0; i < sizeof(oids) / sizeof(oids[0]); ++i) { uint8_t metadata_buffer[OPTIGA_MAX_METADATA_SIZE] = {0}; size_t metadata_size = 0; @@ -334,7 +334,7 @@ void cert_write(uint16_t oid, char *data) { // Enable writing to the certificate slot. optiga_metadata metadata = {0}; - metadata.change = OPTIGA_ACCESS_ALWAYS; + metadata.change = OPTIGA_META_ACCESS_ALWAYS; set_metadata(oid, &metadata); // Ignore result. uint8_t data_bytes[1024]; @@ -360,10 +360,8 @@ void pubkey_read(uint16_t oid) { // Enable key agreement usage. optiga_metadata metadata = {0}; - uint8_t key_usage = OPTIGA_KEY_USAGE_KEYAGREE; - metadata.key_usage.ptr = &key_usage; - metadata.key_usage.len = 1; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.key_usage = OPTIGA_META_KEY_USE_KEYAGREE; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; if (!set_metadata(oid, &metadata)) { return; @@ -402,10 +400,8 @@ void keyfido_write(char *data) { // Enable key agreement usage for device key. optiga_metadata metadata = {0}; - uint8_t key_usage = OPTIGA_KEY_USAGE_KEYAGREE; - metadata.key_usage.ptr = &key_usage; - metadata.key_usage.len = 1; - metadata.execute = OPTIGA_ACCESS_ALWAYS; + metadata.key_usage = OPTIGA_META_KEY_USE_KEYAGREE; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; if (!set_metadata(OID_KEY_DEV, &metadata)) { return; diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index e7e25f32fb..357211d8de 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -84,6 +84,7 @@ fn prepare_bindings() -> bindgen::Builder { "-I../../vendor/micropython/lib/uzlib", "-I../lib", "-I../trezorhal", + "-I../models", format!("-D{}", mcu_type()).as_str(), format!("-DTREZOR_MODEL_{}", model()).as_str(), format!("-DTREZOR_BOARD=\"{}\"", board()).as_str(), @@ -259,6 +260,9 @@ fn generate_trezorhal_bindings() { let bindings = prepare_bindings() .header("trezorhal.h") + // model + .allowlist_var("MODEL_INTERNAL_NAME") + .allowlist_var("MODEL_FULL_NAME") // common .allowlist_var("HW_ENTROPY_DATA") // secbool diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs index 8a55becbb9..86fe0a76be 100644 --- a/core/embed/rust/src/trezorhal/mod.rs +++ b/core/embed/rust/src/trezorhal/mod.rs @@ -8,6 +8,7 @@ pub mod display; pub mod dma2d; mod ffi; pub mod io; +pub mod model; pub mod random; #[cfg(feature = "rgb_led")] pub mod rgb_led; diff --git a/core/embed/rust/src/trezorhal/model.rs b/core/embed/rust/src/trezorhal/model.rs new file mode 100644 index 0000000000..f21bad771e --- /dev/null +++ b/core/embed/rust/src/trezorhal/model.rs @@ -0,0 +1,14 @@ +use super::ffi::{MODEL_FULL_NAME, MODEL_INTERNAL_NAME}; + +// TODO: get bindgen to do this for us more safely +// https://github.com/rust-lang/rust-bindgen/issues/1401 + +const unsafe fn ascii_const_to_str_unchecked(s: &[u8]) -> &str { + unsafe { + let strip_nul = core::slice::from_raw_parts(s.as_ptr(), s.len() - 1); + core::str::from_utf8_unchecked(strip_nul) + } +} + +pub const FULL_NAME: &str = unsafe { ascii_const_to_str_unchecked(MODEL_FULL_NAME) }; +pub const INTERNAL_NAME: &str = unsafe { ascii_const_to_str_unchecked(MODEL_INTERNAL_NAME) }; diff --git a/core/embed/rust/src/ui/component/marquee.rs b/core/embed/rust/src/ui/component/marquee.rs index 7748810944..c9bb1df426 100644 --- a/core/embed/rust/src/ui/component/marquee.rs +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -10,7 +10,6 @@ use crate::{ }, }; -const MILLIS_PER_LETTER_M: u32 = 300; const ANIMATION_DURATION_MS: u32 = 2000; const PAUSE_DURATION_MS: u32 = 1000; @@ -141,22 +140,6 @@ where type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { - let base_width = self.font.text_width("M"); - let text_width = self.font.text_width(self.text.as_ref()); - let area_width = bounds.width(); - - let shift_width = if area_width > text_width { - area_width - text_width - } else { - text_width - area_width - }; - - let mut duration = (MILLIS_PER_LETTER_M * shift_width as u32) / base_width as u32; - if duration < MILLIS_PER_LETTER_M { - duration = MILLIS_PER_LETTER_M; - } - - self.duration = Duration::from_millis(duration); self.area = bounds; self.area } diff --git a/core/embed/rust/src/ui/model_tr/component/homescreen.rs b/core/embed/rust/src/ui/model_tr/component/homescreen.rs index 09a7cd07ef..fe79ad24e9 100644 --- a/core/embed/rust/src/ui/model_tr/component/homescreen.rs +++ b/core/embed/rust/src/ui/model_tr/component/homescreen.rs @@ -28,6 +28,15 @@ const LABEL_OUTSET: i16 = 3; const NOTIFICATION_FONT: Font = Font::NORMAL; const NOTIFICATION_ICON: Icon = theme::ICON_WARNING; +fn paint_default_image() { + theme::ICON_LOGO.draw( + TOP_CENTER + Offset::y(LOGO_ICON_TOP_MARGIN), + Alignment2D::TOP_CENTER, + theme::FG, + theme::BG, + ); +} + pub struct Homescreen where T: StringType, @@ -58,12 +67,7 @@ where let toif_data = unwrap!(Toif::new(user_custom_image.as_ref())); toif_data.draw(TOP_CENTER, Alignment2D::TOP_CENTER, theme::FG, theme::BG); } else { - theme::ICON_LOGO.draw( - TOP_CENTER + Offset::y(LOGO_ICON_TOP_MARGIN), - Alignment2D::TOP_CENTER, - theme::FG, - theme::BG, - ); + paint_default_image(); } } @@ -273,8 +277,13 @@ where fn paint(&mut self) { // Drawing the image full-screen first and then other things on top - let toif_data = unwrap!(Toif::new((self.buffer_func)())); - toif_data.draw(TOP_CENTER, Alignment2D::TOP_CENTER, theme::FG, theme::BG); + let buffer = (self.buffer_func)(); + if buffer.is_empty() { + paint_default_image(); + } else { + let toif_data = unwrap!(Toif::new(buffer)); + toif_data.draw(TOP_CENTER, Alignment2D::TOP_CENTER, theme::FG, theme::BG); + }; // Need to make all the title background black, so the title text is well // visible let title_area = self.title.inner().area(); diff --git a/core/embed/rust/src/ui/model_tr/constant.rs b/core/embed/rust/src/ui/model_tr/constant.rs index b39257d86d..7b7eb01193 100644 --- a/core/embed/rust/src/ui/model_tr/constant.rs +++ b/core/embed/rust/src/ui/model_tr/constant.rs @@ -11,8 +11,6 @@ pub const LOADER_OUTER: i16 = 32; pub const LOADER_INNER: i16 = 18; pub const LOADER_ICON_MAX_SIZE: i16 = 8; -pub const MODEL_NAME: &str = "Trezor Safe 3"; - pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT) } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index a013fc8eb2..24e8b29324 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -17,6 +17,7 @@ use crate::{ util, }, strutil::StringType, + trezorhal::model, ui::{ component::{ base::Component, @@ -1548,7 +1549,7 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut let label: StrBuffer = kwargs .get(Qstr::MP_QSTR_label)? .try_into_option()? - .unwrap_or_else(|| constant::MODEL_NAME.into()); + .unwrap_or_else(|| model::FULL_NAME.into()); let notification: Option = kwargs.get(Qstr::MP_QSTR_notification)?.try_into_option()?; let notification_level: u8 = kwargs.get_or(Qstr::MP_QSTR_notification_level, 0)?; @@ -1569,7 +1570,7 @@ extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut let label: StrBuffer = kwargs .get(Qstr::MP_QSTR_label)? .try_into_option()? - .unwrap_or_else(|| constant::MODEL_NAME.into()); + .unwrap_or_else(|| model::FULL_NAME.into()); let bootscreen: bool = kwargs.get(Qstr::MP_QSTR_bootscreen)?.try_into()?; let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?; diff --git a/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs b/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs index bc167a95b7..482dd53e2d 100644 --- a/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs +++ b/core/embed/rust/src/ui/model_tt/component/welcome_screen.rs @@ -11,7 +11,7 @@ const ICON_TOP_MARGIN: i16 = 48; #[cfg(not(feature = "bootloader"))] const MODEL_NAME_FONT: display::Font = display::Font::DEMIBOLD; #[cfg(not(feature = "bootloader"))] -use crate::ui::{constant::MODEL_NAME, display}; +use crate::{trezorhal::model, ui::display}; pub struct WelcomeScreen { area: Rect, @@ -54,7 +54,7 @@ impl Component for WelcomeScreen { #[cfg(not(feature = "bootloader"))] display::text_center( self.area.bottom_center() - Offset::y(TEXT_BOTTOM_MARGIN), - MODEL_NAME, + model::FULL_NAME, MODEL_NAME_FONT, theme::FG, theme::BG, @@ -74,6 +74,6 @@ impl crate::trace::Trace for WelcomeScreen { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("WelcomeScreen"); #[cfg(not(feature = "bootloader"))] - t.string("model", MODEL_NAME); + t.string("model", model::FULL_NAME); } } diff --git a/core/embed/rust/src/ui/model_tt/constant.rs b/core/embed/rust/src/ui/model_tt/constant.rs index f84b97cf39..3d4f7005bc 100644 --- a/core/embed/rust/src/ui/model_tt/constant.rs +++ b/core/embed/rust/src/ui/model_tt/constant.rs @@ -11,8 +11,6 @@ pub const LOADER_OUTER: i16 = 60; pub const LOADER_INNER: i16 = 42; pub const LOADER_ICON_MAX_SIZE: i16 = 64; -pub const MODEL_NAME: &str = "Trezor Model T"; - pub const fn size() -> Offset { Offset::new(WIDTH, HEIGHT) } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index fc9e602acf..1f1539f211 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -15,6 +15,7 @@ use crate::{ util, }, strutil::StringType, + trezorhal::model, ui::{ component::{ base::ComponentExt, @@ -703,16 +704,25 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m // Layout needs to hold the Obj to play nice with GC. Obj is resolved to &[u8] // in every paint pass. - // SAFETY: We expect no existing mutable reference. Resulting reference is - // discarded before returning to micropython. - let buffer_func = move || unsafe { unwrap!(get_buffer(data)) }; + let buffer_func = move || { + // SAFETY: We expect no existing mutable reference. Resulting reference is + // discarded before returning to micropython. + let buffer = unsafe { unwrap!(get_buffer(data)) }; + // Incoming data may be empty, meaning we should display default homescreen + // image. + if buffer.is_empty() { + theme::IMAGE_HOMESCREEN + } else { + buffer + } + }; let size = match jpeg_info(buffer_func()) { Some(info) => info.0, _ => return Err(value_error!("Invalid image.")), }; - let buttons = Button::cancel_confirm_text(None, Some("CONFIRM")); + let buttons = Button::cancel_confirm_text(None, Some("CHANGE")); let obj = LayoutObj::new(Frame::centered( theme::label_title(), title, @@ -1587,7 +1597,7 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut let label: StrBuffer = kwargs .get(Qstr::MP_QSTR_label)? .try_into_option()? - .unwrap_or_else(|| constant::MODEL_NAME.into()); + .unwrap_or_else(|| model::FULL_NAME.into()); let notification: Option = kwargs.get(Qstr::MP_QSTR_notification)?.try_into_option()?; let notification_level: u8 = kwargs.get_or(Qstr::MP_QSTR_notification_level, 0)?; @@ -1609,7 +1619,7 @@ extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut let label: StrBuffer = kwargs .get(Qstr::MP_QSTR_label)? .try_into_option()? - .unwrap_or_else(|| constant::MODEL_NAME.into()); + .unwrap_or_else(|| model::FULL_NAME.into()); let bootscreen: bool = kwargs.get(Qstr::MP_QSTR_bootscreen)?.try_into()?; let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?; diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h index f8bb797a94..e34711d70c 100644 --- a/core/embed/rust/trezorhal.h +++ b/core/embed/rust/trezorhal.h @@ -7,6 +7,7 @@ #include "dma2d.h" #include "flash.h" #include "fonts/fonts.h" +#include "model.h" #include "rgb_led.h" #include "secbool.h" #include "storage.h" diff --git a/core/embed/trezorhal/optiga.h b/core/embed/trezorhal/optiga.h index 83af3cc022..cb009d901d 100644 --- a/core/embed/trezorhal/optiga.h +++ b/core/embed/trezorhal/optiga.h @@ -23,6 +23,7 @@ #include #include #include +#include "secbool.h" #define OPTIGA_DEVICE_CERT_INDEX 1 #define OPTIGA_DEVICE_ECC_KEY_INDEX 0 @@ -31,6 +32,15 @@ // Error code 7: Access conditions not satisfied #define OPTIGA_ERR_ACCESS_COND_NOT_SAT (OPTIGA_COMMAND_ERROR_OFFSET + 0x07) +// Size of secrets used in PIN processing, e.g. salted PIN, master secret etc. +#define OPTIGA_PIN_SECRET_SIZE 32 + +// The number of milliseconds it takes to execute optiga_pin_set() or +// optiga_pin_verify(). +#define OPTIGA_PIN_DERIVE_MS 1200 + +typedef secbool (*OPTIGA_UI_PROGRESS)(uint32_t elapsed_ms); + int optiga_sign(uint8_t index, const uint8_t *digest, size_t digest_size, uint8_t *signature, size_t max_sig_size, size_t *sig_size); @@ -41,4 +51,12 @@ bool optiga_read_cert(uint8_t index, uint8_t *cert, size_t max_cert_size, bool optiga_random_buffer(uint8_t *dest, size_t size); +bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]); + +bool optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]); + #endif diff --git a/core/embed/trezorhal/optiga/optiga.c b/core/embed/trezorhal/optiga/optiga.c index 50e4cfed18..0ed8a3bb99 100644 --- a/core/embed/trezorhal/optiga/optiga.c +++ b/core/embed/trezorhal/optiga/optiga.c @@ -19,7 +19,63 @@ #include "optiga.h" #include +#include "hash_to_curve.h" +#include "hmac.h" +#include "memzero.h" #include "optiga_commands.h" +#include "rand.h" +#include "storage.h" + +// PIN counter reset key / Master secret (OID 0xF1D0). +#define OID_PIN_SECRET (OPTIGA_OID_DATA + 0) + +// PIN counter (OID 0xE120). +#define OID_PIN_COUNTER (OPTIGA_OID_COUNTER + 0) + +// PIN stretching counter (OID 0xE121). +#define OID_PIN_STRETCH_COUNTER (OPTIGA_OID_COUNTER + 1) + +// Stretched PIN (OID 0xF1D4). +#define OID_STRETCHED_PIN (OPTIGA_OID_DATA + 4) + +// Key for HMAC-SHA256 PIN stretching step (OID 0xF1D1). +#define OID_PIN_HMAC (OPTIGA_OID_DATA + 1) + +// Key for AES-CMAC PIN stretching step (OID 0xE200). +#define OID_PIN_CMAC OPTIGA_OID_SYM_KEY + +// Key for ECDH PIN stretching step (OID 0xE0F3). +#define OID_PIN_ECDH (OPTIGA_OID_ECC_KEY + 3) + +// The number of times the stretching is repeated in each PIN processing phase. +#define PIN_STRETCH_ITERATIONS 1 + +// Value of the PIN counter when it is reset. +static const uint8_t COUNTER_RESET[] = {0, 0, 0, 0, 0, 0, 0, PIN_MAX_TRIES}; + +// Value of the PIN stretching counter when it is initialized. The limit is +// 600000 stretching operations, which equates to +// 100000 / PIN_STRETCH_ITERATIONS unlock operations. +static const uint8_t STRETCH_COUNTER_INIT[] = {0, 0, 0, 0, 0, 0x09, 0x27, 0xC0}; + +static const optiga_metadata_item TYPE_AUTOREF = { + (const uint8_t[]){OPTIGA_DATA_TYPE_AUTOREF}, 1}; +static const optiga_metadata_item TYPE_PRESSEC = { + (const uint8_t[]){OPTIGA_DATA_TYPE_PRESSEC}, 1}; +static const optiga_metadata_item ACCESS_STRETCHED_PIN = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_STRETCHED_PIN); +static const optiga_metadata_item ACCESS_PIN_SECRET = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_AUTO, OID_PIN_SECRET); +static const optiga_metadata_item ACCESS_PIN_COUNTER = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_COUNTER); +static const optiga_metadata_item ACCESS_PIN_STRETCH_COUNTER = + OPTIGA_ACCESS_CONDITION(OPTIGA_ACCESS_COND_LUC, OID_PIN_STRETCH_COUNTER); + +// Size of the DER BIT STRING header required for inputs to optiga_calc_ssec(). +#define BIT_STRING_HEADER_SIZE 3 + +// Size of the CMAC/HMAC prefix returned by Optiga. +#define ENCRYPT_SYM_PREFIX_SIZE 3 int optiga_sign(uint8_t index, const uint8_t *digest, size_t digest_size, uint8_t *signature, size_t max_sig_size, size_t *sig_size) { @@ -109,3 +165,449 @@ bool optiga_random_buffer(uint8_t *dest, size_t size) { return optiga_get_random(dest, size) == OPTIGA_SUCCESS; } + +static bool read_metadata(uint16_t oid, optiga_metadata *metadata) { + static uint8_t serialized[OPTIGA_MAX_METADATA_SIZE] = {0}; + size_t size = 0; + optiga_result ret = + optiga_get_data_object(oid, true, serialized, sizeof(serialized), &size); + if (OPTIGA_SUCCESS != ret) { + return false; + } + + ret = optiga_parse_metadata(serialized, size, metadata); + return OPTIGA_SUCCESS == ret; +} + +static bool write_metadata(uint16_t oid, const optiga_metadata *metadata) { + uint8_t serialized[OPTIGA_MAX_METADATA_SIZE] = {0}; + size_t size = 0; + + optiga_result ret = optiga_serialize_metadata(metadata, serialized, + sizeof(serialized), &size); + if (OPTIGA_SUCCESS != ret) { + return false; + } + + ret = optiga_set_data_object(oid, true, serialized, size); + return OPTIGA_SUCCESS == ret; +} + +bool optiga_set_metadata(uint16_t oid, const optiga_metadata *metadata) { + // Read the stored metadata. + optiga_metadata metadata_stored = {0}; + if (!read_metadata(oid, &metadata_stored)) { + return false; + } + + // If the stored metadata are different, then set them as requested. + if (!optiga_compare_metadata(metadata, &metadata_stored)) { + if (!write_metadata(oid, metadata)) { + return false; + } + + // Check that the metadata was written correctly. + if (!read_metadata(oid, &metadata_stored)) { + return false; + } + if (!optiga_compare_metadata(metadata, &metadata_stored)) { + return false; + } + } + + // If the metadata aren't locked, then lock them. + optiga_metadata metadata_locked = {0}; + metadata_locked.lcso = OPTIGA_META_LCS_OPERATIONAL; + if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) { + if (!write_metadata(oid, &metadata_locked)) { + return false; + } + + // Check that metadata were locked correctly. + if (!read_metadata(oid, &metadata_stored)) { + return false; + } + if (!optiga_compare_metadata(&metadata_locked, &metadata_stored)) { + return false; + } + } + + return true; +} + +static bool optiga_pin_init_metadata(void) { + optiga_metadata metadata = {0}; + + // Set metadata for PIN counter reset key / Master secret. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + metadata.read = ACCESS_STRETCHED_PIN; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; + metadata.data_type = TYPE_AUTOREF; + if (!optiga_set_metadata(OID_PIN_SECRET, &metadata)) { + return false; + } + + // Set metadata for PIN counter. + memzero(&metadata, sizeof(metadata)); + metadata.change = ACCESS_PIN_SECRET; + metadata.read = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; + if (!optiga_set_metadata(OID_PIN_COUNTER, &metadata)) { + return false; + } + + // Initialize the PIN stretching counter if write access is possible. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + if (write_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { + optiga_result res = optiga_set_data_object(OID_PIN_STRETCH_COUNTER, false, + STRETCH_COUNTER_INIT, + sizeof(STRETCH_COUNTER_INIT)); + if (res != OPTIGA_SUCCESS) { + return false; + } + } + + // Set metadata for PIN stretching counter. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_ALWAYS; + metadata.execute = OPTIGA_META_ACCESS_ALWAYS; + if (!optiga_set_metadata(OID_PIN_STRETCH_COUNTER, &metadata)) { + return false; + } + + // Set metadata for stretched PIN. + memzero(&metadata, sizeof(metadata)); + metadata.change = ACCESS_PIN_SECRET; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_PIN_COUNTER; + metadata.data_type = TYPE_AUTOREF; + if (!optiga_set_metadata(OID_STRETCHED_PIN, &metadata)) { + return false; + } + + // Set metadata for AES-CMAC PIN stretching secret. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_PIN_STRETCH_COUNTER; + if (!optiga_set_metadata(OID_PIN_CMAC, &metadata)) { + return false; + } + + // Set metadata for ECDH PIN stretching secret. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_PIN_STRETCH_COUNTER; + metadata.key_usage = OPTIGA_META_KEY_USE_KEYAGREE; + if (!optiga_set_metadata(OID_PIN_ECDH, &metadata)) { + return false; + } + + // Generate and store the HMAC PIN stretching secret in OID_PIN_HMAC, if write + // access is possible. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_ALWAYS; + if (write_metadata(OID_PIN_HMAC, &metadata)) { + uint8_t secret[OPTIGA_PIN_SECRET_SIZE] = {0}; + optiga_result res = optiga_get_random(secret, sizeof(secret)); + if (res != OPTIGA_SUCCESS) { + return false; + } + random_xor(secret, sizeof(secret)); + + res = optiga_set_data_object(OID_PIN_HMAC, false, secret, sizeof(secret)); + memzero(secret, sizeof(secret)); + if (res != OPTIGA_SUCCESS) { + return false; + } + } + + // Set metadata for HMAC-SHA256 PIN stretching secret. + memzero(&metadata, sizeof(metadata)); + metadata.change = OPTIGA_META_ACCESS_NEVER; + metadata.read = OPTIGA_META_ACCESS_NEVER; + metadata.execute = ACCESS_PIN_STRETCH_COUNTER; + metadata.data_type = TYPE_PRESSEC; + if (!optiga_set_metadata(OID_PIN_HMAC, &metadata)) { + return false; + } + + return true; +} + +static bool optiga_pin_init_stretch(void) { + // Generate a new key in OID_PIN_CMAC. + optiga_result res = + optiga_gen_sym_key(OPTIGA_AES_256, OPTIGA_KEY_USAGE_ENC, OID_PIN_CMAC); + if (res != OPTIGA_SUCCESS) { + return false; + } + + // Generate a new key in OID_PIN_ECDH. + uint8_t public_key[6 + 65] = {0}; + size_t size = 0; + res = + optiga_gen_key_pair(OPTIGA_CURVE_P256, OPTIGA_KEY_USAGE_KEYAGREE, + OID_PIN_ECDH, public_key, sizeof(public_key), &size); + if (res != OPTIGA_SUCCESS) { + return false; + } + + return true; +} + +static bool optiga_pin_stretch_secret(OPTIGA_UI_PROGRESS ui_progress, + uint8_t secret[OPTIGA_PIN_SECRET_SIZE]) { + // This step hardens the PIN verification process in case an attacker is able + // to extract the secret value of a data object in Optiga that has a + // particular configuration, but does not allow secret extraction for other + // kinds of data objects. An attacker would need to be able to extract each of + // the secrets in the different data objects to conduct an offline brute-force + // search for the PIN. Thus it reduces the number of PIN values that the + // attacker can test in a unit of time by forcing them to involve the Optiga + // in each attempt. + + // Pseudocode for the stretching process: + // result_0 = secret + // for i in range(PIN_STRETCH_ITERATIONS): + // cmac_i = CMAC(optiga_cmac_key, result_i) + // hmac_i = HMAC(optiga_hmac_key, result_i) + // ecdh_i = ECDH(optiga_ecdh_key, result_i) + // result_{i+1} = HMAC-SHA256(secret, cmac_i || hmac_i || ecdh_i) + // secret = result_{PIN_STRETCH_ITERATIONS} + + HMAC_SHA256_CTX ctx = {0}; + + uint8_t result[OPTIGA_PIN_SECRET_SIZE] = {0}; + memcpy(result, secret, sizeof(result)); + + uint8_t buffer[ENCRYPT_SYM_PREFIX_SIZE + OPTIGA_PIN_SECRET_SIZE] = {0}; + size_t size = 0; + for (int i = 0; i < PIN_STRETCH_ITERATIONS; ++i) { + hmac_sha256_Init(&ctx, secret, OPTIGA_PIN_SECRET_SIZE); + + // Combine intermediate result with OID_PIN_CMAC. + optiga_result res = + optiga_encrypt_sym(OPTIGA_SYM_MODE_CMAC, OID_PIN_CMAC, result, + sizeof(result), buffer, sizeof(buffer), &size); + if (res != OPTIGA_SUCCESS) { + memzero(buffer, sizeof(buffer)); + memzero(result, sizeof(result)); + memzero(&ctx, sizeof(ctx)); + return false; + } + + hmac_sha256_Update(&ctx, buffer, size); + + // Combine intermediate result with OID_PIN_HMAC + res = optiga_encrypt_sym(OPTIGA_SYM_MODE_HMAC_SHA256, OID_PIN_HMAC, result, + sizeof(result), buffer, sizeof(buffer), &size); + if (res != OPTIGA_SUCCESS) { + memzero(buffer, sizeof(buffer)); + memzero(result, sizeof(result)); + memzero(&ctx, sizeof(ctx)); + return false; + } + + hmac_sha256_Update(&ctx, buffer, size); + + ui_progress(200); + + // Combine intermediate result with OID_PIN_ECDH + uint8_t encoded_point[BIT_STRING_HEADER_SIZE + 65] = {0x03, 0x42, 0x00}; + if (!hash_to_curve_optiga(result, &encoded_point[BIT_STRING_HEADER_SIZE])) { + memzero(buffer, sizeof(buffer)); + memzero(result, sizeof(result)); + memzero(&ctx, sizeof(ctx)); + return false; + } + res = + optiga_calc_ssec(OPTIGA_CURVE_P256, OID_PIN_ECDH, encoded_point, + sizeof(encoded_point), buffer, sizeof(buffer), &size); + memzero(encoded_point, sizeof(encoded_point)); + if (res != OPTIGA_SUCCESS) { + memzero(buffer, sizeof(buffer)); + memzero(result, sizeof(result)); + memzero(&ctx, sizeof(ctx)); + return false; + } + + hmac_sha256_Update(&ctx, buffer, size); + + hmac_sha256_Final(&ctx, result); + + ui_progress(200); + } + + memcpy(secret, result, sizeof(result)); + memzero(buffer, sizeof(buffer)); + memzero(result, sizeof(result)); + memzero(&ctx, sizeof(ctx)); + return true; +} + +bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { + if (!optiga_pin_init_metadata() || !optiga_pin_init_stretch()) { + return false; + } + ui_progress(200); + + // Process the PIN-derived secret using a one-way function before sending it + // to the Optiga. This ensures that in the unlikely case of an attacker + // recording communication between the MCU and Optiga, they will not gain + // knowledge of the PIN-derived secret. + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, stretched_pin); + + // Combine the result with stretching secrets from the Optiga. This step + // ensures that if an attacker extracts the value of OID_STRETCHED_PIN, then + // it cannot be used to conduct an offline brute-force search for the PIN. + if (!optiga_pin_stretch_secret(ui_progress, stretched_pin)) { + memzero(stretched_pin, sizeof(stretched_pin)); + return false; + } + + // Generate and store the master secret / PIN counter reset key. + optiga_result res = optiga_get_random(out_secret, OPTIGA_PIN_SECRET_SIZE); + if (res != OPTIGA_SUCCESS) { + memzero(stretched_pin, sizeof(stretched_pin)); + return false; + } + random_xor(out_secret, OPTIGA_PIN_SECRET_SIZE); + + res = optiga_set_data_object(OID_PIN_SECRET, false, out_secret, + OPTIGA_PIN_SECRET_SIZE); + if (res != OPTIGA_SUCCESS) { + memzero(stretched_pin, sizeof(stretched_pin)); + return false; + } + + // Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER and + // OID_STRETCHED_PIN. + res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, + out_secret, OPTIGA_PIN_SECRET_SIZE); + if (res != OPTIGA_SUCCESS) { + memzero(stretched_pin, sizeof(stretched_pin)); + return false; + } + + // Set the stretched PIN. + res = optiga_set_data_object(OID_STRETCHED_PIN, false, stretched_pin, + sizeof(stretched_pin)); + memzero(stretched_pin, sizeof(stretched_pin)); + if (res != OPTIGA_SUCCESS) { + optiga_clear_auto_state(OID_PIN_SECRET); + return false; + } + + // Initialize the PIN counter. + res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); + optiga_clear_auto_state(OID_PIN_SECRET); + if (res != OPTIGA_SUCCESS) { + return false; + } + + ui_progress(200); + + // Combine the value of OID_PIN_SECRET with the PIN-derived secret and + // stretching secrets from the Optiga. This step ensures that if an attacker + // extracts the value of OID_PIN_SECRET, then it cannot be used to conduct an + // offline brute-force search for the PIN. + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, + OPTIGA_PIN_SECRET_SIZE, out_secret); + if (!optiga_pin_stretch_secret(ui_progress, out_secret)) { + return false; + } + + // Combine the stretched master secret with the PIN-derived secret to obtain + // the output secret. This ensures that in the unlikely case of an attacker + // recording communication between the MCU and Optiga, they cannot decrypt the + // storage without having to conduct a brute-force search for the PIN. + // NOTE: Considering how optiga_pin_stretch_secret() works internally and the + // fact that the PIN was already combined with the value of OID_PIN_SECRET, + // this step is not necessary. However, it is preferable to explicitly execute + // it, than to rely on the internals of optiga_pin_stretch_secret(). + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, + OPTIGA_PIN_SECRET_SIZE, out_secret); + + // Recombining the returned secret with the PIN-derived secret means that if + // the user chooses a high-entropy PIN, then even if the Optiga and its + // communication link is completely compromised, it will not reduce the + // security of their device any more than if the Optiga was not integrated + // into the device in the first place. + + return true; +} + +bool optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { + // Process the PIN-derived secret using a one-way function before sending it + // to the Optiga. + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, stretched_pin); + + // Combine the result with stretching secrets from the Optiga. + if (!optiga_pin_stretch_secret(ui_progress, stretched_pin)) { + memzero(stretched_pin, sizeof(stretched_pin)); + return false; + } + + // Authorise using OID_STRETCHED_PIN so that we can read from OID_PIN_SECRET. + optiga_result res = + optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_STRETCHED_PIN, + stretched_pin, sizeof(stretched_pin)); + memzero(stretched_pin, sizeof(stretched_pin)); + if (res != OPTIGA_SUCCESS) { + return false; + } + + // Read the master secret from OID_PIN_SECRET. + size_t size = 0; + res = optiga_get_data_object(OID_PIN_SECRET, false, out_secret, + OPTIGA_PIN_SECRET_SIZE, &size); + optiga_clear_auto_state(OID_STRETCHED_PIN); + if (res != OPTIGA_SUCCESS || size != OPTIGA_PIN_SECRET_SIZE) { + return false; + } + + ui_progress(200); + + // Authorise using OID_PIN_SECRET so that we can write to OID_PIN_COUNTER. + res = optiga_set_auto_state(OPTIGA_OID_SESSION_CTX, OID_PIN_SECRET, + out_secret, OPTIGA_PIN_SECRET_SIZE); + if (res != OPTIGA_SUCCESS) { + return false; + } + + // Reset the PIN counter. + res = optiga_set_data_object(OID_PIN_COUNTER, false, COUNTER_RESET, + sizeof(COUNTER_RESET)); + optiga_clear_auto_state(OID_PIN_SECRET); + if (res != OPTIGA_SUCCESS) { + return false; + } + + ui_progress(200); + + // Combine the value of OID_PIN_SECRET with the PIN-derived secret and + // stretching secrets from the Optiga. + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, + OPTIGA_PIN_SECRET_SIZE, out_secret); + if (!optiga_pin_stretch_secret(ui_progress, out_secret)) { + return false; + } + + // Combine the stretched master secret with the PIN-derived secret to derive + // the output secret. + hmac_sha256(pin_secret, OPTIGA_PIN_SECRET_SIZE, out_secret, + OPTIGA_PIN_SECRET_SIZE, out_secret); + return true; +} diff --git a/core/embed/trezorhal/optiga/optiga_commands.c b/core/embed/trezorhal/optiga/optiga_commands.c index 2aa4c12a46..8ebf28ff76 100644 --- a/core/embed/trezorhal/optiga/optiga_commands.c +++ b/core/embed/trezorhal/optiga/optiga_commands.c @@ -36,13 +36,15 @@ static uint8_t tx_buffer[OPTIGA_MAX_APDU_SIZE] = {0}; static size_t tx_size = 0; -const optiga_metadata_item OPTIGA_LCS_OPERATIONAL = {(const uint8_t *)"\x07", - 1}; -const optiga_metadata_item OPTIGA_ACCESS_ALWAYS = { +const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL = { + (const uint8_t *)"\x07", 1}; +const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS = { (const uint8_t[]){OPTIGA_ACCESS_COND_ALW}, 1}; -const optiga_metadata_item OPTIGA_ACCESS_NEVER = { +const optiga_metadata_item OPTIGA_META_ACCESS_NEVER = { (const uint8_t[]){OPTIGA_ACCESS_COND_NEV}, 1}; -const optiga_metadata_item OPTIGA_VERSION_DEFAULT = { +const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE = { + (const uint8_t[]){OPTIGA_KEY_USAGE_KEYAGREE}, 1}; +static const optiga_metadata_item OPTIGA_META_VERSION_DEFAULT = { (const uint8_t *)"\xC1\x02\x00\x00", 4}; static optiga_result process_output_fixedlen(uint8_t *data, size_t data_size) { @@ -113,13 +115,13 @@ static const struct { uint8_t tag; const optiga_metadata_item *default_value; } METADATA_OFFSET_TAG_MAP[] = { - {offsetof(optiga_metadata, lcso), 0xC0, &OPTIGA_LCS_OPERATIONAL}, - {offsetof(optiga_metadata, version), 0xC1, &OPTIGA_VERSION_DEFAULT}, + {offsetof(optiga_metadata, lcso), 0xC0, &OPTIGA_META_LCS_OPERATIONAL}, + {offsetof(optiga_metadata, version), 0xC1, &OPTIGA_META_VERSION_DEFAULT}, {offsetof(optiga_metadata, max_size), 0xC4, NULL}, {offsetof(optiga_metadata, used_size), 0xC5, NULL}, - {offsetof(optiga_metadata, change), 0xD0, &OPTIGA_ACCESS_NEVER}, - {offsetof(optiga_metadata, read), 0xD1, &OPTIGA_ACCESS_NEVER}, - {offsetof(optiga_metadata, execute), 0xD3, &OPTIGA_ACCESS_NEVER}, + {offsetof(optiga_metadata, change), 0xD0, &OPTIGA_META_ACCESS_NEVER}, + {offsetof(optiga_metadata, read), 0xD1, &OPTIGA_META_ACCESS_NEVER}, + {offsetof(optiga_metadata, execute), 0xD3, &OPTIGA_META_ACCESS_NEVER}, {offsetof(optiga_metadata, meta_update), 0xD8, NULL}, {offsetof(optiga_metadata, algorithm), 0xE0, NULL}, {offsetof(optiga_metadata, key_usage), 0xE1, NULL}, diff --git a/core/embed/trezorhal/optiga/optiga_transport.c b/core/embed/trezorhal/optiga/optiga_transport.c index 5a827dfe9c..e26076371f 100644 --- a/core/embed/trezorhal/optiga/optiga_transport.c +++ b/core/embed/trezorhal/optiga/optiga_transport.c @@ -139,9 +139,26 @@ static size_t sec_chan_size = 0; #else static optiga_log_hex_t log_hex = NULL; void optiga_set_log_hex(optiga_log_hex_t f) { log_hex = f; } -#define OPTIGA_LOG(prefix, data, data_size) \ - if (log_hex != NULL) { \ - log_hex(prefix, data, data_size); \ +#define OPTIGA_LOG(prefix, data, data_size) \ + if (log_hex != NULL) { \ + static uint8_t prev_data[4]; \ + static size_t prev_size = 0; \ + static bool repeated = false; \ + if (prev_size == data_size && memcmp(data, prev_data, data_size) == 0) { \ + if (!repeated) { \ + repeated = true; \ + log_hex(prefix "(REPEATED) ", data, data_size); \ + } \ + } else { \ + repeated = false; \ + if (data_size <= sizeof(prev_data)) { \ + memcpy(prev_data, data, data_size); \ + prev_size = data_size; \ + } else { \ + prev_size = 0; \ + } \ + log_hex(prefix, data, data_size); \ + } \ } #endif @@ -264,20 +281,23 @@ static optiga_result optiga_ensure_ready(void) { return ret; } - if ((frame_buffer[0] & I2C_STATE_BYTE1_BUSY) == 0) { + if ((frame_buffer[0] & I2C_STATE_BYTE1_RESP_RDY) != 0) { + // There is a response that needs to be flushed out. break; } + + if ((frame_buffer[0] & I2C_STATE_BYTE1_BUSY) == 0) { + // Not busy and no response that would need to be flushed out. + return OPTIGA_SUCCESS; + } ret = OPTIGA_ERR_BUSY; } if (ret != OPTIGA_SUCCESS) { + // Optiga is busy even after maximum retries at reading the I2C state. return ret; } - if ((frame_buffer[0] & I2C_STATE_BYTE1_RESP_RDY) == 0) { - return OPTIGA_SUCCESS; - } - // Flush out the previous response. uint16_t size = (frame_buffer[2] << 8) + frame_buffer[3]; @@ -387,6 +407,13 @@ static optiga_result optiga_read(void) { return OPTIGA_SUCCESS; } + + if ((frame_buffer[0] & I2C_STATE_BYTE1_BUSY) == 0) { + // Optiga has no response ready and is not busy. This shouldn't happen if + // we are expecting to read a response, but Optiga occasionally fails to + // give any response to a command. + return OPTIGA_ERR_UNEXPECTED; + } } return OPTIGA_ERR_TIMEOUT; diff --git a/core/embed/trezorhal/optiga_commands.h b/core/embed/trezorhal/optiga_commands.h index df4f8169aa..743f8c0a7d 100644 --- a/core/embed/trezorhal/optiga_commands.h +++ b/core/embed/trezorhal/optiga_commands.h @@ -30,9 +30,11 @@ typedef enum { OPTIGA_OID_COPROC_UID = 0xE0C2, // Coprocessor UID. OPTIGA_OID_CERT = 0xE0E0, // Public key certificates [1-4]. OPTIGA_OID_CA_CERT = 0xE0E8, // Root CA public key certificates [1-2]. - OPTIGA_OID_COUNTER = 0xE120, // Monotonic counters [1-4]. OPTIGA_OID_ECC_KEY = 0xE0F0, // Private ECC keys [1-4]. + OPTIGA_OID_SESSION_CTX = 0xE100, // Session contexts [1-4]. + OPTIGA_OID_COUNTER = 0xE120, // Monotonic counters [1-4]. OPTIGA_OID_PTFBIND_SECRET = 0xE140, // Shared platform binding secret. + OPTIGA_OID_SYM_KEY = 0xE200, // Device symmetric key. OPTIGA_OID_ERROR_CODE = 0xF1C2, // Command error code. OPTIGA_OID_DATA = 0xF1D0, // Arbitrary 140 B data objects [1-12]. OPTIGA_OID_BIG_DATA = 0xF1E0, // Arbitrary 1500 B data objects [1-2]. @@ -131,9 +133,11 @@ typedef struct { #define OPTIGA_ACCESS_CONDITION(ac_id, oid) \ { (const uint8_t[]){ac_id, oid >> 8, oid & 0xff}, 3 } -extern const optiga_metadata_item OPTIGA_LCS_OPERATIONAL; -extern const optiga_metadata_item OPTIGA_ACCESS_ALWAYS; -extern const optiga_metadata_item OPTIGA_ACCESS_NEVER; +// Commonly used data object access conditions. +extern const optiga_metadata_item OPTIGA_META_LCS_OPERATIONAL; +extern const optiga_metadata_item OPTIGA_META_ACCESS_ALWAYS; +extern const optiga_metadata_item OPTIGA_META_ACCESS_NEVER; +extern const optiga_metadata_item OPTIGA_META_KEY_USE_KEYAGREE; optiga_result optiga_parse_metadata(const uint8_t *serialized, size_t serialized_size, diff --git a/core/embed/trezorhal/unix/optiga.c b/core/embed/trezorhal/unix/optiga.c index 5e60c4d1d7..64dfbf3f8e 100644 --- a/core/embed/trezorhal/unix/optiga.c +++ b/core/embed/trezorhal/unix/optiga.c @@ -151,3 +151,19 @@ bool optiga_random_buffer(uint8_t *dest, size_t size) { random_buffer(dest, size); return true; } + +bool optiga_pin_set(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { + memcpy(out_secret, pin_secret, OPTIGA_PIN_SECRET_SIZE); + ui_progress(OPTIGA_PIN_DERIVE_MS); + return true; +} + +bool optiga_pin_verify(OPTIGA_UI_PROGRESS ui_progress, + const uint8_t pin_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t out_secret[OPTIGA_PIN_SECRET_SIZE]) { + memcpy(out_secret, pin_secret, OPTIGA_PIN_SECRET_SIZE); + ui_progress(OPTIGA_PIN_DERIVE_MS); + return true; +} diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index d7f1898866..ff2fa9dcc0 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -88,14 +88,28 @@ def bootloader_locked() -> bool | None: the feature is not supported. """ SCM_REVISION: bytes +"""Git commit hash of the firmware.""" VERSION_MAJOR: int +"""Major version.""" VERSION_MINOR: int +"""Minor version.""" VERSION_PATCH: int +"""Patch version.""" USE_SD_CARD: bool +"""Whether the hardware supports SD card.""" USE_BACKLIGHT: bool +"""Whether the hardware supports backlight brightness control.""" USE_OPTIGA: bool +"""Whether the hardware supports Optiga secure element.""" MODEL: str +"""Model name.""" +MODEL_FULL_NAME: str +"""Full name including Trezor prefix.""" INTERNAL_MODEL: str +"""Internal model code.""" EMULATOR: bool +"""Whether the firmware is running in the emulator.""" BITCOIN_ONLY: bool +"""Whether the firmware is Bitcoin-only.""" UI_LAYOUT: str +"""UI layout identifier ("tt" for model T, "tr" for models One and R).""" diff --git a/core/site_scons/boards/trezor_r_v10.py b/core/site_scons/boards/trezor_r_v10.py index 03f4a152f9..910241626b 100644 --- a/core/site_scons/boards/trezor_r_v10.py +++ b/core/site_scons/boards/trezor_r_v10.py @@ -71,6 +71,7 @@ def configure( sources += ["embed/trezorhal/optiga/optiga_commands.c"] sources += ["embed/trezorhal/optiga/optiga_transport.c"] sources += ["embed/trezorhal/stm32f4/secret.c"] + sources += ["vendor/trezor-crypto/hash_to_curve.c"] features_available.append("optiga") env.get("ENV")["TREZOR_BOARD"] = board diff --git a/core/src/apps/homescreen/__init__.py b/core/src/apps/homescreen/__init__.py index eacf22adc0..3aed7a9207 100644 --- a/core/src/apps/homescreen/__init__.py +++ b/core/src/apps/homescreen/__init__.py @@ -14,15 +14,10 @@ async def busyscreen() -> None: async def homescreen() -> None: - from trezor import utils - if storage.device.is_initialized(): label = storage.device.get_label() else: - if utils.INTERNAL_MODEL in ("T1B1", "T2T1"): - label = f"Trezor Model {utils.MODEL}" - else: - label = f"Trezor {utils.MODEL}" + label = None notification = None notification_is_error = False diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py index 6bf3fffea6..647362860d 100644 --- a/core/src/apps/management/apply_settings.py +++ b/core/src/apps/management/apply_settings.py @@ -153,15 +153,7 @@ async def apply_settings(msg: ApplySettings) -> Success: async def _require_confirm_change_homescreen(homescreen: bytes) -> None: from trezor.ui.layouts import confirm_homescreen - if homescreen == b"": - await confirm_action( - "set_homescreen", - "Set homescreen", - description="Do you really want to set default homescreen image?", - br_code=BRT_PROTECT_CALL, - ) - else: - await confirm_homescreen(homescreen) + await confirm_homescreen(homescreen) async def _require_confirm_change_label(label: str) -> None: diff --git a/core/src/apps/management/authenticate_device.py b/core/src/apps/management/authenticate_device.py index 32bac8ec78..084289258f 100644 --- a/core/src/apps/management/authenticate_device.py +++ b/core/src/apps/management/authenticate_device.py @@ -9,8 +9,10 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: from trezor.crypto import optiga from trezor.crypto.der import read_length from trezor.crypto.hashlib import sha256 + from trezor.loop import sleep from trezor.messages import AuthenticityProof from trezor.ui.layouts import confirm_action + from trezor.ui.layouts.progress import progress from trezor.utils import BufferReader, bootloader_locked from apps.common.writers import write_compact_size @@ -21,7 +23,8 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: await confirm_action( "authenticate_device", "Authenticate device", - description="Do you wish to verify the authenticity of your device?", + description=f"Allow connected computer to confirm your {utils.MODEL_FULL_NAME} is genuine?", + verb="Allow", ) header = b"AuthenticateDevice:" @@ -31,6 +34,9 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: write_compact_size(h, len(msg.challenge)) h.extend(msg.challenge) + spinner = progress("", description="Checking authenticity...") + spinner.report(0) + try: signature = optiga.sign(optiga.DEVICE_ECC_KEY_INDEX, h.get_digest()) except optiga.SigningInaccessible: @@ -47,6 +53,14 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: r.seek(cert_begin) certificates.append(r.read_memoryview(cert_len)) + if not utils.DISABLE_ANIMATION: + frame_delay = sleep(60) + for i in range(1, 20): + spinner.report(i * 50) + await frame_delay + + spinner.report(1000) + return AuthenticityProof( certificates=certificates, signature=signature, diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index acdfa987d3..0392edec8a 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -393,7 +393,7 @@ async def confirm_homescreen( interact( RustLayout( trezorui2.confirm_homescreen( - title="SET HOMESCREEN", + title="CHANGE HOMESCREEN", image=image, ) ), diff --git a/core/src/trezor/ui/layouts/tt/homescreen.py b/core/src/trezor/ui/layouts/tt/homescreen.py index 67c1e7f84c..eda7699b02 100644 --- a/core/src/trezor/ui/layouts/tt/homescreen.py +++ b/core/src/trezor/ui/layouts/tt/homescreen.py @@ -62,7 +62,7 @@ class Homescreen(HomescreenBase): skip = storage_cache.homescreen_shown is self.RENDER_INDICATOR super().__init__( layout=trezorui2.show_homescreen( - label=label or "My Trezor", + label=label, notification=notification, notification_level=level, hold=hold_to_lock, @@ -102,7 +102,7 @@ class Lockscreen(HomescreenBase): ) super().__init__( layout=trezorui2.show_lockscreen( - label=label or "My Trezor", + label=label, bootscreen=bootscreen, skip_first_paint=skip, ), diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index 4c9083feed..8b8a83f419 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -5,6 +5,7 @@ from trezorutils import ( # noqa: F401 EMULATOR, INTERNAL_MODEL, MODEL, + MODEL_FULL_NAME, SCM_REVISION, UI_LAYOUT, USE_BACKLIGHT, diff --git a/crypto/Makefile b/crypto/Makefile index 1d83619dee..627814136f 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -124,6 +124,7 @@ SRCS += zkp_ecdsa.c SRCS += zkp_bip340.c SRCS += cardano.c SRCS += tls_prf.c +SRCS += hash_to_curve.c OBJS = $(SRCS:.c=.o) OBJS += secp256k1-zkp.o diff --git a/crypto/bignum.c b/crypto/bignum.c index b23adf7a83..5c64c8fc21 100644 --- a/crypto/bignum.c +++ b/crypto/bignum.c @@ -80,6 +80,15 @@ #define BN_MAX_DECIMAL_DIGITS \ 79 // floor(log(2**(LIMBS * BITS_PER_LIMB), 10)) + 1 +// y = (bignum256) x +// Assumes x is normalized and x < 2**261 == 2**(BITS_PER_LIMB * LIMBS) +// Guarantees y is normalized +void bn_copy_lower(const bignum512 *x, bignum256 *y) { + for (int i = 0; i < BN_LIMBS; i++) { + y->val[i] = x->val[i]; + } +} + // out_number = (bignum256) in_number // Assumes in_number is a raw bigendian 256-bit number // Guarantees out_number is normalized @@ -98,6 +107,30 @@ void bn_read_be(const uint8_t *in_number, bignum256 *out_number) { out_number->val[BN_LIMBS - 1] = temp; } +// out_number = (bignum512) in_number +// Assumes in_number is a raw bigendian 512-bit number +// Guarantees out_number is normalized +void bn_read_be_512(const uint8_t *in_number, bignum512 *out_number) { + bignum256 lower = {0}, upper = {0}; + + bn_read_be(in_number + 32, &lower); + bn_read_be(in_number, &upper); + + const int shift_length = BN_BITS_PER_LIMB * BN_LIMBS - 256; + uint32_t shift = upper.val[0] & ((1 << shift_length) - 1); + for (int i = 0; i < shift_length; i++) { + bn_rshift(&upper); + } + lower.val[BN_LIMBS - 1] |= shift << (BN_BITS_PER_LIMB - shift_length); + + for (int i = 0; i < BN_LIMBS; i++) { + out_number->val[i] = lower.val[i]; + } + for (int i = 0; i < BN_LIMBS; i++) { + out_number->val[i + BN_LIMBS] = upper.val[i]; + } +} + // out_number = (256BE) in_number // Assumes in_number < 2**256 // Guarantess out_number is a raw bigendian 256-bit number @@ -525,8 +558,7 @@ void bn_mod(bignum256 *x, const bignum256 *prime) { // res = k * x // Assumes k and x are normalized // Guarantees res is normalized 18 digit little endian number in base 2**29 -void bn_multiply_long(const bignum256 *k, const bignum256 *x, - uint32_t res[2 * BN_LIMBS]) { +void bn_multiply_long(const bignum256 *k, const bignum256 *x, bignum512 *res) { // Uses long multiplication in base 2**29, see // https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication @@ -545,7 +577,7 @@ void bn_multiply_long(const bignum256 *k, const bignum256 *x, // <= 2**35 + 9 * 2**58 < 2**64 } - res[i] = acc & BN_LIMB_MASK; + res->val[i] = acc & BN_LIMB_MASK; acc >>= BN_BITS_PER_LIMB; // acc <= 2**35 - 1 == 2**(64 - BITS_PER_LIMB) - 1 } @@ -563,12 +595,12 @@ void bn_multiply_long(const bignum256 *k, const bignum256 *x, // <= 2**35 + 9 * 2**58 < 2**64 } - res[i] = acc & (BN_BASE - 1); + res->val[i] = acc & (BN_BASE - 1); acc >>= BN_BITS_PER_LIMB; // acc < 2**35 == 2**(64 - BITS_PER_LIMB) } - res[2 * BN_LIMBS - 1] = acc; + res->val[2 * BN_LIMBS - 1] = acc; } // Auxiliary function for bn_multiply @@ -576,7 +608,7 @@ void bn_multiply_long(const bignum256 *k, const bignum256 *x, // Assumes res is normalized and res < 2**(256 + 29*d + 31) // Guarantess res in normalized and res < 2 * prime * 2**(29*d) // Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 -void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, +void bn_multiply_reduce_step(bignum512 *res, const bignum256 *prime, uint32_t d) { // clang-format off // Computes res = res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) @@ -598,8 +630,9 @@ void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, // clang-format on uint32_t coef = - (res[d + BN_LIMBS - 1] >> (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB)) + - (res[d + BN_LIMBS] << ((BN_LIMBS * BN_BITS_PER_LIMB) - 256)); + (res->val[d + BN_LIMBS - 1] >> + (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB)) + + (res->val[d + BN_LIMBS] << ((BN_LIMBS * BN_BITS_PER_LIMB) - 256)); // coef == res // 2**(256 + BITS_PER_LIMB * d) @@ -613,7 +646,7 @@ void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, uint64_t acc = 1ull << shift; for (int i = 0; i < BN_LIMBS; i++) { - acc += (((uint64_t)(BN_BASE - 1)) << shift) + res[d + i] - + acc += (((uint64_t)(BN_BASE - 1)) << shift) + res->val[d + i] - prime->val[i] * (uint64_t)coef; // acc neither overflow 64 bits nor underflow zero // Proof: @@ -633,7 +666,7 @@ void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, // == (2**35 - 1) + (2**31 + 1) * (2**29 - 1) // <= 2**35 + 2**60 + 2**29 < 2**64 - res[d + i] = acc & BN_LIMB_MASK; + res->val[d + i] = acc & BN_LIMB_MASK; acc >>= BN_BITS_PER_LIMB; // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 @@ -664,16 +697,14 @@ void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, // == 1 << shift // clang-format on - res[d + BN_LIMBS] = 0; + res->val[d + BN_LIMBS] = 0; } -// Auxiliary function for bn_multiply -// Partly reduces res and stores both in x and res -// Assumes res in normalized and res < 2**519 +// Partly reduces x +// Assumes x in normalized and res < 2**519 // Guarantees x is normalized and partly reduced modulo prime // Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 -void bn_multiply_reduce(bignum256 *x, uint32_t res[2 * BN_LIMBS], - const bignum256 *prime) { +void bn_reduce(bignum512 *x, const bignum256 *prime) { for (int i = BN_LIMBS - 1; i >= 0; i--) { // res < 2**(256 + 29*i + 31) // Proof: @@ -684,11 +715,7 @@ void bn_multiply_reduce(bignum256 *x, uint32_t res[2 * BN_LIMBS], // else: // res < 2 * prime * 2**(29 * (i + 1)) // <= 2**256 * 2**(29*i + 29) < 2**(256 + 29*i + 31) - bn_multiply_reduce_step(res, prime, i); - } - - for (int i = 0; i < BN_LIMBS; i++) { - x->val[i] = res[i]; + bn_multiply_reduce_step(x, prime, i); } } @@ -697,12 +724,13 @@ void bn_multiply_reduce(bignum256 *x, uint32_t res[2 * BN_LIMBS], // Guarantees x is normalized and partly reduced modulo prime // Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime) { - uint32_t res[2 * BN_LIMBS] = {0}; + bignum512 res = {0}; - bn_multiply_long(k, x, res); - bn_multiply_reduce(x, res, prime); + bn_multiply_long(k, x, &res); + bn_reduce(&res, prime); + bn_copy_lower(&res, x); - memzero(res, sizeof(res)); + memzero(&res, sizeof(res)); } // Partly reduces x modulo prime @@ -858,7 +886,7 @@ void bn_sqrt(bignum256 *x, const bignum256 *prime) { // http://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus // If prime % 4 == 3, then sqrt(x) % prime == x**((prime+1)//4) % prime - assert(prime->val[BN_LIMBS - 1] % 4 == 3); + assert(prime->val[0] % 4 == 3); // e = (prime + 1) // 4 bignum256 e = {0}; @@ -1591,6 +1619,39 @@ void bn_subtract(const bignum256 *x, const bignum256 *y, bignum256 *res) { // == 1 } +// Returns 0 if x is zero +// Returns 1 if x is a square modulo prime +// Returns -1 if x is not a square modulo prime +// Assumes x is normalized, x < 2**259 +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// Assumes prime is a prime +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +int bn_legendre(const bignum256 *x, const bignum256 *prime) { + // This is a naive implementation + // A better implementation would be to use the Euclidean algorithm together with the quadratic reciprocity law + + // e = (prime - 1) / 2 + bignum256 e = {0}; + bn_copy(prime, &e); + bn_rshift(&e); + + // res = x**e % prime + bignum256 res = {0}; + bn_power_mod(x, &e, prime, &res); + bn_mod(&res, prime); + + if (bn_is_one(&res)) { + return 1; + } + + if (bn_is_zero(&res)) { + return 0; + } + + return -1; +} + // q = x // d, r = x % d // Assumes x is normalized, 1 <= d <= 61304 // Guarantees q is normalized diff --git a/crypto/bignum.h b/crypto/bignum.h index f9213fbe76..98a0b3c6c9 100644 --- a/crypto/bignum.h +++ b/crypto/bignum.h @@ -43,6 +43,11 @@ typedef struct { uint32_t val[BN_LIMBS]; } bignum256; +// Represents the number sum([val[i] * 2**(29*i) for i in range(18)) +typedef struct { + uint32_t val[2 * BN_LIMBS]; +} bignum512; + static inline uint32_t read_be(const uint8_t *data) { return (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) | (((uint32_t)data[2]) << 8) | (((uint32_t)data[3])); @@ -67,7 +72,9 @@ static inline void write_le(uint8_t *data, uint32_t x) { data[0] = x; } +void bn_copy_lower(const bignum512 *x, bignum256 *y); void bn_read_be(const uint8_t *in_number, bignum256 *out_number); +void bn_read_be_512(const uint8_t *in_number, bignum512 *out_number); void bn_write_be(const bignum256 *in_number, uint8_t *out_number); void bn_read_le(const uint8_t *in_number, bignum256 *out_number); void bn_write_le(const bignum256 *in_number, uint8_t *out_number); @@ -94,6 +101,7 @@ void bn_mult_half(bignum256 *x, const bignum256 *prime); void bn_mult_k(bignum256 *x, uint8_t k, const bignum256 *prime); void bn_mod(bignum256 *x, const bignum256 *prime); void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime); +void bn_reduce(bignum512 *x, const bignum256 *prime); void bn_fast_mod(bignum256 *x, const bignum256 *prime); void bn_power_mod(const bignum256 *x, const bignum256 *e, const bignum256 *prime, bignum256 *res); @@ -108,6 +116,7 @@ void bn_subi(bignum256 *x, uint32_t y, const bignum256 *prime); void bn_subtractmod(const bignum256 *x, const bignum256 *y, bignum256 *res, const bignum256 *prime); void bn_subtract(const bignum256 *x, const bignum256 *y, bignum256 *res); +int bn_legendre(const bignum256 *x, const bignum256 *prime); void bn_long_division(bignum256 *x, uint32_t d, bignum256 *q, uint32_t *r); void bn_divmod58(bignum256 *x, uint32_t *r); void bn_divmod1000(bignum256 *x, uint32_t *r); diff --git a/crypto/hash_to_curve.c b/crypto/hash_to_curve.c new file mode 100644 index 0000000000..e0ca547fc4 --- /dev/null +++ b/crypto/hash_to_curve.c @@ -0,0 +1,413 @@ +/** + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include + +#include "bignum.h" +#include "ecdsa.h" +#include "memzero.h" +#include "nist256p1.h" +#include "sha2.h" + +#include "hash_to_curve.h" + +// https://www.rfc-editor.org/rfc/rfc9380.html#name-hash_to_field-implementatio +static bool hash_to_field(const uint8_t *msg, size_t msg_len, + const uint8_t *dst, // domain separation tag + const size_t dst_len, size_t expansion_len, + const bignum256 *prime, + bool expand(const uint8_t *, size_t, const uint8_t *, + size_t, uint8_t *, size_t), + bignum256 *out, size_t out_len) { + const size_t max_expansion_len = 64; + if (expansion_len > max_expansion_len) { + // Not supported by this implementation + return false; + } + + const size_t expanded_msg_length = out_len * expansion_len; + uint8_t expanded_msg[expanded_msg_length]; + memzero(expanded_msg, sizeof(expanded_msg)); + + if (!expand(msg, msg_len, dst, dst_len, expanded_msg, expanded_msg_length)) { + return false; + } + + uint8_t raw_number[max_expansion_len]; + memzero(raw_number, sizeof(raw_number)); + bignum512 bn_number = {0}; + + for (size_t i = 0; i < out_len; i++) { + memcpy(raw_number + (max_expansion_len - expansion_len), + expanded_msg + i * expansion_len, expansion_len); + + bn_read_be_512(raw_number, &bn_number); + bn_reduce(&bn_number, prime); + bn_copy_lower(&bn_number, &out[i]); + bn_mod(&out[i], prime); + } + + memzero(expanded_msg, sizeof(expanded_msg)); + memzero(raw_number, sizeof(raw_number)); + memzero(&bn_number, sizeof(bn_number)); + + return true; +} + +// Simplified Shallue-van de Woestijne-Ulas Method +// https://www.rfc-editor.org/rfc/rfc9380.html#name-simplified-shallue-van-de-w +// Algorithm assumptions: +// * z is a non-square modulo p +// * z != -1 modulo p +// * x^2 + a * x + b - z is an irreducible polynomial modulo p +// * (b/(z*a))^2 + a * (b/(z*a)) + b is a square modulo p +// * z is not zero +// * a is not zero +// * b is not zero +// * p is at least 6 +// Implementation assumptions: +// * p is a prime +// * 2**256 - 2**224 <= prime <= 2**256 +// * p % 4 == 3 +static bool simple_swu(const bignum256 *u, const bignum256 *a, + const bignum256 *b, const bignum256 *p, + const bignum256 *z, int sign_function(const bignum256 *), + curve_point *point) { + if (bn_is_zero(a) || bn_is_zero(b) || (p->val[0] % 4 != 3)) { + return false; + } + + // c1 = -b / a + bignum256 c1 = {0}; + bn_copy(a, &c1); + bn_subtract(p, &c1, &c1); + bn_inverse(&c1, p); + bn_multiply(b, &c1, p); + bn_mod(&c1, p); + + // c2 = -1 / z + bignum256 c2 = {0}; + bn_copy(z, &c2); + bn_subtract(p, &c2, &c2); + bn_inverse(&c2, p); + bn_mod(&c2, p); + + // t1 = z * u^2 + bignum256 t1 = {0}; + bn_copy(u, &t1); + bn_multiply(&t1, &t1, p); + bn_mod(&t1, p); + bn_multiply(z, &t1, p); + bn_mod(&t1, p); + + // t2 = t1^2 + bignum256 t2 = {0}; + bn_copy(&t1, &t2); + bn_multiply(&t2, &t2, p); + bn_mod(&t2, p); + + // x1 = t1 + t2 + bignum256 x1 = {0}; + bn_copy(&t1, &x1); + bn_add(&x1, &t2); + bn_mod(&x1, p); + + // x1 = inv0(1) + bn_inverse(&x1, p); + + // e1 = x1 == 0 + int e1 = bn_is_zero(&x1); + + // x1 = x1 + 1 + bn_addi(&x1, 1); + bn_mod(&x1, p); + + // x1 = CMOV(x1, c2, e1) + bn_cmov(&x1, e1, &c2, &x1); + memzero(&c2, sizeof(c2)); + + // x1 = x1 * c1 + bn_multiply(&c1, &x1, p); + memzero(&c1, sizeof(c1)); + bn_mod(&x1, p); + + // gx1 = x1^2 + bignum256 gx1 = {0}; + bn_copy(&x1, &gx1); + bn_multiply(&x1, &gx1, p); + bn_mod(&gx1, p); + + // gx1 = gx1 + A + bn_add(&gx1, a); + bn_mod(&gx1, p); + + // gx1 = gx1 * x1 + bn_multiply(&x1, &gx1, p); + bn_mod(&gx1, p); + + // gx1 = gx1 + B + bn_add(&gx1, b); + bn_mod(&gx1, p); + + // x2 = t1 * x1 + bignum256 x2 = {0}; + bn_copy(&t1, &x2); + bn_multiply(&x1, &x2, p); + bn_mod(&x2, p); + + // t2 = t1 * t2 + bn_multiply(&t1, &t2, p); + memzero(&t1, sizeof(t1)); + bn_mod(&t2, p); + + // gx2 = gx1 * t2 + bignum256 gx2 = {0}; + bn_copy(&gx1, &gx2); + bn_multiply(&t2, &gx2, p); + memzero(&t2, sizeof(t2)); + bn_mod(&gx2, p); + + // e2 = is_square(gx1) + int e2 = bn_legendre(&gx1, p) >= 0; + + // x = CMOV(x2, x1, e2) + bignum256 x = {0}; + bn_cmov(&x, e2, &x1, &x2); + memzero(&x1, sizeof(x1)); + memzero(&x2, sizeof(x2)); + + // y2 = CMOV(gx2, gx1, e2) + bignum256 y2 = {0}; + bn_cmov(&y2, e2, &gx1, &gx2); + memzero(&gx1, sizeof(gx1)); + memzero(&gx2, sizeof(gx2)); + + // y = sqrt(y2) + bignum256 y = {0}; + bn_copy(&y2, &y); + memzero(&y2, sizeof(y2)); + bn_sqrt(&y, p); // This is the slowest operation + + // e3 = sgn0(u) == sgn0(y) + int e3 = sign_function(u) == sign_function(&y); + + bignum256 minus_y = {0}; + bn_subtract(p, &y, &minus_y); + + // y = CMOV(-y, y, e3) + bn_cmov(&y, e3, &y, &minus_y); + memzero(&minus_y, sizeof(minus_y)); + + bn_copy(&x, &point->x); + bn_copy(&y, &point->y); + memzero(&x, sizeof(x)); + memzero(&y, sizeof(y)); + + return true; +} + +static void bn_read_int32(int32_t in_number, const bignum256 *prime, + bignum256 *out_number) { + if (in_number < 0) { + bn_read_uint32(-in_number, out_number); + bn_subtract(prime, out_number, out_number); + } else { + bn_read_uint32(in_number, out_number); + } +} + +// https://www.rfc-editor.org/rfc/rfc9380.html#name-encoding-byte-strings-to-el +static bool hash_to_curve(const uint8_t *msg, size_t msg_len, + const ecdsa_curve *curve, const uint8_t *suite_id, + const uint8_t suite_id_len, int z, int cofactor, + bool expand_function(const uint8_t *, size_t, + const uint8_t *, size_t, + uint8_t *, size_t), + int sign_function(const bignum256 *), + curve_point *point) { + if (cofactor != 1) { + // Not supported by this implementation + return false; + } + + bignum256 bn_z = {0}; + bn_read_int32(z, &curve->prime, &bn_z); + + bignum256 bn_a = {0}; + bn_read_int32(curve->a, &curve->prime, &bn_a); + + bignum256 u[2] = {0}; + + if (!hash_to_field(msg, msg_len, suite_id, suite_id_len, 48, &curve->prime, + expand_function, u, 2)) { + return false; + } + + curve_point point1 = {0}, point2 = {0}; + + if (!simple_swu(&u[0], &bn_a, &curve->b, &curve->prime, &bn_z, sign_function, + &point1)) { + memzero(&u[0], sizeof(u[0])); + return false; + } + memzero(&u[0], sizeof(u[0])); + + if (!simple_swu(&u[1], &bn_a, &curve->b, &curve->prime, &bn_z, sign_function, + &point2)) { + memzero(&u[1], sizeof(u[1])); + return false; + } + memzero(&u[1], sizeof(u[1])); + + point_add(curve, &point1, &point2); + + point->x = point2.x; + point->y = point2.y; + + memzero(&point1, sizeof(point1)); + memzero(&point2, sizeof(point2)); + + return true; +} + +static int sgn0(const bignum256 *a) { + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-05#section-4.1.2 + if (bn_is_even(a)) { + return 1; + } + + return -1; +} + +// https://www.rfc-editor.org/rfc/rfc9380.html#hashtofield-expand-xmd +bool expand_message_xmd_sha256(const uint8_t *msg, size_t msg_len, + const uint8_t *dst, // domain separation tag + size_t dst_len, uint8_t *output, + size_t output_len) { + if (dst_len > 255) { + return false; + } + + if ((output_len > 65535) || (output_len > 255 * SHA256_DIGEST_LENGTH)) { + return false; + } + + const uint8_t zero_block[SHA256_BLOCK_LENGTH] = {0}; + const uint8_t output_len_bytes[2] = {(output_len >> 8) & 255, + output_len & 255}; + const uint8_t dst_len_bytes[1] = {dst_len & 255}; + const uint8_t zero[1] = {0}; + + SHA256_CTX ctx = {0}; + sha256_Init(&ctx); + + // Z_pad = I2OSP(0, s_in_bytes) + sha256_Update(&ctx, zero_block, sizeof(zero_block)); + + // msg + sha256_Update(&ctx, msg, msg_len); + + // l_i_b_str = I2OSP(len_in_bytes, 2) + sha256_Update(&ctx, output_len_bytes, sizeof(output_len_bytes)); + + // I2OSP(0, 1) + sha256_Update(&ctx, zero, sizeof(zero)); + + // DST_prime = DST || I2OSP(len(DST), 1) + sha256_Update(&ctx, dst, dst_len); + sha256_Update(&ctx, dst_len_bytes, sizeof(dst_len_bytes)); + + uint8_t first_digest[SHA256_DIGEST_LENGTH] = {0}; // b_0 + sha256_Final(&ctx, first_digest); + + uint8_t current_digest[SHA256_DIGEST_LENGTH] = {0}; // b_i + + size_t output_position = 0; + size_t remaining_output_length = output_len; + int i = 1; + + while (remaining_output_length > 0) { + const uint8_t i_bytes[1] = {i & 255}; + + // strxor(b_0, b_(i - 1)) + for (size_t j = 0; j < sizeof(current_digest); j++) { + current_digest[j] ^= first_digest[j]; + } + + sha256_Init(&ctx); + + // strxor(b_0, b_(i - 1)) + sha256_Update(&ctx, current_digest, sizeof(current_digest)); + + // I2OSP(i, 1) + sha256_Update(&ctx, i_bytes, sizeof(i_bytes)); + + // DST_prime = DST || I2OSP(len(DST), 1) + sha256_Update(&ctx, dst, dst_len); + sha256_Update(&ctx, dst_len_bytes, sizeof(dst_len_bytes)); + + sha256_Final(&ctx, current_digest); + + const size_t copy_length = remaining_output_length > SHA256_DIGEST_LENGTH + ? SHA256_DIGEST_LENGTH + : remaining_output_length; + memcpy(output + output_position, current_digest, copy_length); + + output_position += copy_length; + remaining_output_length -= copy_length; + i++; + } + + memzero(&ctx, sizeof(ctx)); + memzero(first_digest, sizeof(first_digest)); + memzero(current_digest, sizeof(current_digest)); + + return true; +} + +bool hash_to_curve_p256(const uint8_t *msg, size_t msg_len, const uint8_t *dst, + size_t dst_len, curve_point *point) { + // https://www.rfc-editor.org/rfc/rfc9380.html#suites-p256 + // P256_XMD:SHA-256_SSWU_RO_ + if (!hash_to_curve(msg, msg_len, &nist256p1, dst, dst_len, -10, 1, + expand_message_xmd_sha256, sgn0, point)) { + return false; + } + + return true; +} + +bool hash_to_curve_optiga(const uint8_t input[32], uint8_t public_key[65]) { + char dst[] = "OPTIGA-SECRET-V0-P256_XMD:SHA-256_SSWU_RO_"; + curve_point point = {0}; + + if (!hash_to_curve_p256(input, 32, (uint8_t *)dst, sizeof(dst) - 1, &point)) { + return false; + } + + public_key[0] = 0x04; + bn_write_be(&point.x, public_key + 1); + bn_write_be(&point.y, public_key + 33); + + memzero(&point, sizeof(point)); + + return true; +} diff --git a/crypto/hash_to_curve.h b/crypto/hash_to_curve.h new file mode 100644 index 0000000000..aee5d7ef86 --- /dev/null +++ b/crypto/hash_to_curve.h @@ -0,0 +1,32 @@ +/** + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HASH_TO_CURVE_H__ +#define __HASH_TO_CURVE_H__ + +#include "ecdsa.h" + +bool expand_message_xmd_sha256(const uint8_t *msg, size_t msg_len, + const uint8_t *dst, size_t dst_len, + uint8_t *output, size_t output_len); +bool hash_to_curve_p256(const uint8_t *msg, size_t msg_len, const uint8_t *dst, + size_t dst_len, curve_point *point); +bool hash_to_curve_optiga(const uint8_t input[32], uint8_t public_key[65]); +#endif diff --git a/crypto/rand.c b/crypto/rand.c index 201fcf405c..fe750397a1 100644 --- a/crypto/rand.c +++ b/crypto/rand.c @@ -64,6 +64,16 @@ void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { } } +void random_xor(uint8_t *buf, size_t len) { + uint8_t r[4] = {0}; + for (size_t i = 0; i < len; i++) { + if (i % sizeof(r) == 0) { + random_buffer(r, sizeof(r)); + } + buf[i] ^= r[i % sizeof(r)]; + } +} + uint32_t random_uniform(uint32_t n) { uint32_t x = 0, max = 0xFFFFFFFF - (0xFFFFFFFF % n); while ((x = random32()) >= max) diff --git a/crypto/rand.h b/crypto/rand.h index 49d9cfaf21..5dac98f61c 100644 --- a/crypto/rand.h +++ b/crypto/rand.h @@ -30,6 +30,7 @@ void random_reseed(const uint32_t value); uint32_t random32(void); void random_buffer(uint8_t *buf, size_t len); +void random_xor(uint8_t *buf, size_t len); uint32_t random_uniform(uint32_t n); void random_permute(char *buf, size_t len); diff --git a/crypto/tests/test_bignum.py b/crypto/tests/test_bignum.py index 99ad8c86ef..ea6b4eac82 100755 --- a/crypto/tests/test_bignum.py +++ b/crypto/tests/test_bignum.py @@ -24,10 +24,13 @@ lib = ctypes.cdll.LoadLibrary(os.path.join(dir, "libtrezor-crypto.so")) limbs_number = 9 bits_per_limb = 29 +secp256k1_prime = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +p256_prime = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF -@pytest.fixture() + +@pytest.fixture(params=[secp256k1_prime, p256_prime]) def prime(request): - return 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + return request.param @pytest.fixture(params=range(limbs_number * bits_per_limb)) @@ -66,15 +69,23 @@ def uint32_p(): limb_type = c_uint32 -def bignum(limbs_number=limbs_number): +def bignum(limbs_number): return (limbs_number * limb_type)() +def bignum256(): + return bignum(limbs_number) + + +def bignum512(): + return bignum(2 * limbs_number) + + def limbs_to_bignum(limbs): return (limbs_number * limb_type)(*limbs) -def int_to_bignum(number, limbs_number=limbs_number): +def int_to_bignum(number, limbs_number): assert number >= 0 assert number.bit_length() <= limbs_number * bits_per_limb @@ -86,7 +97,15 @@ def int_to_bignum(number, limbs_number=limbs_number): return bn -def bignum_to_int(bignum, limbs_number=limbs_number): +def int_to_bignum256(number): + return int_to_bignum(number, limbs_number) + + +def int_to_bignum512(number): + return int_to_bignum(number, 2 * limbs_number) + + +def bignum_to_int(bignum, limbs_number): number = 0 for i in reversed(range(limbs_number)): @@ -96,16 +115,40 @@ def bignum_to_int(bignum, limbs_number=limbs_number): return number -def raw_number(): - return (32 * c_uint8)() +def bignum256_to_int(bignum): + return bignum_to_int(bignum, limbs_number) + + +def bignum512_to_int(bignum): + return bignum_to_int(bignum, 2 * limbs_number) + + +def raw_number(byte_size): + return (byte_size * c_uint8)() + + +def raw_number256(): + return raw_number(32) + + +def raw_number512(): + return raw_number(64) def raw_number_to_integer(raw_number, endianess): return int.from_bytes(raw_number, endianess) -def integer_to_raw_number(number, endianess): - return (32 * c_uint8)(*number.to_bytes(32, endianess)) +def integer_to_raw_number(number, endianess, byte_size): + return (byte_size * c_uint8)(*number.to_bytes(byte_size, endianess)) + + +def integer_to_raw_number256(number, endianess): + return integer_to_raw_number(number, endianess, 32) + + +def integer_to_raw_number512(number, endianess): + return integer_to_raw_number(number, endianess, 64) def bignum_is_normalised(bignum): @@ -130,6 +173,9 @@ class Random(random.Random): def rand_int_256(self): return self.randrange(0, 2**256) + def rand_int_512(self): + return self.randrange(0, 2**512) + def rand_int_reduced(self, p): return self.randrange(0, 2 * p) @@ -139,35 +185,61 @@ class Random(random.Random): def rand_bit_index(self): return self.randrange(0, limbs_number * bits_per_limb) - def rand_bignum(self, limbs_number=limbs_number): + def rand_bignum(self, limbs_number): return (limb_type * limbs_number)( *[self.randrange(0, 256**4) for _ in range(limbs_number)] ) + def rand_bignum256(self): + return self.rand_bignum(limbs_number) + + def rand_bignum512(self): + return self.rand_bignum(2 * limbs_number) + + +def assert_bn_copy_lower(x): + x_number = int_to_bignum512(x) + y_number = bignum256() + lib.bn_copy_lower(x_number, y_number) + y = bignum256_to_int(y_number) + + assert bignum_is_normalised(y_number) + assert y == x + def assert_bn_read_be(in_number): - raw_in_number = integer_to_raw_number(in_number, "big") - bn_out_number = bignum() + raw_in_number = integer_to_raw_number256(in_number, "big") + bn_out_number = bignum256() lib.bn_read_be(raw_in_number, bn_out_number) - out_number = bignum_to_int(bn_out_number) + out_number = bignum256_to_int(bn_out_number) + + assert bignum_is_normalised(bn_out_number) + assert out_number == in_number + + +def assert_bn_read_be_512(in_number): + raw_in_number = integer_to_raw_number512(in_number, "big") + bn_out_number = bignum512() + lib.bn_read_be_512(raw_in_number, bn_out_number) + out_number = bignum512_to_int(bn_out_number) assert bignum_is_normalised(bn_out_number) assert out_number == in_number def assert_bn_read_le(in_number): - raw_in_number = integer_to_raw_number(in_number, "little") - bn_out_number = bignum() + raw_in_number = integer_to_raw_number256(in_number, "little") + bn_out_number = bignum256() lib.bn_read_le(raw_in_number, bn_out_number) - out_number = bignum_to_int(bn_out_number) + out_number = bignum256_to_int(bn_out_number) assert bignum_is_normalised(bn_out_number) assert out_number == in_number def assert_bn_write_be(in_number): - bn_in_number = int_to_bignum(in_number) - raw_out_number = raw_number() + bn_in_number = int_to_bignum256(in_number) + raw_out_number = raw_number256() lib.bn_write_be(bn_in_number, raw_out_number) out_number = raw_number_to_integer(raw_out_number, "big") @@ -175,8 +247,8 @@ def assert_bn_write_be(in_number): def assert_bn_write_le(in_number): - bn_in_number = int_to_bignum(in_number) - raw_out_number = raw_number() + bn_in_number = int_to_bignum256(in_number) + raw_out_number = raw_number256() lib.bn_write_le(bn_in_number, raw_out_number) out_number = raw_number_to_integer(raw_out_number, "little") @@ -184,100 +256,100 @@ def assert_bn_write_le(in_number): def assert_bn_read_uint32(x): - bn_out_number = bignum() + bn_out_number = bignum256() lib.bn_read_uint32(c_uint32(x), bn_out_number) - out_number = bignum_to_int(bn_out_number) + out_number = bignum256_to_int(bn_out_number) assert bignum_is_normalised(bn_out_number) assert out_number == x def assert_bn_read_uint64(x): - bn_out_number = bignum() + bn_out_number = bignum256() lib.bn_read_uint64(c_uint64(x), bn_out_number) - out_number = bignum_to_int(bn_out_number) + out_number = bignum256_to_int(bn_out_number) assert bignum_is_normalised(bn_out_number) assert out_number == x def assert_bn_bitcount(x): - bn_x = int_to_bignum(x) + bn_x = int_to_bignum256(x) return_value = lib.bn_bitcount(bn_x) assert return_value == x.bit_length() def assert_bn_digitcount(x): - bn_x = int_to_bignum(x) + bn_x = int_to_bignum256(x) return_value = lib.bn_digitcount(bn_x) assert return_value == len(str(x)) def assert_bn_zero(): - bn_x = bignum() + bn_x = bignum256() lib.bn_zero(bn_x) - x = bignum_to_int(bn_x) + x = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x == 0 def assert_bn_one(): - bn_x = bignum() + bn_x = bignum256() lib.bn_one(bn_x) - x = bignum_to_int(bn_x) + x = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x == 1 def assert_bn_is_zero(x): - bn_x = int_to_bignum(x) + bn_x = int_to_bignum256(x) return_value = lib.bn_is_zero(bn_x) assert return_value == (x == 0) def assert_bn_is_one(x): - bn_x = int_to_bignum(x) + bn_x = int_to_bignum256(x) return_value = lib.bn_is_one(bn_x) assert return_value == (x == 1) def assert_bn_is_less(x, y): - bn_x = int_to_bignum(x) - bn_y = int_to_bignum(y) + bn_x = int_to_bignum256(x) + bn_y = int_to_bignum256(y) return_value = lib.bn_is_less(bn_x, bn_y) assert return_value == (x < y) def assert_bn_is_equal(x, y): - bn_x = int_to_bignum(x) - bn_y = int_to_bignum(y) + bn_x = int_to_bignum256(x) + bn_y = int_to_bignum256(y) return_value = lib.bn_is_equal(bn_x, bn_y) assert return_value == (x == y) def assert_bn_cmov(cond, truecase, falsecase): - bn_res = bignum() - bn_truecase = int_to_bignum(truecase) - bn_falsecase = int_to_bignum(falsecase) + bn_res = bignum256() + bn_truecase = int_to_bignum256(truecase) + bn_falsecase = int_to_bignum256(falsecase) lib.bn_cmov(bn_res, c_uint32(cond), bn_truecase, bn_falsecase) - res = bignum_to_int(bn_res) + res = bignum256_to_int(bn_res) assert res == truecase if cond else falsecase def assert_bn_cnegate(cond, x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_cnegate(c_uint32(cond), bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) @@ -285,63 +357,63 @@ def assert_bn_cnegate(cond, x_old, prime): def assert_bn_lshift(x_old): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) lib.bn_lshift(bn_x) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x_new == (x_old << 1) def assert_bn_rshift(x_old): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) lib.bn_rshift(bn_x) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x_new == (x_old >> 1) def assert_bn_setbit(x_old, i): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) lib.bn_setbit(bn_x, c_uint16(i)) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x_new == x_old | (1 << i) def assert_bn_clearbit(x_old, i): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) lib.bn_clearbit(bn_x, c_uint16(i)) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x_new == x_old & ~(1 << i) def assert_bn_testbit(x_old, i): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) return_value = lib.bn_testbit(bn_x, c_uint16(i)) assert return_value == x_old >> i & 1 def assert_bn_xor(x, y): - bn_res = bignum() - bn_x = int_to_bignum(x) - bn_y = int_to_bignum(y) + bn_res = bignum256() + bn_x = int_to_bignum256(x) + bn_y = int_to_bignum256(y) lib.bn_xor(bn_res, bn_x, bn_y) - res = bignum_to_int(bn_res) + res = bignum256_to_int(bn_res) assert res == x ^ y def assert_bn_mult_half(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_mult_half(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert implication( number_is_partly_reduced(x_old, prime), number_is_partly_reduced(x_new, prime) @@ -350,10 +422,10 @@ def assert_bn_mult_half(x_old, prime): def assert_bn_mult_k(x_old, k, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_mult_k(bn_x, c_uint8(k), bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) @@ -361,10 +433,10 @@ def assert_bn_mult_k(x_old, k, prime): def assert_bn_mod(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_mod(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_fully_reduced(x_new, prime) @@ -372,42 +444,53 @@ def assert_bn_mod(x_old, prime): def assert_bn_multiply_long(k_old, x_old): - bn_k = int_to_bignum(k_old) - bn_x = int_to_bignum(x_old) - bn_res = bignum(2 * limbs_number) + bn_k = int_to_bignum256(k_old) + bn_x = int_to_bignum256(x_old) + bn_res = bignum512() lib.bn_multiply_long(bn_k, bn_x, bn_res) - res = bignum_to_int(bn_res, 2 * limbs_number) + res = bignum512_to_int(bn_res) assert res == k_old * x_old def assert_bn_multiply_reduce_step(res_old, prime, d): - bn_res = int_to_bignum(res_old, 2 * limbs_number) - bn_prime = int_to_bignum(prime) + bn_res = int_to_bignum512(res_old) + bn_prime = int_to_bignum256(prime) lib.bn_multiply_reduce_step(bn_res, bn_prime, d) - res_new = bignum_to_int(bn_res, 2 * limbs_number) + res_new = bignum512_to_int(bn_res) assert bignum_is_normalised(bn_res) assert res_new < 2 * prime * 2 ** (d * bits_per_limb) def assert_bn_multiply(k, x_old, prime): - bn_k = int_to_bignum(k) - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_k = int_to_bignum256(k) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_multiply(bn_k, bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) - assert x_new == (k * x_old) % prime + assert x_new % prime == (k * x_old) % prime + + +def assert_bn_reduce(x_old, prime): + bn_x = int_to_bignum512(x_old) + bn_prime = int_to_bignum256(prime) + lib.bn_reduce(bn_x, bn_prime) + x_new = bignum256_to_int(bn_x) + + assert bignum_is_normalised(bn_x) + assert number_is_partly_reduced(x_new, prime) + assert x_new % prime == x_old % prime def assert_bn_fast_mod(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_fast_mod(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) @@ -416,10 +499,10 @@ def assert_bn_fast_mod(x_old, prime): def assert_bn_fast_mod_bn(bn_x, prime): bn_x - x_old = bignum_to_int(bn_x) - bn_prime = int_to_bignum(prime) + x_old = bignum256_to_int(bn_x) + bn_prime = int_to_bignum256(prime) lib.bn_fast_mod(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) @@ -427,12 +510,12 @@ def assert_bn_fast_mod_bn(bn_x, prime): def assert_bn_power_mod(x, e, prime): - bn_x = int_to_bignum(x) - bn_e = int_to_bignum(e) - bn_prime = int_to_bignum(prime) - bn_res_new = bignum() + bn_x = int_to_bignum256(x) + bn_e = int_to_bignum256(e) + bn_prime = int_to_bignum256(prime) + bn_res_new = bignum256() lib.bn_power_mod(bn_x, bn_e, bn_prime, bn_res_new) - res_new = bignum_to_int(bn_res_new) + res_new = bignum256_to_int(bn_res_new) assert bignum_is_normalised(bn_res_new) assert number_is_partly_reduced(res_new, prime) @@ -440,10 +523,10 @@ def assert_bn_power_mod(x, e, prime): def assert_bn_sqrt(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_sqrt(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_fully_reduced(x_new, prime) @@ -457,10 +540,10 @@ def assert_inverse_mod_power_two(x, m): def assert_bn_divide_base(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_divide_base(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert implication( number_is_fully_reduced(x_old, prime), number_is_fully_reduced(x_new, prime) @@ -472,10 +555,10 @@ def assert_bn_divide_base(x_old, prime): def assert_bn_inverse(x_old, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_inverse(bn_x, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_fully_reduced(x_new, prime) @@ -483,31 +566,31 @@ def assert_bn_inverse(x_old, prime): def assert_bn_normalize(bn_x): - x_old = bignum_to_int(bn_x) + x_old = bignum256_to_int(bn_x) lib.bn_normalize(bn_x) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert x_new == x_old % 2 ** (bits_per_limb * limbs_number) assert bignum_is_normalised(bn_x) def assert_bn_add(x_old, y): - bn_x = int_to_bignum(x_old) - bn_y = int_to_bignum(y) + bn_x = int_to_bignum256(x_old) + bn_y = int_to_bignum256(y) lib.bn_add(bn_x, bn_y) - x_new = bignum_to_int(bn_x) - y = bignum_to_int(bn_y) + x_new = bignum256_to_int(bn_x) + y = bignum256_to_int(bn_y) assert bignum_is_normalised(bn_x) assert x_new == x_old + y def assert_bn_addmod(x_old, y, prime): - bn_x = int_to_bignum(x_old) - bn_y = int_to_bignum(y) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_y = int_to_bignum256(y) + bn_prime = int_to_bignum256(prime) lib.bn_addmod(bn_x, bn_y, bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert number_is_partly_reduced(x_new, prime) @@ -515,19 +598,19 @@ def assert_bn_addmod(x_old, y, prime): def assert_bn_addi(x_old, y): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) lib.bn_addi(bn_x, c_uint32(y)) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert x_new == x_old + y def assert_bn_subi(x_old, y, prime): - bn_x = int_to_bignum(x_old) - bn_prime = int_to_bignum(prime) + bn_x = int_to_bignum256(x_old) + bn_prime = int_to_bignum256(prime) lib.bn_subi(bn_x, c_uint32(y), bn_prime) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) assert bignum_is_normalised(bn_x) assert implication( @@ -537,35 +620,50 @@ def assert_bn_subi(x_old, y, prime): def assert_bn_subtractmod(x, y, prime): - bn_x = int_to_bignum(x) - bn_y = int_to_bignum(y) - bn_prime = int_to_bignum(prime) - bn_res = bignum() + bn_x = int_to_bignum256(x) + bn_y = int_to_bignum256(y) + bn_prime = int_to_bignum256(prime) + bn_res = bignum256() lib.bn_subtractmod(bn_x, bn_y, bn_res, bn_prime) - res = bignum_to_int(bn_res) + res = bignum256_to_int(bn_res) assert bignum_is_normalised(bn_x) assert res % prime == (x - y) % prime +def legendre(x, prime): + res = pow(x, (prime - 1) // 2, prime) + if res == prime - 1: + return -1 + return res + + +def assert_bn_legendre(x, prime): + bn_x = int_to_bignum256(x) + bn_prime = int_to_bignum256(prime) + return_value = lib.bn_legendre(bn_x, bn_prime) + + assert return_value == legendre(x, prime) + + def assert_bn_subtract(x, y): - bn_x = int_to_bignum(x) - bn_y = int_to_bignum(y) - bn_res = bignum() + bn_x = int_to_bignum256(x) + bn_y = int_to_bignum256(y) + bn_res = bignum256() lib.bn_subtract(bn_x, bn_y, bn_res) - res = bignum_to_int(bn_res) + res = bignum256_to_int(bn_res) assert bignum_is_normalised(bn_x) assert res == x - y def assert_bn_long_division(x, d): - bn_x = int_to_bignum(x) - bn_q = bignum() + bn_x = int_to_bignum256(x) + bn_q = bignum256() uint32_p_r = uint32_p() lib.bn_long_division(bn_x, d, bn_q, uint32_p_r) r = uint32_p_to_int(uint32_p_r) - q = bignum_to_int(bn_q) + q = bignum256_to_int(bn_q) assert bignum_is_normalised(bn_q) assert q == x // d @@ -573,10 +671,10 @@ def assert_bn_long_division(x, d): def assert_bn_divmod58(x_old): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) uint32_p_r = uint32_p() lib.bn_divmod58(bn_x, uint32_p_r) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) r = uint32_p_to_int(uint32_p_r) assert bignum_is_normalised(bn_x) @@ -585,10 +683,10 @@ def assert_bn_divmod58(x_old): def assert_bn_divmod1000(x_old): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) uint32_p_r = uint32_p() lib.bn_divmod1000(bn_x, uint32_p_r) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) r = uint32_p_to_int(uint32_p_r) assert bignum_is_normalised(bn_x) @@ -597,10 +695,10 @@ def assert_bn_divmod1000(x_old): def assert_bn_divmod10(x_old): - bn_x = int_to_bignum(x_old) + bn_x = int_to_bignum256(x_old) uint32_p_r = uint32_p() lib.bn_divmod10(bn_x, uint32_p_r) - x_new = bignum_to_int(bn_x) + x_new = bignum256_to_int(bn_x) r = uint32_p_to_int(uint32_p_r) assert bignum_is_normalised(bn_x) @@ -636,7 +734,7 @@ def assert_bn_format(x, prefix, suffix, decimals, exponent, trailing, thousands) def char_p_to_string(pointer): return str(pointer.value, "ascii") - bn_x = int_to_bignum(x) + bn_x = int_to_bignum256(x) output_length = 100 output = string_to_char_p("?" * output_length) return_value = lib.bn_format( @@ -661,10 +759,18 @@ def assert_bn_format(x, prefix, suffix, decimals, exponent, trailing, thousands) assert return_value == correct_return_value +def test_bn_copy_lower(r): + assert_bn_copy_lower(r.rand_int_bitsize(261)) + + def test_bn_read_be(r): assert_bn_read_be(r.rand_int_256()) +def test_bn_read_be_512(r): + assert_bn_read_be_512(r.rand_int_512()) + + def test_bn_read_le(r): assert_bn_read_le(r.rand_int_256()) @@ -846,6 +952,11 @@ def test_bn_multiply_reduce_step(r, prime): assert_bn_multiply_reduce_step(res, prime, k) +def test_bn_reduce(r, prime): + x = r.rand_int_bitsize(519) + assert_bn_reduce(x, prime) + + def test_bn_multiply(r, prime): x = r.randrange(floor(sqrt(2**519))) k = r.randrange(floor(sqrt(2**519))) @@ -857,7 +968,7 @@ def test_bn_fast_mod_1(r, prime): def test_bn_fast_mod_2(r, prime): - bn_x = r.rand_bignum() + bn_x = r.rand_bignum256() assert_bn_fast_mod_bn(bn_x, prime) @@ -911,7 +1022,7 @@ def test_bn_inverse_2(r, prime): def test_bn_normalize(r): - assert_bn_normalize(r.rand_bignum()) + assert_bn_normalize(r.rand_bignum256()) def test_bn_add_1(r): @@ -1002,6 +1113,11 @@ def test_bn_subtract_2(r): assert_bn_subtract(a, b) +def test_bn_legendre(r, prime): + x = r.rand_int_bitsize(259) + assert_bn_legendre(x, prime) + + def test_bn_long_division(r): x = r.rand_int_normalized() d = r.randrange(1, 61304 + 1) diff --git a/crypto/tests/test_check.c b/crypto/tests/test_check.c index 128667af54..2dc80ca1da 100644 --- a/crypto/tests/test_check.c +++ b/crypto/tests/test_check.c @@ -58,6 +58,7 @@ #include "ed25519-donna/ed25519-donna.h" #include "ed25519-donna/ed25519-keccak.h" #include "ed25519-donna/ed25519.h" +#include "hash_to_curve.h" #include "hmac_drbg.h" #include "memzero.h" #include "monero/monero.h" @@ -9859,6 +9860,171 @@ START_TEST(test_zkp_bip340_verify_publickey) { } END_TEST +START_TEST(test_expand_message_xmd_sha256) { + static struct { + const char *msg; + const char *dst; + const char *expected_output; + } tests[] = { + // https://www.rfc-editor.org/rfc/rfc9380.html#name-expand_message_xmdsha-256 + {"", "QUUX-V01-CS02-with-expander-SHA256-128", + "68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235"}, + {"abc", "QUUX-V01-CS02-with-expander-SHA256-128", + "d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615"}, + {"abcdef0123456789", "QUUX-V01-CS02-with-expander-SHA256-128", + "eff31487c770a893cfb36f912fbfcbff40d5661771ca4b2cb4eafe524333f5c1"}, + {"q128_" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "QUUX-V01-CS02-with-expander-SHA256-128", + "b23a1d2b4d97b2ef7785562a7e8bac7eed54ed6e97e29aa51bfe3f12ddad1ff9"}, + {"a512_" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaa", + "QUUX-V01-CS02-with-expander-SHA256-128", + "4623227bcc01293b8c130bf771da8c298dede7383243dc0993d2d94823958c4c"}, + {"", "QUUX-V01-CS02-with-expander-SHA256-128", + "af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac06d5e3e29485dadbee0d1215" + "87713a3e0dd4d5e69e93eb7cd4f5df4cd103e188cf60cb02edc3edf18eda8576c412b18" + "ffb658e3dd6ec849469b979d444cf7b26911a08e63cf31f9dcc541708d3491184472c2c" + "29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced"}, + {"abc", "QUUX-V01-CS02-with-expander-SHA256-128", + "abba86a6129e366fc877aab32fc4ffc70120d8996c88aee2fe4b32d6c7b6437a647e6c3" + "163d40b76a73cf6a5674ef1d890f95b664ee0afa5359a5c4e07985635bbecbac65d747d" + "3d2da7ec2b8221b17b0ca9dc8a1ac1c07ea6a1e60583e2cb00058e77b7b72a298425cd1" + "b941ad4ec65e8afc50303a22c0f99b0509b4c895f40"}, + {"abcdef0123456789", "QUUX-V01-CS02-with-expander-SHA256-128", + "ef904a29bffc4cf9ee82832451c946ac3c8f8058ae97d8d629831a74c6572bd9ebd0df6" + "35cd1f208e2038e760c4994984ce73f0d55ea9f22af83ba4734569d4bc95e18350f740c" + "07eef653cbb9f87910d833751825f0ebefa1abe5420bb52be14cf489b37fe1a72f7de2d" + "10be453b2c9d9eb20c7e3f6edc5a60629178d9478df"}, + {"q128_" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "QUUX-V01-CS02-with-expander-SHA256-128", + "80be107d0884f0d881bb460322f0443d38bd222db8bd0b0a5312a6fedb49c1bbd88fd75" + "d8b9a09486c60123dfa1d73c1cc3169761b17476d3c6b7cbbd727acd0e2c942f4dd96ae" + "3da5de368d26b32286e32de7e5a8cb2949f866a0b80c58116b29fa7fabb3ea7d520ee60" + "3e0c25bcaf0b9a5e92ec6a1fe4e0391d1cdbce8c68a"}, + {"a512_" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaa", + "QUUX-V01-CS02-with-expander-SHA256-128", + "546aff5444b5b79aa6148bd81728704c32decb73a3ba76e9e75885cad9def1d06d6792f" + "8a7d12794e90efed817d96920d728896a4510864370c207f99bd4a608ea121700ef01ed" + "879745ee3e4ceef777eda6d9e5e38b90c86ea6fb0b36504ba4a45d22e86f6db5dd43d98" + "a294bebb9125d5b794e9d2a81181066eb954966a487"}, + }; + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + const size_t output_length = strlen(tests[i].expected_output) / 2; + uint8_t output[output_length]; + uint8_t expected_output[output_length]; + + memcpy(expected_output, fromhex(tests[i].expected_output), output_length); + + int res = expand_message_xmd_sha256( + (uint8_t *)tests[i].msg, strlen(tests[i].msg), (uint8_t *)tests[i].dst, + strlen(tests[i].dst), output, output_length); + ck_assert_int_eq(res, true); + ck_assert_mem_eq(output, expected_output, output_length); + } +} +END_TEST + +START_TEST(test_hash_to_curve_p256) { + static struct { + const char *msg; + const char *dst; + const char *expected_x; + const char *expected_y; + } tests[] = { + // https://www.rfc-editor.org/rfc/rfc9380.html#name-p256_xmdsha-256_sswu_ro_ + {"", "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", + "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415"}, + {"abc", "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f", + "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e"}, + {"abcdef0123456789", "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80", + "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3"}, + {"q128_" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", + "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d", + "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e"}, + {"a512_" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaa", + "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5", + "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc"}, + }; + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + curve_point point = {0}; + uint8_t expected_x[32] = {0}; + uint8_t expected_y[32] = {0}; + uint8_t x[32] = {0}; + uint8_t y[32] = {0}; + + memcpy(expected_x, fromhex(tests[i].expected_x), 32); + memcpy(expected_y, fromhex(tests[i].expected_y), 32); + + int res = hash_to_curve_p256((uint8_t *)tests[i].msg, strlen(tests[i].msg), + (uint8_t *)tests[i].dst, strlen(tests[i].dst), + &point); + bn_write_be(&point.x, x); + bn_write_be(&point.y, y); + ck_assert_int_eq(res, true); + ck_assert_mem_eq(x, expected_x, 32); + ck_assert_mem_eq(y, expected_y, 32); + } +} +END_TEST + +START_TEST(test_hash_to_curve_optiga) { + static struct { + const char *input; + const char *public_key; + } tests[] = { + {"0000000000000000000000000000000000000000000000000000000000000000", + "043d2ce2e6ab3d75430c7ce3627d840fef7856bf6a1b4aa7579d583e5906bb3c897ee7a" + "af81ebe6d18a2534e563ba67bb71964d0494737e282f9a99c8370cc7ea6"}, + }; + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + uint8_t input[32] = {0}; + uint8_t expected_public_key[65] = {0}; + uint8_t public_key[65] = {0}; + + memcpy(input, fromhex(tests[i].input), 32); + memcpy(expected_public_key, fromhex(tests[i].public_key), 65); + + int res = hash_to_curve_optiga(input, public_key); + ck_assert_int_eq(res, true); + ck_assert_mem_eq(public_key, expected_public_key, 65); + } +} +END_TEST + static int my_strncasecmp(const char *s1, const char *s2, size_t n) { size_t i = 0; while (i < n) { @@ -10181,6 +10347,12 @@ Suite *test_suite(void) { tcase_add_test(tc, test_zkp_bip340_verify_publickey); suite_add_tcase(s, tc); + tc = tcase_create("hash_to_curve"); + tcase_add_test(tc, test_expand_message_xmd_sha256); + tcase_add_test(tc, test_hash_to_curve_p256); + tcase_add_test(tc, test_hash_to_curve_optiga); + suite_add_tcase(s, tc); + #if USE_CARDANO tc = tcase_create("bip32-cardano"); diff --git a/legacy/firmware/Makefile b/legacy/firmware/Makefile index d6dd702921..8582665e7c 100644 --- a/legacy/firmware/Makefile +++ b/legacy/firmware/Makefile @@ -162,6 +162,7 @@ CFLAGS += -Wno-sequence-point CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1 -DPB_ENCODE_ARRAYS_UNPACKED=1 -DPB_VALIDATE_UTF8=1 CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' CFLAGS += -DUSE_MONERO=0 +CFLAGS += -DUSE_OPTIGA=0 ifneq ($(BITCOIN_ONLY),1) CFLAGS += -DUSE_ETHEREUM=1 CFLAGS += -DUSE_NEM=1 diff --git a/storage/storage.c b/storage/storage.c index b8bac967b7..0c86781003 100644 --- a/storage/storage.c +++ b/storage/storage.c @@ -31,6 +31,10 @@ #include "sha2.h" #include "storage.h" +#if USE_OPTIGA +#include "optiga.h" +#endif + #define LOW_MASK 0x55555555 // The APP namespace which is reserved for storage related values. @@ -78,16 +82,18 @@ const uint32_t V0_PIN_EMPTY = 1; // up constant storage space. #define MAX_WIPE_CODE_LEN 50 -// Maximum number of failed unlock attempts. -// NOTE: The PIN counter logic relies on this constant being less than or equal -// to 16. -#define PIN_MAX_TRIES 16 - // The total number of iterations to use in PBKDF2. #define PIN_ITER_COUNT 20000 -// The number of seconds required to derive the KEK and KEIV. -#define DERIVE_SECS 1 +// The number of milliseconds required to execute PBKDF2. +#define PIN_PBKDF2_MS 1280 + +// The number of milliseconds required to derive the KEK and KEIV. +#if USE_OPTIGA +#define PIN_DERIVE_MS (PIN_PBKDF2_MS + OPTIGA_PIN_DERIVE_MS) +#else +#define PIN_DERIVE_MS PIN_PBKDF2_MS +#endif // The length of the guard key in words. #define GUARD_KEY_WORDS 1 @@ -101,9 +107,6 @@ const uint32_t V0_PIN_EMPTY = 1; // The length of the hashed hardware salt in bytes. #define HARDWARE_SALT_SIZE SHA256_DIGEST_LENGTH -// The length of the random salt in bytes. -#define RANDOM_SALT_SIZE 4 - // The length of the data encryption key in bytes. #define DEK_SIZE 32 @@ -472,79 +475,181 @@ static secbool is_not_wipe_code(const uint8_t *pin, size_t pin_len) { return sectrue; } +static secbool ui_progress(uint32_t elapsed_ms) { + ui_rem -= elapsed_ms; + if (ui_callback && ui_message) { + uint32_t progress = 0; + if (ui_total < 1000000) { + progress = 1000 * (ui_total - ui_rem) / ui_total; + } else { + // Avoid overflow. Precise enough. + progress = (ui_total - ui_rem) / (ui_total / 1000); + } + // Round the remaining time to the nearest second. + return ui_callback((ui_rem + 500) / 1000, progress, ui_message); + } else { + return secfalse; + } +} + +#if !USE_OPTIGA static void derive_kek(const uint8_t *pin, size_t pin_len, - const uint8_t *random_salt, const uint8_t *ext_salt, + const uint8_t *storage_salt, const uint8_t *ext_salt, uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) { - uint8_t salt[HARDWARE_SALT_SIZE + RANDOM_SALT_SIZE + EXTERNAL_SALT_SIZE] = { + uint8_t salt[HARDWARE_SALT_SIZE + STORAGE_SALT_SIZE + EXTERNAL_SALT_SIZE] = { 0}; size_t salt_len = 0; memcpy(salt + salt_len, hardware_salt, HARDWARE_SALT_SIZE); salt_len += HARDWARE_SALT_SIZE; - memcpy(salt + salt_len, random_salt, RANDOM_SALT_SIZE); - salt_len += RANDOM_SALT_SIZE; + memcpy(salt + salt_len, storage_salt, STORAGE_SALT_SIZE); + salt_len += STORAGE_SALT_SIZE; if (ext_salt != NULL) { memcpy(salt + salt_len, ext_salt, EXTERNAL_SALT_SIZE); salt_len += EXTERNAL_SALT_SIZE; } - uint32_t progress = (ui_total - ui_rem) * 1000 / ui_total; - if (ui_callback && ui_message) { - ui_callback(ui_rem, progress, ui_message); - } - PBKDF2_HMAC_SHA256_CTX ctx = {0}; pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 1); for (int i = 1; i <= 5; i++) { pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); - if (ui_callback && ui_message) { - progress = - ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; - ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); - } + ui_progress(PIN_PBKDF2_MS / 10); } pbkdf2_hmac_sha256_Final(&ctx, kek); pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 2); for (int i = 6; i <= 10; i++) { pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); - if (ui_callback && ui_message) { - progress = - ((ui_total - ui_rem) * 1000 + i * DERIVE_SECS * 100) / ui_total; - ui_callback(ui_rem - i * DERIVE_SECS / 10, progress, ui_message); - } + ui_progress(PIN_PBKDF2_MS / 10); } pbkdf2_hmac_sha256_Final(&ctx, keiv); - ui_rem -= DERIVE_SECS; memzero(&ctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); memzero(&salt, sizeof(salt)); } +#endif + +#if USE_OPTIGA +static void stretch_pin_optiga(const uint8_t *pin, size_t pin_len, + const uint8_t storage_salt[STORAGE_SALT_SIZE], + const uint8_t *ext_salt, + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE]) { + // Combining the PIN with the storage salt aims to ensure that if the + // MCU-Optiga communication is compromised, then a user with a low-entropy PIN + // remains protected against an attacker who is not able to read the contents + // of the MCU storage. Stretching the PIN with PBKDF2 ensures that even if + // Optiga itself is completely compromised, it will not reduce the security + // of the device below that of earlier Trezor models which also use PBKDF2 + // with the same number of iterations. + + uint8_t salt[HARDWARE_SALT_SIZE + STORAGE_SALT_SIZE + EXTERNAL_SALT_SIZE] = { + 0}; + size_t salt_len = 0; + + memcpy(salt + salt_len, hardware_salt, HARDWARE_SALT_SIZE); + salt_len += HARDWARE_SALT_SIZE; + + memcpy(salt + salt_len, storage_salt, STORAGE_SALT_SIZE); + salt_len += STORAGE_SALT_SIZE; + + if (ext_salt != NULL) { + memcpy(salt + salt_len, ext_salt, EXTERNAL_SALT_SIZE); + salt_len += EXTERNAL_SALT_SIZE; + } + + PBKDF2_HMAC_SHA256_CTX ctx = {0}; + pbkdf2_hmac_sha256_Init(&ctx, pin, pin_len, salt, salt_len, 1); + memzero(&salt, sizeof(salt)); + + for (int i = 1; i <= 10; i++) { + pbkdf2_hmac_sha256_Update(&ctx, PIN_ITER_COUNT / 10); + ui_progress(PIN_PBKDF2_MS / 10); + } + pbkdf2_hmac_sha256_Final(&ctx, stretched_pin); + + memzero(&ctx, sizeof(ctx)); +} +#endif + +#if USE_OPTIGA +static void derive_kek_optiga( + const uint8_t optiga_secret[OPTIGA_PIN_SECRET_SIZE], + uint8_t kek[SHA256_DIGEST_LENGTH], uint8_t keiv[SHA256_DIGEST_LENGTH]) { + PBKDF2_HMAC_SHA256_CTX ctx = {0}; + pbkdf2_hmac_sha256_Init(&ctx, optiga_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, + 1); + pbkdf2_hmac_sha256_Update(&ctx, 1); + pbkdf2_hmac_sha256_Final(&ctx, kek); + + pbkdf2_hmac_sha256_Init(&ctx, optiga_secret, OPTIGA_PIN_SECRET_SIZE, NULL, 0, + 2); + pbkdf2_hmac_sha256_Update(&ctx, 1); + pbkdf2_hmac_sha256_Final(&ctx, keiv); + + memzero(&ctx, sizeof(ctx)); +} +#endif + +static void derive_kek_set(const uint8_t *pin, size_t pin_len, + const uint8_t *storage_salt, const uint8_t *ext_salt, + uint8_t kek[SHA256_DIGEST_LENGTH], + uint8_t keiv[SHA256_DIGEST_LENGTH]) { +#if USE_OPTIGA + uint8_t optiga_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; + stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, stretched_pin); + optiga_pin_set(ui_progress, stretched_pin, optiga_secret); + memzero(stretched_pin, sizeof(stretched_pin)); + derive_kek_optiga(optiga_secret, kek, keiv); + memzero(optiga_secret, sizeof(optiga_secret)); +#else + derive_kek(pin, pin_len, storage_salt, ext_salt, kek, keiv); +#endif +} + +static void derive_kek_unlock(const uint8_t *pin, size_t pin_len, + const uint8_t *storage_salt, + const uint8_t *ext_salt, + uint8_t kek[SHA256_DIGEST_LENGTH], + uint8_t keiv[SHA256_DIGEST_LENGTH]) { +#if USE_OPTIGA + uint8_t optiga_secret[OPTIGA_PIN_SECRET_SIZE] = {0}; + uint8_t stretched_pin[OPTIGA_PIN_SECRET_SIZE] = {0}; + stretch_pin_optiga(pin, pin_len, storage_salt, ext_salt, stretched_pin); + optiga_pin_verify(ui_progress, stretched_pin, optiga_secret); + memzero(stretched_pin, sizeof(stretched_pin)); + derive_kek_optiga(optiga_secret, kek, keiv); + memzero(optiga_secret, sizeof(optiga_secret)); +#else + derive_kek(pin, pin_len, storage_salt, ext_salt, kek, keiv); +#endif +} static secbool set_pin(const uint8_t *pin, size_t pin_len, const uint8_t *ext_salt) { // Encrypt the cached keys using the new PIN and set the new PVC. - uint8_t buffer[RANDOM_SALT_SIZE + KEYS_SIZE + POLY1305_TAG_SIZE] = {0}; + uint8_t buffer[STORAGE_SALT_SIZE + KEYS_SIZE + POLY1305_TAG_SIZE] = {0}; uint8_t *rand_salt = buffer; - uint8_t *ekeys = buffer + RANDOM_SALT_SIZE; - uint8_t *pvc = buffer + RANDOM_SALT_SIZE + KEYS_SIZE; + uint8_t *ekeys = buffer + STORAGE_SALT_SIZE; + uint8_t *pvc = buffer + STORAGE_SALT_SIZE + KEYS_SIZE; uint8_t kek[SHA256_DIGEST_LENGTH] = {0}; uint8_t keiv[SHA256_DIGEST_LENGTH] = {0}; chacha20poly1305_ctx ctx = {0}; - random_buffer(rand_salt, RANDOM_SALT_SIZE); - derive_kek(pin, pin_len, rand_salt, ext_salt, kek, keiv); + random_buffer(rand_salt, STORAGE_SALT_SIZE); + ui_progress(0); + derive_kek_set(pin, pin_len, rand_salt, ext_salt, kek, keiv); rfc7539_init(&ctx, kek, keiv); memzero(kek, sizeof(kek)); memzero(keiv, sizeof(keiv)); chacha20poly1305_encrypt(&ctx, cached_keys, ekeys, KEYS_SIZE); rfc7539_finish(&ctx, 0, KEYS_SIZE, pvc); memzero(&ctx, sizeof(ctx)); - secbool ret = - norcow_set(EDEK_PVC_KEY, buffer, RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE); + secbool ret = norcow_set(EDEK_PVC_KEY, buffer, + STORAGE_SALT_SIZE + KEYS_SIZE + PVC_SIZE); memzero(buffer, sizeof(buffer)); if (ret == sectrue) { @@ -654,7 +759,15 @@ static void init_wiped_storage(void) { // set. return; } + +#if USE_OPTIGA + ensure(optiga_random_buffer(cached_keys, sizeof(cached_keys)) ? sectrue + : secfalse, + "optiga_random_buffer failed"); + random_xor(cached_keys, sizeof(cached_keys)); +#else random_buffer(cached_keys, sizeof(cached_keys)); +#endif unlocked = sectrue; uint32_t version = NORCOW_VERSION; ensure(auth_init(), "set_storage_auth_tag failed"); @@ -668,7 +781,7 @@ static void init_wiped_storage(void) { ensure(set_wipe_code(WIPE_CODE_EMPTY, WIPE_CODE_EMPTY_LEN), "set_wipe_code failed"); - ui_total = DERIVE_SECS; + ui_total = PIN_DERIVE_MS; ui_rem = ui_total; ui_message = PROCESSING_MSG; ensure(set_pin(PIN_EMPTY, PIN_EMPTY_LEN, NULL), "init_pin failed"); @@ -956,15 +1069,15 @@ static secbool decrypt_dek(const uint8_t *kek, const uint8_t *keiv) { uint16_t len = 0; if (sectrue != initialized || sectrue != norcow_get(EDEK_PVC_KEY, &buffer, &len) || - len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { + len != STORAGE_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { handle_fault("no EDEK"); return secfalse; } - const uint8_t *ekeys = (const uint8_t *)buffer + RANDOM_SALT_SIZE; + const uint8_t *ekeys = (const uint8_t *)buffer + STORAGE_SALT_SIZE; const uint32_t *pvc = (const uint32_t *)buffer + - (RANDOM_SALT_SIZE + KEYS_SIZE) / sizeof(uint32_t); - _Static_assert(((RANDOM_SALT_SIZE + KEYS_SIZE) & 3) == 0, "PVC unaligned"); + (STORAGE_SALT_SIZE + KEYS_SIZE) / sizeof(uint32_t); + _Static_assert(((STORAGE_SALT_SIZE + KEYS_SIZE) & 3) == 0, "PVC unaligned"); _Static_assert((PVC_SIZE & 3) == 0, "PVC size unaligned"); uint8_t keys[KEYS_SIZE] = {0}; @@ -1006,8 +1119,8 @@ static secbool unlock(const uint8_t *pin, size_t pin_len, // storage_upgrade_unlocked(). uint32_t legacy_pin = 0; if (get_lock_version() <= 2) { - ui_total += DERIVE_SECS; - ui_rem += DERIVE_SECS; + ui_total += PIN_DERIVE_MS; + ui_rem += PIN_DERIVE_MS; legacy_pin = pin_to_int(pin, pin_len); unlock_pin = (const uint8_t *)&legacy_pin; unlock_pin_len = sizeof(legacy_pin); @@ -1033,23 +1146,15 @@ static secbool unlock(const uint8_t *pin, size_t pin_len, // Sleep for 2^ctr - 1 seconds before checking the PIN. uint32_t wait = (1 << ctr) - 1; - ui_total += wait; - uint32_t progress = 0; - for (ui_rem = ui_total; ui_rem > ui_total - wait; ui_rem--) { - for (int i = 0; i < 10; i++) { - if (ui_callback && ui_message) { - if (ui_total > 1000000) { // precise enough - progress = (ui_total - ui_rem) / (ui_total / 1000); - } else { - progress = ((ui_total - ui_rem) * 10 + i) * 100 / ui_total; - } - if (sectrue == ui_callback(ui_rem, progress, ui_message)) { - memzero(&legacy_pin, sizeof(legacy_pin)); - return secfalse; - } - } - hal_delay(100); + ui_total += wait * 1000; + ui_rem += wait * 1000; + ui_progress(0); + for (uint32_t i = 0; i < 10 * wait; i++) { + if (sectrue == ui_progress(100)) { + memzero(&legacy_pin, sizeof(legacy_pin)); + return secfalse; } + hal_delay(100); } // Read the random salt from EDEK_PVC_KEY and use it to derive the KEK and @@ -1058,15 +1163,15 @@ static secbool unlock(const uint8_t *pin, size_t pin_len, uint16_t len = 0; if (sectrue != initialized || sectrue != norcow_get(EDEK_PVC_KEY, &rand_salt, &len) || - len != RANDOM_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { + len != STORAGE_SALT_SIZE + KEYS_SIZE + PVC_SIZE) { memzero(&legacy_pin, sizeof(legacy_pin)); handle_fault("no EDEK"); return secfalse; } uint8_t kek[SHA256_DIGEST_LENGTH] = {0}; uint8_t keiv[SHA256_DIGEST_LENGTH] = {0}; - derive_kek(unlock_pin, unlock_pin_len, (const uint8_t *)rand_salt, ext_salt, - kek, keiv); + derive_kek_unlock(unlock_pin, unlock_pin_len, (const uint8_t *)rand_salt, + ext_salt, kek, keiv); memzero(&legacy_pin, sizeof(legacy_pin)); // First, we increase PIN fail counter in storage, even before checking the @@ -1118,7 +1223,7 @@ secbool storage_unlock(const uint8_t *pin, size_t pin_len, return secfalse; } - ui_total = DERIVE_SECS; + ui_total = PIN_DERIVE_MS; ui_rem = ui_total; if (pin_len == 0) { if (ui_message == NULL) { @@ -1412,7 +1517,7 @@ secbool storage_change_pin(const uint8_t *oldpin, size_t oldpin_len, return secfalse; } - ui_total = 2 * DERIVE_SECS; + ui_total = 2 * PIN_DERIVE_MS; ui_rem = ui_total; ui_message = (oldpin_len != 0 && newpin_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG; @@ -1461,7 +1566,7 @@ secbool storage_change_wipe_code(const uint8_t *pin, size_t pin_len, return secfalse; } - ui_total = DERIVE_SECS; + ui_total = PIN_DERIVE_MS; ui_rem = ui_total; ui_message = (pin_len != 0 && wipe_code_len == 0) ? VERIFYING_PIN_MSG : PROCESSING_MSG; @@ -1619,7 +1724,7 @@ static secbool storage_upgrade(void) { } // Set EDEK_PVC_KEY and PIN_NOT_SET_KEY. - ui_total = DERIVE_SECS; + ui_total = PIN_DERIVE_MS; ui_rem = ui_total; ui_message = PROCESSING_MSG; secbool found = norcow_get(V0_PIN_KEY, &val, &len); diff --git a/storage/storage.h b/storage/storage.h index 2c89659823..dcaa8490f3 100644 --- a/storage/storage.h +++ b/storage/storage.h @@ -41,6 +41,18 @@ extern const uint8_t *PIN_EMPTY; #define PIN_EMPTY_LEN 0 +// Maximum number of failed unlock attempts. +// NOTE: The PIN counter logic relies on this constant being less than or equal +// to 16. +#define PIN_MAX_TRIES 16 + +// The length of the random salt in bytes. +#if USE_OPTIGA +#define STORAGE_SALT_SIZE 32 +#else +#define STORAGE_SALT_SIZE 4 +#endif + typedef secbool (*PIN_UI_WAIT_CALLBACK)(uint32_t wait, uint32_t progress, const char *message); diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 932fa69136..89b584a21b 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -737,43 +737,43 @@ }, "TR": { "click_tests": { -"TR_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "757b981b1f41c948785e339e9f678b08925b2c310a1baad2ccb909689ac42e2b", -"TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "acce23fc82d1bd9f2b5f48cae438018aa083916a795e88536d7dd8edf1b37e90", -"TR_test_autolock.py::test_autolock_interrupts_passphrase": "a83a9f7879402e84487efcd838e4297d78d9581e43994acf6403a80527597b34", -"TR_test_autolock.py::test_autolock_interrupts_signing": "5c62b3e0e04f5bf6097309e541b8dad08dbfadd979d04ee60024ba5be66f094d", -"TR_test_autolock.py::test_autolock_passphrase_keyboard": "2f071e084ca8fc21f9304b87174d1326a5083f032be73a5bc075688851369b76", -"TR_test_autolock.py::test_dryrun_enter_word_slowly": "9df4dfa03a9d1f009d748043b818eacd556eaa64addcbce8acc7b611730cdccf", -"TR_test_autolock.py::test_dryrun_locks_at_number_of_words": "7771f9227f39f19c84308873b0fffdbeea0704380d8e19387fc6bae2ff1651fe", -"TR_test_autolock.py::test_dryrun_locks_at_word_entry": "79b8a80451ae0c33bc7cc810e3f26926af0abeaf785841b44411db09338400bd", -"TR_test_lock.py::test_hold_to_lock": "fdb2743cdb7290647a0fe5f1beb0f52016955d36e53cfa517d5eb3f96f16165f", -"TR_test_passphrase_tr.py::test_cancel": "89322822d981b2209ef55903a1c0709acbf112ad0543c63ed93dc0d4e261e595", -"TR_test_passphrase_tr.py::test_passphrase_delete": "bffb5704ff91b9e84d2532fafba0f1353f9038bd635e147079a5e29921cde170", -"TR_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "01aea07d068be07035dfc12c60990d162c44978a6e5c544d7878ac37f5449348", -"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "8225828a81c3d19ca44e4e065d38ce5e150bea5d2b09475cfaaf36f77628f865", -"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "3ff54628778e9fef8d26a834f14b9dda412571313f53e946266a4e86b868887b", -"TR_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "5b83179c8c74914ae62bf60ebdad90b4e50673ec31f439313856f428067d4875", -"TR_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "9087d883f87b4cfad5afb28e8f3bc527fb3f1f396281fdc240d89b131ce05875", -"TR_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "fb72ed83469687d2919536642823874de06958829703b0d3fee945b6a92d9fb3", -"TR_test_passphrase_tr.py::test_passphrase_loop_all_characters": "d3d488b83bce42229e9c493159d143ed9a58477e65e7b86d8143f2ca0b862ba3", -"TR_test_pin.py::test_pin_change": "0d309ac15f08341d9a88aaf156bf06c044acfbf58e8fc34c7ef6d05deb597d8a", -"TR_test_pin.py::test_pin_delete_hold": "1358334ab20735ab820b2f62e27f983301944d7b483bdec2740b4965b85a0c7f", -"TR_test_pin.py::test_pin_incorrect": "7f4748f112fef97fff978524fe911a58e6a2406993330da2fb1343f04aef8050", -"TR_test_pin.py::test_pin_long": "a7e26e67545d218848ba488b9e30ae40c500e8fcb7d46bb945178867f78b71cf", -"TR_test_pin.py::test_pin_long_delete": "b79d0dda5f77754a6ca92c0fb9cdd31362aa07cd36bfc2ce81b6551a814db48e", -"TR_test_pin.py::test_pin_longer_than_max": "f6faf34a87a21bf46a81b072d984dc4d1f5b96276b4fd76108633506ec3de594", -"TR_test_pin.py::test_pin_same_as_wipe_code": "bf1972d45717c8f519d74df04c389dfef3733c9161246e7cd3156e4ed9dc4a50", -"TR_test_pin.py::test_pin_setup": "8b6254dd03d897290be4a3d7e371bd714aa2333990bcb059834435d940db867f", +"TR_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "b96ab80af28b6d5e13ef32a8cc19812445c6258a2ad64f15870c7490a37edc06", +"TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "3d0833002f39256a15ee5008af9d2cd304c61bc2932cb6dc844c027bfed3ed1d", +"TR_test_autolock.py::test_autolock_interrupts_passphrase": "1ab8da65492ade3e6301f784b818d355959c665ea4b91e38f6dd2de8c8b21c84", +"TR_test_autolock.py::test_autolock_interrupts_signing": "5f26a18a711ec7d17c035cc60478249281edeeac512d4561e55bd7a3c2d2ef94", +"TR_test_autolock.py::test_autolock_passphrase_keyboard": "60c8bd539f4fa4e5b4de2ac49f841d526868429ba348293665fd9a8ddc1270bd", +"TR_test_autolock.py::test_dryrun_enter_word_slowly": "817e4222e39fc362bf0b6e997f5d806291556374f1f10c995c1af773ddf51566", +"TR_test_autolock.py::test_dryrun_locks_at_number_of_words": "5b3dfca5a87c24196211c4c69e6b5297424313e8fa5fe8b7ec319c8ab3b44a71", +"TR_test_autolock.py::test_dryrun_locks_at_word_entry": "05f0b0956d12f04f96537bbc612064d25c52ef9b41ab9c8278a812c2ff7a1e08", +"TR_test_lock.py::test_hold_to_lock": "77b90847359d27503f11cddb9900a71cf311a0e1650aeb20bcee052fca5b3e33", +"TR_test_passphrase_tr.py::test_cancel": "4a79e82b3ddf23c087e70767f2c4d4720f980370e22be209189d4ed42a453ee8", +"TR_test_passphrase_tr.py::test_passphrase_delete": "0ec483b293e4cbaeabe1f118edcff55368720429602588f05d277b54826e9bc2", +"TR_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "5fe91eac5909cf3fd10a8f856f1d8f961469b49d8711d6c25788b81bd1c7e4a2", +"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "e0aa81693c8b40ee12a8ff41f66cad491fe8e5a220bb0a915c18ed7b2e8d323c", +"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "9d1762d0db3b5699507b068ec81cb1ed3e265c6ce7a7a2e6d7a878f0471f32b8", +"TR_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "7ac52d0d0de8d7cd35200ec9a2b2738e8e4e9ba87435caefbdbff3776749b83a", +"TR_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "43e693afaa37d22fcbe46a44ca13f6cdd34e86b43320a4226faf04344c07c060", +"TR_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "502828e28edd8147781b1714cae83eea6f71a9820c30446d7e38eeb3a43617c9", +"TR_test_passphrase_tr.py::test_passphrase_loop_all_characters": "5079e557c3ee648d7778154d65ac0f0200e2ee7e6cc88d46dfb753e314582bed", +"TR_test_pin.py::test_pin_change": "5820a43c03708f208059365711b2a46427c279c7462c2c529a73e5ad560bc36b", +"TR_test_pin.py::test_pin_delete_hold": "8f8be800e39bcfc95236abcfbb69477776885471605fe82da5d81fcfe04c397e", +"TR_test_pin.py::test_pin_incorrect": "852bf321191682d6cf67c06098ef87571a42f1337609ea90bf476296b344bab4", +"TR_test_pin.py::test_pin_long": "d2a86a95b38b8c09c04f0e02a118b62174e835c3ea58aace2d46d9af2a1c561b", +"TR_test_pin.py::test_pin_long_delete": "6006261b91ea207efb220cd20cf5574e775e8e4ce04eb6f86f924f12fd818a2f", +"TR_test_pin.py::test_pin_longer_than_max": "87c113c072710169248bb56780ead04d6d4eb5f80892d4327d3af5e98cf0e336", +"TR_test_pin.py::test_pin_same_as_wipe_code": "41e5505d884d87efe05cb9401351f89c7365dbece7f911d4115516c11f52bb9b", +"TR_test_pin.py::test_pin_setup": "040256a62fb20f5284bbec0ababab6d94f6fbed1b9b9a0cebe58a73ccae7ff2d", "TR_test_pin.py::test_pin_setup_mismatch": "4cafbb73ee107509f8766d73d2389ef246d27ade3540b835cdfaf83d2cb4a416", -"TR_test_pin.py::test_pin_short": "20acadc34173f58ce9eb9d712e47732f1c8b03868f0f197be4213b314832ee4c", -"TR_test_pin.py::test_wipe_code_same_as_pin": "17bb952006f42c3948370d1257618be76cf39d2892fd5c491488d9544cdc205d", -"TR_test_pin.py::test_wipe_code_setup": "2f1d097810aa38ff87acef79133ccc735a9523118a1d83420efb84db21a05d97", -"TR_test_recovery.py::test_recovery_bip39": "92c26b0e97c8e950c568bc8c7f71177a6e9338c950d6d6abb129dc217dc57c3a", -"TR_test_recovery.py::test_recovery_slip39_basic": "ccfd227ca8dcd1abf0dc7b5827167279461b7151c810c2a587893d7016d38f01", -"TR_test_reset_bip39.py::test_reset_bip39": "7dee2da63f3d220b4a1466b59da6c89a623d1a55c3d5300efe37761d064435db", -"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "0442794f3b952b6362d6569eaf15af002e7af5ccd3fedcf5339a9e75a61cb6e7", -"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "83d29833c99068a474c7b1c841cd0a88c92d3fba0e471d5e10dcd0d58b164d7a", -"TR_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "11239923e51327c518bcb6123c8e645b0e34174b1857ff64a2e7cbd49b3bcc21", -"TR_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "b51e9c4b3fc6962c128a75676a0ef5a08d72e69abe825707195fcdaf7b114760", +"TR_test_pin.py::test_pin_short": "ab68bf7e867ae8b2ce7a4f036152220f8b7630b7a278ae73c1b85ec4078a0c7b", +"TR_test_pin.py::test_wipe_code_same_as_pin": "e75b67a93ec401ec4a42d3f9d30019ebeb5a8a7a8de26b552680ac6d2de566c9", +"TR_test_pin.py::test_wipe_code_setup": "83e31cdef28ab56a0673113050535c8c495f01143fab2f3702ac3c1cc7310467", +"TR_test_recovery.py::test_recovery_bip39": "445f6fb6eb9cb2c6dc10e497d81612376d1e454732a361f8ef890c7cc36080d2", +"TR_test_recovery.py::test_recovery_slip39_basic": "588c7a31c9813f9cef2c9d07135dcadfece8241fc53b00aeb30b7872a92d0c2c", +"TR_test_reset_bip39.py::test_reset_bip39": "4eb9d3845ef1cfb3ca8de96978693425e27835e65be68f013997243019199ff2", +"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "d9ccb81df7885920427001c913b0e116c2bca8b36681c2c1057126600cd23504", +"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "384d09d17f0dafc2a3c43eaa44573545ca8a4a2bc20bc70b29aa164007364ce1", +"TR_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "431a64338e9a7cf158f2d96bdf18ff29f2bbdf5cdc40cd33d1c3250410c0265b", +"TR_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "bd22117d0ee838824a345206fc19afee95194f1bb6f7b2c25d2cfe5d9f90937d", "TR_test_tutorial.py::test_tutorial_again_and_skip": "c611a049ccc584fd69abf55bac3b185e370e0680990afb3139b755fd1f41a19e", "TR_test_tutorial.py::test_tutorial_finish": "1ae785e1ff678241aa18a254ae755a002ccd62550cbbd1dd92fa3cbd53d071b0", "TR_test_tutorial.py::test_tutorial_skip": "00a893066c2578f14ae9ab749791f1dde753b43be460d69a304aaae8704cfe8a" @@ -794,8 +794,8 @@ "TR_bitcoin-test_authorize_coinjoin.py::test_get_address": "a96bebc82d5aff9c6a8583ddb354e6427689a3be4fddf082c3cd0e8722e54d46", "TR_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "4c2bba305bab30de2fcff0cec5ab1192f2e4d826d86f91f7172dfa624f5f3139", "TR_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "5f70b535406a6254113ed2a5f780ba98b8205abf6425eb7038d22395953aa560", -"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "cf587c961927f76a8e2f361c2c303b913f5ef951f99330c0f081dc9dbdbe7b17", -"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "cf587c961927f76a8e2f361c2c303b913f5ef951f99330c0f081dc9dbdbe7b17", +"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "76fe57750f0bfbaa84aaa99f67329dc93e505d51521c2a2490d523996b403aa3", +"TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "76fe57750f0bfbaa84aaa99f67329dc93e505d51521c2a2490d523996b403aa3", "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "4f275de439c812363140d3839ebddd9243e2bb34d80d02a487361148b2bbab71", "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_migration": "4cf48d6bb48a9efbff9e2949d657fde4dea7ae9e92f47cafdfcd11d7765d76b8", "TR_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "db453154c6d8318befea7230eb2a9639fece5bdfd83c62fbb7a1e9195b77ac1b", @@ -1751,56 +1751,56 @@ "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[pin_protection-True]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[u2f_counter-1]": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "4d5662f4ca0039e496a9fc4e5796df1adf58d923f980625a55fe477bfdb391ea", -"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "c22dc7b9cb52aed5c99977b7421d4e58e7a79db7aec71fcca008776970c9e18a", -"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "8668a2978f4644a20359d46cf641eb82f7ab424825f3793a1143c1a224383ac8", +"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "e711de3d3c2670ccdf0e05ff59bd3b90f54ef999cec741243eacfca3b1712bf3", +"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "b784e54944ee32942129a8bf04dc9374f8f3af38adf28f66247e10aa5920384b", +"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "c77b004677d78eac231cd77ec41dffc99a27f5b98bba7f97dd958c9f362c74ad", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_uninitialized": "f49c8d846c2d56a575f0ad49463845ba641b02656783e4fcfc67d74e8fa671dd", "TR_reset_recovery-test_recovery_bip39_t2.py::test_already_initialized": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "e1d71af79a9838ec9a2f5cb2f28e287624cf84e26871fb5c9f01222fe331456b", -"TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_pin_passphrase": "785735184d03b8b91d14c2ecd2e78cc7d38008a527cdf95573c5b0e3a0c75636", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_abort": "c26116210a8e29082af3e31d4a83d827e4e17e2529d08d916d6b6121f17367da", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "dc031faebd4ba2a3ab3247dcfff8873504476124c8b4c71725d738c5b2b54f12", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "f8bbe7f68cf29c448b3d3b84e4e3df3a7322d4e5af01beb5b09296e3ea56e78d", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "50b636e6cac25d4f77f666a44f33fc4f9373c49f80a5074d347161b5a0ff2513", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "3a0555e0dff9417bf6993545fb6ccd14ef15b156f777ea54a9c2d0246ed31f26", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "69da5a7a67440e607c905dc9bc21d4d61c7a7bb0032687538973b0edfa71e0dc", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "1fb54e6f935758a2dc9d0a40ca93b4f288bddf4c0ec4809da9b21811bfba57a4", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares0-c2d2-850ffa77": "69da5a7a67440e607c905dc9bc21d4d61c7a7bb0032687538973b0edfa71e0dc", -"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares1-c41d-ca9ddec8": "1fb54e6f935758a2dc9d0a40ca93b4f288bddf4c0ec4809da9b21811bfba57a4", -"TR_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "8af441fb283011f446b562363cf475bab53c4f6b027255ec97ec88258acd9dd6", -"TR_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "179041a171a0743d2f97d0d078eca322ce3eb479accdcf4b1e46f9a3b4dac800", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_1of1": "a160d22eb9baf16d562f759692de0408339d42c5746b200a0689de5467bd0ff9", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_abort": "c26116210a8e29082af3e31d4a83d827e4e17e2529d08d916d6b6121f17367da", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_first_share": "f96e64bdce7394e39547d8690fdd35f3634683877be65ff886f317267a1eb518", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_second_share": "f4b4dbffe04180189122cc683402a57ad6e5ed1e698d1e7fe9f292b58f8a07ba", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_noabort": "5771cc8f5e1ea7ef2df7f89368d5a06059aa55ebbdb270c4d426dd925f37e856", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_recover_with_pin_passphrase": "bfde566be7d159aaa4b665f9c184ad6b5d842e8832b5d2ce949b9a120eea7ffa", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_same_share": "85b785e3de718f78f5f248cb015981a2d612bc4882551d118faee2c82454a89d", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "513f731a79335eb91ce6449871b3527dcf5a9e3c67129332a83a699ac4418458", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39-a50896b7": "6f256e0294e4fc6aab3927a42cebc56382eeec1bedd2899fdd2761ba7bf8fdd0", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[0]": "b2d1c2662407aeb45531a90c588a5e8796c49a66b5efeb8146e8d201ad19442d", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[1]": "c6dde1c11e18a0b861e2cd04c10c0c9f339d21e20494fa1a5cd4c7faabf6abcf", -"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "d9b276bea0b70f21e7dd83a4889970c016b82ba562c81b90c535297e1fd1416a", -"TR_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "5d579dcdf4055e97a33f1be9dbd19f06e629404bcdf15aa53816066077e571a5", -"TR_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "6b31053643ee3fc16e284a723a92516a3f3b6cea02e747add5c79af74a484c88", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "f60a5c63c74cafbfda26817acf3a409e05cb3746b916565f3510569754f579a0", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "6d270737f23bd05874d9a7a7234f42fb5e0cec3769dee0186c3337c7c78b79a8", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "809e150906fc39a019d96c88107032ab36a02796ebe6fb5c05075530a1109f60", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "fd5655cdfe6e76fd15ef57712c4ba0439ce20ef37f4ce0e540c1109f924e084c", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "a485b1ec23a04252daf716bde35a344984e538b297d86edd85a2ad30ed3e3a0a", -"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "ce97ecddf443af2f99d4de0403ad83f4db1d9b3db9c0f58cc429d68165785212", +"TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "e1a715364829518c3fc261da5a7448a7114d86535c49317ca45b37d001f22039", +"TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_pin_passphrase": "c2b9192466e08ed3de679d295ebf7f121bd7ea1732b6f2509443b3d57eb47dc3", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_abort": "060ec0289a6768f695d065ca88ba425f29f212bdae5bc43e3d8818269520a596", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "7acaa2e3f003a0c4734ce1584ab6443d45056cc007141887a8217fe6fa68bced", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "90afe7129934f194869bb51c862bd372709cdee322295fe90ec3abfcb5a30767", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "1167f0ea6329304a17761e738bac5f7ade800e8e081cfec4a4ad1c9194ba7ff6", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "91fac23ec2cc55e49910285a55cc168f9942379235aeff684ecfed1ce8f624dc", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "e291c0fe81a55c0c74e8dc2bae308e35220af98e6b70ba90bfa7946ad632ae3d", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "14499d8ecfa9afcb6f9a39b482b213b7d7c4f955314b0b97caf333dfac268033", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares0-c2d2-850ffa77": "e291c0fe81a55c0c74e8dc2bae308e35220af98e6b70ba90bfa7946ad632ae3d", +"TR_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares1-c41d-ca9ddec8": "14499d8ecfa9afcb6f9a39b482b213b7d7c4f955314b0b97caf333dfac268033", +"TR_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "62804786a08c790627d2ce8dce8911f272a5b52753ed37ba36398d11825f24df", +"TR_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "52f2b80c7129f4d6771a6dfe3aa72ca07616760c9cb4919c12dd3b77b9983d7f", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_1of1": "0bbec62f191ad788aecd995d6946d620eab0dd2732fc767c405671e05c420926", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_abort": "060ec0289a6768f695d065ca88ba425f29f212bdae5bc43e3d8818269520a596", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_first_share": "41c240030299d067cb2ee3c217c9bee5a173d2990aa1924fddf96bca95d5d4d8", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_second_share": "3275916b81811499bf0f24327c38a0387f14b4aca6c1719b35c1a1108e4b1562", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_noabort": "d75d74707ac8d02db066102701ea4f73d453ad4ee5d6705ad2ec92874500fb0c", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_recover_with_pin_passphrase": "6e0643d6c87a9841824eb80a71c2c316aa4c0d0b74b17e9261f08af88c1b8256", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_same_share": "c850c79afe7fca84e6ac2bbf0eb4f93ee9e0310cb27e19ffb4cf770f6477c59a", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "7ec1a952dc4b33f1f80eb1638da2a5aed3eea3974a586b09c0fd3419df4e3392", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39-a50896b7": "5cff0e812daee535494ad66823f8e433719fde8566e48b95abae1a799d2bea07", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[0]": "5cf309947f17a97e87382240862858a4600bbc1ebe00326f363340334e41bd35", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[1]": "c01146bfdeb59403d0371dae737a91a8eb355cbbe947fbf3a86611c0b5b7e447", +"TR_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "1157aab76766aacc5ff0b00d3b50c06ae3d19a68579493b4472e0ca797fb76ed", +"TR_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "cbc9f644b50db2d65ec635d7022f70285378b6e8e5fdf9ebb24860278373096b", +"TR_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "6a31db8785559debab2a71fa23d99bea7d4ea96f2d5697e9d5cc5a36a7859030", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "13ac6fd79ff556585bcb4dbb559b098cd99dbe7cddb555ad19f4229f407b65b0", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "d0a0be09646bd8636d8b2929bb799c8a10d176e760413e7c18b02935cec206a5", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "e62013196fe9f126945d934bb4b76c9dfac74dd39c64cbd48e96450ad89833fa", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "5e1918354105bedad0574b9e1cd848c05346df72190cf9724f93c4ee65f51553", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "d0d91db7254b7b0806d976e534bcc3b6a2cc9b1df51894ae9dafba47a61c04a5", +"TR_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "72c6a1bf13f133bfcb8728758c11d9795f8046d6834db059e6a70100ce7c4ff9", "TR_reset_recovery-test_reset_bip39_t2.py::test_already_initialized": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "6b4f02ba25b4199d426a8238bb28fff6bfbeabe1f921352a19b3d651921a4b2b", -"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "ca712743e8937bc7db6265810964d20d292f8166315fd9c30c9a3b5ae82ab6c9", -"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "189b6effb1666f250bec4813028f8af24407c08e6be14287dca563db1c04afd6", -"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "b236293539ac37adb87fb07bfa6de5b4079ded32c437eb79f3fa58fd73a781bd", -"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "e3f1151b8480d47c00d10073fab067096f252a7982abe828f3208efb2aa6220b", -"TR_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "570bffec666f394ade20cee88712e081d624e20e7493794e7d7253ee316bdd92", -"TR_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "6a875da2416faf5a2b21f50fbe55ee28260d4f1053eb0fc44c446df773442e89", -"TR_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "77639c4c8d1f8419ca844d88e65e8256b8b793eac52e76b93cd05c9cbfcae3e2", -"TR_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "19492556256195acbab3865f663cb3127d1bfb8a7d27b2000b870d082f8575a3", -"TR_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "e79608b2335fba3da5b52a896e4ce0095acb5305688993617e09329b8220087b", -"TR_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "a573066d7bedd8318c412de713743bdf3ac50775a634c7ee55435fd37e3a2987", +"TR_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "d6af83c970e96cfabc5f00c7908c56394adf3b752a40887a08161527ac8e5eb0", +"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "320d9308b3dca80ca761a0ba81e7aa9ae7c258ab28ef126fc2bfdcfead69ac5b", +"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "a1b3aae11ff01deaeeb172c24c70c4f41d634496657be020ed95719cbb1f7836", +"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "35a9bdc4fa05691543ba83df37a31e715110338bd3e01ee185291a254d42258c", +"TR_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "7451ffdf25f6fe05106746d86ee8fe1af2d6de229404c6dab92f557fa50ca315", +"TR_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "91b5c342d0eaac82b0c8b80102f865835bd3d53fcb95f36f8e715ad22e1d7ce9", +"TR_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "72872e1cbf1f841df7cb9758eb56173f1424495f75f0b42fb64c2a5c98585076", +"TR_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "2b6362817bef8bbdc12b6bda58452897a932d0d08bb4e6e4e6c2c38ce4ddb955", +"TR_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "21298f777d828539ccefa19265de8ce9eee4125b2fa4a69448e3e3b79f3c38b1", +"TR_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "176b24699a01e3aae257ae97d096ddcfc0b53c72e7c0631c3209c99cb5db55ca", +"TR_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "7970dd8287c49858db96db6eb2d6eed071aebd7c686130396ec43fcc65a86ad5", "TR_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-0-rNaqKtKrMSwpwZSzRckPf-3321e5d1": "5ce82905f4a02d3ff8a2c731b8e6ad770d3e3ad29b089e3a490d2868a41ba8c5", "TR_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-1-rBKz5MC2iXdoS3XgnNSYm-fd75b415": "8633600c89710653f7a0534abc2f3b276116d754d99c0b6f4fcd32be08e343f5", "TR_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-1h-0-0-rJX2KwzaLJDyFhhtXKi3h-af5daf0f": "46f3c94167c31e26ae0dacf3e1f2a408061cc0bc24b030229e495194077f5097", @@ -1860,104 +1860,104 @@ "TR_stellar-test_stellar.py::test_sign_tx[timebounds-0-1575234180]": "769f0c5a338ce0cda686cf16be2dac99aa421c129d27ec0951d196c4d7fb6655", "TR_stellar-test_stellar.py::test_sign_tx[timebounds-461535181-0]": "f554096547fe95c21dd82a32817358628e115997ba789fa269f0fcd11b37cda7", "TR_stellar-test_stellar.py::test_sign_tx[timebounds-461535181-1575234180]": "ae15615f214c9a78205732a8003bf782cb512f83893e7b81856ce505d9d79d79", -"TR_test_authenticate_device.py::test_authenticate_device[!\\xf3\\xd4\\x0ec\\xc3\\x04\\xd01-b\\xeb\\x82-e4b4eb3a": "5bbde07a26ce37bd72d0c792c4dc9807c169c9fc731d1bcf30a72a5f2da7f602", -"TR_test_authenticate_device.py::test_authenticate_device[\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\-d824e03c": "5bbde07a26ce37bd72d0c792c4dc9807c169c9fc731d1bcf30a72a5f2da7f602", -"TR_test_authenticate_device.py::test_authenticate_device[]": "5bbde07a26ce37bd72d0c792c4dc9807c169c9fc731d1bcf30a72a5f2da7f602", -"TR_test_authenticate_device.py::test_authenticate_device[hello world]": "5bbde07a26ce37bd72d0c792c4dc9807c169c9fc731d1bcf30a72a5f2da7f602", -"TR_test_autolock.py::test_apply_auto_lock_delay": "cf93576944395035a45cf62f4b4b22387c595af7646e1863b07bdcc38ccfbb20", -"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[0]": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[1]": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[4194304]": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[536871]": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[9]": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[10]": "5b551998e871f4b9f04125ac7a611dd5de198e32f538b83e9f0a836586160f9a", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[123]": "a4e8d923051636485c83a88ee640fb8ed88a5ca8ff0a6d7801cbf9be7aad9e0c", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "41f074c233ef71ccbf7f86b7204d4ae651731f56ee9fa5e044bed1b3300c238a", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "99ab9324e9f4301093f70c6c2e13e200c1b012a0a99fc935583d5eceebf6308f", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[60]": "c5cf5b97dd7c15263ec70a377abd0864016532d7330b3a500b2ebb7a2c99bfd5", -"TR_test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "7688e81b6c319c61efb123ea22627a8503694733719fd6f7fb366ae9d031ff40", -"TR_test_autolock.py::test_autolock_cancels_ui": "8c6fda1a91ca25d622e51250895dce0fc438e9271453de3b82863da270833c40", -"TR_test_autolock.py::test_autolock_default_value": "35cae6c0efc6345d2211c4373a35931a2f539e203ebaa925f2ea8e4bb1bc38a3", -"TR_test_autolock.py::test_autolock_ignores_getaddress": "94b6e38aa1632c0b2a308f2ca207573cb1fcf12d1c155702135601124e9acaeb", -"TR_test_autolock.py::test_autolock_ignores_initialize": "94b6e38aa1632c0b2a308f2ca207573cb1fcf12d1c155702135601124e9acaeb", -"TR_test_basic.py::test_device_id_different": "683be74c1a68c2209b23964672b8a31a33874bf5abc2ccd3ae4caa57e252da81", +"TR_test_authenticate_device.py::test_authenticate_device[!\\xf3\\xd4\\x0ec\\xc3\\x04\\xd01-b\\xeb\\x82-e4b4eb3a": "9ad82d25e289d01433142a2949389e51a9c9045a46c5fd0f7b2d81c08231a82e", +"TR_test_authenticate_device.py::test_authenticate_device[\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\-d824e03c": "9ad82d25e289d01433142a2949389e51a9c9045a46c5fd0f7b2d81c08231a82e", +"TR_test_authenticate_device.py::test_authenticate_device[]": "9ad82d25e289d01433142a2949389e51a9c9045a46c5fd0f7b2d81c08231a82e", +"TR_test_authenticate_device.py::test_authenticate_device[hello world]": "9ad82d25e289d01433142a2949389e51a9c9045a46c5fd0f7b2d81c08231a82e", +"TR_test_autolock.py::test_apply_auto_lock_delay": "f74f7417dd27279b9c2db47f5d2049c3e10e5b45aeb853fed0ae01da85373a7e", +"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[0]": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[1]": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[4194304]": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[536871]": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_autolock.py::test_apply_auto_lock_delay_out_of_range[9]": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[10]": "5b9a1f82cd9866d3e0ba3b37e65981396d4e7fffa382093ab732d28d9e4440da", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[123]": "e6e4a741f2bb5657deff60936d895445829a1bc3ac13494bbe634a88d123431f", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "46276d5e4c4ff353da50a1a7358c48321af4c2fbdfe2e97372aa862481cb85e4", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "41ae39e0bd2e5c1989f409fc17f5805a7e3cc26a6d25f2b20efdea81f6e24961", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[60]": "1731db71cc1371e3ff3543bc7c4506ab4731905c6f82cd49737962d2b578586c", +"TR_test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "77cce180f4325557e96d54c39e460c15acae1579b7f642f7dbf637db8c685e78", +"TR_test_autolock.py::test_autolock_cancels_ui": "1372af21ad2b3bde2a150bc877a8a44be21583631c6edf6c492064193281c24d", +"TR_test_autolock.py::test_autolock_default_value": "aac9685d90303ec5a167d80a872110efd45dfbd71048754b0c2a9e0dc0db708d", +"TR_test_autolock.py::test_autolock_ignores_getaddress": "4b0bddb201433b29a1e04816070bddceba711da26add6362781defa16a504cf2", +"TR_test_autolock.py::test_autolock_ignores_initialize": "4b0bddb201433b29a1e04816070bddceba711da26add6362781defa16a504cf2", +"TR_test_basic.py::test_device_id_different": "658cea7ce4d82895e8b137e00ba9c3a06f4bc5301696ef497e30a7774ebee8ed", "TR_test_basic.py::test_device_id_same": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_basic.py::test_features": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_basic.py::test_ping": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_busy_state.py::test_busy_expiry": "0cff54da20c3c70d5b5de75a45fc265b29683fe322ad498f947656118691eeca", -"TR_test_busy_state.py::test_busy_state": "977b10875327ff6165c83fad2d9d55b8dec638ae96428bd0c672e439cbabfc7f", +"TR_test_busy_state.py::test_busy_state": "b3efd704b640ffbd4cad56d4702e80a59b92350a7b640753eed0e7ce5602d110", "TR_test_cancel.py::test_cancel_message_via_cancel[message0]": "41b9fc37f230520f9d94df226ff8433822e56ea4276a31ef99171a774e477045", "TR_test_cancel.py::test_cancel_message_via_cancel[message1]": "41b9fc37f230520f9d94df226ff8433822e56ea4276a31ef99171a774e477045", "TR_test_cancel.py::test_cancel_message_via_initialize[message0]": "41b9fc37f230520f9d94df226ff8433822e56ea4276a31ef99171a774e477045", "TR_test_cancel.py::test_cancel_message_via_initialize[message1]": "41b9fc37f230520f9d94df226ff8433822e56ea4276a31ef99171a774e477045", "TR_test_cancel.py::test_cancel_on_paginated": "79f025ebfbdc5a81dae1385b17d2d802280063479c8480fa5281734fe1542385", -"TR_test_debuglink.py::test_softlock_instability": "1ce6f0c2814df27dbfa3944c4a672930744ea24ed7c75cdd9068570af7382d87", +"TR_test_debuglink.py::test_softlock_instability": "37ab10a316f8a8b0e748590f0b0659547e295b9402defa5dd8fe19973aa185b9", "TR_test_firmware_hash.py::test_firmware_hash_emu": "5dde95f0c09df69cdb1e6017ff3269781e499f2c1894db677e1d06971e45d0a1", "TR_test_firmware_hash.py::test_firmware_hash_hw": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_good": "de35f30577c01a7a196f92283911213933027572067dfaf918cf84aa8ce517dc", -"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_with_long_label": "3b77414a4f279309fcb866efa946f2b031099c55268e1e716655d169a03ed392", +"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_good": "633346cfb7dad97e9313c7e40a4ef24de43dc9debb8a133a646c8a3be73b8f6c", +"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_with_long_label": "5805cddcc3b50b90944ef91b44cedf0dcf70ffc8a4aa644071dc96c7d807deea", "TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_with_notification": "5f2cecf0c5bf0e7c8743a3a9f241149abf4a09431d21b801c6989c171208aa6e", -"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_wrong_size": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_msg_applysettings.py::test_apply_homescreen_tr_upload_jpeg_fail": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_msg_applysettings.py::test_apply_homescreen_tr_upload_t1_fail": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_msg_applysettings.py::test_apply_settings": "e7857bc570a8b029795d6d04782580f4402dfb7fac9a8579597599b33fd7d12f", -"TR_test_msg_applysettings.py::test_apply_settings_passphrase": "59383a2f42beae71e56133ccd06e3ac877a83e8edf9ff21c78978dfba0df14ff", +"TR_test_msg_applysettings.py::test_apply_homescreen_tr_toif_wrong_size": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_msg_applysettings.py::test_apply_homescreen_tr_upload_jpeg_fail": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_msg_applysettings.py::test_apply_homescreen_tr_upload_t1_fail": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_msg_applysettings.py::test_apply_settings": "706fb9d11696426598d29e122c73757388f85ddc950dccc5d06b4e1f3a962a59", +"TR_test_msg_applysettings.py::test_apply_settings_passphrase": "c41b08d7df422ee925c35970f1ca621035673eb3e08c865fa4d3afc77363cab5", "TR_test_msg_applysettings.py::test_apply_settings_passphrase_on_device": "caf5f9733414c8edd4f13800250d7d3d0d3a4012844b826ed314db5c608508eb", -"TR_test_msg_applysettings.py::test_apply_settings_rotation": "ee63171d986f52e01f1e7dcd8ac6c51fd7e6285182bb1ddca834f5bc394d0435", -"TR_test_msg_applysettings.py::test_experimental_features": "ff01a9d9dafdb8f92372f414b653cc25418b8ad831ed1c9792429f2b515f28c0", +"TR_test_msg_applysettings.py::test_apply_settings_rotation": "b7bfea9fcc42a15b3e8a78f980bf656f46a0f73d41457b9a0be5a85c87aac11a", +"TR_test_msg_applysettings.py::test_experimental_features": "9c0faf68267426b39258e546df25ea19b81a0fd26a6577efd563b59bc15baaf2", "TR_test_msg_applysettings.py::test_label_too_long": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_msg_applysettings.py::test_safety_checks": "a34a867f1d58b2a29c5d1724378f269ce699b380fd2206cc8cc4c06bcb76565d", -"TR_test_msg_backup_device.py::test_backup_bip39": "a765ee7fbf3e763d7fbfee248d7afe159e1986975e1d94635a50631828e6695b", +"TR_test_msg_backup_device.py::test_backup_bip39": "d8778ebc414b075141007dd18f9ecf652338947187bb2f9770101ccbfa56e233", "TR_test_msg_backup_device.py::test_backup_slip39_advanced[click_info]": "b6e34f1ccd560f41f3b33b4755e900089797898a66c8fdfd28d9f61274e873f4", -"TR_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "d03ec76ae176f4a7dfd3f4df83fdecd8dfd1ef29bec85e5c21e3ec97937b970b", +"TR_test_msg_backup_device.py::test_backup_slip39_advanced[no_click_info]": "2292ff8faec75a418fcee48235a2333843fe1458b4e3ec0af673b5153574152b", "TR_test_msg_backup_device.py::test_backup_slip39_basic[click_info]": "b6e34f1ccd560f41f3b33b4755e900089797898a66c8fdfd28d9f61274e873f4", -"TR_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "9df6f18e33ff9412f0817dc2f0da2b96b1df4fed0252fbe09e1ee0f521dcba47", +"TR_test_msg_backup_device.py::test_backup_slip39_basic[no_click_info]": "fb35ceaf56b93785eb465f5f6b195a4db7fcb0db1cc6e4be32691f5a1a096828", "TR_test_msg_backup_device.py::test_interrupt_backup_fails": "1139f673473fbf725b2ee52d4965917c6a72676e69b073707f40b556a48f4b79", "TR_test_msg_backup_device.py::test_no_backup_fails": "60c13acb4f8e40ee32f9d01415cbcbd75ffcd6a4015003d93562e84c3901a62f", "TR_test_msg_backup_device.py::test_no_backup_show_entropy_fails": "f49c8d846c2d56a575f0ad49463845ba641b02656783e4fcfc67d74e8fa671dd", -"TR_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "90fd42f84ed0cd8a063233b4374b31b8a5a5b5cac790279eb99c26d7665e61b8", -"TR_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "fba90617dfb73988372c4e0791b64806f772378a1976cb77b8ea44f6753bf450", -"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "13823d892a0783ce247b3c3a807268475fe5598d7532a3d7255d33367163f7fb", -"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "49ad6f7ad942fcc37ed219d2032de24eaab868eb255e855b72c82afd4522957c", -"TR_test_msg_changepin_t2.py::test_change_failed": "80ada7c8760bcb529fdb59f089b16c0534d0cf1d28ae7cfd2b8b8187ea78ba29", -"TR_test_msg_changepin_t2.py::test_change_invalid_current": "db8592d6da407ce46d41a2507ae9ad405d7476302a921b50049a3f89489fc98b", -"TR_test_msg_changepin_t2.py::test_change_pin": "2d0eb7c4b71f5e888917e46dbb6f22abf3f2d5015da07ad1e82aaf5fc8b1211e", -"TR_test_msg_changepin_t2.py::test_remove_pin": "e2c941bc00679380380e9264481efcfb2cfa825829cd1b043d75c47a995d8fee", -"TR_test_msg_changepin_t2.py::test_set_failed": "4d29ed842ceb69edae7dc48b02d6ce17867e9b4301b91a2dac27b5822a3adbfb", -"TR_test_msg_changepin_t2.py::test_set_pin": "61e4e39b7e0c1b8a7e08335a8b951abdfcfbbf1469d77e6decad1254b7bba650", +"TR_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "ea276d47a239b8389071eeed9fc51e962340f808ef642343fb30db48c8ff699d", +"TR_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "e64bac244c8055bdcc0a5e63a7a68643a7b21464ed82ee8a6679f01b907d210d", +"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "d9304c9e8f08c28b5d34e4e3575850febe0b6540e526a4a6056a6af16f5c9ec8", +"TR_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "bb13ef4939949aee15b75a3c6fe89912201fa679ec094478ae3c7bc61a207c36", +"TR_test_msg_changepin_t2.py::test_change_failed": "8f37cc04900aa176242ad717b04a87ab6b89c2cfad79e803eceb09c208042a35", +"TR_test_msg_changepin_t2.py::test_change_invalid_current": "d9c649eb007f5d7dd65983373c88002e271377680a3c17260c283166bbd965e6", +"TR_test_msg_changepin_t2.py::test_change_pin": "33264f2e2b0512ef216336ccadcc88cf2ec44a29b1f07b08d2e222cecab5dc25", +"TR_test_msg_changepin_t2.py::test_remove_pin": "9658757510c92d40b545f09fe728778cd10f9f16e89de9936b1d62f2f3dea446", +"TR_test_msg_changepin_t2.py::test_set_failed": "36a5cde2473ea82c5031826ea4c4971043850332ab9eca1020e1ea068e97e3f3", +"TR_test_msg_changepin_t2.py::test_set_pin": "5f29f663413217c1fcf3545ebdc67a0a5c76136f247ca14bd5afc204eb198508", "TR_test_msg_loaddevice.py::test_load_device_1": "8423fca7398f0399843c8f28ae8422bc4f7154420e26bc2fe1b8933eb2d1d4cf", -"TR_test_msg_loaddevice.py::test_load_device_2": "b7b56fdb5978b4ab792f3e05778b0f72dfff68ce8128b718fa727555c323ec03", +"TR_test_msg_loaddevice.py::test_load_device_2": "8ce5629b50cfa80a5e9ddb2fa2125c50c8ea9aa7ca88d4609e34032aac4401c0", "TR_test_msg_loaddevice.py::test_load_device_slip39_advanced": "8423fca7398f0399843c8f28ae8422bc4f7154420e26bc2fe1b8933eb2d1d4cf", "TR_test_msg_loaddevice.py::test_load_device_slip39_basic": "8423fca7398f0399843c8f28ae8422bc4f7154420e26bc2fe1b8933eb2d1d4cf", -"TR_test_msg_loaddevice.py::test_load_device_utf": "f415a91b67787a265270fecbb01f3bb75daf6e72638f25a6f522ca4c66e153b9", +"TR_test_msg_loaddevice.py::test_load_device_utf": "50f2ea17c09003688f51fc9c5bdffa5530f77a33b5de78f07c5dea1938539457", "TR_test_msg_ping.py::test_ping": "4ffbed72e7ed7fbab85f830952200adf7758af81b658b56de4672344120456a6", -"TR_test_msg_wipedevice.py::test_autolock_not_retained": "0d6ac20e093c4eceb09ce0a53be10b36daa80fa72878089c0c12b5abc0949ead", -"TR_test_msg_wipedevice.py::test_wipe_device": "683be74c1a68c2209b23964672b8a31a33874bf5abc2ccd3ae4caa57e252da81", +"TR_test_msg_wipedevice.py::test_autolock_not_retained": "5a0ed602623a079a0f5e20f69f0b8f4f2908e9c9c081aea98ba54f5d45761e0b", +"TR_test_msg_wipedevice.py::test_wipe_device": "658cea7ce4d82895e8b137e00ba9c3a06f4bc5301696ef497e30a7774ebee8ed", "TR_test_passphrase_slip39_advanced.py::test_128bit_passphrase": "0c1d2e8f1e4b43ee5071cf1d6e31c134297ba8315f1f4568765875438e7720f5", "TR_test_passphrase_slip39_advanced.py::test_256bit_passphrase": "0c1d2e8f1e4b43ee5071cf1d6e31c134297ba8315f1f4568765875438e7720f5", "TR_test_passphrase_slip39_basic.py::test_2of5_passphrase": "ed4af0c4901c1f682a4ab2f7f741721265a0829d6eac91b3696f6c2c58eb8b0d", "TR_test_passphrase_slip39_basic.py::test_3of6_passphrase": "ed4af0c4901c1f682a4ab2f7f741721265a0829d6eac91b3696f6c2c58eb8b0d", -"TR_test_pin.py::test_correct_pin": "674ca13c50b342d80ca47ed49bcfaa846bb07db2ba30d6e1414c96db8375457b", -"TR_test_pin.py::test_exponential_backoff_t2": "9a97113901505dba735026399f2c0874ae965259ee719a344db5b994bbb7e625", -"TR_test_pin.py::test_incorrect_pin_t2": "b83df82840ebe780c4abe92c4a7cee106e2bf6ed308f4f79dfa9ea7eb0a1397f", +"TR_test_pin.py::test_correct_pin": "c440a218850ec7ec8a4f8ee218d2c28251d12aa455f4726202d8bde897b0a555", +"TR_test_pin.py::test_exponential_backoff_t2": "77f8c1cc31a378f1b9db216042d74a2317b0d8b7013db9024f183fda9aec18c6", +"TR_test_pin.py::test_incorrect_pin_t2": "9bd79b46c7728319f808dd9a22ea544476fe95ce3935cff993339259c652d4d8", "TR_test_pin.py::test_no_protection": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_test_protection_levels.py::test_apply_settings": "76d2aeb1881d7678828804f5d8ce09b106abd02272bdd5c1f41c58d86690c1be", -"TR_test_protection_levels.py::test_change_pin_t2": "d7d219091c2ed484c8215304b6e247a34e1e52fc998da21823e75a2bb149bfcd", -"TR_test_protection_levels.py::test_get_address": "ed15f789f1902f5805f37baaf21a1e36f664426358196bd61bd7b477a6bf11ee", -"TR_test_protection_levels.py::test_get_entropy": "5b514ec018caf540032adb9c31246178550f1ef99f652cc9be5faf579b06182c", -"TR_test_protection_levels.py::test_get_public_key": "ed15f789f1902f5805f37baaf21a1e36f664426358196bd61bd7b477a6bf11ee", -"TR_test_protection_levels.py::test_initialize": "624bc73ef0907f9b7dbdd8d2177bddf9440f5d759c24f3eaf01c0f93a4980f6f", +"TR_test_protection_levels.py::test_apply_settings": "b1847b253dfc1e5b42128b73e3c7b87d33ae3bbb85d10c75285ed0bdfdee2fe6", +"TR_test_protection_levels.py::test_change_pin_t2": "aaec729d4870269b59979f778350bd8775c36712c6f7c4b84e0ef971ef69a2ee", +"TR_test_protection_levels.py::test_get_address": "c0cfa80fd221f4fe608cfe1a6241b0990cf1c19af21336977e36249c0a3a6370", +"TR_test_protection_levels.py::test_get_entropy": "588c753fd2e1611b1bd6432ac45bccbb7e220da0c62ec3df87aee19852ad80e9", +"TR_test_protection_levels.py::test_get_public_key": "c0cfa80fd221f4fe608cfe1a6241b0990cf1c19af21336977e36249c0a3a6370", +"TR_test_protection_levels.py::test_initialize": "f36666e0d59c24cfc5b6d1de10708564f392e9453de253fba366f142cfb76d13", "TR_test_protection_levels.py::test_passphrase_cached": "5f2c9d15b92ca01e8c682408ddb8ec025aaeb54346a8c13d3bb9fafb682ec5df", -"TR_test_protection_levels.py::test_passphrase_reporting[False]": "567264a37a2fd55ae2e90761260720f3b65a01cef700eb29143ede1804ca4cd2", -"TR_test_protection_levels.py::test_passphrase_reporting[True]": "e5fd5ccf18e35cc8db539c4630c8df287484d437d9a42ff477b4cea458a1ee8b", +"TR_test_protection_levels.py::test_passphrase_reporting[False]": "1fe0732e63ada33b976b58b2a373f5dfff1bd93f339465682cbf34c3ac478216", +"TR_test_protection_levels.py::test_passphrase_reporting[True]": "7dae65c1512ed8de974a84eca83ba46dd158cda0177e149a4d49cf71e9e4c8d7", "TR_test_protection_levels.py::test_ping": "4ffbed72e7ed7fbab85f830952200adf7758af81b658b56de4672344120456a6", -"TR_test_protection_levels.py::test_sign_message": "58ab34aa20756d49c5d5dce4e88c06149e19880c9ea04537bf586cee7ccd3583", -"TR_test_protection_levels.py::test_signtx": "4bce40c49438c144b0af87c7ba66cbf2883455f0d5ce1ad7b759384a77f7564d", -"TR_test_protection_levels.py::test_unlocked": "d3c3c239d8fcab02322f261a7786bbffa34812e84b773290b31eeaeb74d3f66b", -"TR_test_protection_levels.py::test_verify_message_t2": "24ff1c7867ea06030b08400f060ffd3c85ac62b1e6c5095ce6c41a66d5fbc286", -"TR_test_protection_levels.py::test_wipe_device": "ac946eafbd7c1942bef1349609d47eaaf7d23a995a9d539446ea7cd2c01d7267", +"TR_test_protection_levels.py::test_sign_message": "78045e2ab852caab13315ec3be11b0c22d8d82f479a57a6bcb559ebe9eb59efd", +"TR_test_protection_levels.py::test_signtx": "f5debfe54ee83b30255558c63a3b6ffda7685c9b635ee6d417227530e99c78bb", +"TR_test_protection_levels.py::test_unlocked": "80c0b5839cea918213e6fa3c3b5992d178b29c225baf86c32e5ce9172ab5accf", +"TR_test_protection_levels.py::test_verify_message_t2": "a609a2165006f27f6685d7ecb866283718e3dcf57952dd3df77d8aa68d4e3186", +"TR_test_protection_levels.py::test_wipe_device": "db3316bcab137d3bfd3b7a38b8f9abecb23605f7574544ccee303d6756fed8d9", "TR_test_session.py::test_cannot_resume_ended_session": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", -"TR_test_session.py::test_clear_session": "88a216adced296d6623cf6b3d59b1844a6e19323723a453c2d6895a295ebd273", +"TR_test_session.py::test_clear_session": "4545535c96d81601476889cb649b84c4efc9888053b5bc22902381b2ddbdb0e5", "TR_test_session.py::test_derive_cardano_empty_session": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_session.py::test_derive_cardano_running_session": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_session.py::test_end_session": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", @@ -1969,7 +1969,7 @@ "TR_test_session_id_and_passphrase.py::test_multiple_passphrases": "de4cd16001555d8cb75a00f1d1e3f78a0c7613bf5ccc3bdde28f9780a3c0c6d8", "TR_test_session_id_and_passphrase.py::test_multiple_sessions": "8c801bd0142e5c1ad4aad50b34c7debb1b8f17a2e0a87eb7f95531b9fd15e095", "TR_test_session_id_and_passphrase.py::test_passphrase_ack_mismatch": "b70d9d2aa7a8ace3251763c1d2fcb53dd8c741b7520d717398df8f7ff8ac9128", -"TR_test_session_id_and_passphrase.py::test_passphrase_always_on_device": "018cc66078474014af4458aa6d68102dad132e40e98d7b0af744ea175f155a8e", +"TR_test_session_id_and_passphrase.py::test_passphrase_always_on_device": "ac3be5842712eb2d53b443d6b1d5a1189e481f94ba124e04ca75c61ad199473d", "TR_test_session_id_and_passphrase.py::test_passphrase_length": "221e1db4c2a92b5b7fa232c0bd2ecec16365d83b544b22cc47526e59a0d9321f", "TR_test_session_id_and_passphrase.py::test_passphrase_missing": "5f2c9d15b92ca01e8c682408ddb8ec025aaeb54346a8c13d3bb9fafb682ec5df", "TR_test_session_id_and_passphrase.py::test_passphrase_on_device": "033d6c8d27b79461137aa78d9a89acea049f52539bc0d898243e392514e36ff2", @@ -2009,15 +2009,15 @@ }, "TT": { "click_tests": { -"TT_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "fc0bdee925f173d24ef4acd6a0d4759503f01474557af787a6831dcd393c4f14", -"TT_test_autolock.py::test_autolock_does_not_interrupt_signing": "010ddc5aeb8eb1c017fed896b0f6cbd9f745b6ff32e16978f87ad7a60367c814", -"TT_test_autolock.py::test_autolock_interrupts_passphrase": "e945c7c5157677e445c541991db5674d745d4042e964bb7fa235f25c40a7202d", -"TT_test_autolock.py::test_autolock_interrupts_signing": "f8035d4feced430e4900b87b6699848ea976d12925d4d094e4790eedc5c7dda1", -"TT_test_autolock.py::test_autolock_passphrase_keyboard": "26019dccbae363a0e46431fb5bff16cc82188452ea405d5ce3c106fe248d0172", -"TT_test_autolock.py::test_dryrun_enter_word_slowly": "b8fa932373296e1cc9937333913b14fb57e0709d162a8b1ebffa8bc5fca9efb9", -"TT_test_autolock.py::test_dryrun_locks_at_number_of_words": "ed981291c3558a60ea3de1dad36d94cdcbabdd25ddf8e054648de02e0e1ca406", -"TT_test_autolock.py::test_dryrun_locks_at_word_entry": "0f6f8946a99ad3a9b1987aae135565823c302e5ee67ff7315830b5a8defbe584", -"TT_test_lock.py::test_hold_to_lock": "35b70cc4d6e851a77676bf08de9d6779ed2d8a00fcb9b3268db0114acc55dd8e", +"TT_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "7af8c05b7988cfe1d7cf9bc5d50ac67e2b732c32d28ab7036961f6be5cb062e4", +"TT_test_autolock.py::test_autolock_does_not_interrupt_signing": "dad22e16ceeeaf07811ce9968c8b4c55235d1594f1891e33daa4f2de7d668f2b", +"TT_test_autolock.py::test_autolock_interrupts_passphrase": "679b31036355620e3cce93a214e371b19db503ecd0d9b23765a0cea109f9eef2", +"TT_test_autolock.py::test_autolock_interrupts_signing": "a22c666a77b97de8a95cf43e87e9f9b632627f07099988964781c497795e69e2", +"TT_test_autolock.py::test_autolock_passphrase_keyboard": "1c017178b42ab6e0aa011e26403e5d3361648fef95354abee8820b92f196bad3", +"TT_test_autolock.py::test_dryrun_enter_word_slowly": "14e01ad290726b5a5314eeac164555906436ae9e9f3ae8d5d89cc2f6156cb4c3", +"TT_test_autolock.py::test_dryrun_locks_at_number_of_words": "808fa669aba9edf9b9f210703ec0d5ae7323043ffe1936f49586a01a8df3b672", +"TT_test_autolock.py::test_dryrun_locks_at_word_entry": "8d351542bfc92803d0a11c06f9a2e586a0cf47bf0f777e9040ae5ba9c763256c", +"TT_test_lock.py::test_hold_to_lock": "2c2157587d8e8b59c785374056d6be5e7405f4dd4015b84b1d9a2155ded97b3b", "TT_test_passphrase_tt.py::test_cycle_through_last_character": "2a8d54c8014cc0c1bf46c0e4b58d6a002009b62aa8b92db663f88af0ad2f5e19", "TT_test_passphrase_tt.py::test_passphrase_click_same_button_many_times": "6a579067b4395a260d173e78643b67ac701304ea833a112cb2da1bce94cbb102", "TT_test_passphrase_tt.py::test_passphrase_delete": "6f9fd790c360ea8caa60a183f39d6515ce66493786f71611988f20b6fc5af86d", @@ -2033,25 +2033,25 @@ "TT_test_passphrase_tt.py::test_passphrase_loop_all_characters": "82ff267d6ec0d48d8a1e25d1e77e598f563449dbff75fca7f2820dc1409fa453", "TT_test_passphrase_tt.py::test_passphrase_prompt_disappears": "12a0d2dfe50c122326bd7ab6af7dd32008943091757ef6f5e9122dd721414987", "TT_test_pin.py::test_pin_cancel": "05f5f819be61fec8c7c4341fd23c1bccf78cff93f05d573dd4f528bb0f1edbf5", -"TT_test_pin.py::test_pin_change": "2e4cf9eae426b1ba6617f771714ebc21bba641e28812a6b639b6940d74ae75c5", -"TT_test_pin.py::test_pin_delete_hold": "92d3b028c6b4651e1c498a2ffffbc7bc01c348caff02bf3d74f3e114dd13ef96", -"TT_test_pin.py::test_pin_incorrect": "064914e7b61c9af14a3cffc2fa2d4c43a0a751c3b4091aac97e0199763e842aa", -"TT_test_pin.py::test_pin_long": "8526208658adbe3bb1cb37e0cb48a65fa10c513bf15af5f864821391670d5de9", -"TT_test_pin.py::test_pin_long_delete": "7a2e17d73c594674d37aeb5f4ba77a520792295be79872e98926542d8a9ecd10", -"TT_test_pin.py::test_pin_longer_than_max": "8a39e50825ad2b2a024364cf8df9064956563139f2b93eb23bfb4cb148f9a3ff", -"TT_test_pin.py::test_pin_same_as_wipe_code": "e2f3fd3aeda93e4974df1227c2270716ff72cf5d38cc6626b3b1e0b2aade9590", -"TT_test_pin.py::test_pin_setup": "8ed48e3beabb14f964bea53bea1a0e7d05b5ed1eea82f9e8b9360b5742886acc", +"TT_test_pin.py::test_pin_change": "199d5ccb7760efcaafcc1156274c66d983c4b65c5bac4ebc27b14a060bd1ba4c", +"TT_test_pin.py::test_pin_delete_hold": "8fc7930af448875005381482abd1751a980fbb2606d8b764b33bfb3cb1fad483", +"TT_test_pin.py::test_pin_incorrect": "27f7eea0673208eddadf462de2da644675c71ab7a96858b3eda9d1299579cd47", +"TT_test_pin.py::test_pin_long": "42186e29bbae2d52ca2f7616b7812502f485c4677f458f96c2440c8f21b14dff", +"TT_test_pin.py::test_pin_long_delete": "53dced36adc89e59dd0fd1b885d1a1508d5988e77870f12625bc594302067180", +"TT_test_pin.py::test_pin_longer_than_max": "d658aae87ed1cb468e5cb995f4cce7555591042d411f650377e8544c619dbfd3", +"TT_test_pin.py::test_pin_same_as_wipe_code": "60c044453718299827a95447eb053427f6357f375472a240c33a7b6468e82b4f", +"TT_test_pin.py::test_pin_setup": "fb2496fc68103e9a9d0a2778bad867d5ff23ea555edf12ab9d655dcd46695557", "TT_test_pin.py::test_pin_setup_mismatch": "663430644b26f4493fc3ecc03a2bbce5cecda7b6ccb5be5a8a4c0ace239f6dd5", -"TT_test_pin.py::test_pin_short": "d91ec8c948d2514789931d5d8b3bda591ba2d27da26732925edfaa91c85c9507", -"TT_test_pin.py::test_wipe_code_same_as_pin": "e4bdd9c4ca4bfacdf7895cb3d3caf8dc7f0ba558035458591d1abe681e19ff35", -"TT_test_pin.py::test_wipe_code_setup": "359f9a51352738bf73e2148e258f2bd0fc98e3eebdd972e71ce2850171158ccf", -"TT_test_recovery.py::test_recovery_bip39": "6d344e1f5d73a3dcb312d1fcdf222b58a21ce524c89a72afebc318afde7aace1", -"TT_test_recovery.py::test_recovery_slip39_basic": "f4ef920e753c6b5cf4d1e50fe5d37fb1504d3136b4ec9f405d7ae694d1cde343", -"TT_test_reset_bip39.py::test_reset_bip39": "e9e16176c408e1bc55de4a22b8d0954be6863e6aac13a7637751ee7d66a0da6c", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "cb5f677f5dbfff7c2de1156c80adc986cb642dbf34e386a7152a395334888602", -"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "89e3cee846c9b6f4fa7d02997b579d2407aa0f890d38a2ed80b0707bb4349853", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "6034bee4750501ad311b450198489a61abd7dcc423bc05915edbf20c5bd9d499", -"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "6f5f8cfeaedac16dd51ba300ead1bef01f86d2a5b60dac7336c3f777e117e7de" +"TT_test_pin.py::test_pin_short": "0f7d5bd47f9f61133fbba9a1db1486e48dfd53d71d1b7a7d4a22d7062e7ade52", +"TT_test_pin.py::test_wipe_code_same_as_pin": "b558003f3030a5a05db685d32c44ecac0ee9adb43c9fe005b2181ad95e0f08e9", +"TT_test_pin.py::test_wipe_code_setup": "4e82e1a6b33bdfa2b468de4f69a8511ce3be86fb5758005c36644b3960c010f2", +"TT_test_recovery.py::test_recovery_bip39": "13e3f91705759b0c3c5cba1038527daf1f20a1dd3b14c9ac6cd733f492409991", +"TT_test_recovery.py::test_recovery_slip39_basic": "bc5100676562b6089472c293e02ca3af36bebf9a695787061d2be39c08273b3b", +"TT_test_reset_bip39.py::test_reset_bip39": "c9fbd319d9129d9c2ea6a385f6eed4dc8346e09e9eec0438a183fe3f491f05b9", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[16of16]": "8b1f872d8d1d1ef711b977c7775c93f120b59cbeba176913aac0cf7e13d24076", +"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced[2of2]": "c25c0073737e3c5ee5218d992e6c2e22b3c8e1a26ad91ce01725339dadffac6d", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[16of16]": "8e5e4df27c3066c5c6970f5809cfedf4e86bfd718cc337a416ed0165fcd63d90", +"TT_test_reset_slip39_basic.py::test_reset_slip39_basic[1of1]": "014d332f511ccb5500828380e194c6d7ef22d6ea5af947b139e8049b442fd9c2" }, "device_tests": { "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "fa3d667e1439c9c2a475fff22d94e330c14697ff8dda4bcdded775492cb40a90", @@ -2069,8 +2069,8 @@ "TT_bitcoin-test_authorize_coinjoin.py::test_get_address": "f4de0499dac628067619c6081d4ebdba0ad196af7bf9662c5387386eba9e9729", "TT_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "38a7eac0cf0be45770fb28da9c2f74bac6404e85f2a3801acdd4b0107d99f2ff", "TT_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "eae73c583d41d62bcd926f7f99866e6bd2f8d52f7935713fff7564a1be662b56", -"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "6465cef55d4bbceb1e86e93919e657d2474192d5ea065b40fbced8c176af0588", -"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "6465cef55d4bbceb1e86e93919e657d2474192d5ea065b40fbced8c176af0588", +"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx[False]": "6b53c67261c0a0f3d81e39434a1bf5ce6141cce6342cc263137bf651fc8abebe", +"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx[True]": "6b53c67261c0a0f3d81e39434a1bf5ce6141cce6342cc263137bf651fc8abebe", "TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "e0923b25095c90df9e0e73361cd6a68cadc32963917d12f646bf592c9222d306", "TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx_migration": "bae8655b0eb398a327d4dec9b188b074d7e31822d1d5fed37a7d2db28663c2ac", "TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "f544d73cdf88804e8be2667a976d9501da174c678207e72372f3e1a0cf410381", @@ -3085,56 +3085,56 @@ "TT_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[pin_protection-True]": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[u2f_counter-1]": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "5f0c054c9803e8be2d6fe1333f9fb7f291cd64a6830ec379482b35d2a5d51df2", -"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "c4f2c58e82befcbb9d37ebc8742f316bf6e57773084c50900106ceeddf91c123", -"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "e91a12d931a0fb8b820c54e2a2e13a1810194eb0d6be6468767a57558bb39e03", +"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "d5d9ba11838ff35313ee1fa5ed9c1be0850e2a043804eaf9987c788b9301dffd", +"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "e5e9025f2c3744837a76d6f61e0187c33818bba69878ccf153d3286e632207c5", +"TT_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "0c4d23cc4e5a62ff75f53c0ce77a5507af3f122a6e1f4fd6e15514dbc0f4c7df", "TT_reset_recovery-test_recovery_bip39_dryrun.py::test_uninitialized": "001377ce61dcd189e6a9d17e20dcd71130e951dc3314b40ff26f816bd9355bdd", "TT_reset_recovery-test_recovery_bip39_t2.py::test_already_initialized": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "a31cd1f3e37cd44f52922a5c8f4d5a9b31656ce5e9eace366ee5d6be7091a211", -"TT_reset_recovery-test_recovery_bip39_t2.py::test_tt_pin_passphrase": "3c6ee7c2b8c545c9fcf7a1eb7ce9f0d8fa4029446a5224b93230f0173740bdea", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_abort": "858876d8169c44260ae2934b87b1acf646bcc3ce1e6f7950937a4bd073bb4715", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "d0820924fa12486edd435ec05f5e845f7165a64ed1f8d1132c3ffa9e4bbc06f7", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "75cae4b352dbaf04b6caeb1ec987c45c7b0e5d189b75d8ba05e334f6c9e3045b", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "fee2f429d4e99aba5d45003a9d58f57b86d4ec2a04a0a995914c62f12398293f", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "339451bcb40803b606296294f1d463b0d342c82b78f0aa3b5c5f40c00058f370", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "5ffd13685c6e514bd0e804937d0e9f39056e7f829bcab5284103f88d3c1ac488", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "9a24de4708ee2d38790c2489d67b0d4d222978b74b3e2f2340434c84887711fd", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares0-c2d2-850ffa77": "ef1f3f0d1fc85e41c9c02a0c048a14ed6256d308b90a9ccd3d40a284a94f1e4a", -"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares1-c41d-ca9ddec8": "802b57885cad8331a90f44375bd08dc82858356d0d6d2dd0482e37fbc60cd831", -"TT_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "6c20fcc4101c1cab4738817b7c06184c15172572b089fa5cb8828c47c263203d", -"TT_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "4b217d586ba1731ad70c41e3f1d8c76b553285f012902fbfbf6612c4a3d513f1", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_1of1": "162054c186639df3ad2688d57ba98fd69504233a5541de80c199d93b69ce832a", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_abort": "858876d8169c44260ae2934b87b1acf646bcc3ce1e6f7950937a4bd073bb4715", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_first_share": "2f058d9188bb814da1cdae675caf47b74d903c5a7e0444a644ec220daf0d45d6", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_second_share": "df02eb39a5e37cb42e54b9b8ab43c73c165156e40e56c8e58ac3e22da9f80bf0", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_noabort": "8709571d61a9fe62de53c5b7777886165723fab8bf28d1130442c33729a07943", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_recover_with_pin_passphrase": "0f8a48405c459392b230f48bc0ee5a3fb7aec913ddf10207a0985a6b9257d421", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_same_share": "162dc76033fa9846ad1db309483893e310b5727a6efb4e2d8aa5879d0a6ae8bd", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "2d9dd4cd6923e9ec728fbb4247b06293597b2371898c3d0a8bd663ebaa3bcb05", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39-a50896b7": "230861cada99e733ef100837f543821c54e15544831e98c1e30fc9d0c4264f59", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[0]": "15718b67e079ce87d8f78d0c8c62d9b73f3d62344b293a1f5642b8b730b3322f", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[1]": "8685d9dc881dfe199f06f7160cabf6c3b0d164b9addd47e5a38ae4c28fdd686a", -"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "1b5a0b0dcbacaf4f473246e9dfcaa85cf7345f762804cb83cb84af5378c71d65", -"TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "b6330407214a02acdc677418e8075ed9d9c78375269ffa2925ce1e3662ff8aa4", -"TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "4b87f764c3cedbac89ffaa3cee4c488ba7a63d3ae54f1546ffa763c931330937", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "b9eea167b14a9f719fa10dc73038ab4ea8ddc3bc5bd92e3f4269c49e604bea93", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "359021d199f0d371732cce65c543b52a3dd45f7f8f6cb61b5da39b8d4c87501f", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "e0d83f83f5d8147260d40a9f6ed0d8fac05be27b6607344bed304c8c46fd0e05", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "9424d423c173be867fa88abfc26900aa5f0097be538c4cbd5d78cd5b38722a82", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "1e18ab625c1f2f88eaa42d9551e84f26906cbf6b08320da4d39587102f5cecbf", -"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "6c97947fcbdb1f0532fe8525810c1e306a44cab82e8b2e984802a4219307fbda", +"TT_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "7df3b9c226fd6c6380426b1b39f053cc7fd2bb81c70cad70b186a33a004cfb53", +"TT_reset_recovery-test_recovery_bip39_t2.py::test_tt_pin_passphrase": "3245488fcb60acbd10b8aa5836b3e046a2b4e7082cd38a65c007c4a925a657cf", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_abort": "6894943bb92b5f827cc67b5e2cc6cd90e973f09379b25a2e31666ce5e260be80", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_extra_share_entered": "d1e6b42a3fa5e4f3140cbc6d13f0d30479b26b6dfea4cb09b5e60e397b5e078c", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_group_threshold_reached": "5bc28023c3d83bf498084b52263329d82d1de73a7051cd7bf910b7a38dc5c70b", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_noabort": "a719f2f5d3c7f187a2b2d23dcb4428841188697bfc4fc7a517decfaf889ebd71", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_same_share": "0c7a1431a7a29de1adbad7ef6a236d8c0a933963a12853bf219ed1fb5953ff23", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares0-c2d2e26ad06023c60145f1-afc2dad5": "9ed534c74d02a3702cd25c3ac456727d9f06516ce387d7ef2e58381a7a5c27c7", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret[shares1-c41d5cf80fed71a008a3a0-eb47093e": "93cb44b3414b6c5e196e8be6ebf963831f4dbc0d8cb54a9a705db62b1d488db9", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares0-c2d2-850ffa77": "8459510f2f60d2e72447a88ea53795c8073c56ebef53799bdef16acd11e203d2", +"TT_reset_recovery-test_recovery_slip39_advanced.py::test_secret_click_info_button[shares1-c41d-ca9ddec8": "8f93ace3f01d22511173267420e67e81711b99fefdb107d9ee62e384e59f9e7f", +"TT_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_dryrun": "1e2b74eb0d7468cef7fe3d2e8ee85b9e57bd002d67f1194b8b8ba82227430f9a", +"TT_reset_recovery-test_recovery_slip39_advanced_dryrun.py::test_2of3_invalid_seed_dryrun": "6994d059586fbb4532236bb1b9095037df702f982aa40b58827bc8e053c8177c", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_1of1": "bc409ffee818de3ffbccfe9dd816c2f048f3eee3a453f0a17a36440260420240", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_abort": "6894943bb92b5f827cc67b5e2cc6cd90e973f09379b25a2e31666ce5e260be80", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_first_share": "80447255c40dd9106fcb83c102aa371eaa2a81e81098263aaea5e30432acd9e7", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_invalid_mnemonic_second_share": "9f6e30675d09e989841b9cdf68a0da907d8aa06893d7800265ba461c7a1aed11", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_noabort": "98078e1648d88576b9b39e232815f539f42c0c032b3cccaa0c00b4adeadd6511", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_recover_with_pin_passphrase": "2b29d0d3a1d2d7fff59ef9bc32e32de7bdde31c8da043c6318d92850e1d53ac9", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_same_share": "11907eb4445ca350662971a0ef46f785563a5dddd47c7b4e7605a9870c8e76c9", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares0-491b795b80fc21ccdf466c0fbc98c8fc]": "978d0c80b1db9b474585d6aea3f6c14bd1e6796556781afa0a0a67b363caaca0", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_secret[shares1-b770e0da1363247652de97a39-a50896b7": "ff909654212baca59bfccff3bfdee52c55f73dd626fc81ff679169655974acf8", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[0]": "857f2fe0996eec40ac153c78fabb75e1cb2649eed187972db7542a8da5255399", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[1]": "1472555668d2df91ac60ade9bf1dcc149175bab74f7c2ad1493016ce71717bf5", +"TT_reset_recovery-test_recovery_slip39_basic.py::test_wrong_nth_word[2]": "666e4780005d6d2e0487382f0a5c758236cdd50bad4e04fbd6d75e71570d9aa2", +"TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_dryrun": "6f03ecc76b568b44c77d35f92a6eb02f7f474be150afa887ff88ed59f3422776", +"TT_reset_recovery-test_recovery_slip39_basic_dryrun.py::test_2of3_invalid_seed_dryrun": "43e123d83b3a5c36dc9cbba705fc8a4a5b887040015f5da9437e6b51e341a0fc", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "95429ee1ff88a194776674ac656f1e8552af6e9b82eb924d1a0c99132e220d50", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-bac-f67baa1c": "a32e3d1c7600b00f81a97a6f0ea836f9d1225a2884db55674263dafd57b128b3", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup-6348e7fe": "9f4935a388c5bfe67e2473795106fb15daf88d3ff4d4bb055304a783fa0fecb3", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Bip39-backup_flow_bip39]": "95e3741d7d4a16922e3dd0b1112b611f9a7ede012f37db9fdb1a0115268188e0", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Advanced-backup-dcbda5cf": "3f04fcd7d3768f55c58fecd6aadaa7c5f09b72eec482f1e0aa994c62a32a3f80", +"TT_reset_recovery-test_reset_backup.py::test_skip_backup_msg[BackupType.Slip39_Basic-backup_fl-1577de4d": "b48369b16ce1066b94523b4ae4ad252b04e58d8790f03e8ff2707181eecd346b", "TT_reset_recovery-test_reset_bip39_t2.py::test_already_initialized": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "d0a9213b6bbcab5043a45576a9e86c1dc1f85282cbfe7faf280ad9c045651fed", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "0dd4a53c977900a3c5413188ff2cadc25e44ad3eb284d7bfedab0970f8d9b4e4", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "d15f9a0466f4db56c56a815e1a5d7abdc59d4adfb13ccbc7a124c8ef01b75565", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "00727c89c2256d3c6fce05ca1dd087c6fd272eb789e7e577de1b251d7ba5580c", -"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "afa2d5d578859fb342fba8cbd8ef7eb432fa42e401950950b10e63074d69b01f", -"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "cce5222bbe3a3c2f4ac84501918d25371348502911639a4379b4f8f228e363f8", -"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "b9c3bdebf810e59fe3346b9a78a61b03e97ebaccf6b1cb1892bb78d6dcd7926c", -"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "a9f395bfa6ab9a0277335d80eaf6fb3999c8c23e586ca8c47cfe2fa6a3761af3", -"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "0b65a08b2f5a31657a2451560f00d0b7deaffe710843bb103b20f1e58c6fcb05", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "b2801328a1e0ed5d94070c7384421dd8829ccd2e96e1b95335149037bd001a39", -"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "d1a13ca7b225200b1afd3f76144e5247bd2ea66b7687281701bfe2f40038f660", +"TT_reset_recovery-test_reset_bip39_t2.py::test_failed_pin": "8fc6c0f7a9c47cb8655ed2ee5aa099a6c8339d8f09c99c437cf7b120530cba5f", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device": "89d6a55f2387df4aee4cd1d7bafc688ad0ee90d224345ca08aba09534458b81b", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_192": "533b950b7f9cd348759b6d30bbe1d3c6eb28a673508f0fbd1e8ee4338058c04a", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_device_pin": "828b2057437124e260dbd38ccd6f7fd2c3029b89d43b21730ebdff82efb3173a", +"TT_reset_recovery-test_reset_bip39_t2.py::test_reset_failed_check": "05a2933f3201d619a11553417dce8c51c66801e4c894e8cf766709e7532a8408", +"TT_reset_recovery-test_reset_recovery_bip39.py::test_reset_recovery": "04958eea1517675986d720a376fb3c52466e7154b3da48e490191e714aa8a929", +"TT_reset_recovery-test_reset_recovery_slip39_advanced.py::test_reset_recovery": "695caac5efcef3022a8d1fb1016bf5be1f4e5ee3eeaf3b2e64e3d5808677129f", +"TT_reset_recovery-test_reset_recovery_slip39_basic.py::test_reset_recovery": "f983065519c6349bc6e2b7174da1240f3f3723f751dec536ebd78b01ac9d30de", +"TT_reset_recovery-test_reset_slip39_advanced.py::test_reset_device_slip39_advanced": "b7548d6cb3e6c0dc0ec5991ae22dc5e40ca6d9d5416d71f09bba04f038c57c8f", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic": "e140bc32a42ad41921ee6ccf107c86861c368723d11480528948b7a5f24aecef", +"TT_reset_recovery-test_reset_slip39_basic.py::test_reset_device_slip39_basic_256": "f7d49883567d093b5e6adf52115f90362aa627a05117886dad9ceef235c68725", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-0-rNaqKtKrMSwpwZSzRckPf-3321e5d1": "e1714dd0d8e33bc7d38c8a8f0a24082c89846497a98fcfec5136743007da9171", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-0h-0-1-rBKz5MC2iXdoS3XgnNSYm-fd75b415": "6b8a98bb658c617c435cf541e35c1fdb055f86c983add4df01b714836239c1b8", "TT_ripple-test_get_address.py::test_ripple_get_address[m-44h-144h-1h-0-0-rJX2KwzaLJDyFhhtXKi3h-af5daf0f": "f2b9355b112c77ea457568adb7375dd8f2338f5170ba26758751ce3f8cee285b", @@ -3194,45 +3194,45 @@ "TT_stellar-test_stellar.py::test_sign_tx[timebounds-0-1575234180]": "773f25160b9e6460c0f57d095e0bea97fe9cdca6aed22fe4db1d49c46233d025", "TT_stellar-test_stellar.py::test_sign_tx[timebounds-461535181-0]": "b1e1c5442ca424b3b23b3435446826d918a367e202fae8d7db4755d43bb0e579", "TT_stellar-test_stellar.py::test_sign_tx[timebounds-461535181-1575234180]": "d2887d907c9ed542771acde6b01762b660d9427c14156fce365778eaaafbb329", -"TT_test_autolock.py::test_apply_auto_lock_delay": "7bc038ebd72561554adad22ebc231b1deb6bf44f9ca5b1f6a124149f66619204", -"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[0]": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[1]": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[4194304]": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[536871]": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[9]": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[10]": "ed9a6b1d81bcf2f723b94b63a71dfe11b89f839a0e49d4d1cfe7bd8af6303531", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[123]": "564c351b225a725cdbbd65e50cbf9584f13c1b6940e8ce8e2c20f207d3fc6f98", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "3701de80b126f3e6bc53f1d8648cd0ee759304fa4c23911a44aafa57628dddda", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "5eadcc548b07683566b622fa5837273da64c00c92a3338903153767f14a8628a", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[60]": "a9be60d9d6b28d5aef789eb41a567092c9d2515ddf906016cc077ce746d5c591", -"TT_test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "7900e3d2410b3b9030f68390279e6f7a0b29af674575df925628e7b676829aa6", -"TT_test_autolock.py::test_autolock_cancels_ui": "5d3848cb6e70df6dd9813ea6de3f777ad62e7550c93442c552aa408b3c789c3e", -"TT_test_autolock.py::test_autolock_default_value": "e4693a04e626840f3788814fe71338bc33ad7ef1977f16d41f84c3d3176c3d33", -"TT_test_autolock.py::test_autolock_ignores_getaddress": "83c8b332bb0ee135d990c9eb54b3f02176323df69f97cf89427ae85f94f3f71f", -"TT_test_autolock.py::test_autolock_ignores_initialize": "83c8b332bb0ee135d990c9eb54b3f02176323df69f97cf89427ae85f94f3f71f", -"TT_test_basic.py::test_device_id_different": "2619e15523d375f20155c895d385fcd647b88920dc28bb895fcfc28321ef2d33", +"TT_test_autolock.py::test_apply_auto_lock_delay": "788e79b0e010080ebf03c13252b322aaca6d0b1d4fdd9d541d37b54ba377f4a8", +"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[0]": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[1]": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[4194304]": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[536871]": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_autolock.py::test_apply_auto_lock_delay_out_of_range[9]": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[10]": "a30235c21dd7192a0c3a188549b68be441e0048d5b1107de9a7128413fa472d9", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[123]": "c0a3bd35c34abb9914e87851450d72d5d9dd1b93c4b2d402e904878a5e1efe07", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "e64facd745e91dc50d11944570bf85cd2e9c7245a5002a621c2a3eb107b267f9", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "3515687adbf525466e3e8db3bd31efb937f16e8a5247a4c482a11bf3bb13f7cd", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[60]": "2463ffb29d2bf187320c057e4732e2842b7ecacd333d4230efd96894e0160c44", +"TT_test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "d288666907f2172e6e44ca1deae208afacc819b43a954a54e63a379011fff4d2", +"TT_test_autolock.py::test_autolock_cancels_ui": "f371eae4a7891c996219cde1fadd96a6ad615436d73da2c7597c3db19a4429f5", +"TT_test_autolock.py::test_autolock_default_value": "9f058341ff75d682faffd30ac0351ab7eb3ce596e51a3a39194ff63d9269860d", +"TT_test_autolock.py::test_autolock_ignores_getaddress": "6dbde78382fe909ee4bf86693f0397ec4e40b5ecb7ed49f14f7a7069d672475d", +"TT_test_autolock.py::test_autolock_ignores_initialize": "6dbde78382fe909ee4bf86693f0397ec4e40b5ecb7ed49f14f7a7069d672475d", +"TT_test_basic.py::test_device_id_different": "5d94d331908fa838b323551de8cc22b944c068bc6c919b4142a872d4ed23f256", "TT_test_basic.py::test_device_id_same": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_basic.py::test_features": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_basic.py::test_ping": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_busy_state.py::test_busy_expiry": "6509e7c943f2d09e3a5a7c69faee74c75b321ed1614d881d1ebdb57693c7f69a", -"TT_test_busy_state.py::test_busy_state": "b7e688a5a696c3e3df9b949a6abe995cd511a1cb4f6fc0d0ea9e2e6da7b24fe5", +"TT_test_busy_state.py::test_busy_state": "229ae8608ca856c4796c68ed29dfcf10b738a9ee8a3c34b53d014a29b7993069", "TT_test_cancel.py::test_cancel_message_via_cancel[message0]": "1bd3157d54327e33542f89dcac6c7cd23808f7c9aa1b0adb390e5fcc1fd858a5", "TT_test_cancel.py::test_cancel_message_via_cancel[message1]": "1bd3157d54327e33542f89dcac6c7cd23808f7c9aa1b0adb390e5fcc1fd858a5", "TT_test_cancel.py::test_cancel_message_via_initialize[message0]": "1bd3157d54327e33542f89dcac6c7cd23808f7c9aa1b0adb390e5fcc1fd858a5", "TT_test_cancel.py::test_cancel_message_via_initialize[message1]": "1bd3157d54327e33542f89dcac6c7cd23808f7c9aa1b0adb390e5fcc1fd858a5", "TT_test_cancel.py::test_cancel_on_paginated": "a6aadac2d8820228eb8fc0152141350f176095aeb2d9fd6e41581f75a8b962b7", -"TT_test_debuglink.py::test_softlock_instability": "152b9a385df8773bc59bf8122bef7a1b77923d36743c3a557ba57b560a6efed2", +"TT_test_debuglink.py::test_softlock_instability": "8f11f602f353b51e83aa4f3bcfa4356dd2ddb5585150783ec2ee8239a32417dd", "TT_test_firmware_hash.py::test_firmware_hash_emu": "2a63f0bd10ba99e223f571482d4af635653bb8a3bddc1d8400777ee5519bc605", "TT_test_firmware_hash.py::test_firmware_hash_hw": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg": "55b36d304687edb421b3bdb7bce12c4a0b1be95b4d5fd0b96fb839af157fd801", -"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_progressive": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_wrong_size": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_msg_applysettings.py::test_apply_homescreen_toif": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_msg_applysettings.py::test_apply_settings": "20952253ce94f12f091cae7e8f3163a07415a4fac2e1b6fb8d1167ce4727ae12", -"TT_test_msg_applysettings.py::test_apply_settings_passphrase": "c2b3471ea4f29da8b3ce71a8f1d822a6b320a484e0ca75386ad3e32116a8d8ff", +"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg": "46ff4722b65ee55ec7de7718ff2cd726b4e5e3c565db57ba9bcc23e140fd1a68", +"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_progressive": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_msg_applysettings.py::test_apply_homescreen_jpeg_wrong_size": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_msg_applysettings.py::test_apply_homescreen_toif": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_msg_applysettings.py::test_apply_settings": "9706a5c7c3459bfc97bb40b25e272ab711fb2f77c65386cc7eec96b6ceca3598", +"TT_test_msg_applysettings.py::test_apply_settings_passphrase": "97cebb3c2aeb2db5946b3060bf9ab581215acd18862e2d08d9e855fe17ec2d3a", "TT_test_msg_applysettings.py::test_apply_settings_passphrase_on_device": "a1f6dc01500cc25bb7ad302da3e5b6040567d9245486e2ab983bc7b03a511603", -"TT_test_msg_applysettings.py::test_apply_settings_rotation": "28bb0fb9d5296039f379a0c6e9bc38f37358928dd3938cc322d523874c628240", -"TT_test_msg_applysettings.py::test_experimental_features": "38bfcb19f17cd020b3cd38a0a09c1698da86dacc62a43f59b926a50b1fadd041", +"TT_test_msg_applysettings.py::test_apply_settings_rotation": "91a0983ff7041ceb26cccd223f8dd8fb03c7179d0f4860bad8128bdc00ca84b6", +"TT_test_msg_applysettings.py::test_experimental_features": "921c40ff29716ac42ee79b00cbbbf3b74e4dba9265ea1dae690e489f38422621", "TT_test_msg_applysettings.py::test_label_too_long": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_msg_applysettings.py::test_safety_checks": "42b47ae0be7a8c86224b37ee417c1a487057974b7617b89b976b51be8fb05a04", "TT_test_msg_backup_device.py::test_backup_bip39": "0fd75efd974215a43d4b275a173b30ecb41291e5579abf6c15c196c0fc3aa87a", @@ -3243,55 +3243,55 @@ "TT_test_msg_backup_device.py::test_interrupt_backup_fails": "ae147498028d68aa71c7337544e4a5049c4c943897f905c6fe29e88e5c3ab056", "TT_test_msg_backup_device.py::test_no_backup_fails": "fada9d38ec099b3c6a4fd8bf994bb1f3431e40085128b4e0cd9deb8344dec53e", "TT_test_msg_backup_device.py::test_no_backup_show_entropy_fails": "001377ce61dcd189e6a9d17e20dcd71130e951dc3314b40ff26f816bd9355bdd", -"TT_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "e77155a1f0b3c79a1713c3f57c520b51282a972768567d4048c9789cff9640da", -"TT_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "63ee5efe996b936c3e395b950b78adff40887eb005a2a18289b6194e2e6027cb", +"TT_test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "9279e30f1cd8f417d65f8081d04414acdfa4adbefd58920b2f9415792d33de36", +"TT_test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "97a7c0289aa323d776112c9c9eec00e7c17090cd8f85c334d82fdf50cc11db41", "TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "a98ec36a1945f91956ca63a558206d21a5bbf714dfe420f1404d1a5df634a9c3", -"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "ce954f765eac6510ff80fec9a90259ed6be8a0e1e22828331aa8b10b3d75b4bc", -"TT_test_msg_changepin_t2.py::test_change_failed": "97f23132a32c14c8769c969c09fb202cb61c700a011ffdc557098c59112181e2", -"TT_test_msg_changepin_t2.py::test_change_invalid_current": "e52be11ea0034a9f1dace94f7811e6f27e6a6e135572403fee50995adfcaf167", -"TT_test_msg_changepin_t2.py::test_change_pin": "544f464b1460553a57011a6deedf9c0bcb650befdb7cf473bcb47752ea796b08", -"TT_test_msg_changepin_t2.py::test_remove_pin": "95063d1282833732180946ddbabc7a1ea6d9b83dd7a1e4a6bb057723bd94b914", +"TT_test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "6ca2ac9d1a3c521cfafc4c515fdf44e91f524d7ae9b17ff555568af517d7b9c7", +"TT_test_msg_changepin_t2.py::test_change_failed": "8540af0c9696fb250f153b39e9978b13307a7b458a22b8ab6f8d1977d704ec34", +"TT_test_msg_changepin_t2.py::test_change_invalid_current": "a7cf92fc4cb7b688a7649f0fcfd0b2973db6d590e057370247528a9d6522e780", +"TT_test_msg_changepin_t2.py::test_change_pin": "1308303562d48d7e2946c31ccc5e8928ff91cf71e85f0e0360e81199b69bba22", +"TT_test_msg_changepin_t2.py::test_remove_pin": "0e829a68f17c80a507520ad93484177fd2c06fc77c75b08f74f0fd39819a19cb", "TT_test_msg_changepin_t2.py::test_set_failed": "962ba410d395e37ad341b818c026b134891040617e3f9368c4fcaaa9b4a27efe", -"TT_test_msg_changepin_t2.py::test_set_pin": "ab97621e8e3bcb2256bf91e2302b08148a7c28f8f9b2be58db19da8a46a419ab", +"TT_test_msg_changepin_t2.py::test_set_pin": "02e4ee4feb8b87313b0979fe2061574fd4f73228bd3e9b1f37f50eb665e7321a", "TT_test_msg_loaddevice.py::test_load_device_1": "1d1bdf29f677492956efe2ab29ab2481bbb647ac14cef9cfb5d8f62370133b1a", -"TT_test_msg_loaddevice.py::test_load_device_2": "934073d47233f80622a6ccdde024efb3683d866731568c7c4544325b9d6b8fa0", +"TT_test_msg_loaddevice.py::test_load_device_2": "d409f06f9cc73b5f5a32374a7e1dec883109fee88bf07e1a6c6928e6548cdd19", "TT_test_msg_loaddevice.py::test_load_device_slip39_advanced": "1d1bdf29f677492956efe2ab29ab2481bbb647ac14cef9cfb5d8f62370133b1a", "TT_test_msg_loaddevice.py::test_load_device_slip39_basic": "1d1bdf29f677492956efe2ab29ab2481bbb647ac14cef9cfb5d8f62370133b1a", -"TT_test_msg_loaddevice.py::test_load_device_utf": "85e88d81ca493dea83b1d85d9540de591d6dadf7de4b225cf209d52663ec3d6f", +"TT_test_msg_loaddevice.py::test_load_device_utf": "8d92b59ec7388e354f9ddf02dc6c443a675ec40355761503fb19602adb1e5332", "TT_test_msg_ping.py::test_ping": "d702b0f90581cf17e0f77b4d318324a002deec42c2c5cb8860d51f6cb50f5739", -"TT_test_msg_sd_protect.py::test_enable_disable": "29446f2808515e30c599538045d69b5297748e7bd76d652997eecf2505ef1078", -"TT_test_msg_sd_protect.py::test_refresh": "d7bb25f8dd06d945a72d5ea6b2baa8880dbee996e2ca370f60786cfa9d4a9776", -"TT_test_msg_sd_protect.py::test_wipe": "5934273de4f970bb816a8d72acd0495133f7a96cda9258bf5dc4d2d6bf0430b4", -"TT_test_msg_wipedevice.py::test_autolock_not_retained": "e40707bad924c181c1fe9025deed0d5eb34af162a4c1ad16420bd9fef86a4109", -"TT_test_msg_wipedevice.py::test_wipe_device": "2619e15523d375f20155c895d385fcd647b88920dc28bb895fcfc28321ef2d33", +"TT_test_msg_sd_protect.py::test_enable_disable": "95a5bd3071e08243f9e64ad0c017bc16420c57f760fe2f1add381ab55075f3e7", +"TT_test_msg_sd_protect.py::test_refresh": "d132080d9dbfcebcac67ba3856a373b98fc78be445964f31b0b936df7d806d60", +"TT_test_msg_sd_protect.py::test_wipe": "ad83437211358525c72e42b6f1f1aa5593dadb33780cb3ff01040964b2f400ca", +"TT_test_msg_wipedevice.py::test_autolock_not_retained": "49f80c436922e8293655ad89f4815a099ace232179be54e5afa66caa94d4cb1d", +"TT_test_msg_wipedevice.py::test_wipe_device": "5d94d331908fa838b323551de8cc22b944c068bc6c919b4142a872d4ed23f256", "TT_test_passphrase_slip39_advanced.py::test_128bit_passphrase": "c4a8e8e7544a7dd8a7157aef8572f1be99c10b290ca9e0f60763b7e3583c7882", "TT_test_passphrase_slip39_advanced.py::test_256bit_passphrase": "c4a8e8e7544a7dd8a7157aef8572f1be99c10b290ca9e0f60763b7e3583c7882", "TT_test_passphrase_slip39_basic.py::test_2of5_passphrase": "fdf4b1631f9726cd27137dc3e20a78d88d223fd774fee8828a1afb33624e9a11", "TT_test_passphrase_slip39_basic.py::test_3of6_passphrase": "fdf4b1631f9726cd27137dc3e20a78d88d223fd774fee8828a1afb33624e9a11", -"TT_test_pin.py::test_correct_pin": "77189def5f05cc4d4b80670d7e1c7163f24ee4e454f503e79fac7557039b7b4c", -"TT_test_pin.py::test_exponential_backoff_t2": "4f75e26ab7dbdc84dce2191b8a1baa06379dda6b46cfb2acda878ad7bb65405d", -"TT_test_pin.py::test_incorrect_pin_t2": "093aef20cef555253935e2d1204edeca9e22fd7ea1e93b3f37b2c6418d4e3764", +"TT_test_pin.py::test_correct_pin": "35820b3e649e7fe727b36738d2425c443067f296f21f971dc0f7e1dd564c3d2f", +"TT_test_pin.py::test_exponential_backoff_t2": "be21284abdfc09bc0ef587b4fd3d4e211a66f3666d7b1fa1f1d12e849fde923f", +"TT_test_pin.py::test_incorrect_pin_t2": "ffa4ee74b4119af8f147103fbda38ee8ed0cba37e323c1a8c7e84b6ff5467aaa", "TT_test_pin.py::test_no_protection": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_test_protection_levels.py::test_apply_settings": "e6685105f4b87b8437be762c673fb386a8290ac608755b017b992200b3ecb311", -"TT_test_protection_levels.py::test_change_pin_t2": "79696b8eaeadc81bf805357259a2299c2028bf0bfc83be9d45d540dc02d99762", -"TT_test_protection_levels.py::test_get_address": "45faddc8ca38976c59227e907c483d76ba343e8f2c42570efe2f6ba365074598", -"TT_test_protection_levels.py::test_get_entropy": "7394a6a6b5300bda943a4f054a3137e4066a0447b3135912fb5246bf65b20980", -"TT_test_protection_levels.py::test_get_public_key": "45faddc8ca38976c59227e907c483d76ba343e8f2c42570efe2f6ba365074598", -"TT_test_protection_levels.py::test_initialize": "786c1f33933a8d23b5a314a1c7a7753446d3c6c14e46f5d729c4ad9eaf627fca", +"TT_test_protection_levels.py::test_apply_settings": "2edfbf4da923e2e320c8cf611d4698b5b9dd07459cff060ca1c74507d4c3b8bc", +"TT_test_protection_levels.py::test_change_pin_t2": "b9ce905eeb807b6261760f2f329cc00953c5aa5f532da700b1fa4820761ed276", +"TT_test_protection_levels.py::test_get_address": "e0faa1733f9c5268040ed73058ba9303ead58cdd1e7f2c03003d8b59669568aa", +"TT_test_protection_levels.py::test_get_entropy": "7e6ddcb454c4cdc4d4060fd84f7cca373d29bc6c83c385e220dd335cefd508a3", +"TT_test_protection_levels.py::test_get_public_key": "e0faa1733f9c5268040ed73058ba9303ead58cdd1e7f2c03003d8b59669568aa", +"TT_test_protection_levels.py::test_initialize": "2dfd03b08a1f75b7aa31e852d7c0478082f9a25e316a41aef689e43f9cca58db", "TT_test_protection_levels.py::test_passphrase_cached": "2be6f9bdf9836f159ba935cc758afc83121f2ad30f0ea8ae27a0c1d436a2a5cf", -"TT_test_protection_levels.py::test_passphrase_reporting[False]": "8f490cf819b398b112588dacfc453ace534eea4080e3c17e9bb787e385f96dc3", -"TT_test_protection_levels.py::test_passphrase_reporting[True]": "080048e6066d0a81102a9ac6af8ebf7a7ee8ec378685f23299940905b8941030", +"TT_test_protection_levels.py::test_passphrase_reporting[False]": "f91231df26edcb65f5d15eeb3a0a453385fc142265fb307c366b5139aeda8012", +"TT_test_protection_levels.py::test_passphrase_reporting[True]": "fb43bd981b0a7dd400159306b4b430ffeb0036b90e1ab50562871fa66537c9c5", "TT_test_protection_levels.py::test_ping": "d702b0f90581cf17e0f77b4d318324a002deec42c2c5cb8860d51f6cb50f5739", -"TT_test_protection_levels.py::test_sign_message": "f819279dc7118febebc3182dbb402e76c169fae2867c69feb1648070f9ef99f5", -"TT_test_protection_levels.py::test_signtx": "8f5a922f7ff84133ca6586fa18841d882aa0c8f9b29da3da2028bff63a892d0b", -"TT_test_protection_levels.py::test_unlocked": "ab65da2ea346b0f54de6b527d06b3b5b84b106af7c8a520ac7816141ecedf220", -"TT_test_protection_levels.py::test_verify_message_t2": "63ad10518b06fe3ac46c979e965f23817f19f4c47c206569758f221e8133a152", -"TT_test_protection_levels.py::test_wipe_device": "f8a23360d5171de562b4de198aab78f76e4532baab3bfb85a39fd3515d533c3c", -"TT_test_sdcard.py::test_sd_format": "f452c463dba5f19b684fb7738f2bd110908854a8e5981efbf301c55232f05b92", +"TT_test_protection_levels.py::test_sign_message": "9e45f98f244f04895e45e28dc05aaa5216141e4f6816ee68246b4d1325791a70", +"TT_test_protection_levels.py::test_signtx": "aeb4b057e877247543073f8bcd1570c2dd966ef7bea3c5b048f7773417e7a1a5", +"TT_test_protection_levels.py::test_unlocked": "cb0500a853ad6519a41fb8ecbcdc6599258366e5fdfe687f0c0071f1fba48134", +"TT_test_protection_levels.py::test_verify_message_t2": "f7c75af6679a27355819859bc8d6fa4b0f808380e380ba54a0cd817a7dca5cc0", +"TT_test_protection_levels.py::test_wipe_device": "b1739f152dd486d4b5e5646c0055c19ffdfedc79b3ec25a704f022844e4ff2cb", +"TT_test_sdcard.py::test_sd_format": "50820aae86a31a2e910cfab99b8e71815692d1cd6414c80bb2993760afd384fe", "TT_test_sdcard.py::test_sd_no_format": "14511e3d3ee535d97287d8ade25101e8c16db17c1dc5d3151b91e5e8eba61ba5", -"TT_test_sdcard.py::test_sd_protect_unlock": "818a8b275708e87a90dd24597ea4b63edf0b3e7ef9dea739cdc0e9a3aa96c4d3", +"TT_test_sdcard.py::test_sd_protect_unlock": "3059cd2f4f6d821cabe352d7f958878d255175839e6eddb920dbb52796c6ddf0", "TT_test_session.py::test_cannot_resume_ended_session": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", -"TT_test_session.py::test_clear_session": "289705a7742fdf6a0ae69cfb0ec1baa57277484485c20d1d174b880cfc0b2a4e", +"TT_test_session.py::test_clear_session": "b5801ef17f3173fc699aa6eab9e51227d8a8b9cc8bd2c84fa66c71c01b841659", "TT_test_session.py::test_derive_cardano_empty_session": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_session.py::test_derive_cardano_running_session": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", "TT_test_session.py::test_end_session": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3", @@ -3341,14 +3341,14 @@ "TT_zcash-test_sign_tx.py::test_version_group_id_missing": "80a6e289138a604cf351a29511cf6f85e2243591317894703152787e1351a1a3" }, "persistence_tests": { -"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.PromptAlways--081810a6": "37dd60c0f0fe3878b03505025cd0d2117e83d0345bfa8b4a1fc9cbb3a42c0f5a", -"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.PromptTempora-b3d21f4a": "734ce49a97350ac6dba48ed2f987af1836016bc708ab9930c986a53de6676773", -"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.Strict-Safety-f1ff9c26": "a0fb76cd6968c3511ec1ccf4f6cb8d72ea087794a272da2b3c7606b20974b616", -"TT_test_shamir_persistence.py::test_abort": "f60b3515b07f4ba01d7b4f427cb9672dc92b9bb8b167a8e9daca32b1fa17ff51", -"TT_test_shamir_persistence.py::test_recovery_multiple_resets": "5c291e0d73d8a01226f857f926e06afd6ed547fcdeba6b7ad1b9954db805c252", -"TT_test_shamir_persistence.py::test_recovery_on_old_wallet": "2c8ea8ca7ec99fde6b19787a49faf3a449e205727bc5d1473791a049358d46a2", -"TT_test_shamir_persistence.py::test_recovery_single_reset": "7323bc491f2163f5996cad4e9eafce0c690f6c10c078745a0dbae86510bf8782", -"TT_test_wipe_code.py::test_wipe_code_activate_core": "fce0b44825db496f8612dbbdc6b38600462ea842471029bdd279dd122b36a1fd" +"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.PromptAlways--081810a6": "8a0498fe716bf0f1089f549950c378a4b5796a2d694ad678db5d8125f06f1e24", +"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.PromptTempora-b3d21f4a": "9e7540c5d792d6728289354b23ada645b35ab948684770cb4cfe1d61f50f11f9", +"TT_test_safety_checks.py::test_safety_checks_level_after_reboot[SafetyCheckLevel.Strict-Safety-f1ff9c26": "dc62cf266023ef9f64cf7d2a1303431fdd419d34c74fccc42e9ac6faa0c61400", +"TT_test_shamir_persistence.py::test_abort": "2ea0b055da3fafc814f8262a00e8f705f14ed4663fef66d778bc50736cc7984c", +"TT_test_shamir_persistence.py::test_recovery_multiple_resets": "39267f234331d17428269bf9cd7f5aa006b64101d42e3cd62a6822c88265eb20", +"TT_test_shamir_persistence.py::test_recovery_on_old_wallet": "018259a317cf20b473a60f5c70ef79ee106c5975c0e9123d7769344423d206d2", +"TT_test_shamir_persistence.py::test_recovery_single_reset": "4ddbbadb69a1778bca816bf928f63c1d92dde8c5ac31a301c15dda0659e32ca2", +"TT_test_wipe_code.py::test_wipe_code_activate_core": "e21fade21796834c53895529765c143252dcb84713f968df04a33a1061cc2962" } } }