This commit is contained in:
2025-10-09 17:41:57 +00:00
commit 7a3b2960f8
146 changed files with 34948 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace Elliptic\EC;
use BN\BN;
class KeyPair
{
public $ec;
public $pub;
public $priv;
function __construct($ec, #[\SensitiveParameter]
$options)
{
$this->ec = $ec;
$this->priv = null;
$this->pub = null;
if( isset($options["priv"]) )
$this->_importPrivate($options["priv"], $options["privEnc"]);
if( isset($options["pub"]) )
$this->_importPublic($options["pub"], $options["pubEnc"]);
}
public static function fromPublic($ec, $pub, $enc)
{
if( $pub instanceof KeyPair )
return $pub;
return new KeyPair($ec, array(
"pub" => $pub,
"pubEnc" => $enc
));
}
public static function fromPrivate($ec, #[\SensitiveParameter]
$priv, $enc)
{
if( $priv instanceof KeyPair )
return $priv;
return new KeyPair($ec, array(
"priv" => $priv,
"privEnc" => $enc
));
}
public function validate()
{
$pub = $this->getPublic();
if( $pub->isInfinity() )
return array( "result" => false, "reason" => "Invalid public key" );
if( !$pub->validate() )
return array( "result" => false, "reason" => "Public key is not a point" );
if( !$pub->mul($this->ec->curve->n)->isInfinity() )
return array( "result" => false, "reason" => "Public key * N != O" );
return array( "result" => true, "reason" => null );
}
public function getPublic($compact = false, $enc = "")
{
//compact is optional argument
if( is_string($compact) )
{
$enc = $compact;
$compact = false;
}
if( $this->pub === null )
$this->pub = $this->ec->g->mul($this->priv);
if( !$enc )
return $this->pub;
return $this->pub->encode($enc, $compact);
}
public function getPrivate($enc = false)
{
if( $enc === "hex" )
return $this->priv->toString(16, 2);
return $this->priv;
}
private function _importPrivate(#[\SensitiveParameter]
$key, $enc)
{
$this->priv = new BN($key, (isset($enc) && $enc) ? $enc : 16);
// Ensure that the priv won't be bigger than n, otherwise we may fail
// in fixed multiplication method
$this->priv = $this->priv->umod($this->ec->curve->n);
}
private function _importPublic($key, $enc)
{
$x = $y = null;
if ( is_object($key) ) {
$x = $key->x;
$y = $key->y;
} elseif ( is_array($key) ) {
$x = isset($key["x"]) ? $key["x"] : null;
$y = isset($key["y"]) ? $key["y"] : null;
}
if( $x != null || $y != null )
$this->pub = $this->ec->curve->point($x, $y);
else
$this->pub = $this->ec->curve->decodePoint($key, $enc);
}
//ECDH
public function derive($pub) {
return $pub->mul($this->priv)->getX();
}
//ECDSA
public function sign($msg, $enc = false, $options = false) {
return $this->ec->sign($msg, $this, $enc, $options);
}
public function verify($msg, $signature) {
return $this->ec->verify($msg, $signature, $this);
}
public function inspect() {
return "<Key priv: " . (isset($this->priv) ? $this->priv->toString(16, 2) : "") .
" pub: " . (isset($this->pub) ? $this->pub->inspect() : "") . ">";
}
public function __debugInfo() {
return ["priv" => $this->priv, "pub" => $this->pub];
}
}
?>
+155
View File
@@ -0,0 +1,155 @@
<?php
namespace Elliptic\EC;
use Elliptic\Utils;
use BN\BN;
class Signature
{
public $r;
public $s;
public $recoveryParam;
function __construct($options, $enc = false)
{
if ($options instanceof Signature) {
$this->r = $options->r;
$this->s = $options->s;
$this->recoveryParam = $options->recoveryParam;
return;
}
if (isset($options['r'])) {
assert(isset($options["r"]) && isset($options["s"])); //, "Signature without r or s");
$this->r = new BN($options["r"], 16);
$this->s = new BN($options["s"], 16);
if( isset($options["recoveryParam"]) )
$this->recoveryParam = $options["recoveryParam"];
else
$this->recoveryParam = null;
return;
}
if (!$this->_importDER($options, $enc))
throw new \Exception('Unknown signature format');
}
private static function getLength($buf, &$pos)
{
$initial = $buf[$pos++];
if( !($initial & 0x80) )
return $initial;
$octetLen = $initial & 0xf;
$val = 0;
for($i = 0; $i < $octetLen; $i++)
{
$val = $val << 8;
$val = $val | $buf[$pos];
$pos++;
}
return $val;
}
private static function rmPadding(&$buf)
{
$i = 0;
$len = count($buf) - 1;
while($i < $len && !$buf[$i] && !($buf[$i+1] & 0x80) )
$i++;
if( $i === 0 )
return $buf;
return array_slice($buf, $i);
}
private function _importDER($data, $enc)
{
$data = Utils::toArray($data, $enc);
$dataLen = count($data);
$place = 0;
if( $data[$place++] !== 0x30)
return false;
$len = self::getLength($data, $place);
if( ($len + $place) !== $dataLen )
return false;
if( $data[$place++] !== 0x02 )
return false;
$rlen = self::getLength($data, $place);
$r = array_slice($data, $place, $rlen);
$place += $rlen;
if( $data[$place++] !== 0x02 )
return false;
$slen = self::getLength($data, $place);
if( $dataLen !== $slen + $place )
return false;
$s = array_slice($data, $place, $slen);
if( $r[0] === 0 && ($r[1] & 0x80 ) )
$r = array_slice($r, 1);
if( $s[0] === 0 && ($s[1] & 0x80 ) )
$s = array_slice($s, 1);
$this->r = new BN($r);
$this->s = new BN($s);
$this->recoveryParam = null;
return true;
}
private static function constructLength(&$arr, $len)
{
if( $len < 0x80 )
{
array_push($arr, $len);
return;
}
$octets = 1 + (log($len) / M_LN2 >> 3);
array_push($arr, $octets | 0x80);
while(--$octets)
array_push($arr, ($len >> ($octets << 3)) & 0xff);
array_push($arr, $len);
}
public function toDER($enc = false)
{
$r = $this->r->toArray();
$s = $this->s->toArray();
//Pad values
if( $r[0] & 0x80 )
array_unshift($r, 0);
if( $s[0] & 0x80 )
array_unshift($s, 0);
$r = self::rmPadding($r);
$s = self::rmPadding($s);
while(!$s[0] && !($s[1] & 0x80))
array_slice($s, 1);
$arr = array(0x02);
self::constructLength($arr, count($r));
$arr = array_merge($arr, $r, array(0x02));
self::constructLength($arr, count($s));
$backHalf = array_merge($arr, $s);
$res = array(0x30);
self::constructLength($res, count($backHalf));
$res = array_merge($res, $backHalf);
return Utils::encode($res, $enc);
}
}
?>