shadow_crypt_core/v2/
mod.rs1use crate::algorithm::Algorithm;
23
24pub mod crypt;
26
27pub mod file;
29
30pub mod header;
32
33pub mod key;
35
36pub const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305;
38
39#[cfg(test)]
40mod tests {
41 use crate::{
42 file::PlaintextFile,
43 memory::{SecureBytes, SecureString},
44 v2::{
45 crypt::{decrypt_bytes, encrypt_bytes},
46 file::EncryptedFile,
47 header::{AadPurpose, HeaderBinding},
48 key::KeyDerivationParams,
49 },
50 };
51
52 #[test]
58 fn swapped_filename_and_content_ciphertexts_fail_authentication() {
59 let key = [9u8; 32];
60 let salt = [1u8; 16];
61 let params = KeyDerivationParams::test_defaults();
62 let content_nonce = [2u8; 24];
63 let filename_nonce = [3u8; 24];
64 let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
65
66 let (filename_ct, _) = encrypt_bytes(
67 b"secret-name.txt",
68 &key,
69 &filename_nonce,
70 &binding.aad(AadPurpose::Filename),
71 )
72 .unwrap();
73 let (content_ct, _) = encrypt_bytes(
74 b"file content",
75 &key,
76 &content_nonce,
77 &binding.aad(AadPurpose::Content),
78 )
79 .unwrap();
80
81 let swapped_content = decrypt_bytes(
85 &filename_ct,
86 &key,
87 &filename_nonce,
88 &binding.aad(AadPurpose::Content),
89 );
90 let swapped_filename = decrypt_bytes(
91 &content_ct,
92 &key,
93 &content_nonce,
94 &binding.aad(AadPurpose::Filename),
95 );
96
97 assert!(swapped_content.is_err());
98 assert!(swapped_filename.is_err());
99 }
100
101 #[test]
103 fn tampered_header_fields_fail_authentication() {
104 let key = [9u8; 32];
105 let salt = [1u8; 16];
106 let params = KeyDerivationParams::test_defaults();
107 let content_nonce = [2u8; 24];
108 let filename_nonce = [3u8; 24];
109 let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
110
111 let (content_ct, _) = encrypt_bytes(
112 b"file content",
113 &key,
114 &content_nonce,
115 &binding.aad(AadPurpose::Content),
116 )
117 .unwrap();
118
119 let weak_params = KeyDerivationParams::new(8, 1, 1, 32);
121 let tampered = HeaderBinding::new(&salt, &weak_params, &content_nonce, &filename_nonce);
122 assert!(
123 decrypt_bytes(
124 &content_ct,
125 &key,
126 &content_nonce,
127 &tampered.aad(AadPurpose::Content),
128 )
129 .is_err()
130 );
131
132 let other_salt = [7u8; 16];
134 let tampered = HeaderBinding::new(&other_salt, ¶ms, &content_nonce, &filename_nonce);
135 assert!(
136 decrypt_bytes(
137 &content_ct,
138 &key,
139 &content_nonce,
140 &tampered.aad(AadPurpose::Content),
141 )
142 .is_err()
143 );
144 }
145
146 #[test]
149 fn seal_decrypt_round_trip_via_bytes() {
150 let salt = [1u8; 16];
151 let params = KeyDerivationParams::test_defaults();
152 let (key, _) = params.derive_key(b"password", &salt).unwrap();
153
154 let plaintext_file = PlaintextFile::new(
155 SecureString::new("name.txt".to_string()),
156 SecureBytes::new(b"hello".to_vec()),
157 );
158
159 let sealed =
160 EncryptedFile::seal(&plaintext_file, &key, params, salt, [2u8; 24], [3u8; 24]).unwrap();
161
162 let parsed = EncryptedFile::from_bytes(&sealed.to_bytes()).unwrap();
163 let decrypted = parsed.decrypt(&key).unwrap();
164
165 assert_eq!(decrypted.filename().as_str(), "name.txt");
166 assert_eq!(decrypted.content().as_slice(), b"hello");
167 }
168
169 #[test]
171 fn seal_decrypt_with_wrong_key_fails() {
172 let salt = [1u8; 16];
173 let params = KeyDerivationParams::test_defaults();
174 let (key, _) = params.derive_key(b"password", &salt).unwrap();
175 let (wrong_key, _) = params.derive_key(b"wrong", &salt).unwrap();
176
177 let plaintext_file = PlaintextFile::new(
178 SecureString::new("name.txt".to_string()),
179 SecureBytes::new(b"hello".to_vec()),
180 );
181
182 let sealed =
183 EncryptedFile::seal(&plaintext_file, &key, params, salt, [2u8; 24], [3u8; 24]).unwrap();
184
185 assert!(sealed.decrypt(&wrong_key).is_err());
186 }
187}