#[cfg(not(feature = "std"))]
use alloc::format;
use core::str::FromStr;
use hmac::Mac;
use zeroize::Zeroizing;
use super::*;
use crate::{
curve::{Bip32Curve, Curve, CurvePrivateKey, CurvePublicKey, TweakableKey},
error::{Error, ErrorKind, Result},
path::{ChildNumber, DerivationPath},
xkey::payload::*,
};
pub struct ExtendedPrivateKey<C: Curve> {
pub(crate) meta: ExtendedKeyMetadata,
pub(crate) private_key: C::PrivateKey,
}
impl<C: Curve> Clone for ExtendedPrivateKey<C> {
fn clone(&self) -> Self {
Self { meta: self.meta.clone(), private_key: self.private_key.clone() }
}
}
impl<C: Curve> ExtendedPrivateKey<C> {
pub fn new(seed: &[u8]) -> Result<Self>
where
C::PrivateKey: CurvePrivateKey<Bytes = [u8; 32]>,
{
let (master_key, chain_code) = derive_master_key_parts(seed, C::HMAC_KEY);
let master_key = Zeroizing::new(master_key);
let private_key =
<C::PrivateKey as CurvePrivateKey>::from_bytes(&*master_key).map_err(|err| {
Error::new(ErrorKind::InvalidKeyData, "invalid master key derived from seed")
.with_context("seed_len", seed.len())
.set_source(err)
})?;
Ok(Self {
meta: ExtendedKeyMetadata {
depth: 0,
parent_fingerprint: [0u8; 4],
child_number: 0,
chain_code,
},
private_key,
})
}
pub fn public_key(&self) -> ExtendedPublicKey<C> {
ExtendedPublicKey { meta: self.meta.clone(), public_key: self.private_key.to_public() }
}
}
impl<C: Curve> ExtendedPrivateKey<C> {
pub fn parent_fingerprint(&self) -> [u8; 4] {
self.meta.parent_fingerprint
}
pub fn chain_code(&self) -> [u8; 32] {
self.meta.chain_code
}
pub fn to_bytes(&self) -> Zeroizing<<C::PrivateKey as CurvePrivateKey>::Bytes>
where
<C::PrivateKey as CurvePrivateKey>::Bytes: zeroize::Zeroize,
{
Zeroizing::new(CurvePrivateKey::to_bytes(&self.private_key))
}
}
impl<C> ExtendedPrivateKey<C>
where
C: Bip32Curve,
C::PrivateKey: TweakableKey,
{
pub fn derive_child(&self, child: ChildNumber) -> Result<Self> {
let parent_public = self.private_key.to_public();
let parent_public_bytes = CurvePublicKey::to_bytes(&parent_public);
let (left, right) = hmac_sha512_split(&self.meta.chain_code, |mac| {
if child.is_hardened() {
let mut data = Zeroizing::new([0u8; 1 + 32 + 4]);
data[1..33].copy_from_slice(CurvePrivateKey::to_bytes(&self.private_key).as_ref());
data[33..].copy_from_slice(&child.to_bytes());
mac.update(data.as_ref());
} else {
mac.update(parent_public_bytes.as_ref());
mac.update(&child.to_bytes());
}
});
let left = Zeroizing::new(left);
let child_key = self.private_key.add_tweak(&left).map_err(|err| {
Error::new(ErrorKind::InvalidDerivation, "invalid child private key")
.with_context("child_index", child.index())
.with_context("hardened", child.is_hardened())
.set_source(err)
})?;
Ok(Self {
meta: ExtendedKeyMetadata {
depth: self.meta.depth.saturating_add(1),
parent_fingerprint: key_fingerprint(parent_public_bytes.as_ref()),
child_number: child.into(),
chain_code: right,
},
private_key: child_key,
})
}
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> {
let mut key = self.clone();
for child in path.children() {
key = key.derive_child(*child)?;
}
Ok(key)
}
}
impl<C: Curve> Drop for ExtendedPrivateKey<C> {
fn drop(&mut self) {
CurvePrivateKey::zeroize(&mut self.private_key);
}
}
impl<C> ExtendedPrivateKey<C>
where
C: Bip32Curve,
C::PrivateKey: CurvePrivateKey<Bytes = [u8; 32]>,
{
pub fn encode_with(&self, version: Version) -> Result<ExtendedKeyPayload> {
if !version.is_private() {
return Err(Error::new(ErrorKind::InvalidVersion, "expected private version bytes")
.with_context("version", version));
}
Ok(self.encode_with_unchecked(version))
}
pub fn encode_with_unchecked(&self, version: Version) -> ExtendedKeyPayload {
ExtendedKeyPayload {
version,
meta: self.meta.clone(),
key_data: {
let mut key_data = [0u8; 33];
key_data[1..]
.copy_from_slice(CurvePrivateKey::to_bytes(&self.private_key).as_ref());
key_data
},
}
}
}
impl<C> FromStr for ExtendedPrivateKey<C>
where
C: Bip32Curve,
C::PrivateKey: CurvePrivateKey<Bytes = [u8; 32]>,
{
type Err = Error;
fn from_str(encoded: &str) -> Result<Self> {
let payload = encoded.parse::<ExtendedKeyPayload>()?;
Self::try_from(payload)
}
}
impl<C> TryFrom<ExtendedKeyPayload> for ExtendedPrivateKey<C>
where
C: Bip32Curve,
C::PrivateKey: CurvePrivateKey<Bytes = [u8; 32]>,
{
type Error = Error;
fn try_from(payload: ExtendedKeyPayload) -> Result<Self> {
if !payload.version.is_private() {
return Err(Error::new(ErrorKind::InvalidVersion, "extended key is not private")
.with_context("version", payload.version));
}
let mut raw = Zeroizing::new([0u8; 32]);
raw.copy_from_slice(&payload.key_data[1..]);
let private_key = <C::PrivateKey as CurvePrivateKey>::from_bytes(&*raw).map_err(|err| {
Error::new(ErrorKind::InvalidKeyData, "invalid private key data")
.with_context("key_prefix", format!("0x{:02x}", payload.key_data[0]))
.set_source(err)
})?;
Ok(Self { meta: payload.meta.clone(), private_key })
}
}