browserSha1.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. var Buffer = require('buffer/').Buffer;
  2. var hashUtils = require('./browserHashUtils');
  3. var BLOCK_SIZE = 64;
  4. var DIGEST_LENGTH = 20;
  5. var KEY = new Uint32Array([
  6. 0x5a827999,
  7. 0x6ed9eba1,
  8. 0x8f1bbcdc | 0,
  9. 0xca62c1d6 | 0
  10. ]);
  11. var INIT = [
  12. 0x6a09e667,
  13. 0xbb67ae85,
  14. 0x3c6ef372,
  15. 0xa54ff53a,
  16. 0x510e527f,
  17. 0x9b05688c,
  18. 0x1f83d9ab,
  19. 0x5be0cd19,
  20. ];
  21. var MAX_HASHABLE_LENGTH = Math.pow(2, 53) - 1;
  22. /**
  23. * @api private
  24. */
  25. function Sha1() {
  26. this.h0 = 0x67452301;
  27. this.h1 = 0xEFCDAB89;
  28. this.h2 = 0x98BADCFE;
  29. this.h3 = 0x10325476;
  30. this.h4 = 0xC3D2E1F0;
  31. // The first 64 bytes (16 words) is the data chunk
  32. this.block = new Uint32Array(80);
  33. this.offset = 0;
  34. this.shift = 24;
  35. this.totalLength = 0;
  36. }
  37. /**
  38. * @api private
  39. */
  40. module.exports = exports = Sha1;
  41. Sha1.BLOCK_SIZE = BLOCK_SIZE;
  42. Sha1.prototype.update = function (data) {
  43. if (this.finished) {
  44. throw new Error('Attempted to update an already finished hash.');
  45. }
  46. if (hashUtils.isEmptyData(data)) {
  47. return this;
  48. }
  49. data = hashUtils.convertToBuffer(data);
  50. var length = data.length;
  51. this.totalLength += length * 8;
  52. for (var i = 0; i < length; i++) {
  53. this.write(data[i]);
  54. }
  55. return this;
  56. };
  57. Sha1.prototype.write = function write(byte) {
  58. this.block[this.offset] |= (byte & 0xff) << this.shift;
  59. if (this.shift) {
  60. this.shift -= 8;
  61. } else {
  62. this.offset++;
  63. this.shift = 24;
  64. }
  65. if (this.offset === 16) this.processBlock();
  66. };
  67. Sha1.prototype.digest = function (encoding) {
  68. // Pad
  69. this.write(0x80);
  70. if (this.offset > 14 || (this.offset === 14 && this.shift < 24)) {
  71. this.processBlock();
  72. }
  73. this.offset = 14;
  74. this.shift = 24;
  75. // 64-bit length big-endian
  76. this.write(0x00); // numbers this big aren't accurate in javascript anyway
  77. this.write(0x00); // ..So just hard-code to zero.
  78. this.write(this.totalLength > 0xffffffffff ? this.totalLength / 0x10000000000 : 0x00);
  79. this.write(this.totalLength > 0xffffffff ? this.totalLength / 0x100000000 : 0x00);
  80. for (var s = 24; s >= 0; s -= 8) {
  81. this.write(this.totalLength >> s);
  82. }
  83. // The value in state is little-endian rather than big-endian, so flip
  84. // each word into a new Uint8Array
  85. var out = new Buffer(DIGEST_LENGTH);
  86. var outView = new DataView(out.buffer);
  87. outView.setUint32(0, this.h0, false);
  88. outView.setUint32(4, this.h1, false);
  89. outView.setUint32(8, this.h2, false);
  90. outView.setUint32(12, this.h3, false);
  91. outView.setUint32(16, this.h4, false);
  92. return encoding ? out.toString(encoding) : out;
  93. };
  94. Sha1.prototype.processBlock = function processBlock() {
  95. // Extend the sixteen 32-bit words into eighty 32-bit words:
  96. for (var i = 16; i < 80; i++) {
  97. var w = this.block[i - 3] ^ this.block[i - 8] ^ this.block[i - 14] ^ this.block[i - 16];
  98. this.block[i] = (w << 1) | (w >>> 31);
  99. }
  100. // Initialize hash value for this chunk:
  101. var a = this.h0;
  102. var b = this.h1;
  103. var c = this.h2;
  104. var d = this.h3;
  105. var e = this.h4;
  106. var f, k;
  107. // Main loop:
  108. for (i = 0; i < 80; i++) {
  109. if (i < 20) {
  110. f = d ^ (b & (c ^ d));
  111. k = 0x5A827999;
  112. }
  113. else if (i < 40) {
  114. f = b ^ c ^ d;
  115. k = 0x6ED9EBA1;
  116. }
  117. else if (i < 60) {
  118. f = (b & c) | (d & (b | c));
  119. k = 0x8F1BBCDC;
  120. }
  121. else {
  122. f = b ^ c ^ d;
  123. k = 0xCA62C1D6;
  124. }
  125. var temp = (a << 5 | a >>> 27) + f + e + k + (this.block[i]|0);
  126. e = d;
  127. d = c;
  128. c = (b << 30 | b >>> 2);
  129. b = a;
  130. a = temp;
  131. }
  132. // Add this chunk's hash to result so far:
  133. this.h0 = (this.h0 + a) | 0;
  134. this.h1 = (this.h1 + b) | 0;
  135. this.h2 = (this.h2 + c) | 0;
  136. this.h3 = (this.h3 + d) | 0;
  137. this.h4 = (this.h4 + e) | 0;
  138. // The block is now reusable.
  139. this.offset = 0;
  140. for (i = 0; i < 16; i++) {
  141. this.block[i] = 0;
  142. }
  143. };