goldrat/vendor/simplito/elliptic-php/lib/HmacDRBG.php
2025-10-09 17:41:57 +00:00

136 lines
3.7 KiB
PHP

<?php
namespace Elliptic;
class HmacDRBG
{
private $hash;
private $predResist;
private $outLen;
private $minEntropy;
private $reseed;
private $reseedInterval;
private $K;
private $V;
function __construct($options)
{
Utils::optionAssert($options, "predResist");
Utils::optionAssert($options, "hash", null, true);
Utils::optionAssert($options["hash"], "outSize", null, true);
Utils::optionAssert($options["hash"], "hmacStrength", null, true);
Utils::optionAssert($options["hash"], "algo", null, true);
Utils::optionAssert($options, "minEntropy");
Utils::optionAssert($options, "entropy", null, true);
Utils::optionAssert($options, "entropyEnc");
Utils::optionAssert($options, "nonce", "");
Utils::optionAssert($options, "nonceEnc");
Utils::optionAssert($options, "pers", "");
Utils::optionAssert($options, "persEnc");
$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;
$entropy = Utils::toBin($options["entropy"], $options["entropyEnc"]);
$nonce = Utils::toBin($options["nonce"], $options["nonceEnc"]);
$pers = Utils::toBin($options["pers"], $options["persEnc"]);
if (Utils::$ASSERT_ENABLED) {
assert(strlen($entropy) >= ($this->minEntropy / 8));
}
$this->_init($entropy, $nonce, $pers);
}
private function _init($entropy, $nonce, $pers)
{
$seed = $entropy . $nonce . $pers;
$this->K = str_repeat(chr(0x00), $this->outLen / 8);
$this->V = str_repeat(chr(0x01), $this->outLen / 8);
$this->_update($seed);
$this->reseed = 1;
$this->reseedInterval = 0x1000000000000; // 2^48
}
private function _hmac()
{
return hash_init($this->hash["algo"], HASH_HMAC, $this->K);
}
private function _update($seed = false)
{
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
hash_update($kmac, chr(0x00));
if( $seed )
hash_update($kmac, $seed);
$this->K = hash_final($kmac, true);
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
$this->V = hash_final($kmac, true);
if(!$seed)
return;
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
hash_update($kmac, chr(0x01));
hash_update($kmac, $seed);
$this->K = hash_final($kmac, true);
$kmac = $this->_hmac();
hash_update($kmac, $this->V);
$this->V = hash_final($kmac, true);
}
// TODO: reseed()
public function generate($len, $enc = null, $add = null, $addEnc = null)
{
if ($this->reseed > $this->reseedInterval)
throw new \Exception("Reseed is required");
// Optional encoding
if( !is_string($enc) )
{
$addEnc = $enc;
$add = $enc;
$enc = null;
}
// Optional additional data
if( $add != null ) {
$add = Utils::toBin($add, $addEnc);
$this->_update($add);
}
$temp = "";
while( strlen($temp) < $len )
{
$hmac = $this->_hmac();
hash_update($hmac, $this->V);
$this->V = hash_final($hmac, true);
$temp .= $this->V;
}
$res = substr($temp, 0, $len);
$this->_update($add);
$this->reseed++;
return Utils::encode(Utils::toArray($res), $enc);
}
}
?>