docs(crypha): add README and DEMO in the house style (M9)
This commit is contained in:
parent
3f92f93e77
commit
b705f1ddd1
|
|
@ -0,0 +1,257 @@
|
|||
<!-- ©AngelaMos | 2026 -->
|
||||
<!-- DEMO.md -->
|
||||
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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
|
||||
|
||||
```console
|
||||
$ 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.
|
||||
|
||||
```console
|
||||
$ crypha reveal stego.txt
|
||||
Zero-width characters are invisible to the eye.
|
||||
revealed 47 bytes via text -> (stdout)
|
||||
```
|
||||
|
||||
## audio: LSB of PCM samples
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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.
|
||||
|
||||
```console
|
||||
$ 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.
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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:
|
||||
|
||||
```console
|
||||
$ 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.
|
||||
|
||||
```bash
|
||||
crypha
|
||||
```
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
<!-- ©AngelaMos | 2026 -->
|
||||
<!-- README.md -->
|
||||
|
||||
```json
|
||||
██████╗██████╗ ██╗ ██╗██████╗ ██╗ ██╗ █████╗
|
||||
██╔════╝██╔══██╗╚██╗ ██╔╝██╔══██╗██║ ██║██╔══██╗
|
||||
██║ ██████╔╝ ╚████╔╝ ██████╔╝███████║███████║
|
||||
██║ ██╔══██╗ ╚██╔╝ ██╔═══╝ ██╔══██║██╔══██║
|
||||
╚██████╗██║ ██║ ██║ ██║ ██║ ██║██║ ██║
|
||||
╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝
|
||||
```
|
||||
|
||||
[](https://github.com/CarterPerez-dev/Cybersecurity-Projects/tree/main/PROJECTS/beginner/steganography-multi-tool)
|
||||
[](https://go.dev)
|
||||
[](https://go.dev)
|
||||
[](#the-five-carriers)
|
||||
[](https://datatracker.ietf.org/doc/html/rfc8439)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0)
|
||||
|
||||
> A multi-format steganography tool in Go. It takes a message or a file, seals it in a passphrase-encrypted, compressed, integrity-checked envelope, and hides that envelope inside an ordinary-looking carrier: the low bits of an image or an audio file, the Reed-Solomon slack of a QR code, zero-width characters in a block of text, or the structure of a PDF. Point `reveal` at the result and it auto-detects the carrier and hands the message back. It ships as a single static, dependency-free binary and drives from either a scriptable CLI or a guided terminal wizard.
|
||||
|
||||
## Why hide an encrypted message
|
||||
|
||||
Cryptography and steganography answer two different questions. Encryption makes a message unreadable; steganography makes it unnoticeable. Encryption on its own still announces that a secret exists, and an opaque blob is itself a signal, exactly the thing a data-loss-prevention scanner, an intrusion-detection rule, or a border inspection is trained to flag. Steganography removes the signal: the carrier looks like a holiday photo, a voice memo, a PDF invoice, or a QR code on a poster.
|
||||
|
||||
The technique is not academic. In 2022 the Witchetty espionage group concealed a backdoor inside a bitmap of an old Windows logo hosted on a public cloud service, so the payload arrived looking like an image download rather than malware (Symantec). The Stegano exploit kit hid malicious code in the alpha channel of PNG banner ads served to millions of visitors (ESET, 2016). The open-source Invoke-PSImage tool packs a PowerShell script into the pixels of a PNG, and the Stegoloader/Gatak family pulled its own components out of images fetched at runtime to keep obvious code off disk (Dell SecureWorks, 2015). Defenders answer with steganalysis: the statistical hunt for the faint fingerprint that embedding leaves behind.
|
||||
|
||||
crypha exists to teach both sides of that exchange honestly. It encrypts first, so a discovered payload is still unreadable, then hides the ciphertext across five very different carriers, each with its own capacity, fragility, and detection story. The `learn/` track walks the steganalysis that breaks each one.
|
||||
|
||||
## What it is
|
||||
|
||||
Not a stub. Every capability below is exercised by table-driven unit tests, round-trip tests over text and random-binary payloads, and, for the QR carrier, differential tests against reference encoders and decoders.
|
||||
|
||||
**The encrypted envelope (every payload, every carrier)**
|
||||
- A passphrase-derived key via Argon2id (RFC 9106): the 64 MiB default profile, or a 2 GiB profile under `--strength high`
|
||||
- Authenticated encryption with ChaCha20-Poly1305 by default (constant-time in software on any CPU), or AES-256-GCM behind `--cipher aes256gcm`
|
||||
- Optional DEFLATE compression before encryption, a CRC32 integrity check, and the header bound in as authenticated associated data, so a single flipped byte fails to open rather than decrypting to garbage
|
||||
- A passphrase from any source, the `-k` flag, the `CRYPHA_PASSPHRASE` environment variable, or a no-echo terminal prompt, always means the payload is encrypted; crypha never silently writes plaintext when you asked for a key
|
||||
|
||||
**Five carriers behind one interface**
|
||||
- **image**: LSB of RGB in PNG or 24-bit BMP, via the mandatory NRGBA conversion that stops `png.Encode`'s alpha pass from corrupting the low bits; paletted and 16-bit covers are refused rather than silently mangled
|
||||
- **audio**: LSB of 16-bit PCM samples in WAV; a FLAC cover is decoded, embedded, and re-emitted as WAV
|
||||
- **qr**: the payload is injected as Reed-Solomon-correctable errors into a QR code's data codewords, so an ordinary scanner silently self-heals to the visible content while crypha reads the injected bytes back. The placement, masking, and block de-interleave are reimplemented from ISO/IEC 18004
|
||||
- **text**: zero-width U+200B and U+2060 characters appended to any UTF-8 cover; the stego text is visually identical to the original and survives Unicode normalization
|
||||
- **pdf**: a lossless embedded-file attachment by default, or `--technique metadata` / `--technique append`
|
||||
|
||||
**Two frontends over one engine**
|
||||
- A scriptable cobra CLI: `hide`, `reveal`, `capacity`, `formats`, with a global `--json` for machine consumption
|
||||
- A guided bubbletea terminal wizard on bare `crypha`, at full parity with the CLI, that walks operation to format to files to options and shows a live capacity meter before it embeds
|
||||
- `reveal` with no `--format` auto-detects the carrier by trying each one and validating the envelope, so a QR-PNG is never mistaken for an ordinary image
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
curl -fsSL https://angelamos.com/crypha/install.sh | bash
|
||||
```
|
||||
|
||||
One command, zero further steps: it grabs a prebuilt binary for your platform (no Go toolchain needed), drops it on your `PATH`, and leaves `crypha` runnable by name. Then hide your first message:
|
||||
|
||||
```bash
|
||||
crypha # launch the guided wizard
|
||||
crypha capacity -i photo.png # how much can this cover hold?
|
||||
crypha hide -i photo.png -o secret.png --format image -m "meet at noon"
|
||||
crypha reveal secret.png # auto-detects image, prints the message
|
||||
```
|
||||
|
||||
Add a passphrase and `reveal` will ask for it before it decrypts:
|
||||
|
||||
```bash
|
||||
crypha hide -i photo.png -o secret.png --format image -m "coordinates inside" --encrypt --compress
|
||||
crypha reveal secret.png # prompts for the passphrase, then decrypts
|
||||
```
|
||||
|
||||
Prefer the Go toolchain? `go install github.com/CarterPerez-dev/crypha/cmd/crypha@latest` works too, and `just build` builds from a checkout. Building from source needs Go 1.25+, fetched automatically if you are on an older Go.
|
||||
|
||||
> [!TIP]
|
||||
> This project uses [`just`](https://github.com/casey/just) as a command runner. Type `just` to see every recipe.
|
||||
>
|
||||
> Install: `curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin`
|
||||
|
||||
## The five carriers
|
||||
|
||||
| Format | Technique | Cover to output | Capacity (envelope bytes) | Notes |
|
||||
|--------|-----------|-----------------|---------------------------|-------|
|
||||
| image | LSB of RGB pixels | PNG / 24-bit BMP to PNG | `width x height x 3 / 8` minus a 4-byte prefix (640x480 holds **115,196**) | alpha untouched; paletted and 16-bit covers refused |
|
||||
| audio | LSB of 16-bit PCM | WAV / FLAC to WAV | `samples x channels / 8` (2s of 44.1 kHz mono holds **11,021**) | FLAC is decoded and re-emitted as WAV |
|
||||
| text | zero-width U+200B / U+2060 | any UTF-8 to UTF-8 | effectively **unbounded** | stego text is visually identical; survives NFC/NFKC |
|
||||
| pdf | attachment / metadata / append | PDF to PDF | effectively **unbounded** | attachment is lossless; append rides after `%%EOF` |
|
||||
| qr | Reed-Solomon error injection | UTF-8 text to PNG | tens of bytes (a 28-char cover holds **52**) | too small for an encrypted envelope; plaintext only |
|
||||
|
||||
Run `crypha capacity -i <cover>` for the exact number on your file. The envelope adds **14 bytes** of framing to a plaintext payload and about **68 bytes** to an encrypted one, which is precisely why a 52-byte QR envelope has no room left for encryption.
|
||||
|
||||
## The encrypted envelope
|
||||
|
||||
Whatever the carrier, every payload is packed into one versioned envelope before it is hidden. The carrier only ever stores opaque bytes; all crypto, compression, integrity, and versioning live here.
|
||||
|
||||
```
|
||||
plaintext magic(4) ver(1) flags(1) │ len(4) body(N) │ crc32(4) +14 bytes
|
||||
|
||||
encrypted magic(4) ver(1) flags(1) cipher(1) params(9) salt(16) nonce(12) │ len(4) ciphertext+tag(N+16) │ crc32(4)
|
||||
└──────────────── authenticated as AEAD associated data ───────┘ +68 bytes
|
||||
```
|
||||
|
||||
The `params` field carries the Argon2id time, memory, and parallelism used, so `reveal` reproduces the exact key without you re-declaring the profile. Because the whole header up to the nonce is authenticated, tampering with the version, cipher choice, or KDF parameters fails `AEAD.Open` cleanly instead of decrypting to noise. An unknown version is rejected, not guessed. Compression runs before encryption, which is safe for an offline one-shot file tool: the CRIME/BREACH compression oracles need an adaptive network attacker, and there is none here.
|
||||
|
||||
## Honest limits
|
||||
|
||||
Steganography is a set of trade-offs, not magic. crypha is explicit about them.
|
||||
|
||||
- **QR holds tens of bytes.** The channel is the `floor((n - k) / 2)` correctable errors per Reed-Solomon block, which is real but small. An encrypted envelope does not fit, so QR is plaintext-only, and `capacity` says so.
|
||||
- **Image and audio LSB are fragile.** They survive a byte-for-byte copy, not re-encoding. Re-save the PNG as JPEG, or the WAV as MP3, and the payload is gone. This is a property of LSB steganography, not a bug.
|
||||
- **Zero-width text is easy to detect and strip.** It is invisible to a human reader, but trivially visible to any tool that looks for U+200B/U+2060. It is a teaching carrier for the technique, not a covert channel against a motivated inspector.
|
||||
- **FLAC is WAV-primary.** A FLAC cover is decoded and re-emitted as WAV, because the only Go FLAC encoder emits strict-parser-incompatible frames; native FLAC output is deferred by design.
|
||||
|
||||
## Architecture
|
||||
|
||||
One engine, two frontends. The engine is the brain; cobra and bubbletea are thin, interchangeable faces over it, and neither holds any carrier logic.
|
||||
|
||||
```
|
||||
cobra CLI ───┐
|
||||
├──> engine ──> carrier registry ──> image audio qr text pdf
|
||||
bubbletea TUI ──┘ │
|
||||
└──> payload envelope (Argon2id · AEAD · flate · CRC32) ──> bitio
|
||||
```
|
||||
|
||||
Each carrier is an isolated package implementing a single `Carrier` interface (`Hide`, `Reveal`, `Capacity`, `Sniff`) and self-registers through a blank import. `hide`, `reveal`, and `capacity` dispatch through the registry; auto-detect walks every carrier's `Sniff` and then confirms by validating the envelope it extracts, so a carrier that merely *could* read the bytes never shadows the one that actually owns them. If the TUI needs a value, it asks the engine for it (that is how the exact capacity meter works); it never reaches into a carrier.
|
||||
|
||||
## Build and Test
|
||||
|
||||
```bash
|
||||
just build # -> ./dist/crypha (or: go build -o dist/crypha ./cmd/crypha)
|
||||
just test # go test ./...
|
||||
just test-race # the race detector across the suite
|
||||
just lint # golangci-lint, pinned to a Go 1.25 toolchain
|
||||
```
|
||||
|
||||
Coverage is table-driven per carrier. Every carrier round-trips text and random-binary payloads and checks the exact capacity boundary: a payload at the limit succeeds and one byte over fails cleanly. The crypto path is known-answer tested (encrypt-decrypt returns the plaintext, a flipped ciphertext byte fails to open, and the echoed Argon2id parameters reproduce the key). The QR carrier is differentially tested against `skip2/go-qrcode` (its clean matrix must match) and `gozxing` (the stego image must still scan to the cover), so an injection can never exceed the correctable budget without a test noticing.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
steganography-multi-tool/
|
||||
├── cmd/crypha/ # tiny main: cli.Execute()
|
||||
├── internal/
|
||||
│ ├── cli/ # cobra: hide reveal capacity formats version tui + secure passphrase
|
||||
│ ├── tui/ # bubbletea wizard, a pure view over the engine
|
||||
│ ├── engine/ # the shared brain both frontends call
|
||||
│ ├── carrier/ # Carrier interface + Register/Get/All/Detect registry
|
||||
│ │ ├── all/ # blank-import aggregator that registers every carrier
|
||||
│ │ └── image/ audio/ qr/ text/ pdf/
|
||||
│ ├── payload/ # the encrypted envelope: Argon2id, AEAD, flate, CRC32, framing
|
||||
│ ├── bitio/ # MSB-first BitReader / BitWriter
|
||||
│ ├── config/ # every constant (magic, KDF params, format catalog)
|
||||
│ └── report/ # human tables and --json rendering
|
||||
├── learn/ # the teaching track (public)
|
||||
├── install.sh # the one-shot curl-able installer
|
||||
├── .goreleaser.yaml # cross-platform release binaries
|
||||
└── justfile # every recipe
|
||||
```
|
||||
|
||||
## Learn
|
||||
|
||||
This project ships a full teaching track. Read it in order, or jump to what you need.
|
||||
|
||||
| Doc | What it covers |
|
||||
|-----|----------------|
|
||||
| [`learn/00-OVERVIEW.md`](learn/00-OVERVIEW.md) | What the tool is, prerequisites, the project layout, and a quick tour |
|
||||
| [`learn/01-CONCEPTS.md`](learn/01-CONCEPTS.md) | Steganography vs cryptography, the LSB and zero-width channels, and steganalysis, grounded in real incidents |
|
||||
| [`learn/02-ARCHITECTURE.md`](learn/02-ARCHITECTURE.md) | The one-engine-two-frontends design, the carrier registry, and the envelope format |
|
||||
| [`learn/03-IMPLEMENTATION.md`](learn/03-IMPLEMENTATION.md) | A code walkthrough, with the QR-from-ISO-18004 Reed-Solomon injection as the showpiece |
|
||||
| [`learn/04-CHALLENGES.md`](learn/04-CHALLENGES.md) | Extension ideas, from a new carrier to 2-LSB density modes and stronger steganalysis resistance |
|
||||
|
||||
## License
|
||||
|
||||
[AGPL 3.0](LICENSE).
|
||||
Loading…
Reference in New Issue