The Stego Library
With the basic theory out of the way, you can start to design and implement a basic .NET library that supplies a simple API for hiding a message in a 24-bit
.bmp image. The class diagram in
Figure 2 shows the interfaces and classes you are going to meet next in this article.
 | |
| Figure 2. Stego Library Class Diagram: The stego library defines interfaces that support different types of cover and stego files. The BmpCoverFile and BmpStegoFile classes are concrete implementations that operate on 24-bit bitmapped images. |
To handle both the cover and the stego file, you need two interfaces:
ICoverFileI is an abstraction of the cover and defines the
CreateStegoFile method that creates the stego file for hiding a message. The method requires three parameters: the filename for the resulting (output) stego file, the message to hide, and a password that you'll need later to retrieve the hidden message.
IStegoFile is an abstraction of the stego file, and defines the
HiddenMessage property that extracts a hidden message.
The BmpCoverFile and BmpStegoFile classes implement the interfaces using the LSB substitution method. The LSBHelper class contains the implemented logic. Both classes use classes from the System.IO.Stream namespace to perform file I/O operations.
After implementing that API, it's trivial to hide a message inside a
.bmp file:
ICoverFile cover = new BMPCoverFile("cover.bmp");
cover.CreateStegoFile("stego.bmp","Hello","MyPwd");
Likewise, you can extract the message just as easily:
IStegoFile stego = new
BMPStegoFile("stego.bmp","MyPwd");
Console.WriteLine(stego.HiddenMessage);
Because the library uses interfaces, you can extend it by implementing new classes that support different file types, such as other image file formats such as
.jpg,
.gif,
.tiff etc., or audio/video file formats such as
.wav,
.mp3,
.avi,
.mpeg,
.asf, etc. For example, you might implement Mp3CoverFile and Mp3StegoFile classes that function similarly to hide a message inside an MP3 file.
The LSBHelper Class
The LSBHelper class is low-level code that operates on bits and bytes of cover and stego file. The
Encode method hides an array of bytes inside data coming from a stream, and writes the result in another stream. Here's the code:
public static void Encode(Stream inStream,
byte[] message, Stream outStream)
{
int byteRead;
byte byteWrite;
int i = 0;
int j = 0;
byte bit;
while((byteRead = inStream.ReadByte()) != -1)
{
byteWrite = (byte) byteRead;
if (i<message.Length)
{
// Extract the bit at the j position
bit = Bit.Extract(message[i], j++);
// Replace the LSB of byteWrite with "bit"
Bit.Replace(ref byteWrite, 0, bit);
// Every 8 bits,
// process another byte of "message"
if (j==8)
{
j=0; i++;
}
}
outStream.WriteByte(byteWrite);
}
if (i<message.Length)
throw new Exception
("The cover is too small to contain the " +
"message to hide");
}
The
Encode method uses the variable
i to index the message byte-array and
j to index a single bit of
message[i]. The method reads a byte from the input stream, replaces the LSB with the bit at position
j of
message[i], and then writes the resulting byte to the output stream. Every 8 bits, the method processes the next element of the message array. After reading the entire input stream, the method checks if
i is less than the message length. If
true, there are still bytes to hide, but the cover is too small to contain them, so the method throws an exception.
The Bit class, used within the
Encode method, contains the
Replace and
Extract methods thatby using bitwise operatorsreplace or extract a particular bit of a byte:
public static void Replace(ref byte b, int pos,
byte value)
{
b = (byte) (value == 1 ? b | (1 << pos)
: b & ~(1 << pos));
}
public static byte Extract(byte b, int pos) {
return (byte) ((b & (1 << pos)) >> pos);
}
The
LSBHelper.Decode method extracts hidden bytes from an input stream as shown below.
public static byte[] Decode(Stream stream, int length)
{
byte[] hidden = new byte[length];
int i = 0;
int j = 0;
byte bit;
int byteRead;
while((byteRead = stream.ReadByte()) != -1)
{
// Extract the LSB of byteRead
bit = Bit.Extract((byte) byteRead, 0);
// Replace the bit at the j position with "bit"
Bit.Replace(ref hidden[i], j++, bit);
// Every 8 bits process another byte of "hidden"
if (j==8) {j=0; i++;}
// Check bytes left
if (i==length) break;
}
// The hidden var contains the hidden bytes
return hidden;
}
Decode is complementary to
Encodethe method reads bytes from an input stream and composes the hidden message by extracting the LSB from every byte read. The length parameter represents the number of bytes to extract. After extracting
length bytes, the method returns the
hidden[] byte array.