HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Steganographic message hiding

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
messagehidingsteganographic

Problem

This code hides a message in an image by altering the blue values in the top left part. This is just for fun as I think it would be easy for a cryptographer to find and decrypt this.

Original

With message We will meet at London bridge at dawn

As you can (cannot) see, as long as the message is short enough, the naked eye has difficulty noticing the difference. Zooming in the top left corner will reveal the trick though.

```
from PIL import Image
from itertools import zip_longest
import doctest

def read_and_write_after_tranformation(infile, outfile, transformation):
# Based on a StackOverflow answer.
im = Image.open(infile)
list_of_pixels = list(im.getdata())
im2 = Image.new(im.mode, im.size)
im2.putdata(transformation(list_of_pixels))
im2.save(outfile)

def read_rgb(infile):
return list(Image.open(infile).getdata())

def steganographic_encrypt(message, rgb_tuples):
"""
Encodes a message in the blue values of the pixels on the top left corner of the image.

>>> steganographic_encrypt("Hello, World!", [(a, b, c, luminosity) for a, b, c, luminosity in zip(range(12, 30), range(20, 48), range(100, 128), range(100, 128))])
[(12, 20, 72, 100), (13, 21, 101, 101), (14, 22, 108, 102), (15, 23, 108, 103), (16, 24, 111, 104), (17, 25, 44, 105), (18, 26, 32, 106), (19, 27, 87, 107), (20, 28, 111, 108), (21, 29, 114, 109), (22, 30, 108, 110), (23, 31, 100, 111), (24, 32, 33, 112), (25, 33, 113, 113), (26, 34, 114, 114), (27, 35, 115, 115), (28, 36, 116, 116), (29, 37, 117, 117)]
"""
return [(red, green, blue if ascii_val is None else ascii_val, luminosity)
for (red, green, blue, luminosity), ascii_val in zip_longest(rgb_tuples, map(ord, message))]

def steganographic_decrypt(rgb_tuples):
"""
Decodes a message from the blue values of the pixels on the top left corner of the image.

>>> steganographic_decrypt([(12, 20, 72, 100), (13, 21, 101, 101), (14, 22, 108, 102), (15, 23, 108, 103), (16, 24, 1

Solution

Working with resources

In read_and_write_after_tranformation you didn't bother to close any of the image resources:

# Based on a StackOverflow answer.
im = Image.open(infile)
list_of_pixels = list(im.getdata())
im2 = Image.new(im.mode, im.size)
im2.putdata(transformation(list_of_pixels))
im2.save(outfile)


Don't believe everything you read on Stack Overflow =)

You can wrap those in a with ... as. And while at it,
im and im2 are not great names.

with Image.open(infile) as in_img:
    list_of_pixels = list(in_img.getdata())
    with Image.new(in_img.mode, in_img.size) as out_img:
        out_img.putdata(transformation(list_of_pixels))
        out_img.save(outfile)


Usability

This is a nice script.
It would be great to make it more usable,
with command line parameters:

  • hide sub-command: message to hide, input file, output file



  • show sub-command: file



You probably already know this, but argparse would be a good tool for this job.

Code Snippets

# Based on a StackOverflow answer.
im = Image.open(infile)
list_of_pixels = list(im.getdata())
im2 = Image.new(im.mode, im.size)
im2.putdata(transformation(list_of_pixels))
im2.save(outfile)
with Image.open(infile) as in_img:
    list_of_pixels = list(in_img.getdata())
    with Image.new(in_img.mode, in_img.size) as out_img:
        out_img.putdata(transformation(list_of_pixels))
        out_img.save(outfile)

Context

StackExchange Code Review Q#103856, answer score: 4

Revisions (0)

No revisions yet.