DifferentialTest.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //-----------------------------------------------------------------------------
  2. // Differential collision & distribution tests - generate a bunch of random keys,
  3. // see what happens to the hash value when we flip a few bits of the key.
  4. #pragma once
  5. #include "Types.h"
  6. #include "Stats.h" // for chooseUpToK
  7. #include "KeysetTest.h" // for SparseKeygenRecurse
  8. #include "Random.h"
  9. #include <vector>
  10. #include <algorithm>
  11. #include <stdio.h>
  12. //-----------------------------------------------------------------------------
  13. // Sort through the differentials, ignoring collisions that only occured once
  14. // (these could be false positives). If we find collisions of 3 or more, the
  15. // differential test fails.
  16. template < class keytype >
  17. bool ProcessDifferentials ( std::vector<keytype> & diffs, int reps, bool dumpCollisions )
  18. {
  19. std::sort(diffs.begin(), diffs.end());
  20. int count = 1;
  21. int ignore = 0;
  22. bool result = true;
  23. if(diffs.size())
  24. {
  25. keytype kp = diffs[0];
  26. for(int i = 1; i < (int)diffs.size(); i++)
  27. {
  28. if(diffs[i] == kp)
  29. {
  30. count++;
  31. continue;
  32. }
  33. else
  34. {
  35. if(count > 1)
  36. {
  37. result = false;
  38. double pct = 100 * (double(count) / double(reps));
  39. if(dumpCollisions)
  40. {
  41. printbits((unsigned char*)&kp,sizeof(kp));
  42. printf(" - %4.2f%%\n", pct );
  43. }
  44. }
  45. else
  46. {
  47. ignore++;
  48. }
  49. kp = diffs[i];
  50. count = 1;
  51. }
  52. }
  53. if(count > 1)
  54. {
  55. double pct = 100 * (double(count) / double(reps));
  56. if(dumpCollisions)
  57. {
  58. printbits((unsigned char*)&kp,sizeof(kp));
  59. printf(" - %4.2f%%\n", pct );
  60. }
  61. }
  62. else
  63. {
  64. ignore++;
  65. }
  66. }
  67. printf("%d total collisions, of which %d single collisions were ignored",(int)diffs.size(),ignore);
  68. if(result == false)
  69. {
  70. printf(" !!!!! ");
  71. }
  72. printf("\n");
  73. printf("\n");
  74. return result;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Check all possible keybits-choose-N differentials for collisions, report
  78. // ones that occur significantly more often than expected.
  79. // Random collisions can happen with probability 1 in 2^32 - if we do more than
  80. // 2^32 tests, we'll probably see some spurious random collisions, so don't report
  81. // them.
  82. template < typename keytype, typename hashtype >
  83. void DiffTestRecurse ( pfHash hash, keytype & k1, keytype & k2, hashtype & h1, hashtype & h2, int start, int bitsleft, std::vector<keytype> & diffs )
  84. {
  85. const int bits = sizeof(keytype)*8;
  86. for(int i = start; i < bits; i++)
  87. {
  88. flipbit(&k2,sizeof(k2),i);
  89. bitsleft--;
  90. hash(&k2,sizeof(k2),0,&h2);
  91. if(h1 == h2)
  92. {
  93. diffs.push_back(k1 ^ k2);
  94. }
  95. if(bitsleft)
  96. {
  97. DiffTestRecurse(hash,k1,k2,h1,h2,i+1,bitsleft,diffs);
  98. }
  99. flipbit(&k2,sizeof(k2),i);
  100. bitsleft++;
  101. }
  102. }
  103. //----------
  104. template < typename keytype, typename hashtype >
  105. bool DiffTest ( pfHash hash, int diffbits, int reps, bool dumpCollisions )
  106. {
  107. const int keybits = sizeof(keytype) * 8;
  108. const int hashbits = sizeof(hashtype) * 8;
  109. double diffcount = chooseUpToK(keybits,diffbits);
  110. double testcount = (diffcount * double(reps));
  111. double expected = testcount / pow(2.0,double(hashbits));
  112. Rand r(100);
  113. std::vector<keytype> diffs;
  114. keytype k1,k2;
  115. hashtype h1,h2;
  116. printf("Testing %0.f up-to-%d-bit differentials in %d-bit keys -> %d bit hashes.\n",diffcount,diffbits,keybits,hashbits);
  117. printf("%d reps, %0.f total tests, expecting %2.2f random collisions",reps,testcount,expected);
  118. for(int i = 0; i < reps; i++)
  119. {
  120. if(i % (reps/10) == 0) printf(".");
  121. r.rand_p(&k1,sizeof(keytype));
  122. k2 = k1;
  123. hash(&k1,sizeof(k1),0,(uint32_t*)&h1);
  124. DiffTestRecurse<keytype,hashtype>(hash,k1,k2,h1,h2,0,diffbits,diffs);
  125. }
  126. printf("\n");
  127. bool result = true;
  128. result &= ProcessDifferentials(diffs,reps,dumpCollisions);
  129. return result;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Differential distribution test - for each N-bit input differential, generate
  133. // a large set of differential key pairs, hash them, and test the output
  134. // differentials using our distribution test code.
  135. // This is a very hard test to pass - even if the hash values are well-distributed,
  136. // the differences between hash values may not be. It's also not entirely relevant
  137. // for testing hash functions, but it's still interesting.
  138. // This test is a _lot_ of work, as it's essentially a full keyset test for
  139. // each of a potentially huge number of input differentials. To speed things
  140. // along, we do only a few distribution tests per keyset instead of the full
  141. // grid.
  142. // #TODO - put diagram drawing back on
  143. template < typename keytype, typename hashtype >
  144. void DiffDistTest ( pfHash hash, const int diffbits, int trials, double & worst, double & avg )
  145. {
  146. std::vector<keytype> keys(trials);
  147. std::vector<hashtype> A(trials),B(trials);
  148. for(int i = 0; i < trials; i++)
  149. {
  150. rand_p(&keys[i],sizeof(keytype));
  151. hash(&keys[i],sizeof(keytype),0,(uint32_t*)&A[i]);
  152. }
  153. //----------
  154. std::vector<keytype> diffs;
  155. keytype temp(0);
  156. SparseKeygenRecurse<keytype>(0,diffbits,true,temp,diffs);
  157. //----------
  158. worst = 0;
  159. avg = 0;
  160. hashtype h2;
  161. for(size_t j = 0; j < diffs.size(); j++)
  162. {
  163. keytype & d = diffs[j];
  164. for(int i = 0; i < trials; i++)
  165. {
  166. keytype k2 = keys[i] ^ d;
  167. hash(&k2,sizeof(k2),0,&h2);
  168. B[i] = A[i] ^ h2;
  169. }
  170. double dworst,davg;
  171. TestDistributionFast(B,dworst,davg);
  172. avg += davg;
  173. worst = (dworst > worst) ? dworst : worst;
  174. }
  175. avg /= double(diffs.size());
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Simpler differential-distribution test - for all 1-bit differentials,
  179. // generate random key pairs and run full distribution/collision tests on the
  180. // hash differentials
  181. template < typename keytype, typename hashtype >
  182. bool DiffDistTest2 ( pfHash hash )
  183. {
  184. Rand r(857374);
  185. int keybits = sizeof(keytype) * 8;
  186. const int keycount = 256*256*32;
  187. keytype k;
  188. std::vector<hashtype> hashes(keycount);
  189. hashtype h1,h2;
  190. bool result = true;
  191. for(int keybit = 0; keybit < keybits; keybit++)
  192. {
  193. printf("Testing bit %d\n",keybit);
  194. for(int i = 0; i < keycount; i++)
  195. {
  196. r.rand_p(&k,sizeof(keytype));
  197. hash(&k,sizeof(keytype),0,&h1);
  198. flipbit(&k,sizeof(keytype),keybit);
  199. hash(&k,sizeof(keytype),0,&h2);
  200. hashes[i] = h1 ^ h2;
  201. }
  202. result &= TestHashList<hashtype>(hashes,true,true,true);
  203. printf("\n");
  204. }
  205. return result;
  206. }
  207. //----------------------------------------------------------------------------