Skip to content
Steganography with JPEGs.
Scala Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src
.gitignore
LICENSE
README.md
atrium
pom.xml
repl-load.scala
repl.sh

README.md

Atrium -- Steganography with JPEGs

Embed text messages into JPEGs without altering their visual perception.

Atrium is a proof-of-concept command line application that enables you to embed text into a JPEG image without perceptibly altering it.

This is a toy. It's probably not useful in your real-world use case. See the Discussion section for more information.

Background and Motivation

Steganography is the technique of concealing information within some other artifact that is ostensibly only carrying the immediately or visually apparent information. Any medium can be used as the "host" to carry the secret message.

The JPEG image format is ubiquitous but potentially a poor steganographic carrier because it is a lossy file format. This means that modulated RGB pixel values in an image might be altered during the JPEG image encoding (i.e., compression), rendering the secret message encoded by the modulation unreadable.

What is Atrium?

Atrium is a proof-of-concept application for embedding information in an image's perceived visual content, rather than its RGB pixels.

Atrium embeds a secret text message into a JPEG by modulating the quantized values of the underlying DCT coefficients to achieve parity with bits in the message. This makes Atrium resistent to JPEG's lossy format.

Additionally, human eyes cannot detect small visual changes - in fact, that is what JPEG is exploiting in the first place - so, the visual changes due to Atrium's encoding are often imperceptible. The file size overhead is minimal (~3KB for a small message) as well.

Atrium embeds one byte of text into each 8x8 pixel region, but it still relies on Java itself to write out the encoded JPEG. This ensures the output image is a valid JPEG image no matter what.

Installation

  1. Download and install a JVM.
  2. Download and install Scala.
  3. Download and install maven.
  4. Download the Atrium Project.
  5. Compile the project:
$ cd atrium
$ mvn clean package

Usage

The basic usage is to encode text into an image, and then decode it. The application also offers an info command, which prints descriptive information about a JPEG image. Run atrium by itself to see help information.

Encode

To encode text into an image:

$ ./atrium encode --out encoded.jpg image.jpg "Hello, atrium."
Input Image Encoded Image

The output image defaults to the JPEG compression quality of the input image, but you can configure the encoded output image's quality with the --quality argument.

If an output file path is not specified, atrium defaults to the input file name with "-atrium" appended:

$ ./atrium encode image.jpg "Hello, atrium."
$ du -h image*.jpg
  124K  image-atrium.jpg
  120K  image.jpg

Decode

To decode the text from an Atrium-encoded image:

$ ./atrium decode encoded.jpg
  Hello, atrium.

Info

To print file information for a JPG image:

$ ./atrium info image.jpg

Discussion

As mentioned already, this project is purely a proof-of-concept. There are a number of limitations including:

  1. Atrium is untested for long input messages.

  2. Atrium doesn't work for pure white backgrounds. This is because Luminance values won't decode if wrapped-around at 248, so Atrium caps them at 248, which chops off the DCT modulation. See the ball image.

  3. High and low JPEG compression qualities are untested (i.e., unsupported). Testing showed Atrium to be most effective in the 60 - 80 JPEG quality range. You'll notice that the roman-atrium image fails to decode a long message with default encoding due to its high (99) quality. If you encode with a lower quality (e.g., 80), decoding works.

Still, even with its limitations, Atrium works well under the right conditions. For example, Atrium encoded a message into this Instagram post and successfully decodes the message from the downloaded image. This is despite Instagram's re-encoding and metadata stripping process.

Conclusion

Thanks for checking it out.

You can’t perform that action at this time.