1use crate::{
18 account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf},
19 json,
20 random::Random,
21 Error,
22};
23use cfx_crypto::crypto::{
24 aes, derive_mac, is_equal, keccak::Keccak256, pbkdf2, scrypt, KEY_LENGTH,
25};
26use cfxkey::{Password, Secret};
27use smallvec::SmallVec;
28use std::str;
29
30#[derive(Debug, PartialEq, Clone)]
32pub struct Crypto {
33 pub cipher: Cipher,
35 pub ciphertext: Vec<u8>,
37 pub kdf: Kdf,
39 pub mac: [u8; 32],
41}
42
43impl From<json::Crypto> for Crypto {
44 fn from(json: json::Crypto) -> Self {
45 Crypto {
46 cipher: json.cipher.into(),
47 ciphertext: json.ciphertext.into(),
48 kdf: json.kdf.into(),
49 mac: json.mac.into(),
50 }
51 }
52}
53
54impl From<Crypto> for json::Crypto {
55 fn from(c: Crypto) -> Self {
56 json::Crypto {
57 cipher: c.cipher.into(),
58 ciphertext: c.ciphertext.into(),
59 kdf: c.kdf.into(),
60 mac: c.mac.into(),
61 }
62 }
63}
64
65impl str::FromStr for Crypto {
66 type Err = <json::Crypto as str::FromStr>::Err;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 s.parse::<json::Crypto>().map(Into::into)
70 }
71}
72
73impl From<Crypto> for String {
74 fn from(c: Crypto) -> Self { json::Crypto::from(c).into() }
75}
76
77impl Crypto {
78 pub fn with_secret(
80 secret: &Secret, password: &Password, iterations: u32,
81 ) -> Result<Self, Error> {
82 Crypto::with_plain(secret.as_ref(), password, iterations)
83 }
84
85 pub fn with_plain(
87 plain: &[u8], password: &Password, iterations: u32,
88 ) -> Result<Self, Error> {
89 let salt: [u8; 32] = Random::random();
90 let iv: [u8; 16] = Random::random();
91
92 let (derived_left_bits, derived_right_bits) =
96 pbkdf2::derive_key_iterations(
97 password.as_bytes(),
98 &salt,
99 iterations,
100 )
101 .map_err(Error::EthCrypto)?;
102
103 let plain_len = plain.len();
106 let mut ciphertext: SmallVec<[u8; 32]> =
107 SmallVec::from_vec(vec![0; plain_len]);
108
109 aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
111
112 let mac = derive_mac(&derived_right_bits, &*ciphertext).keccak256();
115
116 Ok(Crypto {
117 cipher: Cipher::Aes128Ctr(Aes128Ctr { iv }),
118 ciphertext: ciphertext.into_vec(),
119 kdf: Kdf::Pbkdf2(Pbkdf2 {
120 dklen: KEY_LENGTH as u32,
121 salt: salt.to_vec(),
122 c: iterations,
123 prf: Prf::HmacSha256,
124 }),
125 mac,
126 })
127 }
128
129 pub fn secret(&self, password: &Password) -> Result<Secret, Error> {
131 if self.ciphertext.len() > 32 {
132 return Err(Error::InvalidSecret);
133 }
134
135 let secret = self.do_decrypt(password, 32)?;
136 Ok(Secret::from_unsafe_slice(&secret)?)
137 }
138
139 pub fn decrypt(&self, password: &Password) -> Result<Vec<u8>, Error> {
141 let expected_len = self.ciphertext.len();
142 self.do_decrypt(password, expected_len)
143 }
144
145 fn do_decrypt(
146 &self, password: &Password, expected_len: usize,
147 ) -> Result<Vec<u8>, Error> {
148 let (derived_left_bits, derived_right_bits) = match self.kdf {
149 Kdf::Pbkdf2(ref params) => pbkdf2::derive_key_iterations(
150 password.as_bytes(),
151 ¶ms.salt,
152 params.c,
153 )
154 .map_err(Error::EthCrypto)?,
155 Kdf::Scrypt(ref params) => scrypt::derive_key(
156 password.as_bytes(),
157 ¶ms.salt,
158 params.n,
159 params.p,
160 params.r,
161 )
162 .map_err(Error::EthCrypto)?,
163 };
164
165 let mac = derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
166
167 if !is_equal(&mac, &self.mac) {
168 return Err(Error::InvalidPassword);
169 }
170
171 let mut plain: SmallVec<[u8; 32]> =
172 SmallVec::from_vec(vec![0; expected_len]);
173
174 match self.cipher {
175 Cipher::Aes128Ctr(ref params) => {
176 debug_assert!(expected_len >= self.ciphertext.len());
178
179 let from = expected_len - self.ciphertext.len();
180 aes::decrypt_128_ctr(
181 &derived_left_bits,
182 ¶ms.iv,
183 &self.ciphertext,
184 &mut plain[from..],
185 )?;
186 Ok(plain.into_iter().collect())
187 }
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::{Crypto, Error};
195 use cfxkey::{Generator, Random};
196 use matches::assert_matches;
197
198 #[test]
199 fn crypto_with_secret_create() {
200 let keypair = Random.generate().unwrap();
201 let passwd = "this is sparta".into();
202 let crypto =
203 Crypto::with_secret(keypair.secret(), &passwd, 10240).unwrap();
204 let secret = crypto.secret(&passwd).unwrap();
205 assert_eq!(keypair.secret(), &secret);
206 }
207
208 #[test]
209 fn crypto_with_secret_invalid_password() {
210 let keypair = Random.generate().unwrap();
211 let crypto = Crypto::with_secret(
212 keypair.secret(),
213 &"this is sparta".into(),
214 10240,
215 )
216 .unwrap();
217 assert_matches!(
218 crypto.secret(&"this is sparta!".into()),
219 Err(Error::InvalidPassword)
220 )
221 }
222
223 #[test]
224 fn crypto_with_null_plain_data() {
225 let original_data = b"";
226 let passwd = "this is sparta".into();
227 let crypto =
228 Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
229 let decrypted_data = crypto.decrypt(&passwd).unwrap();
230 assert_eq!(original_data[..], *decrypted_data);
231 }
232
233 #[test]
234 fn crypto_with_tiny_plain_data() {
235 let original_data = b"{}";
236 let passwd = "this is sparta".into();
237 let crypto =
238 Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap();
239 let decrypted_data = crypto.decrypt(&passwd).unwrap();
240 assert_eq!(original_data[..], *decrypted_data);
241 }
242
243 #[test]
244 fn crypto_with_huge_plain_data() {
245 let original_data: Vec<_> =
246 (1..65536).map(|i| (i % 256) as u8).collect();
247 let passwd = "this is sparta".into();
248 let crypto =
249 Crypto::with_plain(&original_data, &passwd, 10240).unwrap();
250 let decrypted_data = crypto.decrypt(&passwd).unwrap();
251 assert_eq!(&original_data, &decrypted_data);
252 }
253}