Expand description
Media-agnostic shadow-layer primitives.
Shadow messages provide plausible deniability: multiple messages can be hidden in a single cover (image, video, …), each with a different passphrase. The frame format, parity tier ladder, RS encoding shape, and capacity arithmetic are the same regardless of medium. Per-medium specifics (which positions to embed at, how to write into them, how to walk the cover at decode time) live in the medium-specific modules:
- Image (Y-channel JPEG nzAC, cost-pool + hash priority):
core/src/stego/ghost/shadow.rs - Video H.264 (4 bypass-bin domains, hash priority + additive
bias for N>1):
core/src/codec/h264/stego/shadow.rs
§Frame formats (no header, fdl recovered via first-block peek)
Two variants exist, distinguished by the width of the plaintext-length prefix:
Standard (image) — SHADOW_FRAME_OVERHEAD = 46 bytes:
[plaintext_len: 2B u16 BE] [salt: 16B] [nonce: 12B] [ciphertext: N+16B]Used by image stego (core/src/stego/ghost/shadow.rs). Hard
cap: 65,535-byte plaintext per shadow. Adequate for image
covers (Y-channel JPEG nzAC capacity ≈ tens of KB).
Wide (video) — SHADOW_FRAME_OVERHEAD_WIDE = 48 bytes:
[plaintext_len: 4B u32 BE] [salt: 16B] [nonce: 12B] [ciphertext: N+16B]Used by H.264 video stego
(core/src/codec/h264/stego/shadow.rs). Hard cap:
4,294,967,295-byte plaintext per shadow. Required because
video covers can support much larger payloads (file
attachments, multi-MB shadows). The two formats are wire-
incompatible by design — image stego has been released and the
u16 layout is locked; video stego is pre-release and uses the
wider layout natively.
No magic byte; AES-256-GCM-SIV authentication is the only
validator. Decoders brute-force (parity, fdl) combinations
using a first-block-peek heuristic to derive fdl from the
plaintext-length prefix once the first 255-byte RS block decodes.
Structs§
- Parsed
Shadow Frame - Parsed shadow frame — output of
parse_shadow_frame. - Parsed
Shadow Frame Wide - Parsed wide shadow frame — output of
parse_shadow_frame_wide. - Shadow
Layer - One shadow layer’s input — message + passphrase + optional file attachments. The encoder takes a slice of these (size-descending for primary-vs-shadow ordering by message size).
Constants§
- MAX_
SHADOW_ FRAME_ BYTES - Maximum RS-encoded frame bytes for the standard (image) format — guards against unreasonable allocations during decode brute-force. Implies plaintext ≤ ~256 KB before RS expansion; in practice u16 caps it at 65,535 bytes.
- MAX_
SHADOW_ FRAME_ BYTES_ WIDE - Maximum RS-encoded frame bytes for the wide (video) format. Bumped to 16 MB to accommodate plausible large attachments (e.g., embedded photos as shadows in long videos). Decoder brute-force scan is bounded; this is the safety upper bound.
- SHADOW_
FRAME_ OVERHEAD - Standard (image) frame overhead inside the RS-encoded payload:
plaintext_len(2) + salt(16) + nonce(12) + tag(16) = 46 bytes. Used by image stego (stego::ghost::shadow); locked at u16 to preserve compatibility with released app versions. - SHADOW_
FRAME_ OVERHEAD_ WIDE - Wide (video) frame overhead — same fields but with a u32
plaintext-length prefix:
plaintext_len(4) + salt(16) + nonce(12) + tag(16) = 48 bytes. Used by H.264 video stego — covers can support multi-MB shadows (file attachments) so the u16 cap is too tight. - SHADOW_
PARITY_ TIERS - RS parity tiers. Brute-forced at decode.
Functions§
- build_
shadow_ frame - Build the shadow inner frame (before RS encoding).
- build_
shadow_ frame_ wide - Build the wide shadow inner frame (u32 plaintext_len) — see module docs for layout. Used by video stego.
- compute_
max_ shadow_ fdl - Compute the maximum
frame_data_len(bytes before RS encoding) that fits inmax_rs_bytesof available LSB capacity at the given parity length. - parse_
shadow_ frame - Parse a shadow inner frame (after RS decoding).
- parse_
shadow_ frame_ wide - Parse a wide shadow inner frame (u32 plaintext_len). Used by video stego.