Skip to content

calum4/image-steganography

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Important

This project is in maintenance mode.

At this time I am not looking to develop new features, nor accepting pull requests for new features. I am also not looking to publish this project on crates.io.

Bug reports and vulnerability reports are of course very welcome.

Image Steganography

A binary and library for hiding data inside a PNG image with minimal visual alteration, see examples here.

Obviously this could be used nefariously, don't be an ass.

Caveats

  • Was initially developed inside a closed-source project and targeted one specific use-case. I recommend thoroughly testing your use-case before relying upon it.
  • The original un-altered image is required for decoding
  • Only supports PNG images
  • Doesn't support images with indexed colour
  • Minimal performance optimisation
  • Whole images are stored in memory whilst processing. I.E. assuming a 10MB reference image, encoding will require at least 10MB of memory and decoding 20MB.
  • Docs are minimal
  • Zero guarantees on API stability

I might one day address some of these caveats.

Examples

Complete Saturation

In this example, I will completely saturate the sls-large image (1920x1080 RGB) with its maximum byte capacity of 3.6 MiB.

BYTES_CAPACITY="$(image-steganography capacity tests/assets/sls-large.png)"
echo "Bytes Capacity: ${BYTES_CAPACITY}"

TEMP_DIR="$(mktemp -d /tmp/image-steganography.XXXXXX)"
DATA_PATH="${TEMP_DIR}/data"
head -c $BYTES_CAPACITY /dev/urandom > $DATA_PATH

cat $DATA_PATH | image-steganography encode tests/assets/sls-large.png assets/sls-large.encoded.png -
Before After

As you can hopefully see, the steganography technique works well for lighter areas, but less-so in darker areas.

Now to extract the data:

EXTRACTED_DATA_PATH="${TEMP_DIR}/decoded_data"
image-steganography decode tests/assets/sls-large.png assets/sls-large.encoded.png > $EXTRACTED_DATA_PATH

and to demonstrate the data is unaltered:

sha256sum $DATA_PATH $EXTRACTED_DATA_PATH
# f94d63eed6ec95d29eba7761a50d5603bf321d54d774e12845c8dc56921e0ed5  /tmp/image-steganography.xULCJo/data
# f94d63eed6ec95d29eba7761a50d5603bf321d54d774e12845c8dc56921e0ed5  /tmp/image-steganography.xULCJo/decoded_data

Cleanup:

rm -r $TEMP_DIR

Partial saturation

Because only the portion of the image that is required for data storage is altered, the effect on images with smaller payloads is much less pronounced.

For example, storing the entire source code of this tool:

TEMP_DIR="$(mktemp -d /tmp/image-steganography.XXXXXX)"
DATA_PATH="${TEMP_DIR}/src.tar.gz"
tar -czf $DATA_PATH src/

cat $DATA_PATH | image-steganography encode assets/sls-launch-large.png assets/sls-launch-large.encoded.png -
Before After

The src.tar.gz at time of writing is 7.2 KiB and the encoding takes up roughly 2 and a half rows of pixels at the top of the image. Therefore, leaving the rest of the image entirely untouched and the difference essentially indiscernible to the eye without zooming in.

Setup

Binary

git clone https://github.com/calum4/image-steganography.git
cd image-steganography
cargo install --path . --bin image-steganography --features="bin-build"

Library

Add the following to your Cargo.toml inside the dependencies table.

Note - I would recomment pinning to a commit instead by replacing branch = "main" with rev = "<commit-hash>".

image-steganography = { git = "https://github.com/calum4/image-steganography.git", branch = "main" }

Usage

Binary

Encode

You can provide the data either directly, or via STDIN:

# Direct
image-steganography encode "<reference-image-path>" "<encoded-image-output-path>" "test"

# STDIN
printf "test" | image-steganography encode "<reference-image-path>" "<encoded-image-output-path>" -

Decode

Decodes the data embedded inside the encoded image and then outputs it to STDOUT.

image-steganography decode "<reference-image-path>" "<encoded-image-output-path>"

Capacity

Returns the capacity in bytes that the provided image can hold encoded.

image-steganography capacity "<image-path>"

Library

TODO - Example

How it works

Encoding

  1. The provided bytes are hex encoded.
  2. The RGBA channels of each pixel are adjusted by the formula (colour_value - 7 + hex_as_u8). E.G. assume a pixel has a red value of 165 and a hex value of 0xC (12), the encoded red value would be 170. In cases where the encoded value overflows the 0-255 range, it is shifted to where 0 == 0x0 or 255 = 0xF, whichever is closer.
  3. After the provided bytes are encoded, the next pixel is adjusted with the same formula with a data end marker of value 16.

Decoding

  1. The reference image and encoded image iterated through on a per-channel-per-pixel basis and the difference is calculated.
  2. That difference is used in the reverse of the encoding formula to determine the hex value it encoded, then that value is added to a buffer.
  3. Once the data end marker is reached, the iteration terminates.
  4. The data buffer is then hex decoded and the encoded data is returned as a vector of bytes.

License

Licensed under either of

at your option.

Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

See CONTRIBUTING.md.

About

A binary and library for hiding data inside a PNG image with minimal visual alteration

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Stars

Watchers

Forks

Contributors

Languages