114 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			114 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var hash = require('hash.js');
 | |
| var utils = require('minimalistic-crypto-utils');
 | |
| var assert = require('minimalistic-assert');
 | |
| 
 | |
| function HmacDRBG(options) {
 | |
|   if (!(this instanceof HmacDRBG))
 | |
|     return new HmacDRBG(options);
 | |
|   this.hash = options.hash;
 | |
|   this.predResist = !!options.predResist;
 | |
| 
 | |
|   this.outLen = this.hash.outSize;
 | |
|   this.minEntropy = options.minEntropy || this.hash.hmacStrength;
 | |
| 
 | |
|   this._reseed = null;
 | |
|   this.reseedInterval = null;
 | |
|   this.K = null;
 | |
|   this.V = null;
 | |
| 
 | |
|   var entropy = utils.toArray(options.entropy, options.entropyEnc || 'hex');
 | |
|   var nonce = utils.toArray(options.nonce, options.nonceEnc || 'hex');
 | |
|   var pers = utils.toArray(options.pers, options.persEnc || 'hex');
 | |
|   assert(entropy.length >= (this.minEntropy / 8),
 | |
|          'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');
 | |
|   this._init(entropy, nonce, pers);
 | |
| }
 | |
| module.exports = HmacDRBG;
 | |
| 
 | |
| HmacDRBG.prototype._init = function init(entropy, nonce, pers) {
 | |
|   var seed = entropy.concat(nonce).concat(pers);
 | |
| 
 | |
|   this.K = new Array(this.outLen / 8);
 | |
|   this.V = new Array(this.outLen / 8);
 | |
|   for (var i = 0; i < this.V.length; i++) {
 | |
|     this.K[i] = 0x00;
 | |
|     this.V[i] = 0x01;
 | |
|   }
 | |
| 
 | |
|   this._update(seed);
 | |
|   this._reseed = 1;
 | |
|   this.reseedInterval = 0x1000000000000;  // 2^48
 | |
| };
 | |
| 
 | |
| HmacDRBG.prototype._hmac = function hmac() {
 | |
|   return new hash.hmac(this.hash, this.K);
 | |
| };
 | |
| 
 | |
| HmacDRBG.prototype._update = function update(seed) {
 | |
|   var kmac = this._hmac()
 | |
|                  .update(this.V)
 | |
|                  .update([ 0x00 ]);
 | |
|   if (seed)
 | |
|     kmac = kmac.update(seed);
 | |
|   this.K = kmac.digest();
 | |
|   this.V = this._hmac().update(this.V).digest();
 | |
|   if (!seed)
 | |
|     return;
 | |
| 
 | |
|   this.K = this._hmac()
 | |
|                .update(this.V)
 | |
|                .update([ 0x01 ])
 | |
|                .update(seed)
 | |
|                .digest();
 | |
|   this.V = this._hmac().update(this.V).digest();
 | |
| };
 | |
| 
 | |
| HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) {
 | |
|   // Optional entropy enc
 | |
|   if (typeof entropyEnc !== 'string') {
 | |
|     addEnc = add;
 | |
|     add = entropyEnc;
 | |
|     entropyEnc = null;
 | |
|   }
 | |
| 
 | |
|   entropy = utils.toArray(entropy, entropyEnc);
 | |
|   add = utils.toArray(add, addEnc);
 | |
| 
 | |
|   assert(entropy.length >= (this.minEntropy / 8),
 | |
|          'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');
 | |
| 
 | |
|   this._update(entropy.concat(add || []));
 | |
|   this._reseed = 1;
 | |
| };
 | |
| 
 | |
| HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) {
 | |
|   if (this._reseed > this.reseedInterval)
 | |
|     throw new Error('Reseed is required');
 | |
| 
 | |
|   // Optional encoding
 | |
|   if (typeof enc !== 'string') {
 | |
|     addEnc = add;
 | |
|     add = enc;
 | |
|     enc = null;
 | |
|   }
 | |
| 
 | |
|   // Optional additional data
 | |
|   if (add) {
 | |
|     add = utils.toArray(add, addEnc || 'hex');
 | |
|     this._update(add);
 | |
|   }
 | |
| 
 | |
|   var temp = [];
 | |
|   while (temp.length < len) {
 | |
|     this.V = this._hmac().update(this.V).digest();
 | |
|     temp = temp.concat(this.V);
 | |
|   }
 | |
| 
 | |
|   var res = temp.slice(0, len);
 | |
|   this._update(add);
 | |
|   this._reseed++;
 | |
|   return utils.encode(res, enc);
 | |
| };
 | 
