Skip to content

Instantly share code, notes, and snippets.

@pcp
Created June 4, 2015 08:47
Show Gist options
  • Save pcp/1ca84b4cb44a4b4be6b7 to your computer and use it in GitHub Desktop.
Save pcp/1ca84b4cb44a4b4be6b7 to your computer and use it in GitHub Desktop.
/* Whirlpool
* fork of http://www.sunsean.com/Whirlpool.js for nodejs
* (c) 2013 David (daXXog) Volm ><> + + + <><
* Released under Apache License, Version 2.0:
* http://www.apache.org/licenses/LICENSE-2.0.html
*/
/* UMD LOADER: https://github.com/umdjs/umd/blob/master/returnExports.js */
(function (root, factory) {
if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
} else {
// Browser globals (root is window)
root.Whirlpool = factory();
}
}(this, function() {
var Whirlpool;
/*! Whirlpool Hashing Function v3.0 ~ Sean Catchpole - Copyright 2009 Public Domain */
/*
Whirlpool was created by Paulo S.L.M. Barreto and Vincent Rijmen in 2000
This javascript implimentation of whirlpool could probably use a lot of
impovement, please feel free to make it better in any way you can.
Please note that javascript only supports 32bit bitwise operations so all
the long(64) were converted into [int(32),int(32)]
*/
;(function(){
var WP, R=10, C=[], rc=[], t, x, c, r, i,
v1, v2, v4, v5, v8, v9, sbox=
"\u1823\uc6E8\u87B8\u014F\u36A6\ud2F5\u796F\u9152"+
"\u60Bc\u9B8E\uA30c\u7B35\u1dE0\ud7c2\u2E4B\uFE57"+
"\u1577\u37E5\u9FF0\u4AdA\u58c9\u290A\uB1A0\u6B85"+
"\uBd5d\u10F4\ucB3E\u0567\uE427\u418B\uA77d\u95d8"+
"\uFBEE\u7c66\udd17\u479E\ucA2d\uBF07\uAd5A\u8333"+
"\u6302\uAA71\uc819\u49d9\uF2E3\u5B88\u9A26\u32B0"+
"\uE90F\ud580\uBEcd\u3448\uFF7A\u905F\u2068\u1AAE"+
"\uB454\u9322\u64F1\u7312\u4008\uc3Ec\udBA1\u8d3d"+
"\u9700\ucF2B\u7682\ud61B\uB5AF\u6A50\u45F3\u30EF"+
"\u3F55\uA2EA\u65BA\u2Fc0\udE1c\uFd4d\u9275\u068A"+
"\uB2E6\u0E1F\u62d4\uA896\uF9c5\u2559\u8472\u394c"+
"\u5E78\u388c\ud1A5\uE261\uB321\u9c1E\u43c7\uFc04"+
"\u5199\u6d0d\uFAdF\u7E24\u3BAB\ucE11\u8F4E\uB7EB"+
"\u3c81\u94F7\uB913\u2cd3\uE76E\uc403\u5644\u7FA9"+
"\u2ABB\uc153\udc0B\u9d6c\u3174\uF646\uAc89\u14E1"+
"\u163A\u6909\u70B6\ud0Ed\ucc42\u98A4\u285c\uF886";
for(t=8; t-->0;) C[t]=[];
for(x=0; x<256; x++) {
c = sbox.charCodeAt(x/2);
v1 = ((x & 1) == 0) ? c >>> 8 : c & 0xff;
v2 = v1 << 1;
if (v2 >= 0x100)
v2 ^= 0x11d;
v4 = v2 << 1;
if (v4 >= 0x100)
v4 ^= 0x11d;
v5 = v4 ^ v1;
v8 = v4 << 1;
if (v8 >= 0x100)
v8 ^= 0x11d;
v9 = v8 ^ v1;
// Build the circulant table C[0][x] = S[x].[1, 1, 4, 1, 8, 5, 2, 9]:
C[0][x]=[0,0];
C[0][x][0] = (v1 << 24) | (v1 << 16) | (v4 << 8) | (v1);
C[0][x][1] = (v8 << 24) | (v5 << 16) | (v2 << 8) | (v9);
// Build the remaining circulant tables C[t][x] = C[0][x] rotr t
for (var t=1; t<8; t++) {
C[t][x]=[0,0];
C[t][x][0] = (C[t - 1][x][0] >>> 8) | ((C[t - 1][x][1] << 24));
C[t][x][1] = (C[t - 1][x][1] >>> 8) | ((C[t - 1][x][0] << 24));
}
}
// Build the round constants:
rc[0] = [0,0];
for (r=1; r<=R; r++) {
i = 8*(r - 1);
rc[r]=[0,0];
rc[r][0] = (C[0][i ][0] & 0xff000000)^
(C[1][i + 1][0] & 0x00ff0000)^
(C[2][i + 2][0] & 0x0000ff00)^
(C[3][i + 3][0] & 0x000000ff);
rc[r][1] = (C[4][i + 4][1] & 0xff000000)^
(C[5][i + 5][1] & 0x00ff0000)^
(C[6][i + 6][1] & 0x0000ff00)^
(C[7][i + 7][1] & 0x000000ff);
}
var bitLength=[], // [32] Global number of hashed bits (256-bit counter).
buffer=[], // [64] Buffer of data to hash.
bufferBits=0, // Current number of bits on the buffer.
bufferPos=0, // Current (possibly incomplete) byte slot on the buffer.
// The following longs are split into [int,int]
hash=[], // [8] the hashing state
K=[], // [8] the round key
L=[], // [8] temp key?
block=[], // [8] mu(buffer)
state=[]; // [8] the chipher state
// The core Whirlpool transform.
var processBuffer = function(){
var i,j,r,s,t;
// map the buffer to a block:
for(i=0,j=0; i<8; i++,j+=8) {
block[i]=[0,0];
block[i][0] = ((buffer[j ] & 0xff) << 24)^
((buffer[j + 1] & 0xff) << 16)^
((buffer[j + 2] & 0xff) << 8)^
((buffer[j + 3] & 0xff) );
block[i][1] = ((buffer[j + 4] & 0xff) << 24)^
((buffer[j + 5] & 0xff) << 16)^
((buffer[j + 6] & 0xff) << 8)^
((buffer[j + 7] & 0xff) );
}
// compute and apply K^0 to the cipher state:
for (i=0; i<8; i++) {
state[i]=[0,0]; K[i]=[0,0];
state[i][0] = block[i][0] ^ (K[i][0] = hash[i][0]);
state[i][1] = block[i][1] ^ (K[i][1] = hash[i][1]);
}
// iterate over all rounds:
for (r=1; r<=R; r++) {
// compute K^r from K^{r-1}:
for (i=0; i<8; i++) {
L[i]=[0,0];
for (t=0,s=56,j=0; t<8; t++,s-=8,j=s<32?1:0) {
L[i][0] ^= C[t][(K[(i - t) & 7][j] >>> (s%32)) & 0xff][0];
L[i][1] ^= C[t][(K[(i - t) & 7][j] >>> (s%32)) & 0xff][1];
}
}
for (i=0; i<8; i++) {
K[i][0] = L[i][0];
K[i][1] = L[i][1];
}
K[0][0] ^= rc[r][0];
K[0][1] ^= rc[r][1];
// apply the r-th round transformation:
for (i=0; i<8; i++) {
L[i][0] = K[i][0];
L[i][1] = K[i][1];
for (t=0,s=56,j=0; t<8; t++,s-=8,j=s<32?1:0) {
L[i][0] ^= C[t][(state[(i - t) & 7][j] >>> (s%32)) & 0xff][0];
L[i][1] ^= C[t][(state[(i - t) & 7][j] >>> (s%32)) & 0xff][1];
}
}
for (i=0; i<8; i++) {
state[i][0] = L[i][0];
state[i][1] = L[i][1];
}
}
// apply the Miyaguchi-Preneel compression function:
for (i=0; i<8; i++) {
hash[i][0] ^= state[i][0] ^ block[i][0];
hash[i][1] ^= state[i][1] ^ block[i][1];
}
};
WP = Whirlpool = function(str){ return WP.init().add(str).finalize(); };
WP.version = "3.0";
// Initialize the hashing state.
WP.init = function(){
for(var i=32; i-->0;) bitLength[i]=0;
bufferBits = bufferPos = 0;
buffer = [0]; // it's only necessary to cleanup buffer[bufferPos].
for(i=8; i-->0;) hash[i]=[0,0];
return WP;
};
// sjcl.bitArray.bitLength
var bitLength = function (a) {
var l = a.length, x;
if (l === 0) { return 0; }
x = a[l - 1];
return (l-1) * 32 + (Math.round(x/0x10000000000) || 32);
};
// Convert a sjcl.bitArray or a string into utf8 byte array
var convert = function(source) {
if(Array.isArray(source)) {
// modified sjcl.codec.utf8String.fromBits
return (function(arr) {
var out = [], bl = bitLength(arr), i, tmp;
for (i=0; i<bl/8; i++) {
if ((i&3) === 0) {
tmp = arr[i/4];
}
out.push(tmp >>> 24);
tmp <<= 8;
}
return out;
})(source);
} else {
return (function(source) {
var i,n,str=source.toString(); source=[];
for(i=0; i<str.length; i++) {
n = str.charCodeAt(i);
if(n>=256) source.push(n>>>8 & 0xFF);
source.push(n & 0xFF);
}
return source;
})(source);
}
};
// Delivers input data to the hashing algorithm. Assumes bufferBits < 512
WP.add = function(source,sourceBits){
/*
sourcePos
|
+-------+-------+-------
||||||||||||||||||||| source
+-------+-------+-------
+-------+-------+-------+-------+-------+-------
|||||||||||||||||||||| buffer
+-------+-------+-------+-------+-------+-------
|
bufferPos
*/
if(!source) return WP;
if(!sourceBits) {
source = convert(source);
sourceBits = source.length*8;
}
var sourcePos = 0, // index of leftmost source byte containing data (1 to 8 bits).
sourceGap = (8 - (sourceBits & 7)) & 7, // space on source[sourcePos].
bufferRem = bufferBits & 7, // occupied bits on buffer[bufferPos].
i, b, carry, value = sourceBits;
for (i=31, carry=0; i>=0; i--) { // tally the length of the added data
carry += (bitLength[i] & 0xff) + (value % 256);
bitLength[i] = carry & 0xff;
carry >>>= 8;
value = Math.floor(value/256);
}
// process data in chunks of 8 bits:
while (sourceBits > 8) { // at least source[sourcePos] and source[sourcePos+1] contain data.
// take a byte from the source:
b = ((source[sourcePos] << sourceGap) & 0xff) |
((source[sourcePos + 1] & 0xff) >>> (8 - sourceGap));
if (b < 0 || b >= 256) return "Whirlpool requires a byte array";
// process this byte:
buffer[bufferPos++] |= b >>> bufferRem;
bufferBits += 8 - bufferRem; // bufferBits = 8*bufferPos;
if (bufferBits == 512) {
processBuffer(); // process data block
bufferBits = bufferPos = 0; buffer=[]; // reset buffer
}
buffer[bufferPos] = ((b << (8 - bufferRem)) & 0xff);
bufferBits += bufferRem;
// proceed to remaining data
sourceBits -= 8;
sourcePos++;
}
// now 0 <= sourceBits <= 8;
// furthermore, all data (if any is left) is in source[sourcePos].
if (sourceBits > 0) {
b = (source[sourcePos] << sourceGap) & 0xff; // bits are left-justified on b.
buffer[bufferPos] |= b >>> bufferRem; // process the remaining bits
} else { b = 0; }
if (bufferRem + sourceBits < 8) {
// all remaining data fits on buffer[bufferPos], and there still remains some space.
bufferBits += sourceBits;
} else {
bufferPos++; // buffer[bufferPos] is full
bufferBits += 8 - bufferRem; // bufferBits = 8*bufferPos;
sourceBits -= 8 - bufferRem;
// now 0 <= sourceBits < 8; furthermore, all data is in source[sourcePos].
if (bufferBits == 512) {
processBuffer(); // process data block
bufferBits = bufferPos = 0; buffer=[]; // reset buffer
}
buffer[bufferPos] = ((b << (8 - bufferRem)) & 0xff);
bufferBits += sourceBits;
}
return WP;
};
// Get the hash value from the hashing state. Assumes bufferBits < 512
WP.finalize = function(){
var i,j,h, str="", digest=[], hex="0123456789abcdef".split('');
buffer[bufferPos] |= 0x80 >>> (bufferBits & 7); // append a '1'-bit:
bufferPos++; // all remaining bits on the current byte are set to zero.
if(bufferPos > 32) { // pad with zero bits to complete 512N + 256 bits:
while (bufferPos < 64) buffer[bufferPos++] = 0;
processBuffer(); // process data block
bufferPos = 0; buffer=[]; // reset buffer
}
while(bufferPos < 32) buffer[bufferPos++] = 0;
buffer.push.apply(buffer,bitLength); // append bit length of hashed data
processBuffer(); // process data block
for(i=0,j=0; i<8; i++,j+=8) { // return the completed message digest
h = hash[i][0];
digest[j ] = h >>> 24 & 0xFF;
digest[j + 1] = h >>> 16 & 0xFF;
digest[j + 2] = h >>> 8 & 0xFF;
digest[j + 3] = h & 0xFF;
h = hash[i][1];
digest[j + 4] = h >>> 24 & 0xFF;
digest[j + 5] = h >>> 16 & 0xFF;
digest[j + 6] = h >>> 8 & 0xFF;
digest[j + 7] = h & 0xFF;
}
for(i=0; i<digest.length; i++) {
str+=hex[digest[i] >>> 4];
str+=hex[digest[i] & 0xF];
}
return str;
};
})();
return Whirlpool;
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment