cardano-crypto-1.3.0: cbits/encrypted_sign.c
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <ed25519.h>
#include <hmac.h>
#include "crypton_pbkdf2.h"
typedef uint8_t crypton_chacha_context[131];
extern void crypton_chacha_init(crypton_chacha_context *ctx, uint8_t nb_rounds, uint32_t keylen, const uint8_t *key, uint32_t ivlen, const uint8_t *iv);
extern void crypton_chacha_combine(uint8_t *dst, crypton_chacha_context *st, const uint8_t *src, uint32_t bytes);
void clear(void *buf, uint32_t const sz)
{
/* FIXME - HERE we need to make sure the compiler is not going to remove the call */
memset(buf, 0, sz);
}
#define NB_ITERATIONS 15000
static
void stretch(uint8_t *buf, uint32_t const buf_len, uint8_t const *pass, uint32_t const pass_len)
{
const uint8_t salt[] = "encrypted wallet salt";
assert(pass_len > 0);
crypton_fastpbkdf2_hmac_sha512(pass, pass_len, salt, sizeof(salt), NB_ITERATIONS, buf, buf_len);
}
#define SYM_KEY_SIZE 32
#define SYM_NONCE_SIZE 8
#define SYM_BUF_SIZE (SYM_KEY_SIZE+SYM_NONCE_SIZE)
#define SECRET_KEY_SEED_SIZE 32
#define ENCRYPTED_KEY_SIZE 64
#define PUBLIC_KEY_SIZE 32
#define CHAIN_CODE_SIZE 32
#define FULL_KEY_SIZE (ENCRYPTED_KEY_SIZE + PUBLIC_KEY_SIZE + CHAIN_CODE_SIZE)
typedef struct {
uint8_t ekey[ENCRYPTED_KEY_SIZE];
uint8_t pkey[PUBLIC_KEY_SIZE];
uint8_t cc[CHAIN_CODE_SIZE];
} encrypted_key;
typedef struct {
uint8_t pkey[PUBLIC_KEY_SIZE];
uint8_t cc[CHAIN_CODE_SIZE];
} public_key;
static void memory_combine(uint8_t const *pass, uint32_t const pass_len, uint8_t const *source, uint8_t *dest, uint32_t sz)
{
uint8_t buf[SYM_BUF_SIZE];
crypton_chacha_context ctx;
static uint8_t const CHACHA_NB_ROUNDS = 20;
if (pass_len) {
memset(&ctx, 0, sizeof(crypton_chacha_context));
/* generate BUF_SIZE bytes where first KEY_SIZE bytes is the key and NONCE_SIZE remaining bytes the nonce */
stretch(buf, SYM_BUF_SIZE, pass, pass_len);
crypton_chacha_init(&ctx, CHACHA_NB_ROUNDS, SYM_KEY_SIZE, buf, SYM_NONCE_SIZE, buf + SYM_KEY_SIZE);
clear(buf, SYM_BUF_SIZE);
crypton_chacha_combine(dest, &ctx, source, sz);
clear(&ctx, sizeof(crypton_chacha_context));
} else {
memcpy(dest, source, sz);
}
}
static void unencrypt_start
(uint8_t const* pass,
uint32_t const pass_len,
encrypted_key const *encrypted_key /* in */,
ed25519_secret_key decrypted_key /* out */)
{
memory_combine(pass, pass_len, encrypted_key->ekey, decrypted_key, ENCRYPTED_KEY_SIZE);
}
static void unencrypt_stop(ed25519_secret_key decrypted_key)
{
clear(decrypted_key, sizeof(ed25519_secret_key));
}
static void wallet_encrypted_initialize
(uint8_t const *pass, uint32_t const pass_len,
const ed25519_secret_key secret_key,
const uint8_t cc[CHAIN_CODE_SIZE],
encrypted_key *encrypted_key)
{
ed25519_public_key pub_key;
cardano_crypto_ed25519_publickey(secret_key, pub_key);
memory_combine(pass, pass_len, secret_key, encrypted_key->ekey, ENCRYPTED_KEY_SIZE);
memcpy(encrypted_key->pkey, pub_key, PUBLIC_KEY_SIZE);
memcpy(encrypted_key->cc, cc, CHAIN_CODE_SIZE);
}
int wallet_encrypted_from_secret
(uint8_t const *pass, uint32_t const pass_len,
const uint8_t seed[SECRET_KEY_SEED_SIZE],
const uint8_t cc[CHAIN_CODE_SIZE],
encrypted_key *encrypted_key)
{
ed25519_secret_key secret_key;
if (cardano_crypto_ed25519_extend(seed, secret_key))
return 1;
wallet_encrypted_initialize(pass, pass_len, secret_key, cc, encrypted_key);
return 0;
}
int wallet_encrypted_new_from_mkg
(uint8_t const *pass, uint32_t const pass_len,
const uint8_t master_key[96],
encrypted_key *encrypted_key)
{
ed25519_secret_key secret_key;
memcpy(secret_key, master_key, 64);
secret_key[0] &= 248; /* clears the bottom 3 bits */
secret_key[31] &= 0x1F; /* clears the 3 highest bits */
secret_key[31] |= 64; /* set the 2nd highest bit */
wallet_encrypted_initialize(pass, pass_len, secret_key, master_key + 64, encrypted_key);
return 0;
}
void wallet_encrypted_sign
(encrypted_key const *encrypted_key, uint8_t const* pass, uint32_t const pass_len,
uint8_t const *data, uint32_t const data_len,
ed25519_signature signature)
{
ed25519_secret_key priv_key;
ed25519_public_key pub_key;
unencrypt_start(pass, pass_len, encrypted_key, priv_key);
cardano_crypto_ed25519_publickey(priv_key, pub_key);
cardano_crypto_ed25519_sign(data, data_len, encrypted_key->cc, CHAIN_CODE_SIZE, priv_key, pub_key, signature);
unencrypt_stop(priv_key);
}
void wallet_encrypted_change_pass
(encrypted_key const *in, uint8_t const *old_pass, uint32_t const old_pass_len,
uint8_t const *new_pass, uint32_t const new_pass_len, encrypted_key *out)
{
ed25519_secret_key priv_key;
unencrypt_start(old_pass, old_pass_len, in, priv_key);
memory_combine(new_pass, new_pass_len, priv_key, out->ekey, ENCRYPTED_KEY_SIZE);
unencrypt_stop(priv_key);
memcpy(out->pkey, in->pkey, PUBLIC_KEY_SIZE);
memcpy(out->cc, in->cc, CHAIN_CODE_SIZE);
}
DECL_HMAC(sha512,
SHA512_BLOCK_SIZE,
SHA512_DIGEST_SIZE,
struct sha512_ctx,
crypton_sha512_init,
crypton_sha512_update,
crypton_sha512_finalize);
typedef enum {
DERIVATION_V1 = 1,
DERIVATION_V2 = 2,
} derivation_scheme_mode;
static void multiply8_v1(uint8_t *dst, uint8_t *src, int bytes)
{
int i;
uint8_t prev_acc = 0;
for (i = 0; i < bytes; i++) {
dst[i] = (src[i] << 3) + (prev_acc & 0x8);
prev_acc = src[i] >> 5;
}
}
static void multiply8_v2(uint8_t *dst, uint8_t *src, int bytes)
{
int i;
uint8_t prev_acc = 0;
for (i = 0; i < bytes; i++) {
dst[i] = (src[i] << 3) + (prev_acc & 0x7);
prev_acc = src[i] >> 5;
}
dst[bytes] = src[bytes-1] >> 5;
}
static void add_256bits_v1(uint8_t *dst, uint8_t *src1, uint8_t *src2)
{
int i;
for (i = 0; i < 32; i++) {
uint8_t a = src1[i];
uint8_t b = src2[i];
uint16_t r = a + b;
dst[i] = r & 0xff;
}
}
static void add_256bits_v2(uint8_t *dst, uint8_t *src1, uint8_t *src2)
{
int i; uint8_t carry = 0;
for (i = 0; i < 32; i++) {
uint8_t a = src1[i];
uint8_t b = src2[i];
uint16_t r = (uint16_t) a + (uint16_t) b + (uint16_t) carry;
dst[i] = r & 0xff;
carry = (r >= 0x100) ? 1 : 0;
}
}
#define TAG_DERIVE_Z_NORMAL "\x2"
#define TAG_DERIVE_Z_HARDENED "\x0"
#define TAG_DERIVE_CC_NORMAL "\x3"
#define TAG_DERIVE_CC_HARDENED "\x1"
static int index_is_hardened(uint32_t index)
{
return (index & (1 << 31));
}
/* sk1 is zl8 and contains only 29 bytes of active data,
* so it's not going to overflow when adding to sk2 */
void scalar_add_no_overflow(const ed25519_secret_key sk1, const ed25519_secret_key sk2, ed25519_secret_key res)
{
uint16_t r = 0; int i;
for (i = 0; i < 32; i++) {
r = (uint16_t) sk1[i] + (uint16_t) sk2[i] + r;
res[i] = (uint8_t) r;
r >>= 8;
}
}
static void serialize_index32(uint8_t *out, uint32_t index, derivation_scheme_mode mode)
{
switch (mode) {
case DERIVATION_V1: /* BIG ENDIAN */
out[0] = index >> 24;
out[1] = index >> 16;
out[2] = index >> 8;
out[3] = index;
break;
case DERIVATION_V2: /* LITTLE ENDIAN */
out[3] = index >> 24;
out[2] = index >> 16;
out[1] = index >> 8;
out[0] = index;
break;
}
}
static void add_left(ed25519_secret_key res_key, uint8_t *z, ed25519_secret_key priv_key, derivation_scheme_mode mode)
{
ed25519_secret_key zl8;
memset(zl8, 0, 64);
switch (mode) {
case DERIVATION_V1:
/* get 8 * Zl */
multiply8_v1(zl8, z, 32);
/* Kl = 8*Zl + parent(K)l */
cardano_crypto_ed25519_scalar_add(zl8, priv_key, res_key);
break;
case DERIVATION_V2:
/* get 8 * Zl */
multiply8_v2(zl8, z, 28);
/* Kl = 8*Zl + parent(K)l */
scalar_add_no_overflow(zl8, priv_key, res_key);
break;
}
}
static void add_right(ed25519_secret_key res_key, uint8_t *z, ed25519_secret_key priv_key, derivation_scheme_mode mode)
{
switch (mode) {
case DERIVATION_V1:
add_256bits_v1(res_key + 32, z+32, priv_key+32);
break;
case DERIVATION_V2:
add_256bits_v2(res_key + 32, z+32, priv_key+32);
break;
}
}
static void add_left_public(uint8_t *out, uint8_t *z, uint8_t *in, derivation_scheme_mode mode)
{
ed25519_secret_key zl8;
ed25519_public_key pub_zl8;
memset(zl8, 0, 64);
switch (mode) {
case DERIVATION_V1:
multiply8_v1(zl8, z, 32);
break;
case DERIVATION_V2:
multiply8_v2(zl8, z, 28);
break;
}
/* Kl = 8*Zl*B + Al */
cardano_crypto_ed25519_publickey(zl8, pub_zl8);
cardano_crypto_ed25519_point_add(pub_zl8, in, out);
}
void wallet_encrypted_derive_private
(encrypted_key const *in,
uint8_t const *pass, uint32_t const pass_len,
uint32_t index,
encrypted_key *out,
derivation_scheme_mode mode)
{
ed25519_secret_key priv_key;
ed25519_secret_key res_key;
HMAC_sha512_ctx hmac_ctx;
uint8_t idxBuf[4];
uint8_t z[64];
uint8_t hmac_out[64];
serialize_index32(idxBuf, index, mode);
unencrypt_start(pass, pass_len, in, priv_key);
/* calculate Z */
HMAC_sha512_init(&hmac_ctx, in->cc, CHAIN_CODE_SIZE);
if (index_is_hardened(index)) {
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_Z_HARDENED, 1);
HMAC_sha512_update(&hmac_ctx, priv_key, ENCRYPTED_KEY_SIZE);
} else {
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_Z_NORMAL, 1);
HMAC_sha512_update(&hmac_ctx, in->pkey, PUBLIC_KEY_SIZE);
}
HMAC_sha512_update(&hmac_ctx, idxBuf, 4);
HMAC_sha512_final(&hmac_ctx, z);
add_left(res_key, z, priv_key, mode);
/* Kr = Zr + parent(K)r */
add_right(res_key, z, priv_key, mode);
/* calculate the new chain code */
HMAC_sha512_init(&hmac_ctx, in->cc, CHAIN_CODE_SIZE);
if (index_is_hardened(index)) {
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_CC_HARDENED, 1);
HMAC_sha512_update(&hmac_ctx, priv_key, ENCRYPTED_KEY_SIZE);
} else {
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_CC_NORMAL, 1);
HMAC_sha512_update(&hmac_ctx, in->pkey, PUBLIC_KEY_SIZE);
}
HMAC_sha512_update(&hmac_ctx, idxBuf, 4);
HMAC_sha512_final(&hmac_ctx, hmac_out);
unencrypt_stop(priv_key);
wallet_encrypted_initialize(pass, pass_len, res_key, hmac_out + 32, out);
clear(res_key, ENCRYPTED_KEY_SIZE);
clear(hmac_out, 64);
}
int wallet_encrypted_derive_public
(uint8_t *pub_in,
uint8_t *cc_in,
uint32_t index,
uint8_t *pub_out,
uint8_t *cc_out,
derivation_scheme_mode mode)
{
HMAC_sha512_ctx hmac_ctx;
uint8_t idxBuf[4];
uint8_t z[64];
uint8_t hmac_out[64];
/* cannot derive hardened key using public bits */
if (index_is_hardened(index))
return 1;
serialize_index32(idxBuf, index, mode);
/* calculate Z */
HMAC_sha512_init(&hmac_ctx, cc_in, CHAIN_CODE_SIZE);
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_Z_NORMAL, 1);
HMAC_sha512_update(&hmac_ctx, pub_in, PUBLIC_KEY_SIZE);
HMAC_sha512_update(&hmac_ctx, idxBuf, 4);
HMAC_sha512_final(&hmac_ctx, z);
/* get 8 * Zl */
add_left_public(pub_out, z, pub_in, mode);
/* calculate the new chain code */
HMAC_sha512_init(&hmac_ctx, cc_in, CHAIN_CODE_SIZE);
HMAC_sha512_update(&hmac_ctx, TAG_DERIVE_CC_NORMAL, 1);
HMAC_sha512_update(&hmac_ctx, pub_in, PUBLIC_KEY_SIZE);
HMAC_sha512_update(&hmac_ctx, idxBuf, 4);
HMAC_sha512_final(&hmac_ctx, hmac_out);
memcpy(cc_out, hmac_out + (sizeof(hmac_out) - CHAIN_CODE_SIZE), CHAIN_CODE_SIZE);
return 0;
}