1use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce};
22use aes_gcm_siv::aead::Aead;
23use argon2::Argon2;
24use zeroize::Zeroizing;
25use crate::stego::error::StegoError;
26
27const STRUCTURAL_SALT: &[u8; 16] = b"phasm-ghost-v1\0\0";
31
32const ARMOR_STRUCTURAL_SALT: &[u8; 16] = b"phasm-armor-v1\0\0";
35
36const TEMPLATE_SALT: &[u8; 16] = b"phasm-tmpl-v1\0\0\0";
39
40const FORTRESS_STRUCTURAL_SALT: &[u8; 16] = b"phasm-fort-v1\0\0\0";
43
44pub const NONCE_LEN: usize = 12;
46pub const SALT_LEN: usize = 16;
48
49const SHADOW_STRUCTURAL_SALT: &[u8; 16] = b"phasm-shdw-v1\0\0\0";
52
53const H264_MVD_STRUCTURAL_SALT: &[u8; 16] = b"phasm-h264mvd-v1";
58
59pub const FORTRESS_EMPTY_SALT: [u8; SALT_LEN] = *b"phasm-fe-salt00\0";
65
66pub const FORTRESS_EMPTY_NONCE: [u8; NONCE_LEN] = *b"ph-fe-nonce\0";
71
72pub fn derive_structural_key(passphrase: &str) -> Result<Zeroizing<[u8; 64]>, StegoError> {
77 let mut output = Zeroizing::new([0u8; 64]);
78 Argon2::default()
79 .hash_password_into(passphrase.as_bytes(), STRUCTURAL_SALT, &mut *output)
80 .map_err(|_| StegoError::KeyDerivationFailed)?;
81 Ok(output)
82}
83
84pub fn derive_h264_mvd_structural_key(
91 passphrase: &str,
92) -> Result<Zeroizing<[u8; 64]>, StegoError> {
93 let mut output = Zeroizing::new([0u8; 64]);
94 Argon2::default()
95 .hash_password_into(passphrase.as_bytes(), H264_MVD_STRUCTURAL_SALT, &mut *output)
96 .map_err(|_| StegoError::KeyDerivationFailed)?;
97 Ok(output)
98}
99
100pub fn derive_per_gop_seed_from_master(
113 master_seed: &[u8; 32],
114 gop_idx: u32,
115 label: &[u8],
116) -> [u8; 32] {
117 use sha2::{Digest, Sha256};
118 let mut hasher = Sha256::new();
119 hasher.update(b"phasm-h264-gop-v1");
120 hasher.update(master_seed);
121 hasher.update(label);
122 hasher.update(gop_idx.to_le_bytes());
123 let digest = hasher.finalize();
124 let mut out = [0u8; 32];
125 out.copy_from_slice(&digest);
126 out
127}
128
129pub fn derive_armor_structural_key(passphrase: &str) -> Result<Zeroizing<[u8; 64]>, StegoError> {
134 let mut output = Zeroizing::new([0u8; 64]);
135 Argon2::default()
136 .hash_password_into(passphrase.as_bytes(), ARMOR_STRUCTURAL_SALT, &mut *output)
137 .map_err(|_| StegoError::KeyDerivationFailed)?;
138 Ok(output)
139}
140
141pub fn derive_template_key(passphrase: &str) -> Result<[u8; 32], StegoError> {
146 let mut output = [0u8; 32];
147 Argon2::default()
148 .hash_password_into(passphrase.as_bytes(), TEMPLATE_SALT, &mut output)
149 .map_err(|_| StegoError::KeyDerivationFailed)?;
150 Ok(output)
151}
152
153pub fn derive_fortress_structural_key(passphrase: &str) -> Result<[u8; 32], StegoError> {
158 let mut output = [0u8; 32];
159 Argon2::default()
160 .hash_password_into(passphrase.as_bytes(), FORTRESS_STRUCTURAL_SALT, &mut output)
161 .map_err(|_| StegoError::KeyDerivationFailed)?;
162 Ok(output)
163}
164
165pub fn derive_shadow_structural_key(passphrase: &str) -> Result<Zeroizing<[u8; 32]>, StegoError> {
172 let mut output = Zeroizing::new([0u8; 32]);
173 Argon2::default()
174 .hash_password_into(passphrase.as_bytes(), SHADOW_STRUCTURAL_SALT, &mut *output)
175 .map_err(|_| StegoError::KeyDerivationFailed)?;
176 Ok(output)
177}
178
179pub fn derive_encryption_key(passphrase: &str, salt: &[u8]) -> Result<Zeroizing<[u8; 32]>, StegoError> {
181 let mut key = Zeroizing::new([0u8; 32]);
182 Argon2::default()
183 .hash_password_into(passphrase.as_bytes(), salt, &mut *key)
184 .map_err(|_| StegoError::KeyDerivationFailed)?;
185 Ok(key)
186}
187
188pub fn encrypt(plaintext: &[u8], passphrase: &str) -> Result<(Vec<u8>, [u8; NONCE_LEN], [u8; SALT_LEN]), StegoError> {
193 use rand::RngCore;
194 let mut rng = rand::thread_rng();
195
196 let mut salt = [0u8; SALT_LEN];
197 rng.fill_bytes(&mut salt);
198
199 let mut nonce_bytes = [0u8; NONCE_LEN];
200 rng.fill_bytes(&mut nonce_bytes);
201
202 let key = derive_encryption_key(passphrase, &salt)?;
203 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
204 let nonce = Nonce::from_slice(&nonce_bytes);
205
206 let ciphertext = cipher.encrypt(nonce, plaintext).expect("AES-GCM-SIV encrypt should not fail");
207
208 Ok((ciphertext, nonce_bytes, salt))
209}
210
211pub fn encrypt_with(
216 plaintext: &[u8],
217 passphrase: &str,
218 salt: &[u8; SALT_LEN],
219 nonce_bytes: &[u8; NONCE_LEN],
220) -> Result<Vec<u8>, StegoError> {
221 let key = derive_encryption_key(passphrase, salt)?;
222 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
223 let nonce = Nonce::from_slice(nonce_bytes);
224
225 Ok(cipher.encrypt(nonce, plaintext).expect("AES-GCM-SIV encrypt should not fail"))
226}
227
228pub fn decrypt(
233 ciphertext: &[u8],
234 passphrase: &str,
235 salt: &[u8],
236 nonce_bytes: &[u8; NONCE_LEN],
237) -> Result<Vec<u8>, StegoError> {
238 let key = derive_encryption_key(passphrase, salt)?;
239 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
240 let nonce = Nonce::from_slice(nonce_bytes);
241
242 cipher
243 .decrypt(nonce, ciphertext)
244 .map_err(|_| StegoError::DecryptionFailed)
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn encrypt_decrypt_roundtrip() {
253 let msg = b"Hello, steganography!";
254 let passphrase = "secret123";
255
256 let (ct, nonce, salt) = encrypt(msg, passphrase).unwrap();
257 let pt = decrypt(&ct, passphrase, &salt, &nonce).unwrap();
258 assert_eq!(pt, msg);
259 }
260
261 #[test]
262 fn wrong_passphrase_fails() {
263 let msg = b"secret message";
264 let (ct, nonce, salt) = encrypt(msg, "correct").unwrap();
265 let result = decrypt(&ct, "wrong", &salt, &nonce);
266 assert!(matches!(result, Err(StegoError::DecryptionFailed)));
267 }
268
269 #[test]
270 fn empty_message_works() {
271 let msg = b"";
272 let passphrase = "pass";
273 let (ct, nonce, salt) = encrypt(msg, passphrase).unwrap();
274 let pt = decrypt(&ct, passphrase, &salt, &nonce).unwrap();
275 assert_eq!(pt, msg.to_vec());
276 }
277
278 #[test]
279 fn structural_key_deterministic() {
280 let a = derive_structural_key("mypass").unwrap();
281 let b = derive_structural_key("mypass").unwrap();
282 assert_eq!(a, b);
283 }
284
285 #[test]
286 fn structural_key_differs_by_passphrase() {
287 let a = derive_structural_key("pass1").unwrap();
288 let b = derive_structural_key("pass2").unwrap();
289 assert_ne!(a, b);
290 }
291
292 #[test]
293 fn ghost_and_armor_structural_keys_differ() {
294 let ghost = derive_structural_key("same_pass").unwrap();
295 let armor = derive_armor_structural_key("same_pass").unwrap();
296 assert_ne!(ghost, armor, "Ghost and Armor keys must differ for the same passphrase");
297 }
298
299 #[test]
300 fn armor_structural_key_deterministic() {
301 let a = derive_armor_structural_key("mypass").unwrap();
302 let b = derive_armor_structural_key("mypass").unwrap();
303 assert_eq!(a, b);
304 }
305
306 #[test]
307 fn template_key_deterministic() {
308 let a = derive_template_key("mypass").unwrap();
309 let b = derive_template_key("mypass").unwrap();
310 assert_eq!(a, b);
311 }
312
313 #[test]
314 fn fortress_key_deterministic() {
315 let a = derive_fortress_structural_key("mypass").unwrap();
316 let b = derive_fortress_structural_key("mypass").unwrap();
317 assert_eq!(a, b);
318 }
319
320 #[test]
321 fn shadow_key_deterministic() {
322 let a = derive_shadow_structural_key("mypass").unwrap();
323 let b = derive_shadow_structural_key("mypass").unwrap();
324 assert_eq!(a, b);
325 }
326
327 #[test]
328 fn shadow_key_differs_from_others() {
329 let ghost = derive_structural_key("same_pass").unwrap();
330 let armor = derive_armor_structural_key("same_pass").unwrap();
331 let fortress = derive_fortress_structural_key("same_pass").unwrap();
332 let template = derive_template_key("same_pass").unwrap();
333 let shadow = derive_shadow_structural_key("same_pass").unwrap();
334 assert_ne!(&ghost[..32], &shadow[..]);
335 assert_ne!(&armor[..32], &shadow[..]);
336 assert_ne!(&fortress[..], &shadow[..]);
337 assert_ne!(&template[..], &shadow[..]);
338 }
339
340 #[test]
341 fn fortress_key_differs_from_others() {
342 let ghost = derive_structural_key("same_pass").unwrap();
343 let armor = derive_armor_structural_key("same_pass").unwrap();
344 let fortress = derive_fortress_structural_key("same_pass").unwrap();
345 let template = derive_template_key("same_pass").unwrap();
346 assert_ne!(&ghost[..32], &fortress[..]);
347 assert_ne!(&armor[..32], &fortress[..]);
348 assert_ne!(&template[..], &fortress[..]);
349 }
350
351 #[test]
352 fn template_key_differs_from_structural() {
353 let ghost = derive_structural_key("same_pass").unwrap();
354 let armor = derive_armor_structural_key("same_pass").unwrap();
355 let template = derive_template_key("same_pass").unwrap();
356 assert_ne!(&ghost[..32], &template[..]);
357 assert_ne!(&armor[..32], &template[..]);
358 }
359
360 #[test]
361 fn encryption_key_differs_by_salt() {
362 let key1 = derive_encryption_key("pass", &[0u8; 16]).unwrap();
363 let key2 = derive_encryption_key("pass", &[1u8; 16]).unwrap();
364 assert_ne!(key1, key2);
365 }
366
367 #[test]
368 fn ciphertext_differs_per_encryption() {
369 let msg = b"same message";
372 let (ct1, _, _) = encrypt(msg, "pass").unwrap();
373 let (ct2, _, _) = encrypt(msg, "pass").unwrap();
374 assert_ne!(ct1, ct2, "repeated encryptions should produce different ciphertext");
375 }
376}