fcoin/lib/crypto/sha256.js
2017-02-03 22:47:26 -08:00

406 lines
7.4 KiB
JavaScript

/*!
* sha256.js - SHA256 implementation for bcoin
* Copyright (c) 2016-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
* Parts of this software based on hash.js.
*/
'use strict';
/**
* @module crypto/sha256
* @ignore
*/
/*
* Constants
*/
var DESC = new Buffer(8);
var BUFFER64 = new Buffer(64);
var K, PADDING;
var ctx, mctx;
PADDING = new Buffer(64);
PADDING.fill(0);
PADDING[0] = 0x80;
K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
/**
* SHA256
* @alias module:crypto/sha256.SHA256
* @constructor
* @property {Number[]} s
* @property {Number[]} w
* @property {Buffer} block
* @property {Number} bytes
*/
function SHA256() {
if (!(this instanceof SHA256))
return new SHA256();
this.s = new Array(8);
this.w = new Array(64);
this.block = new Buffer(64);
this.bytes = 0;
}
/**
* Initialize SHA256 context.
*/
SHA256.prototype.init = function init() {
this.s[0] = 0x6a09e667;
this.s[1] = 0xbb67ae85;
this.s[2] = 0x3c6ef372;
this.s[3] = 0xa54ff53a;
this.s[4] = 0x510e527f;
this.s[5] = 0x9b05688c;
this.s[6] = 0x1f83d9ab;
this.s[7] = 0x5be0cd19;
this.bytes = 0;
};
/**
* Update SHA256 context.
* @param {Buffer} data
*/
SHA256.prototype.update = function update(data) {
return this._update(data, data.length);
};
/**
* Finalize SHA256 context.
* @returns {Buffer}
*/
SHA256.prototype.finish = function finish() {
return this._finish(new Buffer(32));
};
/**
* Update SHA256 context.
* @private
* @param {Buffer} data
* @param {Number} len
*/
SHA256.prototype._update = function update(data, len) {
var size = this.bytes & 0x3f;
var pos = 0;
var i, want;
this.bytes += len;
if (size > 0) {
want = 64 - size;
if (want > len)
want = len;
for (i = 0; i < want; i++)
this.block[size + i] = data[i];
size += want;
len -= want;
pos += want;
if (size < 64)
return;
this.transform(this.block, 0);
}
while (len >= 64) {
this.transform(data, pos);
pos += 64;
len -= 64;
}
for (i = 0; i < len; i++)
this.block[i] = data[pos + i];
};
/**
* Finalize SHA256 context.
* @private
* @param {Buffer} out
* @returns {Buffer}
*/
SHA256.prototype._finish = function _finish(out) {
var i;
writeU32(DESC, this.bytes >>> 29, 0);
writeU32(DESC, this.bytes << 3, 4);
this._update(PADDING, 1 + ((119 - (this.bytes % 64)) % 64));
this._update(DESC, 8);
for (i = 0; i < 8; i++) {
writeU32(out, this.s[i], i * 4);
this.s[i] = 0;
}
return out;
};
/**
* Transform SHA256 block.
* @param {Buffer} chunk
* @param {Number} pos
*/
SHA256.prototype.transform = function transform(chunk, pos) {
var a = this.s[0];
var b = this.s[1];
var c = this.s[2];
var d = this.s[3];
var e = this.s[4];
var f = this.s[5];
var g = this.s[6];
var h = this.s[7];
var w = this.w;
var i, t1, t2;
for (i = 0; i < 16; i++)
w[i] = readU32(chunk, pos + i * 4);
for (; i < 64; i++)
w[i] = sigma1(w[i - 2]) + w[i - 7] + sigma0(w[i - 15]) + w[i - 16];
for (i = 0; i < 64; i++) {
t1 = h + Sigma1(e);
t1 += Ch(e, f, g);
t1 += K[i] + w[i];
t2 = Sigma0(a);
t2 += Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
this.s[0] += a;
this.s[1] += b;
this.s[2] += c;
this.s[3] += d;
this.s[4] += e;
this.s[5] += f;
this.s[6] += g;
this.s[7] += h;
this.s[0] >>>= 0;
this.s[1] >>>= 0;
this.s[2] >>>= 0;
this.s[3] >>>= 0;
this.s[4] >>>= 0;
this.s[5] >>>= 0;
this.s[6] >>>= 0;
this.s[7] >>>= 0;
};
/**
* SHA256Hmac
* @alias module:crypto/sha256.SHA256Hmac
* @constructor
* @property {SHA256} inner
* @property {SHA256} outer
*/
function SHA256Hmac() {
if (!(this instanceof SHA256Hmac))
return new SHA256Hmac();
this.inner = new SHA256();
this.outer = new SHA256();
}
/**
* Initialize HMAC context.
* @param {Buffer} data
*/
SHA256Hmac.prototype.init = function init(data) {
var key = BUFFER64;
var i;
if (data.length > 64) {
this.inner.init();
this.inner.update(data);
this.inner._finish(key);
key.fill(0, 32, 64);
} else {
data.copy(key, 0);
key.fill(0, data.length, 64);
}
for (i = 0; i < key.length; i++)
key[i] ^= 0x36;
this.inner.init();
this.inner.update(key);
for (i = 0; i < key.length; i++)
key[i] ^= 0x6a;
this.outer.init();
this.outer.update(key);
};
/**
* Update HMAC context.
* @param {Buffer} data
*/
SHA256Hmac.prototype.update = function update(data) {
this.inner.update(data);
};
/**
* Finalize HMAC context.
* @returns {Buffer}
*/
SHA256Hmac.prototype.finish = function finish() {
this.outer.update(this.inner.finish());
return this.outer.finish();
};
/*
* Helpers
* @see https://github.com/bitcoin-core/secp256k1/blob/master/src/hash_impl.h
*/
function Sigma0(x) {
return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10);
}
function Sigma1(x) {
return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7);
}
function sigma0(x) {
return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3);
}
function sigma1(x) {
return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10);
}
function Ch(x, y, z) {
return z ^ (x & (y ^ z));
}
function Maj(x, y, z) {
return (x & y) | (z & (x | y));
}
function writeU32(buf, value, offset) {
buf[offset] = value >>> 24;
buf[offset + 1] = (value >> 16) & 0xff;
buf[offset + 2] = (value >> 8) & 0xff;
buf[offset + 3] = value & 0xff;
}
function readU32(buf, offset) {
return ((buf[offset] & 0xff) * 0x1000000)
+ ((buf[offset + 1] & 0xff) << 16)
| ((buf[offset + 2] & 0xff) << 8)
| (buf[offset + 3] & 0xff);
}
/*
* Context Helpers
*/
ctx = new SHA256();
mctx = new SHA256Hmac();
/**
* Hash buffer with sha256.
* @alias module:crypto/sha256.sha256
* @param {Buffer} data
* @returns {Buffer}
*/
function sha256(data) {
ctx.init();
ctx.update(data);
return ctx.finish();
}
/**
* Hash buffer with double sha256.
* @alias module:crypto/sha256.hash256
* @param {Buffer} data
* @returns {Buffer}
*/
function hash256(data) {
var out = new Buffer(32);
ctx.init();
ctx.update(data);
ctx._finish(out);
ctx.init();
ctx.update(out);
ctx._finish(out);
return out;
}
/**
* Create a sha256 HMAC from buffer and key.
* @alias module:crypto/sha256.hmac
* @param {Buffer} data
* @param {Buffer} key
* @returns {Buffer}
*/
function hmac(data, key) {
mctx.init(key);
mctx.update(data);
return mctx.finish();
}
/*
* Expose
*/
exports = SHA256;
exports.SHA256 = SHA256;
exports.SHA256Hmac = SHA256Hmac;
exports.digest = sha256;
exports.hmac = hmac;
exports.hash256 = hash256;
module.exports = exports;