HmacDRBG.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. namespace Elliptic;
  3. class HmacDRBG
  4. {
  5. private $hash;
  6. private $predResist;
  7. private $outLen;
  8. private $minEntropy;
  9. private $reseed;
  10. private $reseedInterval;
  11. private $K;
  12. private $V;
  13. function __construct($options)
  14. {
  15. Utils::optionAssert($options, "predResist");
  16. Utils::optionAssert($options, "hash", null, true);
  17. Utils::optionAssert($options["hash"], "outSize", null, true);
  18. Utils::optionAssert($options["hash"], "hmacStrength", null, true);
  19. Utils::optionAssert($options["hash"], "algo", null, true);
  20. Utils::optionAssert($options, "minEntropy");
  21. Utils::optionAssert($options, "entropy", null, true);
  22. Utils::optionAssert($options, "entropyEnc");
  23. Utils::optionAssert($options, "nonce", "");
  24. Utils::optionAssert($options, "nonceEnc");
  25. Utils::optionAssert($options, "pers", "");
  26. Utils::optionAssert($options, "persEnc");
  27. $this->hash = $options["hash"];
  28. $this->predResist = $options["predResist"];
  29. $this->outLen = $this->hash["outSize"];
  30. $this->minEntropy = $options["minEntropy"] ?: $this->hash["hmacStrength"];
  31. $this->reseed = null;
  32. $this->reseedInterval = null;
  33. $this->K = null;
  34. $this->V = null;
  35. $entropy = Utils::toBin($options["entropy"], $options["entropyEnc"]);
  36. $nonce = Utils::toBin($options["nonce"], $options["nonceEnc"]);
  37. $pers = Utils::toBin($options["pers"], $options["persEnc"]);
  38. if (assert_options(ASSERT_ACTIVE)) {
  39. assert(strlen($entropy) >= ($this->minEntropy / 8));
  40. }
  41. $this->_init($entropy, $nonce, $pers);
  42. }
  43. private function _init($entropy, $nonce, $pers)
  44. {
  45. $seed = $entropy . $nonce . $pers;
  46. $this->K = str_repeat(chr(0x00), $this->outLen / 8);
  47. $this->V = str_repeat(chr(0x01), $this->outLen / 8);
  48. $this->_update($seed);
  49. $this->reseed = 1;
  50. $this->reseedInterval = 0x1000000000000; // 2^48
  51. }
  52. private function _hmac()
  53. {
  54. return hash_init($this->hash["algo"], HASH_HMAC, $this->K);
  55. }
  56. private function _update($seed = false)
  57. {
  58. $kmac = $this->_hmac();
  59. hash_update($kmac, $this->V);
  60. hash_update($kmac, chr(0x00));
  61. if( $seed )
  62. hash_update($kmac, $seed);
  63. $this->K = hash_final($kmac, true);
  64. $kmac = $this->_hmac();
  65. hash_update($kmac, $this->V);
  66. $this->V = hash_final($kmac, true);
  67. if(!$seed)
  68. return;
  69. $kmac = $this->_hmac();
  70. hash_update($kmac, $this->V);
  71. hash_update($kmac, chr(0x01));
  72. hash_update($kmac, $seed);
  73. $this->K = hash_final($kmac, true);
  74. $kmac = $this->_hmac();
  75. hash_update($kmac, $this->V);
  76. $this->V = hash_final($kmac, true);
  77. }
  78. // TODO: reseed()
  79. public function generate($len, $enc = null, $add = null, $addEnc = null)
  80. {
  81. if ($this->reseed > $this->reseedInterval)
  82. throw new \Exception("Reseed is required");
  83. // Optional encoding
  84. if( !is_string($enc) )
  85. {
  86. $addEnc = $enc;
  87. $add = $enc;
  88. $enc = null;
  89. }
  90. // Optional additional data
  91. if( $add != null ) {
  92. $add = Utils::toBin($add, $addEnc);
  93. $this->_update($add);
  94. }
  95. $temp = "";
  96. while( strlen($temp) < $len )
  97. {
  98. $hmac = $this->_hmac();
  99. hash_update($hmac, $this->V);
  100. $this->V = hash_final($hmac, true);
  101. $temp .= $this->V;
  102. }
  103. $res = substr($temp, 0, $len);
  104. $this->_update($add);
  105. $this->reseed++;
  106. return Utils::encode(Utils::toArray($res), $enc);
  107. }
  108. }
  109. ?>