1use futu_core::error::FutuError;
12
13pub fn aes_ecb_encrypt(key: &[u8; 16], data: &[u8]) -> Vec<u8> {
17 use std::iter;
18
19 let block_size = 16;
21 let padding_len = block_size - (data.len() % block_size);
22 let mut padded: Vec<u8> = data.to_vec();
23 padded.extend(iter::repeat_n(padding_len as u8, padding_len));
24
25 let mut output = vec![0u8; padded.len()];
27 for (i, chunk) in padded.chunks(block_size).enumerate() {
28 let block = aes_encrypt_block(key, chunk);
29 output[i * block_size..(i + 1) * block_size].copy_from_slice(&block);
30 }
31
32 output
33}
34
35pub fn aes_ecb_decrypt(
39 key: &[u8; 16],
40 data: &[u8],
41) -> Result<Vec<u8>, futu_core::error::FutuError> {
42 if data.is_empty() || !data.len().is_multiple_of(16) {
43 return Err(futu_core::error::FutuError::Encryption(
44 "ciphertext length must be a multiple of 16".into(),
45 ));
46 }
47
48 let block_size = 16;
49 let mut output = vec![0u8; data.len()];
50 for (i, chunk) in data.chunks(block_size).enumerate() {
51 let block = aes_decrypt_block(key, chunk);
52 output[i * block_size..(i + 1) * block_size].copy_from_slice(&block);
53 }
54
55 let padding_len = output
57 .last()
58 .copied()
59 .ok_or_else(|| futu_core::error::FutuError::Encryption("empty decrypted body".into()))?
60 as usize;
61 if padding_len == 0 || padding_len > block_size {
62 return Err(futu_core::error::FutuError::Encryption(
63 "invalid PKCS7 padding".into(),
64 ));
65 }
66 if output[output.len() - padding_len..]
67 .iter()
68 .any(|&b| b as usize != padding_len)
69 {
70 return Err(futu_core::error::FutuError::Encryption(
71 "invalid PKCS7 padding bytes".into(),
72 ));
73 }
74 output.truncate(output.len() - padding_len);
75
76 Ok(output)
77}
78
79fn aes_encrypt_block(key: &[u8; 16], block: &[u8]) -> [u8; 16] {
84 use aes::cipher::{BlockEncrypt, KeyInit};
85 let encryptor = aes::Aes128::new(key.into());
86 let mut out = [0u8; 16];
87 out.copy_from_slice(block);
88 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
89 encryptor.encrypt_block(ga);
90 out
91}
92
93fn aes_decrypt_block(key: &[u8; 16], block: &[u8]) -> [u8; 16] {
95 use aes::cipher::{BlockDecrypt, KeyInit};
96 let decryptor = aes::Aes128::new(key.into());
97 let mut out = [0u8; 16];
98 out.copy_from_slice(block);
99 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
100 decryptor.decrypt_block(ga);
101 out
102}
103
104fn aes_encrypt_block_var(key: &[u8], block: &[u8]) -> Result<[u8; 16], FutuError> {
108 use aes::cipher::{BlockEncrypt, KeyInit};
109 let mut out = [0u8; 16];
110 out.copy_from_slice(block);
111 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
112 match key.len() {
113 16 => {
114 aes::Aes128::new(aes::cipher::generic_array::GenericArray::from_slice(key))
115 .encrypt_block(ga);
116 }
117 24 => {
118 aes::Aes192::new(aes::cipher::generic_array::GenericArray::from_slice(key))
119 .encrypt_block(ga);
120 }
121 32 => {
122 aes::Aes256::new(aes::cipher::generic_array::GenericArray::from_slice(key))
123 .encrypt_block(ga);
124 }
125 _ => {
126 return Err(unsupported_aes_var_key_len(
127 "aes_encrypt_block_var",
128 key.len(),
129 ));
130 }
131 }
132 Ok(out)
133}
134
135fn aes_decrypt_block_var(key: &[u8], block: &[u8]) -> Result<[u8; 16], FutuError> {
137 use aes::cipher::{BlockDecrypt, KeyInit};
138 let mut out = [0u8; 16];
139 out.copy_from_slice(block);
140 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
141 match key.len() {
142 16 => {
143 aes::Aes128::new(aes::cipher::generic_array::GenericArray::from_slice(key))
144 .decrypt_block(ga);
145 }
146 24 => {
147 aes::Aes192::new(aes::cipher::generic_array::GenericArray::from_slice(key))
148 .decrypt_block(ga);
149 }
150 32 => {
151 aes::Aes256::new(aes::cipher::generic_array::GenericArray::from_slice(key))
152 .decrypt_block(ga);
153 }
154 _ => {
155 return Err(unsupported_aes_var_key_len(
156 "aes_decrypt_block_var",
157 key.len(),
158 ));
159 }
160 }
161 Ok(out)
162}
163
164fn unsupported_aes_var_key_len(context: &str, len: usize) -> FutuError {
165 FutuError::Encryption(format!("{context}: unsupported key length {len}"))
166}
167
168pub fn aes_cbc_md5_encrypt(key: &[u8; 16], data: &[u8]) -> Result<Vec<u8>, FutuError> {
174 aes_cbc_md5_encrypt_var(key, data)
175}
176
177pub fn aes_cbc_md5_encrypt_var(key: &[u8], data: &[u8]) -> Result<Vec<u8>, FutuError> {
183 let input_len = data.len();
184 let modv = input_len % 16;
185 let last_block_size = if modv == 0 { 0u8 } else { modv as u8 };
186
187 let data_blocks = if modv == 0 {
188 input_len
189 } else {
190 input_len - modv + 16
191 };
192 let total_size = data_blocks + 16 + 16;
193 let mut output = vec![0u8; total_size];
194
195 let mut md5_ctx = md5::Context::new();
196 let mut encrypt_pos = 0usize;
197 let aligned_end = input_len - modv;
198
199 while encrypt_pos < aligned_end {
200 let block = &data[encrypt_pos..encrypt_pos + 16];
201 md5_ctx.consume(block);
202 if encrypt_pos == 0 {
203 let enc = aes_encrypt_block_var(key, block)?;
204 output[..16].copy_from_slice(&enc);
205 } else {
206 let mut xor_block = [0u8; 16];
207 for i in 0..16 {
208 xor_block[i] = output[encrypt_pos - 16 + i] ^ block[i];
209 }
210 let enc = aes_encrypt_block_var(key, &xor_block)?;
211 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&enc);
212 }
213 encrypt_pos += 16;
214 }
215
216 if modv != 0 {
217 let mut tmp = [0u8; 16];
218 tmp[..last_block_size as usize].copy_from_slice(&data[encrypt_pos..encrypt_pos + modv]);
219 md5_ctx.consume(tmp);
220 if encrypt_pos > 0 {
221 for i in 0..16 {
222 tmp[i] ^= output[encrypt_pos - 16 + i];
223 }
224 }
225 let enc = aes_encrypt_block_var(key, &tmp)?;
226 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&enc);
227 encrypt_pos += 16;
228 }
229
230 let mut padding = [0u8; 16];
231 padding[15] = last_block_size;
232 let enc_pad = aes_encrypt_block_var(key, &padding)?;
233 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&enc_pad);
234 encrypt_pos += 16;
235
236 let md5_hash = md5_ctx.compute();
237 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&md5_hash.0);
238
239 Ok(output)
240}
241
242pub fn aes_cbc_md5_decrypt_var(
244 key: &[u8],
245 data: &[u8],
246) -> std::result::Result<Vec<u8>, futu_core::error::FutuError> {
247 let input_len = data.len();
248 if input_len < 32 || !input_len.is_multiple_of(16) {
249 return Err(futu_core::error::FutuError::Encryption(format!(
250 "cbc_md5_var: invalid ciphertext length {input_len}"
251 )));
252 }
253
254 let padding_block = aes_decrypt_block_var(key, &data[input_len - 32..input_len - 16])?;
255 let last_block_size = padding_block[15] as usize;
256 if last_block_size > 15 {
257 return Err(futu_core::error::FutuError::Encryption(format!(
258 "cbc_md5_var: last_block_size {last_block_size} > 15"
259 )));
260 }
261
262 let data_blocks_end = input_len - 32;
263 let plaintext_len = if last_block_size == 0 {
264 data_blocks_end
265 } else {
266 data_blocks_end - 16 + last_block_size
267 };
268
269 let mut output = vec![0u8; plaintext_len];
270 let mut md5_ctx = md5::Context::new();
271 let mut pos = 0usize;
272 while pos < data_blocks_end {
273 let block = &data[pos..pos + 16];
274 let decrypted = aes_decrypt_block_var(key, block)?;
275 let mut plain_block = [0u8; 16];
276 if pos == 0 {
277 plain_block.copy_from_slice(&decrypted);
278 } else {
279 for i in 0..16 {
280 plain_block[i] = decrypted[i] ^ data[pos - 16 + i];
281 }
282 }
283 md5_ctx.consume(plain_block);
284
285 let is_last_block = pos + 16 == data_blocks_end;
286 let effective_len = if is_last_block && last_block_size != 0 {
287 last_block_size
288 } else {
289 16
290 };
291 output[pos..pos + effective_len].copy_from_slice(&plain_block[..effective_len]);
292 pos += 16;
293 }
294
295 let computed_md5 = md5_ctx.compute();
296 if computed_md5.0 != data[input_len - 16..] {
297 return Err(futu_core::error::FutuError::Encryption(
298 "cbc_md5_var: MD5 checksum mismatch".into(),
299 ));
300 }
301
302 Ok(output)
303}
304
305pub fn aes_cbc_md5_decrypt(
307 key: &[u8; 16],
308 data: &[u8],
309) -> std::result::Result<Vec<u8>, futu_core::error::FutuError> {
310 aes_cbc_md5_decrypt_var(key, data)
311}
312
313fn load_rsa_private_key(
320 pem_private_key: &str,
321) -> Result<rsa::RsaPrivateKey, futu_core::error::FutuError> {
322 use rsa::pkcs8::DecodePrivateKey;
323
324 rsa::RsaPrivateKey::from_pkcs8_pem(pem_private_key)
325 .or_else(|_| {
326 use rsa::pkcs1::DecodeRsaPrivateKey;
327 rsa::RsaPrivateKey::from_pkcs1_pem(pem_private_key)
328 })
329 .map_err(|e| {
330 futu_core::error::FutuError::Encryption(format!("invalid RSA private key: {e}"))
331 })
332}
333
334pub fn rsa_public_encrypt(
338 pem_private_key: &str,
339 data: &[u8],
340) -> Result<Vec<u8>, futu_core::error::FutuError> {
341 use rsa::Pkcs1v15Encrypt;
342
343 let private_key = load_rsa_private_key(pem_private_key)?;
344 let public_key = rsa::RsaPublicKey::from(&private_key);
345 let mut rng = rand::thread_rng();
346
347 public_key
348 .encrypt(&mut rng, Pkcs1v15Encrypt, data)
349 .map_err(|e| futu_core::error::FutuError::Encryption(format!("RSA encrypt failed: {e}")))
350}
351
352pub fn rsa_private_decrypt(
354 pem_private_key: &str,
355 data: &[u8],
356) -> Result<Vec<u8>, futu_core::error::FutuError> {
357 use rsa::Pkcs1v15Encrypt;
358
359 let private_key = load_rsa_private_key(pem_private_key)?;
360
361 private_key
362 .decrypt(Pkcs1v15Encrypt, data)
363 .map_err(|e| futu_core::error::FutuError::Encryption(format!("RSA decrypt failed: {e}")))
364}
365
366pub fn rsa_public_encrypt_blocks(
372 pem_private_key: &str,
373 data: &[u8],
374) -> Result<Vec<u8>, futu_core::error::FutuError> {
375 use rsa::Pkcs1v15Encrypt;
376 use rsa::traits::PublicKeyParts;
377
378 let private_key = load_rsa_private_key(pem_private_key)?;
379 let public_key = rsa::RsaPublicKey::from(&private_key);
380
381 let key_len = public_key.size();
383 let max_block = key_len - 11;
384
385 let mut result = Vec::with_capacity((data.len() / max_block + 1) * key_len);
386 let mut rng = rand::thread_rng();
387
388 for chunk in data.chunks(max_block) {
389 let encrypted = public_key
390 .encrypt(&mut rng, Pkcs1v15Encrypt, chunk)
391 .map_err(|e| {
392 futu_core::error::FutuError::Encryption(format!("RSA block encrypt failed: {e}"))
393 })?;
394 result.extend_from_slice(&encrypted);
395 }
396
397 Ok(result)
398}
399
400pub fn rsa_private_decrypt_blocks(
404 pem_private_key: &str,
405 data: &[u8],
406) -> Result<Vec<u8>, futu_core::error::FutuError> {
407 use rsa::Pkcs1v15Encrypt;
408 use rsa::traits::PublicKeyParts;
409
410 let private_key = load_rsa_private_key(pem_private_key)?;
411 let key_len = private_key.size();
412
413 if !data.len().is_multiple_of(key_len) {
414 return Err(futu_core::error::FutuError::Encryption(format!(
415 "RSA ciphertext length {} is not a multiple of key size {}",
416 data.len(),
417 key_len
418 )));
419 }
420
421 let mut result = Vec::with_capacity(data.len());
422
423 for chunk in data.chunks(key_len) {
424 let decrypted = private_key.decrypt(Pkcs1v15Encrypt, chunk).map_err(|e| {
425 futu_core::error::FutuError::Encryption(format!("RSA block decrypt failed: {e}"))
426 })?;
427 result.extend_from_slice(&decrypted);
428 }
429
430 Ok(result)
431}
432
433#[cfg(test)]
434mod tests;