/// <summary> /// Extracts a message from a stego image and returns it as byte array /// </summary> /// <param name="stegoImage"></param> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="Exception"></exception> /// <exception cref="FormatException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="DecoderFallbackException"></exception> /// <returns></returns> public Message ExtractMessage(Bitmap stegoImage, string password) { // If no password is specified, the default routine should be used // because it has been well tested and should be more robust if (password == null || password.Equals("")) { return(ExtractMessage(stegoImage)); } // Scramble image stegoImage = ImageScrambler.ScrambleOne(stegoImage); stegoImage = ImageScrambler.ScrambleThree(stegoImage); stegoImage = ImageScrambler.ScrambleTwo(stegoImage); // Base variable declaration StringBuilder messageNameBuilder = new StringBuilder(); StringBuilder payloadSizeBuilder = new StringBuilder(); StringBuilder payloadBuilder = new StringBuilder(); int stegoWidth = stegoImage.Width; int stegoHeight = stegoImage.Height; long maxStegoPixels = (stegoWidth * stegoHeight); // Transform the password into a value defining the distance between pixels byte[] passwordBytes = Encoding.UTF8.GetBytes(password); passwordBytes = Hasher.HashSha256(passwordBytes); uint pixelDistance = (uint)((ulong)BitConverter.ToInt64(passwordBytes, 0) % (uint)maxStegoPixels); //uint pixelDistance = 42; //foreach (byte by in passwordBytes) { // pixelDistance += (uint)(by*by); // //pixelDistance += by; //} //pixelDistance = (uint) (pixelDistance % maxStegoPixels); // Variables for LSB extraction int messageBitCounter = 0; // Counter iterating over all message bits Color pixel; // Pixel object used to generate the new color int currentPixelX; // x coordinate of current pixel int currentPixelY; // y coordinate of current pixel uint currentPixel = 0; // Variable storing the currently considered pixel uint restClassCounter = 0; // Counter iterating over all rest classes uint payloadSize = 0; // Variable indicating the size of the payload string messageName = ""; // String storing the name of the message // Extract the first 512 bits which encode // the message's payload size and the message's name while (messageBitCounter < 512) { // Get current pixel currentPixelX = (int)currentPixel % stegoWidth; currentPixelY = (int)currentPixel / stegoWidth; pixel = stegoImage.GetPixel(currentPixelX, currentPixelY); switch (messageBitCounter % 3) { case 0: messageNameBuilder.Append(ExtractLsbAsString(pixel.R)); break; case 1: messageNameBuilder.Append(ExtractLsbAsString(pixel.G)); break; case 2: messageNameBuilder.Append(ExtractLsbAsString(pixel.B)); // Go to the next pixel currentPixel += pixelDistance; if (currentPixel >= maxStegoPixels) { currentPixel = ++restClassCounter; } break; default: break; } messageBitCounter++; } // Compose the message's name string messageNameString = messageNameBuilder.ToString(); messageName = Converter.BinaryToString(messageNameString).Replace("\0", ""); // Extract the payload's size while (messageBitCounter < 536) { // Get current pixel currentPixelX = (int)currentPixel % stegoWidth; currentPixelY = (int)currentPixel / stegoWidth; pixel = stegoImage.GetPixel(currentPixelX, currentPixelY); switch (messageBitCounter % 3) { case 0: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.R)); break; case 1: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.G)); break; case 2: payloadSizeBuilder.Append(ExtractLsbAsString(pixel.B)); // Go to the next pixel currentPixel += pixelDistance; if (currentPixel >= maxStegoPixels) { currentPixel = ++restClassCounter; } break; default: break; } messageBitCounter++; } // Compose the payloads's size string payloadSizeString = payloadSizeBuilder.ToString(); payloadSize = Converter.BinaryToUint(payloadSizeString); // Extract the payload while (messageBitCounter < payloadSize + 536) { // Get current pixel currentPixelX = (int)currentPixel % stegoWidth; currentPixelY = (int)currentPixel / stegoWidth; pixel = stegoImage.GetPixel(currentPixelX, currentPixelY); switch (messageBitCounter % 3) { case 0: payloadBuilder.Append(ExtractLsbAsString(pixel.R)); break; case 1: payloadBuilder.Append(ExtractLsbAsString(pixel.G)); break; case 2: payloadBuilder.Append(ExtractLsbAsString(pixel.B)); // Go to the next pixel currentPixel += pixelDistance; if (currentPixel >= maxStegoPixels) { currentPixel = ++restClassCounter; } break; default: break; } messageBitCounter++; } // Compose the message object string payloadString = payloadBuilder.ToString(); byte[] payload = Extensions.ConvertBitstringToByteArray(payloadString); Message message = new Message(messageName, payload, payloadSize); return(message); }
/// <summary> /// Calculates the Hamming distance between a full message (not only the payload) /// and the LSBs of an image and returns them as integer /// </summary> /// <param name="carrierImage"></param> /// <param name="message"></param> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="MessageTooBigException"></exception> /// <returns></returns> public int CalculateHammingDistance(Bitmap carrierImage, string completeMessage, string password) { // If no password is specified, the default routine should be used if (password == null || password.Equals("")) { return(CalculateHammingDistance(carrierImage, completeMessage)); } // Get all necessary information about the carrier uint carrierWidth = (uint)carrierImage.Width; uint carrierHeight = (uint)carrierImage.Height; uint maxCarrierPixels = (carrierWidth * carrierHeight); uint capacity = (3 * maxCarrierPixels); // Pipe the exception handling a level higher if (completeMessage.Length > capacity) { throw new MessageTooBigException(); } // Scramble image carrierImage = ImageScrambler.ScrambleOne(carrierImage); carrierImage = ImageScrambler.ScrambleThree(carrierImage); carrierImage = ImageScrambler.ScrambleTwo(carrierImage); // Transform the password into a value defining the distance between pixels byte[] passwordBytes = Encoding.UTF8.GetBytes(password); passwordBytes = Hasher.HashSha256(passwordBytes); uint pixelDistance = (uint)((ulong)BitConverter.ToInt64(passwordBytes, 0) % maxCarrierPixels); //uint pixelDistance = 0; //foreach (byte by in passwordBytes) { // pixelDistance += by; //} //pixelDistance = (uint) (pixelDistance % maxCarrierPixels); // Variables int hammingDistance = 0; // Current hamming distance Color pixel; // Pixel object used to generate the new color int currentPixelX; // x coordinate of current pixel int currentPixelY; // y coordinate of current pixel uint currentPixel = 0; // Variable storing the currently considered pixel uint restClassCounter = 0; // Counter iterating over all rest classes int messageBitCounter = 0; // Counter iterating over all message bits foreach (char bit in completeMessage) { // Get current pixel currentPixelX = (int)(currentPixel % carrierWidth); currentPixelY = (int)(currentPixel / carrierWidth); pixel = carrierImage.GetPixel(currentPixelX, currentPixelY); // Define which of the three LSBs of a pixel should be checked char extractedLsb = 'F'; switch (messageBitCounter % 3) { case 0: extractedLsb = ExtractLsbAsChar(pixel.R); break; case 1: extractedLsb = ExtractLsbAsChar(pixel.G); break; case 2: extractedLsb = ExtractLsbAsChar(pixel.B); // Go to the next pixel currentPixel += pixelDistance; if (currentPixel >= maxCarrierPixels) { currentPixel = ++restClassCounter; } break; default: break; } messageBitCounter++; // Increase Hamming distance if message bit and LSB do not match if (!Char.Equals(bit, extractedLsb)) { hammingDistance++; } } return(hammingDistance); }
/// <summary> /// Hides a given message inside a given carrier (image). /// Thereby, a password indicates the distance between used pixels /// </summary> /// <param name="carrierImage"></param> /// <param name="payload"></param> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="EncoderFallbackException"></exception> /// <exception cref="FormatException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="MessageNameTooBigException"></exception> /// <exception cref="Exception"></exception> /// <returns></returns> public Bitmap HideMessage(Bitmap carrierImage, Message message, string password) { // If no password is specified, the default routine should be used // because it has been well tested and should be more robust if (password == null || password.Equals("")) { return(HideMessage(carrierImage, message)); } // Scramble image carrierImage = ImageScrambler.ScrambleOne(carrierImage); carrierImage = ImageScrambler.ScrambleThree(carrierImage); carrierImage = ImageScrambler.ScrambleTwo(carrierImage); // Base variable declaration Scanner scanner = Scanner.Instance; Bitmap stegoImage = carrierImage; uint carrierWidth = (uint)carrierImage.Width; uint carrierHeight = (uint)carrierImage.Height; ulong maxCarrierPixels = (carrierWidth * carrierHeight); // Get the binary string of a message object and get its length string completeMessage = GenerateMessageBitPattern(message); uint completeMessageLength = (uint)completeMessage.Length; // Throw exception if the message is too big uint carrierCapacity = scanner.CalculateCapacity(carrierImage, "bits"); if (completeMessageLength > carrierCapacity) { throw new MessageTooBigException(); } // Transform the password into a value defining the distance between pixels byte[] passwordBytes = Encoding.UTF8.GetBytes(password); passwordBytes = Hasher.HashSha256(passwordBytes); uint pixelDistance = (uint)((ulong)BitConverter.ToInt64(passwordBytes, 0) % maxCarrierPixels); //uint pixelDistance = 1; //foreach (byte by in passwordBytes) { // pixelDistance += (uint) (by * by); // //pixelDistance += by; //} //pixelDistance = (uint) (pixelDistance % maxCarrierPixels); // Hiding variables int messageBitCounter = 0; // Counter iterating over all message bits Color pixel; // Pixel object used to generate the new color byte color = 0x00; // Variable storing an RGB color value int currentPixelX; // x coordinate of current pixel int currentPixelY; // y coordinate of current pixel uint currentPixel = 0; // Variable storing the currently considered pixel uint restClassCounter = 0; // Counter iterating over all rest classes // While there is something left to hide while (messageBitCounter < completeMessageLength) { // Get current pixel currentPixelX = (int)(currentPixel % carrierWidth); currentPixelY = (int)(currentPixel / carrierWidth); pixel = stegoImage.GetPixel(currentPixelX, currentPixelY); // Define which of the three LSBs of a pixel should be used switch (messageBitCounter % 3) { case 0: color = InsertMessageBit(pixel.R, completeMessage[messageBitCounter].ToString()); stegoImage.SetPixel(currentPixelX, currentPixelY, Color.FromArgb(color, pixel.G, pixel.B)); break; case 1: color = InsertMessageBit(pixel.G, completeMessage[messageBitCounter].ToString()); stegoImage.SetPixel(currentPixelX, currentPixelY, Color.FromArgb(pixel.R, color, pixel.B)); break; case 2: color = InsertMessageBit(pixel.B, completeMessage[messageBitCounter].ToString()); stegoImage.SetPixel(currentPixelX, currentPixelY, Color.FromArgb(pixel.R, pixel.G, color)); // Go to the next pixel currentPixel += pixelDistance; if (currentPixel >= maxCarrierPixels) { currentPixel = ++restClassCounter; } break; default: break; } messageBitCounter++; } stegoImage = ImageScrambler.ScrambleOne(ImageScrambler.ScrambleThree(ImageScrambler.ScrambleTwo(stegoImage))); //stegoImage = ImageScrambler.ScrambleThree(ImageScrambler.ScrambleOne(stegoImage)); //stegoImage = ImageScrambler.ScrambleThree(stegoImage); return(stegoImage); }