/*! * 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;