1use aes_gcm::{
2 aead::{consts::U12, Aead},
3 aes::Aes192,
4 Aes128Gcm, Aes256Gcm, AesGcm, KeyInit, Nonce,
5};
6use chacha20poly1305::ChaCha20Poly1305;
7
8use crate::error::ShadowsocksError;
9use crate::method::CipherMethod;
10
11pub const MAX_CHUNK_PAYLOAD: usize = 16 * 1024 - 1;
16
17const AEAD_TAG_SIZE: usize = 16;
19
20type Aes192Gcm = AesGcm<Aes192, U12>;
21
22pub(crate) enum AeadCipher {
23 Aes128(Aes128Gcm),
24 Aes192(Aes192Gcm),
25 Aes256(Aes256Gcm),
26 ChaCha20(ChaCha20Poly1305),
27}
28
29impl AeadCipher {
30 pub(crate) fn new(method: CipherMethod, key: &[u8]) -> Result<Self, ShadowsocksError> {
31 match method {
32 CipherMethod::Aes128Gcm => Aes128Gcm::new_from_slice(key)
33 .map(Self::Aes128)
34 .map_err(|e| ShadowsocksError::Other(e.to_string())),
35 CipherMethod::Aes192Gcm => Aes192Gcm::new_from_slice(key)
36 .map(Self::Aes192)
37 .map_err(|e| ShadowsocksError::Other(e.to_string())),
38 CipherMethod::Aes256Gcm => Aes256Gcm::new_from_slice(key)
39 .map(Self::Aes256)
40 .map_err(|e| ShadowsocksError::Other(e.to_string())),
41 CipherMethod::ChaCha20IetfPoly1305 => ChaCha20Poly1305::new_from_slice(key)
42 .map(Self::ChaCha20)
43 .map_err(|e| ShadowsocksError::Other(e.to_string())),
44 }
45 }
46
47 pub(crate) fn encrypt(
48 &self,
49 nonce: &[u8],
50 plaintext: &[u8],
51 ) -> Result<Vec<u8>, ShadowsocksError> {
52 let nonce = Nonce::from_slice(nonce);
53 match self {
54 Self::Aes128(cipher) => cipher
55 .encrypt(nonce, plaintext)
56 .map_err(|e| ShadowsocksError::EncryptionFailed(e.to_string())),
57 Self::Aes192(cipher) => cipher
58 .encrypt(nonce, plaintext)
59 .map_err(|e| ShadowsocksError::EncryptionFailed(e.to_string())),
60 Self::Aes256(cipher) => cipher
61 .encrypt(nonce, plaintext)
62 .map_err(|e| ShadowsocksError::EncryptionFailed(e.to_string())),
63 Self::ChaCha20(cipher) => cipher
64 .encrypt(nonce, plaintext)
65 .map_err(|e| ShadowsocksError::EncryptionFailed(e.to_string())),
66 }
67 }
68
69 pub(crate) fn decrypt(
70 &self,
71 nonce: &[u8],
72 ciphertext: &[u8],
73 ) -> Result<Vec<u8>, ShadowsocksError> {
74 let nonce = Nonce::from_slice(nonce);
75 match self {
76 Self::Aes128(cipher) => cipher
77 .decrypt(nonce, ciphertext)
78 .map_err(|e| ShadowsocksError::DecryptionFailed(e.to_string())),
79 Self::Aes192(cipher) => cipher
80 .decrypt(nonce, ciphertext)
81 .map_err(|e| ShadowsocksError::DecryptionFailed(e.to_string())),
82 Self::Aes256(cipher) => cipher
83 .decrypt(nonce, ciphertext)
84 .map_err(|e| ShadowsocksError::DecryptionFailed(e.to_string())),
85 Self::ChaCha20(cipher) => cipher
86 .decrypt(nonce, ciphertext)
87 .map_err(|e| ShadowsocksError::DecryptionFailed(e.to_string())),
88 }
89 }
90}
91
92pub fn encrypt_frame(
96 method: CipherMethod,
97 key: &[u8],
98 plaintext: &[u8],
99) -> Result<Vec<u8>, ShadowsocksError> {
100 use rand::RngCore;
101
102 let salt_size = method.salt_size();
103 let nonce_size = method.nonce_size();
104
105 let mut salt_buf = [0u8; 32];
107 rand::thread_rng().fill_bytes(&mut salt_buf[..salt_size]);
108 let salt = &salt_buf[..salt_size];
109
110 let subkey = method.derive_key(key, salt)?;
112
113 let nonce_bytes = [0u8; 12];
115
116 let ciphertext = aead_encrypt(method, &subkey, &nonce_bytes[..nonce_size], plaintext)?;
118
119 let mut output = Vec::with_capacity(salt_size + ciphertext.len());
121 output.extend_from_slice(salt);
122 output.extend_from_slice(&ciphertext);
123
124 Ok(output)
125}
126
127pub fn decrypt_frame(
132 method: CipherMethod,
133 key: &[u8],
134 data: &[u8],
135) -> Result<Vec<u8>, ShadowsocksError> {
136 let salt_size = method.salt_size();
137 let nonce_size = method.nonce_size();
138
139 if data.len() < salt_size {
140 return Err(ShadowsocksError::DecryptionFailed(
141 "data too short for salt".into(),
142 ));
143 }
144
145 let salt = &data[..salt_size];
147 let ciphertext = &data[salt_size..];
148
149 let subkey = method.derive_key(key, salt)?;
151
152 let nonce_bytes = [0u8; 12];
154
155 aead_decrypt(method, &subkey, &nonce_bytes[..nonce_size], ciphertext)
157}
158
159pub fn encrypt_chunk(
164 method: CipherMethod,
165 key: &[u8],
166 nonce: &[u8],
167 plaintext: &[u8],
168) -> Result<Vec<u8>, ShadowsocksError> {
169 if plaintext.len() > MAX_CHUNK_PAYLOAD {
170 return Err(ShadowsocksError::Other(format!(
171 "plaintext too large for AEAD chunk: {} bytes (max {})",
172 plaintext.len(),
173 MAX_CHUNK_PAYLOAD
174 )));
175 }
176 let len = plaintext.len() as u16;
178 let mut payload = Vec::with_capacity(2 + plaintext.len());
179 payload.extend_from_slice(&len.to_be_bytes());
180 payload.extend_from_slice(plaintext);
181
182 aead_encrypt(method, key, nonce, &payload)
183}
184
185pub fn decrypt_chunk(
190 method: CipherMethod,
191 key: &[u8],
192 nonce: &[u8],
193 data: &[u8],
194) -> Result<Vec<u8>, ShadowsocksError> {
195 let plaintext = aead_decrypt(method, key, nonce, data)?;
196
197 if plaintext.len() < 2 {
198 return Err(ShadowsocksError::DecryptionFailed("chunk too short".into()));
199 }
200
201 let len = u16::from_be_bytes([plaintext[0], plaintext[1]]) as usize;
202 if plaintext.len() < 2 + len {
203 return Err(ShadowsocksError::DecryptionFailed(
204 "chunk length mismatch".into(),
205 ));
206 }
207
208 Ok(plaintext[2..2 + len].to_vec())
209}
210
211pub fn encrypt_chunk_standard(
216 method: CipherMethod,
217 key: &[u8],
218 nonce: &[u8],
219 payload: &[u8],
220) -> Result<Vec<u8>, ShadowsocksError> {
221 if payload.len() > MAX_CHUNK_PAYLOAD {
222 return Err(ShadowsocksError::Other(format!(
223 "payload too large for standard AEAD chunk: {} bytes (max {})",
224 payload.len(),
225 MAX_CHUNK_PAYLOAD,
226 )));
227 }
228 encrypt_chunk_with(&AeadCipher::new(method, key)?, nonce, payload)
229}
230
231fn encrypt_chunk_with(
234 cipher: &AeadCipher,
235 nonce: &[u8],
236 payload: &[u8],
237) -> Result<Vec<u8>, ShadowsocksError> {
238 let len_bytes = (payload.len() as u16).to_be_bytes();
239
240 let len_ct = cipher.encrypt(nonce, &len_bytes)?;
242
243 let mut payload_nonce = [0u8; 12];
245 nonce_increment(nonce, &mut payload_nonce)?;
246
247 let payload_ct = cipher.encrypt(&payload_nonce, payload)?;
249
250 let mut output = Vec::with_capacity(len_ct.len() + payload_ct.len());
251 output.extend_from_slice(&len_ct);
252 output.extend_from_slice(&payload_ct);
253 Ok(output)
254}
255
256pub fn decrypt_chunk_standard(
261 method: CipherMethod,
262 key: &[u8],
263 nonce: &[u8],
264 data: &[u8],
265) -> Result<Vec<u8>, ShadowsocksError> {
266 decrypt_chunk_with(&AeadCipher::new(method, key)?, nonce, data)
267}
268
269fn decrypt_chunk_with(
272 cipher: &AeadCipher,
273 nonce: &[u8],
274 data: &[u8],
275) -> Result<Vec<u8>, ShadowsocksError> {
276 let len_block_size = 2 + AEAD_TAG_SIZE;
277
278 if data.len() < len_block_size {
279 return Err(ShadowsocksError::DecryptionFailed(
280 "data too short for length block".into(),
281 ));
282 }
283
284 let len_plaintext = cipher.decrypt(nonce, &data[..len_block_size])?;
286 if len_plaintext.len() != 2 {
287 return Err(ShadowsocksError::DecryptionFailed(
288 "length block plaintext invalid".into(),
289 ));
290 }
291
292 let payload_len = u16::from_be_bytes([len_plaintext[0], len_plaintext[1]]) as usize;
293 if payload_len > MAX_CHUNK_PAYLOAD {
294 return Err(ShadowsocksError::DecryptionFailed(format!(
295 "payload length {} exceeds maximum {}",
296 payload_len, MAX_CHUNK_PAYLOAD
297 )));
298 }
299 let expected_total = len_block_size + payload_len + AEAD_TAG_SIZE;
300
301 if data.len() < expected_total {
302 return Err(ShadowsocksError::DecryptionFailed(format!(
303 "insufficient data: expected {} bytes, got {}",
304 expected_total,
305 data.len(),
306 )));
307 }
308
309 let mut payload_nonce = [0u8; 12];
311 nonce_increment(nonce, &mut payload_nonce)?;
312
313 let payload_start = len_block_size;
315 let payload_end = payload_start + payload_len + AEAD_TAG_SIZE;
316 cipher.decrypt(&payload_nonce, &data[payload_start..payload_end])
317}
318
319pub(crate) fn encrypt_standard_chunks(
321 method: CipherMethod,
322 key: &[u8],
323 nonce: &[u8],
324 plaintext: &[u8],
325) -> Result<Vec<u8>, ShadowsocksError> {
326 let cipher = AeadCipher::new(method, key)?;
327 let chunk_count = plaintext.len().div_ceil(MAX_CHUNK_PAYLOAD);
328 let mut output = Vec::with_capacity(plaintext.len() + chunk_count * (2 + 2 * AEAD_TAG_SIZE));
329 let mut current_nonce = [0u8; 12];
330 current_nonce.copy_from_slice(nonce);
331 for chunk in plaintext.chunks(MAX_CHUNK_PAYLOAD) {
332 output.extend_from_slice(&encrypt_chunk_with(&cipher, ¤t_nonce, chunk)?);
333 let mut length_nonce = [0u8; 12];
334 nonce_increment(¤t_nonce, &mut length_nonce)?;
335 nonce_increment(&length_nonce, &mut current_nonce)?;
336 }
337 Ok(output)
338}
339
340pub(crate) fn decrypt_standard_chunks(
342 method: CipherMethod,
343 key: &[u8],
344 nonce: &[u8],
345 data: &[u8],
346) -> Result<Vec<u8>, ShadowsocksError> {
347 let cipher = AeadCipher::new(method, key)?;
348 let mut current_nonce = [0u8; 12];
349 current_nonce.copy_from_slice(nonce);
350 let mut offset = 0;
351 let mut plaintext = Vec::with_capacity(data.len());
352
353 while offset < data.len() {
354 if data.len() - offset < 2 + AEAD_TAG_SIZE {
355 return Err(ShadowsocksError::DecryptionFailed(
356 "data too short for pproxy length block".into(),
357 ));
358 }
359 let length_plaintext =
360 cipher.decrypt(¤t_nonce, &data[offset..offset + 2 + AEAD_TAG_SIZE])?;
361 let payload_len = u16::from_be_bytes([length_plaintext[0], length_plaintext[1]]) as usize;
362 if payload_len > MAX_CHUNK_PAYLOAD {
363 return Err(ShadowsocksError::DecryptionFailed(format!(
364 "payload length {} exceeds maximum {}",
365 payload_len, MAX_CHUNK_PAYLOAD
366 )));
367 }
368 offset += 2 + AEAD_TAG_SIZE;
369 let payload_wire_len = payload_len + AEAD_TAG_SIZE;
370 if data.len() - offset < payload_wire_len {
371 return Err(ShadowsocksError::DecryptionFailed(
372 "data too short for pproxy payload block".into(),
373 ));
374 }
375 let mut length_nonce = [0u8; 12];
376 nonce_increment(¤t_nonce, &mut length_nonce)?;
377 let payload = cipher.decrypt(&length_nonce, &data[offset..offset + payload_wire_len])?;
378 plaintext.extend_from_slice(&payload);
379 offset += payload_wire_len;
380 nonce_increment(&length_nonce, &mut current_nonce)?;
381 }
382
383 Ok(plaintext)
384}
385
386fn nonce_increment(nonce: &[u8], result: &mut [u8]) -> Result<(), ShadowsocksError> {
388 if nonce.len() != result.len() {
389 return Err(ShadowsocksError::Other("nonce size mismatch".into()));
390 }
391 result.copy_from_slice(nonce);
392 for byte in result.iter_mut() {
393 let (val, carry) = byte.overflowing_add(1);
394 *byte = val;
395 if !carry {
396 return Ok(());
397 }
398 }
399 Err(ShadowsocksError::Other("nonce increment overflow".into()))
400}
401
402pub fn aead_encrypt_raw(
404 method: CipherMethod,
405 key: &[u8],
406 nonce: &[u8],
407 plaintext: &[u8],
408) -> Result<Vec<u8>, ShadowsocksError> {
409 AeadCipher::new(method, key)?.encrypt(nonce, plaintext)
410}
411
412pub fn aead_decrypt_raw(
414 method: CipherMethod,
415 key: &[u8],
416 nonce: &[u8],
417 ciphertext: &[u8],
418) -> Result<Vec<u8>, ShadowsocksError> {
419 AeadCipher::new(method, key)?.decrypt(nonce, ciphertext)
420}
421
422fn aead_encrypt(
424 method: CipherMethod,
425 key: &[u8],
426 nonce: &[u8],
427 plaintext: &[u8],
428) -> Result<Vec<u8>, ShadowsocksError> {
429 aead_encrypt_raw(method, key, nonce, plaintext)
430}
431
432fn aead_decrypt(
434 method: CipherMethod,
435 key: &[u8],
436 nonce: &[u8],
437 ciphertext: &[u8],
438) -> Result<Vec<u8>, ShadowsocksError> {
439 aead_decrypt_raw(method, key, nonce, ciphertext)
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn test_encrypt_decrypt_roundtrip_aes128() {
448 let key = b"0123456789abcdef";
449 let plaintext = b"hello shadowsocks";
450 let encrypted = encrypt_frame(CipherMethod::Aes128Gcm, key, plaintext).unwrap();
451 let decrypted = decrypt_frame(CipherMethod::Aes128Gcm, key, &encrypted).unwrap();
452 assert_eq!(decrypted, plaintext);
453 }
454
455 #[test]
456 fn test_encrypt_decrypt_roundtrip_aes256() {
457 let key = b"0123456789abcdef0123456789abcdef";
458 let plaintext = b"hello shadowsocks";
459 let encrypted = encrypt_frame(CipherMethod::Aes256Gcm, key, plaintext).unwrap();
460 let decrypted = decrypt_frame(CipherMethod::Aes256Gcm, key, &encrypted).unwrap();
461 assert_eq!(decrypted, plaintext);
462 }
463
464 #[test]
465 fn test_encrypt_decrypt_roundtrip_aes192() {
466 let key = b"0123456789abcdef01234567";
467 let plaintext = b"hello shadowsocks";
468 let encrypted = encrypt_frame(CipherMethod::Aes192Gcm, key, plaintext).unwrap();
469 let decrypted = decrypt_frame(CipherMethod::Aes192Gcm, key, &encrypted).unwrap();
470 assert_eq!(decrypted, plaintext);
471 }
472
473 #[test]
474 fn test_encrypt_decrypt_roundtrip_chacha20() {
475 let key = b"0123456789abcdef0123456789abcdef";
476 let plaintext = b"hello shadowsocks";
477 let encrypted = encrypt_frame(CipherMethod::ChaCha20IetfPoly1305, key, plaintext).unwrap();
478 let decrypted = decrypt_frame(CipherMethod::ChaCha20IetfPoly1305, key, &encrypted).unwrap();
479 assert_eq!(decrypted, plaintext);
480 }
481
482 #[test]
483 fn test_tampered_ciphertext_fails() {
484 let key = b"0123456789abcdef";
485 let plaintext = b"hello shadowsocks";
486 let mut encrypted = encrypt_frame(CipherMethod::Aes128Gcm, key, plaintext).unwrap();
487 let last = encrypted.len() - 1;
489 encrypted[last] ^= 0xFF;
490 assert!(decrypt_frame(CipherMethod::Aes128Gcm, key, &encrypted).is_err());
491 }
492
493 #[test]
494 fn test_wrong_key_fails() {
495 let key1 = b"0123456789abcdef";
496 let key2 = b"fedcba9876543210";
497 let plaintext = b"hello shadowsocks";
498 let encrypted = encrypt_frame(CipherMethod::Aes128Gcm, key1, plaintext).unwrap();
499 assert!(decrypt_frame(CipherMethod::Aes128Gcm, key2, &encrypted).is_err());
500 }
501
502 #[test]
503 fn test_encrypt_decrypt_chunk_roundtrip() {
504 let key = b"0123456789abcdef";
505 let nonce = [0u8; 12];
506 let plaintext = b"chunk data";
507 let encrypted = encrypt_chunk(CipherMethod::Aes128Gcm, key, &nonce, plaintext).unwrap();
508 let decrypted = decrypt_chunk(CipherMethod::Aes128Gcm, key, &nonce, &encrypted).unwrap();
509 assert_eq!(decrypted, plaintext);
510 }
511
512 #[test]
513 fn test_empty_plaintext() {
514 let key = b"0123456789abcdef";
515 let encrypted = encrypt_frame(CipherMethod::Aes128Gcm, key, b"").unwrap();
516 let decrypted = decrypt_frame(CipherMethod::Aes128Gcm, key, &encrypted).unwrap();
517 assert!(decrypted.is_empty());
518 }
519
520 #[test]
521 fn test_large_plaintext() {
522 let key = b"0123456789abcdef";
523 let plaintext = vec![0xABu8; 65536];
524 let encrypted = encrypt_frame(CipherMethod::Aes128Gcm, key, &plaintext).unwrap();
525 let decrypted = decrypt_frame(CipherMethod::Aes128Gcm, key, &encrypted).unwrap();
526 assert_eq!(decrypted, plaintext);
527 }
528
529 #[test]
530 fn test_different_nonces_produce_different_ciphertext() {
531 let key = b"0123456789abcdef";
532 let plaintext = b"same data";
533 let enc1 = encrypt_frame(CipherMethod::Aes128Gcm, key, plaintext).unwrap();
534 let enc2 = encrypt_frame(CipherMethod::Aes128Gcm, key, plaintext).unwrap();
535 assert_ne!(enc1, enc2);
537 }
538
539 #[test]
540 fn test_encrypt_decrypt_chunk_standard_roundtrip_aes128() {
541 let key = b"0123456789abcdef";
542 let nonce = vec![0u8; 12];
543 let payload = b"hello shadowsocks standard";
544 let wire = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload).unwrap();
545 assert_eq!(wire.len(), 18 + payload.len() + 16);
547 let decrypted =
548 decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire).unwrap();
549 assert_eq!(decrypted, payload);
550 }
551
552 #[test]
553 fn test_encrypt_decrypt_chunk_standard_roundtrip_aes256() {
554 let key = b"0123456789abcdef0123456789abcdef";
555 let nonce = vec![0u8; 12];
556 let payload = b"standard chunk test";
557 let wire = encrypt_chunk_standard(CipherMethod::Aes256Gcm, key, &nonce, payload).unwrap();
558 let decrypted =
559 decrypt_chunk_standard(CipherMethod::Aes256Gcm, key, &nonce, &wire).unwrap();
560 assert_eq!(decrypted, payload);
561 }
562
563 #[test]
564 fn test_encrypt_decrypt_chunk_standard_roundtrip_aes192() {
565 let key = b"0123456789abcdef01234567";
566 let nonce = vec![0u8; 12];
567 let payload = b"aes-192 standard chunk test";
568 let wire = encrypt_chunk_standard(CipherMethod::Aes192Gcm, key, &nonce, payload).unwrap();
569 let decrypted =
570 decrypt_chunk_standard(CipherMethod::Aes192Gcm, key, &nonce, &wire).unwrap();
571 assert_eq!(decrypted, payload);
572 }
573
574 #[test]
575 fn test_pproxy_known_answer_vectors() {
576 let password = b"phase1-vector-password";
579 let plaintext = b"phase-1-known-answer";
580 let cases = [
581 (
582 CipherMethod::Aes128Gcm,
583 "000102030405060708090a0b0c0d0e0f",
584 "9c9ad70264cd061c56f1e3492fbf1528",
585 "a8e3c5be9630d97db3fdc8024c606d78aba455a8d0d5c7d7c5148cc4e076ee7fc2367b6f",
586 ),
587 (
588 CipherMethod::Aes192Gcm,
589 "000102030405060708090a0b0c0d0e0f1011121314151617",
590 "7b56c2ce83b11c5ca9d5401b08d0cea7bcc15428500352d6",
591 "e4345ba47ae93b62f6a6450a55e96f01803c46b46500f30d290566a1617c5b97f16995d5",
592 ),
593 (
594 CipherMethod::Aes256Gcm,
595 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
596 "9e3a9a86f6293e1d6ddb2f4285a818bb9ebb9a9fe08498735ba4967425f88fc9",
597 "506e8c5971d7e254b42b6fcc6c7919c11baf60bd7406966bfaf1838bbdcd1d4ade826c22",
598 ),
599 (
600 CipherMethod::ChaCha20IetfPoly1305,
601 "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
602 "9e3a9a86f6293e1d6ddb2f4285a818bb9ebb9a9fe08498735ba4967425f88fc9",
603 "cdaa76308bb8e130e2f4d351494ca1efca87098c744a0c2b8afe27f77f1999dbd13bee5e",
604 ),
605 ];
606
607 for (method, salt_hex, subkey_hex, ciphertext_hex) in cases {
608 let salt = hex_bytes(salt_hex);
609 let expected_subkey = hex_bytes(subkey_hex);
610 let expected_ciphertext = hex_bytes(ciphertext_hex);
611 let subkey = method.derive_key(password, &salt).unwrap();
612 assert_eq!(subkey, expected_subkey, "subkey mismatch for {method}");
613 let ciphertext = aead_encrypt_raw(method, &subkey, &[0u8; 12], plaintext).unwrap();
614 assert_eq!(
615 ciphertext, expected_ciphertext,
616 "ciphertext mismatch for {method}"
617 );
618 assert_eq!(
619 aead_decrypt_raw(method, &subkey, &[0u8; 12], &ciphertext).unwrap(),
620 plaintext
621 );
622 }
623 }
624
625 fn hex_bytes(value: &str) -> Vec<u8> {
626 value
627 .as_bytes()
628 .chunks_exact(2)
629 .map(|pair| u8::from_str_radix(std::str::from_utf8(pair).unwrap(), 16).unwrap())
630 .collect()
631 }
632
633 #[test]
634 fn test_encrypt_decrypt_chunk_standard_roundtrip_chacha20() {
635 let key = b"0123456789abcdef0123456789abcdef";
636 let nonce = vec![0u8; 12];
637 let payload = b"chacha standard chunk";
638 let wire = encrypt_chunk_standard(CipherMethod::ChaCha20IetfPoly1305, key, &nonce, payload)
639 .unwrap();
640 let decrypted =
641 decrypt_chunk_standard(CipherMethod::ChaCha20IetfPoly1305, key, &nonce, &wire).unwrap();
642 assert_eq!(decrypted, payload);
643 }
644
645 #[test]
646 fn test_encrypt_decrypt_chunk_standard_empty_payload() {
647 let key = b"0123456789abcdef";
648 let nonce = vec![0u8; 12];
649 let payload = b"";
650 let wire = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload).unwrap();
651 assert_eq!(wire.len(), 34);
653 let decrypted =
654 decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire).unwrap();
655 assert_eq!(decrypted, payload);
656 }
657
658 #[test]
659 fn test_encrypt_decrypt_chunk_standard_max_payload() {
660 let key = b"0123456789abcdef";
661 let nonce = vec![0u8; 12];
662 let payload = vec![0xABu8; MAX_CHUNK_PAYLOAD];
663 let wire = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &payload).unwrap();
664 assert_eq!(wire.len(), 18 + MAX_CHUNK_PAYLOAD + 16);
665 let decrypted =
666 decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire).unwrap();
667 assert_eq!(decrypted, payload);
668 }
669
670 #[test]
671 fn test_encrypt_chunk_standard_payload_too_large() {
672 let key = b"0123456789abcdef";
673 let nonce = vec![0u8; 12];
674 let payload = vec![0xABu8; MAX_CHUNK_PAYLOAD + 1];
675 let result = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &payload);
676 assert!(result.is_err());
677 }
678
679 #[test]
680 fn test_decrypt_chunk_standard_tampered_length_block() {
681 let key = b"0123456789abcdef";
682 let nonce = vec![0u8; 12];
683 let payload = b"secret data";
684 let mut wire =
685 encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload).unwrap();
686 wire[0] ^= 0xFF;
688 let result = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire);
689 assert!(result.is_err());
690 }
691
692 #[test]
693 fn test_decrypt_chunk_standard_tampered_payload_block() {
694 let key = b"0123456789abcdef";
695 let nonce = vec![0u8; 12];
696 let payload = b"secret data";
697 let mut wire =
698 encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload).unwrap();
699 wire[18] ^= 0xFF;
701 let result = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire);
702 assert!(result.is_err());
703 }
704
705 #[test]
706 fn test_decrypt_chunk_standard_too_short() {
707 let key = b"0123456789abcdef";
708 let nonce = vec![0u8; 12];
709 let data = vec![0u8; 10]; let result = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &data);
711 assert!(result.is_err());
712 }
713
714 #[test]
715 fn test_decrypt_chunk_standard_wrong_key() {
716 let key1 = b"0123456789abcdef";
717 let key2 = b"fedcba9876543210";
718 let nonce = vec![0u8; 12];
719 let payload = b"secret data";
720 let wire = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key1, &nonce, payload).unwrap();
721 let result = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key2, &nonce, &wire);
722 assert!(result.is_err());
723 }
724
725 #[test]
726 fn test_decrypt_chunk_standard_wrong_nonce() {
727 let key = b"0123456789abcdef";
728 let nonce1 = vec![0u8; 12];
729 let mut nonce2 = vec![0u8; 12];
730 nonce2[0] = 1; let payload = b"secret data";
732 let wire = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce1, payload).unwrap();
733 let result = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce2, &wire);
734 assert!(result.is_err());
735 }
736
737 #[test]
738 fn test_encrypt_decrypt_chunk_standard_sequential_nonces() {
739 let key = b"0123456789abcdef";
740 let mut nonce = vec![0u8; 12];
741 nonce[0] = 1; let payload1 = b"first chunk";
744 let wire1 = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload1).unwrap();
745 let dec1 = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire1).unwrap();
746 assert_eq!(dec1, payload1);
747
748 nonce[0] = 3;
750 let payload2 = b"second chunk";
751 let wire2 = encrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, payload2).unwrap();
752 let dec2 = decrypt_chunk_standard(CipherMethod::Aes128Gcm, key, &nonce, &wire2).unwrap();
753 assert_eq!(dec2, payload2);
754 }
755
756 #[test]
757 fn test_nonce_increment_basic() {
758 let nonce = vec![0u8; 12];
759 let mut result = vec![0u8; nonce.len()];
760 nonce_increment(&nonce, &mut result).unwrap();
761 assert_eq!(result[0], 1);
762 }
763
764 #[test]
765 fn test_nonce_increment_carry() {
766 let mut nonce = vec![0u8; 12];
767 nonce[0] = 0xFF;
768 let mut result = vec![0u8; nonce.len()];
769 nonce_increment(&nonce, &mut result).unwrap();
770 assert_eq!(result[0], 0);
771 assert_eq!(result[1], 1);
772 }
773
774 #[test]
775 fn test_nonce_increment_overflow() {
776 let nonce = vec![0xFFu8; 12];
777 let mut result = vec![0u8; nonce.len()];
778 let result = nonce_increment(&nonce, &mut result);
779 assert!(result.is_err());
780 }
781}