Skip to content

Commit

Permalink
Restructure the lib, make HD derivations optional
Browse files Browse the repository at this point in the history
Signed-off-by: Denis Varlakov <[email protected]>
  • Loading branch information
survived committed Nov 21, 2024
1 parent 08cdae4 commit bc7513f
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 292 deletions.
25 changes: 18 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ repository = "https://github.com/LFDT-Lockness/hd-wallet"
[dependencies]
generic-ec = { version = "0.4", default-features = false }

hmac = { version = "0.12", default-features = false }
sha2 = { version = "0.10", default-features = false }
subtle = { version = "2", default-features = false }
generic-array = "0.14"
hmac = { version = "0.12", default-features = false, optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2", default-features = false, optional = true }
generic-array = { version = "0.14", default-features = false, optional = true }

serde = { version = "1", default-features = false, features = ["derive"], optional = true }

Expand All @@ -27,13 +27,24 @@ rug = "1.26"
generic-ec = { version = "0.4", default-features = false, features = ["curve-stark"] }

[features]
default = []
std = []
curve-secp256k1 = ["generic-ec/curve-secp256k1"]
curve-secp256r1 = ["generic-ec/curve-secp256r1"]
curve-ed25519 = ["generic-ec/curve-ed25519"]
# Adds support of secp256k1 curve to slip10 derivation
curve-secp256k1 = ["generic-ec/curve-secp256k1", "slip10"]
# Adds support of secp256r1 curve to slip10 derivation
curve-secp256r1 = ["generic-ec/curve-secp256r1", "slip10"]
# Enables Edwards-specific derivation
curve-ed25519 = ["generic-ec/curve-ed25519", "edwards"]
all-curves = ["curve-secp256k1", "curve-secp256r1", "curve-ed25519"]
serde = ["dep:serde", "generic-ec/serde"]

# Enables Slip10 derivation
slip10 = ["hmac", "sha2", "generic-array", "subtle"]
# Enables Edwards-specific derivation
edwards = ["hmac", "sha2", "generic-array", "curve-ed25519"]
# Enables Stark-specific derivation
stark = []

[[test]]
name = "slip10_test_vector"
required-features = ["curve-secp256k1", "curve-secp256r1"]
Expand Down
110 changes: 110 additions & 0 deletions src/edwards.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! Edwards HD derivation
//!
//! This module provides [`Edwards`] derivation as well as aliases for calling
//! `<Edwards as HdWallet<_>>::*` methods for convenience when you don't need to support
//! generic HD derivation algorithm.
//!
//! See [`Edwards`] docs to learn more about the derivation method.
use generic_ec::{curves, Point, Scalar};
use hmac::Mac;

use crate::{
DeriveShift, DerivedShift, ExtendedKeyPair, ExtendedPublicKey, HardenedIndex, NonHardenedIndex,
};

type HmacSha512 = hmac::Hmac<sha2::Sha512>;

/// HD derivation for Ed25519 curve
///
/// This type of derivation isn't defined in any known to us standards, but it can be often
/// found in other libraries. It is secure and efficient (much more efficient than using
/// [`Slip10Like<Ed25519>`](Slip10Like), for instance).
///
/// ## Example
/// ```rust
/// use hd_wallet::{HdWallet, Edwards, curves::Ed25519};
///
/// # fn load_key() -> hd_wallet::ExtendedKeyPair<Ed25519> {
/// # hd_wallet::ExtendedSecretKey {
/// # secret_key: generic_ec::SecretScalar::random(&mut rand::rngs::OsRng),
/// # chain_code: rand::Rng::gen(&mut rand::rngs::OsRng),
/// # }.into()
/// # }
/// #
/// let parent_key: hd_wallet::ExtendedKeyPair<Ed25519> = load_key();
///
/// let child_key_pair = Edwards::derive_child_key_pair_with_path(
/// &parent_key,
/// [1 + hd_wallet::H, 10],
/// );
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub struct Edwards;

impl DeriveShift<curves::Ed25519> for Edwards {
fn derive_public_shift(
parent_public_key: &ExtendedPublicKey<curves::Ed25519>,
child_index: NonHardenedIndex,
) -> DerivedShift<curves::Ed25519> {
let hmac = HmacSha512::new_from_slice(&parent_public_key.chain_code)
.expect("this never fails: hmac can handle keys of any size");
let i = hmac
.clone()
.chain_update(parent_public_key.public_key.to_bytes(true))
// we append 0 byte to the public key for compatibility with other libs
.chain_update([0x00])
.chain_update(child_index.to_be_bytes())
.finalize()
.into_bytes();
Self::calculate_shift(parent_public_key, i)
}

fn derive_hardened_shift(
parent_key: &ExtendedKeyPair<curves::Ed25519>,
child_index: HardenedIndex,
) -> DerivedShift<curves::Ed25519> {
let hmac = HmacSha512::new_from_slice(parent_key.chain_code())
.expect("this never fails: hmac can handle keys of any size");
let i = hmac
.clone()
.chain_update([0x00])
.chain_update(parent_key.secret_key.secret_key.as_ref().to_be_bytes())
.chain_update(child_index.to_be_bytes())
.finalize()
.into_bytes();
Self::calculate_shift(&parent_key.public_key, i)
}
}

impl Edwards {
fn calculate_shift(
parent_public_key: &ExtendedPublicKey<curves::Ed25519>,
i: hmac::digest::Output<HmacSha512>,
) -> DerivedShift<curves::Ed25519> {
let (i_left, i_right) = split_into_two_halves(&i);

let shift = Scalar::from_be_bytes_mod_order(i_left);
let child_pk = parent_public_key.public_key + Point::generator() * shift;

DerivedShift {
shift,
child_public_key: ExtendedPublicKey {
public_key: child_pk,
chain_code: (*i_right).into(),
},
}
}
}

/// Splits array `I` of 64 bytes into two arrays `I_L = I[..32]` and `I_R = I[32..]`
fn split_into_two_halves(
i: &generic_array::GenericArray<u8, generic_array::typenum::U64>,
) -> (
&generic_array::GenericArray<u8, generic_array::typenum::U32>,
&generic_array::GenericArray<u8, generic_array::typenum::U32>,
) {
generic_array::sequence::Split::split(i)
}

super::create_aliases!(Edwards, edwards, hd_wallet::curves::Ed25519);
Loading

0 comments on commit bc7513f

Please sign in to comment.