1pub fn aes_ecb_encrypt(key: &[u8; 16], data: &[u8]) -> Vec<u8> {
15 use std::iter;
16
17 let block_size = 16;
19 let padding_len = block_size - (data.len() % block_size);
20 let mut padded: Vec<u8> = data.to_vec();
21 padded.extend(iter::repeat_n(padding_len as u8, padding_len));
22
23 let mut output = vec![0u8; padded.len()];
25 for (i, chunk) in padded.chunks(block_size).enumerate() {
26 let block = aes_encrypt_block(key, chunk);
27 output[i * block_size..(i + 1) * block_size].copy_from_slice(&block);
28 }
29
30 output
31}
32
33pub fn aes_ecb_encrypt_with_tail(key: &[u8; 16], data: &[u8]) -> Vec<u8> {
38 let block_size = 16;
39 let remainder = (data.len() % block_size) as u8;
40 let full_blocks = data.len() / block_size;
41
42 let aligned_len = if remainder == 0 {
43 data.len()
44 } else {
45 data.len() - remainder as usize + block_size
46 };
47
48 let mut output = Vec::with_capacity(aligned_len + block_size);
49
50 for i in 0..full_blocks {
52 let block = aes_encrypt_block(key, &data[i * block_size..(i + 1) * block_size]);
53 output.extend_from_slice(&block);
54 }
55
56 if remainder != 0 {
58 let mut last_block = [0u8; 16];
59 last_block[..remainder as usize].copy_from_slice(&data[full_blocks * block_size..]);
60 let encrypted = aes_encrypt_block(key, &last_block);
61 output.extend_from_slice(&encrypted);
62 }
63
64 let mut tail = [0u8; 16];
66 tail[15] = remainder;
67 output.extend_from_slice(&tail);
68
69 output
70}
71
72pub fn aes_ecb_decrypt_with_tail(
74 key: &[u8; 16],
75 data: &[u8],
76) -> Result<Vec<u8>, futu_core::error::FutuError> {
77 if data.len() < 32 || !data.len().is_multiple_of(16) {
78 return Err(futu_core::error::FutuError::Encryption(
79 "ecb_tail: invalid length".into(),
80 ));
81 }
82
83 let tail = &data[data.len() - 16..];
85 let remainder = tail[15] as usize;
86 let encrypted_data = &data[..data.len() - 16];
87
88 let block_size = 16;
89 let mut output = Vec::with_capacity(encrypted_data.len());
90
91 for chunk in encrypted_data.chunks(block_size) {
92 let decrypted = aes_decrypt_block(key, chunk);
93 output.extend_from_slice(&decrypted);
94 }
95
96 if remainder != 0 && !output.is_empty() {
98 let final_len = output.len() - block_size + remainder;
99 output.truncate(final_len);
100 }
101
102 Ok(output)
103}
104
105pub fn aes_ecb_decrypt(
109 key: &[u8; 16],
110 data: &[u8],
111) -> Result<Vec<u8>, futu_core::error::FutuError> {
112 if data.is_empty() || !data.len().is_multiple_of(16) {
113 return Err(futu_core::error::FutuError::Encryption(
114 "ciphertext length must be a multiple of 16".into(),
115 ));
116 }
117
118 let block_size = 16;
119 let mut output = vec![0u8; data.len()];
120 for (i, chunk) in data.chunks(block_size).enumerate() {
121 let block = aes_decrypt_block(key, chunk);
122 output[i * block_size..(i + 1) * block_size].copy_from_slice(&block);
123 }
124
125 let padding_len = *output.last().unwrap() as usize;
127 if padding_len == 0 || padding_len > block_size {
128 return Err(futu_core::error::FutuError::Encryption(
129 "invalid PKCS7 padding".into(),
130 ));
131 }
132 if output[output.len() - padding_len..]
133 .iter()
134 .any(|&b| b as usize != padding_len)
135 {
136 return Err(futu_core::error::FutuError::Encryption(
137 "invalid PKCS7 padding bytes".into(),
138 ));
139 }
140 output.truncate(output.len() - padding_len);
141
142 Ok(output)
143}
144
145fn aes_encrypt_block(key: &[u8; 16], block: &[u8]) -> [u8; 16] {
150 use aes::cipher::{BlockEncrypt, KeyInit};
151 let encryptor = aes::Aes128::new(key.into());
152 let mut out = [0u8; 16];
153 out.copy_from_slice(block);
154 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
155 encryptor.encrypt_block(ga);
156 out
157}
158
159fn aes_decrypt_block(key: &[u8; 16], block: &[u8]) -> [u8; 16] {
161 use aes::cipher::{BlockDecrypt, KeyInit};
162 let decryptor = aes::Aes128::new(key.into());
163 let mut out = [0u8; 16];
164 out.copy_from_slice(block);
165 let ga = aes::cipher::generic_array::GenericArray::from_mut_slice(&mut out);
166 decryptor.decrypt_block(ga);
167 out
168}
169
170pub fn aes_cbc_md5_encrypt(key: &[u8; 16], data: &[u8]) -> Vec<u8> {
176 let input_len = data.len();
177 let modv = input_len % 16;
178 let last_block_size = if modv == 0 { 0u8 } else { modv as u8 };
179
180 let data_blocks = if modv == 0 {
182 input_len
183 } else {
184 input_len - modv + 16
185 };
186 let total_size = data_blocks + 16 + 16; let mut output = vec![0u8; total_size];
188
189 let mut md5_ctx = md5::Context::new();
191
192 let mut encrypt_pos = 0usize;
193 let aligned_end = input_len - modv;
194
195 while encrypt_pos < aligned_end {
197 let block = &data[encrypt_pos..encrypt_pos + 16];
198 md5_ctx.consume(block);
199
200 if encrypt_pos == 0 {
201 let encrypted = aes_encrypt_block(key, block);
202 output[..16].copy_from_slice(&encrypted);
203 } else {
204 let mut xor_block = [0u8; 16];
205 for i in 0..16 {
206 xor_block[i] = output[encrypt_pos - 16 + i] ^ block[i];
207 }
208 let encrypted = aes_encrypt_block(key, &xor_block);
209 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&encrypted);
210 }
211 encrypt_pos += 16;
212 }
213
214 if modv != 0 {
216 let mut tmp = [0u8; 16];
217 tmp[..last_block_size as usize].copy_from_slice(&data[encrypt_pos..encrypt_pos + modv]);
218 md5_ctx.consume(tmp);
219
220 if encrypt_pos > 0 {
221 for i in 0..16 {
222 tmp[i] ^= output[encrypt_pos - 16 + i];
223 }
224 }
225 let encrypted = aes_encrypt_block(key, &tmp);
226 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&encrypted);
227 encrypt_pos += 16;
228 }
229
230 let mut padding = [0u8; 16];
232 padding[15] = last_block_size;
233 let encrypted_padding = aes_encrypt_block(key, &padding);
234 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&encrypted_padding);
235 encrypt_pos += 16;
236
237 let md5_hash = md5_ctx.compute();
239 output[encrypt_pos..encrypt_pos + 16].copy_from_slice(&md5_hash.0);
240
241 output
242}
243
244pub fn aes_cbc_md5_decrypt(
246 key: &[u8; 16],
247 data: &[u8],
248) -> std::result::Result<Vec<u8>, futu_core::error::FutuError> {
249 let input_len = data.len();
250 if input_len < 32 || !input_len.is_multiple_of(16) {
251 return Err(futu_core::error::FutuError::Encryption(format!(
252 "cbc_md5: invalid ciphertext length {input_len} (need >= 32 and multiple of 16)"
253 )));
254 }
255
256 let padding_block = aes_decrypt_block(key, &data[input_len - 32..input_len - 16]);
258 let last_block_size = padding_block[15] as usize;
259 if last_block_size > 15 {
260 return Err(futu_core::error::FutuError::Encryption(
261 "cbc_md5: invalid last_block_size".into(),
262 ));
263 }
264
265 let encrypt_len = input_len - 32; let mut output = vec![0u8; encrypt_len];
267 let mut md5_ctx = md5::Context::new();
268
269 let mut decrypt_pos = 0usize;
270 while encrypt_len >= 16 && decrypt_pos < encrypt_len - 16 {
271 let decrypted = aes_decrypt_block(key, &data[decrypt_pos..decrypt_pos + 16]);
272 if decrypt_pos > 0 {
273 for i in 0..16 {
274 output[decrypt_pos + i] = data[decrypt_pos - 16 + i] ^ decrypted[i];
275 }
276 } else {
277 output[..16].copy_from_slice(&decrypted);
278 }
279 md5_ctx.consume(&output[decrypt_pos..decrypt_pos + 16]);
280 decrypt_pos += 16;
281 }
282
283 if last_block_size != 0 {
285 let decrypted = aes_decrypt_block(key, &data[decrypt_pos..decrypt_pos + 16]);
286 let mut tmp = [0u8; 16];
287 if decrypt_pos > 0 {
288 for i in 0..16 {
289 tmp[i] = data[decrypt_pos - 16 + i] ^ decrypted[i];
290 }
291 } else {
292 tmp = decrypted;
293 }
294 output.truncate(decrypt_pos + last_block_size);
295 output[decrypt_pos..decrypt_pos + last_block_size].copy_from_slice(&tmp[..last_block_size]);
296 md5_ctx.consume(tmp);
297 } else {
298 let decrypted = aes_decrypt_block(key, &data[decrypt_pos..decrypt_pos + 16]);
299 if decrypt_pos > 0 {
300 for i in 0..16 {
301 output[decrypt_pos + i] = data[decrypt_pos - 16 + i] ^ decrypted[i];
302 }
303 } else {
304 output[decrypt_pos..decrypt_pos + 16].copy_from_slice(&decrypted);
305 }
306 md5_ctx.consume(&output[decrypt_pos..decrypt_pos + 16]);
307 }
308
309 let computed_md5 = md5_ctx.compute();
311 if computed_md5.0 != data[input_len - 16..] {
312 return Err(futu_core::error::FutuError::Encryption(
313 "cbc_md5: MD5 checksum mismatch".into(),
314 ));
315 }
316
317 Ok(output)
318}
319
320fn load_rsa_private_key(
327 pem_private_key: &str,
328) -> Result<rsa::RsaPrivateKey, futu_core::error::FutuError> {
329 use rsa::pkcs8::DecodePrivateKey;
330
331 rsa::RsaPrivateKey::from_pkcs8_pem(pem_private_key)
332 .or_else(|_| {
333 use rsa::pkcs1::DecodeRsaPrivateKey;
334 rsa::RsaPrivateKey::from_pkcs1_pem(pem_private_key)
335 })
336 .map_err(|e| {
337 futu_core::error::FutuError::Encryption(format!("invalid RSA private key: {e}"))
338 })
339}
340
341pub fn rsa_public_encrypt(
345 pem_private_key: &str,
346 data: &[u8],
347) -> Result<Vec<u8>, futu_core::error::FutuError> {
348 use rsa::Pkcs1v15Encrypt;
349
350 let private_key = load_rsa_private_key(pem_private_key)?;
351 let public_key = rsa::RsaPublicKey::from(&private_key);
352 let mut rng = rand::thread_rng();
353
354 public_key
355 .encrypt(&mut rng, Pkcs1v15Encrypt, data)
356 .map_err(|e| futu_core::error::FutuError::Encryption(format!("RSA encrypt failed: {e}")))
357}
358
359pub fn rsa_private_decrypt(
361 pem_private_key: &str,
362 data: &[u8],
363) -> Result<Vec<u8>, futu_core::error::FutuError> {
364 use rsa::Pkcs1v15Encrypt;
365
366 let private_key = load_rsa_private_key(pem_private_key)?;
367
368 private_key
369 .decrypt(Pkcs1v15Encrypt, data)
370 .map_err(|e| futu_core::error::FutuError::Encryption(format!("RSA decrypt failed: {e}")))
371}
372
373pub fn rsa_public_encrypt_blocks(
379 pem_private_key: &str,
380 data: &[u8],
381) -> Result<Vec<u8>, futu_core::error::FutuError> {
382 use rsa::traits::PublicKeyParts;
383 use rsa::Pkcs1v15Encrypt;
384
385 let private_key = load_rsa_private_key(pem_private_key)?;
386 let public_key = rsa::RsaPublicKey::from(&private_key);
387
388 let key_len = public_key.size();
390 let max_block = key_len - 11;
391
392 let mut result = Vec::with_capacity((data.len() / max_block + 1) * key_len);
393 let mut rng = rand::thread_rng();
394
395 for chunk in data.chunks(max_block) {
396 let encrypted = public_key
397 .encrypt(&mut rng, Pkcs1v15Encrypt, chunk)
398 .map_err(|e| {
399 futu_core::error::FutuError::Encryption(format!("RSA block encrypt failed: {e}"))
400 })?;
401 result.extend_from_slice(&encrypted);
402 }
403
404 Ok(result)
405}
406
407pub fn rsa_private_decrypt_blocks(
411 pem_private_key: &str,
412 data: &[u8],
413) -> Result<Vec<u8>, futu_core::error::FutuError> {
414 use rsa::traits::PublicKeyParts;
415 use rsa::Pkcs1v15Encrypt;
416
417 let private_key = load_rsa_private_key(pem_private_key)?;
418 let key_len = private_key.size();
419
420 if !data.len().is_multiple_of(key_len) {
421 return Err(futu_core::error::FutuError::Encryption(format!(
422 "RSA ciphertext length {} is not a multiple of key size {}",
423 data.len(),
424 key_len
425 )));
426 }
427
428 let mut result = Vec::with_capacity(data.len());
429
430 for chunk in data.chunks(key_len) {
431 let decrypted = private_key.decrypt(Pkcs1v15Encrypt, chunk).map_err(|e| {
432 futu_core::error::FutuError::Encryption(format!("RSA block decrypt failed: {e}"))
433 })?;
434 result.extend_from_slice(&decrypted);
435 }
436
437 Ok(result)
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_aes_ecb_roundtrip() {
446 let key = [
447 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
448 0x4f, 0x3c,
449 ];
450 let plaintext = b"hello futu world";
451
452 let ciphertext = aes_ecb_encrypt(&key, plaintext);
453 let decrypted = aes_ecb_decrypt(&key, &ciphertext).unwrap();
454
455 assert_eq!(decrypted, plaintext);
456 }
457
458 #[test]
459 fn test_aes_ecb_roundtrip_multi_block() {
460 let key = [0x01; 16];
461 let plaintext = b"this is a longer message that spans multiple AES blocks for testing";
462
463 let ciphertext = aes_ecb_encrypt(&key, plaintext);
464 let decrypted = aes_ecb_decrypt(&key, &ciphertext).unwrap();
465
466 assert_eq!(decrypted, plaintext.as_slice());
467 }
468
469 #[test]
470 fn test_aes_ecb_roundtrip_exact_block() {
471 let key = [0xAA; 16];
472 let plaintext = b"exactly16bytes!!";
474
475 let ciphertext = aes_ecb_encrypt(&key, plaintext);
476 assert_eq!(ciphertext.len(), 32); let decrypted = aes_ecb_decrypt(&key, &ciphertext).unwrap();
478
479 assert_eq!(decrypted, plaintext.as_slice());
480 }
481
482 #[test]
483 fn test_aes_ecb_empty_ciphertext() {
484 let key = [0x00; 16];
485 assert!(aes_ecb_decrypt(&key, &[]).is_err());
486 }
487
488 #[test]
489 fn test_aes_ecb_invalid_length() {
490 let key = [0x00; 16];
491 assert!(aes_ecb_decrypt(&key, &[0u8; 15]).is_err());
492 }
493
494 #[test]
495 fn test_nist_aes128_vector() {
496 let key: [u8; 16] = [
498 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf,
499 0x4f, 0x3c,
500 ];
501 let input: [u8; 16] = [
502 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37,
503 0x07, 0x34,
504 ];
505 let expected: [u8; 16] = [
506 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a,
507 0x0b, 0x32,
508 ];
509
510 let output = aes_encrypt_block(&key, &input);
511 assert_eq!(output, expected);
512
513 let decrypted = aes_decrypt_block(&key, &output);
514 assert_eq!(decrypted, input);
515 }
516
517 #[test]
518 fn test_rsa_roundtrip() {
519 use rsa::pkcs8::EncodePrivateKey;
520
521 let mut rng = rand::thread_rng();
523 let private_key =
524 rsa::RsaPrivateKey::new(&mut rng, 2048).expect("failed to generate RSA key");
525 let pem = private_key
526 .to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
527 .expect("failed to encode PEM");
528
529 let plaintext = b"hello futu RSA encryption test";
530
531 let encrypted = rsa_public_encrypt(pem.as_ref(), plaintext).unwrap();
532 assert_ne!(encrypted, plaintext);
533
534 let decrypted = rsa_private_decrypt(pem.as_ref(), &encrypted).unwrap();
535 assert_eq!(decrypted, plaintext);
536 }
537
538 #[test]
539 fn test_rsa_blocks_roundtrip_small() {
540 use rsa::pkcs8::EncodePrivateKey;
541
542 let mut rng = rand::thread_rng();
544 let private_key =
545 rsa::RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate RSA key");
546 let pem = private_key
547 .to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
548 .expect("failed to encode PEM");
549
550 let plaintext = b"small data";
551 let encrypted = rsa_public_encrypt_blocks(pem.as_ref(), plaintext).unwrap();
552 let decrypted = rsa_private_decrypt_blocks(pem.as_ref(), &encrypted).unwrap();
553 assert_eq!(decrypted, plaintext);
554 }
555
556 #[test]
557 fn test_rsa_blocks_roundtrip_large() {
558 use rsa::pkcs8::EncodePrivateKey;
559
560 let mut rng = rand::thread_rng();
562 let private_key =
563 rsa::RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate RSA key");
564 let pem = private_key
565 .to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
566 .expect("failed to encode PEM");
567
568 let plaintext: Vec<u8> = (0u8..=255).chain(0u8..44).collect();
570 assert_eq!(plaintext.len(), 300);
571
572 let encrypted = rsa_public_encrypt_blocks(pem.as_ref(), &plaintext).unwrap();
573 assert_eq!(encrypted.len(), 384);
575
576 let decrypted = rsa_private_decrypt_blocks(pem.as_ref(), &encrypted).unwrap();
577 assert_eq!(decrypted, plaintext);
578 }
579}