1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 07:18:09 +00:00

Refactor mnemonic_to_entropy to not (misleadingly) return checksum

This commit is contained in:
Rafael Korbas 2020-12-15 16:04:36 +01:00 committed by Andrew Kozlik
parent d38540ca7a
commit edf9b4ee85
6 changed files with 230 additions and 65 deletions

View File

@ -600,16 +600,22 @@ STATIC mp_obj_t mod_trezorcrypto_bip32_from_mnemonic_cardano(
const char *pmnemonic = mnemo.len > 0 ? mnemo.buf : "";
const char *ppassphrase = phrase.len > 0 ? phrase.buf : "";
uint8_t entropy[64] = {0};
int entropy_len = mnemonic_to_entropy(pmnemonic, entropy);
uint8_t mnemonic_bits[64] = {0};
int mnemonic_bits_len = mnemonic_to_bits(pmnemonic, mnemonic_bits);
if (entropy_len == 0) {
if (mnemonic_bits_len == 0) {
mp_raise_ValueError("Invalid mnemonic");
}
// BEWARE: passing of mnemonic_bits (i.e. entropy + checksum bits) into
// hdnode_from_entropy_cardano_icarus() is actually not correct and we should
// be passing the entropy alone. However, the bug is there since Cardano
// support has been launched for Trezor and its reversal would result in
// people with a 24-word mnemonic on Trezor losing access to their Cardano
// funds. More info at https://github.com/trezor/trezor-firmware/issues/1387
const int res = hdnode_from_entropy_cardano_icarus(
(const uint8_t *)ppassphrase, phrase.len, entropy, entropy_len / 8,
&hdnode);
(const uint8_t *)ppassphrase, phrase.len, mnemonic_bits,
mnemonic_bits_len / 8, &hdnode);
if (!res) {
mp_raise_ValueError(

View File

@ -7,7 +7,7 @@ from trezor.crypto import bip32, slip39
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestCardanoGetPublicKey(unittest.TestCase):
def test_get_public_key_scheme(self):
def test_get_public_key_scheme_12_words(self):
mnemonic = "all all all all all all all all all all all all"
passphrase = ""
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
@ -68,6 +68,74 @@ class TestCardanoGetPublicKey(unittest.TestCase):
self.assertEqual(hexlify(key.node.chain_code), chain_codes[index])
self.assertEqual(key.xpub, xpub_keys[index])
def test_get_public_key_scheme_18_words(self):
mnemonic = "found differ bulb shadow wrist blue bind vessel deposit tip pelican action surprise weapon check fiction muscle this"
passphrase = ""
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
keychain = Keychain(node)
derivation_paths = [
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0x80000000],
[0x80000000 | 1852, 0x80000000 | 1815, 0x80000000, 0, 0],
]
public_keys = [
b'82f12f1916c0c35a412291e72204f17f033b0b7edf148dfd7d75acd3975c9ac0',
b'd92d0590e76bbf0300112a9f283fba2f7f8af5cf1054d634b610d1e4f541df90',
]
chain_codes = [
b'974f9cd4336e23b976f934752026a2d4c32d2e23f0532f4f87152b45fa5ca81f',
b'352499ed19b47f2bc8c164b47df0d55f428cc8c12f96b7e65d7563114ddfd75b',
]
xpub_keys = [
'82f12f1916c0c35a412291e72204f17f033b0b7edf148dfd7d75acd3975c9ac0974f9cd4336e23b976f934752026a2d4c32d2e23f0532f4f87152b45fa5ca81f',
'd92d0590e76bbf0300112a9f283fba2f7f8af5cf1054d634b610d1e4f541df90352499ed19b47f2bc8c164b47df0d55f428cc8c12f96b7e65d7563114ddfd75b',
]
for index, derivation_path in enumerate(derivation_paths):
key = _get_public_key(keychain, derivation_path)
self.assertEqual(hexlify(key.node.public_key), public_keys[index])
self.assertEqual(hexlify(key.node.chain_code), chain_codes[index])
self.assertEqual(key.xpub, xpub_keys[index])
def test_get_public_key_scheme_24_words(self):
mnemonic = "balance exotic ranch knife glory slow tape favorite yard gym awake ill exist useless parent aim pig stay effort into square gasp credit butter"
passphrase = ""
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
keychain = Keychain(node)
derivation_paths = [
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0x80000000],
[0x80000000 | 1852, 0x80000000 | 1815, 0x80000000, 0, 0],
]
public_keys = [
b'9d45d1e979bd0b942adb1896019c85d08fbc562f012775a1f72fc7be8fe9e4b6',
b'a85a339897354931d584f828f6d79d4227ed16f3468990687ab42f13a87c9ea8',
]
chain_codes = [
b'aad67fd6d620f7af88ad816a229de09cfacff3e28008a528759b2e2cf28d859a',
b'e6f844931e7e2ec724e6e62efde662ae2669355322dc3eb9b307bc1c8e75e219',
]
xpub_keys = [
'9d45d1e979bd0b942adb1896019c85d08fbc562f012775a1f72fc7be8fe9e4b6aad67fd6d620f7af88ad816a229de09cfacff3e28008a528759b2e2cf28d859a',
'a85a339897354931d584f828f6d79d4227ed16f3468990687ab42f13a87c9ea8e6f844931e7e2ec724e6e62efde662ae2669355322dc3eb9b307bc1c8e75e219',
]
for index, derivation_path in enumerate(derivation_paths):
key = _get_public_key(keychain, derivation_path)
self.assertEqual(hexlify(key.node.public_key), public_keys[index])
self.assertEqual(hexlify(key.node.chain_code), chain_codes[index])
self.assertEqual(key.xpub, xpub_keys[index])
def test_slip39_128(self):
mnemonics = [
"extra extend academic bishop cricket bundle tofu goat apart victim "

View File

@ -94,7 +94,7 @@ const char *mnemonic_from_data(const uint8_t *data, int len) {
void mnemonic_clear(void) { memzero(mnemo, sizeof(mnemo)); }
int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy) {
int mnemonic_to_bits(const char *mnemonic, uint8_t *bits) {
if (!mnemonic) {
return 0;
}
@ -116,9 +116,9 @@ int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy) {
char current_word[10] = {0};
uint32_t j = 0, k = 0, ki = 0, bi = 0;
uint8_t bits[32 + 1] = {0};
uint8_t result[32 + 1] = {0};
memzero(bits, sizeof(bits));
memzero(result, sizeof(result));
i = 0;
while (mnemonic[i]) {
j = 0;
@ -142,7 +142,7 @@ int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy) {
if (strcmp(current_word, wordlist[k]) == 0) { // word found on index k
for (ki = 0; ki < 11; ki++) {
if (k & (1 << (10 - ki))) {
bits[bi / 8] |= 1 << (7 - (bi % 8));
result[bi / 8] |= 1 << (7 - (bi % 8));
}
bi++;
}
@ -154,17 +154,21 @@ int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy) {
if (bi != n * 11) {
return 0;
}
memcpy(entropy, bits, sizeof(bits));
memcpy(bits, result, sizeof(result));
memzero(result, sizeof(result));
// returns amount of entropy + checksum BITS
return n * 11;
}
int mnemonic_check(const char *mnemonic) {
uint8_t bits[32 + 1] = {0};
int seed_len = mnemonic_to_entropy(mnemonic, bits);
if (seed_len != (12 * 11) && seed_len != (18 * 11) && seed_len != (24 * 11)) {
int mnemonic_bits_len = mnemonic_to_bits(mnemonic, bits);
if (mnemonic_bits_len != (12 * 11) && mnemonic_bits_len != (18 * 11) &&
mnemonic_bits_len != (24 * 11)) {
return 0;
}
int words = seed_len / 11;
int words = mnemonic_bits_len / 11;
uint8_t checksum = bits[words * 4 / 3];
sha256_Raw(bits, words * 4 / 3, bits);

View File

@ -36,7 +36,7 @@ void mnemonic_clear(void);
int mnemonic_check(const char *mnemonic);
int mnemonic_to_entropy(const char *mnemonic, uint8_t *entropy);
int mnemonic_to_bits(const char *mnemonic, uint8_t *bits);
// passphrase must be at most 256 characters otherwise it would be truncated
void mnemonic_to_seed(const char *mnemonic, const char *passphrase,

View File

@ -5114,7 +5114,7 @@ START_TEST(test_mnemonic_check) {
}
END_TEST
START_TEST(test_mnemonic_to_entropy) {
START_TEST(test_mnemonic_to_bits) {
static const char *vectors[] = {
"00000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon "
@ -5198,16 +5198,16 @@ START_TEST(test_mnemonic_to_entropy) {
};
const char **a, **b;
uint8_t entropy[64];
uint8_t mnemonic_bits[64];
a = vectors;
b = vectors + 1;
while (*a && *b) {
int seed_len = mnemonic_to_entropy(*b, entropy);
ck_assert_int_eq(seed_len % 33, 0);
seed_len = seed_len * 4 / 33;
ck_assert_int_eq(seed_len, strlen(*a) / 2);
ck_assert_mem_eq(entropy, fromhex(*a), seed_len);
int mnemonic_bits_len = mnemonic_to_bits(*b, mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len % 33, 0);
mnemonic_bits_len = mnemonic_bits_len * 4 / 33;
ck_assert_int_eq(mnemonic_bits_len, strlen(*a) / 2);
ck_assert_mem_eq(mnemonic_bits, fromhex(*a), mnemonic_bits_len);
a += 2;
b += 2;
}
@ -8836,7 +8836,7 @@ Suite *test_suite(void) {
tc = tcase_create("bip39");
tcase_add_test(tc, test_mnemonic);
tcase_add_test(tc, test_mnemonic_check);
tcase_add_test(tc, test_mnemonic_to_entropy);
tcase_add_test(tc, test_mnemonic_to_bits);
tcase_add_test(tc, test_mnemonic_find_word);
suite_add_tcase(s, tc);
@ -8965,6 +8965,8 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_bip32_cardano_hdnode_vector_5);
tcase_add_test(tc, test_bip32_cardano_hdnode_vector_6);
tcase_add_test(tc, test_bip32_cardano_hdnode_vector_7);
tcase_add_test(tc, test_bip32_cardano_hdnode_vector_8);
tcase_add_test(tc, test_bip32_cardano_hdnode_vector_9);
tcase_add_test(tc, test_ed25519_cardano_sign_vectors);
suite_add_tcase(s, tc);

View File

@ -112,14 +112,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_1) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
ck_assert_mem_eq(
node.chain_code,
@ -148,14 +148,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_2) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
@ -186,14 +186,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_3) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -224,14 +224,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_4) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -263,14 +263,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_5) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -303,14 +303,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_6) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -344,14 +344,14 @@ END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_7) {
HDNode node;
uint8_t seed[66];
int seed_len = mnemonic_to_entropy(
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"ring crime symptom enough erupt lady behave ramp apart settle citizen "
"junk",
seed);
ck_assert_int_eq(seed_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, seed, seed_len / 8,
&node);
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 132);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
@ -382,3 +382,88 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) {
32);
}
END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_8) {
HDNode node;
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"found differ bulb shadow wrist blue bind vessel deposit tip pelican "
"action surprise weapon check fiction muscle this",
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 198);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
hdnode_private_ckd_cardano(&node, 0x80000002);
hdnode_private_ckd_cardano(&node, 0x80000002);
hdnode_private_ckd_cardano(&node, 0xBB9ACA00);
ck_assert_mem_eq(
node.chain_code,
fromhex(
"6fb22a4531ad79e828c4907c5fff3ecf686c16cb195f81243f1f0330173380e4"),
32);
ck_assert_mem_eq(
node.private_key,
fromhex(
"a0baa34e4e24f0500ed6e5e90ab41984b965b7464b0b28640528778dd8a6b854"),
32);
ck_assert_mem_eq(
node.private_key_extension,
fromhex(
"170e0d3b65ba8d71f27a6db60d0ac26dcb16e52e08cc259db72066f206b258d5"),
32);
hdnode_fill_public_key(&node);
ck_assert_mem_eq(
node.public_key + 1,
fromhex(
"3dae0c06d87db618d73ee808425898cdd882f9eb43bf139c6b3a4760551ee89f"),
32);
}
END_TEST
START_TEST(test_bip32_cardano_hdnode_vector_9) {
HDNode node;
uint8_t mnemonic_bits[66];
int mnemonic_bits_len = mnemonic_to_bits(
"balance exotic ranch knife glory slow tape favorite yard gym awake "
"ill exist useless parent aim pig stay effort into square gasp credit "
"butter",
mnemonic_bits);
ck_assert_int_eq(mnemonic_bits_len, 264);
hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits,
mnemonic_bits_len / 8, &node);
hdnode_private_ckd_cardano(&node, 0x80000000);
hdnode_private_ckd_cardano(&node, 0x80000001);
hdnode_private_ckd_cardano(&node, 0x80000002);
hdnode_private_ckd_cardano(&node, 0x80000002);
hdnode_private_ckd_cardano(&node, 0xBB9ACA00);
ck_assert_mem_eq(
node.chain_code,
fromhex(
"9b226add79f90086ea18b260da633089fe121db758aa31284ad1affaf3c9bb68"),
32);
ck_assert_mem_eq(
node.private_key,
fromhex(
"38eb2a79486e516cb6658700503a3e2c870c03e9d1aec731f780aa6fb7f7de44"),
32);
ck_assert_mem_eq(
node.private_key_extension,
fromhex(
"80d2c677638e5dbd4395cdec279bf2a42077f2797c9e887949d37cdb317fce6a"),
32);
hdnode_fill_public_key(&node);
ck_assert_mem_eq(
node.public_key + 1,
fromhex(
"115a365b2aad1d8eba7d379de518f1fa8553855110af24e5695011c32ce9a300"),
32);
}
END_TEST