stegtext
Text steganography command-line tool and library.
Links:
- stegtext Git Repository (codeberg.org)
- stegtext Package Page (crates.io)
- stegtext API Documentation (docs.rs)
How It Works
Unicode has many homoglyphs; that is, different characters with the same appearance.
This tool hides a secret value in plain text by selectively replacing characters with homoglyphs.
The encoded result is visually indistinguishable from the plain text.
The amount of secret data that you can hide depends on the embedding capacity (the number of characters with homoglyphs) of the plain text.
See the Technical Details section of the API documentation for additional details.
Example
Hide a secret message in plain text:
# show "input.txt"
$ cat input.txt
The Hitchhiker's Guide to the Galaxy is a 1979 science fiction comedy
novel by English author Douglas Adams, adapted from the first four parts
of his radio comedy series of the same name.
# hide secret, write to "hidden.txt"
$ stegtext encode 'hi there' < input.txt > hidden.txt
# show "hidden.txt"
$ cat hidden.txt
Тhe Нitchhiker'ѕ Guide to the Galaxy is a 1979 ѕcіеncе fictіon соmedy
novel by Englіsh author Douglаs Аdаmѕ, adapted frоm the fіrѕt four рartѕ
of hiѕ rаdio сomedу ѕеriеs оf the sаmе name.
# read secret from "hidden.txt"
$ stegtext decode < hidden.txt
hi there
Install
stegtext can be installed several ways:
- as a binary from the stegtext package on crates.io
- as a binary from the stegtext Git repository
- as a container
Install from Package
Install the stegtext package with cargo install:
Install from Git Repository
Install directly from the main branch of the Git
repository:
Install as Container
Install as a container with Podman:
# pull container image
Run container:
# container image url
IMAGE_URL=codeberg.org/pablotron/stegtext
# run ephemeral stegtext container, read text from "input.txt", hide
# secret "hi there", and write result to "hidden.txt"
Container image notes:
- less than 750k in size
- contains stripped, statically-linked binary and nothing else
- can run rootless and with no capabilities (e.g.
--cap-drop ALL) - signed by OpenPGP key ID
021136521548EB198F64FF738E182534CDD1F2B8
Build
Build release binary using cargo build:
# clone git repo
# build release binary
Build stegtext container image using Podman:
# clone git repo
# build image
Run previously-built stegtext container:
# run ephemeral stegtext container, read text from "input.txt", hide
# secret "hi there", and write result to "hidden.txt"
Usage
Available subcommands:
capacity: Print embedding capacity of input, in bytes.encode: Embed secret in text and then print result.decode: Extract secret from text and print it.help: Print usage.
Rust Library
stegtext can also be used as a Rust library. Here are a couple of examples.
Hide secret in text:
// secret
const SECRET: = *b"hello";
// input text
const TEXT: &str = "\
Тhe Нitсhhikеr's Guіdе to thе Galaxy is a 1979 ѕcіеncе fiсtion соmedy
novеl bу Englіѕh author Douglаѕ Adаmѕ, adарtеd frоm the fіrѕt four parts
of hiѕ radiо соmedу ѕеriеѕ оf thе sаmе name.";
// expected output
const EXP: &str = "\
Тhe Нitсhhikеr's Guide to the Galaxy is a 1979 ѕcіеncе fiсtion соmedy
noνеl by Еnglіsh author Dоuglаs Аdаms, аdарtеd from thе fіrst four parts
of hiѕ radiо соmedу ѕеriеѕ оf thе sаmе name.";
// encode secret, check result
assert_eq!;
Get secret from text:
// text with embedded secret
const TEXT: &str = "\
Тhe Нitсhhikеr's Guide to the Galaxy is a 1979 ѕcіеncе fiсtion соmedy
noνеl by Еnglіsh author Dоuglаs Аdаms, аdарtеd from thе fіrst four parts
of hiѕ radiо соmedу ѕеriеѕ оf thе sаmе name.";
// decode secret, check result
assert_eq!;
Get text embedding capacity:
// input text
const TEXT: &str = "\
Тhe Нitсhhikеr's Guіdе to thе Galaxy is a 1979 ѕcіеncе fiсtion соmedy
novеl bу Englіѕh author Douglаѕ Adаmѕ, adарtеd frоm the fіrѕt four parts
of hiѕ radiо соmedу ѕеriеѕ оf thе sаmе name. Іt centrеѕ оn the
misаdvеntureѕ оf Arthur Dent, the оnly man tо ѕurvive the destruction of
Earth, as he roams the cosmos and learns the truth behind his very
existence. The novel's namesake is an in-universe electronic travel
guide written in the form of an encyclopaedia, through which the story
is framed.";
// calculate capacity, check result
assert_eq!;
Additional documentation is available here: stegtext API documentation.
Testing
Tests are run automatically as part of the CI/CD pipeline (see
.forgejo/workflows/ci.yml) and release process (see release.py and
Containerfile.release).
However, you can manually run the audit, code coverage, linting, and testing commands with the commands in the following sections.
Audit Dependencies
Install cargo-audit and then use cargo audit to check dependencies
for security vulnerabilities:
cargo audit
Fetching advisory database from `https://github.com/RustSec/advisory-db.git`
Loaded 1271 security advisories (from /data/home/pabs/.cargo/advisory-db)
Updating crates.io index
Scanning Cargo.lock for vulnerabilities (22 crate dependencies)
Check Code Coverage
Install cargo-tarpaulin and use cargo tarpaulin to check code
coverage:
cargo tarpaulin --engine llvm --fail-under 95
...
|| Uncovered Lines:
|| src/bin/stegtext.rs: 80-84
|| src/lib.rs: 393, 434, 565
|| Tested/Total Lines:
|| src/bin/stegtext.rs: 5/10 +0.00%
|| src/lib.rs: 150/153 +0.03%
||
95.09% coverage, 155/163 lines covered, +0.06% change in coverage
Lint Code
Use cargo clippy to run the code linter:
cargo clippy
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
Lint Markdown
Use markdownlint-cli via Podman to lint the Markdown in
README.md:
# container image URL
IMAGE_URL=ghcr.io/igorshubovych/markdownlint-cli
# run markdownlint-cli in ephemeral container, lint README.md
Run Tests
Use cargo test to run the test suite and check the examples in the
API documentation:
cargo test
...
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out