private static UInt64 DifferentialTestRecursion(List <Byte[]> diffs, Hash hash, Int32 keysBytes, Byte[] k1, Byte[] k2, Int32 hashBytes, Byte[] h1, Byte[] h2, Int32 start, Int32 bitsleft) { UInt64 skipped = 0; for (Int32 i = start; i < (keysBytes * 8); ++i) { --bitsleft; BitsUtilities.FlipBit(k2, 0, keysBytes, i); Byte[] h = hash.ComputeHash(k2); UnsafeBuffer.BlockCopy(h, 0, h2, 0, hashBytes); if (NativeMethods.EqualSequences(h1, h2)) { if (diffs.Count < DT_MAXIMUMERRORS) { Byte[] diff = new Byte[keysBytes]; for (Int32 j = 0; j < keysBytes; ++j) { diff[j] = (Byte)(k1[j] ^ k2[j]); } diffs.Add(diff); } else { ++skipped; } } if (bitsleft > 0) { skipped += DifferentialTestRecursion(diffs, hash, keysBytes, k1, k2, hashBytes, h1, h2, i + 1, bitsleft); } BitsUtilities.FlipBit(k2, 0, keysBytes, i); ++bitsleft; } return(skipped); }
public static void AvalancheTest(HashInfo hashInfo) { RandomXS r = new RandomXS(0x000000B0u); Hash hash = hashInfo.Initializer(r.NextValue()); Int32 hashBytes = hashInfo.Length / 8; Int32 hashBits = hashInfo.Length; Console.WriteLine("[[ AVALANCHE TEST ]]"); Boolean resultOverall = true; for (Int32 i = 0; i < s_ParametersAT.Length; ++i) { dynamic p = s_ParametersAT[i]; Int32 keyBytes = p.KeysBytes; Int32 keyBits = keyBytes * 8; Byte[] key = new Byte[p.KeysBytes]; Int32 repetitions = p.Repetitions; Int32 step = repetitions / 10; Console.WriteLine($"> Keys Length: {keyBytes} Bytes"); Console.WriteLine($" Repetitions: {repetitions}"); Console.Write(" Result: "); Int32 binsCount = keyBits * hashBits; Int32[] bins = new Int32[keyBits * hashBits]; for (Int32 j = 0; j < repetitions; ++j) { if ((j % step) == 0) { Console.Write("."); } r.NextBytes(key); Byte[] h0 = hash.ComputeHash(key); Int32 binsIndex = 0; for (Int32 keyBit = 0; keyBit < keyBits; ++keyBit) { BitsUtilities.FlipBit(key, 0, keyBytes, keyBit); Byte[] hi = hash.ComputeHash(key); BitsUtilities.FlipBit(key, 0, keyBytes, keyBit); for (Int32 hashBit = 0; hashBit < hashBits; ++hashBit) { Byte a = BitsUtilities.GetBit(h0, 0, hashBytes, hashBit); Byte b = BitsUtilities.GetBit(hi, 0, hashBytes, hashBit); bins[binsIndex++] += a ^ b; } } } Double worstBias = 0.0d; for (Int32 j = 0; j < binsCount; ++j) { Double c = (Double)bins[j] / repetitions; Double bias = Math.Abs((c * 2.0d) - 1.0d); if (bias > worstBias) { worstBias = bias; } } Boolean result = (worstBias <= 0.01d); Console.WriteLine(result ? " PASSED" : " FAILED"); Console.WriteLine($" - Worst Bias: {(worstBias.Equals(0.0d) ? "Unbiased" : $"{(worstBias * 100.0d):F2}%")}"); resultOverall &= result; } Console.WriteLine($"Test Result: {(resultOverall ? "PASSED" : "FAILED")}"); }
public static void BitIndependenceCriterionTest(HashInfo hashInfo) { RandomXS r = new RandomXS(0x000000B1u); Hash hash = hashInfo.Initializer(r.NextValue()); Int32 hashBytes = hashInfo.Length / 8; Int32 hashBits = hashInfo.Length; Int32 pageSize = hashBits * hashBits * 4; Int32 keyBytes = BICT_KEYSBYTES; Int32 keyBits = keyBytes * 8; Byte[] key = new Byte[keyBytes]; Double biasFactor = BICT_REPETITIONS / 2.0d; Int32 step = keyBits / 10; Console.WriteLine("[[ BIT INDEPENDENCE CRITERION TEST ]]"); Console.Write("Result: "); Int32[] bins = new Int32[keyBits * pageSize]; for (Int32 keyBit = 0; keyBit < keyBits; ++keyBit) { if ((keyBit % step) == 0) { Console.Write("."); } Int32 pageOffset = keyBit * pageSize; for (Int32 irep = 0; irep < BICT_REPETITIONS; ++irep) { r.NextBytes(key); Byte[] h1 = hash.ComputeHash(key); BitsUtilities.FlipBit(key, 0, keyBytes, keyBit); Byte[] h2 = hash.ComputeHash(key); Byte[] d = new Byte[hashBytes]; for (Int32 i = 0; i < hashBytes; ++i) { d[i] = (Byte)(h1[i] ^ h2[i]); } for (Int32 hashBit1 = 0; hashBit1 < hashBits - 1; ++hashBit1) { for (Int32 hashBit2 = hashBit1 + 1; hashBit2 < hashBits; ++hashBit2) { Int32 x = BitsUtilities.GetBit(d, 0, hashBytes, hashBit1) | (BitsUtilities.GetBit(d, 0, hashBytes, hashBit2) << 1); ++bins[pageOffset + (((hashBit1 * hashBits) + hashBit2) * 4) + x]; } } } } Double worstBias = 0.0d; Int32 worstKeyBit = -1; Int32 worstHashBit1 = -1; Int32 worstHashBit2 = -1; for (Int32 hashBit1 = 0; hashBit1 < hashBits - 1; ++hashBit1) { for (Int32 hashBit2 = hashBit1 + 1; hashBit2 < hashBits; ++hashBit2) { for (Int32 keyBit = 0; keyBit < keyBits; ++keyBit) { Int32 binsOffset = (keyBit * pageSize) + ((hashBit1 * hashBits) + hashBit2) * 4; for (Int32 b = 0; b < 4; ++b) { Double c = bins[binsOffset + b] / biasFactor; Double bias = Math.Abs((c * 2.0d) - 1.0d); if (bias > worstBias) { worstBias = bias; worstKeyBit = keyBit; worstHashBit1 = hashBit1; worstHashBit2 = hashBit2; } } } } } Boolean result = (worstBias <= 0.05d); Console.WriteLine(result ? " PASSED" : " FAILED"); Console.WriteLine($" - Worst Bias: {(worstBias.Equals(0.0d) ? "Unbiased" : $"{(worstBias * 100.0d):F2}% (Key Bit: {worstKeyBit} | Hash Bit 1: {worstHashBit1} | Hash Bit 2: {worstHashBit2})")}"); }
public static AnalysisResult AnalyzeHashes(List <Byte[]> hashes, Int32 hashBytes) { if (hashes == null) { throw new ArgumentNullException(nameof(hashes)); } Int32 hashBits = hashBytes * 8; Int32 hashesCount = hashes.Count; Double hashesCountFloat = hashesCount; hashes.Sort(NativeMethods.CompareSequences); Double expectedCollisions = Math.Round((hashesCountFloat * (hashesCountFloat - 1.0d)) / Math.Pow(2.0d, hashBits + 1)); Double observedCollisions = 0.0d; Boolean result = true; for (Int32 i = 1; i < hashesCount; ++i) { if (NativeMethods.EqualSequences(hashes[i], hashes[i - 1])) { ++observedCollisions; } } if (hashBits <= 32) { if (((observedCollisions / expectedCollisions) > 2.0d) && (Math.Abs(observedCollisions - expectedCollisions) > 1.0d)) { result = false; } } else if (observedCollisions > 0.0d) { result = false; } Int32 maximumLength = MAXIMUM_LENGTH; while ((hashesCountFloat / (1 << maximumLength)) < 5.0d) { --maximumLength; } Int32[] bins = new Int32[1 << maximumLength]; Double worstBias = 0.0d; Int32 worstBit = -1; Int32 worstWindow = -1; for (Int32 start = 0; start < hashBits; ++start) { Int32 length = maximumLength; Int32 binsCount = (1 << length); for (Int32 i = 0; i < binsCount; ++i) { bins[i] = 0; } for (Int32 i = 0; i < hashesCount; ++i) { Byte[] hash = hashes[i]; Int32 index = (Int32)BitsUtilities.Window(hash, start, length); ++bins[index]; } while (binsCount >= 256) { Double r = 0.0d; for (Int32 i = 0; i < binsCount; ++i) { r += Math.Pow(bins[i], 2.0d); } r = Math.Sqrt(r / binsCount); Double f = (Math.Pow(hashesCount, 2.0d) - 1.0d) / ((binsCount * Math.Pow(r, 2.0d)) - hashesCount); Double bias = (1.0d - (f / binsCount)) * 100.0d; if (bias > worstBias) { worstBias = bias; worstBit = start; worstWindow = length; } --length; binsCount /= 2; if (length < 8) { break; } for (Int32 i = 0; i < binsCount; ++i) { bins[i] += bins[binsCount + i]; } } } return(new AnalysisResult { Outcome = result, HashesCount = hashesCount, ExpectedCollisions = expectedCollisions, ObservedCollisions = observedCollisions, WorstBias = worstBias, WorstBit = worstBit, WorstWindow = worstWindow }); }