You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
2.9 KiB

4 years ago
  1. 'use strict';
  2. var hash = require('hash.js');
  3. var utils = require('minimalistic-crypto-utils');
  4. var assert = require('minimalistic-assert');
  5. function HmacDRBG(options) {
  6. if (!(this instanceof HmacDRBG))
  7. return new HmacDRBG(options);
  8. this.hash = options.hash;
  9. this.predResist = !!options.predResist;
  10. this.outLen = this.hash.outSize;
  11. this.minEntropy = options.minEntropy || this.hash.hmacStrength;
  12. this._reseed = null;
  13. this.reseedInterval = null;
  14. this.K = null;
  15. this.V = null;
  16. var entropy = utils.toArray(options.entropy, options.entropyEnc || 'hex');
  17. var nonce = utils.toArray(options.nonce, options.nonceEnc || 'hex');
  18. var pers = utils.toArray(options.pers, options.persEnc || 'hex');
  19. assert(entropy.length >= (this.minEntropy / 8),
  20. 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');
  21. this._init(entropy, nonce, pers);
  22. }
  23. module.exports = HmacDRBG;
  24. HmacDRBG.prototype._init = function init(entropy, nonce, pers) {
  25. var seed = entropy.concat(nonce).concat(pers);
  26. this.K = new Array(this.outLen / 8);
  27. this.V = new Array(this.outLen / 8);
  28. for (var i = 0; i < this.V.length; i++) {
  29. this.K[i] = 0x00;
  30. this.V[i] = 0x01;
  31. }
  32. this._update(seed);
  33. this._reseed = 1;
  34. this.reseedInterval = 0x1000000000000; // 2^48
  35. };
  36. HmacDRBG.prototype._hmac = function hmac() {
  37. return new hash.hmac(this.hash, this.K);
  38. };
  39. HmacDRBG.prototype._update = function update(seed) {
  40. var kmac = this._hmac()
  41. .update(this.V)
  42. .update([ 0x00 ]);
  43. if (seed)
  44. kmac = kmac.update(seed);
  45. this.K = kmac.digest();
  46. this.V = this._hmac().update(this.V).digest();
  47. if (!seed)
  48. return;
  49. this.K = this._hmac()
  50. .update(this.V)
  51. .update([ 0x01 ])
  52. .update(seed)
  53. .digest();
  54. this.V = this._hmac().update(this.V).digest();
  55. };
  56. HmacDRBG.prototype.reseed = function reseed(entropy, entropyEnc, add, addEnc) {
  57. // Optional entropy enc
  58. if (typeof entropyEnc !== 'string') {
  59. addEnc = add;
  60. add = entropyEnc;
  61. entropyEnc = null;
  62. }
  63. entropy = utils.toArray(entropy, entropyEnc);
  64. add = utils.toArray(add, addEnc);
  65. assert(entropy.length >= (this.minEntropy / 8),
  66. 'Not enough entropy. Minimum is: ' + this.minEntropy + ' bits');
  67. this._update(entropy.concat(add || []));
  68. this._reseed = 1;
  69. };
  70. HmacDRBG.prototype.generate = function generate(len, enc, add, addEnc) {
  71. if (this._reseed > this.reseedInterval)
  72. throw new Error('Reseed is required');
  73. // Optional encoding
  74. if (typeof enc !== 'string') {
  75. addEnc = add;
  76. add = enc;
  77. enc = null;
  78. }
  79. // Optional additional data
  80. if (add) {
  81. add = utils.toArray(add, addEnc || 'hex');
  82. this._update(add);
  83. }
  84. var temp = [];
  85. while (temp.length < len) {
  86. this.V = this._hmac().update(this.V).digest();
  87. temp = temp.concat(this.V);
  88. }
  89. var res = temp.slice(0, len);
  90. this._update(add);
  91. this._reseed++;
  92. return utils.encode(res, enc);
  93. };