use std::io::{Read, Write};
use std::path::Path;
use bytes::Bytes;
use chrono::{DateTime, Utc};
use crate::domain::errors::{
AdaptiveError, AnalysisError, ArchiveError, CanaryError, CorrectionError, CryptoError,
DeadDropError, DeniableError, DistributionError, MediaError, OpsecError, PdfError,
ReconstructionError, ScrubberError, StegoError, TimeLockError,
};
use crate::domain::types::{
AnalysisReport, ArchiveFormat, CanaryShard, Capacity, CoverMedia, DeniableKeySet,
DeniablePayloadPair, EmbeddingProfile, GeographicManifest, KeyPair, Payload, PlatformProfile,
Shard, Signature, StegoTechnique, StyloProfile, TimeLockPuzzle, WatermarkReceipt,
WatermarkTripwireTag,
};
pub trait Encryptor {
fn generate_keypair(&self) -> Result<KeyPair, CryptoError>;
fn encapsulate(&self, public_key: &[u8]) -> Result<(Bytes, Bytes), CryptoError>;
fn decapsulate(&self, secret_key: &[u8], ciphertext: &[u8]) -> Result<Bytes, CryptoError>;
}
pub trait Signer {
fn generate_keypair(&self) -> Result<KeyPair, CryptoError>;
fn sign(&self, secret_key: &[u8], message: &[u8]) -> Result<Signature, CryptoError>;
fn verify(
&self,
public_key: &[u8],
message: &[u8],
signature: &Signature,
) -> Result<bool, CryptoError>;
}
pub trait SymmetricCipher {
fn encrypt(&self, key: &[u8], nonce: &[u8], plaintext: &[u8]) -> Result<Bytes, CryptoError>;
fn decrypt(&self, key: &[u8], nonce: &[u8], ciphertext: &[u8]) -> Result<Bytes, CryptoError>;
}
pub trait ErrorCorrector {
fn encode(
&self,
data: &[u8],
data_shards: u8,
parity_shards: u8,
) -> Result<Vec<Shard>, CorrectionError>;
fn decode(
&self,
shards: &[Option<Shard>],
data_shards: u8,
parity_shards: u8,
) -> Result<Bytes, CorrectionError>;
}
pub trait EmbedTechnique {
fn technique(&self) -> StegoTechnique;
fn capacity(&self, cover: &CoverMedia) -> Result<Capacity, StegoError>;
fn embed(&self, cover: CoverMedia, payload: &Payload) -> Result<CoverMedia, StegoError>;
}
pub trait ExtractTechnique {
fn technique(&self) -> StegoTechnique;
fn extract(&self, stego: &CoverMedia) -> Result<Payload, StegoError>;
}
pub trait MediaLoader {
fn load(&self, path: &Path) -> Result<CoverMedia, MediaError>;
fn save(&self, media: &CoverMedia, path: &Path) -> Result<(), MediaError>;
}
pub trait PdfProcessor {
fn load_pdf(&self, path: &Path) -> Result<CoverMedia, PdfError>;
fn save_pdf(&self, media: &CoverMedia, path: &Path) -> Result<(), PdfError>;
fn render_pages_to_images(&self, pdf: &CoverMedia) -> Result<Vec<CoverMedia>, PdfError>;
fn rebuild_pdf_from_images(
&self,
images: Vec<CoverMedia>,
original: &CoverMedia,
) -> Result<CoverMedia, PdfError>;
fn embed_in_content_stream(
&self,
pdf: CoverMedia,
payload: &Payload,
) -> Result<CoverMedia, PdfError>;
fn extract_from_content_stream(&self, pdf: &CoverMedia) -> Result<Payload, PdfError>;
fn embed_in_metadata(&self, pdf: CoverMedia, payload: &Payload)
-> Result<CoverMedia, PdfError>;
fn extract_from_metadata(&self, pdf: &CoverMedia) -> Result<Payload, PdfError>;
}
pub trait Distributor {
fn distribute(
&self,
payload: &Payload,
profile: &EmbeddingProfile,
covers: Vec<CoverMedia>,
embedder: &dyn EmbedTechnique,
) -> Result<Vec<CoverMedia>, DistributionError>;
}
pub trait Reconstructor {
fn reconstruct(
&self,
covers: Vec<CoverMedia>,
extractor: &dyn ExtractTechnique,
progress_cb: &dyn Fn(usize, usize),
) -> Result<Payload, ReconstructionError>;
}
pub trait CapacityAnalyser {
fn analyse(
&self,
cover: &CoverMedia,
technique: StegoTechnique,
) -> Result<AnalysisReport, AnalysisError>;
}
pub trait ArchiveHandler {
fn pack(&self, files: &[(&str, &[u8])], format: ArchiveFormat) -> Result<Bytes, ArchiveError>;
fn unpack(
&self,
archive: &[u8],
format: ArchiveFormat,
) -> Result<Vec<(String, Bytes)>, ArchiveError>;
}
#[derive(Debug, Clone)]
pub struct CameraProfile {
pub quantisation_table: [u16; 64],
pub noise_floor_db: f64,
pub model_id: String,
}
pub trait AdaptiveOptimiser {
fn optimise(
&self,
stego: CoverMedia,
original: &CoverMedia,
target_db: f64,
) -> Result<CoverMedia, AdaptiveError>;
}
pub trait CoverProfileMatcher {
fn profile_for(&self, cover: &CoverMedia) -> Option<CameraProfile>;
fn apply_profile(
&self,
cover: CoverMedia,
profile: &CameraProfile,
) -> Result<CoverMedia, AdaptiveError>;
}
pub trait CompressionSimulator {
fn simulate(
&self,
cover: CoverMedia,
platform: &PlatformProfile,
) -> Result<CoverMedia, AdaptiveError>;
fn survivable_capacity(
&self,
cover: &CoverMedia,
platform: &PlatformProfile,
) -> Result<Capacity, AdaptiveError>;
}
pub trait DeniableEmbedder {
fn embed_dual(
&self,
cover: CoverMedia,
pair: &DeniablePayloadPair,
keys: &DeniableKeySet,
embedder: &dyn EmbedTechnique,
) -> Result<CoverMedia, DeniableError>;
fn extract_with_key(
&self,
stego: &CoverMedia,
key: &[u8],
extractor: &dyn ExtractTechnique,
) -> Result<Payload, DeniableError>;
}
pub trait PanicWiper {
fn wipe(&self, config: &crate::domain::types::PanicWipeConfig) -> Result<(), OpsecError>;
}
pub trait ForensicWatermarker {
fn embed_tripwire(
&self,
cover: CoverMedia,
tag: &WatermarkTripwireTag,
) -> Result<CoverMedia, OpsecError>;
fn identify_recipient(
&self,
stego: &CoverMedia,
tags: &[WatermarkTripwireTag],
) -> Result<Option<WatermarkReceipt>, OpsecError>;
}
pub trait AmnesiaPipeline {
fn embed_in_memory(
&self,
payload_input: &mut dyn Read,
cover_input: &mut dyn Read,
output: &mut dyn Write,
technique: &dyn EmbedTechnique,
) -> Result<(), OpsecError>;
}
pub trait GeographicDistributor {
fn distribute_with_manifest(
&self,
payload: &Payload,
covers: Vec<CoverMedia>,
manifest: &GeographicManifest,
embedder: &dyn EmbedTechnique,
) -> Result<Vec<CoverMedia>, OpsecError>;
}
pub trait CanaryService {
fn embed_canary(
&self,
covers: Vec<CoverMedia>,
embedder: &dyn EmbedTechnique,
) -> Result<(Vec<CoverMedia>, CanaryShard), CanaryError>;
fn check_canary(&self, shard: &CanaryShard) -> bool;
}
pub trait DeadDropEncoder {
fn encode_for_platform(
&self,
cover: CoverMedia,
payload: &Payload,
platform: &PlatformProfile,
embedder: &dyn EmbedTechnique,
) -> Result<CoverMedia, DeadDropError>;
}
pub trait TimeLockService {
fn lock(
&self,
payload: &Payload,
unlock_at: DateTime<Utc>,
) -> Result<TimeLockPuzzle, TimeLockError>;
fn unlock(&self, puzzle: &TimeLockPuzzle) -> Result<Payload, TimeLockError>;
fn try_unlock(&self, puzzle: &TimeLockPuzzle) -> Result<Option<Payload>, TimeLockError>;
}
pub trait StyloScrubber {
fn scrub(&self, text: &str, profile: &StyloProfile) -> Result<String, ScrubberError>;
}
pub trait CorpusIndex {
fn search(
&self,
payload: &Payload,
technique: StegoTechnique,
max_results: usize,
) -> Result<Vec<crate::domain::types::CorpusEntry>, crate::domain::errors::CorpusError>;
fn add_to_index(
&self,
path: &Path,
) -> Result<crate::domain::types::CorpusEntry, crate::domain::errors::CorpusError>;
fn build_index(&self, corpus_dir: &Path) -> Result<usize, crate::domain::errors::CorpusError>;
}
#[cfg(test)]
mod object_safety_tests {
use super::*;
#[test]
fn all_port_traits_are_object_safe() {
fn assert_object_safe<T: ?Sized>() {}
assert_object_safe::<dyn Encryptor>();
assert_object_safe::<dyn Signer>();
assert_object_safe::<dyn SymmetricCipher>();
assert_object_safe::<dyn ErrorCorrector>();
assert_object_safe::<dyn EmbedTechnique>();
assert_object_safe::<dyn ExtractTechnique>();
assert_object_safe::<dyn MediaLoader>();
assert_object_safe::<dyn PdfProcessor>();
assert_object_safe::<dyn Distributor>();
assert_object_safe::<dyn Reconstructor>();
assert_object_safe::<dyn CapacityAnalyser>();
assert_object_safe::<dyn ArchiveHandler>();
assert_object_safe::<dyn AdaptiveOptimiser>();
assert_object_safe::<dyn CoverProfileMatcher>();
assert_object_safe::<dyn CompressionSimulator>();
assert_object_safe::<dyn DeniableEmbedder>();
assert_object_safe::<dyn PanicWiper>();
assert_object_safe::<dyn ForensicWatermarker>();
assert_object_safe::<dyn AmnesiaPipeline>();
assert_object_safe::<dyn GeographicDistributor>();
assert_object_safe::<dyn CanaryService>();
assert_object_safe::<dyn DeadDropEncoder>();
assert_object_safe::<dyn TimeLockService>();
assert_object_safe::<dyn StyloScrubber>();
assert_object_safe::<dyn CorpusIndex>();
}
}