2

I've got an array containing lots of "Color" and I want to change the order of this colors (random) but with a password: so another user can get the "original" (and correct) sequence of the array only with the right password. How can I do this in Visual Basic .NET or C# ? Do have I to use a particular encryption engine?

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
Enigmo96
  • 111
  • 1
  • 9

2 Answers2

4

Here is a simple method to scramble an array of colors using a password.

The key is to transform your password from a string to a number. Then, you can use that number as a seed for a random number generator. After that, you can use that random number generator to get a permutation of the same length as the color array. You can use that permutation to change the order of the colors and scramble the array.

When decrypting, if you are given the same password, you can generate the same permutation and unscramble the array to its original form.

Here is an example of that principle, written in C#:

int[] GetPermutation(int size, int seed)
{
    Random random = new Random(seed);
    int[] array = new int[size];
    for (int i = 0; i < size; i++)
        array[i] = i;
    for (int i = array.Length; i > 1; i--)
    {
        int j = random.Next(i);
        int tmp = array[j];
        array[j] = array[i - 1];
        array[i - 1] = tmp;
    }
    return array;
}

Color[] Encrypt(Color[] input, string password)
{
    int seed = password.GetHashCode();
    int[] perm = GetPermutation(input.Length, seed);
    Color[] encrypted = new Color[input.Length];
    for (int i = 0; i < input.Length; i++)
    {
        encrypted[perm[i]] = input[i];
    }
    return encrypted;
}

Color[] Decrypt(Color[] input, string password)
{
    int seed = password.GetHashCode();
    int[] perm = GetPermutation(input.Length, seed);
    Color[] decrypted = new Color[input.Length];
    for (int i = 0; i < input.Length; i++)
    {
        decrypted[i] = input[perm[i]];
    }
    return decrypted;
}

The GetPermutation function generates a permutation based on a seed passed as a parameter. The Encrypt and Decrypt functions actually scramble and unscramble the arrays.

Here is a usage example:

Color[] array = new Color[5] { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Black };
string password = "secret";
Color[] enc = Encrypt(array, password); // will always return Blue, Green, Black, Yellow, Red for the "secret" password
Color[] dec = Decrypt(enc, password); // will return the original array: Red, Green, Blue, Yellow, Black if given the "secret" password
Color[] dec2 = Decrypt(enc, "incorrectpwd"); // will return Green, Blue, Yellow, Black, Red, which is incorrect because the password was incorrect

Notes:

  • I used the .GetHashCode() method for simplicity. This method might generate different numbers for the same string in future versions of the .NET framework
  • If the length of the array is small, you can get the same encryption order with different passwords, and decryption will be successful even if the password is incorrect. For example:

    Color[] array = new Color[2] {Color.Red, Color.Green};
    Color[] enc = Encrypt(array, "one"); // will return Green, Red
    Color[] dec = Decrypt(enc, "one"); // will return Red, Green
    Color[] dec2 = Decrypt(enc, "two"); // will also return Red, Green, even though the password was incorrect
    
  • If you want to use colors as an alternative to passwords, you must note that you lose some security because the space of all the passwords is not as high as a password made up of characters, numbers and signs. Brute-forcing such a password will be much easier.

Ove
  • 6,227
  • 2
  • 39
  • 68
  • Thanks a lot for your great code! Just a question: I'm trying to use it with the "pixels"'colors of a Bitmap Image and if the size of the image if more than (about) 100 * 100 pixel (so 10000 pixels), it stops working! I got "The application doesn't respond.." etc.. How can I do? – Enigmo96 Jun 01 '12 at 19:52
  • Do you store the pixels in a color array (`Color[]`) of size 10000? Does the app crash, or does it just take a long time for the colors to be scrambled? You should post the code that is giving you trouble. If it's large you can post it on pastebin.com (or a similar site) and put the link in a comment, and I will take a look at it. – Ove Jun 01 '12 at 21:02
  • In my code I'm trying to get all the pixels of an image and then "randomize" them with a password, using the code you provided me. The function to get all the pixels is here pastebin.com/yfvAtRYH But I can use it with an image which has got about 10000 pixels and no more, because the app says "The applicazion does not respond.." and I have to click on "End Now" to close while debugging. How can I do? (The same thing by setting the pixels (http://pastebin.com/9fsJEK26)) Is there another way to do what I want? (Your code comes next to "GetImagePixels", of course) Thanks! – Enigmo96 Jun 04 '12 at 10:20
  • The problem was that the method for generating permutations was not efficient and it was taking a very long time to generate a permutation for a large number. I wrote that method for a simple problem and it wasn't built for speed. I have modified my answer, and replaced the method with the [Fisher-Yates shuffle algorithm](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle). It is much faster now, it should take 4-5 seconds to read, encrypt and decrypt a 1920x1080 image. – Ove Jun 04 '12 at 11:40
  • Your code works very well but the problem is when I use the GetImagePixels function (pastebin.com/yfvAtRYH): here the application does not respond (before calling "your" Encrypt)! How can I read the image's pixels faster? – Enigmo96 Jun 04 '12 at 12:40
  • I solved this problem using this LockBitmap class (http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp) but when I try to use your codes (the old and the new), it works ONLY with images of no more than 2000x2000 pixels. How can I do? – Enigmo96 Jun 04 '12 at 15:53
  • If the problem you have is that you get "the application has stopped responding", that means that the processing simply takes too much time. You can try to optimize the algorithm, but for images that are large enough, you will always get "this application has stopped responding". Because processing takes too much time, you need to do the work on another thread, so that your GUI thread will keep responding, and the app won't hang. I suggest you use a [BackgroundWorker](http://www.dotnetperls.com/backgroundworker). That will keep your app responsive. – Ove Jun 04 '12 at 20:46
  • Now (without using a BackgroundWorker) I've got another "strange" error.... Please help me, here is the link of my test project (http://www.ddlstorage.com/n2g0m4i18l9z/Image_Pixels.zip.htm) Thanks! – Enigmo96 Jun 05 '12 at 16:27
  • You are getting a `System.OutOfMemoryException`. You are working with very large images, and constantly converting to and from color arrays of such large sizes takes up a lot of memory. There is [a limit](http://stackoverflow.com/q/1109558/80003) on how much memory a 32-bit process can use, and you use more than that limit, so your app crashes. You can either modify the algorithm so that you don't convert to and from arrays of Color (`Color[]`), or you can compile your app as 64-bit, so you have a much larger limit. – Ove Jun 05 '12 at 17:34
  • Thanks for your answer! If you undestrand what I'm going to do, can I ask you if there's another way to do it? – Enigmo96 Jun 06 '12 at 12:52
  • I have modified your project to be more memory efficient. I have tested it with an image that is 5000x5000 px, and it does not run out of memory. You can get it [here](https://www.wetransfer.com/dl/BoJoi2p3/2173c5d281c3cf14e7e0333077799cca0de5e09f82071aa4af7b0b7b4161d8acb8ea6e5f3c2b63a). I will leave my answer unchanged, because it **is** the answer to the original question, and I think you should accept it as the answer ( [you should take a look here](http://meta.stackexchange.com/a/5235) ) – Ove Jun 06 '12 at 15:21
  • (Sorry but I get an error here while debugging (encrypt mode): lb2.SetPixel(x2, y2, lb.GetPixel(x1, y1)); It says IndexOutOfRangeException, why? I haven't changed your code!) – Enigmo96 Jun 06 '12 at 17:39
  • Sorry, my bad. I haven't tested it enough. In the Encrypt and Decrypt functions, use `bmp.Width` instead of `bmp.Height` (only inside the for). Here is the correct version: [http://pastebin.com/iXWUVVMK](http://pastebin.com/iXWUVVMK) – Ove Jun 07 '12 at 06:31
3

I'm guessing this may work:

  1. Have user enter password
  2. Hash the password (Some sample hashes in C++ here) and store the number result
  3. Use a shuffle algorithm with the hash as the seed. See here for example, just initialize the Random object with the hash as the seed.

Again, hashing is not always perfect (as it can collide), so you may need to find a hashing algorithm that is suitable for your problem.

EDIT:

Here's some code that may help you:

String input = "User inputted string here";
int hashSeed = input.GetHashCode();
Random rnd = new Random(hashSeed);
Color[] MyRandomColorArray = MyColorArray.OrderBy(x => rnd.Next()).ToArray();

Note: GetHashCode() returns differnet values whether it's on a 32-bit vs. 64-bit system.

Community
  • 1
  • 1
justderb
  • 2,835
  • 1
  • 25
  • 38
  • Sorry but I wrote VB.NET or C# and not C++.. can you give me a code example of what I need? – Enigmo96 May 31 '12 at 20:11
  • Ove has provided a good example (which you probably should use). My example can go one way (Encrypt), I don't have the code to go back (Which Ove's code does) – justderb May 31 '12 at 20:35