feat(crypto/cardano): implement Cardano Ledger derivation

pull/1882/head
matejcik 3 years ago committed by matejcik
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…
Cancel
Save