Quick'n'dirty web based steganography implementation.
bzt 3ac63cc53f Code cleanup | 2 years ago | |
---|---|---|
bin | 2 years ago | |
public | 2 years ago | |
src | 2 years ago | |
.gitlab-ci.yml | 2 years ago | |
LICENSE | 2 years ago | |
README.md | 2 years ago | |
screenshot.png | 2 years ago |
In a nutshell, steganography is the art of hiding messages in another medium in plain sight without people realizing. This implementation reads in images in popular web formats (WEBP, PNG, GIF, JPEG), hides the (optionally encrypted) text in them and saves the result as a WEBP image. You can then upload this new image to any public website, forum, insta, FB chat, attach to emails, whatever. No one will be the wiser, nobody will know that there's actually a message in the picture unless you explicitly tell them (and if encryption was used, then they'll also need the correct password of course).
select an input image (either by an URL or from a local file)
optionally enter password (if you want additional military grade encryption)
3/a. to check the message in an image, press Load
. If there's no message or your password is wrong, you might see gibberish.
3/b. type your message and press Save
. A new image (with the hidden message inside) will be saved, use that file as you wish.
Go to the website.
Download for Linux (GTK) and Windows (GDI). These are portable executables, no installation required, and they don't leave any trace on your machine. The GUI looks like and works like the webservice exactly.
Download for Linux and Windows (both libc only). Then type
./stegano-cli <image file>
./stegano-cli -p <password> <image file>
to print the message in an image to stdout, and
./stegano-cli <image file> <message>
./stegano-cli -p <password> <image file> <message>
to encode a message into a new .webp image file (this might overwrite the original file). Just like with the webservice, the password is optional. Unlike the GUI version above, the command line version has absolutely no dependencies, it Just Works(tm) everywhere.
Just run make
in the src directory, it is suckless.
Only needs emscripten's emcc
and gcc
to compile (the Linux GUI version also needs pkg-config
and GTK, GDK, pango, cairo,
etc. the usual GTK bloatware).
make wasm
if you only want to compile the WebAssembly version (the required boilerplate html is in the public directory).make cli
if you only want to compile the command line version (totally dependency-free, should work on any POSIX system).make gui
if you only want to compile the Grapical User Interface version (for Windows GDI, otherwise depends on GTK).If you have specified a password, then first your text is encrypted. Then the image is uncompressed into a pixelbuffer, where the bits of your (encrypted) message are stored in the brightness of colors channels. This results in very slight wave patterns in the picture, which are totally unnoticable by the naked human eye (and in lack of comparing to the original image, totally unnoticable to computers as well).
On load, the image is again uncompressed into a pixelbuffer, and these brightness waves in the color channels are converted to a concatenated bit-chunk. If a password is given, then the bit-chunk is decrypted. The final message is checked for UTF-8 validity, and if it's valid, then it is displayed. (This UTF-8 check is needed because no one can tell if there's really a message in the image, or if the given password was correct or not.)
The message is stored as a zero terminated UTF-8 multibyte stream, in which only \t
(horizontal tab), \r
(carriage return)
and \n
(newline) control sequences are allowed. When loaded back, the Windows GUI version converts standalone \n
characters
to \r\n
CRLF sequences. On saving, the CLI version accepts \
+t
, \
+r
, \
+n
two bytes character sequences, and
converts them to the corresponding single byte control characters.
For the encryption, the message is padded with zeros to make its length multiple of 16, and then first
sha256(salt+sha256(password))
is calculated, which gives 32 binary bytes, out of which 16 consecutive bytes are used as
AES-256-CBC iv starting from the 4th byte's value modulo 16. The AES-256-CBC key is then initialized as
sha256(sha256(salt+sha256(password))+salt)
(using sha of sha makes it extremely difficult if not impossible to use
rainbowhashing attack against the password). The salt is Libre Steganography Standard
.
For the pixels, all images are converted to 32 bit packed RGBA pixelbuffers. Each of the four channel's least significant 2 bits are used to store 8 bits of the message (which gives 1 message byte per 1 carrier pixel). Only pixels with alpha channel greater than 3 are used for storing, and all versions report the number of those pixels as "Capacity N bytes."
1 message byte's bits (7 MSB, 0 LSB): 76543210
1 carrier pixel (alpha last, red first byte): rrrrrr76 gggggg32 bbbbbb54 aaaaaa10 (a!=0)
(This non-transparent hack is needed because WEBP codec isn't as lossless as it claims to be. With alpha greater than 3, it is guaranteed that the pixel isn't transparent even if the encoded bits are zero.)
This steganography tool is licensed under the terms of GPLv3+ (or any later version of that license). It is statically linked with stb_image (which is public domain), and libwebp (which is licensed under BSD-3-Clause). The SHA hash and AES encoder/decoder parts are inlined (written by yours truly), no third party cryptographic library needed at all.
Cheers, bzt