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
195 let mut salt = [0u8; SALT_LEN];
196 let mut nonce_bytes = [0u8; NONCE_LEN];
197
198 if let Ok(seed_str) = std::env::var("PHASM_DETERMINISTIC_SEED")
205 && let Ok(seed) = seed_str.parse::<u64>()
206 {
207 use rand::SeedableRng;
208 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
209 rng.fill_bytes(&mut salt);
210 rng.fill_bytes(&mut nonce_bytes);
211 } else {
212 let mut rng = rand::thread_rng();
213 rng.fill_bytes(&mut salt);
214 rng.fill_bytes(&mut nonce_bytes);
215 }
216
217 let key = derive_encryption_key(passphrase, &salt)?;
218 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
219 let nonce = Nonce::from_slice(&nonce_bytes);
220
221 let ciphertext = cipher.encrypt(nonce, plaintext).expect("AES-GCM-SIV encrypt should not fail");
222
223 Ok((ciphertext, nonce_bytes, salt))
224}
225
226pub fn encrypt_with(
231 plaintext: &[u8],
232 passphrase: &str,
233 salt: &[u8; SALT_LEN],
234 nonce_bytes: &[u8; NONCE_LEN],
235) -> Result<Vec<u8>, StegoError> {
236 let key = derive_encryption_key(passphrase, salt)?;
237 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
238 let nonce = Nonce::from_slice(nonce_bytes);
239
240 Ok(cipher.encrypt(nonce, plaintext).expect("AES-GCM-SIV encrypt should not fail"))
241}
242
243pub fn decrypt(
248 ciphertext: &[u8],
249 passphrase: &str,
250 salt: &[u8],
251 nonce_bytes: &[u8; NONCE_LEN],
252) -> Result<Vec<u8>, StegoError> {
253 let key = derive_encryption_key(passphrase, salt)?;
254 let cipher = Aes256GcmSiv::new_from_slice(&*key).expect("valid key length");
255 let nonce = Nonce::from_slice(nonce_bytes);
256
257 cipher
258 .decrypt(nonce, ciphertext)
259 .map_err(|_| StegoError::DecryptionFailed)
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn encrypt_decrypt_roundtrip() {
268 let msg = b"Hello, steganography!";
269 let passphrase = "secret123";
270
271 let (ct, nonce, salt) = encrypt(msg, passphrase).unwrap();
272 let pt = decrypt(&ct, passphrase, &salt, &nonce).unwrap();
273 assert_eq!(pt, msg);
274 }
275
276 #[test]
277 fn wrong_passphrase_fails() {
278 let msg = b"secret message";
279 let (ct, nonce, salt) = encrypt(msg, "correct").unwrap();
280 let result = decrypt(&ct, "wrong", &salt, &nonce);
281 assert!(matches!(result, Err(StegoError::DecryptionFailed)));
282 }
283
284 #[test]
285 fn empty_message_works() {
286 let msg = b"";
287 let passphrase = "pass";
288 let (ct, nonce, salt) = encrypt(msg, passphrase).unwrap();
289 let pt = decrypt(&ct, passphrase, &salt, &nonce).unwrap();
290 assert_eq!(pt, msg.to_vec());
291 }
292
293 #[test]
294 fn structural_key_deterministic() {
295 let a = derive_structural_key("mypass").unwrap();
296 let b = derive_structural_key("mypass").unwrap();
297 assert_eq!(a, b);
298 }
299
300 #[test]
301 fn structural_key_differs_by_passphrase() {
302 let a = derive_structural_key("pass1").unwrap();
303 let b = derive_structural_key("pass2").unwrap();
304 assert_ne!(a, b);
305 }
306
307 #[test]
308 fn ghost_and_armor_structural_keys_differ() {
309 let ghost = derive_structural_key("same_pass").unwrap();
310 let armor = derive_armor_structural_key("same_pass").unwrap();
311 assert_ne!(ghost, armor, "Ghost and Armor keys must differ for the same passphrase");
312 }
313
314 #[test]
315 fn armor_structural_key_deterministic() {
316 let a = derive_armor_structural_key("mypass").unwrap();
317 let b = derive_armor_structural_key("mypass").unwrap();
318 assert_eq!(a, b);
319 }
320
321 #[test]
322 fn template_key_deterministic() {
323 let a = derive_template_key("mypass").unwrap();
324 let b = derive_template_key("mypass").unwrap();
325 assert_eq!(a, b);
326 }
327
328 #[test]
329 fn fortress_key_deterministic() {
330 let a = derive_fortress_structural_key("mypass").unwrap();
331 let b = derive_fortress_structural_key("mypass").unwrap();
332 assert_eq!(a, b);
333 }
334
335 #[test]
336 fn shadow_key_deterministic() {
337 let a = derive_shadow_structural_key("mypass").unwrap();
338 let b = derive_shadow_structural_key("mypass").unwrap();
339 assert_eq!(a, b);
340 }
341
342 #[test]
343 fn shadow_key_differs_from_others() {
344 let ghost = derive_structural_key("same_pass").unwrap();
345 let armor = derive_armor_structural_key("same_pass").unwrap();
346 let fortress = derive_fortress_structural_key("same_pass").unwrap();
347 let template = derive_template_key("same_pass").unwrap();
348 let shadow = derive_shadow_structural_key("same_pass").unwrap();
349 assert_ne!(&ghost[..32], &shadow[..]);
350 assert_ne!(&armor[..32], &shadow[..]);
351 assert_ne!(&fortress[..], &shadow[..]);
352 assert_ne!(&template[..], &shadow[..]);
353 }
354
355 #[test]
356 fn fortress_key_differs_from_others() {
357 let ghost = derive_structural_key("same_pass").unwrap();
358 let armor = derive_armor_structural_key("same_pass").unwrap();
359 let fortress = derive_fortress_structural_key("same_pass").unwrap();
360 let template = derive_template_key("same_pass").unwrap();
361 assert_ne!(&ghost[..32], &fortress[..]);
362 assert_ne!(&armor[..32], &fortress[..]);
363 assert_ne!(&template[..], &fortress[..]);
364 }
365
366 #[test]
367 fn template_key_differs_from_structural() {
368 let ghost = derive_structural_key("same_pass").unwrap();
369 let armor = derive_armor_structural_key("same_pass").unwrap();
370 let template = derive_template_key("same_pass").unwrap();
371 assert_ne!(&ghost[..32], &template[..]);
372 assert_ne!(&armor[..32], &template[..]);
373 }
374
375 #[test]
376 fn encryption_key_differs_by_salt() {
377 let key1 = derive_encryption_key("pass", &[0u8; 16]).unwrap();
378 let key2 = derive_encryption_key("pass", &[1u8; 16]).unwrap();
379 assert_ne!(key1, key2);
380 }
381
382 #[test]
383 fn ciphertext_differs_per_encryption() {
384 let msg = b"same message";
387 let (ct1, _, _) = encrypt(msg, "pass").unwrap();
388 let (ct2, _, _) = encrypt(msg, "pass").unwrap();
389 assert_ne!(ct1, ct2, "repeated encryptions should produce different ciphertext");
390 }
391}