CS-171

Program Assignment 4

 

Objective

The objectives of this assignment include the following:

 

Overview

People use cryptography to send secret messages to one another without a third party observing the message.  Steganography is a type of cryptography in which the secret message is hidden in digital media, like in a digital picture.  Just imagine how many pixels are contained in a large picture, and each pixel has three color values.  For instance, in a moderate sized picture of 640 by 480, there are 307,200 different pixels, and with three color channels per pixel, that yields nearly a million numbers that each range in value from 0 to 255.  If you were to change a few of these color numbers, the resulting picture would probably look a lot like the original image; in fact, most people might not be able to tell that the image had been changed at all.  This is the challenge of steganography ... to change the digital media so that the perception of a difference is kept as small as possible.  This helps avoid detection and the efforts of the "adversary" to try and discover the secret message.

 

Steganography works by changing a few pixel color values.  We will use these changed color values to encode the secret message -- in other words, to use them to represent the characters that make up the string of a message.  Depending on how well we have done our job, the resulting picture should look like the original, but perhaps with a few "blips" that seem out of place if you look very closely.  We can then send the image to our cohort and they can extract the message if they know which pixels to decode.

 

In this assignment, you will be writing a complete Java application that will enable you to exchange secret messages with another person.  We will use our knowledge of pixel manipulation to implement a steganographic system.

Assignment Specification:

The core requirements for this assignment, which will achieve the first 45 points:

Here are the commands to convert and save a file to bmp format.

> Picture p1 = new Picture(FileChooser.pickAFile());
> p1 = p1.scale(0.4);
> p1.saveBMP(FileChooser.pickSaveFile());

Note this last step makes use of one of the modifications to the FileChooser class that allows you to navigate to a directory and specify the name of a file that does not exist to use in a subsequent write/save operation.

There is also a loadBMP() method available from the Picture class that takes a String argument specifying the name of the bitmap file to load.

Be sure and use comments to describe the purpose of every method that you write, as well as to lead me through what you are doing with your code.  Also put a comment at the top of all classes that you write that includes your name and what encoding methods you have designed and implemented.  To turn your assignment in, create a proj4 folder in the assignment inbox and copy your Steganography.java file in that folder.  If your design includes additional classes, the source for each of those should be included as well.

Encoding/Decoding Methods:

You have three options of how to encode/decode the message.  You must implement one of these options.  You can start with the simplest method (A) and then add to it to create (B) and then (C).  If you think of another design that does a better job and reducing the perception that the picture has changed with the addition of the secret message, talk with me about it and you may be able to substitute your own methodology for one of the given techniques, depending on its level of difficulty.

A.  Standard Method.  (Good for 30 additional points for a total of 75)

B.  Key method.  (Good for 45 additional points above the core for a total of 90)

C.  Key + bit method.  (Good for 60 additional points above the core for a total of 105 ... ie includes some extra credit)

A.  Standard Method.  (Good for 30 additional points for a total of 75)

This encoding method changes the red channel for a set of pixels in the picture.  The red channel is overwritten with the character encoding of a single character from the string containing the secret message.  The first affected pixel's red value is overwritten with the number of the characters in the secret message string.  Note that this limits your secret message to a maximum length of 255 characters.

One design choice that must be made is which pixels to affect.  So that we are all performing an identical algorithm, we will say that the secret message character count goes in the red channel for pixel 0, and we use every 20th pixel thereafter to contain each of the characters of the secret message.  The encoding of characters into their computer recognizable binary format is known as Unicode.  Each character has a mapping to a number.  So, for instance, the character 't' maps to 116, 'o' maps to 111, and 'm' maps to 109.  So to encode the secret message "tom" in a picture, I would place the length of 3 in the red channel of pixel 0, 116 in the red channel of pixel 20, 111 in the red channel of pixel 40, and 109 in the red channel of pixel 60.

B.  Key method.  (Good for 45 additional points above the core for a total of 90)

If an adversary knew how our steganography algorithm worked, it would be easy for them to extract secret messages from intercepted image files.  We can improve our security by using a "key."  In this case, a key will be a short word or phrase.  You can combine the key with the message to produce a scrambled message that is not immediately legible.  Only by using this key can you unscramble the message to obtain the original message.

Here is how we will implement the key feature.  The secret message is one string while the key is a second string that is usually shorter than the original secret string.  We will pair up the characters in each string by their positions.  Because the key is usually shorter, we will have to reuse the key starting back at the beginning.  For example, suppose the secret message is "comp science is fun", while the key is "passkey".  The pairing between the secret message and the key is as follows (notice that the blank space is treated as a character like any other):

c o m p   s c i e n c e   i s   f u n
p a s s k e y p a s s k e y p a s s k

A simple operation called xor (this is the ^ operator between two integral operands) can be used to operate on the unicode encoding of each of the pairs of characters, one character from the secret message and the other character from the key.  Xor has the very useful property that it is reversible.  If C = (A ^ B), then I can retrieve A if I have C and B, so A = (B ^ C).  Applying to the above pairings, I can obscure the message by performing xor on the pairs of characters and store that in the image.  When I decode, I can perform the reverse, by applying the key against the obscured secret values to get the original secret message.

Now you will be able to communicate the key to your cohort and then convey the image, encoded as described above, and they will be able to retrieve the secret message.

C.  Key + bit method.  (Good for 60 additional points above the core for a total of 105)

The problem with changing the red values in our encode/decode steps, is that these often cause fairly visible changes in the resulting image.  This is especially true if the pixels that are being changed are part of a large section of uniformly colored pixels -- the "dots" stand out and are noticeable.  As an alternative, we can change only the lower order bits of each pixel color (in all three channels).  This will make much more subtle changes to each pixel's color and will not be as evident.

Recall that each pixel has three bytes: one for red, one for blue, and one for green.  Each byte has 8 bits to encode a number between 0 and 255.  When we overwrite the red color byte from its original red channel value to the encoding of a character, it is possible that we are changing the redness of that pixel by quite a bit.  For example, we might have a had a pixel with values of (225, 100, 100), a color with mostly red, and enough blue and green to create a dark pink.  Now suppose we store the encoding of the character 'a' in the red channel of this pixel.  The encoding for 'a' is decimal number 97, so our new pixel becomes (97, 100, 100), which is a medium grey.  This change from dark pink to medium grey can stand out in a displayed image, particularly if the other nearby pixels are all dark pink.

So how do we solve this problem.  We need at least 8 bits to represent the encoding of the characters in our secret message.  The answer comes from an understanding of how numbers are represented in binary.  Recall from our class discussion that, in a 8 bit (unsigned) value, our encoding uses the same positional/value system that we learned in 4th grade with decimal numbers, only now we are dealing with a base 2 number system.

Suppose we label a binary number with the symbols b7, b6, b5, b4, b3, b2, b1, b0.  We use the b0 position for the 1s place, the b1 position for the 2s place, the b2 position for the 4s place, and so on.  If we only change the low order bits if each pixel, then the numeric values can only change by a small percentage.  For example, suppose we only change the last three bits.  We can only change the value by at most 7.

Think of the three color channels for a pixel in terms of the 24 bits that is their actual representation (8 each for red, green and blue).  Using the notation from above, a pixel is represented in binary as

(r7 r6 r5 r4 r3 r2 r1 r0 , g7 g6 g5 g4 g3 g2 g1 g0 , b7 b6 b5 b4 b3 b2 b1 b0)

Likewise our character encoding in its binary representation is

c7 c6 c5 c4 c3 c2 c1 c0

Then, we can distribute the bits that make up our character into the low order bits of the red, green, and blue channels, making sure to only change the low order bits, while keeping the high order bits intact.  Let us put 3 bits of our character in red, 3 in green, and two in blue.  In terms of bits, our result would be:

(r7 r6 r5 r4 r3 c7 c6 c5 , g7 g6 g5 g4 g3 c4 c3 c2 , b7 b6 b5 b4 b3 b2 c1 c0)

If we had done this to our example pixel of (225, 100, 100) with character 'a', we obtain (in binary):

original pixel = ( 11100001, 01100100, 01100100 )
'a' = 01100001
new pixel (binary) = ( 11100011, 01100000, 01100101 )
new pixel (decimal) = ( 227, 96, 101 )

So now, the pixel with the character encoded into it is almost the same value as the old pixel (227, 96, 101) versus (225, 100, 100).  This will be within the normal range of variations in any color and will not be noticeable.

Retrieving the character is a matter of reversing the process -- namely for a pixel that encodes a secret character, extract the low order three bits from the red channel and place them in the three high order bits of a result; extract the low order three bits from the green channel and place them in bit position 4, 3, and 2 in the result, and finally extract the low order 2 bits from the blue channel and put them in the low order two bits of the result.

We will use some class time to review binary encoding and some of the Java operators (namely the xor, bit shift, bitwise and, and bitwise or) that operate on the bits within a Java integer or byte value.