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 |
|---|---|---|---|
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 |