shadowforge_lib/domain/deadrop/
mod.rs1use sha2::{Digest, Sha256};
7
8use crate::domain::types::{PlatformProfile, RetrievalManifest, StegoTechnique};
9
10pub fn build_retrieval_manifest(
14 platform: &PlatformProfile,
15 retrieval_url: String,
16 technique: StegoTechnique,
17 stego_bytes: &[u8],
18) -> RetrievalManifest {
19 let hash = Sha256::digest(stego_bytes);
20 let stego_hash = hex::encode(hash);
21
22 RetrievalManifest {
23 platform: platform.clone(),
24 retrieval_url,
25 technique,
26 stego_hash,
27 }
28}
29
30#[must_use]
32pub const fn platform_is_lossless(platform: &PlatformProfile) -> bool {
33 matches!(platform, PlatformProfile::Telegram)
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 type TestResult = Result<(), Box<dyn std::error::Error>>;
41
42 #[test]
43 fn retrieval_manifest_serialises_to_json() -> TestResult {
44 let manifest = build_retrieval_manifest(
45 &PlatformProfile::Instagram,
46 "https://instagram.com/p/abc123".to_owned(),
47 StegoTechnique::LsbImage,
48 b"fake stego bytes",
49 );
50
51 let json = serde_json::to_string_pretty(&manifest)?;
52 assert!(json.contains("Instagram"));
53 assert!(json.contains("abc123"));
54 assert!(!manifest.stego_hash.is_empty());
55 Ok(())
56 }
57
58 #[test]
59 fn retrieval_manifest_hash_changes_with_content() {
60 let m1 = build_retrieval_manifest(
61 &PlatformProfile::Twitter,
62 "https://x.com/post/1".to_owned(),
63 StegoTechnique::LsbImage,
64 b"content A",
65 );
66 let m2 = build_retrieval_manifest(
67 &PlatformProfile::Twitter,
68 "https://x.com/post/1".to_owned(),
69 StegoTechnique::LsbImage,
70 b"content B",
71 );
72
73 assert_ne!(m1.stego_hash, m2.stego_hash);
74 }
75
76 #[test]
77 fn telegram_is_lossless() {
78 assert!(platform_is_lossless(&PlatformProfile::Telegram));
79 }
80
81 #[test]
82 fn instagram_is_not_lossless() {
83 assert!(!platform_is_lossless(&PlatformProfile::Instagram));
84 }
85
86 #[test]
87 fn retrieval_manifest_deserialises_roundtrip() -> TestResult {
88 let manifest = build_retrieval_manifest(
89 &PlatformProfile::Custom {
90 quality: 85,
91 subsampling: crate::domain::types::ChromaSubsampling::Yuv420,
92 },
93 "https://example.com/image.png".to_owned(),
94 StegoTechnique::LsbImage,
95 b"test data",
96 );
97
98 let json = serde_json::to_string(&manifest)?;
99 let recovered: RetrievalManifest = serde_json::from_str(&json)?;
100
101 assert_eq!(recovered.stego_hash, manifest.stego_hash);
102 assert_eq!(recovered.retrieval_url, manifest.retrieval_url);
103 Ok(())
104 }
105}