nam_tiny_hderive/
bip32.rs

1use base58::FromBase58;
2use hmac::{Hmac, Mac};
3use k256::SecretKey;
4use sha2::Sha512;
5use std::fmt;
6use std::ops::Deref;
7use std::str::FromStr;
8use zeroize::ZeroizeOnDrop;
9
10use crate::bip44::{ChildNumber, IntoDerivationPath};
11use crate::Error;
12
13#[derive(Clone, PartialEq, Eq, ZeroizeOnDrop)]
14pub struct Protected([u8; 32]);
15
16impl<Data: AsRef<[u8]>> From<Data> for Protected {
17    fn from(data: Data) -> Protected {
18        let mut buf = [0u8; 32];
19
20        buf.copy_from_slice(data.as_ref());
21
22        Protected(buf)
23    }
24}
25
26impl Deref for Protected {
27    type Target = [u8];
28
29    fn deref(&self) -> &[u8] {
30        self.0.as_ref()
31    }
32}
33
34impl fmt::Debug for Protected {
35    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36        write!(f, "Protected")
37    }
38}
39
40#[derive(Clone, PartialEq, Eq, Debug)]
41pub struct ExtendedPrivKey {
42    secret_key: SecretKey,
43    chain_code: Protected,
44}
45
46impl ExtendedPrivKey {
47    /// Attempts to derive an extended private key from a path.
48    pub fn derive<Path>(seed: &[u8], path: Path) -> Result<ExtendedPrivKey, Error>
49    where
50        Path: IntoDerivationPath,
51    {
52        let mut hmac: Hmac<Sha512> =
53            Hmac::new_from_slice(b"Bitcoin seed").expect("seed is always correct; qed");
54        hmac.update(seed);
55
56        let result = hmac.finalize().into_bytes();
57        let (secret_key, chain_code) = result.split_at(32);
58
59        let mut sk = ExtendedPrivKey {
60            secret_key: SecretKey::from_slice(secret_key).map_err(Error::Secp256k1)?,
61            chain_code: Protected::from(chain_code),
62        };
63
64        for child in path.into()?.as_ref() {
65            sk = sk.child(*child)?;
66        }
67
68        Ok(sk)
69    }
70
71    pub fn secret(&self) -> [u8; 32] {
72        self.secret_key.to_bytes().into()
73    }
74
75    pub fn child(&self, child: ChildNumber) -> Result<ExtendedPrivKey, Error> {
76        let mut hmac: Hmac<Sha512> =
77            Hmac::new_from_slice(&self.chain_code).map_err(|_| Error::InvalidChildNumber)?;
78
79        if child.is_normal() {
80            hmac.update(&self.secret_key.public_key().to_sec1_bytes());
81        } else {
82            hmac.update(&[0]);
83            hmac.update(&self.secret());
84        }
85
86        hmac.update(&child.to_bytes());
87
88        let result = hmac.finalize().into_bytes();
89        let (secret_key, chain_code) = result.split_at(32);
90
91        let mut secret_key = SecretKey::from_slice(secret_key).map_err(Error::Secp256k1)?;
92        let raw = *secret_key.as_scalar_primitive() + self.secret_key.as_scalar_primitive();
93        if raw.is_zero().into() {
94            return Err(Error::ZeroChildKey);
95        }
96        secret_key = SecretKey::new(raw);
97
98        Ok(ExtendedPrivKey {
99            secret_key,
100            chain_code: Protected::from(&chain_code),
101        })
102    }
103}
104
105impl FromStr for ExtendedPrivKey {
106    type Err = Error;
107
108    fn from_str(xprv: &str) -> Result<ExtendedPrivKey, Error> {
109        let data = xprv
110            .from_base58()
111            .map_err(|_| Error::InvalidExtendedPrivKey)?;
112
113        if data.len() != 82 {
114            return Err(Error::InvalidExtendedPrivKey);
115        }
116
117        Ok(ExtendedPrivKey {
118            chain_code: Protected::from(&data[13..45]),
119            secret_key: SecretKey::from_slice(&data[46..78]).map_err(Error::Secp256k1)?,
120        })
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use bip39::{Language, Mnemonic, Seed};
128    use ethsign::SecretKey;
129
130    #[test]
131    fn bip39_to_address() {
132        let phrase = "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside";
133
134        let expected_secret_key = b"\xff\x1e\x68\xeb\x7b\xf2\xf4\x86\x51\xc4\x7e\xf0\x17\x7e\xb8\x15\x85\x73\x22\x25\x7c\x58\x94\xbb\x4c\xfd\x11\x76\xc9\x98\x93\x14";
135        let expected_address: &[u8] =
136            b"\x63\xF9\xA9\x2D\x8D\x61\xb4\x8a\x9f\xFF\x8d\x58\x08\x04\x25\xA3\x01\x2d\x05\xC8";
137
138        let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
139        let seed = Seed::new(&mnemonic, "");
140
141        let account = ExtendedPrivKey::derive(seed.as_bytes(), "m/44'/60'/0'/0/0").unwrap();
142
143        assert_eq!(
144            expected_secret_key,
145            &account.secret(),
146            "Secret key is invalid"
147        );
148
149        let secret_key = SecretKey::from_raw(&account.secret()).unwrap();
150        let public_key = secret_key.public();
151
152        assert_eq!(expected_address, public_key.address(), "Address is invalid");
153
154        // Test child method
155        let account = ExtendedPrivKey::derive(seed.as_bytes(), "m/44'/60'/0'/0")
156            .unwrap()
157            .child(ChildNumber::from_str("0").unwrap())
158            .unwrap();
159
160        assert_eq!(
161            expected_secret_key,
162            &account.secret(),
163            "Secret key is invalid"
164        );
165
166        let secret_key = SecretKey::from_raw(&account.secret()).unwrap();
167        let public_key = secret_key.public();
168
169        assert_eq!(expected_address, public_key.address(), "Address is invalid");
170    }
171}