#![no_std]
#![forbid(missing_docs, unsafe_code)]
#![cfg_attr(not(test), forbid(unused_crate_dependencies))]
use core::ops;
use generic_ec::{Curve, Point, Scalar, SecretScalar};
pub use generic_ec::curves;
#[cfg(feature = "edwards")]
pub mod edwards;
pub mod errors;
#[cfg(feature = "slip10")]
pub mod slip10;
#[cfg(feature = "stark")]
pub mod stark;
#[cfg(feature = "edwards")]
pub use edwards::Edwards;
#[cfg(feature = "slip10")]
pub use slip10::Slip10;
#[cfg(feature = "stark")]
pub use stark::Stark;
pub const H: u32 = 1 << 31;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(from = "u32"))]
pub enum ChildIndex {
Hardened(HardenedIndex),
NonHardened(NonHardenedIndex),
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(try_from = "u32"))]
pub struct HardenedIndex(u32);
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "u32"))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(try_from = "u32"))]
pub struct NonHardenedIndex(u32);
#[derive(Clone, Copy, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound = "")
)]
pub struct ExtendedPublicKey<E: Curve> {
pub public_key: Point<E>,
pub chain_code: ChainCode,
}
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound = "")
)]
pub struct ExtendedSecretKey<E: Curve> {
pub secret_key: SecretScalar<E>,
pub chain_code: ChainCode,
}
#[derive(Clone, Debug)]
pub struct ExtendedKeyPair<E: Curve> {
public_key: ExtendedPublicKey<E>,
secret_key: ExtendedSecretKey<E>,
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound = "")
)]
pub struct DerivedShift<E: Curve> {
pub shift: Scalar<E>,
pub child_public_key: ExtendedPublicKey<E>,
}
pub type ChainCode = [u8; 32];
impl HardenedIndex {
pub const MIN: Self = Self(H);
pub const MAX: Self = Self(u32::MAX);
}
impl NonHardenedIndex {
pub const MIN: Self = Self(0);
pub const MAX: Self = Self(H - 1);
}
impl ops::Deref for HardenedIndex {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::Deref for NonHardenedIndex {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ops::Deref for ChildIndex {
type Target = u32;
fn deref(&self) -> &Self::Target {
match self {
Self::Hardened(i) => i,
Self::NonHardened(i) => i,
}
}
}
impl From<u32> for ChildIndex {
fn from(value: u32) -> Self {
match value {
H.. => Self::Hardened(HardenedIndex(value)),
_ => Self::NonHardened(NonHardenedIndex(value)),
}
}
}
impl TryFrom<u32> for HardenedIndex {
type Error = errors::OutOfRange;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match ChildIndex::from(value) {
ChildIndex::Hardened(v) => Ok(v),
_ => Err(errors::OutOfRange),
}
}
}
impl TryFrom<u32> for NonHardenedIndex {
type Error = errors::OutOfRange;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match ChildIndex::from(value) {
ChildIndex::NonHardened(v) => Ok(v),
_ => Err(errors::OutOfRange),
}
}
}
impl From<ChildIndex> for u32 {
fn from(value: ChildIndex) -> Self {
match value {
ChildIndex::Hardened(v) => v.0,
ChildIndex::NonHardened(v) => v.0,
}
}
}
impl From<HardenedIndex> for u32 {
fn from(value: HardenedIndex) -> Self {
value.0
}
}
impl From<NonHardenedIndex> for u32 {
fn from(value: NonHardenedIndex) -> Self {
value.0
}
}
impl core::str::FromStr for ChildIndex {
type Err = core::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<u32>().map(Into::into)
}
}
impl core::str::FromStr for HardenedIndex {
type Err = errors::ParseChildIndexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let index = s
.parse::<u32>()
.map_err(errors::ParseChildIndexError::ParseInt)?;
HardenedIndex::try_from(index).map_err(errors::ParseChildIndexError::IndexNotInRange)
}
}
impl core::str::FromStr for NonHardenedIndex {
type Err = errors::ParseChildIndexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let index = s
.parse::<u32>()
.map_err(errors::ParseChildIndexError::ParseInt)?;
NonHardenedIndex::try_from(index).map_err(errors::ParseChildIndexError::IndexNotInRange)
}
}
impl<E: Curve> From<&ExtendedSecretKey<E>> for ExtendedPublicKey<E> {
fn from(sk: &ExtendedSecretKey<E>) -> Self {
ExtendedPublicKey {
public_key: Point::generator() * &sk.secret_key,
chain_code: sk.chain_code,
}
}
}
impl<E: Curve> From<ExtendedSecretKey<E>> for ExtendedKeyPair<E> {
fn from(secret_key: ExtendedSecretKey<E>) -> Self {
Self {
public_key: (&secret_key).into(),
secret_key,
}
}
}
impl<E: Curve> ExtendedKeyPair<E> {
pub fn chain_code(&self) -> &ChainCode {
debug_assert_eq!(self.public_key.chain_code, self.secret_key.chain_code);
&self.public_key.chain_code
}
pub fn public_key(&self) -> &ExtendedPublicKey<E> {
&self.public_key
}
pub fn secret_key(&self) -> &ExtendedSecretKey<E> {
&self.secret_key
}
}
#[cfg(feature = "serde")]
impl<E: Curve> serde::Serialize for ExtendedKeyPair<E> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.secret_key.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, E: Curve> serde::Deserialize<'de> for ExtendedKeyPair<E> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let secret_key = ExtendedSecretKey::<E>::deserialize(deserializer)?;
Ok(secret_key.into())
}
}
#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))]
macro_rules! create_aliases {
($t:ty, $m:expr) => { $crate::create_aliases!($t, $m, hd_wallet::curves::Secp256k1); };
($t:ty, $m:expr, $e:ty) => {
#[doc = concat!("Alias to [`<", stringify!($t), " as DeriveShift<E>>::derive_public_shift`](crate::DeriveShift::derive_public_shift)")]
pub fn derive_public_shift<E>(
parent_public_key: &crate::ExtendedPublicKey<E>,
child_index: crate::NonHardenedIndex,
) -> crate::DerivedShift<E>
where
E: generic_ec::Curve,
$t: crate::DeriveShift<E>,
{
<$t as crate::DeriveShift<E>>::derive_public_shift(parent_public_key, child_index)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as DeriveShift<E>>::derive_hardened_shift`](crate::DeriveShift::derive_hardened_shift)")]
pub fn derive_hardened_shift<E>(
parent_key: &crate::ExtendedKeyPair<E>,
child_index: crate::HardenedIndex,
) -> crate::DerivedShift<E>
where
E: generic_ec::Curve,
$t: crate::DeriveShift<E>,
{
<$t as crate::DeriveShift<E>>::derive_hardened_shift(parent_key, child_index)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_public_key`](crate::HdWallet::derive_child_public_key)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let derived_key = hd_wallet::", stringify!($m), "::derive_child_public_key(")]
pub fn derive_child_public_key<E>(
parent_public_key: &crate::ExtendedPublicKey<E>,
child_index: crate::NonHardenedIndex,
) -> crate::ExtendedPublicKey<E>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::derive_child_public_key(parent_public_key, child_index)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_key_pair`](crate::HdWallet::derive_child_key_pair)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let derived_key = hd_wallet::", stringify!($m), "::derive_child_key_pair(")]
pub fn derive_child_key_pair<E>(
parent_key: &crate::ExtendedKeyPair<E>,
child_index: impl Into<crate::ChildIndex>,
) -> crate::ExtendedKeyPair<E>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::derive_child_key_pair(parent_key, child_index)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::try_derive_child_key_pair_with_path`](crate::HdWallet::try_derive_child_key_pair_with_path)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::try_derive_child_key_pair_with_path(")]
pub fn try_derive_child_key_pair_with_path<E, Err>(
parent_key: &crate::ExtendedKeyPair<E>,
path: impl IntoIterator<Item = Result<impl Into<crate::ChildIndex>, Err>>,
) -> Result<crate::ExtendedKeyPair<E>, Err>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::try_derive_child_key_pair_with_path(parent_key, path)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_key_pair_with_path`](crate::HdWallet::derive_child_key_pair_with_path)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::derive_child_key_pair_with_path(")]
pub fn derive_child_key_pair_with_path<E>(
parent_key: &crate::ExtendedKeyPair<E>,
path: impl IntoIterator<Item = impl Into<crate::ChildIndex>>,
) -> crate::ExtendedKeyPair<E>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::derive_child_key_pair_with_path(parent_key, path)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::try_derive_child_public_key_with_path`](crate::HdWallet::try_derive_child_public_key_with_path)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::try_derive_child_public_key_with_path(")]
pub fn try_derive_child_public_key_with_path<E, Err>(
parent_public_key: &crate::ExtendedPublicKey<E>,
path: impl IntoIterator<Item = Result<crate::NonHardenedIndex, Err>>,
) -> Result<crate::ExtendedPublicKey<E>, Err>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::try_derive_child_public_key_with_path(parent_public_key, path)
}
#[doc = concat!("Alias to [`<", stringify!($t), " as HdWallet<E>>::derive_child_public_key_with_path`](crate::HdWallet::derive_child_public_key_with_path)")]
#[doc = concat!( "# type E = ", stringify!($e), ";" )]
#[doc = concat!("let child_key = hd_wallet::", stringify!($m), "::derive_child_public_key_with_path(")]
pub fn derive_child_public_key_with_path<E>(
parent_public_key: &crate::ExtendedPublicKey<E>,
path: impl IntoIterator<Item = crate::NonHardenedIndex>,
) -> crate::ExtendedPublicKey<E>
where
E: generic_ec::Curve,
$t: crate::HdWallet<E>,
{
<$t as crate::HdWallet<E>>::derive_child_public_key_with_path(parent_public_key, path)
}
};
}
#[cfg(any(feature = "slip10", feature = "edwards", feature = "stark"))]
pub(crate) use create_aliases;
pub trait HdWallet<E: Curve>: DeriveShift<E> {
fn derive_child_public_key(
parent_public_key: &ExtendedPublicKey<E>,
child_index: NonHardenedIndex,
) -> ExtendedPublicKey<E> {
Self::derive_public_shift(parent_public_key, child_index).child_public_key
}
fn derive_child_key_pair(
parent_key: &ExtendedKeyPair<E>,
child_index: impl Into<ChildIndex>,
) -> ExtendedKeyPair<E> {
let child_index = child_index.into();
let shift = match child_index {
ChildIndex::Hardened(i) => Self::derive_hardened_shift(parent_key, i),
ChildIndex::NonHardened(i) => Self::derive_public_shift(&parent_key.public_key, i),
};
let mut child_sk = &parent_key.secret_key.secret_key + shift.shift;
let child_sk = SecretScalar::new(&mut child_sk);
ExtendedKeyPair {
secret_key: ExtendedSecretKey {
secret_key: child_sk,
chain_code: shift.child_public_key.chain_code,
},
public_key: shift.child_public_key,
}
}
fn try_derive_child_key_pair_with_path<Err>(
parent_key: &ExtendedKeyPair<E>,
path: impl IntoIterator<Item = Result<impl Into<ChildIndex>, Err>>,
) -> Result<ExtendedKeyPair<E>, Err> {
let mut derived_key = parent_key.clone();
for child_index in path {
derived_key = Self::derive_child_key_pair(&derived_key, child_index?);
}
Ok(derived_key)
}
fn derive_child_key_pair_with_path(
parent_key: &ExtendedKeyPair<E>,
path: impl IntoIterator<Item = impl Into<ChildIndex>>,
) -> ExtendedKeyPair<E> {
let result = Self::try_derive_child_key_pair_with_path(
parent_key,
path.into_iter().map(Ok::<_, core::convert::Infallible>),
);
match result {
Ok(key) => key,
Err(err) => match err {},
}
}
fn try_derive_child_public_key_with_path<Err>(
parent_public_key: &ExtendedPublicKey<E>,
path: impl IntoIterator<Item = Result<NonHardenedIndex, Err>>,
) -> Result<ExtendedPublicKey<E>, Err> {
let mut derived_key = *parent_public_key;
for child_index in path {
derived_key = Self::derive_child_public_key(&derived_key, child_index?);
}
Ok(derived_key)
}
fn derive_child_public_key_with_path(
parent_public_key: &ExtendedPublicKey<E>,
path: impl IntoIterator<Item = NonHardenedIndex>,
) -> ExtendedPublicKey<E> {
let result = Self::try_derive_child_public_key_with_path(
parent_public_key,
path.into_iter().map(Ok::<_, core::convert::Infallible>),
);
match result {
Ok(key) => key,
Err(err) => match err {},
}
}
}
impl<E: Curve, S: DeriveShift<E>> HdWallet<E> for S {}
pub trait DeriveShift<E: Curve> {
fn derive_public_shift(
parent_public_key: &ExtendedPublicKey<E>,
child_index: NonHardenedIndex,
) -> DerivedShift<E>;
fn derive_hardened_shift(
parent_key: &ExtendedKeyPair<E>,
child_index: HardenedIndex,
) -> DerivedShift<E>;
}