Search code examples
c#-3.0steganography

wrong password in image steganography


I got a project on image steganography( for lossless image). I’m trying to learn the process behind it. But when I run it to extract message ,an exception is generated for wrong password as well as for an empty-image(no data encoded) .It selects pixels randomly and uses hash set. But I don’t understand some steps in middle.I doubt whether any mistake in those steps cause exception. Moreover in some cases the project extracts hidden-message even for wrong passwords too. How can I solve these problems?

Apologize for this long code, but hoping a solution.. thanks for any help.

code is as shown below:

//encoding data in image

private void EncodeByte(Bitmap bm, Bitmap visible_bm, Random rand,
        byte value, HashSet<string> used_positions)
    {
        for (int i = 0; i < 8; i++)
        {
            // Pick a position for the ith bit.
            int row, col, pix;
            PickPosition(bm, rand, used_positions, out row, out col, out pix);

            // Get the color's pixel components.
            Color clr = bm.GetPixel(row, col);
            byte r = clr.R;
            byte g = clr.G;
            byte b = clr.B;

doubt: how the next bit to store is taken in the 'next two lines' shown below?

// Get the next bit to store.
            int bit = 0;
            if ((value & 1) == 1) bit = 1;  //’value’ is the byte to be encoded

// Update the color.
            switch (pix)
            {
                case 0:
                    r = (byte)((r & 0xFE) | bit);
                    break;
                case 1:
                    g = (byte)((g & 0xFE) | bit);
                    break;
                case 2:
                    b = (byte)((b & 0xFE) | bit);
                    break;
            }
            clr = Color.FromArgb(clr.A, r, g, b);
            bm.SetPixel(row, col, clr);

  // Move to the next bit in the value.
            value >>= 1;
        }
    }

  //decoding  image
  private string DecodeMessageInImage(Bitmap bm, string password)
    {
        // Initialize a random number generator.
        Random rand = new Random(obj.NumericPassword(password));

        // Create a new HashSet.
        HashSet<string> used_positions = new HashSet<string>();

        // Make a byte array big enough to hold the message length.
        int len = 0;
        byte[] bytes = BitConverter.GetBytes(len);

   // Decode the message length.
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = DecodeByte(bm, rand, used_positions);
        }
        len = BitConverter.ToInt32(bytes, 0);

doubt: Is the following check(if len>10000) is a proper one?

 // Sanity check.

        if(len>10000)
        {
            throw new InvalidDataException(
            "Message length " + len.ToString() +
                " is too big to make sense. Invalid password.");
        }

        // Decode the message bytes.
        char[] chars = new char[len];
        for (int i = 0; i < chars.Length; i++)
        {
            chars[i] = (char)DecodeByte(bm, rand, used_positions);
        }
        return new string(chars);
    }

    // Decode a byte.
    private byte DecodeByte(Bitmap bm, Random rand, HashSet<string>   used_positions)
    {

        byte value = 0;
        byte value_mask = 1;
        for (int i = 0; i < 8; i++)
        {
            // Find the position for the ith bit.
            int row, col, pix;
           obj.PickPosition(bm, rand, used_positions, out row, out col, out pix);

            // Get the color component value.
            byte color_value = 0;
            switch (pix)
            {
                case 0:
                    color_value = bm.GetPixel(row, col).R;
                    break;
                case 1:
                    color_value = bm.GetPixel(row, col).G;
                    break;
                case 2:
                    color_value = bm.GetPixel(row, col).B;
                    break;
            }

doubt: could you tell me how the following works:

// Set the next bit if appropriate.
            if ((color_value & 1) == 1)
            {
                // Set the bit.
                value = (byte)(value | value_mask);
            }

            // Move to the next bit.
            value_mask <<= 1;
        }

        return value;
      }

doubt: Is this infinite loop makes exception?Is this a right condition?

  // Pick an unused (r, c, pixel) combination.
    public void PickPosition(Bitmap bm, Random rand,
        HashSet<string> used_positions,
        out int row, out int col, out int pix)
    {
        for (; ; )
        {
            // Pick random r, c, and pix.
            row = rand.Next(0, bm.Width);
            col = rand.Next(0, bm.Height);
            pix = rand.Next(0, 3);

            // See if this location is available.
            string key =
                row.ToString() + "/" +
                col.ToString() + "/" +
                pix.ToString();
            if (!used_positions.Contains(key))
            {
                used_positions.Add(key);
                return;
            }
        }
      }

  // Convert a string password into a numeric value.
    public int NumericPassword(string password)
    {
        // Initialize the shift values to different non-zero values.
        int shift1 = 3;
        int shift2 = 17;

        // Process the message.
        char[] chars = password.ToCharArray();
        int value = 0;
        for (int i = 1; i < password.Length; i++)
        {
            // Add the next letter.
            int ch_value = (int)chars[i];
            value ^= (ch_value << shift1);
            value ^= (ch_value << shift2);

            // Change the shifts.
            shift1 = (shift1 + 7) % 19;
            shift2 = (shift2 + 13) % 23;
        }
        return value;
    }

Solution

  • First, why wrong passwords may be accepted:

    It is possible that NumericPassword returns the same value for two different passwords (“hash collission”). NumericPassword is basically a hash function and thus vulnerable to collission attacks. You could improve the situation by using more bytes for the hash, although that would require a different algorithm and a random number generator which is able to deal with the larger seed.

    In that case, the Random Number Generator would produce the same series of numbers for different passwords if they yield the same hash, i.e. leading to “decryption” of the steganographic content.

    One could circumvent that situation by encrypting the data itself with an encryption algorithm using the password as key.

    On the algorithm

    For your questions on how the value is packed/unpacked into the image, have a look at Bitwise Operators.

    • For example:

      if ((value & 1) == 1) bit = 1;  //’value’ is the byte to be encoded
      

      will take the least significant bit of value and compare it to 1. If it's one, bit is set to 1. This is how the byte value is decomposed into single bits which are then written into the least significant bits of the colour values in the image with the case exceprt you have shown.

      Example on the bitwise and (&) operator: Having a binary value 00101010, we could bitwise and it with 00010111, which would give us 00000010.

    • The bitshift operator (<<) is also used. It shifts the bits in the integer left (i.e. up in their significance) by one in your case (<<= 1). The mask there is used to mask the place where the currently read bit has to be put in the value.

      Basically one iterates over all bits in the byte from lower to upper in both the encoding and decoding function. Then one writes or reads respectively one bit after the other to/from the image by packing it into the least significant bits of the pixels.

    • The length check is sensible, but I would choose a different condition (namely one which is dependend on the image size, i.e. the actual size of the container). It is neccessary to detect a possibly wrong password by checking whether the data encoded in the image can be decoded with the given password (sanity check).

      For a wrong password, the extracted length could be irrationally high. This is not a safe check to detect a wrong password though (a wrong password could also yield a low length number).

    • “Infinite” loop in PickPosition. This loop may indeed be infinite, namely if there are no positions left in the image. Personally, I would pick an algorithm which goes through the neigbours of the “bad” (i.e. already used) pixel first before taking a new random value.

      This way it may take a long time to find the next valid position. Also it's lacking a check that any valid positions are left at all, but that might happen in a part of code you did not show.

    The number of bits which can be stored in an image can easily be calculated given the algorithm. The algorithm stores the information in the least significant bit of each color channel, so we get three bits per pixel. The total amount of bits in the image is thus:

    capacityInBits = width * height * 3