Lupin

Hide data inside PDF, PNG, JPEG and MKV files.

Lupin is a Rust crate and command-line tool. The demo below runs the crate compiled to WebAssembly, so your files are never uploaded: they stay in this browser tab.

Try it

No file to hand? Try , , or . To see a file that already hides a message, try .

Use it from Rust

cargo add lupin --no-default-features
use lupin::operations::{embed, extract};
use lupin::EmbedMode;

let source = std::fs::read("document.pdf")?;
let payload = std::fs::read("secret.txt")?;

let (embedded, meta) =
    embed(&source, &payload, EmbedMode::Capacity)?;
let (extracted, _) = extract(&embedded)?;

Use it from the command line

cargo install lupin

lupin embed document.pdf secret.txt output.pdf
lupin extract output.pdf recovered.txt

Lupin picks the engine from the file's magic bytes. Files made here and with the CLI are interchangeable.

Where the data goes

Every engine currently favours capacity: payloads can be very large and the file renders or plays as before, but anyone who inspects its structure will find the hidden data. The API has a stealth mode, but no engine supports it yet.

Format Where Lupin writes Encoding What gives it away
PDF After the last %%EOF Base64 Non-whitespace bytes after the last %%EOF
PNG A private lpNg chunk before IEND Base64 An unknown chunk in a chunk listing
JPEG APP13 segments signed Lupin\0, each up to 64 KB, after the leading APPn segments Raw The signature in a segment listing
MKV, WebM A Void element signed Lupin\0 at the end of the Segment Raw A Void element that holds data instead of padding