Click here to Skip to main content
Click here to Skip to main content

Steganography II - multiple key and carrier files

By , 3 Apr 2004
 

Sample Image - steganodotnet2.png

Introduction

This article enhances the last article Steganography - Hiding messages in the Noise of a Picture. It adds three important features to the application:

  • Multiple key files
  • Password protection for each key file
  • Multiple carrier images

Background

Before reading this, you should have read part one, Steganography - Hiding messages in the Noise of a Picture. This article uses the application described in part one, and adds functionality to spread the message over multiple carrier images, use more than one key file, and protect each key with a password.

Multiple keys

Symmetric keys contain a central problem: how do you transfer the key? Anyone who gets his hands on the key is able to decrypt every message you send or receive. So we have to make it difficult to steal the key. One idea might be keeping the keys short and don’t save them at all. But a short key always leaves a footprint in the encrypted file. In our case, it wouldn't be visible as a regular noise pattern. Why not combine a short password with a long key? You can store a long key in a file, and encrypt that key using a short password which is stored only in your brain.

A password always belongs to its key file, just like that:

public struct FilePasswordPair{
    public String fileName;
    public String password;

    public FilePasswordPair(String fileName, String password){
        this.fileName = fileName;
        this.password = password;
    }
}

Before we can use the keys, the file/password pairs have to be combined. In this example, the key file contains the text "hello-world". Whenever you hide or extract a message with the "hello-world"-key, you type the password "nothing". The application XOR-combines the bytes of the key file with the bytes of the password, repeating the password again and again.

protecting a key file with a password

Here is the same thing in C#:

public static MemoryStream CreateKeyStream(FilePasswordPair key){
    FileStream fileStream = new FileStream(key.fileName, FileMode.Open);
    MemoryStream resultStream = new MemoryStream();
    int passwordIndex = 0;
    int currentByte = 0;

    while( (currentByte = fileStream.ReadByte()) >= 0 ){
        //combine the key-byte with the corresponding password-byte
        currentByte = currentByte ^ key.password[passwordIndex];
        
        //add the result to the key stream
        resultStream.WriteByte((byte)currentByte);
        
        //proceed to the next letter or repeat the password
        passwordIndex++;
        if(passwordIndex == key.password.Length){
            passwordIndex = 0;
        }
    }

    fileStream.Close();
    resultStream.Seek(0, SeekOrigin.Begin);
    return resultStream;
}

The resulting stream is as long as your key file, but never stored anywhere. Of course, this encryption is not really safe, so we’ll use more keys and more passwords. Before the recipient is able to extract the message, he must have all the keys and know all the passwords. If somebody manages to copy one key or guess two passwords, there's no need to panic as long as the other key files are still safe. That means, before hiding or extracting data, we create each key stream as shown above, and then combine all streams into one key stream:

combining three stream

private static MemoryStream GetKeyStream(FilePasswordPair[] keys){
    //Xor the keys an their passwords
    MemoryStream[] keyStreams = new MemoryStream[keys.Length];
    for(int n=0; n<keys.Length; n++){
        keyStreams[n] = CreateKeyStream(keys[n]);
    }
    
    //Buffer for the resulting stream
    MemoryStream resultKeyStream = new MemoryStream();

    //Find length of longest stream
    long maxLength = 0;
    foreach(MemoryStream stream in keyStreams){
        if( stream.Length > maxLength ){ maxLength = stream.Length; }
    }
    
    int readByte = 0;
    for(long n=0; n<=maxLength; n++){
        for(int streamIndex=0; streamIndex<keyStreams.Length; streamIndex++){
            if(keyStreams[streamIndex] != null){
                readByte = keyStreams[streamIndex].ReadByte();
                if(readByte < 0){
                    //end of stream - close the file
                    //the last loop (n==maxLength) will close the last stream
                    keyStreams[streamIndex].Close();
                    keyStreams[streamIndex] = null;
                }else{
                    //copy a byte into the result key
                    resultKeyStream.WriteByte( (byte)readByte );
                }
            }
        }
    }
    
    return resultKeyStream;
}

As you can see, we don’t have to change the algorithm at all. We just call GetKeyStream before hiding or extracting the message, then we can pass the complete key stream to HideOrExtract(). After that, we have one picture containing all hidden information. Again, we are facing the problem of transfer.

Spread the information over the images

If you're really paranoid, you won’t trust your mailbox. Of course, you won’t trust the postman either. That means, you don’t dare sending the carrier bitmap in one piece. That is no problem anymore, because you can hide one message in many bitmaps. It is quite similar to writing text across a couple of pages. According to this application, it means spreading the pixels over multiple images.

multiple sheets of paper

multiple images

You can send each image in a separate E-mail, post them in different mailboxes, or store them on different discs. The GUI allows selecting carrier bitmaps the same way as selecting key files. The selection is stored as an array of CarrierImages.

public struct CarrierImage{
    //file name of the clean image
    public String sourceFileName;
    //file name to save the new image
    public String resultFileName;
    //width * height
    public long countPixels;
    //produce colorful (false) or grayscale noise (true) for this picture
    public bool useGrayscale;
    //how many bytes will be hidden in this image - 
    //this field is set by CryptUtility.HideOrExtract()
    public long messageBytesToHide;

    public CarrierImage(String sourceFileName, 
      String resultFileName, long countPixels, 
      bool useGrayscale)
    {
        this.sourceFileName = sourceFileName;
        this.resultFileName = resultFileName;
        this.countPixels = countPixels;
        this.useGrayscale = useGrayscale;
        this.messageBytesToHide = 0;
    }
}

Larger images can hide more bytes (more pixels) than smaller images. This application uses the most simple distribution:

//calculate count of message-bytes to hide in (or extract from) each image 
for(int n=0; n<imageFiles.Length; n++){
    //pixels = count of pixel in this image / count of all available pixels
    float pixels = (float)imageFiles[n].countPixels / (float)countPixels;
    //this image will hide (length of the message * pixels) bytes
    imageFiles[n].messageBytesToHide = 
      (long)Math.Ceiling( (float)messageLength * pixels );
}

Now, we start with the first carrier bitmap, loop over the message, hide a number of bytes, switch to the second carrier bitmap, and so on.

//Current position in the carrier bitmap
//Start with 1, because (0,0) contains the message length
Point pixelPosition = new Point(1,0);

//Count of bytes already hidden in the current image
int countBytesInCurrentImage = 0;

//Index of the currently used image
int indexBitmaps = 0;

//Loop over the message and hide each byte
for(int messageIndex=0; messageIndex<messageLength; messageIndex++){
    //Calculate the position of the next pixel
    //...

    //Proceed to the next bitmap
    if(countBytesInCurrentImage == 
       imageFiles[indexBitmaps].messageBytesToHide)
    {
        indexBitmaps++;
        pixelPosition.Y = 0;
        countBytesInCurrentImage = 0;
        bitmapWidth = bitmaps[indexBitmaps].Width-1;
        bitmapHeight = bitmaps[indexBitmaps].Height-1;
        if(pixelPosition.X > bitmapWidth){ pixelPosition.X = 0; }
    }
    
    //hide or extract a byte
    //...
    
    countBytesInCurrentImage++;
}

In the end, we must save the new images. Each image can be saved using a different format (BMP, TIFF or PNG). The new format has nothing to do with the format of the original image file. That means, you can select a BMP, two PNG and a TIFF file as carrier images, and save the results into three TIFF and one PNG file.

//...
    for(indexBitmaps=0; indexBitmaps<bitmaps.Length; indexBitmaps++){
        if( ! extract ){ //Extract mode does not change any images
            //Save the resulting image and close the source file
            SaveBitmap( bitmaps[indexBitmaps], 
               imageFiles[indexBitmaps].resultFileName );
        }
    }
//...

private static void SaveBitmap(Bitmap bitmap, String fileName){
    String fileNameLower = fileName.ToLower();
    
    //Get the format from the file extension
    System.Drawing.Imaging.ImageFormat format = 
                   System.Drawing.Imaging.ImageFormat.Bmp;            
    if((fileNameLower.EndsWith("tif"))||
             (fileNameLower.EndsWith("tiff")))
    {
        format = System.Drawing.Imaging.ImageFormat.Tiff;
    }
    else if(fileNameLower.EndsWith("png"))
    {
        format = System.Drawing.Imaging.ImageFormat.Png;
    }
        
    //copy the bitmap
    Image img = new Bitmap(bitmap);
        
    //close bitmap file
    bitmap.Dispose();
    //save new bitmap
    img.Save(fileName, format);
    img.Dispose();
}

Lazy users

Somebody asked me for a compiled application. So, if (for whatever reasons) you do not want to mess with the code, you can now play with the binary, available with the downloads.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Corinna John
Software Developer
Germany Germany
Member
Corinna lives in Hannover/Germany (CeBIT City) and works as a Delphi developer, though her favorite language is C#.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionkey filememberHidden_Code1 Sep '07 - 7:19 
I want to ask you about "key file" , you mean of thet the message or you but key inside the image rather than the message and use it to combine with the password by XOR ?
AnswerRe: key filememberCorinna John1 Sep '07 - 10:54 
Hi,
could you please try and find easier words for your question? I really don't understand what you mean. English is not my native language, so I need proper grammar to get the point in a sentence.
Confused | :confused:
 

____________________________________
There is no proof for this sentence.

QuestionRe: key filememberHidden_Code1 Sep '07 - 22:24 
I am sorry about my bad language.
 
about my question,
If i understand your program the sender and resever most know the password for each key file, so what is "key file" and where it saved? with the message inside the image or not?
And there is one key file for each image or more than one?
And the key result after XOR with the password it saved in one pixel in the image or more than one pixel?
 
Sorry about my long question Rose | [Rose]
AnswerRe: key filememberCorinna John2 Sep '07 - 10:21 
Hi,
 
Hidden_Code wrote:
the sender and resever must know the password for each key file

 
They arrange a key and password in advance. It is a little like PGP, where the sender must have your public key to send you encrypted messages.
 
Hidden_Code wrote:
what is "key file" and where it saved?

 
A key file can be any file. Its content is read as a binary stream. The stream is combined with the password to make it useless for people who own the file but not the word, just like a PGP private key without the mantra.
 
Hidden_Code wrote:
with the message inside the image or not?

 
The key/password is not saved at all. The users should keep their keys in a save place. The image contains only the message itself, which can be read only by people who have the key and know the password.
 
Hidden_Code wrote:
And there is one key file for each image or more than one?

 
The user interface allows you to define many keys for one image. But then all keys are merged into one long key. This long key is used to distribute the message over the pixels.
 
Hidden_Code wrote:
And the key result after XOR with the password it saved in one pixel in the image or more than one pixel?

 
The key/password is only used to calculate an index. A part of the message is hidden in the pixel at that index, but nothing about the key is stored.
 
I hope I could help you Rose | [Rose]
 

____________________________________
There is no proof for this sentence.

GeneralRe: key filememberHidden_Code3 Sep '07 - 5:58 
Thanks. This a pest answer and it help me so much Rose | [Rose]
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 4 Apr 2004
Article Copyright 2003 by Corinna John
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid