shadow_crypt_core/v2/
mod.rs1pub mod crypt;
17
18pub mod file;
20
21pub mod file_ops;
23
24pub mod header;
26
27pub mod header_ops;
29
30pub mod key;
32
33pub mod key_ops;
35
36#[cfg(test)]
37mod tests {
38 use crate::v2::{
39 crypt::{decrypt_bytes, encrypt_bytes},
40 header::{AadPurpose, FileHeader, HeaderBinding},
41 key::KeyDerivationParams,
42 };
43
44 #[test]
50 fn swapped_filename_and_content_ciphertexts_fail_authentication() {
51 let key = [9u8; 32];
52 let salt = [1u8; 16];
53 let params = KeyDerivationParams::test_defaults();
54 let content_nonce = [2u8; 24];
55 let filename_nonce = [3u8; 24];
56 let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
57
58 let (filename_ct, _) = encrypt_bytes(
59 b"secret-name.txt",
60 &key,
61 &filename_nonce,
62 &binding.aad(AadPurpose::Filename),
63 )
64 .unwrap();
65 let (content_ct, _) = encrypt_bytes(
66 b"file content",
67 &key,
68 &content_nonce,
69 &binding.aad(AadPurpose::Content),
70 )
71 .unwrap();
72
73 let swapped_content = decrypt_bytes(
77 &filename_ct,
78 &key,
79 &filename_nonce,
80 &binding.aad(AadPurpose::Content),
81 );
82 let swapped_filename = decrypt_bytes(
83 &content_ct,
84 &key,
85 &content_nonce,
86 &binding.aad(AadPurpose::Filename),
87 );
88
89 assert!(swapped_content.is_err());
90 assert!(swapped_filename.is_err());
91 }
92
93 #[test]
95 fn tampered_header_fields_fail_authentication() {
96 let key = [9u8; 32];
97 let salt = [1u8; 16];
98 let params = KeyDerivationParams::test_defaults();
99 let content_nonce = [2u8; 24];
100 let filename_nonce = [3u8; 24];
101 let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
102
103 let (content_ct, _) = encrypt_bytes(
104 b"file content",
105 &key,
106 &content_nonce,
107 &binding.aad(AadPurpose::Content),
108 )
109 .unwrap();
110
111 let weak_params = KeyDerivationParams::new(8, 1, 1, 32);
113 let tampered = HeaderBinding::new(&salt, &weak_params, &content_nonce, &filename_nonce);
114 assert!(
115 decrypt_bytes(
116 &content_ct,
117 &key,
118 &content_nonce,
119 &tampered.aad(AadPurpose::Content),
120 )
121 .is_err()
122 );
123
124 let other_salt = [7u8; 16];
126 let tampered = HeaderBinding::new(&other_salt, ¶ms, &content_nonce, &filename_nonce);
127 assert!(
128 decrypt_bytes(
129 &content_ct,
130 &key,
131 &content_nonce,
132 &tampered.aad(AadPurpose::Content),
133 )
134 .is_err()
135 );
136 }
137
138 #[test]
140 fn header_round_trip_decrypts_with_header_binding() {
141 let key = [4u8; 32];
142 let salt = [1u8; 16];
143 let params = KeyDerivationParams::test_defaults();
144 let content_nonce = [2u8; 24];
145 let filename_nonce = [3u8; 24];
146 let binding = HeaderBinding::new(&salt, ¶ms, &content_nonce, &filename_nonce);
147
148 let (filename_ct, _) = encrypt_bytes(
149 b"name.txt",
150 &key,
151 &filename_nonce,
152 &binding.aad(AadPurpose::Filename),
153 )
154 .unwrap();
155 let (content_ct, _) = encrypt_bytes(
156 b"hello",
157 &key,
158 &content_nonce,
159 &binding.aad(AadPurpose::Content),
160 )
161 .unwrap();
162
163 let header =
164 FileHeader::new(salt, params, content_nonce, filename_nonce, filename_ct).unwrap();
165 let serialized = crate::v2::header_ops::serialize(&header);
166 let parsed = crate::v2::header_ops::try_deserialize(&serialized).unwrap();
167
168 let (name, _) = decrypt_bytes(
169 &parsed.filename_ciphertext,
170 &key,
171 &parsed.filename_nonce,
172 &parsed.binding().aad(AadPurpose::Filename),
173 )
174 .unwrap();
175 let (content, _) = decrypt_bytes(
176 &content_ct,
177 &key,
178 &parsed.content_nonce,
179 &parsed.binding().aad(AadPurpose::Content),
180 )
181 .unwrap();
182
183 assert_eq!(name.as_slice(), b"name.txt");
184 assert_eq!(content.as_slice(), b"hello");
185 }
186}