Expand description
Shadow messages: Y-channel direct LSB embedding + RS ECC (headerless brute-force).
Shadow messages provide plausible deniability for Ghost mode steganography. Multiple messages can be hidden in a single image, each with a different passphrase. They are embedded as absolute-value LSBs into Y-channel nzAC positions (the same domain as primary STC), using nsf5 modification.
The system auto-sorts messages by size: the largest becomes the primary (embedded via STC for full stealth), smaller messages become shadow channels (embedded via direct LSB with Reed-Solomon error correction).
§Headerless design
No magic byte, no frame_data_len in the bitstream. The decoder brute-forces all (parity, fdl) combinations. AES-256-GCM-SIV authentication is the only validator — successful decryption proves correct parameters.
§Short STC + Dynamic w
Since shadows and primary STC share the same Y-channel LSBs, the primary
STC uses “short” message mode: only the actual m message bits are passed
(not zero-padded to m_max). With dynamic w = min(floor(n/m), 10), small
messages get high w, meaning 2,500x fewer modifications. When w >= 2, shadow
positions get f32::INFINITY cost in STC, so Viterbi routes around them,
achieving BER ~ 0% on shadows.
§Cost-pool position selection
Shadow positions are selected in two tiers for stealth:
- Tier 1 (cost pool): Filter all Y nzAC positions to the cheapest fraction (5%, 10%, 20%, 50%, or 100%) by UNIWARD cost. Cheap positions are in textured regions — modifications there are least detectable.
- Tier 2 (hash permutation): Within the cost pool, select positions
by keyed hash
ChaCha20(seed, flat_idx)priority.
Encoder uses cover-image costs; decoder uses stego-image costs. The cost pools differ slightly at the boundary (~2-5% positions), but RS error correction handles the resulting BER. Primary Ghost proves this works: encoder and decoder already use cover vs stego costs for STC positions. The decoder brute-forces the fraction alongside parity and fdl.
§Frame format (inside RS-encoded data, no header)
RS-encoded frame (all positions):
Inner: [plaintext_len: 2B] [salt: 16B] [nonce: 12B] [ciphertext: N+16B]§nzAC invariance
The nsf5 anti-shrinkage rule (|coeff|==1 -> away from zero) ensures no
coefficient ever becomes zero. Since both shadow embedding and primary STC
use nsf5, the Y nzAC set is identical at encoder and decoder, guaranteeing
position agreement.
Structs§
- Shadow
State - State for a single shadow layer during encoding.
Functions§
- embed_
shadow_ lsb - Embed shadow intended bits as absolute-value LSBs into the Y DctGrid.
- prepare_
shadow - Prepare a shadow layer for embedding.
- rebuild_
shadow - Rebuild a shadow state with new parity and/or fraction.
- shadow_
capacity - Compute shadow capacity in plaintext bytes from Y nzAC count.
- shadow_
extract - Full shadow decode pipeline (headerless brute-force).
- verify_
shadow - Verify a shadow layer can be correctly decoded from the current image state.
- verify_
shadow_ decoder_ side - Verify a shadow can be decoded from the decoder’s perspective.