Studio

Fun with secret messages hidden in pictures

A previous Wiki article discussed how to hide a secret message in a block of apparently random text, a 500-year old technique known as a Grille Cypher. For a bit of fun, let's look at the modern equivalent - how to hide a message inside a photograph. In the example below the first picture is unmodified, but the second one has a 1000 word secret message hidden in it. Can you spot the difference?

/!\ This needs attention since somce modern browers (Chrome for a start) have stopped supporting BMPs

photo.bmp

photo.bmp

modified.bmp

Although hiding secret messages inside photos might attract the attention of the NSA it does have legitimate uses. For example you could protect the copyright of your own photos by adding a secret watermark.

Pixel Colours

Modern computers typically use a 24-bit encoding for colours. Each of the Red, Green and Blue components of a pixel is represented using 8 bits, so a modern graphics card can display (2*8)*3 or 16777216 different colours. In fact the colour value is often encoded in a 32-bit word with one byte for each of Red, Green and Blue; the high byte is either unused or represents a transparency value.

A person looking at a colour image is very unlikely to notice if we make very slight changes to the pixel values. For example we can flip the bottom bit of the pixel colour, making a tiny change to the red component, and it's almost certain to pass undetected. We can use this to hide a secret message inside a picture.

For the demonstration which follows I'll assume that the photograph has already been read into an APL variable, and that it takes the form of an M x N matrix, where M and N correspond to the dimensions of the photo. Each matrix element is an integer representing the 32-bit encoded colour of a single pixel. (At the end of this article I've included a brief description of how to read in a picture using APLX).

Random Numbers

APL is able to generate random numbers using the ? function. For example to get 10 random numbers in the range 1 - 20 you could type:

      10?20
10 9 13 3 20 11 6 16 5 8

Normally if we type 10?20 again we will get a completely different sequence of random numbers. However by re-setting the Random Link ⎕RL we get a repeatable sequence:

      save_rl←⎕RL      
      10?20
20 6 10 7 4 2 14 18 15 19
      ⎕RL←save_rl
      10?20
20 6 10 7 4 2 14 18 15 19

We can use this information when hiding the message in the picture. First we choose a random sequence of pixels to modify, so that the tampering is less likely to be noticed. Provided that the person receiving the modified picture knows the value of ⎕RL we used and the sequence length, they will be able to reconstruct the random sequence so they will know which pixels contain the message.

For simplicity the example below hides ⎕RL at the start of the picture, together with the length of the secret message. If you care about security you could omit ⎕RL and use a pre-agreed value instead!

Converting a value to Binary

We want to hide the secret message by only modifying the bottom bits of randomly selected pixels, which means that we can only store 1 bit of information per pixel. (You could be more sophisticated and modify the bottom bit of each of the Red, Green and Blue components, allowing 3 bits per pixel, but this is left as an exercise for the reader).

This means that we need to convert the secret message to Binary, which can be done using APL's Encode primitive . Here is an example of an encode of the numbers 1 2 3 4 5 and the corresponding decode:

      encoded←(8⍴2) ⊤ 1 2 3 4 5
      encoded
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 1
0 1 1 0 0
1 0 1 0 1
      2 ⊥ encoded
1 2 3 4 5

If you're familiar with binary format you can see that each column in encoded corresponds to one of the numbers 1 2 3 4 5 converted to binary.

Modifying the bottom bit of a number

We're now ready to insert our binary-encoded message into some randomly chosen pixels. One way to do this would be to convert the pixel values to binary too, replace the bottom bits with values from our message, and convert back to integers. However, there's an easier way; we compare the current value of each bottom bit to the desired value, and flip it if it's wrong.

For example, imagine you want to insert the binary message 1 0 1 1 0 1 0 1 into the bottom bits of pixels with the values 5012853 1921862 1858884 5282424 9758136 7981975 9492903 6200428. The current values of the bottom bits are found by testing whether the numbers are even:

      vals←5012853 1921862 1858884 5282424 9758136 7981975 9492903 6200428
      2 | vals
1 0 0 0 0 1 1 0

In some cases the current bit already equals the desired bit. In other cases it's a 0 and needs to be a 1; and in other cases it's a 1 and needs to be a 0. We can handle this as follows:

      encoded←1 0 1 1 0 1 0 1
      vals ← vals + encoded - 2|vals
      vals
5012853 1921862 1858885 5282425 9758136 7981975 9492902 6200429

Finally we can recover the secret message:

      2 | vals
1 0 1 1 0 1 0 1

The final functions

We're now ready to put all the steps together into two functions as follows:

∇  R←bitmap EncodeText text;bm;length;encoded;random;idx;vals
   ⍝ Takes text and encodes it into least significant bits
   ⍝ of bitmap, where it will be embedded without affecting the
   ⍝ visual appearance of the bitmap

   bm←,bitmap

   ⍝ Each character of text will give 8 binary digits
   length←8×⍴text

   ⍝ Encode the value of ⎕RL and the text length. Each one is a 32-bit quantity
   encoded←,(32⍴2)⊤⎕RL,length

   ⍝ Encoded the text message too
   encoded←encoded, ,(8⍴2) ⊤ ⎕av⍳text

   ⍝ Choose random positions to modify, except for ⎕RL and the length,
   ⍝ which we'll put at the start of the bitmap so the decoder can find them
   random←64+length?¯64+⍴bm
   idx←(⍳64),random

   ⍝ Modify the bottom bits
   vals←bm[idx]
   bm[idx]←vals + encoded - 2|vals

   ⍝ Reconstruct bitmap with secret text embedded
   R←(⍴bitmap)⍴bm

∇ R←DecodeText bitmap;bm;rl;random;vals;⎕RL

   ⍝ Regenerate the sequence of random numbers
   bm←,bitmap
   (rl length)←2⊥(32 2)⍴2|64↑bm
   ⎕RL←rl
   random←64+length?¯64+⍴bm

   ⍝ Extract the binary representation of the text
   vals←2|bm[random]

   ⍝ Reconstitute the secret message
   R←⎕av[ 2⊥(8,length÷8)⍴vals ]

Working with images in APLX

Different APLs have different ways of reading and writing images. The following explanation describes how it can be done in APLX.

The simplest way to read an image is by using APLX's built-in Picture class as follows:

      ⍝ Create a window containing a picture object
      w←'⎕' ⎕new 'window'
      w.picture.New 'picture'

      ⍝ Ask the user where the picture is located on disk, and read it in.
      ⍝ (This will cause a file selector dialog to be displayed)           
      w.picture.bitmap←''

      ⍝ Resize the picture frame to fit the whole image (optional)
      w.picture.size←w.picture.imagesize

You can then obtain a bitmap in a suitable form using bitmap ← w.picture.bitmap . The encoder and decoder functions could be used as follows:

      bitmap ← w.picture.bitmap
      secret_message  ← 'This is my top secret message'
      modified_bitmap  ← bitmap EncodeText secret_message

      ⍝ Optionally display the modified bitmap 
      w.picture.bitmap ← modified_bitmap 

Unfortunately the built-in Picture class is only designed for displaying images, it cannot be used to save a modified image to disk. One way to do this is to make use of the Image class, which provides a wrapper around the ImageMagick library. To use this you need to download and install ImageMagick on your machine - see the APLX documentation for details. You can then manipulate images as follows:

      image ← '⎕' ⎕NEW 'image'
      image.load 'C:\Users\Simon\Desktop\photo.bmp'
      bitmap ← image.bitmap

      secret_message  ← 'This is my top secret message'
      modified_bitmap  ← bitmap EncodeText secret_message

      modified_image ← '⎕' ⎕NEW 'image'
      modified_image.bitmap ← modified_bitmap 
      modified_image.save 'C:\Users\Simon\Desktop\modified.bmp'

Make sure to choose a lossless format like BMP otherwise the secret message will be destroyed when the image file is compressed.

Author: SimonMarsden


CategoryAplx