bip32/
private_key.rs

1//! Trait for deriving child keys on a given type.
2
3use crate::{ChainCode, ChildNumber, Error, HmacSha512, PublicKey, Result, KEY_SIZE};
4use hmac::Mac;
5
6#[cfg(feature = "secp256k1")]
7use crate::XPrv;
8
9/// Bytes which represent a private key.
10pub type PrivateKeyBytes = [u8; KEY_SIZE];
11
12/// Trait for key types which can be derived using BIP32.
13pub trait PrivateKey: Sized {
14    /// Public key type which corresponds to this private key.
15    type PublicKey: PublicKey;
16
17    /// Initialize this key from bytes.
18    fn from_bytes(bytes: &PrivateKeyBytes) -> Result<Self>;
19
20    /// Serialize this key as bytes.
21    fn to_bytes(&self) -> PrivateKeyBytes;
22
23    /// Derive a child key from a parent key and the a provided tweak value,
24    /// i.e. where `other` is referred to as "I sub L" in BIP32 and sourced
25    /// from the left half of the HMAC-SHA-512 output.
26    fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self>;
27
28    /// Get the [`Self::PublicKey`] that corresponds to this private key.
29    fn public_key(&self) -> Self::PublicKey;
30
31    /// Derive a tweak value that can be used to generate the child key (see [`derive_child`]).
32    ///
33    /// The `chain_code` is either a newly initialized one,
34    /// or one obtained from the previous invocation of `derive_tweak()`
35    /// (for a multi-level derivation).
36    ///
37    /// **Warning:** make sure that if you are creating a new `chain_code`, you are doing so
38    /// in a cryptographically safe way.
39    /// Normally this would be done according to BIP-39 (within [`ExtendedPrivateKey::new`]).
40    fn derive_tweak(
41        &self,
42        chain_code: &ChainCode,
43        child_number: ChildNumber,
44    ) -> Result<(PrivateKeyBytes, ChainCode)> {
45        let mut hmac = HmacSha512::new_from_slice(chain_code).map_err(|_| Error::Crypto)?;
46
47        if child_number.is_hardened() {
48            hmac.update(&[0]);
49            hmac.update(&self.to_bytes());
50        } else {
51            hmac.update(&self.public_key().to_bytes());
52        }
53
54        hmac.update(&child_number.to_bytes());
55
56        let result = hmac.finalize().into_bytes();
57        let (tweak_bytes, chain_code_bytes) = result.split_at(KEY_SIZE);
58
59        // Note that at this point we are only asserting that `tweak_bytes` have the expected size.
60        // Checking if it actually fits the curve scalar happens in `derive_child()`.
61        let tweak = tweak_bytes.try_into()?;
62
63        let chain_code = chain_code_bytes.try_into()?;
64
65        Ok((tweak, chain_code))
66    }
67}
68
69#[cfg(feature = "secp256k1")]
70impl PrivateKey for k256::SecretKey {
71    type PublicKey = k256::PublicKey;
72
73    fn from_bytes(bytes: &PrivateKeyBytes) -> Result<Self> {
74        Ok(k256::SecretKey::from_slice(bytes)?)
75    }
76
77    fn to_bytes(&self) -> PrivateKeyBytes {
78        k256::SecretKey::to_bytes(self).into()
79    }
80
81    fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self> {
82        let child_scalar =
83            Option::<k256::NonZeroScalar>::from(k256::NonZeroScalar::from_repr(other.into()))
84                .ok_or(Error::Crypto)?;
85
86        let derived_scalar = self.to_nonzero_scalar().as_ref() + child_scalar.as_ref();
87
88        Option::<k256::NonZeroScalar>::from(k256::NonZeroScalar::new(derived_scalar))
89            .map(Into::into)
90            .ok_or(Error::Crypto)
91    }
92
93    fn public_key(&self) -> Self::PublicKey {
94        k256::SecretKey::public_key(self)
95    }
96}
97
98#[cfg(feature = "secp256k1")]
99impl PrivateKey for k256::ecdsa::SigningKey {
100    type PublicKey = k256::ecdsa::VerifyingKey;
101
102    fn from_bytes(bytes: &PrivateKeyBytes) -> Result<Self> {
103        Ok(k256::ecdsa::SigningKey::from_slice(bytes)?)
104    }
105
106    fn to_bytes(&self) -> PrivateKeyBytes {
107        k256::ecdsa::SigningKey::to_bytes(self).into()
108    }
109
110    fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self> {
111        k256::SecretKey::from(self)
112            .derive_child(other)
113            .map(Into::into)
114    }
115
116    fn public_key(&self) -> Self::PublicKey {
117        *self.verifying_key()
118    }
119}
120
121#[cfg(feature = "secp256k1")]
122impl From<XPrv> for k256::ecdsa::SigningKey {
123    fn from(xprv: XPrv) -> k256::ecdsa::SigningKey {
124        k256::ecdsa::SigningKey::from(&xprv)
125    }
126}
127
128#[cfg(feature = "secp256k1")]
129impl From<&XPrv> for k256::ecdsa::SigningKey {
130    fn from(xprv: &XPrv) -> k256::ecdsa::SigningKey {
131        xprv.private_key().clone()
132    }
133}
134
135#[cfg(feature = "secp256k1-ffi")]
136impl PrivateKey for secp256k1_ffi::SecretKey {
137    type PublicKey = secp256k1_ffi::PublicKey;
138
139    fn from_bytes(bytes: &PrivateKeyBytes) -> Result<Self> {
140        Ok(secp256k1_ffi::SecretKey::from_slice(bytes)?)
141    }
142
143    fn to_bytes(&self) -> PrivateKeyBytes {
144        *self.as_ref()
145    }
146
147    fn derive_child(&self, bytes: PrivateKeyBytes) -> Result<Self> {
148        let scalar = secp256k1_ffi::Scalar::from_be_bytes(bytes)?;
149        Ok(self.add_tweak(&scalar)?)
150    }
151
152    fn public_key(&self) -> Self::PublicKey {
153        use secp256k1_ffi::{Secp256k1, SignOnly};
154        let engine = Secp256k1::<SignOnly>::signing_only();
155        secp256k1_ffi::PublicKey::from_secret_key(&engine, self)
156    }
157}
158
159/// `secp256k1-ffi` smoke tests
160#[cfg(all(test, feature = "bip39", feature = "secp256k1-ffi"))]
161mod tests {
162    use hex_literal::hex;
163
164    type XPrv = crate::ExtendedPrivateKey<secp256k1_ffi::SecretKey>;
165
166    #[test]
167    fn secp256k1_ffi_derivation() {
168        let seed = hex!(
169            "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a2
170             9f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
171        );
172
173        let path = "m/0/2147483647'/1/2147483646'/2";
174        let xprv = XPrv::derive_from_path(&seed, &path.parse().unwrap()).unwrap();
175
176        assert_eq!(
177            xprv,
178            "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j".parse().unwrap()
179        );
180    }
181}