diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 936233ed98..6ab2734876 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -29,6 +29,7 @@ static void _librust_qstrs(void) { MP_QSTR_set_rotation; MP_QSTR_get_label; MP_QSTR_set_label; + MP_QSTR_get_device_id; MP_QSTR_get_mnemonic_secret; MP_QSTR_set_mnemonic_secret; MP_QSTR_is_passphrase_enabled; diff --git a/core/embed/rust/src/storagedevice/storage_device.rs b/core/embed/rust/src/storagedevice/storage_device.rs index 7aca4bbeaa..69c9b829fd 100644 --- a/core/embed/rust/src/storagedevice/storage_device.rs +++ b/core/embed/rust/src/storagedevice/storage_device.rs @@ -7,12 +7,12 @@ use crate::{ obj::Obj, qstr::Qstr, }, - trezorhal::secbool, + trezorhal::{random, secbool}, util, }; use cstr_core::cstr; -use heapless::Vec; +use heapless::{String, Vec}; use core::{ convert::{TryFrom, TryInto}, @@ -196,6 +196,26 @@ extern "C" fn storagedevice_set_label(label: Obj) -> Obj { unsafe { util::try_or_raise(block) } } +extern "C" fn storagedevice_get_device_id() -> Obj { + let block = || { + let key = _get_appkey(DEVICE_ID, true); + let device_id = &storagedevice_storage_get(key) as &[u8]; + + // TODO: is there some easier way? + // TODO: replace unwrap() with "?" + if device_id.is_empty() { + let new_device_id = &random::get_random_bytes(12) as &[u8]; + let hex_id = _hexlify_bytes(new_device_id); + let device_id_bytes = hex_id.as_str().as_bytes(); + storagedevice_storage_set(key, device_id_bytes.as_ptr(), device_id_bytes.len() as u16); + hex_id.as_str().try_into() + } else { + str::from_utf8(device_id).unwrap().try_into() + } + }; + unsafe { util::try_or_raise(block) } +} + extern "C" fn storagedevice_get_mnemonic_secret() -> Obj { let block = || { let key = _get_appkey(_MNEMONIC_SECRET, false); @@ -548,13 +568,7 @@ pub fn storagedevice_storage_get(key: u16) -> Vec { // Would mean having Option as the return type let result = &buf[..len as usize]; - // TODO: can we somehow convert it more easily? - let mut vector_result = Vec::::new(); - for byte in result { - vector_result.push(*byte).unwrap(); - } - - vector_result + result.iter().cloned().collect() } // TODO: is it worth having a special function to not allocate so much in all @@ -693,6 +707,23 @@ fn _normalize_autolock_delay(delay_ms: u32) -> u32 { } } +// TODO: could be put elsewhere to be available for all modules +pub fn _hexlify_bytes(bytes: &[u8]) -> String<64> { + let mut buf = String::<64>::from(""); + for byte in bytes { + fn hex_from_digit(num: u8) -> char { + if num < 10 { + (b'0' + num) as char + } else { + (b'A' + num - 10) as char + } + } + buf.push(hex_from_digit(byte / 16)).unwrap(); + buf.push(hex_from_digit(byte % 16)).unwrap(); + } + buf +} + #[no_mangle] pub static mp_module_trezorstoragedevice: Module = obj_module! { Qstr::MP_QSTR___name_storage__ => Qstr::MP_QSTR_trezorstoragedevice.to_obj(), @@ -734,6 +765,10 @@ pub static mp_module_trezorstoragedevice: Module = obj_module! { /// """Set label.""" Qstr::MP_QSTR_set_label => obj_fn_1!(storagedevice_set_label).as_obj(), + /// def get_device_id() -> str: + /// """Get device ID.""" + Qstr::MP_QSTR_get_device_id => obj_fn_0!(storagedevice_get_device_id).as_obj(), + /// def get_mnemonic_secret() -> bytes: /// """Get mnemonic secret.""" Qstr::MP_QSTR_get_mnemonic_secret => obj_fn_0!(storagedevice_get_mnemonic_secret).as_obj(), @@ -917,4 +952,11 @@ mod tests { let result = _normalize_autolock_delay(1_000_000); assert_eq!(result, 1_000_000); } + + #[test] + fn hexlify_bytes() { + let bytes: &[u8; 6] = &[52, 241, 6, 151, 173, 74]; + let result = _hexlify_bytes(bytes); + assert_eq!(result, String::<64>::from("34F10697AD4A")); + } } diff --git a/core/embed/rust/src/trezorhal/random.rs b/core/embed/rust/src/trezorhal/random.rs index 6ff3482899..59b1b1b5aa 100644 --- a/core/embed/rust/src/trezorhal/random.rs +++ b/core/embed/rust/src/trezorhal/random.rs @@ -1,8 +1,14 @@ +use heapless::Vec; + extern "C" { // trezor-crypto/rand.h fn random_uniform(n: u32) -> u32; + fn random_buffer(buf: *const u8, len: u16); } +// TODO: what is the sensible max size not to allocate too much every time? +const MAX_LEN: usize = u8::MAX as usize; + pub fn uniform(n: u32) -> u32 { unsafe { random_uniform(n) } } @@ -15,6 +21,10 @@ pub fn shuffle(slice: &mut [T]) { } } -// TODO: create random_bytes() -// from trezorcrypto import random -// random_buffer +pub fn get_random_bytes(len: u8) -> Vec { + let mut buf: [u8; MAX_LEN] = [0; MAX_LEN]; + unsafe { random_buffer(&mut buf as *mut _, len as u16) }; + + let result = &buf[..len as usize]; + result.iter().cloned().collect() +} diff --git a/core/mocks/generated/trezorstoragedevice.pyi b/core/mocks/generated/trezorstoragedevice.pyi index fbc61251ed..d3af0c26e1 100644 --- a/core/mocks/generated/trezorstoragedevice.pyi +++ b/core/mocks/generated/trezorstoragedevice.pyi @@ -43,6 +43,11 @@ def set_label(label: str) -> bool: """Set label.""" +# rust/src/storagedevice/storage_device.rs +def get_device_id() -> str: + """Get device ID.""" + + # rust/src/storagedevice/storage_device.rs def get_mnemonic_secret() -> bytes: """Get mnemonic secret.""" diff --git a/core/src/apps/base.py b/core/src/apps/base.py index 7e3273344d..db9e99e154 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -42,7 +42,7 @@ def get_features() -> Features: patch_version=utils.VERSION_PATCH, revision=utils.SCM_REVISION, model=utils.MODEL, - device_id=storage.device.get_device_id(), + device_id=storagedevice.get_device_id(), label=storagedevice.get_label(), pin_protection=config.has_pin(), unlocked=config.is_unlocked(), diff --git a/core/src/main.py b/core/src/main.py index f0c9a54b06..f2ca2ea998 100644 --- a/core/src/main.py +++ b/core/src/main.py @@ -45,9 +45,9 @@ with unimport_manager: del boot # start the USB -import storage.device +from trezor import storagedevice -usb.bus.open(storage.device.get_device_id()) +usb.bus.open(storagedevice.get_device_id()) # run the endless loop while True: diff --git a/core/src/storage/__init__.py b/core/src/storage/__init__.py index 0119dfee1b..d98229d784 100644 --- a/core/src/storage/__init__.py +++ b/core/src/storage/__init__.py @@ -27,7 +27,7 @@ def reset() -> None: """ Wipes storage but keeps the device id unchanged. """ - device_id = device.get_device_id() + device_id = storagedevice.get_device_id() wipe() common.set(common.APP_DEVICE, device.DEVICE_ID, device_id.encode(), public=True) diff --git a/core/src/storage/device.py b/core/src/storage/device.py index 50b12de0fe..b279286803 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -1,10 +1,12 @@ from micropython import const from typing import TYPE_CHECKING -from ubinascii import hexlify import storage.cache from storage import common +# from ubinascii import hexlify + + # TODO: try ... from trezorstoragedevice import * @@ -82,18 +84,18 @@ SD_SALT_AUTH_KEY_LEN_BYTES = const(16) # return common.get_bool(_NAMESPACE, INITIALIZED, public=True) -def _new_device_id() -> str: - from trezorcrypto import random # avoid pulling in trezor.crypto +# def _new_device_id() -> str: +# from trezorcrypto import random # avoid pulling in trezor.crypto - return hexlify(random.bytes(12)).decode().upper() +# return hexlify(random.bytes(12)).decode().upper() -def get_device_id() -> str: - dev_id = common.get(_NAMESPACE, DEVICE_ID, public=True) - if not dev_id: - dev_id = _new_device_id().encode() - common.set(_NAMESPACE, DEVICE_ID, dev_id, public=True) - return dev_id.decode() +# def get_device_id() -> str: +# dev_id = common.get(_NAMESPACE, DEVICE_ID, public=True) +# if not dev_id: +# dev_id = _new_device_id().encode() +# common.set(_NAMESPACE, DEVICE_ID, dev_id, public=True) +# return dev_id.decode() # def get_rotation() -> int: diff --git a/core/src/storage/sd_salt.py b/core/src/storage/sd_salt.py index 9a53ed3e2c..bf9b6021e0 100644 --- a/core/src/storage/sd_salt.py +++ b/core/src/storage/sd_salt.py @@ -1,7 +1,6 @@ from micropython import const from typing import TYPE_CHECKING -import storage.device from trezor import io, storagedevice from trezor.sdcard import with_filesystem from trezor.utils import consteq @@ -32,7 +31,7 @@ def compute_auth_tag(salt: bytes, auth_key: bytes) -> bytes: def _get_device_dir() -> str: - return f"/trezor/device_{storage.device.get_device_id().lower()}" + return f"/trezor/device_{storagedevice.get_device_id().lower()}" def _get_salt_path(new: bool = False) -> str: