@pinta365/steganography
v0.3.2
Published
A steganography library supporting image and text steganography with LSB embedding, JPEG DCT coefficients, and zero-width character encoding.
Maintainers
Readme
@pinta365/steganography
A steganography library supporting image and text steganography.
Features
Image Steganography
- LSB (Least Significant Bit) embedding for lossless formats
- JPEG DCT coefficient-domain embedding
- File embedding with metadata support
Text Steganography
- Zero-Width Character (ZWC) encoding
- Optional AES-256-CTR encryption
- Compression support
Installation
JSR (Recommended)
# Deno
deno add jsr:@pinta365/steganography
# pnpm
pnpm i jsr:@pinta365/steganography
# Bun
bunx jsr add @pinta365/steganography
# Node.js (npx)
npx jsr add @pinta365/steganographyOr import directly:
import { decodeText, encodeText } from "jsr:@pinta365/[email protected]";npm / Node.js / Bun
npm install @pinta365/steganographyOr with other package managers:
# yarn
yarn add @pinta365/steganography
# pnpm
pnpm add @pinta365/steganography
# bun
bun add @pinta365/steganographyNote: This library is runtime-agnostic and works on Deno, Node.js (18+), Bun, and browsers.
Usage
Text Steganography
import { decodeText, encodeText } from "@pinta365/steganography";
// Hide text in text
const coverText = "# Project README\n\nInstallation guide...";
const secret = "API key: sk_live_1234567890";
const stegaText = await encodeText(coverText, secret, "password", true);
const { visibleText, secretMessage } = await decodeText(stegaText, "password");Image Steganography
import { createImage, decodeImage, embedTextInImage, encodeImage, extractTextFromImage } from "@pinta365/steganography";
// Load image
const imageData = await Deno.readFile("image.png");
const image = await decodeImage(imageData);
// Embed message
const message = "Hidden secret message";
const modifiedData = embedTextInImage(image.data, message);
// Save image with hidden data
const stegaImage = createImage(image.width, image.height, modifiedData);
const output = await encodeImage(stegaImage, "png");
await Deno.writeFile("stega.png", output);
// Extract message
const loadedImage = await decodeImage(await Deno.readFile("stega.png"));
const extractedMessage = extractTextFromImage(loadedImage.data);
console.log(extractedMessage); // "Hidden secret message"Animated Image Steganography
import { decodeImageFrames, embedTextInImageFrames, encodeImageFrames, extractTextFromImageFrames } from "@pinta365/steganography";
// Load animated GIF
const gifData = await Deno.readFile("animated.gif");
const multiFrame = await decodeImageFrames(gifData, "gif");
// Embed message in first frame
const message = "Hidden message";
const stegaMultiFrame = embedTextInImageFrames(multiFrame, message, 1, "first");
// Save as GIF
const stegaGifData = await encodeImageFrames("gif", stegaMultiFrame);
await Deno.writeFile("stega.gif", stegaGifData);
// Extract message
const decoded = await decodeImageFrames(stegaGifData, "gif");
const extracted = extractTextFromImageFrames(decoded);
console.log(extracted); // "Hidden message"Embedding modes:
"first"- Embed only in the first frame (recommended for most cases)"all"- Embed the same message in all frames"split"- Distribute message across frames (useful for large messages)
## Capacity and Configuration
### Capacity Calculation
The library automatically calculates capacity based on your cover media:
- **Text Steganography**: Capacity depends on cover text length. Each byte requires ~4 zero-width characters. Use `calculateTextCapacity(coverText)`
to check capacity.
- **Image Steganography**: Capacity depends on image dimensions and bit depth. Use `calculateBitCapacity(width, height, bitDepth)` to check capacity.
### Configurable Limits
All encoding functions support optional configuration via options:
**Text Functions** (`encodeText`, `encodeBinary`):
```typescript
interface EncodeOptions {
maxPayloadBytes?: number; // Override calculated capacity
maxSecretLength?: number; // Max secret size (default: 50KB)
maxCoverLength?: number; // Max cover text size (default: 100KB)
strictCapacity?: boolean; // Throw error vs warn (default: true)
}Image Functions (embedTextInImage, embedDataInImage):
interface ImageEncodeOptions {
maxPayloadBytes?: number; // Override calculated capacity
maxMessageLength?: number; // Max message size (default: 10MB)
strictCapacity?: boolean; // Throw error vs warn (default: true)
}Capacity Warnings
By default, the library will throw errors if payload exceeds capacity. Set strictCapacity: false to get warnings instead:
// Warns but continues encoding
const stegaText = await encodeText(cover, secret, undefined, false, {
strictCapacity: false,
});Examples
// Check capacity before encoding
const capacity = calculateTextCapacity(coverText);
console.log(`Can hide up to ${capacity} bytes`);
// Override capacity limit
const stegaText = await encodeText(coverText, secret, undefined, false, {
maxPayloadBytes: 10000, // Allow up to 10KB
maxSecretLength: 20000, // Allow up to 20KB secret
});
// Use calculated capacity with warnings
const stegaImage = embedTextInImage(image.data, message, 1, {
strictCapacity: false, // Warn instead of error
});API Reference
Types
StegaText
Branded type for steganographic text containing hidden ZWC data. Returned by encoding functions and accepted by decoding functions for type safety.
type StegaText = string & { readonly __brand: "StegaText" };Image
Image type from @cross/image, re-exported for convenience. Use for type annotations when working with image helper functions.
import { decodeImage, Image } from "@pinta365/steganography";
const image: Image = await decodeImage(imageData);Text Steganography
encodeText(coverText, secretMessage, password?, distribute?)
Encodes a secret text message into cover text using zero-width characters.
- coverText (
string): Visible text that will contain the hidden message - secretMessage (
string): Secret text to hide - password (
string?): Optional password for AES-256-CTR encryption - distribute (
boolean?): Iftrue, distributes ZWC characters throughout text (default:false) - Returns:
Promise<StegaText>- Cover text with invisible ZWC payload embedded
decodeText(stegaText, password?)
Decodes a hidden text message from text with hidden data.
- stegaText (
string | StegaText): Text containing hidden ZWC data - password (
string?): Password if the message was encrypted - Returns:
Promise<{ visibleText: string; secretMessage: string | null }>
encodeBinary(coverText, binaryData, password?, distribute?)
Encodes binary data (images, files) into cover text using zero-width characters.
- coverText (
string): Visible text that will contain the hidden data - binaryData (
Uint8Array): Binary data to hide - password (
string?): Optional password for AES-256-CTR encryption - distribute (
boolean?): Iftrue, distributes ZWC characters throughout text (default:false) - Returns:
Promise<StegaText>- Cover text with invisible ZWC payload embedded
decodeBinary(stegaText, password?)
Decodes binary data from text with hidden data.
- stegaText (
string | StegaText): Text containing hidden ZWC data - password (
string?): Password if the data was encrypted - Returns:
Promise<{ visibleText: string; binaryData: Uint8Array | null }>
decode(stegaText, password?)
Unified decode function that auto-detects payload type (text or binary).
- stegaText (
string | StegaText): Text containing hidden ZWC data - password (
string?): Password if the data was encrypted - Returns:
Promise<{ visibleText: string; payloadType: "text" | "binary" | null; textData: string | null; binaryData: Uint8Array | null }>
hasHiddenData(text)
Checks if text contains hidden ZWC steganography data.
- text (
string | StegaText): Text to check - Returns:
boolean
analyzeZWC(text)
Returns statistics about hidden data in text.
- text (
string): Text to analyze - Returns:
ZWCStats- Object withhasHiddenData,visibleLength,zwcCount,estimatedPayloadBytes,breakdown
stripZWC(text)
Removes all ZWC characters from text (removes any hidden data).
- text (
string): Text to clean - Returns:
string
Image Utilities
decodeImage(imageData)
Decodes image file data into an Image object.
- imageData (
Uint8Array): Image file data (PNG, JPEG, WebP, etc.) - Returns:
Promise<Image>- Image object with width, height, and RGBA data
encodeImage(image, format, options?)
Encodes an Image object to file data.
- image (
Image): Image object - format (
string): Output format (png, jpeg, webp, etc.) - options (
ImageFormatEncodeOptions?): Optional encoding options (format-specific). See @cross/image documentation for format-specific option types:- PNG/APNG:
PNGEncoderOptions(compressionLevel: 0-9, default: 6) - WebP:
WebPEncoderOptions(quality: 1-100, lossless: boolean, default: quality: 90) - TIFF:
TIFFEncoderOptions(compression: "none" | "lzw" | "packbits" | "deflate", default: "lzw") - Other formats: See @cross/image types
- PNG/APNG:
- Returns:
Promise<Uint8Array>- Encoded image file data
createImage(width, height, data)
Creates a new Image object.
- width (
number): Image width in pixels - height (
number): Image height in pixels - data (
Uint8Array): RGBA image data - Returns:
Image- Image object
Image Steganography (LSB)
LSB (Least Significant Bit) steganography embeds data in the least significant bits of image pixels. Works with lossless formats (PNG, WebP lossless, BMP, etc.). Lossy formats (JPEG) will destroy hidden data on re-encoding.
embedTextInImage(imageData, message, bitDepth?, options?)
Embeds a text message into image data using LSB (Least Significant Bit).
- imageData (
Uint8Array): RGBA image data - message (
string): Text message to embed - bitDepth (
number?): Bits per pixel (1-4, default:1) - options (
ImageEncodeOptions?): Optional encoding options (capacity limits, strict mode) - Returns:
Uint8Array- Modified image data
extractTextFromImage(imageData, bitDepth?)
Extracts a text message from image data using LSB. The message length is automatically read from the embedded header.
- imageData (
Uint8Array): RGBA image data - bitDepth (
number?): Bits per pixel (1-4, default:1) - Returns:
string- Extracted text message
embedDataInImage(imageData, data, bitDepth?, options?)
Embeds binary data into image data using LSB.
- imageData (
Uint8Array): RGBA image data - data (
Uint8Array): Binary data to embed - bitDepth (
number?): Bits per pixel (1-4, default:1) - options (
ImageEncodeOptions?): Optional encoding options (capacity limits, strict mode) - Returns:
Uint8Array- Modified image data
extractDataFromImage(imageData, dataLength, bitDepth?)
Extracts binary data from image data using LSB.
- imageData (
Uint8Array): RGBA image data - dataLength (
number): Length of the data in bytes - bitDepth (
number?): Bits per pixel (1-4, default:1) - Returns:
Uint8Array- Extracted binary data
embedLSB(imageData, messageBits, bitDepth?)
Low-level function to embed bits into image pixels.
- imageData (
Uint8Array): RGBA image data - messageBits (
Uint8Array): Bits to embed - bitDepth (
number?): Bits per pixel (1-4, default:1) - Returns:
Uint8Array- Modified image data
extractLSB(imageData, bitCount, bitDepth?)
Low-level function to extract bits from image pixels.
- imageData (
Uint8Array): RGBA image data - bitCount (
number): Number of bits to extract - bitDepth (
number?): Bits per pixel (1-4, default:1) - Returns:
Uint8Array- Extracted bits
calculateBitCapacity(width, height, bitDepth?)
Calculates the bit capacity of an image.
- width (
number): Image width in pixels - height (
number): Image height in pixels - bitDepth (
number?): Bits per pixel (1-4, default:1) - Returns:
number- Number of bytes that can be hidden
generateLSBStats(imageData, originalData?)
Generates LSB statistics for display.
- imageData (
Uint8Array): RGBA image data - originalData (
Uint8Array?): Original image data for comparison - Returns: Object with LSB statistics per channel (red, green, blue, total)
JPEG Coefficient Steganography
embedDataInJpegCoefficients(coefficients, data, useChroma?)
Embeds binary data into JPEG coefficients using DCT coefficient-domain embedding.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients (will be modified) - data (
Uint8Array): Binary data to embed - useChroma (
boolean?): Use chroma components (default:true) - Returns:
JPEGQuantizedCoefficients- Modified coefficients
extractDataFromJpegCoefficients(coefficients, maxBytes, useChroma?)
Extracts binary data from JPEG coefficients.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients - maxBytes (
number): Maximum number of bytes to extract - useChroma (
boolean?): Extract from chroma components (default:true) - Returns:
Uint8Array- Extracted binary data
embedInCoefficients(coefficients, messageBits, useChroma?)
Low-level function to embed bits into JPEG coefficients.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients (will be modified) - messageBits (
Uint8Array): Bits to embed - useChroma (
boolean?): Use chroma components (default:true) - Returns:
JPEGQuantizedCoefficients- Modified coefficients
extractFromCoefficients(coefficients, bitCount, useChroma?)
Low-level function to extract bits from JPEG coefficients.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients - bitCount (
number): Number of bits to extract - useChroma (
boolean?): Extract from chroma components (default:true) - Returns:
Uint8Array- Extracted bits
calculateJpegCoefficientCapacity(coefficients, useChroma?)
Calculates the embedding capacity of JPEG coefficients.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients - useChroma (
boolean?): Include chroma components (default:true) - Returns:
number- Number of bytes that can be hidden
extractJpegCoefficients(jpegData)
Extracts JPEG quantized coefficients from JPEG data.
- jpegData (
Uint8Array): JPEG file data - Returns:
Promise<JPEGQuantizedCoefficients | null>
encodeJpegFromCoefficients(coefficients)
Encodes JPEG from quantized coefficients.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients - Returns:
Promise<Uint8Array>- JPEG file data
cloneJpegCoefficients(coefficients)
Deep clones JPEG coefficients to avoid modifying the original.
- coefficients (
JPEGQuantizedCoefficients): JPEG quantized coefficients - Returns:
JPEGQuantizedCoefficients- Cloned coefficients
Utility Functions
bytesToBits(bytes)
Converts a byte array to a bit array (LSB first).
- bytes (
Uint8Array): Bytes to convert - Returns:
Uint8Array- Bit array
bitsToBytes(bits)
Converts a bit array back to bytes (LSB first).
- bits (
Uint8Array): Bits to convert - Returns:
Uint8Array- Byte array
xorEncrypt(data, password)
XOR encrypts data using a cyclic password key.
- data (
Uint8Array): Data to encrypt - password (
string): Password key - Returns:
Uint8Array- Encrypted data
xorDecrypt(data, password)
XOR decrypts data (XOR is its own inverse).
- data (
Uint8Array): Data to decrypt - password (
string): Password key - Returns:
Uint8Array- Decrypted data
detectImageFormat(data)
Detects image format from file data.
- data (
Uint8Array): Image file data - Returns:
string | null- Format name or null if unknown
isLossyFormat(format)
Checks if a format is lossy (will destroy pixel-domain embedding data on re-encoding).
- format (
string | null): Format name - Returns:
boolean
getRecommendedOutputFormat(inputFormat)
Gets a recommended output format based on input format.
- inputFormat (
string | null): Input format name - Returns: Object with
format,reason, and optionaluseWebP
License
MIT
