shadow_crypt_core/v1/
crypt.rs1use chacha20poly1305::{KeyInit, XChaCha20Poly1305, aead::Aead};
2
3use crate::{algorithm::Algorithm, errors::CryptError, memory::SecureBytes};
4
5pub fn encrypt_bytes(
6 plaintext: &[u8],
7 key: &[u8; 32],
8 nonce: &[u8; 24],
9) -> Result<(Vec<u8>, Algorithm), CryptError> {
10 let cipher = XChaCha20Poly1305::new(key.into());
11 let ciphertext = cipher
12 .encrypt(nonce.into(), plaintext)
13 .map_err(|e| CryptError::EncryptionError(format!("Encryption failed: {}", e)))?;
14
15 Ok((ciphertext, Algorithm::XChaCha20Poly1305))
16}
17
18pub fn decrypt_bytes(
20 ciphertext: &[u8],
21 key: &[u8; 32],
22 nonce: &[u8; 24],
23) -> Result<(SecureBytes, Algorithm), CryptError> {
24 let cipher = XChaCha20Poly1305::new(key.into());
25 let plaintext = cipher
26 .decrypt(nonce.into(), ciphertext)
27 .map_err(|_| {
30 CryptError::DecryptionError(
31 "authentication failed (wrong password, or the file is corrupted)".to_string(),
32 )
33 })?;
34
35 Ok((SecureBytes::new(plaintext), Algorithm::XChaCha20Poly1305))
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn test_encrypt_decrypt_round_trip() {
44 let plaintext = b"Hello, world!";
45 let key = [0u8; 32];
46 let nonce = [0u8; 24];
47
48 let (ciphertext, algorithm) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
49 assert_eq!(algorithm, Algorithm::XChaCha20Poly1305);
50 assert_ne!(ciphertext, plaintext);
51
52 let (decrypted, algorithm) = decrypt_bytes(&ciphertext, &key, &nonce).unwrap();
53 assert_eq!(algorithm, Algorithm::XChaCha20Poly1305);
54 assert_eq!(decrypted.as_slice(), plaintext);
55 }
56
57 #[test]
58 fn test_encrypt_different_nonce_produces_different_ciphertext() {
59 let plaintext = b"Test message";
60 let key = [1u8; 32];
61 let nonce1 = [0u8; 24];
62 let nonce2 = [1u8; 24];
63
64 let (ciphertext1, _) = encrypt_bytes(plaintext, &key, &nonce1).unwrap();
65 let (ciphertext2, _) = encrypt_bytes(plaintext, &key, &nonce2).unwrap();
66
67 assert_ne!(ciphertext1, ciphertext2);
68 }
69
70 #[test]
71 fn test_encrypt_same_inputs_produce_same_output() {
72 let plaintext = b"Deterministic test";
73 let key = [2u8; 32];
74 let nonce = [2u8; 24];
75
76 let (ciphertext1, _) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
77 let (ciphertext2, _) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
78
79 assert_eq!(ciphertext1, ciphertext2);
80 }
81
82 #[test]
83 fn test_decrypt_with_wrong_key_fails() {
84 let plaintext = b"Secret message";
85 let key = [3u8; 32];
86 let wrong_key = [4u8; 32];
87 let nonce = [3u8; 24];
88
89 let (ciphertext, _) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
90 let result = decrypt_bytes(&ciphertext, &wrong_key, &nonce);
91
92 assert!(result.is_err());
93 }
94
95 #[test]
96 fn test_decrypt_with_wrong_nonce_fails() {
97 let plaintext = b"Secret message";
98 let key = [5u8; 32];
99 let nonce = [5u8; 24];
100 let wrong_nonce = [6u8; 24];
101
102 let (ciphertext, _) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
103 let result = decrypt_bytes(&ciphertext, &key, &wrong_nonce);
104
105 assert!(result.is_err());
106 }
107
108 #[test]
109 fn test_encrypt_empty_plaintext() {
110 let plaintext = b"";
111 let key = [7u8; 32];
112 let nonce = [7u8; 24];
113
114 let (ciphertext, algorithm) = encrypt_bytes(plaintext, &key, &nonce).unwrap();
115 assert_eq!(algorithm, Algorithm::XChaCha20Poly1305);
116
117 let (decrypted, algorithm) = decrypt_bytes(&ciphertext, &key, &nonce).unwrap();
118 assert_eq!(algorithm, Algorithm::XChaCha20Poly1305);
119 assert_eq!(decrypted.as_slice(), plaintext);
120 }
121}