fcoin/lib/crypto/hmac-drbg.js
2017-07-31 18:21:02 -07:00

121 lines
2.6 KiB
JavaScript

/*!
* hmac-drbg.js - hmac-drbg implementation for bcoin
* Copyright (c) 2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
* Parts of this software based on hmac-drbg.
*/
'use strict';
const assert = require('assert');
const digest = require('./digest');
/*
* Constants
*/
const HASH_ALG = 'sha256';
const HASH_SIZE = 32;
const RESEED_INTERVAL = 0x1000000000000;
const POOL33 = Buffer.allocUnsafe(HASH_SIZE + 1);
const POOL112 = Buffer.allocUnsafe(HASH_SIZE * 2 + 48);
const POOL145 = Buffer.allocUnsafe(POOL33.length + POOL112.length);
/**
* HmacDRBG
* @constructor
*/
function HmacDRBG(entropy, nonce, pers) {
if (!(this instanceof HmacDRBG))
return new HmacDRBG(entropy, nonce, pers);
this.K = Buffer.allocUnsafe(HASH_SIZE);
this.V = Buffer.allocUnsafe(HASH_SIZE);
this.rounds = 0;
this.init(entropy, nonce, pers);
}
HmacDRBG.prototype.init = function init(entropy, nonce, pers) {
for (let i = 0; i < this.V.length; i++) {
this.K[i] = 0x00;
this.V[i] = 0x01;
}
this.reseed(entropy, nonce, pers);
};
HmacDRBG.prototype.reseed = function reseed(entropy, nonce, pers) {
const seed = POOL112;
assert(Buffer.isBuffer(entropy));
assert(Buffer.isBuffer(nonce));
assert(Buffer.isBuffer(pers));
assert(entropy.length === HASH_SIZE);
assert(nonce.length === HASH_SIZE);
assert(pers.length === 48);
entropy.copy(seed, 0);
nonce.copy(seed, HASH_SIZE);
pers.copy(seed, HASH_SIZE * 2);
this.update(seed);
this.rounds = 1;
};
HmacDRBG.prototype.iterate = function iterate() {
const data = POOL33;
this.V.copy(data, 0);
data[HASH_SIZE] = 0x00;
this.K = digest.hmac(HASH_ALG, data, this.K);
this.V = digest.hmac(HASH_ALG, this.V, this.K);
};
HmacDRBG.prototype.update = function update(seed) {
const data = POOL145;
assert(Buffer.isBuffer(seed));
assert(seed.length === HASH_SIZE * 2 + 48);
this.V.copy(data, 0);
data[HASH_SIZE] = 0x00;
seed.copy(data, HASH_SIZE + 1);
this.K = digest.hmac(HASH_ALG, data, this.K);
this.V = digest.hmac(HASH_ALG, this.V, this.K);
data[HASH_SIZE] = 0x01;
this.K = digest.hmac(HASH_ALG, data, this.K);
this.V = digest.hmac(HASH_ALG, this.V, this.K);
};
HmacDRBG.prototype.generate = function generate(len) {
if (this.rounds > RESEED_INTERVAL)
throw new Error('Reseed is required.');
const data = Buffer.allocUnsafe(len);
let pos = 0;
while (pos < len) {
this.V = digest.hmac(HASH_ALG, this.V, this.K);
this.V.copy(data, pos);
pos += HASH_SIZE;
}
this.iterate();
this.rounds++;
return data;
};
/*
* Expose
*/
module.exports = HmacDRBG;