Merge branch 'master' into release/23.09

release/23.09
Martin Milata 8 months ago
commit 040f6c2c8e

@ -0,0 +1 @@
Integrate Optiga into PIN verification for Model R.

@ -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.

@ -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.

@ -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

@ -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) | \

@ -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
{

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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;

@ -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

@ -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;

@ -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) };

@ -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
}

@ -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<T>
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();

@ -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)
}

@ -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<StrBuffer> =
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()?;

@ -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);
}
}

@ -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)
}

@ -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<StrBuffer> =
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()?;

@ -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"

@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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

@ -19,7 +19,63 @@
#include "optiga.h"
#include <string.h>
#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;
}

@ -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},

@ -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;

@ -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,

@ -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;
}

@ -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)."""

@ -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

@ -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

@ -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:

@ -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,

@ -393,7 +393,7 @@ async def confirm_homescreen(
interact(
RustLayout(
trezorui2.confirm_homescreen(
title="SET HOMESCREEN",
title="CHANGE HOMESCREEN",
image=image,
)
),

@ -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,
),

@ -5,6 +5,7 @@ from trezorutils import ( # noqa: F401
EMULATOR,
INTERNAL_MODEL,
MODEL,
MODEL_FULL_NAME,
SCM_REVISION,
UI_LAYOUT,
USE_BACKLIGHT,

@ -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

@ -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

@ -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);

@ -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 <assert.h>
#include <stdint.h>
#include <string.h>
#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;
}

@ -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

@ -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)

@ -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);

@ -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)

@ -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");

@ -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

@ -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);

@ -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);

@ -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"
}
}
}

Loading…
Cancel
Save