Ejemplo n.º 1
0
        /// <summary>
        /// Hides a <see cref="StegoMessage"/> object inside a
        /// <see cref="StegoImage"/> object.
        /// </summary>
        /// <param name="carrierImage">The carrier image which is to be used to hide the message</param>
        /// <param name="message">The message which is to be hidden inside the carrier image</param>
        /// <param name="stegoPassword">The password which is to be used to hide the message (Randomized Hide and Seek)</param>
        /// <param name="bitPlanes">The amount of bit planes that are to be used to hide the message</param>
        /// <param name="bitPlanesFirst">true for bit planes first-mode and false for pixels first-mode</param>
        /// <exception cref="MessageNameTooBigException"></exception>
        /// <exception cref="MessageTooBigException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="FormatException"></exception>
        /// <exception cref="EncoderFallbackException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="System.Reflection.TargetInvocationException"></exception>
        /// <exception cref="Exception"></exception>
        /// <returns>The stego image containing the hidden message</returns>
        public StegoImage HideMessage(
            StegoImage carrierImage,
            StegoMessage message,
            string stegoPassword,
            int bitPlanes,
            bool bitPlanesFirst)
        {
            // Check if a password is set
            bool passwordSet = !(stegoPassword.Equals(""));

            // Generate stego image (Pass by reference)
            StegoImage stegoImage = new StegoImage(
                new Bitmap(
                    carrierImage.Image
                    ),
                carrierImage.Name,
                carrierImage.SizeInBytes
                );

            // Rename the stego image
            stegoImage.Name = "stegged_" + Path.GetFileNameWithoutExtension(stegoImage.Name) + ".png";

            // Base variable declaration
            int  carrierWidth     = carrierImage.Image.Width;
            int  carrierHeight    = carrierImage.Image.Height;
            uint maxCarrierPixels = (uint)(carrierWidth * carrierHeight);
            uint pixelDistance    = 1;

            // Get the binary string of a message object and its length
            string completeMessage       = GenerateMessageBitPattern(message);
            uint   completeMessageLength = (uint)completeMessage.Length;

            // Throw exception if the message is too big
            uint carrierCapacity = CalculateCapacity(carrierImage, bitPlanes);

            if (completeMessageLength > carrierCapacity * 8)
            {
                throw new MessageTooBigException();
            }

            // If a stego password is specified, scramble the image and change the pixelDistance value
            if (passwordSet)
            {
                // Transform the password into a value defining the distance between pixels
                byte[] passwordBytes = Encoding.UTF8.GetBytes(stegoPassword);
                passwordBytes = Hasher.HashSha256(passwordBytes);
                pixelDistance = (uint)((ulong)BitConverter.ToInt64(passwordBytes, 0) % maxCarrierPixels);

                // Scramble image
                stegoImage.Image = ImageScrambler.ScrambleOne(stegoImage.Image);
                stegoImage.Image = ImageScrambler.ScrambleThree(stegoImage.Image);
                stegoImage.Image = ImageScrambler.ScrambleTwo(stegoImage.Image);
            }

            // Hiding variables
            int  messageBitCounter = 0;                 // Counter iterating over all message bits
            uint currentPixel      = 0;                 // Variable storing the currently considered pixel
            uint restClassCounter  = 0;                 // Counter iterating over all rest classes
            byte bitPlaneSelector  = 0;                 // Variable used to select which bit plane is used for embedding

            // While there is something left to hide
            while (messageBitCounter < completeMessageLength)
            {
                var currentPixelXValue = (int)(currentPixel % carrierWidth);                    // x coordinate of current pixel
                var currentPixelYValue = (int)(currentPixel / carrierWidth);                    // y coordinate of current pixel

                // Get current pixel
                var pixel = stegoImage.Image.GetPixel(currentPixelXValue, currentPixelYValue);                  // Pixel object used to generate the new color

                // Define which of the three LSBs of a pixel should be used
                byte color;
                switch (messageBitCounter % 3)
                {
                case 0:
                    color = SetBit(pixel.R, completeMessage[messageBitCounter].ToString(), bitPlaneSelector);
                    stegoImage.Image.SetPixel(currentPixelXValue, currentPixelYValue, Color.FromArgb(color, pixel.G, pixel.B));
                    break;

                case 1:
                    color = SetBit(pixel.G, completeMessage[messageBitCounter].ToString(), bitPlaneSelector);
                    stegoImage.Image.SetPixel(currentPixelXValue, currentPixelYValue, Color.FromArgb(pixel.R, color, pixel.B));
                    break;

                case 2:
                    color = SetBit(pixel.B, completeMessage[messageBitCounter].ToString(), bitPlaneSelector);
                    stegoImage.Image.SetPixel(currentPixelXValue, currentPixelYValue, Color.FromArgb(pixel.R, pixel.G, color));

                    // If the whole bit plane should be filled before a pixel should be reused
                    if (bitPlanesFirst)
                    {
                        // Go to the next pixel
                        currentPixel += pixelDistance;

                        // If the pixel exceeds the maximum amount of pixels
                        // go to the next rest class
                        if (currentPixel >= maxCarrierPixels)
                        {
                            currentPixel = ++restClassCounter;

                            // If all rest classes are exhausted,
                            // begin at pixel 0 again but go to the next bit plane
                            if (restClassCounter >= pixelDistance)
                            {
                                currentPixel     = 0;
                                restClassCounter = 0;
                                bitPlaneSelector++;
                            }
                        }

                        // If a pixel should be used in full before the next pixel is chosen
                    }
                    else
                    {
                        // Go to the next bit plane
                        bitPlaneSelector++;

                        // If all allowed bits of a single pixel are used
                        // go to the next pixel
                        if (bitPlaneSelector >= bitPlanes)
                        {
                            currentPixel    += pixelDistance;
                            bitPlaneSelector = 0;

                            // If the pixel exceeds the maximum amount of pixels
                            // go to the next rest class
                            if (currentPixel >= maxCarrierPixels)
                            {
                                currentPixel = ++restClassCounter;
                            }
                        }         // if
                    }             // else
                    break;
                }                 // switch
                messageBitCounter++;
            }

            // If a password has been used, scramble back the image
            if (passwordSet)
            {
                stegoImage.Image = ImageScrambler.ScrambleOne(ImageScrambler.ScrambleThree(ImageScrambler.ScrambleTwo(stegoImage.Image)));
            }
            return(stegoImage);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Extracts a <see cref="StegoMessage"/> object from a
        /// <see cref="StegoImage"/> object.
        /// </summary>
        /// <param name="stegoImage">The stego image which hides the message</param>
        /// <param name="stegoPassword">The password used to hide the message (Randomized Hide and Seek)</param>
        /// <param name="bitPlanes">The amount of bit planes that have been used to hide the message</param>
        /// <param name="bitPlanesFirst">true for bit planes first-mode and false for pixels first-mode</param>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="System.Reflection.TargetInvocationException"></exception>
        /// <exception cref="FormatException"></exception>
        /// <exception cref="OverflowException"></exception>
        /// <exception cref="DecoderFallbackException"></exception>
        /// <exception cref="Exception"></exception>
        /// <returns>The message that has been hidden inside the stego image</returns>
        public StegoMessage ExtractMessage(StegoImage stegoImage,
                                           string stegoPassword,
                                           int bitPlanes,
                                           bool bitPlanesFirst)
        {
            // Generate new stego image object to omit f*****g pass by reference
            Bitmap stegoImageCopy = new Bitmap(stegoImage.Image);

            // Set extraction option
            bool passwordSet = !(stegoPassword.Equals(""));

            // Base variable declaration
            StringBuilder messageNameBuilder = new StringBuilder();
            StringBuilder payloadSizeBuilder = new StringBuilder();
            StringBuilder payloadBuilder     = new StringBuilder();
            int           stegoWidth         = stegoImageCopy.Width;
            int           stegoHeight        = stegoImageCopy.Height;
            uint          maxStegoPixels     = (uint)(stegoWidth * stegoHeight);
            uint          pixelDistance      = 1;

            // If a stego password is specified
            if (passwordSet)
            {
                // Transform the password into a value defining the distance between pixels
                byte[] passwordBytes = Encoding.UTF8.GetBytes(stegoPassword);
                passwordBytes = Hasher.HashSha256(passwordBytes);
                pixelDistance = (uint)((ulong)BitConverter.ToInt64(passwordBytes, 0) % maxStegoPixels);

                // Scramble image
                stegoImageCopy = ImageScrambler.ScrambleOne(stegoImageCopy);
                stegoImageCopy = ImageScrambler.ScrambleThree(stegoImageCopy);
                stegoImageCopy = ImageScrambler.ScrambleTwo(stegoImageCopy);
            }

            // Variables for LSB extraction
            int   messageBitCounter = 0;            // Counter iterating over all message bits
            Color pixel;                            // Pixel object used to generate the new color
            int   currentPixelXValue;               // x coordinate of current pixel
            int   currentPixelYValue;               // y coordinate of current pixel
            uint  currentPixel     = 0;             // Variable storing the currently considered pixel
            uint  restClassCounter = 0;             // Counter iterating over all rest classes
            byte  bitPlaneSelector = 0;             // Variable used to select which bit plane is used for embedding

            // Extract the first 512 bits which encode the message's name
            while (messageBitCounter < 512)
            {
                // Get current pixel
                currentPixelXValue = (int)currentPixel % stegoWidth;
                currentPixelYValue = (int)currentPixel / stegoWidth;
                pixel = stegoImageCopy.GetPixel(currentPixelXValue, currentPixelYValue);

                switch (messageBitCounter % 3)
                {
                case 0:
                    messageNameBuilder.Append(GetBit(pixel.R, bitPlaneSelector));
                    break;

                case 1:
                    messageNameBuilder.Append(GetBit(pixel.G, bitPlaneSelector));
                    break;

                case 2:
                    messageNameBuilder.Append(GetBit(pixel.B, bitPlaneSelector));

                    // If the whole bit plane should be filled before a pixel should be reused
                    if (bitPlanesFirst)
                    {
                        // Go to the next pixel
                        currentPixel += pixelDistance;

                        // If the pixel exceeds the maximum amount of pixels
                        // go to the next rest class
                        if (currentPixel >= maxStegoPixels)
                        {
                            currentPixel = ++restClassCounter;

                            // If all rest classes are exhausted,
                            // begin at pixel 0 again but go to the next bit plane
                            if (restClassCounter >= pixelDistance)
                            {
                                currentPixel     = 0;
                                restClassCounter = 0;
                                bitPlaneSelector++;
                            }
                        }

                        // If a pixel should be used in full before the next pixel is chosen
                    }
                    else
                    {
                        // Go to the next bit plane
                        bitPlaneSelector++;

                        // If all allowed bits of a single pixel are used
                        // go to the next pixel
                        if (bitPlaneSelector >= bitPlanes)
                        {
                            currentPixel    += pixelDistance;
                            bitPlaneSelector = 0;

                            // If the pixel exceeds the maximum amount of pixels
                            // go to the next rest class
                            if (currentPixel >= maxStegoPixels)
                            {
                                currentPixel = ++restClassCounter;
                            }
                        }         // if
                    }             // else
                    break;
                }                 // switch
                messageBitCounter++;
            }

            // Compose the message's name
            string messageNameString = messageNameBuilder.ToString();
            string messageName       = Converter.BinaryToString(messageNameString).Replace("\0", "");

            // Extract the next 24 bits which encode the message's payload size
            while (messageBitCounter < 536)
            {
                // Get current pixel
                currentPixelXValue = (int)currentPixel % stegoWidth;
                currentPixelYValue = (int)currentPixel / stegoWidth;
                pixel = stegoImageCopy.GetPixel(currentPixelXValue, currentPixelYValue);

                switch (messageBitCounter % 3)
                {
                case 0:
                    payloadSizeBuilder.Append(GetBit(pixel.R, bitPlaneSelector));
                    break;

                case 1:
                    payloadSizeBuilder.Append(GetBit(pixel.G, bitPlaneSelector));
                    break;

                case 2:
                    payloadSizeBuilder.Append(GetBit(pixel.B, bitPlaneSelector));

                    // If the whole bit plane should be filled before a pixel should be reused
                    if (bitPlanesFirst)
                    {
                        // Go to the next pixel
                        currentPixel += pixelDistance;

                        // If the pixel exceeds the maximum amount of pixels
                        // go to the next rest class
                        if (currentPixel >= maxStegoPixels)
                        {
                            currentPixel = ++restClassCounter;

                            // If all rest classes are exhausted,
                            // begin at pixel 0 again but go to the next bit plane
                            if (restClassCounter >= pixelDistance)
                            {
                                currentPixel     = 0;
                                restClassCounter = 0;
                                bitPlaneSelector++;
                            }
                        }

                        // If a pixel should be used in full before the next pixel is chosen
                    }
                    else
                    {
                        // Go to the next bit plane
                        bitPlaneSelector++;

                        // If all allowed bits of a single pixel are used
                        // go to the next pixel
                        if (bitPlaneSelector >= bitPlanes)
                        {
                            currentPixel    += pixelDistance;
                            bitPlaneSelector = 0;

                            // If the pixel exceeds the maximum amount of pixels
                            // go to the next rest class
                            if (currentPixel >= maxStegoPixels)
                            {
                                currentPixel = ++restClassCounter;
                            }
                        }         // if
                    }             // else
                    break;
                }                 // switch
                messageBitCounter++;
            }

            // Compose the payloads's size
            string payloadSizeString = payloadSizeBuilder.ToString();
            uint   payloadSize       = Converter.BinaryToUint(payloadSizeString);

            // Extract the payload
            while (messageBitCounter < payloadSize + 536)
            {
                // Get current pixel
                currentPixelXValue = (int)currentPixel % stegoWidth;
                currentPixelYValue = (int)currentPixel / stegoWidth;
                pixel = stegoImageCopy.GetPixel(currentPixelXValue, currentPixelYValue);

                switch (messageBitCounter % 3)
                {
                case 0:
                    payloadBuilder.Append(GetBit(pixel.R, bitPlaneSelector));
                    break;

                case 1:
                    payloadBuilder.Append(GetBit(pixel.G, bitPlaneSelector));
                    break;

                case 2:
                    payloadBuilder.Append(GetBit(pixel.B, bitPlaneSelector));

                    // If the whole bit plane should be filled before a pixel should be reused
                    if (bitPlanesFirst)
                    {
                        // Go to the next pixel
                        currentPixel += pixelDistance;

                        // If the pixel exceeds the maximum amount of pixels
                        // go to the next rest class
                        if (currentPixel >= maxStegoPixels)
                        {
                            currentPixel = ++restClassCounter;

                            // If all rest classes are exhausted,
                            // begin at pixel 0 again but go to the next bit plane
                            if (restClassCounter >= pixelDistance)
                            {
                                currentPixel     = 0;
                                restClassCounter = 0;
                                bitPlaneSelector++;
                            }
                        }

                        // If a pixel should be used in full before the next pixel is chosen
                    }
                    else
                    {
                        // Go to the next bit plane
                        bitPlaneSelector++;

                        // If all allowed bits of a single pixel are used
                        // go to the next pixel
                        if (bitPlaneSelector >= bitPlanes)
                        {
                            currentPixel    += pixelDistance;
                            bitPlaneSelector = 0;

                            // If the pixel exceeds the maximum amount of pixels
                            // go to the next rest class
                            if (currentPixel >= maxStegoPixels)
                            {
                                currentPixel = ++restClassCounter;
                            }
                        }         // if
                    }             // else
                    break;
                }                 // switch
                messageBitCounter++;
            }

            // Compose the message object
            string payloadString = payloadBuilder.ToString();

            byte[]       payload = payloadString.ConvertBitstringToByteArray();
            StegoMessage message = new StegoMessage(messageName, payload);

            return(message);
        }