/** SHA512 for JavaScript
*
* The MIT License (MIT)
* Copyright (c) 2016 Leonardone @ NEETSDKASU
*/
/** @namespace */
var SHA512JS = new function() {
function IntClass(sz) {
// sz: byte size (i.e. sz = 8 -> 64bit Int)
// require sz == pow(2,x) (x >= 1)
var BITLEN = 16; // bit length of a unit
var MASK = 0xFFFF; // bit mask of a unit
var UNITSHIFTMASK = BITLEN - 1; // shifter mask in a unit
var BYTESIZE = sz; // byte size of Integer
var INTBITS = BYTESIZE << 3; // bit size of Integer
var SHIFTMASK = INTBITS - 1; // shifter mask for bitwise shift
var LEN = INTBITS >> 4; // count of units for Integer
var SIZE = LEN + 1; // count of units with a unit for carry
var ROTMASK = LEN - 1; // unit index mask for rotation
this.ZERO = (function() {
var i, v = new Array(SIZE);
for (i = 0; i < SIZE; i++) {
v[i] = 0;
}
return v;
})();
this.valueOf = function(n) {
// n: Number(Integer)
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = n & MASK;
n >>= BITLEN; // shift right n as 32bits singed integer on javascript
}
v[LEN] = 0;
return v;
};
this.parse = function(s) {
// s: Hex String
var i, v = new Array(SIZE), j;
i = 0;
for (j = s.length - 4; j >= 0 && i < LEN; j -= 4) { // BITLEN(16) -> 4 Hex-characters
v[i] = parseInt('0x' + s.substring(j, j + 4));
i++;
}
if (j > -4 && i < LEN) {
v[i] = parseInt('0x' + s.substring(0, j + 4));
i++;
}
for (; i < SIZE; i++) {
v[i] = 0;
}
return v;
};
this.equals = function(v1, v2) {
var i;
for (i = 0; i < SIZE; i++) {
if (v1[i] !== v2[i]) {
return false;
}
}
return true;
};
this.add = function(v1, v2) {
var i, v = new Array(SIZE), t = 0;
for (i = 0; i < LEN; i++) {
t += v1[i] + v2[i];
v[i] = t & MASK;
t >>= BITLEN;
}
v[LEN] = 0;
return v;
};
this.add4 = function(v1, v2, v3, v4) {
var i, v = new Array(SIZE), t = 0;
for (i = 0; i < LEN; i++) {
t += v1[i] + v2[i] + v3[i] + v4[i];
v[i] = t & MASK;
t >>= BITLEN;
}
v[LEN] = 0;
return v;
};
this.add5 = function(v1, v2, v3, v4, v5) {
var i, v = new Array(SIZE), t = 0;
for (i = 0; i < LEN; i++) {
t += v1[i] + v2[i] + v3[i] + v4[i] + v5[i];
v[i] = t & MASK;
t >>= BITLEN;
}
v[LEN] = 0;
return v;
};
this.bwXor = function(v1, v2) {
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = v1[i] ^ v2[i];
}
v[LEN] = 0;
return v;
};
this.bwXor3 = function(v1, v2, v3) {
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = v1[i] ^ v2[i] ^ v3[i];
}
v[LEN] = 0;
return v;
};
this.bwAnd = function(v1, v2) {
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = v1[i] & v2[i];
}
v[LEN] = 0;
return v;
};
this.bwOr = function(v1, v2) {
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = v1[i] | v2[i];
}
v[LEN] = 0;
return v;
};
this.bwNot = function(v1) {
var i, v = new Array(SIZE);
for (i = 0; i < LEN; i++) {
v[i] = v1[i] ^ MASK;
}
v[LEN] = 0;
return v;
};
this.shiftR = function(v1, s) {
var i, v = new Array(SIZE), k, m, p;
k = (s & SHIFTMASK) >> 4; // div BITLEN(16)
for (i = 0; i + k < LEN; i++) {
v[i] = v1[i + k];
}
s &= UNITSHIFTMASK;
m = (1 << s) - 1; // mask
p = BITLEN - s; // shiftL
for (i = 0; i < LEN; i++) {
v[i] >>= s;
v[i] |= (v[i + 1] & m) << p;
}
v[LEN] = 0;
return v;
};
this.rotateR = function(v1, r) {
var i, v = new Array(SIZE), k, m, p;
k = (r & SHIFTMASK) >> 4; // div BITLEN(16)
for (i = 0; i < LEN; i++) {
v[i] = v1[(i + k) & ROTMASK];
}
r &= UNITSHIFTMASK;
m = (1 << r) - 1; // mask
p = BITLEN - r; // shiftL
v[LEN] = v[0] & m; // lowest bits
for (i = 0; i < LEN; i++) {
v[i] >>= r;
v[i] |= (v[i + 1] & m) << p;
}
v[LEN] = 0;
return v;
};
this.toHex = function(v) {
var i, c, s = '';
for (i = LEN - 1; i >= 0; i--) {
c = v[i].toString(16);
while (c.length < 4) { c = '0' + c; } // 4 hex-characters at BITLEN(16)
s += c;
}
return s;
};
this.copyBytes = function(v, dest, offset) {
var i, j = offset;
for (i = LEN - 1; i >= 0; i--) {
dest[j] = v[i] >> 8; // BITLEN(16) -> 2 bytes
dest[j + 1] = v[i] & 0xFF;
j += 2;
}
};
var packer = new function () {
this.create = function() {
return {
"v": new Array(LEN),
"p": 0
};
};
this.init = function(vp) {
var i;
for (i = 0; i < LEN; i++) {
vp.v[i] = 0;
}
vp.p = BYTESIZE;
};
this.push = function(vp, x) {
if (vp.p > 0) {
vp.p--;
vp.v[vp.p >> 1] |= (x & 0xFF) << ((vp.p & 1) << 3); // BITLEN(16)
}
};
this.getCopy = function(vp) {
var i, r = new Array(SIZE);
for (i = 0; i < LEN; i++) {
r[i] = vp.v[i]
}
r[LEN] = 0;
return r;
};
}
this.getPacker = function() {
return packer;
};
};
var EMPTYITER = new function() {
this.hasNext = function() { return false; };
this.next = function() { throw 'EMPTYITER: No item!'; };
this.size = function() { return 0; };
};
function ByteArrayByteIterator() {
var _arr = [];
var _idx = 0;
var _iend = 0;
var _size = 0;
this.hasNext = function() { return _idx < _iend; };
this.next = function() { return _arr[_idx++]; };
this.size = function() { return _size; };
this.init = function(data, offset, len) {
_arr = data; _idx = offset; _iend = offset + len; _size = len;
};
}
function ByteStringByteIterator() {
var _str ='';
var _idx = 0;
var _iend = 0;
var _size = 0;
this.hasNext = function() { return _idx < _iend; };
this.next = function() { return _str.charCodeAt(_idx++) & 0xFF; };
this.size = function() { return _size; };
this.init = function(str, offset, len) {
_str = str; _idx = offset; _iend = offset + len; _size = len;
};
}
function NumberArrayByteIterator() { // 32bits number (Big Endian)
var _arr = [];
var _idx = 0;
var _iend = 0;
var _size = 0;
var _mask = 0;
var _shift = 0;
var _i;
this.hasNext = function() { return _idx < _iend; };
this.next = function() {
_i = (_mask - (_idx & _mask)) << 3;
return (_arr[(_idx++) >> _shift] >> _i) & 0xFF;
};
this.size = function() { return _size; }
this.init = function(data, offset, len, bits) {
// bits: Integer bits (16 or 32) (javascript's shift operators support only 32bits number)
_mask = (bits >> 3) - 1; _shift = bits >> 4;
_arr = data; _idx = offset << _shift; _iend = (offset + len) << _shift; _size = len * (bits >> 3);
};
}
function NumberArrayByteIteratorLE() { // 32bits number (Little Endian)
var _arr = [];
var _idx = 0;
var _iend = 0;
var _size = 0;
var _mask = 0;
var _shift = 0;
var _i;
this.hasNext = function() { return _idx < _iend; };
this.next = function() {
_i = (_idx & _mask) << 3;
return (_arr[(_idx++) >> _shift] >> _i) & 0xFF;
};
this.size = function() { return _size; }
this.init = function(data, offset, len, bits) {
// bits: Integer bits (16 or 32) (javascript's shift operators support only 32bits number)
_mask = (bits >> 3) - 1; _shift = bits >> 4;
_arr = data; _idx = offset << _shift; _iend = (offset + len) << _shift; _size = len * (bits >> 3);
};
}
var Int64 = new IntClass(8);
var Packer = Int64.getPacker();
this.getInt64 = function() { return Int64; };
var _init_hash_value = [
Int64.parse('6a09e667f3bcc908'),
Int64.parse('bb67ae8584caa73b'),
Int64.parse('3c6ef372fe94f82b'),
Int64.parse('a54ff53a5f1d36f1'),
Int64.parse('510e527fade682d1'),
Int64.parse('9b05688c2b3e6c1f'),
Int64.parse('1f83d9abfb41bd6b'),
Int64.parse('5be0cd19137e2179')
];
var _K = [
Int64.parse('428a2f98d728ae22'), Int64.parse('7137449123ef65cd'), Int64.parse('b5c0fbcfec4d3b2f'), Int64.parse('e9b5dba58189dbbc'),
Int64.parse('3956c25bf348b538'), Int64.parse('59f111f1b605d019'), Int64.parse('923f82a4af194f9b'), Int64.parse('ab1c5ed5da6d8118'),
Int64.parse('d807aa98a3030242'), Int64.parse('12835b0145706fbe'), Int64.parse('243185be4ee4b28c'), Int64.parse('550c7dc3d5ffb4e2'),
Int64.parse('72be5d74f27b896f'), Int64.parse('80deb1fe3b1696b1'), Int64.parse('9bdc06a725c71235'), Int64.parse('c19bf174cf692694'),
Int64.parse('e49b69c19ef14ad2'), Int64.parse('efbe4786384f25e3'), Int64.parse('0fc19dc68b8cd5b5'), Int64.parse('240ca1cc77ac9c65'),
Int64.parse('2de92c6f592b0275'), Int64.parse('4a7484aa6ea6e483'), Int64.parse('5cb0a9dcbd41fbd4'), Int64.parse('76f988da831153b5'),
Int64.parse('983e5152ee66dfab'), Int64.parse('a831c66d2db43210'), Int64.parse('b00327c898fb213f'), Int64.parse('bf597fc7beef0ee4'),
Int64.parse('c6e00bf33da88fc2'), Int64.parse('d5a79147930aa725'), Int64.parse('06ca6351e003826f'), Int64.parse('142929670a0e6e70'),
Int64.parse('27b70a8546d22ffc'), Int64.parse('2e1b21385c26c926'), Int64.parse('4d2c6dfc5ac42aed'), Int64.parse('53380d139d95b3df'),
Int64.parse('650a73548baf63de'), Int64.parse('766a0abb3c77b2a8'), Int64.parse('81c2c92e47edaee6'), Int64.parse('92722c851482353b'),
Int64.parse('a2bfe8a14cf10364'), Int64.parse('a81a664bbc423001'), Int64.parse('c24b8b70d0f89791'), Int64.parse('c76c51a30654be30'),
Int64.parse('d192e819d6ef5218'), Int64.parse('d69906245565a910'), Int64.parse('f40e35855771202a'), Int64.parse('106aa07032bbd1b8'),
Int64.parse('19a4c116b8d2d0c8'), Int64.parse('1e376c085141ab53'), Int64.parse('2748774cdf8eeb99'), Int64.parse('34b0bcb5e19b48a8'),
Int64.parse('391c0cb3c5c95a63'), Int64.parse('4ed8aa4ae3418acb'), Int64.parse('5b9cca4f7763e373'), Int64.parse('682e6ff3d6b2b8a3'),
Int64.parse('748f82ee5defb2fc'), Int64.parse('78a5636f43172f60'), Int64.parse('84c87814a1f0ab72'), Int64.parse('8cc702081a6439ec'),
Int64.parse('90befffa23631e28'), Int64.parse('a4506cebde82bde9'), Int64.parse('bef9a3f7b2c67915'), Int64.parse('c67178f2e372532b'),
Int64.parse('ca273eceea26619c'), Int64.parse('d186b8c721c0c207'), Int64.parse('eada7dd6cde0eb1e'), Int64.parse('f57d4f7fee6ed178'),
Int64.parse('06f067aa72176fba'), Int64.parse('0a637dc5a2c898a6'), Int64.parse('113f9804bef90dae'), Int64.parse('1b710b35131c471b'),
Int64.parse('28db77f523047d84'), Int64.parse('32caab7b40c72493'), Int64.parse('3c9ebe0a15c9bebc'), Int64.parse('431d67c49c100d4c'),
Int64.parse('4cc5d4becb3e42b6'), Int64.parse('597f299cfc657e2a'), Int64.parse('5fcb6fab3ad6faec'), Int64.parse('6c44198c4a475817')
];
/** ハッシュを初期化する
* @alias SHA512JS.init
* @param {Hash} x
*/
var __init_hash = function(x) {
var i, hash = x.hash;
for (i = 0; i < 8; i++) {
hash[i] = _init_hash_value[i]
}
x.size = 0;
Packer.init(x.packer);
};
var __Ch = function(x, y, z) {
return Int64.bwXor(
Int64.bwAnd(x, y),
Int64.bwAnd(Int64.bwNot(x), z)
);
};
var __Maj = function(x, y, z) {
return Int64.bwXor3(
Int64.bwAnd(x, y),
Int64.bwAnd(x, z),
Int64.bwAnd(y, z)
);
};
var __ucSigma0 = function(x) {
return Int64.bwXor3(
Int64.rotateR(x, 28),
Int64.rotateR(x, 34),
Int64.rotateR(x, 39)
);
};
var __ucSigma1 = function(x) {
return Int64.bwXor3(
Int64.rotateR(x, 14),
Int64.rotateR(x, 18),
Int64.rotateR(x, 41)
);
};
var __lcSigma0 = function(x) {
return Int64.bwXor3(
Int64.rotateR(x, 1),
Int64.rotateR(x, 8),
Int64.shiftR(x, 7)
);
};
var __lcSigma1 = function(x) {
return Int64.bwXor3(
Int64.rotateR(x, 19),
Int64.rotateR(x, 61),
Int64.shiftR(x, 6)
);
};
/** 初期化済みの新しいハッシュを生成する
* @function
* @alias SHA512JS.create
* @returns {Hash}
*/
this.create = function() {
/** ハッシュの計算を保持
* @class Hash
* @hideconstructor
*/
var x = {
"hash" : new Array(8),
"w" : new Array(80),
"packer": Packer.create(),
"b_iter": EMPTYITER,
"s_iter": EMPTYITER,
"n_iter": EMPTYITER,
"l_iter": EMPTYITER
};
__init_hash(x);
return x;
};
this.init = __init_hash;
var __calcWj = function(x) {
var j, w = x.w;
for (j = 16; j < 80; j++) {
w[j] = Int64.add4(__lcSigma1(w[j - 2]), w[j - 7], __lcSigma0(w[j - 15]), w[j - 16]);
// console.log('w[' + j + '] = ' + Int64.toHex(w[j]) + ', ' + Int64.toHex(x.w[j]));
}
return w;
};
var __compress = function(x) {
// console.log('compress!');
var w = __calcWj(x);
var a = x.hash[0];
var b = x.hash[1];
var c = x.hash[2];
var d = x.hash[3];
var e = x.hash[4];
var f = x.hash[5];
var g = x.hash[6];
var h = x.hash[7];
var j, t1, t2;
// console.log('init');
// console.log(Int64.toHex(a) + ' ' + Int64.toHex(b) + ' ' + Int64.toHex(c) + ' ' + Int64.toHex(d));
// console.log(Int64.toHex(e) + ' ' + Int64.toHex(f) + ' ' + Int64.toHex(g) + ' ' + Int64.toHex(h));
for (j = 0; j < 80; j++) {
t1 = Int64.add5(h, __ucSigma1(e), __Ch(e, f, g), _K[j], w[j]);
t2 = Int64.add(__ucSigma0(a), __Maj(a, b, c));
h = g;
g = f;
f = e;
e = Int64.add(d, t1);
d = c;
c = b;
b = a;
a = Int64.add(t1, t2);
// console.log('t = ' + j);
// console.log(Int64.toHex(a) + ' ' + Int64.toHex(b) + ' ' + Int64.toHex(c) + ' ' + Int64.toHex(d));
// console.log(Int64.toHex(e) + ' ' + Int64.toHex(f) + ' ' + Int64.toHex(g) + ' ' + Int64.toHex(h));
}
x.hash[0] = Int64.add(x.hash[0], a);
x.hash[1] = Int64.add(x.hash[1], b);
x.hash[2] = Int64.add(x.hash[2], c);
x.hash[3] = Int64.add(x.hash[3], d);
x.hash[4] = Int64.add(x.hash[4], e);
x.hash[5] = Int64.add(x.hash[5], f);
x.hash[6] = Int64.add(x.hash[6], g);
x.hash[7] = Int64.add(x.hash[7], h);
};
var __tryCompress = function(p, x) {
if ((p & 63) !== 0) { return false; }
x.w[(p >> 6) - 1] = Packer.getCopy(x.packer);
// console.log('w[' + ((p >> 6) - 1) + '] = ' + Int64.toHex(x.w[(p >> 6) - 1]));
Packer.init(x.packer);
if (p !== 1024) { return false; }
__compress(x);
return true;
};
var __update = function(x, iter) {
var p = x.size & 1023, v;
// var b = 0;
// console.log('iter.size: ' + iter.size());
while (iter.hasNext()) {
v = iter.next();
// console.log('__update: ' + (b++) + ', ' + v.toString(16));
Packer.push(x.packer, v);
p += 8;
if (__tryCompress(p, x)) {
p = 0;
}
}
x.size += iter.size() << 3; // bytes to bits
};
/** ハッシュを8bit整数(0-255)の配列データで更新する
* @alias SHA512JS.updateByByteArray
* @param {Hash} x
* @param {number[]} data
* @param {number} offset
* @param {number} len
*/
this.updateByByteArray = function(x, data, offset, len) {
if (x.b_iter === EMPTYITER) {
x.b_iter = new ByteArrayByteIterator();
}
x.b_iter.init(data, offset, len)
__update(x, x.b_iter);
};
/** ハッシュを文字列の文字コード(下位8bit)で更新する
* @alias SHA512JS.updateByByteString
* @param {Hash} x
* @param {string} s
* @param {number} offset
* @param {number} len
*/
this.updateByByteString = function(x, s, offset, len) {
if (x.s_iter === EMPTYITER) {
x.s_iter = new ByteStringByteIterator();
}
x.s_iter.init(s, offset, len);
__update(x, x.s_iter);
};
/** ハッシュを整数の配列で更新する
* 整数の各数値は上位バイトから下位バイトの順に消費される
* @alias SHA512JS.updateByNumberArray
* @param {Hash} x
* @param {number[]} data
* @param {number} offset
* @param {number} len
* @param {number} bits 整数のbitサイズ(16か32を指定する)
*/
this.updateByNumberArray = function(x, data, offset, len, bits) {
if (x.n_iter === EMPTYITER) {
x.n_iter = new NumberArrayByteIterator();
}
x.n_iter.init(data, offset, len, bits);
__update(x, x.n_iter);
};
/** ハッシュを整数の配列で更新する
* 整数の各数値は下位バイトから上位バイトの順に消費される
* @alias SHA512JS.updateByNumberArrayLE
* @param {Hash} x
* @param {number[]} data
* @param {number} offset
* @param {number} len
* @param {number} bits 整数のbitサイズ(16か32を指定する)
*/
this.updateByNumberArrayLE = function(x, data, offset, len, bits) {
if (x.l_iter === EMPTYITER) {
x.l_iter = new NumberArrayByteIteratorLE();
}
x.l_iter.init(data, offset, len, bits);
__update(x, x.l_iter);
};
/** ハッシュの計算を完了させる
* @alias SHA512JS.finish
* @param {Hash} x
*/
this.finish = function(x) {
var p = x.size & 1023;
Packer.push(x.packer, 0x80); // end of message
p += 8;
if (__tryCompress(p, x)) {
p = 0;
}
if (p >= 896) {
// need to compress this block
do {
Packer.push(x.packer, 0x00);
p += 8;
} while (__tryCompress(p, x) === false);
p = 0;
}
while (p < 896) {
Packer.push(x.packer, 0x00);
p += 8;
__tryCompress(p, x);
}
// 128bits (message bits)
x.w[14] = Int64.ZERO; // I think ... maybe, allmost, message bits < 2^64
x.w[15] = Int64.parse(x.size.toString(16));
// console.log('w[14] = ' + Int64.toHex(x.w[14]));
// console.log('w[15] = ' + Int64.toHex(x.w[15]));
// final compress
__compress(x);
};
/** 計算済みのハッシュを整数(0-255)の配列へ書き出す
* @alias SHA512JS.getHash
* @param {Hash} x
* @param {number[]} dest
* @param {number} offset
*/
this.getHash = function(x, dest, offset) {
// ByteArray
var i;
for (i = 0; i < 8; i++) {
Int64.copyBytes(x.hash[i], dest, offset + i * 8);
}
};
/** 計算済みのハッシュを整数の配列へ書き出す
* 上位バイトから下位バイトの順に整数に書き込まれる
* @alias SHA512JS.getHashToNumberArray
* @param {Hash} x
* @param {number} bits 整数のbitサイズ(16か32を指定する)
* @param {number[]} dest
* @param {number} offset
*/
this.getHashToNumberArray = function(x, bits, dest, offset) { // pack Big Endian
// bits: 16 or 32
var i, b, j, p = offset, s = bits >> 2;
for (i = 0; i < 8; i++) {
b = Int64.toHex(x.hash[i]);
for (j = 0; j < 16; j += s) {
dest[p] = parseInt(b.substring(j, j + s), 16);
p++;
}
}
};
/** 計算済みのハッシュを整数の配列へ書き出す
* 下位から上位バイトの順に整数に書き込まれる
* @alias SHA512JS.getHashToNumberArrayLE
* @param {Hash} x
* @param {number} bits 整数のbitサイズ(16か32を指定する)
* @param {number[]} dest
* @param {number} offset
*/
this.getHashToNumberArrayLE = function(x, bits, dest, offset) { // pack Little Endian
// bits: 16 or 32
var i, b, j, p = offset, s = bits >> 2, k, u;
for (i = 0; i < 8; i++) {
b = Int64.toHex(x.hash[i]);
for (j = 0; j < 16; j += s) {
u = '';
for (k = 0; k < s; k += 2) {
u = b.substring(j + k, j + k + 2) + u;
}
dest[p] = parseInt(u, 16);
p++;
}
}
};
/** 計算済みのハッシュを16進表記した文字列に変換する
* @alias SHA512JS.toHexString
* @param {Hash} x
* @returns {string}
*/
this.toHexString = function(x) {
var i, s = '';
for (i = 0; i < 8; i++) {
s += Int64.toHex(x.hash[i]);
}
return s;
};
/** 計算済みのハッシュの各バイト値を1文字の文字コードとして文字列に変換する
* @alias SHA512JS.toByteString
* @param {Hash} x
* @returns {string}
*/
this.toByteString = function(x) {
var b = new Array(8);
var i, j, s = '';
for (i = 0; i < 8; i++) {
Int64.copyBytes(x.hash[i], b, 0);
for (j = 0; j < 8; j++) {
s += String.fromCharCode(b[j]);
}
}
return s;
};
// Tester
// =============================================
if (typeof IntClassTester !== 'undefined') {
IntClassTester.bind({
"IntClass": IntClass
});
}
if (typeof ByteIteratorTester !== 'undefined') {
ByteIteratorTester.bind({
"EMPTYITER": EMPTYITER,
"ByteArrayByteIterator": ByteArrayByteIterator,
"ByteStringByteIterator": ByteStringByteIterator,
"NumberArrayByteIterator": NumberArrayByteIterator,
"NumberArrayByteIteratorLE": NumberArrayByteIteratorLE
});
}
if (typeof SHA512JSTester !== 'undefined') {
SHA512JSTester.bind({
"Int64": Int64,
"_init_hash_value": _init_hash_value
});
}
}