Cybersecurity-Projects/PROJECTS/beginner/steganography-multi-tool/DEMO.md

9.0 KiB

crypha demo

A hands-on tour of every carrier, end to end, with the real output crypha produces. Nothing here is faked; each block is a command you can run and the result it gives back.

Two things to know before you start:

  • crypha writes the recovered payload to stdout and a one-line status to stderr. On a terminal they run together, because the payload has no trailing newline. Redirect with -o out.bin, or use --json, when you want them cleanly separated.
  • A passphrase from any source (-k, CRYPHA_PASSPHRASE, or the interactive prompt) always encrypts. crypha never writes plaintext when you asked for a key.

Sample covers

Any files work. To reproduce this page exactly, make a throwaway set with tools you probably already have:

convert -size 640x480 gradient:navy-white -depth 8 PNG24:cover.png   # an 8-bit truecolor image
ffmpeg -f lavfi -i "sine=frequency=440:duration=2" \
       -ac 1 -c:a pcm_s16le cover.wav                        # 2 seconds of mono audio
ffmpeg -i cover.wav cover.flac                               # the same audio, as FLAC
convert -size 612x792 xc:white cover.pdf                     # a one-page PDF
printf 'The quick brown fox jumps over the lazy dog.' > cover.txt
printf 'https://angelamos.com/crypha' > qr.txt               # the QR's visible content

The image carrier wants an 8-bit truecolor PNG (or a 24-bit BMP); the -depth 8 PNG24: above forces that, since ImageMagick otherwise defaults to a 16-bit PNG, which crypha refuses.

What can it hide, and where

$ crypha formats
FORMAT  COVER                                  OUTPUT  OPTIONS                         DESCRIPTION
audio   16-bit PCM WAV or FLAC                 WAV     -                               LSB of 16-bit PCM samples
image   PNG or 24-bit BMP                      PNG     -                               LSB of RGB pixel data
pdf     PDF                                    PDF     attachment | metadata | append  embedded attachment, metadata, or append-after-EOF
qr      UTF-8 text (the QR's visible content)  PNG     -                               Reed-Solomon-correctable error injection
text    any UTF-8 text                         text    -                               zero-width U+200B and U+2060 characters

capacity tells you how much a specific cover will hold, plaintext and encrypted:

$ crypha capacity -i cover.png --format image
FORMAT  ENVELOPE  MAX PLAINTEXT  MAX ENCRYPTED  NOTE
image   115196    115182         115128

$ crypha capacity -i qr.txt --format qr
FORMAT  ENVELOPE  MAX PLAINTEXT  MAX ENCRYPTED  NOTE
qr      52        38             does not fit

Point capacity at a cover with no --format and it reports every carrier at once, flagging the ones the cover cannot serve:

$ crypha capacity -i cover.png
FORMAT  ENVELOPE   MAX PLAINTEXT  MAX ENCRYPTED  NOTE
audio   n/a        n/a            n/a            cover must be a 16-bit PCM WAV or a FLAC file
image   115196     115182         115128
pdf     n/a        n/a            n/a            cover must be a PDF
qr      0          0              does not fit
text    unbounded  unbounded      unbounded

image: LSB of RGB pixels

$ crypha hide --format image -i cover.png -o stego.png -m "The treasure is buried under the third oak past the old mill."
OUTPUT      stego.png
FORMAT      image
PAYLOAD     61 bytes
ENVELOPE    75 bytes
ENCRYPTED   no
COMPRESSED  no

reveal needs no --format; it detects the carrier itself:

$ crypha reveal stego.png
The treasure is buried under the third oak past the old mill.
revealed 61 bytes via image -> (stdout)

stego.png is pixel-for-pixel indistinguishable to the eye. The 61-byte message became a 75-byte envelope (a plaintext payload adds 14 bytes of framing) and rode in the low bit of the RGB channels.

text: zero-width characters

$ crypha hide --format text -i cover.txt -o stego.txt -m "Zero-width characters are invisible to the eye."
OUTPUT      stego.txt
FORMAT      text
PAYLOAD     47 bytes
ENVELOPE    61 bytes
ENCRYPTED   no
COMPRESSED  no

The stego file reads identically to the cover, but the byte count gives it away: the cover was 44 bytes and stego.txt is far larger, because the envelope was appended as invisible U+200B and U+2060 runes.

$ crypha reveal stego.txt
Zero-width characters are invisible to the eye.
revealed 47 bytes via text -> (stdout)

audio: LSB of PCM samples

$ crypha hide --format audio -i cover.wav -o stego.wav -m "Frequencies carry more than music."
OUTPUT      stego.wav
FORMAT      audio
PAYLOAD     34 bytes
ENVELOPE    48 bytes
ENCRYPTED   no
COMPRESSED  no

$ crypha reveal stego.wav
Frequencies carry more than music.
revealed 34 bytes via audio -> (stdout)

The tone sounds the same; the payload lives in the low bit of each 16-bit sample. Hand it a FLAC cover and crypha decodes it, embeds, and writes the result back as a standard WAV:

$ crypha hide --format audio -i cover.flac -o from-flac.wav -m "Decoded from FLAC, embedded, written back as WAV."
OUTPUT      from-flac.wav
FORMAT      audio
PAYLOAD     49 bytes
ENVELOPE    63 bytes
ENCRYPTED   no
COMPRESSED  no

$ crypha reveal from-flac.wav
Decoded from FLAC, embedded, written back as WAV.
revealed 49 bytes via audio -> (stdout)

qr: Reed-Solomon error injection

This is the showpiece. The payload is injected as errors into the QR's data codewords, inside the Reed-Solomon correction budget. An ordinary scanner reads the visible content and silently self-heals the injected errors away; crypha reads the errors back as the hidden bytes.

$ crypha hide --format qr -i qr.txt -o stego-qr.png -m "meet me at the docks, midnight"
OUTPUT      stego-qr.png
FORMAT      qr
PAYLOAD     30 bytes
ENVELOPE    44 bytes
ENCRYPTED   no
COMPRESSED  no

Scan stego-qr.png with any phone and you get https://angelamos.com/crypha. Point crypha at it and you get the secret. Auto-detect has to be careful here, because a QR stego is a PNG: it must not be mistaken for an ordinary image carrier. It is not.

$ crypha reveal stego-qr.png
meet me at the docks, midnight
revealed 30 bytes via qr -> (stdout)

The 30-byte message fit in the 38-byte plaintext budget of this cover. An encrypted envelope (68 bytes of overhead) would not, which is exactly what capacity warned above.

pdf: attachment, metadata, or append

The default technique embeds the envelope as a lossless file attachment:

$ crypha hide --format pdf -i cover.pdf -o stego.pdf -m "Attached, not appended. Look inside the file."
OUTPUT      stego.pdf
FORMAT      pdf
PAYLOAD     45 bytes
ENVELOPE    59 bytes
ENCRYPTED   no
COMPRESSED  no

$ crypha reveal stego.pdf
Attached, not appended. Look inside the file.
revealed 45 bytes via pdf -> (stdout)

--technique append writes the envelope after the %%EOF marker instead, where it survives naive copies:

$ crypha hide --format pdf --technique append -i cover.pdf -o stego-append.pdf -m "This rides after the EOF marker."
OUTPUT      stego-append.pdf
FORMAT      pdf (append)
PAYLOAD     32 bytes
ENVELOPE    46 bytes
ENCRYPTED   no
COMPRESSED  no

reveal tries every technique, so you never have to remember which one you used.

Encryption, end to end

Set a passphrase, ask to encrypt, and (optionally) compress. Here the passphrase comes from the environment so it never lands in your shell history:

$ export CRYPHA_PASSPHRASE="correct horse battery staple"
$ crypha hide --format image -i cover.png -o enc.png -m "Only the passphrase opens this." --encrypt --compress
OUTPUT      enc.png
FORMAT      image
PAYLOAD     31 bytes
ENVELOPE    105 bytes
ENCRYPTED   yes
COMPRESSED  yes

With the right passphrase it comes straight back:

$ crypha reveal enc.png
Only the passphrase opens this.
revealed 31 bytes via image -> (stdout)

With the wrong one, it fails closed. The header is authenticated, so a bad key or a tampered byte fails to open rather than returning garbage:

$ CRYPHA_PASSPHRASE="wrong" crypha reveal enc.png
crypha: decryption failed (wrong passphrase or tampered data)
$ echo $?
1

JSON for scripts

Every command takes a global --json. reveal --json base64-encodes the payload so binary is safe to pipe:

$ crypha hide --format image -i cover.png -o j.png -m "json demo" --json
{
  "format": "image",
  "payload_bytes": 9,
  "envelope_bytes": 23,
  "encrypted": false,
  "compressed": false,
  "output": "j.png"
}

$ crypha reveal j.png --json
{
  "format": "image",
  "bytes": 9,
  "encrypted": false,
  "data": "anNvbiBkZW1v"
}

The interactive wizard

Run crypha with no arguments in a terminal and it launches the bubbletea wizard: pick an operation, choose a format, browse to a cover with the file picker, type your message or select a payload file, set the secure options, and watch a live capacity meter fill as it checks the fit before embedding. It is the same engine as the CLI, so anything above works there too, with nothing to memorize.

crypha