1use crate::cpc_aes::{AESParams, AES, Mode, InitVector};
2use rand::thread_rng;
3use rand_core::RngCore;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6use web3::signing::keccak256;
7
8use crate::accounts::Account;
9
10use self::{kdf::KDF, crypto_info::{CryptoInfo, CipherParams}};
11
12mod kdf;
13mod crypto_info;
14pub mod my_scrypt;
15
16#[derive(Serialize, Deserialize, Debug)]
18pub struct Keystore {
19 address: String,
20 #[serde(alias="Crypto")]
21 crypto: CryptoInfo,
22 id: String,
23 version: usize,
24}
25
26impl Keystore {
27 pub fn to_string(&self) -> Result<String, Box<dyn std::error::Error>> {
28 match serde_json::to_string(&self) {
29 Ok(s) => return Ok(s),
30 Err(e) => return Err(format!("{}", e).into()),
31 }
32 }
33
34 fn derive(kdf: &KDF, password: &str) -> Result<([u8; 16], [u8; 16], KDF), Box<dyn std::error::Error + Send + Sync>> {
35 let (password_hash, kdf) = kdf.encrypt(password)?;
36 let mut derived_key: [u8; 16] = [0; 16];
37 password_hash[..16].iter().enumerate().for_each(|(index, elem)| {
38 derived_key[index] = elem.clone();
39 });
40 let mut mac_prefix: [u8; 16] = [0; 16];
41 password_hash[16..].iter().enumerate().for_each(|(index, elem)| {
42 mac_prefix[index] = elem.clone();
43 });
44 Ok((derived_key, mac_prefix, kdf))
45 }
46
47 fn aes_128_ctr(derived_key: [u8; 16], data: &Vec<u8>, iv: [u8; 16]) -> Vec<u8> {
48 let params = AESParams {
50 mode: Some(Mode::CTR(InitVector::I16(iv)))
51 };
52 let encrypted = AES::AES128(derived_key).encrypt(&data, ¶ms).unwrap();
53 encrypted
54 }
55
56 fn rand_iv() -> [u8; 16] {
57 let mut iv = [0x0; 16];
58 let mut rng = thread_rng();
59 rng.fill_bytes(&mut iv);
60 iv
61 }
62
63 fn caclute_mac(prefix: &[u8; 16], encrypted: &Vec<u8>) -> [u8; 32] {
64 let mut bytes = prefix.to_vec();
65 bytes.append(&mut encrypted.clone());
66 let mac = keccak256(&bytes);
67 mac
68 }
69
70 fn encrypt(account: &Account, derived_key: &[u8; 16], mac_prefix: &[u8; 16], kdf: KDF) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
71 let iv = Keystore::rand_iv();
75 let encrypted = Keystore::aes_128_ctr(*derived_key, &account.private_key_bytes().to_vec(), iv);
76 let mac = Keystore::caclute_mac(&mac_prefix, &encrypted);
78 let id = Uuid::new_v4();
80 Ok(Self {
81 address: account.address.to_string().to_lowercase(),
82 crypto: CryptoInfo {
83 cipher: "aes-128-ctr".to_string(),
84 cipher_params: CipherParams { iv: hex::encode(iv) },
85 cipher_text: hex::encode(&encrypted),
86 kdf: kdf,
87 mac: hex::encode(mac),
88 },
89 id: id.to_string(),
90 version: 3,
91 })
92 }
93
94 pub fn encrypt_pbkdf2(account: &Account, password: &str) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
95 let (derived_key, mac_prefix, kdf) = Keystore::derive(&KDF::PBKDF2(None), password)?;
97 Keystore::encrypt(account, &derived_key, &mac_prefix, kdf)
98 }
99
100 pub fn encrypt_scrypt(account: &Account, password: &str) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
101 let (derived_key, mac_prefix, kdf) = Keystore::derive(&KDF::SCRYPT(None), password)?;
103 Keystore::encrypt(account, &derived_key, &mac_prefix, kdf)
104 }
105
106 pub fn decrypt(&self, password: &str) -> Result<Account, Box<dyn std::error::Error + Send + Sync>> {
107 let (key, mac_prefix, _) = Keystore::derive(&self.crypto.kdf, password)?;
109 let bytes = hex::decode(&self.crypto.cipher_text)?;
110 let expected_mac = Keystore::caclute_mac(&mac_prefix, &bytes);
112 if hex::encode(expected_mac) != self.crypto.mac {
113 return Err("invalid mac".into())
114 }
115 let mut iv:[u8; 16] = [0; 16];
117 hex::decode(&self.crypto.cipher_params.iv)?.iter().enumerate().for_each(|(i, e)| {
118 iv[i] = e.clone();
119 });
120 let decrypted = Keystore::aes_128_ctr(key, &bytes, iv);
121 let account = Account::from_private_key(&hex::encode(&decrypted))?;
122 let mut addr = self.address.clone();
123 if !addr.starts_with("0x") {
124 addr = "0x".to_string() + &addr;
125 }
126 if addr != account.address.to_checksum().to_lowercase() {
127 return Err("address mismatch".into())
128 }
129 Ok(account)
130 }
131}
132
133impl From<String> for Keystore {
134 fn from(s: String) -> Self {
135 let ks: Keystore = serde_json::from_str(s.as_str()).unwrap();
136 ks
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use crate::keystore::kdf::{Pbkdf2Params, ScryptParams};
143
144 use super::*;
145
146 #[test]
147 fn test_serialize() {
148 let pbkdf2_params = Pbkdf2Params {
150 c: 26144,
151 dklen: 32,
152 prf: "hmac-sha256".to_string(),
153 salt: "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd".to_string(),
154 };
155 let keystore = Keystore {
156 address: "0x888".to_string(),
157 crypto: CryptoInfo {
158 cipher: "aes-128-ctr".to_string(),
159 cipher_params: CipherParams {
160 iv: "83dbcc02d8ccb40e466191a123791e0e".to_string(),
161 },
162 cipher_text: "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c"
163 .to_string(),
164 kdf: KDF::PBKDF2(Some(pbkdf2_params.clone())),
165 mac: "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2".to_string(),
166 },
167 id: "3198bc9c-6672-5ab3-d995-4942343ae5b6".to_string(),
168 version: 1,
169 };
170 let serialized = keystore.to_string().unwrap();
171 println!("{}", serialized);
172
173 let scrypt_params = ScryptParams {
175 dklen: 32,
176 n: 262144,
177 p: 8,
178 r: 1,
179 salt: "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19".to_string(),
180 };
181 let keystore = Keystore {
182 address: "0x888".to_string(),
183 crypto: CryptoInfo {
184 cipher: "aes-128-ctr".to_string(),
185 cipher_params: CipherParams {
186 iv: "83dbcc02d8ccb40e466191a123791e0e".to_string(),
187 },
188 cipher_text: "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c"
189 .to_string(),
190 kdf: KDF::SCRYPT(Some(scrypt_params.clone())),
191 mac: "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2".to_string(),
192 },
193 id: "3198bc9c-6672-5ab3-d995-4942343ae5b6".to_string(),
194 version: 1,
195 };
196 let serialized = keystore.to_string().unwrap();
197 println!("{}", serialized);
198 }
199
200 #[test]
201 fn test_deserialize() {
202 let j = "
203 {\"address\":\"0xd7998FD7F5454722a16Cd67E881CedF9896CE396\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"24242424242424242424242424242424\"},\"ciphertext\":\"9a4785cd9c59ac3550e7be9c47045e24ae93bcd85518dd510f0e83537a6b1cf7\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"6949646d3976356e2b636e74462b32476d3742465677\"},\"mac\":\"1da9056f139ee205cc342233a1bfc3fb7cbd9f4d904aa9b971e373c369aee840\"},\"id\":\"2a327cb3-776a-4a77-8cf9-66b1a615d9b5\",\"version\":3}
204 ";
205 let ks = Keystore::from(j.to_string());
206 println!("{:?}", ks);
207 }
208
209 #[test]
210 fn test_encrypt_pbkdf2() {
211 let mnemonic = "lyrics mean wisdom census merit sample always escape spread tone pipe current";
212 let account = Account::from_phrase(mnemonic, None).unwrap();
213 let ks = Keystore::encrypt_pbkdf2(&account, "123456").unwrap();
214 println!("{} {:?}", ks.crypto.cipher_text, account.private_key());
215 println!("{}", ks.to_string().unwrap());
216 }
217
218 #[test]
219 fn test_pbkdf2() {
220 let password = "password";
222 let mnemonic = "lyrics mean wisdom census merit sample always escape spread tone pipe current";
223 let account1 = Account::from_phrase(mnemonic, None).unwrap();
224 let ks = Keystore::encrypt_pbkdf2(&account1, password).unwrap();
225 let account2 = Keystore::from(ks.to_string().unwrap()).decrypt(password).unwrap();
227 assert_eq!(account1.address.to_checksum(), account2.address.to_checksum());
228 assert_eq!(account1.private_key(), account2.private_key())
229 }
230
231 #[test]
232 fn test_decrypt_pbkdf2() {
233 let j = "
234 {\"address\":\"0xd7998fd7f5454722a16cd67e881cedf9896ce396\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"fa9d17a51ba92d61515a6bb629ad842e\"},\"ciphertext\":\"89ad80b27d8325bf79dcef52994e8b4ea626a357120dc8fdca4f0e61058f0496\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"dc0b353e69db487ee8ea3716085fe7b6\"},\"mac\":\"e15200e3b57f22714f09629fcf98b79fd8af6f8493c663f09c771e9cb17c1075\"},\"id\":\"83798882-4537-41ae-b205-30b032f2c88d\",\"version\":3}
235 ";
236 let account = Keystore::from(j.to_string()).decrypt("123456").unwrap();
237 assert_eq!(account.address.to_string(), "0xd7998FD7F5454722a16Cd67E881CedF9896CE396")
238 }
239
240 #[test]
241 fn test_decrypt_pbkdf2_password_error() {
242 let j = "
243 {\"address\":\"0xd7998fd7f5454722a16cd67e881cedf9896ce396\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"fa9d17a51ba92d61515a6bb629ad842e\"},\"ciphertext\":\"89ad80b27d8325bf79dcef52994e8b4ea626a357120dc8fdca4f0e61058f0496\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"dc0b353e69db487ee8ea3716085fe7b6\"},\"mac\":\"e15200e3b57f22714f09629fcf98b79fd8af6f8493c663f09c771e9cb17c1075\"},\"id\":\"83798882-4537-41ae-b205-30b032f2c88d\",\"version\":3}
244 ";
245 let r = Keystore::from(j.to_string()).decrypt("1234567");
246 assert_eq!(r.is_err(), true);
247 assert_eq!(r.err().unwrap().to_string(), "invalid mac");
249 }
250
251 #[test]
252 fn test_decrypt_pbkdf2_address_mismatch() {
253 let j = "
254 {\"address\":\"0x17998fd7f5454722a16cd67e881cedf9896ce396\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"fa9d17a51ba92d61515a6bb629ad842e\"},\"ciphertext\":\"89ad80b27d8325bf79dcef52994e8b4ea626a357120dc8fdca4f0e61058f0496\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"dc0b353e69db487ee8ea3716085fe7b6\"},\"mac\":\"e15200e3b57f22714f09629fcf98b79fd8af6f8493c663f09c771e9cb17c1075\"},\"id\":\"83798882-4537-41ae-b205-30b032f2c88d\",\"version\":3}
255 ";
256 let r = Keystore::from(j.to_string()).decrypt("123456");
257 assert_eq!(r.is_err(), true);
258 assert_eq!(r.err().unwrap().to_string(), "address mismatch");
259 }
260
261 #[test]
262 fn test_decrypt_pbkdf2_invalid_mac() {
263 let j = "
264 {\"address\":\"0xd7998fd7f5454722a16cd67e881cedf9896ce396\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"fa9d17a51ba92d61515a6bb629ad842e\"},\"ciphertext\":\"89ad80b27d8325bf79dcef52994e8b4ea626a357120dc8fdca4f0e61058f0496\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"dc0b353e69db487ee8ea3716085fe7b6\"},\"mac\":\"e15200e3b57f22714f09629fcf98b79fd8af6f8493c663f09c771e9cb17c1076\"},\"id\":\"83798882-4537-41ae-b205-30b032f2c88d\",\"version\":3}
265 ";
266 let r = Keystore::from(j.to_string()).decrypt("123456");
267 assert_eq!(r.is_err(), true);
268 assert_eq!(r.err().unwrap().to_string(), "invalid mac");
269 }
270
271 #[test]
272 fn test_encrypt_scrypt() {
273 let mnemonic = "lyrics mean wisdom census merit sample always escape spread tone pipe current";
274 let account = Account::from_phrase(mnemonic, None).unwrap();
275 let ks = Keystore::encrypt_scrypt(&account, "123456").unwrap();
276 println!("{} {:?}", ks.crypto.cipher_text, account.private_key());
277 println!("{}", ks.to_string().unwrap());
278 }
279
280 #[test]
281 fn test_scrypt() {
282 let password = "cpchain";
284 let mnemonic = "lyrics mean wisdom census merit sample always escape spread tone pipe current";
285 let account1 = Account::from_phrase(mnemonic, None).unwrap();
286 let ks = Keystore::encrypt_scrypt(&account1, password).unwrap();
287 let account2 = Keystore::from(ks.to_string().unwrap()).decrypt(password).unwrap();
289 assert_eq!(account1.address.to_checksum(), account2.address.to_checksum());
290 assert_eq!(account1.private_key(), account2.private_key())
291 }
292
293 #[test]
294 fn test_decrypt_scrypt() {
295 let password = "123456";
297 let j = "
298 {
299 \"address\": \"6cbea203f4061855247cea3843e2e5957c4cd428\",
300 \"id\": \"873cffc0-efe3-4efa-a68d-a22f8b333c96\",
301 \"version\": 3,
302 \"Crypto\": {
303 \"cipher\": \"aes-128-ctr\",
304 \"cipherparams\": { \"iv\": \"cca926fcfd00939b9846b876d447f436\" },
305 \"ciphertext\": \"2c3d7a39276a57a12acb3f4282f015a483a2c5453526f6c6184bd5273d9e0ca1\",
306 \"kdf\": \"scrypt\",
307 \"kdfparams\": {
308 \"salt\": \"91ac0a8a80bcc103a11057b9cef57e25057ce680b1b5ab844e11c32627bd2e50\",
309 \"n\": 131072,
310 \"dklen\": 32,
311 \"p\": 1,
312 \"r\": 8
313 },
314 \"mac\": \"4af097b05e403e8e135b3c2e7bcc73d3bae62989206f55b66856ce1d7e5e3977\"
315 }
316 }
317 ";
318 let r = Keystore::from(j.to_string()).decrypt(password).unwrap();
319 assert_eq!(r.address.to_checksum().to_lowercase(), "0x6cbea203f4061855247cea3843e2e5957c4cd428")
320 }
321
322}