1use std::str::FromStr;
2
3use aes::{
4 cipher::{self, InnerIvInit, KeyInit, StreamCipherCore},
5 Aes128,
6};
7use ethers_hash_rs::pbkdf2::pbkdf2_hmac;
8use ethers_types_rs::{bytes_def, Address, AddressEx};
9use rand::{CryptoRng, Rng};
10use scrypt::{scrypt, Params as ScryptParams};
11use serde::{Deserialize, Serialize};
12use sha2::Sha256;
13use sha3::{Digest, Keccak256};
14use uuid::Uuid;
15
16use crate::{wallet::KeyProvider, WalletError};
17
18bytes_def!(IV);
19bytes_def!(CipherText);
20bytes_def!(MAC);
21bytes_def!(Salt);
22
23const DEFAULT_CIPHER: &str = "aes-128-ctr";
24const DEFAULT_KEY_SIZE: usize = 32usize;
25const DEFAULT_IV_SIZE: usize = 16usize;
26const DEFAULT_KDF_PARAMS_DKLEN: u8 = 32u8;
27const DEFAULT_KDF_PARAMS_LOG_N: u8 = 13u8;
28const DEFAULT_KDF_PARAMS_R: u32 = 8u32;
29const DEFAULT_KDF_PARAMS_P: u32 = 1u32;
30
31#[derive(Debug, thiserror::Error)]
32pub enum KeyStoreError {
33 #[error("Scrypt error,{0}")]
34 Scrypt(String),
35 #[error("Keystore from json, mac mismatch")]
36 MacMismatch,
37
38 #[error("Load key error,{0}")]
39 KeyProvider(WalletError),
40}
41
42#[derive(Debug, Serialize, Deserialize)]
43pub struct KeyStore {
44 pub address: Option<Address>,
45 pub crypto: CryptoJson,
46 pub id: Uuid,
47 pub version: u8,
48}
49
50#[derive(Debug, Deserialize, Serialize)]
51pub struct CryptoJson {
53 pub cipher: String,
54 pub cipherparams: CipherparamsJson,
55 pub ciphertext: CipherText,
56 pub kdf: KdfType,
57 pub kdfparams: KdfparamsType,
58 pub mac: MAC,
59}
60
61#[derive(Debug, Deserialize, Serialize)]
62pub struct CipherparamsJson {
64 pub iv: IV,
65}
66
67#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
68#[serde(rename_all = "lowercase")]
69pub enum KdfType {
71 Pbkdf2,
72 Scrypt,
73}
74
75#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
76#[serde(untagged)]
77pub enum KdfparamsType {
79 Pbkdf2 {
80 c: u32,
81 dklen: u8,
82 prf: String,
83 salt: Salt,
84 },
85 Scrypt {
86 dklen: u8,
87 n: u32,
88 p: u32,
89 r: u32,
90 salt: Salt,
91 },
92}
93
94impl TryInto<String> for KeyStore {
95 type Error = serde_json::Error;
96 fn try_into(self) -> Result<String, Self::Error> {
97 serde_json::to_string_pretty(&self)
98 }
99}
100
101impl FromStr for KeyStore {
102 type Err = serde_json::Error;
103 fn from_str(s: &str) -> Result<Self, Self::Err> {
104 serde_json::from_str(s)
105 }
106}
107
108pub trait KeyStoreEncrypt {
109 fn encrypt<B, P>(pk: B, password: P) -> anyhow::Result<KeyStore>
111 where
112 P: AsRef<[u8]>,
113 B: KeyProvider,
114 {
115 Self::encrypt_with(&mut rand::rngs::OsRng, pk, password)
116 }
117 fn encrypt_with<R, B, P>(rng: &mut R, pk: B, password: P) -> anyhow::Result<KeyStore>
118 where
119 R: Rng + CryptoRng,
120 P: AsRef<[u8]>,
121 B: KeyProvider,
122 {
123 let mut salt = vec![0u8; DEFAULT_KEY_SIZE];
125 rng.fill_bytes(salt.as_mut_slice());
126
127 let mut key = vec![0u8; DEFAULT_KDF_PARAMS_DKLEN as usize];
129 let scrypt_params = ScryptParams::new(
130 DEFAULT_KDF_PARAMS_LOG_N,
131 DEFAULT_KDF_PARAMS_R,
132 DEFAULT_KDF_PARAMS_P,
133 )
134 .map_err(|err| KeyStoreError::Scrypt(err.to_string()))?;
135
136 scrypt(password.as_ref(), &salt, &scrypt_params, key.as_mut_slice())
137 .map_err(|err| KeyStoreError::Scrypt(err.to_string()))?;
138
139 let mut iv = vec![0u8; DEFAULT_IV_SIZE];
141 rng.fill_bytes(iv.as_mut_slice());
142
143 let encryptor = Aes128Ctr::new(&key[..16], &iv[..16]).expect("invalid length");
144
145 let mut ciphertext = pk.load()?;
146
147 let address = Address::from_private_key(&ciphertext).ok();
148
149 encryptor.apply_keystream(&mut ciphertext);
150
151 let mut mac = Keccak256::new();
153
154 mac.update(&key[16..32]);
155 mac.update(&ciphertext);
156
157 let mac = mac.finalize();
158
159 let id = Uuid::new_v4();
161
162 Ok(KeyStore {
164 id,
165 version: 3,
166 crypto: CryptoJson {
167 cipher: String::from(DEFAULT_CIPHER),
168 cipherparams: CipherparamsJson { iv: iv.into() },
169 ciphertext: ciphertext.to_vec().into(),
170 kdf: KdfType::Scrypt,
171 kdfparams: KdfparamsType::Scrypt {
172 dklen: DEFAULT_KDF_PARAMS_DKLEN,
173 n: 2u32.pow(DEFAULT_KDF_PARAMS_LOG_N as u32),
174 p: DEFAULT_KDF_PARAMS_P,
175 r: DEFAULT_KDF_PARAMS_R,
176 salt: salt.into(),
177 },
178 mac: mac.to_vec().into(),
179 },
180 address,
181 })
182 }
183}
184
185struct Aes128Ctr {
186 inner: ctr::CtrCore<Aes128, ctr::flavors::Ctr128BE>,
187}
188
189impl Aes128Ctr {
190 fn new(key: &[u8], iv: &[u8]) -> Result<Self, cipher::InvalidLength> {
191 let cipher = aes::Aes128::new_from_slice(key).unwrap();
192 let inner = ctr::CtrCore::inner_iv_slice_init(cipher, iv).unwrap();
193 Ok(Self { inner })
194 }
195
196 fn apply_keystream(self, buf: &mut [u8]) {
197 self.inner.apply_keystream_partial(buf.into());
198 }
199}
200
201impl KeyStoreEncrypt for KeyStore {}
202
203impl KeyStore {
204 pub fn decrypt_into<S>(self, password: S) -> Result<Vec<u8>, KeyStoreError>
205 where
206 S: AsRef<[u8]>,
207 {
208 let keystore = self;
209
210 let key = match keystore.crypto.kdfparams {
212 KdfparamsType::Pbkdf2 {
213 c,
214 dklen,
215 prf: _,
216 salt,
217 } => {
218 let mut key = vec![0u8; dklen as usize];
219 pbkdf2_hmac::<Sha256>(password.as_ref(), &salt.0, c, key.as_mut_slice());
220 key
221 }
222 KdfparamsType::Scrypt {
223 dklen,
224 n,
225 p,
226 r,
227 salt,
228 } => {
229 let mut key = vec![0u8; dklen as usize];
230 let log_n = (n as f32).log2() as u8;
231 let scrypt_params = ScryptParams::new(log_n, r, p)
232 .map_err(|err| KeyStoreError::Scrypt(err.to_string()))?;
233
234 scrypt(
235 password.as_ref(),
236 &salt.0,
237 &scrypt_params,
238 key.as_mut_slice(),
239 )
240 .map_err(|err| KeyStoreError::Scrypt(err.to_string()))?;
241 key
242 }
243 };
244
245 let mut derived_mac = Keccak256::new();
247
248 derived_mac.update(&key[16..32]);
249 derived_mac.update(&keystore.crypto.ciphertext.0);
250 let derived_mac = derived_mac.finalize();
251
252 if derived_mac.as_slice() != keystore.crypto.mac.0.as_slice() {
253 return Err(KeyStoreError::MacMismatch);
254 }
255
256 let decryptor = Aes128Ctr::new(&key[..16], &keystore.crypto.cipherparams.iv.0[..16])
258 .expect("invalid length");
259
260 let mut pk = keystore.crypto.ciphertext.0;
261
262 decryptor.apply_keystream(&mut pk);
263
264 Ok(pk)
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use ethers_types_rs::{bytes::bytes_to_string, Address, AddressEx};
271
272 use super::{KeyStore, KeyStoreEncrypt};
273
274 #[test]
275 fn test_encrypt_decrypt() {
276 let keystore = KeyStore::encrypt(
277 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
278 "thebestrandompassword",
279 )
280 .expect("Encrypt into keystore format");
281
282 assert_eq!(
284 keystore.address,
285 Some(
286 "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
287 .try_into()
288 .expect("")
289 )
290 );
291
292 let pk = keystore
293 .decrypt_into("thebestrandompassword")
294 .expect("Descrypt keystore");
295
296 assert_eq!(
297 bytes_to_string(&pk),
298 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
299 );
300 }
301
302 #[test]
303 fn test_decrypt_pbkdf2() {
304 let keystore: KeyStore = include_str!("test-keys/pbkdf2.json")
305 .parse()
306 .expect("Load keystore from file");
307
308 let pk = keystore
309 .decrypt_into("testpassword")
310 .expect("Decrypt keystore with password");
311
312 assert_eq!(
313 bytes_to_string(&pk),
314 "0x7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
315 );
316 }
317
318 #[test]
319 fn test_decrypt_scrypt() {
320 let _ = pretty_env_logger::try_init();
321
322 let keystore: KeyStore = include_str!("test-keys/scrypt.json")
323 .parse()
324 .expect("Load keystore from file");
325
326 let pk = keystore
327 .decrypt_into("grOQ8QDnGHvpYJf")
328 .expect("Decrypt keystore with password");
329
330 assert_eq!(
331 bytes_to_string(&pk),
332 "0x80d3a6ed7b24dcd652949bc2f3827d2f883b3722e3120b15a93a2e0790f03829"
333 );
334 }
335
336 #[test]
337 fn test_decrypt_ethersjs_keystore() {
338 let keystore: KeyStore = include_str!("test-keys/ethersjs.json")
339 .parse()
340 .expect("Load keystore from file");
341
342 let expect_address = keystore.address.clone();
343
344 let pk = keystore
345 .decrypt_into("test")
346 .expect("Decrypt keystore with password");
347
348 let address = Address::from_private_key(&pk).expect("Get address from descrypt pk");
349
350 assert_eq!(expect_address, Some(address));
351 }
352}