Stegman
Stegman is a small utility for safely encoding files or messages in other files using steganography.
Stegman

This is my project for the conclusion of C programming labs in the 18L semester on the Electronics and Information Technology faculty of Warsaw Institute of Technology.

Description

Stegman is a tool that employs cryptography and steganography to safely hide messages in various files. It can also reverse the process to extract information from a file.

Encoding

The user has to specify the file in which data will be encoded, a password, and the message to hide. The hiding procedure works as follows:

  1. User specifies the password, target file, and either a message or source file for encoding. The target file is the file the data will be encoded into. Source file is equivalent to a message, and it's the data that will be encoded.
  2. Program creates an IV for encryption, by creating a 16-byte array and filling it with data generated by a CSPRNG.
  3. Program creates a salt for hashing, in the same manner as the IV.
  4. Program generates a random number between 32767 and 65535, which determines the number of cycles the password will be hashed.
  5. The program salts the password, hashes it with SHA-256, and repeats the cycle n times (n is the number generated in step 6).
  6. The resulting 256-bit value is then used as key for AES-256 encryption.
  7. The program compresses the plain message using ZLib.
  8. The program encrypts a value of 0x0BADFACE as a control value, then the compressed message.
  9. Program loads all the pixels from the source PNG file. If the file is not a 24-bit RGB or 32-bit RGBA image, the program notifies the user and quits.
  10. The program encodes a value of 0x0BADFACE into the target image, followed by flags specifying properties of the message, followed by hash cycle count, followed by IV, followed by salt, followed by length of encrypted message, then the encrypted message.
  11. The program rewrites the source PNG file, now containing data encoded in its pixels.

The encoding occurs on 2 least significant bits of each color component of each pixel in the target PNG image. So assuming ARGB32-encoded image, it takes 4 channels to encode 1 byte (4 x 2 bits = 8 bits = 1 byte). Thus for ARGB32 images, the final (compressed + encrypted) message can be width * height - 4 - 4 - 2 - 16 - 16 - 8 bytes long. This is because:

  • 0x0BADFACE is a 32-bit integer (4 bytes), used as magic value to determine whether any data is present at all.
  • The flags are encoded as a 32-bit integer (4 bytes). The documentation for them is available below.
  • Hash cycle count is a 16-bit unsigned integer (2 bytes).
  • IV is 16 bytes.
  • Salt is 16 bytes.
  • Length is encoded as a 64-bit integer (8 bytes), it does not include padding.

The length encoded is without padding. AES-256 encrypts data in 128-bit (16-byte) blocks. This means the encrypted message length will be rounded up to nearby 16. Knowing that, we can encode the length without padding, because we can easily calculate the length of the padded message.

Decoding

The user has to specify the PNG file containing a hidden message, and password to decrypt it. The program then attempts to find, identify, and extract the message. If the input message was a file, the user has to specify the output file name. The procedure works as follows:

  1. Program loads all pixels from the souce PNG image. If the pixels format is not 24-bit RGB or 32-bit RGBA, the program quits.
  2. Program decodes first 4 bytes, and checks if they equal 0x0BADFACE. If they don't, the user is notified that the file does not contain a message and the program exits.
  3. Program decodes the message properties, hash cycle count, IV, salt, and data length.
  4. Program calculates the encrypted length by rounding up length to nearest 16.
  5. Program decodes first 16 bytes of the encrypted message.
  6. Program calculates the encryption key by salting the key, hashing it with SHA-256, and repeating the procedure n times, where n is the hash cycle count.
  7. Program attempts to decrypt the 16 bytes using the AES-256 algorithm, with the IV and key it computed.
  8. If the first 4 bytes of the decoded message are not equal to 0x0BADFACE, the user is notified that the password they supplied is invalid, and the program exits.
  9. The program decodes and decrypts the rest of the message.
  10. The program decompresses the decrypted message using ZLib.
  11. The program displays the message, or, if the message was a file, writes it to specified path.

Message Properties

The 32-bit properties bitfield contains information about the source message. Descriptions of specific bits can be found below:

**Bit** Value Description
0 0x00000001 The input message was a file

Requirements

The program was designed to work under GNU/Linux environments. It might work under other POSIX-compatible systems, provided appropriate prerequisites are installed, however that is not guaranteed. Furthermore the program was not tested under Windows at all.

Under Debian 10 (Buster) all requirements can be installed by doing:

$ sudo apt-get install clang-6.0 lld-6.0 llvm-6.0 libssl-dev zlib1g-dev libpng-dev make build-essential git bash

Then you can simply clone this repository, and in its root directory, run CC=clang-6.0 CFLAGS="-O2 -march=native" LDFLAGS=-fuse-ld=lld ./config.sh && make

Libraries

  • OpenSSL
  • ZLib
  • libpng

Operating System

Any modern GNU/Linux distribution. The program was written for Debian 10 (Buster), and tester under Debian 9 (Stretch).

Any POSIX-compatible UNIX (such as FreeBSD) might also work.

Compiler, Linker, Make

The program was tested to compile with Clang 6.0, and linked with LLD 6.0. However, any C99-capable compiler should do the job.

To build the program, you need a Make implementation available on your system. The program was tested with GNU Make.

Shell

The configuration script uses Bashisms extensively, so you need a modern version of Bash installed.

Using the program

Using the program is fairly straightforward. It has 2 operation modes: encode and decode.

In below descriptions, <> indicates a required argument, [] indicates an optional one. Do not put the brackets in actual command invocation. E.g. for invocation like ./test <arg1> <arg2> you would invoke as ./test "asdf" 123.

Encoding

To encode a message (or a file) into a file, run the program as:

./stegman encode <password> <target file> <message> to encode a text message, or ./stegman encode <password> <target file> @<source file> to encode a file.

**Argument** Description
password The password to secure your data before encoding.
target file The file in which the data will be encoded.
message Text message to encode in the file.
source file File to encode in the file.

Decoding

To decode a message from a file, you would run the program as ./stegman decode <password> <source file> [target file]. Note that if source message was a file, you must specify target file.

**Argument** Description
password The password used to secure your encoded data.
source file The file in which the data was encoded.
target file The file in which the decoded data will be placed.

License

The program and the source are shared under MIT License. See LICENSE file for details.