nam_tiny_hderive/
bip32.rs1use 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 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 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}