Igor Zhukov
10 years ago
6 changed files with 417 additions and 16 deletions
@ -0,0 +1,392 @@ |
|||||||
|
/* |
||||||
|
* 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 (global) { |
||||||
|
// 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.
|
||||||
|
if (typeof global !== 'undefined') { |
||||||
|
global.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; |
||||||
|
if (data instanceof Blob) { |
||||||
|
try { |
||||||
|
data = reader.readAsBinaryString(data); |
||||||
|
} catch (e) { |
||||||
|
self.postMessage({ |
||||||
|
id: event.data.id, |
||||||
|
error: e.name |
||||||
|
}); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
hash = hasher.digest(data); |
||||||
|
self.postMessage({ |
||||||
|
id: event.data.id, |
||||||
|
hash: hash |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
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'; |
||||||
|
} |
||||||
|
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$2 = { 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) { |
||||||
|
for (var i = len >> 2; i < bin.length; i++) |
||||||
|
bin[i] = 0; |
||||||
|
}; |
||||||
|
var padData = function (bin, chunkLen, msgLen) { |
||||||
|
bin[chunkLen >> 2] |= 128 << 24 - (chunkLen % 4 << 3); |
||||||
|
bin[((chunkLen >> 2) + 2 & ~15) + 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 % 4, j = len - lm; |
||||||
|
if (j > 0) { |
||||||
|
switch (om) { |
||||||
|
case 0: |
||||||
|
H8[off + 3 | 0] = str.charCodeAt(start); |
||||||
|
case 1: |
||||||
|
H8[off + 2 | 0] = str.charCodeAt(start + 1); |
||||||
|
case 2: |
||||||
|
H8[off + 1 | 0] = str.charCodeAt(start + 2); |
||||||
|
case 3: |
||||||
|
H8[off | 0] = str.charCodeAt(start + 3); |
||||||
|
} |
||||||
|
} |
||||||
|
for (i = 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 % 4, j = len - lm; |
||||||
|
if (j > 0) { |
||||||
|
switch (om) { |
||||||
|
case 0: |
||||||
|
H8[off + 3 | 0] = buf[start]; |
||||||
|
case 1: |
||||||
|
H8[off + 2 | 0] = buf[start + 1]; |
||||||
|
case 2: |
||||||
|
H8[off + 1 | 0] = buf[start + 2]; |
||||||
|
case 3: |
||||||
|
H8[off | 0] = buf[start + 3]; |
||||||
|
} |
||||||
|
} |
||||||
|
for (i = 4 - om; i < j; i = i += 4 | 0) { |
||||||
|
H32[off + i >> 2] = 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 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)); |
||||||
|
} |
||||||
|
}; |
||||||
|
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); |
||||||
|
} |
||||||
|
}; |
||||||
|
// Convert an ArrayBuffer into its hexadecimal string representation.
|
||||||
|
var hex = function (arrayBuffer) { |
||||||
|
var i, x, hex_tab = '0123456789abcdef', res = [], binarray = new Uint8Array(arrayBuffer); |
||||||
|
for (i = 0; i < binarray.length; i++) { |
||||||
|
x = binarray[i]; |
||||||
|
res[i] = hex_tab.charAt(x >> 4 & 15) + hex_tab.charAt(x >> 0 & 15); |
||||||
|
} |
||||||
|
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$2.maxChunkLen = size; |
||||||
|
self$2.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$2.heap = new ArrayBuffer(ceilHeapSize(self$2.padMaxChunkLen + 320 + 20)); |
||||||
|
self$2.h32 = new Int32Array(self$2.heap); |
||||||
|
self$2.h8 = new Int8Array(self$2.heap); |
||||||
|
self$2.core = RushaCore({ |
||||||
|
Int32Array: Int32Array, |
||||||
|
DataView: DataView |
||||||
|
}, {}, self$2.heap); |
||||||
|
self$2.buffer = null; |
||||||
|
}; |
||||||
|
// Iinitializethe datastructures according
|
||||||
|
// to a chunk siyze.
|
||||||
|
init(chunkSize || 64 * 1024); |
||||||
|
var initState = function (heap, padMsgLen) { |
||||||
|
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$2.heap, 0, padChunkLen >> 2); |
||||||
|
padZeroes(view, chunkLen); |
||||||
|
padData(view, chunkLen, msgLen); |
||||||
|
return padChunkLen; |
||||||
|
}; |
||||||
|
// Write data to the heap.
|
||||||
|
var write = function (data, chunkOffset, chunkLen) { |
||||||
|
convFn(data)(self$2.h8, self$2.h32, chunkOffset, chunkLen, 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; |
||||||
|
if (finalize) { |
||||||
|
padChunkLen = padChunk(chunkLen, msgLen); |
||||||
|
} |
||||||
|
write(data, chunkOffset, chunkLen); |
||||||
|
self$2.core.hash(padChunkLen, self$2.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; |
||||||
|
initState(self$2.heap, self$2.padMaxChunkLen); |
||||||
|
var chunkOffset = 0, chunkLen = self$2.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$2.heap, self$2.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); |
||||||
|
}; |
||||||
|
} |
||||||
|
; |
||||||
|
// 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.
|
||||||
|
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 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0; |
||||||
|
y4 = y3; |
||||||
|
y3 = y2; |
||||||
|
y2 = y1 << 30 | y1 >>> 2; |
||||||
|
y1 = y0; |
||||||
|
y0 = t0; |
||||||
|
; |
||||||
|
H[k + j >> 2] = t1; |
||||||
|
} |
||||||
|
for (j = k + 64 | 0; (j | 0) < (k + 80 | 0); j = j + 4 | 0) { |
||||||
|
t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31; |
||||||
|
t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0; |
||||||
|
y4 = y3; |
||||||
|
y3 = y2; |
||||||
|
y2 = y1 << 30 | y1 >>> 2; |
||||||
|
y1 = y0; |
||||||
|
y0 = t0; |
||||||
|
; |
||||||
|
H[j >> 2] = t1; |
||||||
|
} |
||||||
|
for (j = k + 80 | 0; (j | 0) < (k + 160 | 0); j = j + 4 | 0) { |
||||||
|
t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31; |
||||||
|
t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) + 1859775393 | 0) | 0; |
||||||
|
y4 = y3; |
||||||
|
y3 = y2; |
||||||
|
y2 = y1 << 30 | y1 >>> 2; |
||||||
|
y1 = y0; |
||||||
|
y0 = t0; |
||||||
|
; |
||||||
|
H[j >> 2] = t1; |
||||||
|
} |
||||||
|
for (j = k + 160 | 0; (j | 0) < (k + 240 | 0); j = j + 4 | 0) { |
||||||
|
t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31; |
||||||
|
t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | y1 & y3 | y2 & y3) | 0) + ((t1 + y4 | 0) - 1894007588 | 0) | 0; |
||||||
|
y4 = y3; |
||||||
|
y3 = y2; |
||||||
|
y2 = y1 << 30 | y1 >>> 2; |
||||||
|
y1 = y0; |
||||||
|
y0 = t0; |
||||||
|
; |
||||||
|
H[j >> 2] = t1; |
||||||
|
} |
||||||
|
for (j = k + 240 | 0; (j | 0) < (k + 320 | 0); j = j + 4 | 0) { |
||||||
|
t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31; |
||||||
|
t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) - 899497514 | 0) | 0; |
||||||
|
y4 = y3; |
||||||
|
y3 = y2; |
||||||
|
y2 = y1 << 30 | y1 >>> 2; |
||||||
|
y1 = y0; |
||||||
|
y0 = 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 }; |
||||||
|
} |
||||||
|
}(this)); |
Loading…
Reference in new issue