Skip to main content

Module generate

Module generate 

Source
Expand description

Building a container around a payload, instead of hiding a payload inside one.

§Who this is for

Someone with no usable photograph. A laptop with no comfortable way to move pictures across from a phone, or a camera that only ever emits JPEG or HEIC — both lossy, both leaving the 8x8 grid the validation layer refuses, and neither repaired by converting the file to PNG. Until this mode existed that user had no way through at all.

§Why generating is more secure than embedding, not less

Embedding modifies an image, and a modification is a thing a detector can hunt for; the argument is only ever about how well it is hidden. Generating allows something an embedder cannot do: draw each sample from the cover distribution conditioned on its least significant bit being the ciphertext bit it must carry. Rejection sampling does that in about two draws, and then, for a uniform carrier bit,

sum over b of  P(sample = v | LSB = b) P(b)  =  P(sample = v)

exactly, provided the least significant bit of the unconditioned distribution is a fair coin. The container that carries a message and the container that carries nothing are draws from one distribution. There are not two hypotheses to separate, so there is no statistic to find and no future detector that changes the answer. The naive construction — overwrite the least significant bit rather than draw conditioned on it — forfeits exactly this, and is measurably worse than doing nothing; the reasoning is with the sampler that refuses to use it.

Three consequences follow, and the code below depends on all of them:

  • The 0.02 bpp cap does not apply here. It exists because a photograph’s distribution is unknown to the sender. Here the sender is the distribution, and every sample carries a bit: 1.45 MB in a 2000x2000 container against roughly 7 KB for the embedding path.
  • The HILL cost map and the trellis take no part. There is no cost to minimise when every position is equally free.
  • “One image + one password = one message” holds by construction. Each generation is a new container with a different perceptual hash, and therefore a different salt, key and nonce. For the embedding path that rule is a matter of the user’s discipline; here it cannot be broken.

§What this mode does not do

It hides which, not whether. The two hypotheses it equalises are “generated around a message” and “generated around nothing”. It says nothing about “generated” versus “photographed”: the container looks like a synthetic texture, and someone looking at a folder of them sees images nobody has an obvious reason to keep. Against “which of these hundred carries the message?” it is a complete answer. Against “why do you have this folder?” it is no answer at all.

§Untying the circularity

The key comes from the perceptual hash of the container, which now depends on the ciphertext, which depends on the key. The knot unties because the hash reads a 32x32 thumbnail where some four thousand grain samples average away to nothing, and layer 1 already refuses any container whose coefficients sit within 5.0 of their median. So a draft container fixes the hash, the hash fixes the key, and the final container is checked to hash the same before anything is written.

The draft never touches the disk. It is built in memory and handed straight to the gates. A draft on disk would be the original cover, and the original cover not existing is precisely what this mode buys.

§The seed is key material

There is no cover to subtract, but an adversary who can reproduce the generator’s random state can regenerate the container and read the difference — and the confirmation is unmistakable, because the right state reproduces the image and a wrong one differs in millions of samples. So the generator is seeded with 32 bytes from the system CSPRNG and from nothing else: never a timestamp, never a counter, never anything derived from the password. The seed is not persisted, printed or logged anywhere.

Structs§

ContainerDimensions
The size of the container to draw, checked against the two size gates.
GenerateReport
What one generation produced.
RejectionExhausted
The one way conditioned sampling can fail.

Enums§

GenerateError
Everything that can go wrong between a plaintext and a generated container.

Constants§

DEFAULT_CONTAINER_SIDE
Side of the square container generated when no size is requested.
MAX_CONTAINER_PIXELS
Largest pixel count a generated container may have.
MIN_CONTAINER_SIDE
Smallest side a generated container may have, in pixels.

Functions§

generate_container
Builds a dimensions container around plaintext and writes it to output_path.