0

我有一个关于图像隐写术的项目(用于无损图像)。我正在努力学习它背后的过程。但是当我运行它来提取消息时,会为错误的密码以及空图像(无数据编码)生成异常。它随机选择像素并使用哈希集。但是我不明白中间的一些步骤。我怀疑这些步骤中的任何错误是否会导致异常。此外,在某些情况下, 即使密码错误,该项目也会提取隐藏消息。我该如何解决这些问题?

为这个长代码道歉,但希望有一个解决方案..感谢您的帮助。

代码如下所示:

//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;

疑问:如何在下面显示的“下两行”中获取下一个要存储的位?

// 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);

疑问:以下检查(如果 len>10000)是否正确?

 // 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;
            }

疑问:你能告诉我以下是如何工作的:

// 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;
      }

疑问:这个无限循环是否异常?这是一个正确的条件吗?

  // 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;
    }
4

1 回答 1

1

首先,为什么会接受错误的密码:

可能会NumericPassword为两个不同的密码返回相同的值(“<a href="https://en.wikipedia.org/wiki/Collision_%28computer_science%29" rel="nofollow">hash collission”)。NumericPassword基本上是一个哈希函数,因此容易受到碰撞攻击。您可以通过为哈希使用更多字节来改善这种情况,尽管这需要不同的算法和能够处理更大种子的随机数生成器。

在这种情况下,如果它们产生相同的散列,随机数生成器将为不同的密码产生相同的数字序列,即导致对隐写内容的“解密”。

可以通过使用密码作为密钥的加密算法加密数据本身来规避这种情况。

关于算法

有关如何将值打包/解包到图像中的问题,请查看Bitwise Operators

  • 例如:

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

    将取值的最低有效位并将其与 1 进行比较。如果为 1,bit则设置为 1。这就是将字节值分解为单个位的方式,然后将其写入图像中颜色值的最低有效位除了您展示的案例之外。

    bitwise and( ) 运算符的示例&:有一个二进制值00101010,我们可以bitwise and00010111,这将给我们00000010

  • 还使用了位移运算符 ( <<)。在您的情况下( ),它将整数中的位向左移动(即它们的重要性向上<<= 1)。那里的掩码用于掩码必须将当前读取的位放入值的位置。

    基本上,在编码和解码功能中,从低到高遍历字节中的所有位。然后,通过将图像打包到像素的最低有效位中,一个位接着一个位地写入或读取图像。

  • 长度检查是明智的,但我会选择不同的条件(即取决于图像大小的条件,即容器的实际大小)。有必要通过检查图像中编码的数据是否可以使用给定的密码解码来检测可能错误的密码(完整性检查)。

    对于错误的密码,提取的长度可能会非常高。但是,这不是检测错误密码的安全检查(错误的密码也可能产生低长度的数字)。

  • PickPosition 中的“无限”循环。这个循环可能确实是无限的,即如果图像中没有任何位置。就个人而言,我会选择一种算法,它首先通过“坏”(即已使用)像素的邻居,然后再获取一个新的随机值。

    这样,可能需要很长时间才能找到下一个有效位置。此外,它还没有检查是否留下了任何有效位置,但这可能发生在您未显示的部分代码中。

给定算法,可以很容易地计算出可以存储在图像中的位数。该算法将信息存储在每个颜色通道的最低有效位中,因此我们得到每个像素三个位。因此,图像中的总位数为:

capacityInBits = width * height * 3
于 2012-06-14T17:13:47.473 回答