167 lines
3.9 KiB
JavaScript
167 lines
3.9 KiB
JavaScript
|
var Buffer = require('buffer/').Buffer;
|
||
|
var hashUtils = require('./browserHashUtils');
|
||
|
|
||
|
var BLOCK_SIZE = 64;
|
||
|
|
||
|
var DIGEST_LENGTH = 20;
|
||
|
|
||
|
var KEY = new Uint32Array([
|
||
|
0x5a827999,
|
||
|
0x6ed9eba1,
|
||
|
0x8f1bbcdc | 0,
|
||
|
0xca62c1d6 | 0
|
||
|
]);
|
||
|
|
||
|
var INIT = [
|
||
|
0x6a09e667,
|
||
|
0xbb67ae85,
|
||
|
0x3c6ef372,
|
||
|
0xa54ff53a,
|
||
|
0x510e527f,
|
||
|
0x9b05688c,
|
||
|
0x1f83d9ab,
|
||
|
0x5be0cd19,
|
||
|
];
|
||
|
|
||
|
var MAX_HASHABLE_LENGTH = Math.pow(2, 53) - 1;
|
||
|
|
||
|
/**
|
||
|
* @api private
|
||
|
*/
|
||
|
function Sha1() {
|
||
|
this.h0 = 0x67452301;
|
||
|
this.h1 = 0xEFCDAB89;
|
||
|
this.h2 = 0x98BADCFE;
|
||
|
this.h3 = 0x10325476;
|
||
|
this.h4 = 0xC3D2E1F0;
|
||
|
// The first 64 bytes (16 words) is the data chunk
|
||
|
this.block = new Uint32Array(80);
|
||
|
this.offset = 0;
|
||
|
this.shift = 24;
|
||
|
this.totalLength = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @api private
|
||
|
*/
|
||
|
module.exports = exports = Sha1;
|
||
|
|
||
|
Sha1.BLOCK_SIZE = BLOCK_SIZE;
|
||
|
|
||
|
Sha1.prototype.update = function (data) {
|
||
|
if (this.finished) {
|
||
|
throw new Error('Attempted to update an already finished hash.');
|
||
|
}
|
||
|
|
||
|
if (hashUtils.isEmptyData(data)) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
data = hashUtils.convertToBuffer(data);
|
||
|
|
||
|
var length = data.length;
|
||
|
this.totalLength += length * 8;
|
||
|
for (var i = 0; i < length; i++) {
|
||
|
this.write(data[i]);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
Sha1.prototype.write = function write(byte) {
|
||
|
this.block[this.offset] |= (byte & 0xff) << this.shift;
|
||
|
if (this.shift) {
|
||
|
this.shift -= 8;
|
||
|
} else {
|
||
|
this.offset++;
|
||
|
this.shift = 24;
|
||
|
}
|
||
|
|
||
|
if (this.offset === 16) this.processBlock();
|
||
|
};
|
||
|
|
||
|
Sha1.prototype.digest = function (encoding) {
|
||
|
// Pad
|
||
|
this.write(0x80);
|
||
|
if (this.offset > 14 || (this.offset === 14 && this.shift < 24)) {
|
||
|
this.processBlock();
|
||
|
}
|
||
|
this.offset = 14;
|
||
|
this.shift = 24;
|
||
|
|
||
|
// 64-bit length big-endian
|
||
|
this.write(0x00); // numbers this big aren't accurate in javascript anyway
|
||
|
this.write(0x00); // ..So just hard-code to zero.
|
||
|
this.write(this.totalLength > 0xffffffffff ? this.totalLength / 0x10000000000 : 0x00);
|
||
|
this.write(this.totalLength > 0xffffffff ? this.totalLength / 0x100000000 : 0x00);
|
||
|
for (var s = 24; s >= 0; s -= 8) {
|
||
|
this.write(this.totalLength >> s);
|
||
|
}
|
||
|
// The value in state is little-endian rather than big-endian, so flip
|
||
|
// each word into a new Uint8Array
|
||
|
var out = new Buffer(DIGEST_LENGTH);
|
||
|
var outView = new DataView(out.buffer);
|
||
|
outView.setUint32(0, this.h0, false);
|
||
|
outView.setUint32(4, this.h1, false);
|
||
|
outView.setUint32(8, this.h2, false);
|
||
|
outView.setUint32(12, this.h3, false);
|
||
|
outView.setUint32(16, this.h4, false);
|
||
|
|
||
|
return encoding ? out.toString(encoding) : out;
|
||
|
};
|
||
|
|
||
|
Sha1.prototype.processBlock = function processBlock() {
|
||
|
// Extend the sixteen 32-bit words into eighty 32-bit words:
|
||
|
for (var i = 16; i < 80; i++) {
|
||
|
var w = this.block[i - 3] ^ this.block[i - 8] ^ this.block[i - 14] ^ this.block[i - 16];
|
||
|
this.block[i] = (w << 1) | (w >>> 31);
|
||
|
}
|
||
|
|
||
|
// Initialize hash value for this chunk:
|
||
|
var a = this.h0;
|
||
|
var b = this.h1;
|
||
|
var c = this.h2;
|
||
|
var d = this.h3;
|
||
|
var e = this.h4;
|
||
|
var f, k;
|
||
|
|
||
|
// Main loop:
|
||
|
for (i = 0; i < 80; i++) {
|
||
|
if (i < 20) {
|
||
|
f = d ^ (b & (c ^ d));
|
||
|
k = 0x5A827999;
|
||
|
}
|
||
|
else if (i < 40) {
|
||
|
f = b ^ c ^ d;
|
||
|
k = 0x6ED9EBA1;
|
||
|
}
|
||
|
else if (i < 60) {
|
||
|
f = (b & c) | (d & (b | c));
|
||
|
k = 0x8F1BBCDC;
|
||
|
}
|
||
|
else {
|
||
|
f = b ^ c ^ d;
|
||
|
k = 0xCA62C1D6;
|
||
|
}
|
||
|
var temp = (a << 5 | a >>> 27) + f + e + k + (this.block[i]|0);
|
||
|
e = d;
|
||
|
d = c;
|
||
|
c = (b << 30 | b >>> 2);
|
||
|
b = a;
|
||
|
a = temp;
|
||
|
}
|
||
|
|
||
|
// Add this chunk's hash to result so far:
|
||
|
this.h0 = (this.h0 + a) | 0;
|
||
|
this.h1 = (this.h1 + b) | 0;
|
||
|
this.h2 = (this.h2 + c) | 0;
|
||
|
this.h3 = (this.h3 + d) | 0;
|
||
|
this.h4 = (this.h4 + e) | 0;
|
||
|
|
||
|
// The block is now reusable.
|
||
|
this.offset = 0;
|
||
|
for (i = 0; i < 16; i++) {
|
||
|
this.block[i] = 0;
|
||
|
}
|
||
|
};
|