sha1.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright 2005 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 SHA-1 cryptographic hash.
  16. * Variable names follow the notation in FIPS PUB 180-3:
  17. * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.
  18. *
  19. * Usage:
  20. * var sha1 = new goog.crypt.sha1();
  21. * sha1.update(bytes);
  22. * var hash = sha1.digest();
  23. *
  24. * Performance:
  25. * Chrome 23: ~400 Mbit/s
  26. * Firefox 16: ~250 Mbit/s
  27. *
  28. */
  29. goog.provide('goog.crypt.Sha1');
  30. goog.require('goog.crypt.Hash');
  31. /**
  32. * SHA-1 cryptographic hash constructor.
  33. *
  34. * The properties declared here are discussed in the above algorithm document.
  35. * @constructor
  36. * @extends {goog.crypt.Hash}
  37. * @final
  38. * @struct
  39. */
  40. goog.crypt.Sha1 = function() {
  41. goog.crypt.Sha1.base(this, 'constructor');
  42. this.blockSize = 512 / 8;
  43. /**
  44. * Holds the previous values of accumulated variables a-e in the compress_
  45. * function.
  46. * @type {!Array<number>}
  47. * @private
  48. */
  49. this.chain_ = [];
  50. /**
  51. * A buffer holding the partially computed hash result.
  52. * @type {!Array<number>}
  53. * @private
  54. */
  55. this.buf_ = [];
  56. /**
  57. * An array of 80 bytes, each a part of the message to be hashed. Referred to
  58. * as the message schedule in the docs.
  59. * @type {!Array<number>}
  60. * @private
  61. */
  62. this.W_ = [];
  63. /**
  64. * Contains data needed to pad messages less than 64 bytes.
  65. * @type {!Array<number>}
  66. * @private
  67. */
  68. this.pad_ = [];
  69. this.pad_[0] = 128;
  70. for (var i = 1; i < this.blockSize; ++i) {
  71. this.pad_[i] = 0;
  72. }
  73. /**
  74. * @private {number}
  75. */
  76. this.inbuf_ = 0;
  77. /**
  78. * @private {number}
  79. */
  80. this.total_ = 0;
  81. this.reset();
  82. };
  83. goog.inherits(goog.crypt.Sha1, goog.crypt.Hash);
  84. /** @override */
  85. goog.crypt.Sha1.prototype.reset = function() {
  86. this.chain_[0] = 0x67452301;
  87. this.chain_[1] = 0xefcdab89;
  88. this.chain_[2] = 0x98badcfe;
  89. this.chain_[3] = 0x10325476;
  90. this.chain_[4] = 0xc3d2e1f0;
  91. this.inbuf_ = 0;
  92. this.total_ = 0;
  93. };
  94. /**
  95. * Internal compress helper function.
  96. * @param {!Array<number>|!Uint8Array|string} buf Block to compress.
  97. * @param {number=} opt_offset Offset of the block in the buffer.
  98. * @private
  99. */
  100. goog.crypt.Sha1.prototype.compress_ = function(buf, opt_offset) {
  101. if (!opt_offset) {
  102. opt_offset = 0;
  103. }
  104. var W = this.W_;
  105. // get 16 big endian words
  106. if (goog.isString(buf)) {
  107. for (var i = 0; i < 16; i++) {
  108. // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS
  109. // have a bug that turns the post-increment ++ operator into pre-increment
  110. // during JIT compilation. We have code that depends heavily on SHA-1 for
  111. // correctness and which is affected by this bug, so I've removed all uses
  112. // of post-increment ++ in which the result value is used. We can revert
  113. // this change once the Safari bug
  114. // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and
  115. // most clients have been updated.
  116. W[i] = (buf.charCodeAt(opt_offset) << 24) |
  117. (buf.charCodeAt(opt_offset + 1) << 16) |
  118. (buf.charCodeAt(opt_offset + 2) << 8) |
  119. (buf.charCodeAt(opt_offset + 3));
  120. opt_offset += 4;
  121. }
  122. } else {
  123. for (var i = 0; i < 16; i++) {
  124. W[i] = (buf[opt_offset] << 24) | (buf[opt_offset + 1] << 16) |
  125. (buf[opt_offset + 2] << 8) | (buf[opt_offset + 3]);
  126. opt_offset += 4;
  127. }
  128. }
  129. // expand to 80 words
  130. for (var i = 16; i < 80; i++) {
  131. var t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
  132. W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;
  133. }
  134. var a = this.chain_[0];
  135. var b = this.chain_[1];
  136. var c = this.chain_[2];
  137. var d = this.chain_[3];
  138. var e = this.chain_[4];
  139. var f, k;
  140. // TODO(user): Try to unroll this loop to speed up the computation.
  141. for (var i = 0; i < 80; i++) {
  142. if (i < 40) {
  143. if (i < 20) {
  144. f = d ^ (b & (c ^ d));
  145. k = 0x5a827999;
  146. } else {
  147. f = b ^ c ^ d;
  148. k = 0x6ed9eba1;
  149. }
  150. } else {
  151. if (i < 60) {
  152. f = (b & c) | (d & (b | c));
  153. k = 0x8f1bbcdc;
  154. } else {
  155. f = b ^ c ^ d;
  156. k = 0xca62c1d6;
  157. }
  158. }
  159. var t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;
  160. e = d;
  161. d = c;
  162. c = ((b << 30) | (b >>> 2)) & 0xffffffff;
  163. b = a;
  164. a = t;
  165. }
  166. this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;
  167. this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;
  168. this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;
  169. this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;
  170. this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;
  171. };
  172. /** @override */
  173. goog.crypt.Sha1.prototype.update = function(bytes, opt_length) {
  174. // TODO(johnlenz): tighten the function signature and remove this check
  175. if (bytes == null) {
  176. return;
  177. }
  178. if (!goog.isDef(opt_length)) {
  179. opt_length = bytes.length;
  180. }
  181. var lengthMinusBlock = opt_length - this.blockSize;
  182. var n = 0;
  183. // Using local instead of member variables gives ~5% speedup on Firefox 16.
  184. var buf = this.buf_;
  185. var inbuf = this.inbuf_;
  186. // The outer while loop should execute at most twice.
  187. while (n < opt_length) {
  188. // When we have no data in the block to top up, we can directly process the
  189. // input buffer (assuming it contains sufficient data). This gives ~25%
  190. // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that
  191. // the data is provided in large chunks (or in multiples of 64 bytes).
  192. if (inbuf == 0) {
  193. while (n <= lengthMinusBlock) {
  194. this.compress_(bytes, n);
  195. n += this.blockSize;
  196. }
  197. }
  198. if (goog.isString(bytes)) {
  199. while (n < opt_length) {
  200. buf[inbuf] = bytes.charCodeAt(n);
  201. ++inbuf;
  202. ++n;
  203. if (inbuf == this.blockSize) {
  204. this.compress_(buf);
  205. inbuf = 0;
  206. // Jump to the outer loop so we use the full-block optimization.
  207. break;
  208. }
  209. }
  210. } else {
  211. while (n < opt_length) {
  212. buf[inbuf] = bytes[n];
  213. ++inbuf;
  214. ++n;
  215. if (inbuf == this.blockSize) {
  216. this.compress_(buf);
  217. inbuf = 0;
  218. // Jump to the outer loop so we use the full-block optimization.
  219. break;
  220. }
  221. }
  222. }
  223. }
  224. this.inbuf_ = inbuf;
  225. this.total_ += opt_length;
  226. };
  227. /** @override */
  228. goog.crypt.Sha1.prototype.digest = function() {
  229. var digest = [];
  230. var totalBits = this.total_ * 8;
  231. // Add pad 0x80 0x00*.
  232. if (this.inbuf_ < 56) {
  233. this.update(this.pad_, 56 - this.inbuf_);
  234. } else {
  235. this.update(this.pad_, this.blockSize - (this.inbuf_ - 56));
  236. }
  237. // Add # bits.
  238. for (var i = this.blockSize - 1; i >= 56; i--) {
  239. this.buf_[i] = totalBits & 255;
  240. totalBits /= 256; // Don't use bit-shifting here!
  241. }
  242. this.compress_(this.buf_);
  243. var n = 0;
  244. for (var i = 0; i < 5; i++) {
  245. for (var j = 24; j >= 0; j -= 8) {
  246. digest[n] = (this.chain_[i] >> j) & 255;
  247. ++n;
  248. }
  249. }
  250. return digest;
  251. };