Coverless steganography¶
Classical steganography modifies a cover file. The modification is the vulnerability: a detector can compare the stego file against statistical models of unmodified files and flag anomalies. The modification does not have to be found; it only has to be noticed.
Coverless steganography removes the modification. Instead of modifying an existing image, a generative system produces a new image that encodes the message by construction. There is no original to compare against. The stego image is not a modified file; it is a generated one, and statistically it looks like any other generated image.
GAN-based coverless: SteganoGAN¶
SteganoGAN trains a GAN where the generator produces images that encode a given bit sequence, and a decoder network recovers the bits. The discriminator ensures the images look like real photographs from the training distribution.
pip install steganogan
Encode a message:
from steganogan import SteganoGAN
model = SteganoGAN.load(architecture='dense')
model.encode('cover.png', 'stego.png', 'the payload text or binary string')
Decode:
print(model.decode('stego.png'))
The dense architecture achieves approximately 4.4 bits per pixel on COCO-style images.
The residual architecture trades capacity for visual quality. Use the residual variant
for targets that might run image quality metrics; use dense where capacity matters more.
SteganoGAN expects PNG input. For JPEG-heavy delivery channels, save as PNG, encode, and convert the stego output to high-quality JPEG only after encoding. Do not encode directly into JPEG-destined images, as JPEG quantisation will corrupt the payload.
Diffusion-based approaches¶
Stable Diffusion and similar models generate images from latent noise. The noise vector is the generative seed; modifying it deterministically encodes information into the output image. The message is not in the pixels in the classical sense; it is in the generation process.
The Invisible Watermark library provides a practical implementation:
pip install invisible-watermark
Embed a 48-bit identifier into a generated image:
from imwatermark import WatermarkEncoder
encoder = WatermarkEncoder()
encoder.set_watermark('bits', [1,0,1,1,0,0,...]) # 48 bits
bgr_img = cv2.imread('generated.png')
bgr_encoded = encoder.encode(bgr_img, 'rivaGan')
cv2.imwrite('stego.png', bgr_encoded)
For higher capacity, the research direction is latent diffusion steganography: encoding the payload into the initial noise tensor before sampling. This requires access to the diffusion model itself (Stable Diffusion weights run locally), not just the output image. The advantage is that the encoding is part of the generation process and leaves no detectable modification artefact in the output.
Why this matters operationally¶
The traditional detection workflow is:
Obtain the image
Compare against statistical models of clean images
Flag anomalies
This fails for coverless content because step 3 has no baseline. A generated image with an embedded payload is statistically identical to a generated image without one. The only distinguishing feature is knowledge of the encoding scheme, which the detector does not have.
For payload delivery, the practical implication is that per-file detection tools (stegdetect, zsteg, Aletheia operating on single images) are largely ineffective. Defenders are pushed toward traffic analysis and behavioural detection rather than file inspection.
The operational constraint is model availability: generating a stego image with a diffusion model requires either a local GPU or a capable API. For a single delivery, that is manageable. For a high-volume C2 channel that updates instructions frequently, compute cost becomes relevant.
Choosing an approach¶
For one-time payload delivery where detectability matters more than convenience: diffusion or SteganoGAN. No modification artefact, no classical steganalysis baseline.
For an ongoing C2 channel where images are generated in bulk: SteganoGAN is faster to run repeatedly than a full diffusion pipeline.
For environments where neural tools are not available: LSB with encryption still raises the bar significantly over unencrypted embedding, and is harder to act on even when detected statistically.