# Install
cargo install lupin
# Or build from source: git clone && cargo build --releaseDownload pre-built binaries from the releases page.
Lupin automatically detects the file format and uses the appropriate steganography engine.
PDF files (appends data after %%EOF marker):
lupin embed source.pdf payload.txt output.pdfPNG files (uses custom ancillary chunks with zero visual artifacts):
lupin embed photo.png message.txt stego_photo.pngJPEG files (uses signed APP13 application markers, split across segments as needed, zero visual artifacts):
lupin embed photo.jpg message.txt stego_photo.jpglupin embed accepts a mode flag:
--capacity(default): unlimited payload size, but easy to spot withstringsor a hex dump. Used automatically if neither flag is passed.--stealth: reserved for a future strategy that resists casual detection. No format implements it yet — requesting it returns a clear error rather than silently falling back to capacity mode.
# Default: capacity mode
lupin embed document.pdf secret.txt output.pdf
# Stealth mode (currently returns "Stealth mode is not yet supported")
lupin embed document.pdf secret.txt output.pdf --stealthExtraction never takes a mode flag — lupin extract detects the payload automatically.
Extract hidden payload
# Extract to a file
lupin extract output.pdf payload.txt
lupin extract stego_photo.png message.txt
lupin extract stego_photo.jpg message.txt
# Extract to stdout (useful for piping)
lupin extract output.pdf -Lupin provides flexible logging and output control:
lupin --log-level debug embed source.pdf payload.txt output.pdf # Detailed debug info
lupin --log-level info embed source.pdf payload.txt output.pdf # Normal operation info
lupin --log-level warn embed source.pdf payload.txt output.pdf # Warnings only
lupin --log-level error embed source.pdf payload.txt output.pdf # Errors onlylupin --verbose embed source.pdf payload.txt output.pdf # Same as --log-level debug
lupin --quiet embed source.pdf payload.txt output.pdf # Same as --log-level errorNote: Explicit --log-level takes precedence over --verbose/--quiet flags. If you use both, you'll see a warning.
Each line is prefixed with a timestamp, thread ID, and module target (e.g. 20:27:37 [DEBUG] (1) lupin: ...); the examples below omit that prefix for readability.
Verbose mode:
lupin --verbose embed document.pdf secret.txt output.pdf
# Output:
# [DEBUG] Verbose mode enabled
# [DEBUG] Running command: embed
# [DEBUG] Source: document.pdf, Payload: secret.txt, Output: output.pdf
# [DEBUG] Using PDF engine
# [INFO] Embedded payload into 234.5 KiB source → 235.8 KiB output (+1%)Normal mode:
lupin embed document.pdf secret.txt output.pdf
# Output:
# [INFO] Embedded payload into 234.5 KiB source → 235.8 KiB output (+1%)Quiet mode:
lupin --quiet embed document.pdf secret.txt output.pdf
# Output: (none, unless there's an error)Text message:
echo "Meet me at the park at 5 pm" > secret.txt
lupin embed document.pdf secret.txt innocent_looking.pdfImage:
# Hide an image in a PDF
lupin embed report.pdf vacation_photo.jpg boring_report.pdf
# Or hide data in an image itself
lupin embed cover.png hidden.jpg stego_cover.pngArchive:
# Create a zip archive
zip -r secrets.zip confidential_folder/
# Embed the archive
lupin embed presentation.pdf secrets.zip presentation_with_secrets.pdfExtract to file:
lupin extract presentation_with_secrets.pdf extracted_secrets.zipExtract and pipe to another command:
lupin extract hidden_data.pdf - | file -Note: piping straight into unzip will not work, since unzip needs to seek to the end of the archive to read its central directory and can't read a zip file from a pipe. Extract to a file first:
lupin extract presentation_with_secrets.pdf secrets.zip
unzip secrets.zip