ethers_wallet_rs/hd_wallet/
bip32.rs1use std::io::Write;
2
3use ethers_hash_rs::pbkdf2::pbkdf2_hmac_array;
4use hmac::{Hmac, Mac};
5use k256::ecdsa::SigningKey;
6use sha2::Sha512;
7use thiserror::Error;
8
9use super::bip44::{Bip44Error, Bip44Node, Bip44Path};
10
11#[derive(Debug, Error)]
12pub enum Bip32Error {
13 #[error("Parse bip44 path error,{0}")]
14 Bip44(Bip44Error),
15}
16
17fn mnemonic_to_send<M, P>(mnemonic: M, password: P) -> [u8; 64]
18where
19 M: AsRef<[u8]>,
20 P: AsRef<[u8]>,
21{
22 let mut salt = "mnemonic".as_bytes().to_vec();
23
24 salt.append(&mut password.as_ref().to_vec());
25
26 pbkdf2_hmac_array::<Sha512, 64>(mnemonic.as_ref(), salt.as_ref(), 2048)
27}
28
29pub struct DriveKey {
30 pub public_key: [u8; 33],
31 pub private_key: [u8; 32],
32 pub chain_code: [u8; 32],
33 pub node: Option<Bip44Node>,
34}
35
36type HmacSha512 = Hmac<Sha512>;
37
38impl DriveKey {
39 pub fn new<M, P>(mnemonic: M, password: P) -> Self
40 where
41 M: AsRef<[u8]>,
42 P: AsRef<[u8]>,
43 {
44 let seed = mnemonic_to_send(mnemonic, password);
45
46 Self::new_master_key(seed)
47 }
48
49 pub(crate) fn new_master_key(seed: [u8; 64]) -> Self {
50 let mut h = HmacSha512::new_from_slice(b"Bitcoin seed").expect("Create HmacSha512");
51
52 h.write(&seed).expect("Write hmac data");
53
54 let intermediary = h.finalize().into_bytes();
55
56 let key_data = &intermediary[..32];
57 let chain_code = &intermediary[32..];
58
59 let key = SigningKey::from_bytes(key_data).expect("Create ecdsa signing key");
60
61 let public_key = key.verifying_key().to_encoded_point(true);
62
63 DriveKey {
64 public_key: public_key
65 .as_bytes()
66 .try_into()
67 .expect("Convert public key"),
68 private_key: key_data.try_into().expect("Convert private key"),
69 chain_code: chain_code.try_into().expect("Convert chain code"),
70 node: None,
71 }
72 }
73
74 pub(crate) fn child_key(&self, node: Bip44Node) -> Self {
75 let mut h = HmacSha512::new_from_slice(&self.chain_code).expect("HmacSha512 new");
76
77 match node {
78 Bip44Node::Hardened(_) => {
79 h.write(&[0]).expect("Hmac write");
80
81 h.write(&self.private_key).expect("Hmac write");
82 }
83 Bip44Node::Normal(_) => {
84 h.write(&self.public_key).expect("Hmac write");
85 }
86 }
87
88 let index_bytes = (u64::from(&node) as u32).to_be_bytes();
89
90 h.write(&index_bytes).expect("Hmac write");
91
92 let intermediary = h.finalize().into_bytes();
93
94 let key_data = &intermediary[..32];
95 let chain_code = &intermediary[32..];
96
97 let key = SigningKey::from_bytes(key_data).expect("Create ecdsa signing key");
98
99 let scalar = key.as_nonzero_scalar().add(
100 SigningKey::from_bytes(&self.private_key)
101 .expect("Create ecdsa signing key")
102 .as_nonzero_scalar(),
103 );
104
105 let key = SigningKey::from_bytes(&scalar.to_bytes()).expect("Create ecdsa signing key");
106
107 let public_key = key.verifying_key().to_encoded_point(true);
108
109 DriveKey {
110 public_key: public_key
111 .as_bytes()
112 .try_into()
113 .expect("Convert public key"),
114 private_key: key.to_bytes().try_into().expect("Convert private key"),
115 chain_code: chain_code.try_into().expect("Convert chain code"),
116 node: Some(node),
117 }
118 }
119
120 pub fn drive<P>(&self, path: P) -> Result<DriveKey, Bip32Error>
121 where
122 P: AsRef<str>,
123 {
124 let path: Bip44Path = path
125 .as_ref()
126 .parse()
127 .map_err(|err| Bip32Error::Bip44(err))?;
128
129 Ok(self
130 .child_key(path.purpose)
131 .child_key(path.coin)
132 .child_key(path.account)
133 .child_key(path.change)
134 .child_key(path.address))
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use ethers_types_rs::{bytes::bytes_to_string, Address, AddressEx, Eip55};
141
142 use super::{mnemonic_to_send, DriveKey};
143
144 #[test]
145 fn test_gen_seed() {
146 let seed = mnemonic_to_send(
147 "canal walnut regular license dust liberty story expect repeat design picture medal",
148 "",
149 );
150
151 assert_eq!("0x15cba277c500b4e0c777d563278130f4c24b52532b3c8c45e051d417bebc5c007243c07d2e341a2d7c17bbd3880b968ca60869edab8f015be30674ad4d3d260f",bytes_to_string(&seed));
152 }
153
154 #[test]
155 fn test_hardhat_default_accounts() {
156 let _ = pretty_env_logger::try_init();
157
158 let drive_key = DriveKey::new(
159 "test test test test test test test test test test test junk",
160 "",
161 );
162
163 fn check_drive(drive_key: &DriveKey, id: usize, pk: &str, expect_address: &str) {
164 let key = drive_key
165 .drive(format!("m/44'/60'/0'/0/{}", id))
166 .expect("Bip32 drive key");
167
168 assert_eq!(bytes_to_string(&key.private_key), pk);
169
170 let address = Address::from_pub_key_compressed(key.public_key).expect("Parse address");
171
172 assert_eq!(address.to_checksum_string(), expect_address);
173 }
174
175 check_drive(
176 &drive_key,
177 0,
178 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
179 "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
180 );
181
182 check_drive(
183 &drive_key,
184 19,
185 "0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e",
186 "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199",
187 );
188 }
189}