1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-19 13:08:14 +00:00

WIP - using obj_module!, is_initialized rust binding

This commit is contained in:
grdddj 2022-03-11 10:22:54 +01:00
parent bcf837694b
commit 1c87e57570
27 changed files with 157 additions and 98 deletions

View File

@ -23,41 +23,6 @@
#include "librust.h"
/// def is_version_stored() -> bool:
/// """Whether version is in storage."""
STATIC MP_DEFINE_CONST_FUN_OBJ_0(
mod_trezorutils_storagedevice_is_version_stored_obj,
storagedevice_is_version_stored);
/// def get_version() -> bytes:
/// """Get from storage."""
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_storagedevice_get_version_obj,
storagedevice_get_version);
/// def set_version(version: bytes) -> bool:
/// """Save to storage."""
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorutils_storagedevice_set_version_obj,
storagedevice_set_version);
STATIC const mp_rom_map_elem_t mp_module_trezorstoragedevice_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorstoragedevice)},
{MP_ROM_QSTR(MP_QSTR_is_version_stored),
MP_ROM_PTR(&mod_trezorutils_storagedevice_is_version_stored_obj)},
{MP_ROM_QSTR(MP_QSTR_get_version),
MP_ROM_PTR(&mod_trezorutils_storagedevice_get_version_obj)},
{MP_ROM_QSTR(MP_QSTR_set_version),
MP_ROM_PTR(&mod_trezorutils_storagedevice_set_version_obj)},
};
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorstoragedevice_globals,
mp_module_trezorstoragedevice_globals_table);
const mp_obj_module_t mp_module_trezorstoragedevice = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&mp_module_trezorstoragedevice_globals,
};
MP_REGISTER_MODULE(MP_QSTR_trezorstoragedevice, mp_module_trezorstoragedevice,
MICROPY_PY_TREZORSTORAGEDEVICE);

View File

@ -143,6 +143,7 @@ fn generate_micropython_bindings() {
.allowlist_var("mp_type_dict")
// fun
.allowlist_type("mp_obj_fun_builtin_fixed_t")
.allowlist_var("mp_type_fun_builtin_0")
.allowlist_var("mp_type_fun_builtin_1")
.allowlist_var("mp_type_fun_builtin_2")
.allowlist_var("mp_type_fun_builtin_3")

View File

@ -9,16 +9,13 @@ mp_obj_t protobuf_decode(mp_obj_t buf, mp_obj_t def,
mp_obj_t protobuf_len(mp_obj_t obj);
mp_obj_t protobuf_encode(mp_obj_t buf, mp_obj_t obj);
mp_obj_t storagedevice_is_version_stored();
mp_obj_t storagedevice_get_version();
mp_obj_t storagedevice_set_version(mp_obj_t key);
#ifdef TREZOR_EMULATOR
mp_obj_t protobuf_debug_msg_type();
mp_obj_t protobuf_debug_msg_def_type();
#endif
extern mp_obj_module_t mp_module_trezorui2;
extern mp_obj_module_t mp_module_trezorstoragedevice;
#ifdef TREZOR_EMULATOR
mp_obj_t ui_debug_layout_type();

View File

@ -19,6 +19,13 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_text;
MP_QSTR_request_pin;
// storagedevice
MP_QSTR___name_storage__;
MP_QSTR_is_version_stored;
MP_QSTR_is_initialized;
MP_QSTR_get_version;
MP_QSTR_set_version;
MP_QSTR_set_timer_fn;
MP_QSTR_touch_event;
MP_QSTR_button_event;

View File

@ -5,10 +5,10 @@
#![allow(dead_code)]
mod error;
mod storagedevice;
#[macro_use]
mod micropython;
mod protobuf;
mod storagedevice;
mod time;
#[cfg(feature = "ui_debug")]
mod trace;

View File

@ -1,3 +1,20 @@
/// Create an object for an exported function taking 0 args.
macro_rules! obj_fn_0 {
($f:expr) => {{
#[allow(unused_unsafe)]
unsafe {
use $crate::micropython::ffi;
ffi::mp_obj_fun_builtin_fixed_t {
base: ffi::mp_obj_base_t {
type_: &ffi::mp_type_fun_builtin_0,
},
fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _0: Some($f) },
}
}
}};
}
/// Create an object for an exported function taking 1 arg.
macro_rules! obj_fn_1 {
($f:expr) => {{

View File

@ -1 +1,2 @@
#[macro_use]
mod storagedevice;

View File

@ -1,15 +1,18 @@
use crate::{
micropython::{buffer::Buffer, obj::Obj},
micropython::{buffer::Buffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
trezorhal::secbool,
util,
};
use core::convert::{TryFrom, TryInto};
// TODO: transfer this into a struct with field specifying data type and
// max_length
const FLAG_PUBLIC: u8 = 0x80;
const APP_DEVICE: u8 = 0x01;
// TODO: transfer this into a struct with field specifying data type and
// `max_length`, possibly even `is_public`
const DEVICE_ID: u8 = 0x01;
const _VERSION: u8 = 0x02;
const _MNEMONIC_SECRET: u8 = 0x03;
@ -50,20 +53,18 @@ extern "C" {
fn storage_set(key: u16, val: Buffer, len: u16) -> secbool::Secbool;
}
#[no_mangle]
pub extern "C" fn storagedevice_is_version_stored() -> Obj {
extern "C" fn storagedevice_is_version_stored() -> Obj {
let block = || {
let key = _get_appkey(_VERSION);
let key = _get_appkey(_VERSION, false);
let result = storagedevice_storage_has(key);
Ok(result.into())
};
unsafe { util::try_or_raise(block) }
}
#[no_mangle]
pub extern "C" fn storagedevice_get_version() -> Obj {
extern "C" fn storagedevice_get_version() -> Obj {
let block = || {
let key = _get_appkey(_VERSION);
let key = _get_appkey(_VERSION, false);
let (buf, len) = storagedevice_storage_get(key);
let result = &buf[..len as usize];
// let result = storagedevice_storage_get(key);
@ -72,18 +73,27 @@ pub extern "C" fn storagedevice_get_version() -> Obj {
unsafe { util::try_or_raise(block) }
}
#[no_mangle]
pub extern "C" fn storagedevice_set_version(version: Obj) -> Obj {
extern "C" fn storagedevice_set_version(version: Obj) -> Obj {
let block = || {
let value = Buffer::try_from(version)?;
let key = _get_appkey(_VERSION);
let key = _get_appkey(_VERSION, false);
let result = storagedevice_storage_set(key, value);
Ok(result.into())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn storagedevice_is_initialized() -> Obj {
let block = || {
let key = _get_appkey(INITIALIZED, true);
let (_, len) = storagedevice_storage_get(key);
let result = if len > 0 { true } else { false };
Ok(result.into())
};
unsafe { util::try_or_raise(block) }
}
// TODO: find out how to return the real result
// pub fn storagedevice_storage_get(key: u16) -> &'static [u8] {
// pub fn storagedevice_storage_get(key: u16) -> [u8] {
@ -123,17 +133,51 @@ pub fn storagedevice_storage_delete(key: u16) -> bool {
}
}
fn _get_appkey(key: u8) -> u16 {
((APP_DEVICE as u16) << 8) | key as u16
// TODO: we could include `is_public` bool into each key's struct item
fn _get_appkey(key: u8, is_public: bool) -> u16 {
let app = if is_public {
APP_DEVICE | FLAG_PUBLIC
} else {
APP_DEVICE
};
((app as u16) << 8) | key as u16
}
#[no_mangle]
pub static mp_module_trezorstoragedevice: Module = obj_module! {
Qstr::MP_QSTR___name_storage__ => Qstr::MP_QSTR_trezorstoragedevice.to_obj(),
/// def is_version_stored() -> bool:
/// """Whether version is in storage."""
Qstr::MP_QSTR_is_version_stored => obj_fn_0!(storagedevice_is_version_stored).as_obj(),
/// def is_initialized() -> bool:
/// """Whether device is initialized."""
Qstr::MP_QSTR_is_initialized => obj_fn_0!(storagedevice_is_initialized).as_obj(),
/// def get_version() -> bytes:
/// """Get from storage."""
Qstr::MP_QSTR_get_version => obj_fn_0!(storagedevice_get_version).as_obj(),
/// def set_version(version: bytes) -> bool:
/// """Save to storage."""
Qstr::MP_QSTR_set_version => obj_fn_1!(storagedevice_set_version).as_obj(),
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_appkey() {
let result = _get_appkey(0x11);
let result = _get_appkey(0x11, false);
assert_eq!(result, 0x0111);
}
#[test]
fn get_appkey_public() {
let result = _get_appkey(0x11, true);
assert_eq!(result, 0x8111);
}
}

View File

@ -1,16 +1,21 @@
from typing import *
# extmod/rustmods/modtrezorstoragedevice.c
# rust/src/storagedevice/storagedevice.rs
def is_version_stored() -> bool:
"""Whether version is in storage."""
# extmod/rustmods/modtrezorstoragedevice.c
# rust/src/storagedevice/storagedevice.rs
def is_initialized() -> bool:
"""Whether device is initialized."""
# rust/src/storagedevice/storagedevice.rs
def get_version() -> bytes:
"""Get from storage."""
# extmod/rustmods/modtrezorstoragedevice.c
# rust/src/storagedevice/storagedevice.rs
def set_version(version: bytes) -> bool:
"""Save to storage."""

View File

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
import storage.cache
import storage.device
from storage import trezorstoragedevice
from trezor import config, utils, wire, workflow
from trezor.enums import MessageType
from trezor.messages import Success
@ -76,7 +77,7 @@ def get_features() -> Features:
Capability.PassphraseEntry,
]
f.sd_card_present = sdcard.is_present()
f.initialized = storage.device.is_initialized()
f.initialized = trezorstoragedevice.is_initialized()
# private fields:
if config.is_unlocked():

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
from storage import cache, device
from storage import cache, trezorstoragedevice
from trezor import wire
from trezor.crypto import bip32, cardano
from trezor.enums import CardanoDerivationType
@ -115,7 +115,7 @@ def is_minting_path(path: Bip32Path) -> bool:
def derive_and_store_secrets(passphrase: str) -> None:
assert device.is_initialized()
assert trezorstoragedevice.is_initialized()
assert cache.get(cache.APP_COMMON_DERIVE_CARDANO)
if not mnemonic.is_bip39():
@ -152,7 +152,7 @@ async def _get_secret(ctx: wire.Context, cache_entry: int) -> bytes:
async def _get_keychain_bip39(
ctx: wire.Context, derivation_type: CardanoDerivationType
) -> Keychain:
if not device.is_initialized():
if not trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if derivation_type == CardanoDerivationType.LEDGER:

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
from storage import cache, device
from storage import cache, device, trezorstoragedevice
from trezor import utils, wire
from trezor.crypto import bip32, hmac
@ -48,7 +48,7 @@ if not utils.BITCOIN_ONLY:
# expose a method for Cardano to do the same
async def derive_and_store_roots(ctx: wire.Context) -> None:
if not device.is_initialized():
if not trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
need_seed = not cache.is_set(cache.APP_COMMON_SEED)
@ -89,7 +89,7 @@ else:
@cache.stored(cache.APP_COMMON_SEED_WITHOUT_PASSPHRASE)
def _get_seed_without_passphrase() -> bytes:
if not device.is_initialized():
if not trezorstoragedevice.is_initialized():
raise Exception("Device is not initialized")
return mnemonic.get_seed(progress_bar=False)

View File

@ -51,7 +51,7 @@ async def load_device(ctx: wire.Context, msg: LoadDevice) -> Success:
def _validate(msg: LoadDevice) -> int:
if storage.device.is_initialized():
if storage.trezorstoragedevice.is_initialized():
raise wire.UnexpectedMessage("Already initialized")
if not msg.mnemonics:

View File

@ -25,7 +25,7 @@ class Homescreen(HomescreenBase):
def __init__(self) -> None:
super().__init__()
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
self.label = "Go to trezor.io/start"
self.loader = Loader(
@ -38,13 +38,19 @@ class Homescreen(HomescreenBase):
def do_render(self) -> None:
# warning bar on top
if storage.device.is_initialized() and storage.device.no_backup():
if storage.trezorstoragedevice.is_initialized() and storage.device.no_backup():
ui.header_error("SEEDLESS")
elif storage.device.is_initialized() and storage.device.unfinished_backup():
elif (
storage.trezorstoragedevice.is_initialized()
and storage.device.unfinished_backup()
):
ui.header_error("BACKUP FAILED!")
elif storage.device.is_initialized() and storage.device.needs_backup():
elif (
storage.trezorstoragedevice.is_initialized()
and storage.device.needs_backup()
):
ui.header_warning("NEEDS BACKUP!")
elif storage.device.is_initialized() and not config.has_pin():
elif storage.trezorstoragedevice.is_initialized() and not config.has_pin():
ui.header_warning("PIN NOT SET!")
elif storage.device.get_experimental_features():
ui.header_warning("EXPERIMENTAL MODE!")

View File

@ -10,7 +10,7 @@ if TYPE_CHECKING:
async def apply_flags(ctx: wire.GenericContext, msg: ApplyFlags) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
set_flags(msg.flags)
return Success(message="Flags applied")

View File

@ -34,7 +34,7 @@ def validate_homescreen(homescreen: bytes) -> None:
async def apply_settings(ctx: wire.Context, msg: ApplySettings) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if (
msg.homescreen is None

View File

@ -14,7 +14,7 @@ if TYPE_CHECKING:
async def backup_device(ctx: wire.Context, msg: BackupDevice) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if not storage.device.needs_backup():
raise wire.ProcessError("Seed already backed up")

View File

@ -8,7 +8,7 @@ from trezor.ui.layouts import confirm_action
async def get_next_u2f_counter(
ctx: wire.Context, msg: GetNextU2FCounter
) -> NextU2FCounter:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
await confirm_action(

View File

@ -70,9 +70,9 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
def _validate(msg: RecoveryDevice) -> None:
if not msg.dry_run and storage.device.is_initialized():
if not msg.dry_run and storage.trezorstoragedevice.is_initialized():
raise wire.UnexpectedMessage("Already initialized")
if msg.dry_run and not storage.device.is_initialized():
if msg.dry_run and not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if msg.enforce_wordlist is False:

View File

@ -185,7 +185,7 @@ def _validate_reset_device(msg: ResetDevice) -> None:
raise wire.ProcessError("Invalid strength (has to be 128, 192 or 256 bits)")
if msg.display_random and (msg.skip_backup or msg.no_backup):
raise wire.ProcessError("Can't show internal entropy when backup is skipped")
if storage.device.is_initialized():
if storage.trezorstoragedevice.is_initialized():
raise wire.UnexpectedMessage("Already initialized")

View File

@ -39,7 +39,7 @@ async def _set_salt(
async def sd_protect(ctx: wire.Context, msg: SdProtect) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if msg.operation == SdProtectOperationType.ENABLE:

View File

@ -6,7 +6,7 @@ from trezor.ui.layouts import confirm_action
async def set_u2f_counter(ctx: wire.Context, msg: SetU2FCounter) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if msg.u2f_counter is None:
raise wire.ProcessError("No value provided")

View File

@ -28,7 +28,7 @@ class ConfirmAddCredential(ConfirmInfo):
async def add_resident_credential(
ctx: wire.Context, msg: WebAuthnAddResidentCredential
) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if not msg.credential_id:
raise wire.ProcessError("Missing credential ID parameter.")

View File

@ -1190,7 +1190,7 @@ def msg_register(req: Msg, dialog_mgr: DialogManager) -> Cmd:
dialog_mgr.set_state(new_state)
return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
if __debug__:
log.warning(__name__, "not initialized")
# There is no standard way to decline a U2F request, but responding with ERR_CHANNEL_BUSY
@ -1271,7 +1271,7 @@ def msg_authenticate(req: Msg, dialog_mgr: DialogManager) -> Cmd:
dialog_mgr.set_state(new_state)
return msg_error(req.cid, _SW_CONDITIONS_NOT_SATISFIED)
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
if __debug__:
log.warning(__name__, "not initialized")
# Device is not registered with the RP.
@ -1450,7 +1450,7 @@ def cbor_make_credential(req: Cmd, dialog_mgr: DialogManager) -> Cmd | None:
def cbor_make_credential_process(req: Cmd, dialog_mgr: DialogManager) -> State | Cmd:
from . import knownapps
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
if __debug__:
log.warning(__name__, "not initialized")
return cbor_error(req.cid, _ERR_OTHER)
@ -1628,7 +1628,7 @@ def cbor_get_assertion(req: Cmd, dialog_mgr: DialogManager) -> Cmd | None:
def cbor_get_assertion_process(req: Cmd, dialog_mgr: DialogManager) -> State | Cmd:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
if __debug__:
log.warning(__name__, "not initialized")
return cbor_error(req.cid, _ERR_OTHER)
@ -1888,7 +1888,7 @@ def cbor_client_pin(req: Cmd) -> Cmd:
def cbor_reset(req: Cmd, dialog_mgr: DialogManager) -> Cmd | None:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
if __debug__:
log.warning(__name__, "not initialized")
# Return success, because the authenticator is already in factory default state.

View File

@ -28,7 +28,7 @@ class ConfirmRemoveCredential(ConfirmInfo):
async def remove_resident_credential(
ctx: wire.Context, msg: WebAuthnRemoveResidentCredential
) -> Success:
if not storage.device.is_initialized():
if not storage.trezorstoragedevice.is_initialized():
raise wire.NotInitialized("Device is not initialized")
if msg.index is None:
raise wire.ProcessError("Missing credential index parameter.")

View File

@ -21,7 +21,10 @@ def init_unlocked() -> None:
# In FWs <= 2.3.1 'version' denoted whether the device is initialized or not.
# In 2.3.2 we have introduced a new field 'initialized' for that.
if trezorstoragedevice.is_version_stored() and not device.is_initialized():
if (
trezorstoragedevice.is_version_stored()
and not trezorstoragedevice.is_initialized()
):
common.set_bool(common.APP_DEVICE, device.INITIALIZED, True, public=True)

View File

@ -12,7 +12,7 @@ class TestCredential(unittest.TestCase):
def test_fido2_credential_decode(self):
mnemonic_secret = b"all all all all all all all all all all all all"
mnemonic.get_secret = lambda: mnemonic_secret
storage.device.is_initialized = lambda: True
storage.trezorstoragedevice.is_initialized = lambda: True
cred_id = (
b"f1d0020013e65c865634ad8abddf7a66df56ae7d8c3afd356f76426801508b2e"
@ -24,9 +24,7 @@ class TestCredential(unittest.TestCase):
rp_id = "example.com"
rp_id_hash = sha256(rp_id).digest()
user_id = (
b"3082019330820138a0030201023082019330820138a003020102308201933082"
)
user_id = b"3082019330820138a0030201023082019330820138a003020102308201933082"
user_name = "johnpsmith@example.com"
@ -108,18 +106,32 @@ class TestCredential(unittest.TestCase):
c2 = U2fCredential()
self.assertEqual(sorted(distinguishable_cred_list([a1, a2, a3, b1, b2, c1, c2])), [b2, a3, a1, c1])
self.assertEqual(sorted(distinguishable_cred_list([c2, c1, b2, b1, a3, a2, a1])), [b2, a3, a1, c2])
self.assertEqual(
sorted(distinguishable_cred_list([a1, a2, a3, b1, b2, c1, c2])),
[b2, a3, a1, c1],
)
self.assertEqual(
sorted(distinguishable_cred_list([c2, c1, b2, b1, a3, a2, a1])),
[b2, a3, a1, c2],
)
# Test input by creation time.
self.assertEqual(sorted(distinguishable_cred_list([b2, a3, c1, a2, b1, a1, c2])), [b2, a3, a1, c1])
self.assertEqual(sorted(distinguishable_cred_list([c2, a1, b1, a2, c1, a3, b2])), [b2, a3, a1, c2])
self.assertEqual(
sorted(distinguishable_cred_list([b2, a3, c1, a2, b1, a1, c2])),
[b2, a3, a1, c1],
)
self.assertEqual(
sorted(distinguishable_cred_list([c2, a1, b1, a2, c1, a3, b2])),
[b2, a3, a1, c2],
)
# Test duplicities.
self.assertEqual(sorted(distinguishable_cred_list([c1, a1, a1, c2, c1])), [a1, c1])
self.assertEqual(
sorted(distinguishable_cred_list([c1, a1, a1, c2, c1])), [a1, c1]
)
self.assertEqual(sorted(distinguishable_cred_list([b2, b3])), [b2])
self.assertEqual(sorted(distinguishable_cred_list([b3, b2])), [b3])
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()