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