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