From f6f52445bde7d2926a6cb15198b662f188345b3b Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 20 Oct 2021 16:18:15 +0200 Subject: [PATCH] feat(core/cardano): support Icarus, Icarus-Trezor, and Ledger derivations --- .../modtrezorcrypto/modtrezorcrypto-cardano.h | 83 +++++++++++++++---- core/mocks/generated/trezorcrypto/cardano.pyi | 16 +++- core/src/apps/common/mnemonic.py | 10 ++- 3 files changed, 85 insertions(+), 24 deletions(-) diff --git a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-cardano.h b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-cardano.h index da3e6f993..e3e520cf6 100644 --- a/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-cardano.h +++ b/core/embed/extmod/modtrezorcrypto/modtrezorcrypto-cardano.h @@ -30,29 +30,33 @@ #include "memzero.h" /// package: trezorcrypto.cardano +/// from trezorcrypto.bip32 import HDNode -/// def derive_icarus_trezor( +/// def derive_icarus( /// mnemonic: str, /// passphrase: str, +/// trezor_derivation: bool, /// callback: Callable[[int, int], None] | None = None, /// ) -> bytes: /// """ /// Derives a Cardano master secret from a mnemonic and passphrase using the -/// Icarus-Trezor derivation scheme. This differs from the Icarus scheme by -/// including checksum if the mnemonic is 24 words. +/// Icarus derivation scheme. +/// If `trezor_derivation` is True, the Icarus-Trezor variant is used (see +/// CIP-3). /// """ -STATIC mp_obj_t mod_trezorcrypto_cardano_derive_icarus_trezor( - size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_trezorcrypto_cardano_derive_icarus(size_t n_args, + const mp_obj_t *args) { mp_buffer_info_t mnemo = {0}, phrase = {0}; mp_get_buffer_raise(args[0], &mnemo, MP_BUFFER_READ); mp_get_buffer_raise(args[1], &phrase, MP_BUFFER_READ); const char *pmnemonic = mnemo.len > 0 ? mnemo.buf : ""; const char *ppassphrase = phrase.len > 0 ? phrase.buf : ""; + bool trezor_derivation = mp_obj_is_true(args[2]); + uint8_t mnemonic_bits[64] = {0}; int mnemonic_bits_len = mnemonic_to_bits(pmnemonic, mnemonic_bits); - - if (mnemonic_bits_len == 0) { + if (mnemonic_bits_len == 0 || mnemonic_bits_len % 33 != 0) { mp_raise_ValueError("Invalid mnemonic"); } @@ -60,18 +64,25 @@ STATIC mp_obj_t mod_trezorcrypto_cardano_derive_icarus_trezor( vstr_init_len(&vstr, CARDANO_SECRET_LENGTH); void (*callback)(uint32_t current, uint32_t total) = NULL; - if (n_args > 2) { + if (n_args > 3) { // generate with a progress callback - ui_wait_callback = args[2]; + ui_wait_callback = args[3]; callback = wrapped_ui_wait_callback; } - // BEWARE: unlike `derive_icarus`, we are passing the raw result of - // `mnemonic_bits`, i.e., entropy + checksum bits. More info at - // https://github.com/trezor/trezor-firmware/issues/1387 and CIP-3 + int entropy_len = mnemonic_bits_len - mnemonic_bits_len / 33; + int mnemonic_bytes_used = 0; + if (!trezor_derivation) { + // Exclude checksum (original Icarus spec) + mnemonic_bytes_used = entropy_len / 8; + } else { + // Include checksum if it is a full byte (Trezor bug) + // see also https://github.com/trezor/trezor-firmware/issues/1387 and CIP-3 + mnemonic_bytes_used = mnemonic_bits_len / 8; + } const int res = secret_from_entropy_cardano_icarus( (const uint8_t *)ppassphrase, phrase.len, mnemonic_bits, - mnemonic_bits_len / 8, (uint8_t *)vstr.buf, callback); + mnemonic_bytes_used, (uint8_t *)vstr.buf, callback); ui_wait_callback = mp_const_none; @@ -84,8 +95,8 @@ STATIC mp_obj_t mod_trezorcrypto_cardano_derive_icarus_trezor( } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( - mod_trezorcrypto_cardano_derive_icarus_trezor_obj, 2, 3, - mod_trezorcrypto_cardano_derive_icarus_trezor); + mod_trezorcrypto_cardano_derive_icarus_obj, 3, 4, + mod_trezorcrypto_cardano_derive_icarus); /// def from_secret(secret: bytes) -> HDNode: /// """ @@ -148,14 +159,52 @@ STATIC mp_obj_t mod_trezorcrypto_from_seed_slip23(mp_obj_t seed) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_from_seed_slip23_obj, mod_trezorcrypto_from_seed_slip23); +/// def from_seed_ledger(seed: bytes) -> HDNode: +/// """ +/// Creates a Cardano HD node from a seed via Ledger derivation. +/// """ +STATIC mp_obj_t mod_trezorcrypto_from_seed_ledger(mp_obj_t seed) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(seed, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len == 0) { + mp_raise_ValueError("Invalid seed"); + } + + uint8_t secret[CARDANO_SECRET_LENGTH] = {0}; + HDNode hdnode = {0}; + int res = 0; + + res = secret_from_seed_cardano_ledger(bufinfo.buf, bufinfo.len, secret); + if (res != 1) { + mp_raise_msg(&mp_type_RuntimeError, + "Unexpected failure in Ledger derivation."); + } + res = hdnode_from_secret_cardano(secret, &hdnode); + if (res != 1) { + mp_raise_msg(&mp_type_RuntimeError, + "Unexpected failure in constructing Cardano node."); + } + + mp_obj_HDNode_t *o = m_new_obj_with_finaliser(mp_obj_HDNode_t); + o->base.type = &mod_trezorcrypto_HDNode_type; + o->hdnode = hdnode; + o->fingerprint = hdnode_fingerprint(&o->hdnode); + return MP_OBJ_FROM_PTR(o); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_from_seed_ledger_obj, + mod_trezorcrypto_from_seed_ledger); + STATIC const mp_rom_map_elem_t mod_trezorcrypto_cardano_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardano)}, - {MP_ROM_QSTR(MP_QSTR_derive_icarus_trezor), - MP_ROM_PTR(&mod_trezorcrypto_cardano_derive_icarus_trezor_obj)}, + {MP_ROM_QSTR(MP_QSTR_derive_icarus), + MP_ROM_PTR(&mod_trezorcrypto_cardano_derive_icarus_obj)}, {MP_ROM_QSTR(MP_QSTR_from_secret), MP_ROM_PTR(&mod_trezorcrypto_from_secret_obj)}, {MP_ROM_QSTR(MP_QSTR_from_seed_slip23), MP_ROM_PTR(&mod_trezorcrypto_from_seed_slip23_obj)}, + {MP_ROM_QSTR(MP_QSTR_from_seed_ledger), + MP_ROM_PTR(&mod_trezorcrypto_from_seed_ledger_obj)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_cardano_globals, mod_trezorcrypto_cardano_globals_table); diff --git a/core/mocks/generated/trezorcrypto/cardano.pyi b/core/mocks/generated/trezorcrypto/cardano.pyi index 3779fb300..f5b839b9e 100644 --- a/core/mocks/generated/trezorcrypto/cardano.pyi +++ b/core/mocks/generated/trezorcrypto/cardano.pyi @@ -1,16 +1,19 @@ from typing import * +from trezorcrypto.bip32 import HDNode # extmod/modtrezorcrypto/modtrezorcrypto-cardano.h -def derive_icarus_trezor( +def derive_icarus( mnemonic: str, passphrase: str, + trezor_derivation: bool, callback: Callable[[int, int], None] | None = None, ) -> bytes: """ Derives a Cardano master secret from a mnemonic and passphrase using the - Icarus-Trezor derivation scheme. This differs from the Icarus scheme by - including checksum if the mnemonic is 24 words. + Icarus derivation scheme. + If `trezor_derivation` is True, the Icarus-Trezor variant is used (see + CIP-3). """ @@ -26,3 +29,10 @@ def from_seed_slip23(seed: bytes) -> HDNode: """ Creates a Cardano HD node from a seed via SLIP-23 derivation. """ + + +# extmod/modtrezorcrypto/modtrezorcrypto-cardano.h +def from_seed_ledger(seed: bytes) -> HDNode: + """ + Creates a Cardano HD node from a seed via Ledger derivation. + """ diff --git a/core/src/apps/common/mnemonic.py b/core/src/apps/common/mnemonic.py index 32e108fd5..3dc917ff7 100644 --- a/core/src/apps/common/mnemonic.py +++ b/core/src/apps/common/mnemonic.py @@ -59,8 +59,10 @@ def get_seed(passphrase: str = "", progress_bar: bool = True) -> bytes: if not utils.BITCOIN_ONLY: - def derive_cardano_icarus_trezor( - passphrase: str = "", progress_bar: bool = True + def derive_cardano_icarus( + passphrase: str = "", + trezor_derivation: bool = True, + progress_bar: bool = True, ) -> bytes: if not is_bip39(): raise ValueError # should not be called for SLIP-39 @@ -76,8 +78,8 @@ if not utils.BITCOIN_ONLY: from trezor.crypto import cardano - return cardano.derive_icarus_trezor( - mnemonic_secret, passphrase, render_func + return cardano.derive_icarus( + mnemonic_secret.decode(), passphrase, trezor_derivation, render_func )