//! Attachment encrypt/decrypt + content-addressed AID + cap enforcement. use idfoto_core::{ AttachmentId, IdfotoError, crypto::KdfParams, decrypt_attachment, derive_master_key, encrypt_attachment, }; use zeroize::Zeroizing; fn fast_params() -> KdfParams { KdfParams { argon2_m: 256, argon2_t: 1, argon2_p: 1 } } fn make_key() -> Zeroizing<[u8; 32]> { derive_master_key(b"x", &[0u8; 32], &[0u8; 32], &fast_params()).unwrap() } #[test] fn attachment_round_trip_5kb() { let plaintext: Vec = (0..5000u32).map(|i| (i & 0xff) as u8).collect(); let key = make_key(); let enc = encrypt_attachment(&plaintext, &key, 10 * 1024 * 1024).unwrap(); assert_eq!(enc.id, AttachmentId::from_plaintext(&plaintext)); let dec = decrypt_attachment(&enc.bytes, &key).unwrap(); assert_eq!(&*dec, &plaintext); } #[test] fn identical_plaintexts_yield_identical_aids() { let plaintext = b"hello world"; let key = make_key(); let a = encrypt_attachment(plaintext, &key, 1024).unwrap(); let b = encrypt_attachment(plaintext, &key, 1024).unwrap(); assert_eq!(a.id, b.id); // (Bytes will differ because nonce is random per-encryption — that's expected.) } #[test] fn cap_enforcement_at_exact_max() { let plaintext = vec![0u8; 1024]; let key = make_key(); // Exactly at max — should pass let _ = encrypt_attachment(&plaintext, &key, 1024).unwrap(); // One byte over — should fail let err = encrypt_attachment(&plaintext, &key, 1023); match err { Err(IdfotoError::AttachmentTooLarge { size, max }) => { assert_eq!(size, 1024); assert_eq!(max, 1023); } other => panic!("expected AttachmentTooLarge, got {other:?}"), } }