hashtester.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Unit tests for the abstract cryptographic hash interface.
  16. *
  17. */
  18. goog.provide('goog.crypt.hashTester');
  19. goog.require('goog.array');
  20. goog.require('goog.crypt');
  21. goog.require('goog.dom');
  22. goog.require('goog.dom.TagName');
  23. goog.require('goog.testing.PerformanceTable');
  24. goog.require('goog.testing.PseudoRandom');
  25. goog.require('goog.testing.asserts');
  26. goog.setTestOnly('hashTester');
  27. /**
  28. * Runs basic tests.
  29. *
  30. * @param {!goog.crypt.Hash} hash A hash instance.
  31. */
  32. goog.crypt.hashTester.runBasicTests = function(hash) {
  33. // Compute first hash.
  34. hash.update([97, 158]);
  35. var golden1 = hash.digest();
  36. // Compute second hash.
  37. hash.reset();
  38. hash.update('aB');
  39. var golden2 = hash.digest();
  40. assertTrue(
  41. 'Two different inputs resulted in a hash collision',
  42. !!goog.testing.asserts.findDifferences(golden1, golden2));
  43. // Empty hash.
  44. hash.reset();
  45. var empty = hash.digest();
  46. assertTrue(
  47. 'Empty hash collided with a non-trivial one',
  48. !!goog.testing.asserts.findDifferences(golden1, empty) &&
  49. !!goog.testing.asserts.findDifferences(golden2, empty));
  50. // Zero-length array update.
  51. hash.reset();
  52. hash.update([]);
  53. assertArrayEquals(
  54. 'Updating with an empty array did not give an empty hash', empty,
  55. hash.digest());
  56. // Zero-length string update.
  57. hash.reset();
  58. hash.update('');
  59. assertArrayEquals(
  60. 'Updating with an empty string did not give an empty hash', empty,
  61. hash.digest());
  62. // Recompute the first hash.
  63. hash.reset();
  64. hash.update([97, 158]);
  65. assertArrayEquals(
  66. 'The reset did not produce the initial state', golden1, hash.digest());
  67. // Check for a trivial collision.
  68. hash.reset();
  69. hash.update([158, 97]);
  70. assertTrue(
  71. 'Swapping bytes resulted in a hash collision',
  72. !!goog.testing.asserts.findDifferences(golden1, hash.digest()));
  73. // Compare array and string input.
  74. hash.reset();
  75. hash.update([97, 66]);
  76. assertArrayEquals(
  77. 'String and array inputs should give the same result', golden2,
  78. hash.digest());
  79. // Compute in parts.
  80. hash.reset();
  81. hash.update('a');
  82. hash.update([158]);
  83. assertArrayEquals(
  84. 'Partial updates resulted in a different hash', golden1, hash.digest());
  85. // Test update with specified length.
  86. hash.reset();
  87. hash.update('aB', 0);
  88. hash.update([97, 158, 32], 2);
  89. assertArrayEquals(
  90. 'Updating with an explicit buffer length did not work', golden1,
  91. hash.digest());
  92. };
  93. /**
  94. * Runs block tests.
  95. *
  96. * @param {!goog.crypt.Hash} hash A hash instance.
  97. * @param {number} blockBytes Size of the hash block.
  98. */
  99. goog.crypt.hashTester.runBlockTests = function(hash, blockBytes) {
  100. // Compute a message which is 1 byte shorter than hash block size.
  101. var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  102. var message = '';
  103. for (var i = 0; i < blockBytes - 1; i++) {
  104. message += chars.charAt(i % chars.length);
  105. }
  106. // Compute golden hash for 1 block + 2 bytes.
  107. hash.update(message + '123');
  108. var golden1 = hash.digest();
  109. // Compute golden hash for 2 blocks + 1 byte.
  110. hash.reset();
  111. hash.update(message + message + '123');
  112. var golden2 = hash.digest();
  113. // Almost fill a block, then overflow.
  114. hash.reset();
  115. hash.update(message);
  116. hash.update('123');
  117. assertArrayEquals(golden1, hash.digest());
  118. // Fill a block.
  119. hash.reset();
  120. hash.update(message + '1');
  121. hash.update('23');
  122. assertArrayEquals(golden1, hash.digest());
  123. // Overflow a block.
  124. hash.reset();
  125. hash.update(message + '12');
  126. hash.update('3');
  127. assertArrayEquals(golden1, hash.digest());
  128. // Test single overflow with an array.
  129. hash.reset();
  130. hash.update(goog.crypt.stringToByteArray(message + '123'));
  131. assertArrayEquals(golden1, hash.digest());
  132. // Almost fill a block, then overflow this and the next block.
  133. hash.reset();
  134. hash.update(message);
  135. hash.update(message + '123');
  136. assertArrayEquals(golden2, hash.digest());
  137. // Fill two blocks.
  138. hash.reset();
  139. hash.update(message + message + '12');
  140. hash.update('3');
  141. assertArrayEquals(golden2, hash.digest());
  142. // Test double overflow with an array.
  143. hash.reset();
  144. hash.update(goog.crypt.stringToByteArray(message));
  145. hash.update(goog.crypt.stringToByteArray(message + '123'));
  146. assertArrayEquals(golden2, hash.digest());
  147. };
  148. /**
  149. * Runs performance tests.
  150. *
  151. * @param {function():!goog.crypt.Hash} hashFactory A hash factory.
  152. * @param {string} hashName Name of the hashing function.
  153. */
  154. goog.crypt.hashTester.runPerfTests = function(hashFactory, hashName) {
  155. var body = goog.dom.getDocument().body;
  156. var perfTable = goog.dom.createElement(goog.dom.TagName.DIV);
  157. goog.dom.appendChild(body, perfTable);
  158. var table = new goog.testing.PerformanceTable(perfTable);
  159. function runPerfTest(byteLength, updateCount) {
  160. var label =
  161. (hashName + ': ' + updateCount + ' update(s) of ' + byteLength +
  162. ' bytes');
  163. function run(data, dataType) {
  164. table.run(function() {
  165. var hash = hashFactory();
  166. for (var i = 0; i < updateCount; i++) {
  167. hash.update(data, byteLength);
  168. }
  169. var digest = hash.digest();
  170. }, label + ' (' + dataType + ')');
  171. }
  172. var byteArray = goog.crypt.hashTester.createRandomByteArray_(byteLength);
  173. var byteString = goog.crypt.hashTester.createByteString_(byteArray);
  174. run(byteArray, 'byte array');
  175. run(byteString, 'byte string');
  176. }
  177. var MESSAGE_LENGTH_LONG = 10000000; // 10 Mbytes
  178. var MESSAGE_LENGTH_SHORT = 10; // 10 bytes
  179. var MESSAGE_COUNT_SHORT = MESSAGE_LENGTH_LONG / MESSAGE_LENGTH_SHORT;
  180. runPerfTest(MESSAGE_LENGTH_LONG, 1);
  181. runPerfTest(MESSAGE_LENGTH_SHORT, MESSAGE_COUNT_SHORT);
  182. };
  183. /**
  184. * Creates and returns a random byte array.
  185. *
  186. * @param {number} length Length of the byte array.
  187. * @return {!Array<number>} An array of bytes.
  188. * @private
  189. */
  190. goog.crypt.hashTester.createRandomByteArray_ = function(length) {
  191. var random = new goog.testing.PseudoRandom(0);
  192. var bytes = [];
  193. for (var i = 0; i < length; ++i) {
  194. // Generates an integer from 0 to 255.
  195. var b = Math.floor(random.random() * 0x100);
  196. bytes.push(b);
  197. }
  198. return bytes;
  199. };
  200. /**
  201. * Creates a string from an array of bytes.
  202. *
  203. * @param {!Array<number>} bytes An array of bytes.
  204. * @return {string} The string encoded by the bytes.
  205. * @private
  206. */
  207. goog.crypt.hashTester.createByteString_ = function(bytes) {
  208. var str = '';
  209. goog.array.forEach(bytes, function(b) { str += String.fromCharCode(b); });
  210. return str;
  211. };