use std::fmt;
use crate::cost::CostMap;
use crate::stego::stc::MAX_BPP;
const STC_EFFICIENCY: f32 = 0.85;
const MAC_OVERHEAD_BYTES: usize = 16;
#[cfg(feature = "pqc")]
const ML_KEM_1024_CIPHERTEXT_BYTES: usize = 1568;
const BITS_PER_BYTE: usize = 8;
#[derive(Debug)]
pub enum SizerError {
PayloadTooLarge {
payload: usize,
available: usize,
deficit: usize,
},
}
impl fmt::Display for SizerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SizerError::PayloadTooLarge { .. } => write!(
f,
"the message does not fit in this image; shorten the message or use an image of \
higher resolution"
),
}
}
}
impl std::error::Error for SizerError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum EmbeddingMode {
#[default]
Symmetric,
#[cfg(feature = "pqc")]
AsymmetricPqc,
}
impl EmbeddingMode {
pub fn key_transport_overhead_bytes(self) -> usize {
match self {
EmbeddingMode::Symmetric => 0,
#[cfg(feature = "pqc")]
EmbeddingMode::AsymmetricPqc => ML_KEM_1024_CIPHERTEXT_BYTES,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct CapacityReport {
pub(crate) total_pixels: usize,
pub(crate) textured_pixels: usize,
pub(crate) gross_capacity_bits: usize,
pub(crate) net_capacity_bits: usize,
pub(crate) mac_overhead_bytes: usize,
pub(crate) available_bytes: usize,
}
impl CapacityReport {
pub fn total_pixels(&self) -> usize {
self.total_pixels
}
pub fn textured_pixels(&self) -> usize {
self.textured_pixels
}
pub fn gross_capacity_bits(&self) -> usize {
self.gross_capacity_bits
}
pub fn net_capacity_bits(&self) -> usize {
self.net_capacity_bits
}
pub fn mac_overhead_bytes(&self) -> usize {
self.mac_overhead_bytes
}
pub fn available_bytes(&self) -> usize {
self.available_bytes
}
}
pub fn compute_capacity(cost_map: &CostMap<'_>, mode: EmbeddingMode) -> CapacityReport {
let total_pixels = cost_map.pixel_count();
let textured_pixels = cost_map.costs().iter().filter(|&&cost| cost > 0.0).count();
let gross_capacity_bits = (textured_pixels as f32 * MAX_BPP) as usize;
let net_capacity_bits = (gross_capacity_bits as f32 * STC_EFFICIENCY) as usize;
let available_bytes = (net_capacity_bits / BITS_PER_BYTE)
.saturating_sub(MAC_OVERHEAD_BYTES)
.saturating_sub(mode.key_transport_overhead_bytes());
CapacityReport {
total_pixels,
textured_pixels,
gross_capacity_bits,
net_capacity_bits,
mac_overhead_bytes: MAC_OVERHEAD_BYTES,
available_bytes,
}
}
pub fn validate_payload_fits(
payload_len: usize,
report: &CapacityReport,
) -> Result<(), SizerError> {
if payload_len > report.available_bytes {
return Err(SizerError::PayloadTooLarge {
payload: payload_len,
available: report.available_bytes,
deficit: payload_len - report.available_bytes,
});
}
Ok(())
}