Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- string key = BuildKey(myHiddenByte); // give that to the end user
- ...
- byte hidden;
- if (!TryReadKey(key, out hidden))
- {
- Console.WriteLine("key is not ok");
- }
- else
- {
- Console.WriteLine("key is ok and hidden byte is " + hidden);
- }
- KZGMB0-XYJC2-YRKH3-8LD8G-5FUZG
- YKU93K-M34PD-E5PL0-QM91J-QLDZF
- DH27H9-NCW8E-EMGPL-YCJXJ-N2PRG
- WDAKDE-G56NR-6BA3R-0JE6U-625EB
- 6D5EJ0-NJDAK-EMGZR-Z6ZDF-JHJGF
- ....
- // need 32 characters, so we can use a 5-bit base encoding
- // 0 1 2 3
- // 01234567890123456789012345678901
- private const string KeyChars = "ABCDEFGHJKLMNPQRTUWXYZ0123456789";
- private const int MinSum = 2000;
- private const int Mask = 0xFFFFF; // beware, this can have a dramatic influence on performance
- /// <summary>
- /// Builds a key and hides data in it.
- /// Key format is XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX.
- /// </summary>
- /// <param name="hiddenData">The hidden data.</param>
- /// <returns>The build key</returns>
- public static string BuildKey(byte hiddenData)
- {
- int index;
- Guid guid;
- uint[] word = new uint[4];
- byte[] array;
- while (true)
- {
- // we use guid initial randomness characteristics
- guid = Guid.NewGuid();
- array = guid.ToByteArray();
- int sum = array.Aggregate(0, (current, b) => current + b);
- // a simple check to make sure the guid is not filled with too much zeros...
- if (sum < MinSum)
- continue;
- // build a 32-bit word array
- for (int i = 0; i < 4; i++)
- {
- word[i] = BitConverter.ToUInt32(array, i * 4) & Mask;
- }
- // Here we check the guid follows some algorithm. if it doesn't we skip it
- // the algorithm presented here is to check one of the 32-bit word is equal to the sum of the others
- // (modulo a mask otherwise it takes too long!)
- //
- // This algorithm can be changed at your will (and change the TryReadKey as well)
- if ((word[0] + word[1] + word[2]) == word[3])
- {
- index = 3;
- break;
- }
- if ((word[0] + word[1] + word[3]) == word[2])
- {
- index = 2;
- break;
- }
- if ((word[0] + word[3] + word[2]) == word[1])
- {
- index = 1;
- break;
- }
- if ((word[3] + word[1] + word[2]) == word[0])
- {
- index = 0;
- break;
- }
- }
- // hidden info is also xor'd with other words hi order (except the one we masked)
- // so it's not too easy to guess
- hiddenData = (byte)(hiddenData ^ array[0] ^ array[4] ^ array[8] ^ array[12]);
- // rebuild words without mask
- for (int i = 0; i < 4; i++)
- {
- word[i] = BitConverter.ToUInt32(array, i * 4);
- }
- // hidden info is stored in the block that is the sum of other blocks, in hi order byte (outside the mask)
- word[index] &= 0x00FFFFFF;
- word[index] |= ((uint)hiddenData) << 24;
- // rebuild the array back
- for (int i = 0; i < 4; i++)
- {
- byte[] ui = BitConverter.GetBytes(word[i]);
- for (int j = 0; j < 4; j++)
- {
- array[i * 4 + j] = ui[j];
- }
- }
- // now use 5-bits encoding
- int encodingBitIndex = 0;
- StringBuilder key = new StringBuilder();
- while (encodingBitIndex < 128)
- {
- byte encodingByte = Get5Bits(array, encodingBitIndex);
- if ((key.Length > 0) && (key.Length % 6) == 0)
- {
- key.Append('-');
- }
- key.Append(KeyChars[encodingByte]);
- encodingBitIndex += 5;
- }
- return key.ToString();
- }
- /// <summary>
- /// Validates the specified key and reads hidden data from it.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="hiddenData">The hidden data.</param>
- /// <returns>true if the key is valid and hidden data was read; false otherwise.</returns>
- public static bool TryReadKey(string key, out byte hiddenData)
- {
- hiddenData = 0;
- if (key == null)
- return false;
- key = key.Replace("-", string.Empty);
- if (key.Length != 26)
- return false;
- byte[] array = new byte[16];
- int encodingBitIndex = 0;
- foreach (char t in key)
- {
- byte b = 255;
- for (byte k = 0; k < KeyChars.Length; k++)
- {
- if (KeyChars[k] != t) continue;
- b = k;
- break;
- }
- if (b == 255) // char not found
- return false;
- Put5Bits(array, encodingBitIndex, b);
- encodingBitIndex += 5;
- }
- // quick sum check
- int sum = array.Aggregate(0, (current, b) => current + b);
- // add 256 because we changed the hidden byte
- sum += 256;
- if (sum < MinSum)
- return false;
- uint[] word = new uint[4];
- for (int i = 0; i < 4; i++)
- {
- word[i] = BitConverter.ToUInt32(array, i * 4) & Mask;
- }
- // This must match BuildKey algorithm
- int index;
- if ((word[0] + word[1] + word[2]) == word[3])
- {
- index = 3;
- }
- else if ((word[0] + word[1] + word[3]) == word[2])
- {
- index = 2;
- }
- else if ((word[0] + word[3] + word[2]) == word[1])
- {
- index = 1;
- }
- else if ((word[3] + word[1] + word[2]) == word[0])
- {
- index = 0;
- }
- else
- return false;
- // reread word & extract hidden byte back
- word[index] = BitConverter.ToUInt32(array, index * 4);
- hiddenData = (byte)(word[index] >> 24);
- hiddenData = (byte)(hiddenData ^ array[0] ^ array[4] ^ array[8] ^ array[12]);
- return true;
- }
- // get 5 bits from a byte buffer at an arbitrary bit index
- private static byte Get5Bits(byte[] buffer, int bitIndex)
- {
- int r = bitIndex % 8;
- if (r < 4)
- return (byte)(((buffer[bitIndex / 8]) & (0xFF >> r)) >> (3 - r));
- byte b0 = (byte)((buffer[bitIndex / 8] & (0xFF >> r)) << (r - 3));
- if ((1 + (bitIndex / 8)) == buffer.Length) // last
- return (byte)(buffer[buffer.Length - 1] & 0x7);
- if ((bitIndex / 8) < 16)
- return (byte)(b0 | buffer[1 + (bitIndex / 8)] >> (11 - r));
- return b0;
- }
- // put 5 bits into a byte buffer at an arbitrary bit index
- private static void Put5Bits(byte[] buffer, int bitIndex, byte value)
- {
- int r = bitIndex % 8;
- if (r < 4)
- {
- buffer[bitIndex / 8] |= (byte)((value << (3 - r)));
- }
- else
- {
- if ((1 + (bitIndex / 8)) == buffer.Length) // last
- {
- buffer[buffer.Length - 1] |= (byte)(value & 0x7);
- }
- else if ((bitIndex / 8) < 16)
- {
- buffer[bitIndex / 8] |= (byte)((value >> (r - 3)));
- buffer[1 + (bitIndex / 8)] |= (byte)((value << (11 - r)));
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement