Skip to content

Ext module

The ext module (module zeroecho.ext) contains experimental integrations that sit outside the core library. Everything in this module should be treated as provisional: APIs may change without notice between releases.

Experimental

The ext module is not covered by the library's API stability guarantees. Production use requires independent review of the specific component.

Steganography

Package: zeroecho.ext.integrations.stegano

LSBSteganographyMethod

Embeds and extracts arbitrary payloads in raster images using the least-significant-bit (LSB) spatial technique.

Property Value
Technique LSB, spatial domain, 1 bit per channel
Framing 4-byte big-endian length prefix, then raw payload bytes
Capacity width × height − 32 bits (the prefix consumes 32 pixels worth of LSBs)
Output format Lossless only (PNG, BMP). JPEG is rejected — lossy compression destroys the hidden bits.
Colour output Grayscale (identical R, G, B values). The cover image is converted to grayscale during embedding.
import zeroecho.ext.integrations.stegano.LSBSteganographyMethod;
import zeroecho.ext.integrations.stegano.SteganographyMethod;
import zeroecho.ext.integrations.stegano.ImageFormat;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

SteganographyMethod method = new LSBSteganographyMethod();

// Embed
try (InputStream cover  = Files.newInputStream(Path.of("cover.png"));
     InputStream secret = new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8));
     InputStream stego  = method.embed(cover, ImageFormat.PNG, secret)) {
    Files.write(Path.of("stego.png"), stego.readAllBytes());
}

// Extract
try (InputStream stego   = Files.newInputStream(Path.of("stego.png"));
     InputStream payload = method.extract(stego)) {
    String recovered = new String(payload.readAllBytes(), StandardCharsets.UTF_8);
}

embed and extract throw IOException if the image cannot be decoded, the cover is too small to hold the payload, or the length prefix in the stego image is implausible.

Covert JPEG EXIF channel

Package: zeroecho.ext.integrations.covert.jpeg

JpegExifEmbedder

Stores a payload in one or more EXIF tag slots inside a JPEG file without altering the pixel data or visible metadata.

Property Value
Carrier format JPEG
Framing Pack7 length framing spread across EXIF tag slots
Maximum payload 1 MiB
Dependency Apache Commons Imaging (must be on the module path)

Because JPEG is a lossy format, the pixel data is unchanged by this technique — only EXIF metadata slots are used.

import zeroecho.ext.integrations.covert.jpeg.JpegExifEmbedder;
import zeroecho.ext.integrations.covert.jpeg.Slot;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

JpegExifEmbedder embedder = new JpegExifEmbedder();
// setSlots must be called before embed or extract; use Slot.defaults() for standard EXIF slots.
embedder.setSlots(Slot.defaults());

// Embed: Path carrier, InputStream payload → OutputStream receives output JPEG
try (InputStream payload = new ByteArrayInputStream(
         "secret message".getBytes(StandardCharsets.UTF_8));
     OutputStream out = Files.newOutputStream(Path.of("output.jpg"))) {
    embedder.embed(Path.of("carrier.jpg"), payload, out);
}

// Extract: Path source, OutputStream receives recovered payload bytes
try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
    embedder.extract(Path.of("output.jpg"), buf);
    byte[] recovered = buf.toByteArray();
}

The payload must not exceed 1 MiB. embed and extract throw IOException on format errors or capacity overflow.

Textual codec

Package: zeroecho.ext.integrations.covert

TextualCodec.Generator

Generates pseudo-random text that matches a configurable character-frequency distribution. This is useful for producing natural-looking cover text when hiding data in text media.

The Generator is initialised with a Map<Character, Double> that maps characters to their relative frequency (e.g. English letter frequencies). getText(int length) produces a string of length characters drawn from that distribution.

Not thread-safe — create one Generator per thread

Generator carries mutable state. The built-in ENGLISH constant and the convenience field EN are not safe for concurrent use. Always create a new instance per thread or confine access.

import zeroecho.ext.integrations.covert.TextualCodec;
import java.util.Map;

// Built-in English distribution — factory creates a fresh (non-shared) instance
TextualCodec.Generator gen = TextualCodec.Generator.english();
String coverText = gen.getText(256);

// Custom distribution: Map<Character, Double> (character → relative frequency)
TextualCodec.Generator custom = new TextualCodec.Generator(
    Map.of('e', 12.7, 't', 9.1, 'a', 8.2, ' ', 25.4));
String cover = custom.getText(128);