1use bip39::{Language, Mnemonic, Seed, MnemonicType};
2use hex_literal::hex;
3use hmac::{Hmac, Mac};
4use num_bigint::BigUint;
5use regex::Regex;
6use secp256k1::{PublicKey, Secp256k1, SecretKey};
7use sha2::Sha512;
8
9const MASTER_SECRET: &str = "Bitcoin seed";
11
12type HmacSha512 = Hmac<Sha512>;
14
15const HARDENED_BIT: u64 = 0x80000000;
17
18#[derive(Debug, Clone)]
22pub struct HDNode {
23 pub private_key: Option<[u8; 32]>,
24 pub public_key: [u8; 33], pub mnemonic: Option<Mnemonic>,
27 pub path: String,
28
29 chain_code: [u8; 32],
30
31 pub index: u64,
32 pub depth: u32,
33}
34
35impl HDNode {
36 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
37 let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
39 HDNode::from_mnemonic(&mnemonic)
40 }
41
42 pub fn from_mnemonic(mnemonic: &Mnemonic) -> Result<HDNode, Box<dyn std::error::Error>> {
43 let seed = Seed::new_cpc(&mnemonic, "");
44 let bytes = compute_hmac(MASTER_SECRET.as_bytes(), &seed.as_bytes())?;
45 let mut master_private_key: [u8; 32] = [0; 32];
46 bytes[0..32]
47 .to_vec()
48 .iter()
49 .enumerate()
50 .for_each(|(index, elem)| {
51 master_private_key[index] = elem.clone();
52 });
53 let secp = Secp256k1::new();
54 let secret_key = SecretKey::from_slice(&master_private_key)?;
56 let public_key = PublicKey::from_secret_key(&secp, &secret_key);
57 let public_key: [u8; 33] = public_key.serialize();
58
59 let mut master_chain_code: [u8; 32] = [0; 32];
60 bytes[32..64]
61 .to_vec()
62 .iter()
63 .enumerate()
64 .for_each(|(index, elem)| {
65 master_chain_code[index] = elem.clone();
66 });
67 Ok(HDNode::new_hdnode(
68 Some(master_private_key),
69 public_key,
70 master_chain_code,
71 0,
72 0,
73 "m".to_string(),
74 Some(mnemonic.clone()),
75 ))
76 }
77
78 pub fn from_phrase(phrase: &str) -> Result<HDNode, Box<dyn std::error::Error>> {
79 let mnemonic = Mnemonic::from_phrase(phrase, Language::English)?;
81 HDNode::from_mnemonic(&mnemonic)
82 }
83
84 fn new_hdnode(
85 private_key: Option<[u8; 32]>,
86 public_key: [u8; 33],
87 chain_code: [u8; 32],
88 index: u64,
89 depth: u32,
90 path: String,
91 mnemonic: Option<Mnemonic>,
92 ) -> Self {
93 Self {
94 private_key,
95 public_key,
96 chain_code,
97 index,
98 depth,
99 mnemonic,
100 path,
101 }
102 }
103 fn _derive(&self, index: u64) -> Result<HDNode, Box<dyn std::error::Error>> {
104 if index > 0xffffffff {
105 return Err(format!("invalid index - {}", index).into());
106 }
107
108 let mut path = self.path.clone();
110 if path.len() > 0 {
111 path += &format!("/{}", (index & !HARDENED_BIT))
112 }
113
114 let mut data: [u8; 37] = [0; 37];
115
116 if (index & HARDENED_BIT) > 0 {
117 match &self.private_key {
118 Some(pk) => {
119 pk.iter().enumerate().for_each(|(index, e)| {
121 data[index + 1] = e.clone();
122 });
123 if path.len() > 0 {
125 path += "'";
126 }
127 }
128 None => return Err("cannot derive child of neutered node".into()),
129 }
130 } else {
131 self.public_key.iter().enumerate().for_each(|(index, e)| {
133 data[index] = *e;
134 });
135 }
136
137 let mut i: i32 = 24;
139 while i >= 0 {
140 data[(33 + (i >> 3)) as usize] = ((index >> (24 - i)) & 0xff) as u8;
141 i -= 8;
142 }
143
144 let hmac_result = compute_hmac(&self.chain_code, &data)?;
145 let hmac_result_left: &[u8; 32] = &hmac_result[..32].try_into().unwrap();
146 let child_chain_code: &[u8; 32] = &hmac_result[32..].try_into().unwrap();
147
148 let secp = Secp256k1::new();
149
150 let (private_key, public_key): ([u8; 32], [u8; 33]) = match self.private_key {
152 Some(pk) => {
153 let bn1 = BigUint::from_bytes_be(&pk);
154 let bn2 = BigUint::from_bytes_be(hmac_result_left);
155 let r = (bn1 + bn2)
156 % BigUint::from_bytes_be(&hex!(
157 "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
158 ));
159 let mut ki: [u8; 32] = [0; 32];
160 r.to_bytes_be()[..32]
161 .iter()
162 .enumerate()
163 .for_each(|(index, e)| {
164 ki[index] = e.clone();
165 });
166 let secret_key = SecretKey::from_slice(&ki)?;
167 let public_key = PublicKey::from_secret_key(&secp, &secret_key);
168 let public_key: [u8; 33] = public_key.serialize();
169 (ki, public_key)
170 }
171 None => {
172 todo!()
173 }
174 };
175
176 let node = HDNode::new_hdnode(
177 Some(private_key),
178 public_key,
179 child_chain_code.clone(),
180 index,
181 self.depth + 1,
182 path,
183 self.mnemonic.clone(),
184 );
185 Ok(node)
186 }
187
188 pub fn derive_path(&self, path: &str) -> Result<HDNode, Box<dyn std::error::Error>> {
189 let mut components = path.split("/").collect::<Vec<_>>();
190 if components.len() == 0 || path == "" || (components[0] == "m" && self.depth != 0) {
191 return Err(format!("invalid path - {}", path).into());
192 }
193
194 if components[0] == "m" {
195 components = components[1..components.len()].to_vec();
196 }
197 let r1 = Regex::new(r"^\d+'$")?;
198 let r2 = Regex::new(r"^\d+$")?;
199 let mut node = self.clone();
200 for elem in components.iter() {
201 if r1.is_match(elem) {
202 let index = elem[..elem.len() - 1].to_string().parse::<u64>()?;
203 if index > HARDENED_BIT {
204 return Err(format!("invalid path index - {}", index).into());
205 }
206 node = node._derive(HARDENED_BIT + index)?;
207 } else if r2.is_match(elem) {
208 let index = elem.to_string().parse::<u64>()?;
209 if index > HARDENED_BIT {
210 return Err(format!("invalid path index - {}", index).into());
211 }
212 node = node._derive(index)?;
213 }
214 }
215 Ok(node)
216 }
217}
218
219fn compute_hmac(key: &[u8], message: &[u8]) -> Result<[u8; 64], Box<dyn std::error::Error>> {
220 let mut mac = HmacSha512::new_from_slice(key)?;
221 mac.update(message);
222 let result = mac.finalize();
223 let code_bytes: [u8; 64] = result.into_bytes().into();
224 Ok(code_bytes)
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use hex_literal::hex;
231
232 #[test]
233 fn test_hd() {
234 let a = HDNode::new().unwrap();
235 let node = a.derive_path("m/44'/337'/0'/0/0").unwrap();
236 let pk = hex::encode(&node.private_key.unwrap());
237 println!("{}", node.mnemonic.unwrap().phrase());
238 println!("{}", pk);
239 }
240
241 #[test]
242 fn test_from_phrase() {
243 let node = HDNode::from_phrase("office picture sausage either disease ordinary comic loan unknown entire winner twice").unwrap().derive_path("m/44'/337'/0'/0/0").unwrap();
244 let expected = hex!("31e880225ba9103ed82b0d404518e303210d7fc0ecb6091be440112d59cda185");
245 assert_eq!(node.private_key.unwrap(), expected)
246 }
247
248 #[test]
249 fn test_hmac() {
250 let mut mac = HmacSha512::new_from_slice(b"my secret and secure key")
252 .expect("HMAC can take key of any size");
253 mac.update(b"input message");
254 let result = mac.finalize();
255 let code_bytes = result.into_bytes();
259 let expected = hex!(
260 "
261 e51c913d44379e50c69201a5d95fb43ec0d
262 c5b1736cd6f2214b506e64bd35c9dc0214c
263 900f62be4b61d507a60299b6bb1625e5e36
264 5a9aa4ed1089b0262fb99a5
265 "
266 );
267 assert_eq!(code_bytes[..], expected[..]);
268 }
269
270 #[test]
271 fn test_compute_hmac() {
272 let code_bytes = compute_hmac(b"my secret and secure key", b"input message").unwrap();
273 let expected = hex!(
274 "
275 e51c913d44379e50c69201a5d95fb43ec0d
276 c5b1736cd6f2214b506e64bd35c9dc0214c
277 900f62be4b61d507a60299b6bb1625e5e36
278 5a9aa4ed1089b0262fb99a5
279 "
280 );
281 assert_eq!(code_bytes[..], expected[..]);
282 }
283}