mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-29 08:40:57 +00:00
feat(crypto/cardano): implement Cardano Ledger derivation
This commit is contained in:
parent
1174648777
commit
80e4b27f09
@ -49,7 +49,7 @@
|
||||
#include "memzero.h"
|
||||
|
||||
const curve_info ed25519_info = {
|
||||
.bip32_name = "ed25519 seed",
|
||||
.bip32_name = ED25519_SEED_NAME,
|
||||
.params = NULL,
|
||||
.hasher_base58 = HASHER_SHA2D,
|
||||
.hasher_sign = HASHER_SHA2D,
|
||||
|
@ -40,7 +40,7 @@
|
||||
#define CARDANO_MAX_NODE_DEPTH 1048576
|
||||
|
||||
const curve_info ed25519_cardano_info = {
|
||||
.bip32_name = "ed25519 cardano seed",
|
||||
.bip32_name = ED25519_CARDANO_NAME,
|
||||
.params = NULL,
|
||||
.hasher_base58 = HASHER_SHA2D,
|
||||
.hasher_sign = HASHER_SHA2D,
|
||||
@ -194,13 +194,67 @@ int secret_from_seed_cardano_slip23(const uint8_t *seed, int seed_len,
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Derives the root Cardano secret from a BIP-32 master secret via the Ledger
|
||||
// derivation:
|
||||
// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Ledger.md
|
||||
int secret_from_seed_cardano_ledger(const uint8_t *seed, int seed_len,
|
||||
uint8_t secret_out[CARDANO_SECRET_LENGTH]) {
|
||||
static CONFIDENTIAL uint8_t chain_code[SHA256_DIGEST_LENGTH];
|
||||
static CONFIDENTIAL uint8_t root_key[SHA512_DIGEST_LENGTH];
|
||||
static CONFIDENTIAL HMAC_SHA256_CTX ctx;
|
||||
static CONFIDENTIAL HMAC_SHA512_CTX sctx;
|
||||
|
||||
const uint8_t *intermediate_result = seed;
|
||||
int intermediate_result_len = seed_len;
|
||||
do {
|
||||
// STEP 1: derive a master secret like in BIP-32/SLIP-10
|
||||
hmac_sha512_Init(&sctx, (const uint8_t *)ED25519_SEED_NAME,
|
||||
strlen(ED25519_SEED_NAME));
|
||||
hmac_sha512_Update(&sctx, intermediate_result, intermediate_result_len);
|
||||
hmac_sha512_Final(&sctx, root_key);
|
||||
|
||||
// STEP 2: check that the resulting key does not have a particular bit set,
|
||||
// otherwise iterate like in SLIP-10
|
||||
intermediate_result = root_key;
|
||||
intermediate_result_len = sizeof(root_key);
|
||||
} while (root_key[31] & 0x20);
|
||||
|
||||
// STEP 3: calculate the chain code as a HMAC-SHA256 of "\x01" + seed,
|
||||
// key is "ed25519 seed"
|
||||
hmac_sha256_Init(&ctx, (const unsigned char *)ED25519_SEED_NAME,
|
||||
strlen(ED25519_SEED_NAME));
|
||||
hmac_sha256_Update(&ctx, (const unsigned char *)"\x01", 1);
|
||||
hmac_sha256_Update(&ctx, seed, seed_len);
|
||||
hmac_sha256_Final(&ctx, chain_code);
|
||||
|
||||
// STEP 4: extract information into output
|
||||
_Static_assert(
|
||||
SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH,
|
||||
"Invalid configuration of Cardano secret size");
|
||||
memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH);
|
||||
memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH);
|
||||
|
||||
// STEP 5: tweak bits of the private key
|
||||
cardano_ed25519_tweak_bits(secret_out);
|
||||
|
||||
memzero(&ctx, sizeof(ctx));
|
||||
memzero(&sctx, sizeof(sctx));
|
||||
memzero(root_key, sizeof(root_key));
|
||||
memzero(chain_code, sizeof(chain_code));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define CARDANO_ICARUS_STEPS 32
|
||||
_Static_assert(
|
||||
CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0,
|
||||
"CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS");
|
||||
#define CARDANO_ICARUS_ROUNDS_PER_STEP \
|
||||
(CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS)
|
||||
|
||||
// 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.
|
||||
// scheme:
|
||||
// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Icarus.md
|
||||
int secret_from_entropy_cardano_icarus(
|
||||
const uint8_t *pass, int pass_len, const uint8_t *entropy, int entropy_len,
|
||||
uint8_t secret_out[CARDANO_SECRET_LENGTH],
|
||||
|
@ -28,7 +28,10 @@ const char SECP256K1_GROESTL_NAME[] = "secp256k1-groestl";
|
||||
const char SECP256K1_SMART_NAME[] = "secp256k1-smart";
|
||||
const char NIST256P1_NAME[] = "nist256p1";
|
||||
const char ED25519_NAME[] = "ed25519";
|
||||
const char ED25519_SEED_NAME[] = "ed25519 seed";
|
||||
#if USE_CARDANO
|
||||
const char ED25519_CARDANO_NAME[] = "ed25519 cardano seed";
|
||||
#endif
|
||||
const char ED25519_SHA3_NAME[] = "ed25519-sha3";
|
||||
#if USE_KECCAK
|
||||
const char ED25519_KECCAK_NAME[] = "ed25519-keccak";
|
||||
|
@ -31,6 +31,7 @@ extern const char SECP256K1_GROESTL_NAME[];
|
||||
extern const char SECP256K1_SMART_NAME[];
|
||||
extern const char NIST256P1_NAME[];
|
||||
extern const char ED25519_NAME[];
|
||||
extern const char ED25519_SEED_NAME[];
|
||||
extern const char ED25519_CARDANO_NAME[];
|
||||
extern const char ED25519_SHA3_NAME[];
|
||||
#if USE_KECCAK
|
||||
|
@ -9686,6 +9686,10 @@ Suite *test_suite(void) {
|
||||
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_cardano_ledger_vector_1);
|
||||
tcase_add_test(tc, test_cardano_ledger_vector_2);
|
||||
tcase_add_test(tc, test_cardano_ledger_vector_3);
|
||||
|
||||
tcase_add_test(tc, test_ed25519_cardano_sign_vectors);
|
||||
suite_add_tcase(s, tc);
|
||||
#endif
|
||||
|
@ -501,3 +501,72 @@ START_TEST(test_bip32_cardano_hdnode_vector_9) {
|
||||
32);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_cardano_ledger_vector_1) {
|
||||
uint8_t seed[512 / 8];
|
||||
uint8_t cardano_secret[CARDANO_SECRET_LENGTH];
|
||||
|
||||
const char *mnemonic =
|
||||
"recall grace sport punch exhibit mad harbor stand obey "
|
||||
"short width stem awkward used stairs wool ugly "
|
||||
"trap season stove worth toward congress jaguar";
|
||||
|
||||
mnemonic_to_seed(mnemonic, "", seed, NULL);
|
||||
const int res =
|
||||
secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret);
|
||||
ck_assert_int_eq(res, 1);
|
||||
ck_assert_mem_eq(
|
||||
cardano_secret,
|
||||
fromhex(
|
||||
"a08cf85b564ecf3b947d8d4321fb96d70ee7bb760877e371899b14e2ccf88658"
|
||||
"104b884682b57efd97decbb318a45c05a527b9cc5c2f64f7352935a049ceea60"
|
||||
"680d52308194ccef2a18e6812b452a5815fbd7f5babc083856919aaf668fe7e4"),
|
||||
CARDANO_SECRET_LENGTH);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_cardano_ledger_vector_2) {
|
||||
uint8_t seed[512 / 8];
|
||||
uint8_t cardano_secret[CARDANO_SECRET_LENGTH];
|
||||
|
||||
const char *mnemonic =
|
||||
"correct cherry mammal bubble want mandate polar hazard "
|
||||
"crater better craft exotic choice fun tourist census "
|
||||
"gap lottery neglect address glow carry old business";
|
||||
|
||||
mnemonic_to_seed(mnemonic, "", seed, NULL);
|
||||
const int res =
|
||||
secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret);
|
||||
ck_assert_int_eq(res, 1);
|
||||
ck_assert_mem_eq(
|
||||
cardano_secret,
|
||||
fromhex(
|
||||
"587c6774357ecbf840d4db6404ff7af016dace0400769751ad2abfc77b9a3844"
|
||||
"cc71702520ef1a4d1b68b91187787a9b8faab0a9bb6b160de541b6ee62469901"
|
||||
"fc0beda0975fe4763beabd83b7051a5fd5cbce5b88e82c4bbaca265014e524bd"),
|
||||
CARDANO_SECRET_LENGTH);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_cardano_ledger_vector_3) {
|
||||
uint8_t seed[512 / 8];
|
||||
uint8_t cardano_secret[CARDANO_SECRET_LENGTH];
|
||||
|
||||
const char *mnemonic =
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon "
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon "
|
||||
"abandon abandon abandon abandon abandon abandon abandon art";
|
||||
|
||||
mnemonic_to_seed(mnemonic, "foo", seed, NULL);
|
||||
const int res =
|
||||
secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret);
|
||||
ck_assert_int_eq(res, 1);
|
||||
ck_assert_mem_eq(
|
||||
cardano_secret,
|
||||
fromhex(
|
||||
"f053a1e752de5c26197b60f032a4809f08bb3e5d90484fe42024be31efcba757"
|
||||
"8d914d3ff992e21652fee6a4d99f6091006938fac2c0c0f9d2de0ba64b754e92"
|
||||
"a4f3723f23472077aa4cd4dd8a8a175dba07ea1852dad1cf268c61a2679c3890"),
|
||||
CARDANO_SECRET_LENGTH);
|
||||
}
|
||||
END_TEST
|
||||
|
Loading…
Reference in New Issue
Block a user