c9-core/node_modules/rusha/rusha.sweet.js

526 wiersze
17 KiB
JavaScript

/*
* Rusha, a JavaScript implementation of the Secure Hash Algorithm, SHA-1,
* as defined in FIPS PUB 180-1, tuned for high performance with large inputs.
* (http://github.com/srijs/rusha)
*
* Inspired by Paul Johnstons implementation (http://pajhome.org.uk/crypt/md5).
*
* Copyright (c) 2013 Sam Rijs (http://awesam.de).
* Released under the terms of the MIT license as follows:
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
(function () {
var util = {
getDataType: function (data) {
if (typeof data === 'string') {
return 'string';
}
if (data instanceof Array) {
return 'array';
}
if (typeof global !== 'undefined' && global.Buffer && global.Buffer.isBuffer(data)) {
return 'buffer';
}
if (data instanceof ArrayBuffer) {
return 'arraybuffer';
}
if (data.buffer instanceof ArrayBuffer) {
return 'view';
}
if (data instanceof Blob) {
return 'blob';
}
throw new Error('Unsupported data type.');
}
};
// The Rusha object is a wrapper around the low-level RushaCore.
// It provides means of converting different inputs to the
// format accepted by RushaCore as well as other utility methods.
function Rusha (chunkSize) {
"use strict";
// Private object structure.
var self = {fill: 0};
// Calculate the length of buffer that the sha1 routine uses
// including the padding.
var padlen = function (len) {
for (len += 9; len % 64 > 0; len += 1);
return len;
};
var padZeroes = function (bin, len) {
var h8 = new Uint8Array(bin.buffer);
var om = len % 4, align = len - om;
switch (om) {
case 0: h8[align + 3] = 0;
case 1: h8[align + 2] = 0;
case 2: h8[align + 1] = 0;
case 3: h8[align + 0] = 0;
}
for (var i = (len >> 2) + 1; i < bin.length; i++)
bin[i] = 0;
};
var padData = function (bin, chunkLen, msgLen) {
bin[chunkLen>>2] |= 0x80 << (24 - (chunkLen % 4 << 3));
// To support msgLen >= 2 GiB, use a float division when computing the
// high 32-bits of the big-endian message length in bits.
bin[(((chunkLen >> 2) + 2) & ~0x0f) + 14] = (msgLen / (1 << 29)) |0;
bin[(((chunkLen >> 2) + 2) & ~0x0f) + 15] = msgLen << 3;
};
// Convert a binary string and write it to the heap.
// A binary string is expected to only contain char codes < 256.
var convStr = function (H8, H32, start, len, off) {
var str = this, i, om = off % 4, lm = (len + om) % 4, j = len - lm;
switch (om) {
case 0: H8[off] = str.charCodeAt(start+3);
case 1: H8[off+1-(om<<1)|0] = str.charCodeAt(start+2);
case 2: H8[off+2-(om<<1)|0] = str.charCodeAt(start+1);
case 3: H8[off+3-(om<<1)|0] = str.charCodeAt(start);
}
if (len < lm + om) {
return;
}
for (i = 4 - om; i < j; i = i + 4 | 0) {
H32[off+i>>2] = str.charCodeAt(start+i) << 24 |
str.charCodeAt(start+i+1) << 16 |
str.charCodeAt(start+i+2) << 8 |
str.charCodeAt(start+i+3);
}
switch (lm) {
case 3: H8[off+j+1|0] = str.charCodeAt(start+j+2);
case 2: H8[off+j+2|0] = str.charCodeAt(start+j+1);
case 1: H8[off+j+3|0] = str.charCodeAt(start+j);
}
};
// Convert a buffer or array and write it to the heap.
// The buffer or array is expected to only contain elements < 256.
var convBuf = function (H8, H32, start, len, off) {
var buf = this, i, om = off % 4, lm = (len + om) % 4, j = len - lm;
switch (om) {
case 0: H8[off] = buf[start + 3];
case 1: H8[off+1-(om<<1)|0] = buf[start+2];
case 2: H8[off+2-(om<<1)|0] = buf[start+1];
case 3: H8[off+3-(om<<1)|0] = buf[start];
}
if (len < lm + om) {
return;
}
for (i = 4 - om; i < j; i = i + 4 | 0) {
H32[off+i>>2|0] = buf[start+i] << 24 |
buf[start+i+1] << 16 |
buf[start+i+2] << 8 |
buf[start+i+3];
}
switch (lm) {
case 3: H8[off+j+1|0] = buf[start+j+2];
case 2: H8[off+j+2|0] = buf[start+j+1];
case 1: H8[off+j+3|0] = buf[start+j];
}
};
var convBlob = function (H8, H32, start, len, off) {
var blob = this, i, om = off % 4, lm = (len + om) % 4, j = len - lm;
var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len)));
switch (om) {
case 0: H8[off] = buf[3];
case 1: H8[off+1-(om<<1)|0] = buf[2];
case 2: H8[off+2-(om<<1)|0] = buf[1];
case 3: H8[off+3-(om<<1)|0] = buf[0];
}
if (len < lm + om) {
return;
}
for (i = 4 - om; i < j; i = i + 4 | 0) {
H32[off+i>>2|0] = buf[i] << 24 |
buf[i+1] << 16 |
buf[i+2] << 8 |
buf[i+3];
}
switch (lm) {
case 3: H8[off+j+1|0] = buf[j + 2];
case 2: H8[off+j+2|0] = buf[j + 1];
case 1: H8[off+j+3|0] = buf[j];
}
};
var convFn = function (data) {
switch (util.getDataType(data)) {
case 'string': return convStr.bind(data);
case 'array': return convBuf.bind(data);
case 'buffer': return convBuf.bind(data);
case 'arraybuffer': return convBuf.bind(new Uint8Array(data));
case 'view': return convBuf.bind(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
case 'blob': return convBlob.bind(data);
}
};
var slice = function (data, offset) {
switch (util.getDataType(data)) {
case 'string': return data.slice(offset);
case 'array': return data.slice(offset);
case 'buffer': return data.slice(offset);
case 'arraybuffer': return data.slice(offset);
case 'view': return data.buffer.slice(offset);
}
};
// Precompute 00 - ff strings
var precomputedHex = new Array(256);
for (var i = 0; i < 256; i++) {
precomputedHex[i] = (i < 0x10 ? '0' : '') + i.toString(16);
}
// Convert an ArrayBuffer into its hexadecimal string representation.
var hex = function (arrayBuffer) {
var binarray = new Uint8Array(arrayBuffer);
var res = new Array(arrayBuffer.byteLength);
for (var i = 0; i < res.length; i++) {
res[i] = precomputedHex[binarray[i]];
}
return res.join('');
};
var ceilHeapSize = function (v) {
// The asm.js spec says:
// The heap object's byteLength must be either
// 2^n for n in [12, 24) or 2^24 * n for n ≥ 1.
// Also, byteLengths smaller than 2^16 are deprecated.
var p;
// If v is smaller than 2^16, the smallest possible solution
// is 2^16.
if (v <= 65536) return 65536;
// If v < 2^24, we round up to 2^n,
// otherwise we round up to 2^24 * n.
if (v < 16777216) {
for (p = 1; p < v; p = p << 1);
} else {
for (p = 16777216; p < v; p += 16777216);
}
return p;
};
// Initialize the internal data structures to a new capacity.
var init = function (size) {
if (size % 64 > 0) {
throw new Error('Chunk size must be a multiple of 128 bit');
}
self.offset = 0;
self.maxChunkLen = size;
self.padMaxChunkLen = padlen(size);
// The size of the heap is the sum of:
// 1. The padded input message size
// 2. The extended space the algorithm needs (320 byte)
// 3. The 160 bit state the algoritm uses
self.heap = new ArrayBuffer(ceilHeapSize(self.padMaxChunkLen + 320 + 20));
self.h32 = new Int32Array(self.heap);
self.h8 = new Int8Array(self.heap);
self.core = new Rusha._core({Int32Array: Int32Array, DataView: DataView}, {}, self.heap);
self.buffer = null;
};
// Iinitializethe datastructures according
// to a chunk siyze.
init(chunkSize || 64 * 1024);
var initState = function (heap, padMsgLen) {
self.offset = 0;
var io = new Int32Array(heap, padMsgLen + 320, 5);
io[0] = 1732584193;
io[1] = -271733879;
io[2] = -1732584194;
io[3] = 271733878;
io[4] = -1009589776;
};
var padChunk = function (chunkLen, msgLen) {
var padChunkLen = padlen(chunkLen);
var view = new Int32Array(self.heap, 0, padChunkLen >> 2);
padZeroes(view, chunkLen);
padData(view, chunkLen, msgLen);
return padChunkLen;
};
// Write data to the heap.
var write = function (data, chunkOffset, chunkLen, off) {
convFn(data)(self.h8, self.h32, chunkOffset, chunkLen, off || 0);
};
// Initialize and call the RushaCore,
// assuming an input buffer of length len * 4.
var coreCall = function (data, chunkOffset, chunkLen, msgLen, finalize) {
var padChunkLen = chunkLen;
write(data, chunkOffset, chunkLen);
if (finalize) {
padChunkLen = padChunk(chunkLen, msgLen);
}
self.core.hash(padChunkLen, self.padMaxChunkLen);
};
var getRawDigest = function (heap, padMaxChunkLen) {
var io = new Int32Array(heap, padMaxChunkLen + 320, 5);
var out = new Int32Array(5);
var arr = new DataView(out.buffer);
arr.setInt32(0, io[0], false);
arr.setInt32(4, io[1], false);
arr.setInt32(8, io[2], false);
arr.setInt32(12, io[3], false);
arr.setInt32(16, io[4], false);
return out;
};
// Calculate the hash digest as an array of 5 32bit integers.
var rawDigest = this.rawDigest = function (str) {
var msgLen = str.byteLength || str.length || str.size || 0;
initState(self.heap, self.padMaxChunkLen);
var chunkOffset = 0, chunkLen = self.maxChunkLen, last;
for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) {
coreCall(str, chunkOffset, chunkLen, msgLen, false);
}
coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true);
return getRawDigest(self.heap, self.padMaxChunkLen);
};
// The digest and digestFrom* interface returns the hash digest
// as a hex string.
this.digest = this.digestFromString =
this.digestFromBuffer = this.digestFromArrayBuffer =
function (str) {
return hex(rawDigest(str).buffer);
};
this.resetState = function () {
initState(self.heap, self.padMaxChunkLen);
return this;
};
this.append = function (chunk) {
var chunkOffset = 0;
var chunkLen = chunk.byteLength || chunk.length || chunk.size || 0;
var turnOffset = self.offset % self.maxChunkLen;
var inputLen;
self.offset += chunkLen;
while (chunkOffset < chunkLen) {
inputLen = Math.min(chunkLen - chunkOffset, self.maxChunkLen - turnOffset);
write(chunk, chunkOffset, inputLen, turnOffset);
turnOffset += inputLen;
chunkOffset += inputLen;
if (turnOffset === self.maxChunkLen) {
self.core.hash(self.maxChunkLen, self.padMaxChunkLen);
turnOffset = 0;
}
}
return this;
};
this.getState = function () {
var turnOffset = self.offset % self.maxChunkLen;
var heap;
if (!turnOffset) {
var io = new Int32Array(self.heap, self.padMaxChunkLen + 320, 5);
heap = io.buffer.slice(io.byteOffset, io.byteOffset + io.byteLength)
} else {
heap = self.heap.slice(0);
}
return {
offset: self.offset,
heap: heap
};
};
this.setState = function (state) {
self.offset = state.offset;
if (state.heap.byteLength === 20) {
var io = new Int32Array(self.heap, self.padMaxChunkLen + 320, 5);
io.set(new Int32Array(state.heap));
} else {
self.h32.set(new Int32Array(state.heap));
}
return this;
};
var rawEnd = this.rawEnd = function () {
var msgLen = self.offset;
var chunkLen = msgLen % self.maxChunkLen;
var padChunkLen = padChunk(chunkLen, msgLen);
self.core.hash(padChunkLen, self.padMaxChunkLen);
var result = getRawDigest(self.heap, self.padMaxChunkLen);
initState(self.heap, self.padMaxChunkLen);
return result;
};
this.end = function () {
return hex(rawEnd().buffer);
};
};
macro rol1 { rule { ($v:expr) } => { ($v << 1 | $v >>> 31) } }
macro rol5 { rule { ($v:expr) } => { ($v << 5 | $v >>> 27) } }
macro rol30 { rule { ($v:expr) } => { ($v << 30 | $v >>> 2) } }
macro extended {
rule { ($H, $j:expr) } => {
rol1($H[$j-12>>2] ^ $H[$j-32>>2] ^ $H[$j-56>>2] ^ $H[$j-64>>2])
}
}
macro F0 { rule { ($b,$c,$d) } => { ($b & $c | ~$b & $d) } }
macro F1 { rule { ($b,$c,$d) } => { ($b ^ $c ^ $d) }}
macro F2 { rule { ($b,$c,$d) } => { ($b & $c | $b & $d | $c & $d) }}
macro swap {
rule { ($y0, $y1, $y2, $y3, $y4, $t0) } => {
$y4 = $y3;
$y3 = $y2;
$y2 = rol30($y1);
$y1 = $y0;
$y0 = $t0;
}
}
macro roundL { rule { ($y0, $f:expr) } => { (rol5($y0) + $f |0) } }
macro roundR { rule { ($y4, $t1) } => { ($t1 + $y4 |0) } }
// The low-level RushCore module provides the heart of Rusha,
// a high-speed sha1 implementation working on an Int32Array heap.
// At first glance, the implementation seems complicated, however
// with the SHA1 spec at hand, it is obvious this almost a textbook
// implementation that has a few functions hand-inlined and a few loops
// hand-unrolled.
Rusha._core = function RushaCore (stdlib, foreign, heap) {
"use asm";
var H = new stdlib.Int32Array(heap);
function hash (k, x) { // k in bytes
k = k|0;
x = x|0;
var i = 0, j = 0,
y0 = 0, z0 = 0, y1 = 0, z1 = 0,
y2 = 0, z2 = 0, y3 = 0, z3 = 0,
y4 = 0, z4 = 0, t0 = 0, t1 = 0;
y0 = H[x+320>>2]|0;
y1 = H[x+324>>2]|0;
y2 = H[x+328>>2]|0;
y3 = H[x+332>>2]|0;
y4 = H[x+336>>2]|0;
for (i = 0; (i|0) < (k|0); i = i + 64 |0) {
z0 = y0;
z1 = y1;
z2 = y2;
z3 = y3;
z4 = y4;
for (j = 0; (j|0) < 64; j = j + 4 |0) {
t1 = H[i+j>>2]|0;
t0 = roundL(y0, F0(y1, y2, y3)) + (roundR(y4, t1) + 1518500249 |0) |0;
swap(y0, y1, y2, y3, y4, t0)
H[k+j>>2] = t1;
}
for (j = k + 64 |0; (j|0) < (k + 80 |0); j = j + 4 |0) {
t1 = extended(H, j);
t0 = roundL(y0, F0(y1, y2, y3)) + (roundR(y4, t1) + 1518500249 |0) |0;
swap(y0, y1, y2, y3, y4, t0)
H[j>>2] = t1;
}
for (j = k + 80 |0; (j|0) < (k + 160 |0); j = j + 4 |0) {
t1 = extended(H, j);
t0 = roundL(y0, F1(y1, y2, y3)) + (roundR(y4, t1) + 1859775393 |0) |0;
swap(y0, y1, y2, y3, y4, t0)
H[j>>2] = t1;
}
for (j = k + 160 |0; (j|0) < (k + 240 |0); j = j + 4 |0) {
t1 = extended(H, j);
t0 = roundL(y0, F2(y1, y2, y3)) + (roundR(y4, t1) - 1894007588 |0) |0;
swap(y0, y1, y2, y3, y4, t0)
H[j>>2] = t1;
}
for (j = k + 240 |0; (j|0) < (k + 320 |0); j = j + 4 |0) {
t1 = extended(H, j);
t0 = roundL(y0, F1(y1, y2, y3)) + (roundR(y4, t1) - 899497514 |0) |0;
swap(y0, y1, y2, y3, y4, t0)
H[j>>2] = t1;
}
y0 = y0 + z0 |0;
y1 = y1 + z1 |0;
y2 = y2 + z2 |0;
y3 = y3 + z3 |0;
y4 = y4 + z4 |0;
}
H[x+320>>2] = y0;
H[x+324>>2] = y1;
H[x+328>>2] = y2;
H[x+332>>2] = y3;
H[x+336>>2] = y4;
}
return {hash: hash};
}
// If we'e running in Node.JS, export a module.
if (typeof module !== 'undefined') {
module.exports = Rusha;
}
// If we're running in a DOM context, export
// the Rusha object to toplevel.
else if (typeof window !== 'undefined') {
window.Rusha = Rusha;
}
// If we're running in a webworker, accept
// messages containing a jobid and a buffer
// or blob object, and return the hash result.
if (typeof FileReaderSync !== 'undefined') {
var reader = new FileReaderSync(),
hasher = new Rusha(4 * 1024 * 1024);
self.onmessage = function onMessage (event) {
var hash, data = event.data.data;
try {
hash = hasher.digest(data);
self.postMessage({id: event.data.id, hash: hash});
} catch (e) {
self.postMessage({id: event.data.id, error: e.name});
}
};
}
})();