Steganon is an extended implementation of the LSB steganography algorithm.
In short, steganography LSB is a method of hiding data inside pixels of image. Every pixel of an image consist of three integers (0-255, or one byte). They describe an amount of Red, Green and Blue colors. We take User's data to hide and convert it to a bit string; then we take next target pixel and select first integer: R. We convert this integer into bit string and change it's least significant bit to the next bit of User's data. Repeat this process for G and B then write altered pixel back into image. Repeat until User's data is not empty. Such change to RGB is invisible to human eye and gives us chance to hide three bits of data per pixel (three pixels per byte).
This repository implements a different type of LSB: LSB With Seed (short. LSB_WS
). This is a (designed by me) subclass of LSB that is use PRNG with changeable seed to determine targeted pixels. Here, Seed acts like password. You must know it to extract any hidden data, and fact that image contains any hidden data is more obscure. I believe that LSB_WS
is more strong against any analyze.
You can install SteganoN from PIP
pip install steganon
Or you can install with git clone
python -m venv steganon-env
cd steganon-env && . bin/activate
git clone https://github.com/NonProjects/steganon
pip install ./steganon
SteganoN has basic CLI implementation
# Install SteganoN with CLI
pip install steganon[cli]
from steganon import Image, LSB_WS
image = Image.open('example.png') # Open targeted Image
lsb_ws = LSB_WS(image, b'seed_0') # Init with seed=b'seed_0'
lsb_ws.hide(b'Secret!!!') # Write secret message to image pixels
image.save('example.png') # Save altered image with secret data
# Pseudo-code here! See source file for exact implementation
image = Image.open('example.png')
secret, seed = 'Secret!!!', b'seed_0'
secret_bin = binary(secret) = list('010100110110010101100011011100100110010101110100001000010010000100100001')
prng = PRNG(seed) # In reality we use Python's random module, which is Mersenne Twister
# The first 9 pixels of "prng.random_pixel" will be "Information
# pixels". We will store the total bytesize of secret data in them,
# so it's easy to know how much pixels we need to extract. As
# 9 pixels is 3 bytes, the maximum allowed bytesize to hide per
# one image is 16MiB-1 bytes == 16777215 (as integer).
info_pixels = [prng.random_pixel(image) for _ in range(9)]
while secret_bin:
# In the actual code we store position of pixels that we changed, so
# if "random_pixel" will return us pixel that already been altered
# we will request next position, up until we will not find empty one
next_pixel = image.get_pixel(prng.random_pixel(image)) # (255, 0, 0)
# Also, as there is three integers per pixel and only eight bits
# per byte of secret data we add zero (0) to start of every bits of
# byte of secret data so it's total size will be 9 and we can
# easily hide whole byte of secret data in three pixels. In this
# pseudo-code example this is ignored. 11111111 will be 011111111.
new_pixel = []
for color_integer in next_pixel:
color_bits = binary(color_integer) # 255 = 11111111 (First Iteration)
color_bits[-1] = secret_bin.pop(0) # color_bits ~= 11111110 (First Iteration)
new_pixel.append(binary_to_int(color_bits)) # 11111110 = 254 (First Iteration)
image.put_pixel(new_pixel) # [254, 1, 0] (After all Iterations)
# Secret Data extraction schema is pretty the same as hide process,
# the only difference is that we only take last significant byte
from steganon import Image, LSB_WS
image = Image.open('example.png') # Open Image with hidden data
lsb_ws = LSB_WS(image, b'seed_0') # Init with seed=b'seed_0'
print(lsb_ws.extract()) # b'Secret!!!'
Here is comparison between Original image (1) and Image with written on pixels data (2).
Modified Image (2) has whole Zen of Python written on it.
You can extract Zen from (2) by using Seed b'spam_eggs'
steganon.LSB_WS
class has a testmode
key. We can use it to check affected pixels under different seeds
- Always use a different seed! Pixel positions will be the same on different images and text!
- All of this developed by me and currently wasn't verified by cryptography experts. Use with caution!
hide()
process can be long on big Data and small image. Note: one byte of data is 3 pixels;- This library will not work with JPEG. PNG is OK and recommended. Other formats need testing;
- Best template to hide data is a compressed JPEG turned to PNG. Library have
tools.pngify
, use it; - Recommended image size is 2000x2000+. The bigger your image the bigger amount of bytes you can hide.
Contact me on thenonproton@pm.me (or just open Issue) if you have any feedback on this library.
Try to download this example image and extract secret information from it
Seed is b'OZZY'
, save data to the file with .ogg
extension and play with your media player