5,667,575 members and growing! (16,043 online)
Email Password   helpLost your password?
General Programming » Cryptography & Security » Security     Intermediate License: The Code Project Open License (CPOL)

Steganography VIII - Hiding Data in Wave Audio Files

By Corinna John

How to hide data of any kind inside a sound.
C#, Windows, .NET 1.0, .NET 1.1, .NETVisual Studio, VS.NET2002, Dev

Posted: 5 May 2004
Updated: 5 May 2004
Views: 64,234
Bookmarked: 40 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
20 votes for this Article.
Popularity: 4.69 Rating: 3.61 out of 5
3 votes, 15.0%
1
0 votes, 0.0%
2
2 votes, 10.0%
3
2 votes, 10.0%
4
13 votes, 65.0%
5

Introduction

Now that we have hidden data in bitmaps, MIDI tracks and .NET assemblies, you might miss one important file format. You might miss the files that can hide lots of bytes without becoming larger, and can be generated in a few seconds, so that you don't have to store the original files on your disk. It is time to add Wave Audio to the list.

This article uses code from A full-duplex audio player in C# using the waveIn/waveOut APIs.

The Wave File Format

Have you ever looked at a Wave file in a HEX editor? It starts like that, and continues with unreadable binary data:

Every RIFF file starts with the text "RIFF", followed by the Int32 length of the entire file:

The next fields say that this RIFF file contains Wave data and open the format chunk:

The length of the following format chunk must be 16 for PCM files:

Now the format is being specified by a WAVEFORMATEX structure:

The format chunk can be followed by some extra information. Then the interesting parts begin with the data chunk.

The data chunk contains all the Wave samples. That means the rest of the file is pure audio data. Little changes might be hearable, but won't destroy the file.

Hiding the Message

Hiding a message in Wave samples is very similar to hiding it in the pixels of a bitmap. Again, we use a key stream to skip a number of carrier units (samples/pixels), grab one carrier unit, put one bit of the message into the lowest bit of the carrier unit, and write the changed unit to the destination stream. When the entire message has been hidden like that, we copy the rest of the carrier stream.

public void Hide(Stream messageStream, Stream keyStream){
    
    byte[] waveBuffer = new byte;
    byte message, bit, waveByte;
    int messageBuffer; //receives the next byte of the message or -1

    int keyByte; //distance of the next carrier sample

    
    //loop over the message, hide each byte

    while( (messageBuffer=messageStream.ReadByte()) >= 0 ){
        //read one byte of the message stream

        message = (byte)messageBuffer;
        
        //for each bit in [message]

        for(int bitIndex=0; bitIndex<8; bitIndex++){
            
            //read a byte from the key

            keyByte = GetKeyValue(keyStream);
            
            //skip a couple of samples

            for(int n=0; n<keyByte-1; n++){
                //copy one sample from the clean stream to the carrier stream

                sourceStream.Copy(
                    waveBuffer, 0,
                    waveBuffer.Length, destinationStream);
            }

            //read one sample from the wave stream

            sourceStream.Read(waveBuffer, 0, waveBuffer.Length);
            waveByte = waveBuffer;
            
            //get the next bit from the current message byte...

            bit = (byte)(((message & (byte)(1 << bitIndex)) > 0) ? 1 : 0);
                
            //...place it in the last bit of the sample

            if((bit == 1) && ((waveByte % 2) == 0)){
                waveByte += 1;
            }else if((bit == 0) && ((waveByte % 2) == 1)){
                waveByte -= 1;
            }

            waveBuffer = waveByte;

            //write the result to destinationStream

            destinationStream.Write(waveBuffer, 0, bytesPerSample);
        }
    }

    //copy the rest of the wave without changes

    //...

}

Extracting the Message

Again, we use the key stream to locate the right samples, just as we did while hiding the message. Then we read the last bit of the sample and shift it into the current byte of the message. When the byte is complete, we write it into the message stream and continue with the next one.

public void Extract(Stream messageStream, Stream keyStream){

    byte[] waveBuffer = new byte;
    byte message, bit, waveByte;
    int messageLength = 0; //expected length of the message

    int keyByte; //distance of the next carrier sample

    
    while( (messageLength==0 || messageStream.Length<messageLength) ){
        //clear the message-byte

        message = 0;
        
        //for each bit in [message]

        for(int bitIndex=0; bitIndex<8; bitIndex++){

            //read a byte from the key

            keyByte = GetKeyValue(keyStream);
            
            //skip a couple of samples

            for(int n=0; n<keyByte; n++){
                //read one sample from the wave stream

                sourceStream.Read(waveBuffer, 0, waveBuffer.Length);
            }
            waveByte = waveBuffer;
            
            //get the last bit of the sample...

            bit = (byte)(((waveByte % 2) == 0) ? 0 : 1);

            //...write it into the message-byte

            message += (byte)(bit << bitIndex);
        }

        //add the re-constructed byte to the message

        messageStream.WriteByte(message);
        
        if(messageLength==0 && messageStream.Length==4){
            //first 4 bytes contain the message's length

            //...

        }
    }
}

Recording a Wave

Keeping the original clean carriers can be dangerous. Somebody who has already got a carrier file with a secret message in it, and manages to get the original file without the hidden message, can easily compare the two files, count the distance in bytes between two non-equal samples, and quickly reconstruct the key.

That is why we have to delete and destroy our clean carrier files after we've used them once, or record a wave on the fly. Thanks to Ianier Munoz' WaveInRecorder, it is no problem to record Wave data and hide the message in it before saving anything to a disk. There is no original file, so we do not need to care about one. In the main form, the user can choose between using an existing Wave file or recording a sound right then. If he wants to record a unique, not reproducible sound, he can plug in a microphone and speak/play/... whatever he likes:

if(rdoSrcFile.Checked){
    //use a .wav file as the carrier

    //do not complain later on, you have been warned

    sourceStream = new FileStream(txtSrcFile.Text, FileMode.Open);
}else{
    //record a carrier wave

    frmRecorder recorder = new frmRecorder(countSamplesRequired);
    recorder.ShowDialog(this);
    sourceStream = recorder.RecordedStream;
}

frmRecorder is a small GUI for the WaveIn Recorder that counts the recorded samples and enables a Stop button when the sound is long enough to hide the specified message.

The new sound is stored in a MemoryStream and passed to WaveUtility. From now on, it does not matter where the stream came from, WaveUtility makes no difference between sounds read from a file or recorded on the fly.

WaveUtility utility = new WaveUtility(sourceStream, destinationStream);
utility.Hide(messageStream, keyStream);

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


Corinna lives in Hannover/Germany (CeBIT City), and works as a C#-developer for a social insurance.
Occupation: Software Developer
Location: Germany Germany

Other popular Cryptography & Security articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 34 (Total in Forum: 34) (Refresh)FirstPrevNext
GeneralMultiple watermarksmemberbrucelee058:18 21 Feb '08  
Generalhow many character that can stored in one file..?membermerdiansigit19:48 5 Jan '08  
GeneralAUDIO STEGANOGRAPHY [modified]memberpranav4u071:56 27 Dec '07  
Questionsteganography in audiomemberDamnape9:57 14 Nov '07  
AnswerRe: steganography in audiomemberCorinna John11:51 14 Nov '07  
GeneralRobustness Attackmemberlonelywind198218:10 4 Sep '07  
General.mp3, .jpeg in C#.NETmemberSoftware_Specialist12:52 24 Apr '07  
GeneralRe: .mp3, .jpeg in C#.NETmemberSoftware_Specialist6:22 3 May '07  
Generalsizemember21:56 21 Mar '07  
GeneralRe: sizememberCorinna John9:58 22 Mar '07  
Generalsizemember5:16 21 Mar '07  
GeneralRe: sizememberCorinna John6:15 21 Mar '07  
GeneralVS2005 Compiler WarningmemberMoomansun21:28 7 Sep '06  
Generalwavmembermaduram7:51 8 Feb '06  
GeneralRe: wavmemberCorinna John0:15 9 Feb '06  
Generalhow many character that can stored in one file?memberpinhard9:09 22 Jan '06  
GeneralRe: how many character that can stored in one file?memberCorinna John9:51 22 Jan '06  
Generalcomparing wave filessusskahuri7:15 4 May '05  
GeneralRe: comparing wave filesmemberJoshua M. Gauthier3:03 19 Jul '05  
GeneralAdd Wave header to MPEG audiomemberKenneta5:28 24 Jan '05  
GeneralRe: Add Wave header to MPEG audiomemberCorinna John6:45 24 Jan '05  
GeneralRe: Add Wave header to MPEG audiomemberKenneta21:40 24 Jan '05  
GeneralVC6++ HELP HOW???membercnncnn21:22 18 Aug '04  
GeneralRe: VC6++ HELP HOW???sussAnonymous23:43 7 Feb '05  
GeneralMore profissional Messagesmemberunitecsoft23:53 16 Jun '04  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 5 May 2004
Editor: Chris Maunder
Copyright 2004 by Corinna John
Everything else Copyright © CodeProject, 1999-2008
Web13 | Advertise on the Code Project