1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-25 00:48:19 +00:00

cardano: Implement SLIP-0023 and add SLIP-0039 support for Cardano.

This commit is contained in:
Andrew Kozlik 2019-07-24 19:37:04 +02:00
parent 6e249a9d20
commit 217c910b4b
8 changed files with 192 additions and 46 deletions

View File

@ -601,10 +601,19 @@ STATIC mp_obj_t mod_trezorcrypto_bip32_from_seed(mp_obj_t seed,
if (curveb.len == 0) {
mp_raise_ValueError("Invalid curve name");
}
HDNode hdnode;
if (!hdnode_from_seed(seedb.buf, seedb.len, curveb.buf, &hdnode)) {
int res = 0;
if (strcmp(curveb.buf, ED25519_CARDANO_NAME) != 0) {
res = hdnode_from_seed(seedb.buf, seedb.len, curveb.buf, &hdnode);
} else {
res = hdnode_from_seed_cardano(seedb.buf, seedb.len, &hdnode);
}
if (!res) {
mp_raise_ValueError("Failed to derive the root node");
}
mp_obj_HDNode_t *o = m_new_obj(mp_obj_HDNode_t);
o->base.type = &mod_trezorcrypto_HDNode_type;
o->hdnode = hdnode;
@ -616,7 +625,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorcrypto_bip32_from_seed_obj,
/// def from_mnemonic_cardano(mnemonic: str, passphrase: str) -> bytes:
/// """
/// Convert mnemonic to hdnode
/// Construct a HD node from a BIP-0039 mnemonic using the Icarus derivation
/// scheme, aka v2 derivation scheme.
/// """
STATIC mp_obj_t mod_trezorcrypto_bip32_from_mnemonic_cardano(
mp_obj_t mnemonic, mp_obj_t passphrase) {
@ -634,9 +644,9 @@ STATIC mp_obj_t mod_trezorcrypto_bip32_from_mnemonic_cardano(
mp_raise_ValueError("Invalid mnemonic");
}
const int res =
hdnode_from_seed_cardano((const uint8_t *)ppassphrase, phrase.len,
entropy, entropy_len / 8, &hdnode);
const int res = hdnode_from_entropy_cardano_icarus(
(const uint8_t *)ppassphrase, phrase.len, entropy, entropy_len / 8,
&hdnode);
if (!res) {
mp_raise_ValueError(

View File

@ -133,5 +133,6 @@ def from_seed(seed: bytes, curve_name: str) -> HDNode:
# extmod/modtrezorcrypto/modtrezorcrypto-bip32.h
def from_mnemonic_cardano(mnemonic: str, passphrase: str) -> bytes:
"""
Convert mnemonic to hdnode
Construct a HD node from a BIP-0039 mnemonic using the Icarus derivation
scheme, aka v2 derivation scheme.
"""

View File

@ -28,22 +28,30 @@ class Keychain:
return node
async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized():
raise wire.ProcessError("Device is not initialized")
# derive the root node from mnemonic and passphrase
async def _get_passphrase(ctx: wire.Context) -> bytes:
passphrase = cache.get_passphrase()
if passphrase is None:
passphrase = await protect_by_passphrase(ctx)
cache.set_passphrase(passphrase)
# TODO fix for SLIP-39!
mnemonic_secret, mnemonic_module = mnemonic.get()
if mnemonic_module == mnemonic.slip39:
# TODO: we need to modify bip32.from_mnemonic_cardano to accept entropy directly
raise NotImplementedError("SLIP-39 currently does not support Cardano")
return passphrase
async def get_keychain(ctx: wire.Context) -> Keychain:
if not storage.is_initialized():
raise wire.ProcessError("Device is not initialized")
if mnemonic.get_type() == mnemonic.TYPE_SLIP39:
seed = cache.get_seed()
if seed is None:
passphrase = await _get_passphrase(ctx)
seed = mnemonic.get_seed(passphrase)
cache.set_seed(seed)
root = bip32.from_seed(seed, "ed25519 cardano seed")
else:
root = bip32.from_mnemonic_cardano(mnemonic_secret.decode(), passphrase)
# derive the root node from mnemonic and passphrase
passphrase = await _get_passphrase(ctx)
root = bip32.from_mnemonic_cardano(mnemonic.get_secret().decode(), passphrase)
# derive the namespaced root node
for i in SEED_NAMESPACE:

View File

@ -6,7 +6,7 @@ from trezor.crypto import bip39, slip39
from apps.common import storage
if False:
from typing import Tuple
from typing import Optional, Tuple
TYPE_BIP39 = const(0)
TYPE_SLIP39 = const(1)
@ -20,16 +20,25 @@ TYPES_WORD_COUNT = {
}
def get() -> Tuple[bytes, int]:
mnemonic_secret = storage.device.get_mnemonic_secret()
def get() -> Tuple[Optional[bytes], int]:
return get_secret(), get_type()
def get_secret() -> Optional[bytes]:
return storage.device.get_mnemonic_secret()
def get_type() -> int:
mnemonic_type = storage.device.get_mnemonic_type() or TYPE_BIP39
if mnemonic_type not in (TYPE_BIP39, TYPE_SLIP39):
raise RuntimeError("Invalid mnemonic type")
return mnemonic_secret, mnemonic_type
return mnemonic_type
def get_seed(passphrase: str = "", progress_bar: bool = True) -> bytes:
mnemonic_secret, mnemonic_type = get()
if mnemonic_secret is None:
raise ValueError("Mnemonic not set")
render_func = None
if progress_bar:

View File

@ -9,7 +9,7 @@ from apps.cardano.address import (
derive_address_and_node
)
from apps.cardano.seed import Keychain
from trezor.crypto import bip32
from trezor.crypto import bip32, slip39
class TestCardanoAddress(unittest.TestCase):
@ -176,5 +176,78 @@ class TestCardanoAddress(unittest.TestCase):
address_root = _get_address_root(root_node, {1: b'X\x1cr,zu\x81?\xaf\xde\x9f\xf9\xe4\xd4\x90\xadH$\xe9\xf3\x88\x16\xcb\xd2)\x02M\x0c#\xde'})
self.assertEqual(address_root, b'\xb3\xbbS\xa8;uN:E=\xe8\xe5\x9c\x18\xbcn\xcf\xd0c\xba\x0e\xba\xaelL}\xba\xbb')
def test_slip39_128(self):
mnemonics = ["extra extend academic bishop cricket bundle tofu goat apart victim enlarge program behavior permit course armed jerky faint language modern",
"extra extend academic acne away best indicate impact square oasis prospect painting voting guest either argue username racism enemy eclipse",
"extra extend academic arcade born dive legal hush gross briefing talent drug much home firefly toxic analysis idea umbrella slice"]
passphrase = b"TREZOR"
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)
node = bip32.from_seed(master_secret, "ed25519 cardano seed")
# Check root node.
root_priv = b"c0fe4a6973df4de06262693fc9186f71faf292960350882d49456bf108d13954"
root_pub = b"83e3ecaf57f90f022c45e10d1b8cb78499c30819515ad9a81ad82139fdb12a90"
root_ext = b"4064253ffefc4127489bce1b825a47329010c5afb4d21154ef949ef786204405"
root_chain = b"22c12755afdd192742613b3062069390743ea232bc1b366c8f41e37292af9305"
self.assertEqual(hexlify(node.private_key()), root_priv)
self.assertEqual(hexlify(node.private_key_ext()), root_ext)
self.assertEqual(hexlify(seed.remove_ed25519_prefix(node.public_key())), root_pub)
self.assertEqual(hexlify(node.chain_code()), root_chain)
# Check derived nodes and addresses.
node.derive_cardano(0x80000000 | 44)
node.derive_cardano(0x80000000 | 1815)
keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)
addresses = [
"Ae2tdPwUPEYxF9NAMNdd3v2LZoMeWp7gCZiDb6bZzFQeeVASzoP7HC4V9s6",
"Ae2tdPwUPEZ1TjYcvfkWAbiHtGVxv4byEHHZoSyQXjPJ362DifCe1ykgqgy",
"Ae2tdPwUPEZGXmSbda1kBNfyhRQGRcQxJFdk7mhWZXAGnapyejv2b2U3aRb"
]
for i, expected in enumerate(addresses):
# 44'/1815'/0'/0/i
address, _ = derive_address_and_node(keychain, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
self.assertEqual(address, expected)
def test_slip39_256(self):
mnemonics = ["hobo romp academic axis august founder knife legal recover alien expect emphasis loan kitchen involve teacher capture rebuild trial numb spider forward ladle lying voter typical security quantity hawk legs idle leaves gasoline",
"hobo romp academic agency ancestor industry argue sister scene midst graduate profile numb paid headset airport daisy flame express scene usual welcome quick silent downtown oral critical step remove says rhythm venture aunt"]
passphrase = b"TREZOR"
identifier, exponent, ems = slip39.combine_mnemonics(mnemonics)
master_secret = slip39.decrypt(identifier, exponent, ems, passphrase)
node = bip32.from_seed(master_secret, "ed25519 cardano seed")
# Check root node.
root_priv = b"90633724b5daf770a8b420b8658e7d8bc21e066b60ec8cd4d5730681cc294e4f"
root_pub = b"eea170f0ef97b59d22907cb429888029721ed67d3e7a1b56b81731086ab7db64"
root_ext = b"f9d99bf3cd9c7e12663e8646afa40cb3aecf15d91f2abc15d21056c6bccb3414"
root_chain = b"04f1de750b62725fcc1ae1b93ca4063acb53c486b959cadaa100ebd7828e5460"
self.assertEqual(hexlify(node.private_key()), root_priv)
self.assertEqual(hexlify(node.private_key_ext()), root_ext)
self.assertEqual(hexlify(seed.remove_ed25519_prefix(node.public_key())), root_pub)
self.assertEqual(hexlify(node.chain_code()), root_chain)
# Check derived nodes and addresses.
node.derive_cardano(0x80000000 | 44)
node.derive_cardano(0x80000000 | 1815)
keychain = Keychain([0x80000000 | 44, 0x80000000 | 1815], node)
addresses = [
"Ae2tdPwUPEYyDD1C2FbVJFAE3FuAxLspfMYt29TJ1urnSKr57cVhEcioSCC",
"Ae2tdPwUPEZHJGtyz47F6wD7qAegt1JNRJWuiE36QLvFzeqJPBZ2EBvhr8M",
"Ae2tdPwUPEYxD9xNPBJTzYmtFVVWEPB6KW4TCDijQ4pDwU11wt5621PyCi4"
]
for i, expected in enumerate(addresses):
# 44'/1815'/0'/0/i
address, _ = derive_address_and_node(keychain, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
self.assertEqual(address, expected)
if __name__ == '__main__':
unittest.main()

View File

@ -50,6 +50,8 @@
#endif
#include "memzero.h"
#define CARDANO_MAX_NODE_DEPTH 1048576
const curve_info ed25519_info = {
.bip32_name = "ed25519 seed",
.params = NULL,
@ -288,6 +290,10 @@ static void scalar_add_256bits(const uint8_t *src1, const uint8_t *src2,
}
int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) {
if (inout->depth >= CARDANO_MAX_NODE_DEPTH) {
return 0;
}
// checks for hardened/non-hardened derivation, keysize 32 means we are
// dealing with public key and thus non-h, keysize 64 is for private key
int keysize = 32;
@ -356,31 +362,61 @@ int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) {
return 1;
}
int hdnode_from_seed_cardano(const uint8_t *pass, int pass_len,
const uint8_t *seed, int seed_len, HDNode *out) {
static CONFIDENTIAL uint8_t secret[96];
pbkdf2_hmac_sha512(pass, pass_len, seed, seed_len, 4096, secret, 96);
secret[0] &= 248;
secret[31] &= 31;
secret[31] |= 64;
static int hdnode_from_secret_cardano(const uint8_t *k,
const uint8_t *chain_code, HDNode *out) {
memzero(out, sizeof(HDNode));
out->depth = 0;
out->child_num = 0;
out->curve = get_curve_by_name(ED25519_CARDANO_NAME);
out->curve = &ed25519_cardano_info;
memcpy(out->private_key, k, 32);
memcpy(out->private_key_extension, k + 32, 32);
memcpy(out->chain_code, chain_code, 32);
memcpy(out->private_key, secret, 32);
memcpy(out->private_key_extension, secret + 32, 32);
out->private_key[0] &= 0xf8;
out->private_key[31] &= 0x1f;
out->private_key[31] |= 0x40;
out->public_key[0] = 0;
hdnode_fill_public_key(out);
memcpy(out->chain_code, secret + 64, 32);
memzero(secret, sizeof(secret));
return 1;
}
// Derives the root Cardano HDNode from a master secret, aka seed, as defined in
// SLIP-0023.
int hdnode_from_seed_cardano(const uint8_t *seed, int seed_len, HDNode *out) {
static CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH];
static CONFIDENTIAL uint8_t k[SHA512_DIGEST_LENGTH];
static CONFIDENTIAL HMAC_SHA512_CTX ctx;
hmac_sha512_Init(&ctx, (const uint8_t *)ED25519_CARDANO_NAME,
strlen(ED25519_CARDANO_NAME));
hmac_sha512_Update(&ctx, seed, seed_len);
hmac_sha512_Final(&ctx, I);
sha512_Raw(I, 32, k);
int ret = hdnode_from_secret_cardano(k, I + 32, out);
memzero(I, sizeof(I));
memzero(k, sizeof(k));
memzero(&ctx, sizeof(ctx));
return ret;
}
// Derives the root Cardano HDNode from a passphrase and the entropy encoded in
// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation
// scheme.
int hdnode_from_entropy_cardano_icarus(const uint8_t *pass, int pass_len,
const uint8_t *entropy, int entropy_len,
HDNode *out) {
static CONFIDENTIAL uint8_t secret[96];
pbkdf2_hmac_sha512(pass, pass_len, entropy, entropy_len, 4096, secret, 96);
int ret = hdnode_from_secret_cardano(secret, secret + 64, out);
memzero(secret, sizeof(secret));
return ret;
}
#endif
int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent,

View File

@ -71,8 +71,10 @@ int hdnode_private_ckd(HDNode *inout, uint32_t i);
#if USE_CARDANO
int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i);
int hdnode_from_seed_cardano(const uint8_t *pass, int pass_len,
const uint8_t *seed, int seed_len, HDNode *out);
int hdnode_from_seed_cardano(const uint8_t *seed, int seed_len, HDNode *out);
int hdnode_from_entropy_cardano_icarus(const uint8_t *pass, int pass_len,
const uint8_t *seed, int seed_len,
HDNode *out);
#endif
int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent,

View File

@ -118,7 +118,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_1) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
ck_assert_mem_eq(
node.chain_code,
@ -153,7 +154,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_2) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000000);
@ -190,7 +192,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_3) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -227,7 +230,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_4) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -265,7 +269,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_5) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -304,7 +309,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_6) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -344,7 +350,8 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) {
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_seed_cardano((const uint8_t *)"", 0, seed, seed_len / 8, &node);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);