Viel neues
This commit is contained in:
547
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/Common.php
Normal file
547
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/Common.php
Normal file
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic EC Key Parsing Helper functions
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnexpectedValueException;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\File\ASN1;
|
||||
use phpseclib3\File\ASN1\Maps;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Generic EC Key Parsing Helper functions
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
trait Common
|
||||
{
|
||||
/**
|
||||
* Curve OIDs
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $curveOIDs = [];
|
||||
|
||||
/**
|
||||
* Child OIDs loaded
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $childOIDsLoaded = false;
|
||||
|
||||
/**
|
||||
* Use Named Curves
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $useNamedCurves = true;
|
||||
|
||||
/**
|
||||
* Initialize static variables
|
||||
*/
|
||||
private static function initialize_static_variables(): void
|
||||
{
|
||||
if (empty(self::$curveOIDs)) {
|
||||
// the sec* curves are from the standards for efficient cryptography group
|
||||
// sect* curves are curves over binary finite fields
|
||||
// secp* curves are curves over prime finite fields
|
||||
// sec*r* curves are regular curves; sec*k* curves are koblitz curves
|
||||
// brainpool*r* curves are regular prime finite field curves
|
||||
// brainpool*t* curves are twisted versions of the brainpool*r* curves
|
||||
self::$curveOIDs = [
|
||||
'prime192v1' => '1.2.840.10045.3.1.1', // J.5.1, example 1 (aka secp192r1)
|
||||
'prime192v2' => '1.2.840.10045.3.1.2', // J.5.1, example 2
|
||||
'prime192v3' => '1.2.840.10045.3.1.3', // J.5.1, example 3
|
||||
'prime239v1' => '1.2.840.10045.3.1.4', // J.5.2, example 1
|
||||
'prime239v2' => '1.2.840.10045.3.1.5', // J.5.2, example 2
|
||||
'prime239v3' => '1.2.840.10045.3.1.6', // J.5.2, example 3
|
||||
'prime256v1' => '1.2.840.10045.3.1.7', // J.5.3, example 1 (aka secp256r1)
|
||||
|
||||
// https://tools.ietf.org/html/rfc5656#section-10
|
||||
'nistp256' => '1.2.840.10045.3.1.7', // aka secp256r1
|
||||
'nistp384' => '1.3.132.0.34', // aka secp384r1
|
||||
'nistp521' => '1.3.132.0.35', // aka secp521r1
|
||||
|
||||
'nistk163' => '1.3.132.0.1', // aka sect163k1
|
||||
'nistp192' => '1.2.840.10045.3.1.1', // aka secp192r1
|
||||
'nistp224' => '1.3.132.0.33', // aka secp224r1
|
||||
'nistk233' => '1.3.132.0.26', // aka sect233k1
|
||||
'nistb233' => '1.3.132.0.27', // aka sect233r1
|
||||
'nistk283' => '1.3.132.0.16', // aka sect283k1
|
||||
'nistk409' => '1.3.132.0.36', // aka sect409k1
|
||||
'nistb409' => '1.3.132.0.37', // aka sect409r1
|
||||
'nistt571' => '1.3.132.0.38', // aka sect571k1
|
||||
|
||||
// from https://tools.ietf.org/html/rfc5915
|
||||
'secp192r1' => '1.2.840.10045.3.1.1', // aka prime192v1
|
||||
'sect163k1' => '1.3.132.0.1',
|
||||
'sect163r2' => '1.3.132.0.15',
|
||||
'secp224r1' => '1.3.132.0.33',
|
||||
'sect233k1' => '1.3.132.0.26',
|
||||
'sect233r1' => '1.3.132.0.27',
|
||||
'secp256r1' => '1.2.840.10045.3.1.7', // aka prime256v1
|
||||
'sect283k1' => '1.3.132.0.16',
|
||||
'sect283r1' => '1.3.132.0.17',
|
||||
'secp384r1' => '1.3.132.0.34',
|
||||
'sect409k1' => '1.3.132.0.36',
|
||||
'sect409r1' => '1.3.132.0.37',
|
||||
'secp521r1' => '1.3.132.0.35',
|
||||
'sect571k1' => '1.3.132.0.38',
|
||||
'sect571r1' => '1.3.132.0.39',
|
||||
// from http://www.secg.org/SEC2-Ver-1.0.pdf
|
||||
'secp112r1' => '1.3.132.0.6',
|
||||
'secp112r2' => '1.3.132.0.7',
|
||||
'secp128r1' => '1.3.132.0.28',
|
||||
'secp128r2' => '1.3.132.0.29',
|
||||
'secp160k1' => '1.3.132.0.9',
|
||||
'secp160r1' => '1.3.132.0.8',
|
||||
'secp160r2' => '1.3.132.0.30',
|
||||
'secp192k1' => '1.3.132.0.31',
|
||||
'secp224k1' => '1.3.132.0.32',
|
||||
'secp256k1' => '1.3.132.0.10',
|
||||
|
||||
'sect113r1' => '1.3.132.0.4',
|
||||
'sect113r2' => '1.3.132.0.5',
|
||||
'sect131r1' => '1.3.132.0.22',
|
||||
'sect131r2' => '1.3.132.0.23',
|
||||
'sect163r1' => '1.3.132.0.2',
|
||||
'sect193r1' => '1.3.132.0.24',
|
||||
'sect193r2' => '1.3.132.0.25',
|
||||
'sect239k1' => '1.3.132.0.3',
|
||||
|
||||
// from http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf#page=36
|
||||
/*
|
||||
'c2pnb163v1' => '1.2.840.10045.3.0.1', // J.4.1, example 1
|
||||
'c2pnb163v2' => '1.2.840.10045.3.0.2', // J.4.1, example 2
|
||||
'c2pnb163v3' => '1.2.840.10045.3.0.3', // J.4.1, example 3
|
||||
'c2pnb172w1' => '1.2.840.10045.3.0.4', // J.4.2, example 1
|
||||
'c2tnb191v1' => '1.2.840.10045.3.0.5', // J.4.3, example 1
|
||||
'c2tnb191v2' => '1.2.840.10045.3.0.6', // J.4.3, example 2
|
||||
'c2tnb191v3' => '1.2.840.10045.3.0.7', // J.4.3, example 3
|
||||
'c2onb191v4' => '1.2.840.10045.3.0.8', // J.4.3, example 4
|
||||
'c2onb191v5' => '1.2.840.10045.3.0.9', // J.4.3, example 5
|
||||
'c2pnb208w1' => '1.2.840.10045.3.0.10', // J.4.4, example 1
|
||||
'c2tnb239v1' => '1.2.840.10045.3.0.11', // J.4.5, example 1
|
||||
'c2tnb239v2' => '1.2.840.10045.3.0.12', // J.4.5, example 2
|
||||
'c2tnb239v3' => '1.2.840.10045.3.0.13', // J.4.5, example 3
|
||||
'c2onb239v4' => '1.2.840.10045.3.0.14', // J.4.5, example 4
|
||||
'c2onb239v5' => '1.2.840.10045.3.0.15', // J.4.5, example 5
|
||||
'c2pnb272w1' => '1.2.840.10045.3.0.16', // J.4.6, example 1
|
||||
'c2pnb304w1' => '1.2.840.10045.3.0.17', // J.4.7, example 1
|
||||
'c2tnb359v1' => '1.2.840.10045.3.0.18', // J.4.8, example 1
|
||||
'c2pnb368w1' => '1.2.840.10045.3.0.19', // J.4.9, example 1
|
||||
'c2tnb431r1' => '1.2.840.10045.3.0.20', // J.4.10, example 1
|
||||
*/
|
||||
|
||||
// http://www.ecc-brainpool.org/download/Domain-parameters.pdf
|
||||
// https://tools.ietf.org/html/rfc5639
|
||||
'brainpoolP160r1' => '1.3.36.3.3.2.8.1.1.1',
|
||||
'brainpoolP160t1' => '1.3.36.3.3.2.8.1.1.2',
|
||||
'brainpoolP192r1' => '1.3.36.3.3.2.8.1.1.3',
|
||||
'brainpoolP192t1' => '1.3.36.3.3.2.8.1.1.4',
|
||||
'brainpoolP224r1' => '1.3.36.3.3.2.8.1.1.5',
|
||||
'brainpoolP224t1' => '1.3.36.3.3.2.8.1.1.6',
|
||||
'brainpoolP256r1' => '1.3.36.3.3.2.8.1.1.7',
|
||||
'brainpoolP256t1' => '1.3.36.3.3.2.8.1.1.8',
|
||||
'brainpoolP320r1' => '1.3.36.3.3.2.8.1.1.9',
|
||||
'brainpoolP320t1' => '1.3.36.3.3.2.8.1.1.10',
|
||||
'brainpoolP384r1' => '1.3.36.3.3.2.8.1.1.11',
|
||||
'brainpoolP384t1' => '1.3.36.3.3.2.8.1.1.12',
|
||||
'brainpoolP512r1' => '1.3.36.3.3.2.8.1.1.13',
|
||||
'brainpoolP512t1' => '1.3.36.3.3.2.8.1.1.14',
|
||||
];
|
||||
ASN1::loadOIDs([
|
||||
'prime-field' => '1.2.840.10045.1.1',
|
||||
'characteristic-two-field' => '1.2.840.10045.1.2',
|
||||
'characteristic-two-basis' => '1.2.840.10045.1.2.3',
|
||||
// per http://www.secg.org/SEC1-Ver-1.0.pdf#page=84, gnBasis "not used here"
|
||||
'gnBasis' => '1.2.840.10045.1.2.3.1', // NULL
|
||||
'tpBasis' => '1.2.840.10045.1.2.3.2', // Trinomial
|
||||
'ppBasis' => '1.2.840.10045.1.2.3.3', // Pentanomial
|
||||
] + self::$curveOIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly set the curve
|
||||
*
|
||||
* If the key contains an implicit curve phpseclib needs the curve
|
||||
* to be explicitly provided
|
||||
*/
|
||||
public static function setImplicitCurve(BaseCurve $curve): void
|
||||
{
|
||||
self::$implicitCurve = $curve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
|
||||
* on the curve parameters
|
||||
*
|
||||
* @return BaseCurve|false
|
||||
*/
|
||||
protected static function loadCurveByParam(array $params)
|
||||
{
|
||||
if (count($params) > 1) {
|
||||
throw new RuntimeException('No parameters are present');
|
||||
}
|
||||
if (isset($params['namedCurve'])) {
|
||||
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $params['namedCurve'];
|
||||
if (!class_exists($curve)) {
|
||||
throw new UnsupportedCurveException('Named Curve of ' . $params['namedCurve'] . ' is not supported');
|
||||
}
|
||||
return new $curve();
|
||||
}
|
||||
if (isset($params['implicitCurve'])) {
|
||||
if (!isset(self::$implicitCurve)) {
|
||||
throw new RuntimeException('Implicit curves can be provided by calling setImplicitCurve');
|
||||
}
|
||||
return self::$implicitCurve;
|
||||
}
|
||||
if (isset($params['specifiedCurve'])) {
|
||||
$data = $params['specifiedCurve'];
|
||||
switch ($data['fieldID']['fieldType']) {
|
||||
case 'prime-field':
|
||||
$curve = new PrimeCurve();
|
||||
$curve->setModulo($data['fieldID']['parameters']);
|
||||
$curve->setCoefficients(
|
||||
new BigInteger($data['curve']['a'], 256),
|
||||
new BigInteger($data['curve']['b'], 256)
|
||||
);
|
||||
$point = self::extractPoint("\0" . $data['base'], $curve);
|
||||
$curve->setBasePoint(...$point);
|
||||
$curve->setOrder($data['order']);
|
||||
return $curve;
|
||||
case 'characteristic-two-field':
|
||||
$curve = new BinaryCurve();
|
||||
$params = ASN1::decodeBER($data['fieldID']['parameters']);
|
||||
$params = ASN1::asn1map($params[0], Maps\Characteristic_two::MAP);
|
||||
$modulo = [(int) $params['m']->toString()];
|
||||
switch ($params['basis']) {
|
||||
case 'tpBasis':
|
||||
$modulo[] = (int) $params['parameters']->toString();
|
||||
break;
|
||||
case 'ppBasis':
|
||||
$temp = ASN1::decodeBER($params['parameters']);
|
||||
$temp = ASN1::asn1map($temp[0], Maps\Pentanomial::MAP);
|
||||
$modulo[] = (int) $temp['k3']->toString();
|
||||
$modulo[] = (int) $temp['k2']->toString();
|
||||
$modulo[] = (int) $temp['k1']->toString();
|
||||
}
|
||||
$modulo[] = 0;
|
||||
$curve->setModulo(...$modulo);
|
||||
$len = ceil($modulo[0] / 8);
|
||||
$curve->setCoefficients(
|
||||
Strings::bin2hex($data['curve']['a']),
|
||||
Strings::bin2hex($data['curve']['b'])
|
||||
);
|
||||
$point = self::extractPoint("\0" . $data['base'], $curve);
|
||||
$curve->setBasePoint(...$point);
|
||||
$curve->setOrder($data['order']);
|
||||
return $curve;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Field Type of ' . $data['fieldID']['fieldType'] . ' is not supported');
|
||||
}
|
||||
}
|
||||
throw new RuntimeException('No valid parameters are present');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract points from a string
|
||||
*
|
||||
* Supports both compressed and uncompressed points
|
||||
*
|
||||
* @return object[]
|
||||
*/
|
||||
public static function extractPoint(string $str, BaseCurve $curve): array
|
||||
{
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
// first step of point deciding as discussed at the following URL's:
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.1.3
|
||||
// https://tools.ietf.org/html/rfc8032#section-5.2.3
|
||||
$y = $str;
|
||||
$y = strrev($y);
|
||||
$sign = (bool) (ord($y[0]) & 0x80);
|
||||
$y[0] = $y[0] & chr(0x7F);
|
||||
$y = new BigInteger($y, 256);
|
||||
if ($y->compare($curve->getModulo()) >= 0) {
|
||||
throw new RuntimeException('The Y coordinate should not be >= the modulo');
|
||||
}
|
||||
$point = $curve->recoverX($y, $sign);
|
||||
if (!$curve->verifyPoint($point)) {
|
||||
throw new RuntimeException('Unable to verify that point exists on curve');
|
||||
}
|
||||
return $point;
|
||||
}
|
||||
|
||||
// the first byte of a bit string represents the number of bits in the last byte that are to be ignored but,
|
||||
// currently, bit strings wanting a non-zero amount of bits trimmed are not supported
|
||||
if (($val = Strings::shift($str)) != "\0") {
|
||||
throw new UnexpectedValueException('extractPoint expects the first byte to be null - not ' . Strings::bin2hex($val));
|
||||
}
|
||||
if ($str == "\0") {
|
||||
return [];
|
||||
}
|
||||
|
||||
$keylen = strlen($str);
|
||||
$order = $curve->getLengthInBytes();
|
||||
// point compression is being used
|
||||
if ($keylen == $order + 1) {
|
||||
return $curve->derivePoint($str);
|
||||
}
|
||||
|
||||
// point compression is not being used
|
||||
if ($keylen == 2 * $order + 1) {
|
||||
preg_match("#(.)(.{{$order}})(.{{$order}})#s", $str, $matches);
|
||||
[, $w, $x, $y] = $matches;
|
||||
if ($w != "\4") {
|
||||
throw new UnexpectedValueException('The first byte of an uncompressed point should be 04 - not ' . Strings::bin2hex($val));
|
||||
}
|
||||
$point = [
|
||||
$curve->convertInteger(new BigInteger($x, 256)),
|
||||
$curve->convertInteger(new BigInteger($y, 256)),
|
||||
];
|
||||
|
||||
if (!$curve->verifyPoint($point)) {
|
||||
throw new RuntimeException('Unable to verify that point exists on curve');
|
||||
}
|
||||
|
||||
return $point;
|
||||
}
|
||||
|
||||
throw new UnexpectedValueException('The string representation of the points is not of an appropriate length');
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Parameters
|
||||
*
|
||||
* @param bool $returnArray optional
|
||||
* @param array $options optional
|
||||
* @return string|false
|
||||
* @todo Maybe at some point this could be moved to __toString() for each of the curves?
|
||||
*/
|
||||
private static function encodeParameters(BaseCurve $curve, bool $returnArray = false, array $options = [])
|
||||
{
|
||||
$useNamedCurves = $options['namedCurve'] ?? self::$useNamedCurves;
|
||||
|
||||
$reflect = new \ReflectionClass($curve);
|
||||
$name = $reflect->getShortName();
|
||||
if ($useNamedCurves) {
|
||||
if (isset(self::$curveOIDs[$name])) {
|
||||
if ($reflect->isFinal()) {
|
||||
$reflect = $reflect->getParentClass();
|
||||
$name = $reflect->getShortName();
|
||||
}
|
||||
return $returnArray ?
|
||||
['namedCurve' => $name] :
|
||||
ASN1::encodeDER(['namedCurve' => $name], Maps\ECParameters::MAP);
|
||||
}
|
||||
foreach (new \DirectoryIterator(__DIR__ . '/../../Curves/') as $file) {
|
||||
if ($file->getExtension() != 'php') {
|
||||
continue;
|
||||
}
|
||||
$testName = $file->getBasename('.php');
|
||||
$class = 'phpseclib3\Crypt\EC\Curves\\' . $testName;
|
||||
$reflect = new \ReflectionClass($class);
|
||||
if ($reflect->isFinal()) {
|
||||
continue;
|
||||
}
|
||||
$candidate = new $class();
|
||||
switch ($name) {
|
||||
case 'Prime':
|
||||
if (!$candidate instanceof PrimeCurve) {
|
||||
break;
|
||||
}
|
||||
if (!$candidate->getModulo()->equals($curve->getModulo())) {
|
||||
break;
|
||||
}
|
||||
if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
|
||||
break;
|
||||
}
|
||||
if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
|
||||
break;
|
||||
}
|
||||
|
||||
[$candidateX, $candidateY] = $candidate->getBasePoint();
|
||||
[$curveX, $curveY] = $curve->getBasePoint();
|
||||
if ($candidateX->toBytes() != $curveX->toBytes()) {
|
||||
break;
|
||||
}
|
||||
if ($candidateY->toBytes() != $curveY->toBytes()) {
|
||||
break;
|
||||
}
|
||||
|
||||
return $returnArray ?
|
||||
['namedCurve' => $testName] :
|
||||
ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
|
||||
case 'Binary':
|
||||
if (!$candidate instanceof BinaryCurve) {
|
||||
break;
|
||||
}
|
||||
if ($candidate->getModulo() != $curve->getModulo()) {
|
||||
break;
|
||||
}
|
||||
if ($candidate->getA()->toBytes() != $curve->getA()->toBytes()) {
|
||||
break;
|
||||
}
|
||||
if ($candidate->getB()->toBytes() != $curve->getB()->toBytes()) {
|
||||
break;
|
||||
}
|
||||
|
||||
[$candidateX, $candidateY] = $candidate->getBasePoint();
|
||||
[$curveX, $curveY] = $curve->getBasePoint();
|
||||
if ($candidateX->toBytes() != $curveX->toBytes()) {
|
||||
break;
|
||||
}
|
||||
if ($candidateY->toBytes() != $curveY->toBytes()) {
|
||||
break;
|
||||
}
|
||||
|
||||
return $returnArray ?
|
||||
['namedCurve' => $testName] :
|
||||
ASN1::encodeDER(['namedCurve' => $testName], Maps\ECParameters::MAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$order = $curve->getOrder();
|
||||
// we could try to calculate the order thusly:
|
||||
// https://crypto.stackexchange.com/a/27914/4520
|
||||
// https://en.wikipedia.org/wiki/Schoof%E2%80%93Elkies%E2%80%93Atkin_algorithm
|
||||
if (!$order) {
|
||||
throw new RuntimeException('Specified Curves need the order to be specified');
|
||||
}
|
||||
$point = $curve->getBasePoint();
|
||||
$x = $point[0]->toBytes();
|
||||
$y = $point[1]->toBytes();
|
||||
|
||||
if ($curve instanceof PrimeCurve) {
|
||||
/*
|
||||
* valid versions are:
|
||||
*
|
||||
* ecdpVer1:
|
||||
* - neither the curve or the base point are generated verifiably randomly.
|
||||
* ecdpVer2:
|
||||
* - curve and base point are generated verifiably at random and curve.seed is present
|
||||
* ecdpVer3:
|
||||
* - base point is generated verifiably at random but curve is not. curve.seed is present
|
||||
*/
|
||||
// other (optional) parameters can be calculated using the methods discused at
|
||||
// https://crypto.stackexchange.com/q/28947/4520
|
||||
$data = [
|
||||
'version' => 'ecdpVer1',
|
||||
'fieldID' => [
|
||||
'fieldType' => 'prime-field',
|
||||
'parameters' => $curve->getModulo(),
|
||||
],
|
||||
'curve' => [
|
||||
'a' => $curve->getA()->toBytes(),
|
||||
'b' => $curve->getB()->toBytes(),
|
||||
],
|
||||
'base' => "\4" . $x . $y,
|
||||
'order' => $order,
|
||||
];
|
||||
|
||||
return $returnArray ?
|
||||
['specifiedCurve' => $data] :
|
||||
ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
|
||||
}
|
||||
if ($curve instanceof BinaryCurve) {
|
||||
$modulo = $curve->getModulo();
|
||||
$basis = count($modulo);
|
||||
$m = array_shift($modulo);
|
||||
array_pop($modulo); // the last parameter should always be 0
|
||||
//rsort($modulo);
|
||||
switch ($basis) {
|
||||
case 3:
|
||||
$basis = 'tpBasis';
|
||||
$modulo = new BigInteger($modulo[0]);
|
||||
break;
|
||||
case 5:
|
||||
$basis = 'ppBasis';
|
||||
// these should be in strictly ascending order (hence the commented out rsort above)
|
||||
$modulo = [
|
||||
'k1' => new BigInteger($modulo[2]),
|
||||
'k2' => new BigInteger($modulo[1]),
|
||||
'k3' => new BigInteger($modulo[0]),
|
||||
];
|
||||
$modulo = ASN1::encodeDER($modulo, Maps\Pentanomial::MAP);
|
||||
$modulo = new ASN1\Element($modulo);
|
||||
}
|
||||
$params = ASN1::encodeDER([
|
||||
'm' => new BigInteger($m),
|
||||
'basis' => $basis,
|
||||
'parameters' => $modulo,
|
||||
], Maps\Characteristic_two::MAP);
|
||||
$params = new ASN1\Element($params);
|
||||
$a = ltrim($curve->getA()->toBytes(), "\0");
|
||||
if (!strlen($a)) {
|
||||
$a = "\0";
|
||||
}
|
||||
$b = ltrim($curve->getB()->toBytes(), "\0");
|
||||
if (!strlen($b)) {
|
||||
$b = "\0";
|
||||
}
|
||||
$data = [
|
||||
'version' => 'ecdpVer1',
|
||||
'fieldID' => [
|
||||
'fieldType' => 'characteristic-two-field',
|
||||
'parameters' => $params,
|
||||
],
|
||||
'curve' => [
|
||||
'a' => $a,
|
||||
'b' => $b,
|
||||
],
|
||||
'base' => "\4" . $x . $y,
|
||||
'order' => $order,
|
||||
];
|
||||
|
||||
return $returnArray ?
|
||||
['specifiedCurve' => $data] :
|
||||
ASN1::encodeDER(['specifiedCurve' => $data], Maps\ECParameters::MAP);
|
||||
}
|
||||
|
||||
throw new UnsupportedCurveException('Curve cannot be serialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Specified Curve
|
||||
*
|
||||
* A specified curve has all the coefficients, the base points, etc, explicitely included.
|
||||
* A specified curve is a more verbose way of representing a curve
|
||||
*/
|
||||
public static function useSpecifiedCurve(): void
|
||||
{
|
||||
self::$useNamedCurves = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Named Curve
|
||||
*
|
||||
* A named curve does not include any parameters. It is up to the EC parameters to
|
||||
* know what the coefficients, the base points, etc, are from the name of the curve.
|
||||
* A named curve is a more concise way of representing a curve
|
||||
*/
|
||||
public static function useNamedCurve(): void
|
||||
{
|
||||
self::$useNamedCurves = true;
|
||||
}
|
||||
}
|
||||
184
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/JWK.php
Normal file
184
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/JWK.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* JSON Web Key (RFC7517 / RFC8037) Formatted EC Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\Common\Formats\Keys\JWK as Progenitor;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib3\Crypt\EC\Curves\secp256k1;
|
||||
use phpseclib3\Crypt\EC\Curves\secp256r1;
|
||||
use phpseclib3\Crypt\EC\Curves\secp384r1;
|
||||
use phpseclib3\Crypt\EC\Curves\secp521r1;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* JWK Formatted EC Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class JWK extends Progenitor
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
*/
|
||||
public static function load($key, ?string $password = null): array
|
||||
{
|
||||
$key = parent::loadHelper($key);
|
||||
|
||||
switch ($key->kty) {
|
||||
case 'EC':
|
||||
switch ($key->crv) {
|
||||
case 'P-256':
|
||||
case 'P-384':
|
||||
case 'P-521':
|
||||
case 'secp256k1':
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Only P-256, P-384, P-521 and secp256k1 curves are accepted (' . $key->crv . ' provided)');
|
||||
}
|
||||
break;
|
||||
case 'OKP':
|
||||
switch ($key->crv) {
|
||||
case 'Ed25519':
|
||||
case 'Ed448':
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Only Ed25519 and Ed448 curves are accepted (' . $key->crv . ' provided)');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Only EC and OKP JWK keys are supported');
|
||||
}
|
||||
|
||||
$curve = '\phpseclib3\Crypt\EC\Curves\\' . str_replace('P-', 'nistp', $key->crv);
|
||||
$curve = new $curve();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
$QA = self::extractPoint(Strings::base64url_decode($key->x), $curve);
|
||||
if (!isset($key->d)) {
|
||||
return compact('curve', 'QA');
|
||||
}
|
||||
$arr = $curve->extractSecret(Strings::base64url_decode($key->d));
|
||||
return compact('curve', 'QA') + $arr;
|
||||
}
|
||||
|
||||
$QA = [
|
||||
$curve->convertInteger(new BigInteger(Strings::base64url_decode($key->x), 256)),
|
||||
$curve->convertInteger(new BigInteger(Strings::base64url_decode($key->y), 256)),
|
||||
];
|
||||
|
||||
if (!$curve->verifyPoint($QA)) {
|
||||
throw new \RuntimeException('Unable to verify that point exists on curve');
|
||||
}
|
||||
|
||||
if (!isset($key->d)) {
|
||||
return compact('curve', 'QA');
|
||||
}
|
||||
|
||||
$dA = new BigInteger(Strings::base64url_decode($key->d), 256);
|
||||
|
||||
$curve->rangeCheck($dA);
|
||||
|
||||
return compact('curve', 'dA', 'QA');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias that corresponds to a curve
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getAlias(BaseCurve $curve)
|
||||
{
|
||||
switch (true) {
|
||||
case $curve instanceof secp256r1:
|
||||
return 'P-256';
|
||||
case $curve instanceof secp384r1:
|
||||
return 'P-384';
|
||||
case $curve instanceof secp521r1:
|
||||
return 'P-521';
|
||||
case $curve instanceof secp256k1:
|
||||
return 'secp256k1';
|
||||
}
|
||||
|
||||
$reflect = new \ReflectionClass($curve);
|
||||
$curveName = $reflect->isFinal() ?
|
||||
$reflect->getParentClass()->getShortName() :
|
||||
$reflect->getShortName();
|
||||
throw new UnsupportedCurveException("$curveName is not a supported curve");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array superstructure for an EC public key
|
||||
*
|
||||
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
|
||||
*/
|
||||
private static function savePublicKeyHelper(BaseCurve $curve, array $publicKey): array
|
||||
{
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
return [
|
||||
'kty' => 'OKP',
|
||||
'crv' => $curve instanceof Ed25519 ? 'Ed25519' : 'Ed448',
|
||||
'x' => Strings::base64url_encode($curve->encodePoint($publicKey)),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'kty' => 'EC',
|
||||
'crv' => self::getAlias($curve),
|
||||
'x' => Strings::base64url_encode($publicKey[0]->toBytes()),
|
||||
'y' => Strings::base64url_encode($publicKey[1]->toBytes()),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
|
||||
*/
|
||||
public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
|
||||
{
|
||||
$key = self::savePublicKeyHelper($curve, $publicKey);
|
||||
|
||||
return self::wrapKey($key, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(
|
||||
BigInteger $privateKey,
|
||||
BaseCurve $curve,
|
||||
array $publicKey,
|
||||
?string $secret = null,
|
||||
?string $password = null,
|
||||
array $options = []
|
||||
): string {
|
||||
$key = self::savePublicKeyHelper($curve, $publicKey);
|
||||
$key['d'] = $curve instanceof TwistedEdwardsCurve ? $secret : $privateKey->toBytes();
|
||||
$key['d'] = Strings::base64url_encode($key['d']);
|
||||
|
||||
return self::wrapKey($key, $options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Montgomery Private Key Handler
|
||||
*
|
||||
* "Naked" Curve25519 private keys can pretty much be any sequence of random 32x bytes so unless
|
||||
* we have a "hidden" key handler pretty much every 32 byte string will be loaded as a curve25519
|
||||
* private key even if it probably isn't one by PublicKeyLoader.
|
||||
*
|
||||
* "Naked" Curve25519 public keys also a string of 32 bytes so distinguishing between a "naked"
|
||||
* curve25519 private key and a public key is nigh impossible, hence separate plugins for each
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib3\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib3\Crypt\EC\Curves\Curve448;
|
||||
use phpseclib3\Exception\LengthException;
|
||||
use phpseclib3\Exception\UnsupportedFormatException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* Montgomery Curve Private Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class MontgomeryPrivate
|
||||
{
|
||||
/**
|
||||
* Is invisible flag
|
||||
*/
|
||||
public const IS_INVISIBLE = true;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*/
|
||||
public static function load(string $key, ?string $password = null): array
|
||||
{
|
||||
switch (strlen($key)) {
|
||||
case 32:
|
||||
$curve = new Curve25519();
|
||||
break;
|
||||
case 56:
|
||||
$curve = new Curve448();
|
||||
break;
|
||||
default:
|
||||
throw new LengthException('The only supported lengths are 32 and 56');
|
||||
}
|
||||
|
||||
$components = ['curve' => $curve];
|
||||
$components['dA'] = new BigInteger($key, 256);
|
||||
$curve->rangeCheck($components['dA']);
|
||||
// note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result)
|
||||
$components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePublicKey(MontgomeryCurve $curve, array $publicKey): string
|
||||
{
|
||||
return strrev($publicKey[0]->toBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $privateKey, MontgomeryCurve $curve, array $publicKey, ?string $password = null): string
|
||||
{
|
||||
if (!empty($password) && is_string($password)) {
|
||||
throw new UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption');
|
||||
}
|
||||
|
||||
return $privateKey->toBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Montgomery Public Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib3\Crypt\EC\Curves\Curve25519;
|
||||
use phpseclib3\Crypt\EC\Curves\Curve448;
|
||||
use phpseclib3\Exception\LengthException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* Montgomery Public Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class MontgomeryPublic
|
||||
{
|
||||
/**
|
||||
* Is invisible flag
|
||||
*/
|
||||
public const IS_INVISIBLE = true;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*/
|
||||
public static function load(string $key, ?string $password = null): array
|
||||
{
|
||||
switch (strlen($key)) {
|
||||
case 32:
|
||||
$curve = new Curve25519();
|
||||
break;
|
||||
case 56:
|
||||
$curve = new Curve448();
|
||||
break;
|
||||
default:
|
||||
throw new LengthException('The only supported lengths are 32 and 56');
|
||||
}
|
||||
|
||||
$components = ['curve' => $curve];
|
||||
$components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key), 256))];
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePublicKey(MontgomeryCurve $curve, array $publicKey): string
|
||||
{
|
||||
return strrev($publicKey[0]->toBytes());
|
||||
}
|
||||
}
|
||||
208
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php
Normal file
208
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/OpenSSH.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* OpenSSH Formatted EC Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Place in $HOME/.ssh/authorized_keys
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* OpenSSH Formatted EC Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class OpenSSH extends Progenitor
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Supported Key Types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $types = [
|
||||
'ecdsa-sha2-nistp256',
|
||||
'ecdsa-sha2-nistp384',
|
||||
'ecdsa-sha2-nistp521',
|
||||
'ssh-ed25519',
|
||||
];
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
*/
|
||||
public static function load($key, ?string $password = null): array
|
||||
{
|
||||
$parsed = parent::load($key, $password);
|
||||
|
||||
if (isset($parsed['paddedKey'])) {
|
||||
$paddedKey = $parsed['paddedKey'];
|
||||
[$type] = Strings::unpackSSH2('s', $paddedKey);
|
||||
if ($type != $parsed['type']) {
|
||||
throw new RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
|
||||
}
|
||||
if ($type == 'ssh-ed25519') {
|
||||
[, $key, $comment] = Strings::unpackSSH2('sss', $paddedKey);
|
||||
$key = libsodium::load($key);
|
||||
$key['comment'] = $comment;
|
||||
return $key;
|
||||
}
|
||||
[$curveName, $publicKey, $privateKey, $comment] = Strings::unpackSSH2('ssis', $paddedKey);
|
||||
$curve = self::loadCurveByParam(['namedCurve' => $curveName]);
|
||||
$curve->rangeCheck($privateKey);
|
||||
return [
|
||||
'curve' => $curve,
|
||||
'dA' => $privateKey,
|
||||
'QA' => self::extractPoint("\0$publicKey", $curve),
|
||||
'comment' => $comment,
|
||||
];
|
||||
}
|
||||
|
||||
if ($parsed['type'] == 'ssh-ed25519') {
|
||||
if (Strings::shift($parsed['publicKey'], 4) != "\0\0\0\x20") {
|
||||
throw new RuntimeException('Length of ssh-ed25519 key should be 32');
|
||||
}
|
||||
|
||||
$curve = new Ed25519();
|
||||
$qa = self::extractPoint($parsed['publicKey'], $curve);
|
||||
} else {
|
||||
[$curveName, $publicKey] = Strings::unpackSSH2('ss', $parsed['publicKey']);
|
||||
$curveName = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
|
||||
$curve = new $curveName();
|
||||
|
||||
$qa = self::extractPoint("\0" . $publicKey, $curve);
|
||||
}
|
||||
|
||||
return [
|
||||
'curve' => $curve,
|
||||
'QA' => $qa,
|
||||
'comment' => $parsed['comment'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias that corresponds to a curve
|
||||
*/
|
||||
private static function getAlias(BaseCurve $curve): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
$reflect = new \ReflectionClass($curve);
|
||||
$name = $reflect->getShortName();
|
||||
|
||||
$oid = self::$curveOIDs[$name];
|
||||
$aliases = array_filter(self::$curveOIDs, fn ($v) => $v == $oid);
|
||||
$aliases = array_keys($aliases);
|
||||
|
||||
for ($i = 0; $i < count($aliases); $i++) {
|
||||
if (in_array('ecdsa-sha2-' . $aliases[$i], self::$types)) {
|
||||
$alias = $aliases[$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($alias)) {
|
||||
throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
|
||||
}
|
||||
|
||||
return $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
* @param array $options optional
|
||||
*/
|
||||
public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
|
||||
{
|
||||
$comment = $options['comment'] ?? self::$comment;
|
||||
|
||||
if ($curve instanceof Ed25519) {
|
||||
$key = Strings::packSSH2('ss', 'ssh-ed25519', $curve->encodePoint($publicKey));
|
||||
|
||||
if ($options['binary'] ?? self::$binary) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$key = 'ssh-ed25519 ' . base64_encode($key) . ' ' . $comment;
|
||||
return $key;
|
||||
}
|
||||
|
||||
$alias = self::getAlias($curve);
|
||||
|
||||
$points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
$key = Strings::packSSH2('sss', 'ecdsa-sha2-' . $alias, $alias, $points);
|
||||
|
||||
if ($options['binary'] ?? self::$binary) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$key = 'ecdsa-sha2-' . $alias . ' ' . base64_encode($key) . ' ' . $comment;
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Ed25519 $curve
|
||||
* @param Integer[] $publicKey
|
||||
* @param string|false $password
|
||||
* @param array $options optional
|
||||
*/
|
||||
public static function savePrivateKey(
|
||||
BigInteger $privateKey,
|
||||
BaseCurve $curve,
|
||||
array $publicKey,
|
||||
?string $secret = null,
|
||||
?string $password = null,
|
||||
array $options = []
|
||||
): string {
|
||||
if ($curve instanceof Ed25519) {
|
||||
if (!isset($secret)) {
|
||||
throw new RuntimeException('Private Key does not have a secret set');
|
||||
}
|
||||
if (strlen($secret) != 32) {
|
||||
throw new RuntimeException('Private Key secret is not of the correct length');
|
||||
}
|
||||
|
||||
$pubKey = $curve->encodePoint($publicKey);
|
||||
|
||||
$publicKey = Strings::packSSH2('ss', 'ssh-ed25519', $pubKey);
|
||||
$privateKey = Strings::packSSH2('sss', 'ssh-ed25519', $pubKey, $secret . $pubKey);
|
||||
|
||||
return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
|
||||
}
|
||||
|
||||
$alias = self::getAlias($curve);
|
||||
|
||||
$points = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
$publicKey = self::savePublicKey($curve, $publicKey, ['binary' => true]);
|
||||
|
||||
$privateKey = Strings::packSSH2('sssi', 'ecdsa-sha2-' . $alias, $alias, $points, $privateKey);
|
||||
|
||||
return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
|
||||
}
|
||||
}
|
||||
189
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php
Normal file
189
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PKCS1.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* "PKCS1" (RFC5915) Formatted EC Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Used by File/X509.php
|
||||
*
|
||||
* Processes keys with the following headers:
|
||||
*
|
||||
* -----BEGIN EC PRIVATE KEY-----
|
||||
* -----BEGIN EC PARAMETERS-----
|
||||
*
|
||||
* Technically, PKCS1 is for RSA keys, only, but we're using PKCS1 to describe
|
||||
* DSA, whose format isn't really formally described anywhere, so might as well
|
||||
* use it to describe this, too. PKCS1 is easier to remember than RFC5915, after
|
||||
* all. I suppose this could also be named IETF but idk
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnexpectedValueException;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\File\ASN1;
|
||||
use phpseclib3\File\ASN1\Maps;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* "PKCS1" (RFC5915) Formatted EC Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class PKCS1 extends Progenitor
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
*/
|
||||
public static function load($key, ?string $password = null): array
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if (!Strings::is_stringable($key)) {
|
||||
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
|
||||
}
|
||||
|
||||
if (strpos($key, 'BEGIN EC PARAMETERS') && strpos($key, 'BEGIN EC PRIVATE KEY')) {
|
||||
$components = [];
|
||||
|
||||
preg_match('#-*BEGIN EC PRIVATE KEY-*[^-]*-*END EC PRIVATE KEY-*#s', $key, $matches);
|
||||
$decoded = parent::load($matches[0], $password);
|
||||
$decoded = ASN1::decodeBER($decoded);
|
||||
if (!$decoded) {
|
||||
throw new RuntimeException('Unable to decode BER');
|
||||
}
|
||||
|
||||
$ecPrivate = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
|
||||
if (!is_array($ecPrivate)) {
|
||||
throw new RuntimeException('Unable to perform ASN1 mapping');
|
||||
}
|
||||
|
||||
if (isset($ecPrivate['parameters'])) {
|
||||
$components['curve'] = self::loadCurveByParam($ecPrivate['parameters']);
|
||||
}
|
||||
|
||||
preg_match('#-*BEGIN EC PARAMETERS-*[^-]*-*END EC PARAMETERS-*#s', $key, $matches);
|
||||
$decoded = parent::load($matches[0], '');
|
||||
$decoded = ASN1::decodeBER($decoded);
|
||||
if (!$decoded) {
|
||||
throw new RuntimeException('Unable to decode BER');
|
||||
}
|
||||
$ecParams = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
|
||||
if (!is_array($ecParams)) {
|
||||
throw new RuntimeException('Unable to perform ASN1 mapping');
|
||||
}
|
||||
$ecParams = self::loadCurveByParam($ecParams);
|
||||
|
||||
// comparing $ecParams and $components['curve'] directly won't work because they'll have different Math\Common\FiniteField classes
|
||||
// even if the modulo is the same
|
||||
if (isset($components['curve']) && self::encodeParameters($ecParams, false, []) != self::encodeParameters($components['curve'], false, [])) {
|
||||
throw new RuntimeException('EC PARAMETERS does not correspond to EC PRIVATE KEY');
|
||||
}
|
||||
|
||||
if (!isset($components['curve'])) {
|
||||
$components['curve'] = $ecParams;
|
||||
}
|
||||
|
||||
$components['dA'] = new BigInteger($ecPrivate['privateKey'], 256);
|
||||
$components['curve']->rangeCheck($components['dA']);
|
||||
$components['QA'] = isset($ecPrivate['publicKey']) ?
|
||||
self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
|
||||
$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
$key = parent::load($key, $password);
|
||||
|
||||
$decoded = ASN1::decodeBER($key);
|
||||
if (!$decoded) {
|
||||
throw new RuntimeException('Unable to decode BER');
|
||||
}
|
||||
|
||||
$key = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
|
||||
if (is_array($key)) {
|
||||
return ['curve' => self::loadCurveByParam($key)];
|
||||
}
|
||||
|
||||
$key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
|
||||
if (!is_array($key)) {
|
||||
throw new RuntimeException('Unable to perform ASN1 mapping');
|
||||
}
|
||||
if (!isset($key['parameters'])) {
|
||||
throw new RuntimeException('Key cannot be loaded without parameters');
|
||||
}
|
||||
|
||||
$components = [];
|
||||
$components['curve'] = self::loadCurveByParam($key['parameters']);
|
||||
$components['dA'] = new BigInteger($key['privateKey'], 256);
|
||||
$components['QA'] = isset($ecPrivate['publicKey']) ?
|
||||
self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
|
||||
$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert EC parameters to the appropriate format
|
||||
*/
|
||||
public static function saveParameters(BaseCurve $curve, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
$key = self::encodeParameters($curve, false, $options);
|
||||
|
||||
return "-----BEGIN EC PARAMETERS-----\r\n" .
|
||||
chunk_split(Strings::base64_encode($key), 64) .
|
||||
"-----END EC PARAMETERS-----\r\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, ?string $secret = null, ?string $password = null, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards Curves are not supported');
|
||||
}
|
||||
|
||||
$publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
|
||||
$key = [
|
||||
'version' => 'ecPrivkeyVer1',
|
||||
'privateKey' => $privateKey->toBytes(),
|
||||
'parameters' => new ASN1\Element(self::encodeParameters($curve)),
|
||||
'publicKey' => "\0" . $publicKey,
|
||||
];
|
||||
|
||||
$key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
|
||||
|
||||
return self::wrapPrivateKey($key, 'EC', $password, $options);
|
||||
}
|
||||
}
|
||||
228
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php
Normal file
228
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PKCS8.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted EC Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* Processes keys with the following headers:
|
||||
*
|
||||
* -----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
* -----BEGIN PRIVATE KEY-----
|
||||
* -----BEGIN PUBLIC KEY-----
|
||||
*
|
||||
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
|
||||
* is specific to private keys it's basically creating a DER-encoded wrapper
|
||||
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib3\Crypt\EC\Curves\Ed448;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\File\ASN1;
|
||||
use phpseclib3\File\ASN1\Maps;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* PKCS#8 Formatted EC Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class PKCS8 extends Progenitor
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* OID Name
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448'];
|
||||
|
||||
/**
|
||||
* OID Value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113'];
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
*/
|
||||
public static function load($key, ?string $password = null): array
|
||||
{
|
||||
// initialize_static_variables() is defined in both the trait and the parent class
|
||||
// when it's defined in two places it's the traits one that's called
|
||||
// the parent one is needed, as well, but the parent one is called by other methods
|
||||
// in the parent class as needed and in the context of the parent it's the parent
|
||||
// one that's called
|
||||
self::initialize_static_variables();
|
||||
|
||||
$key = parent::load($key, $password);
|
||||
|
||||
$type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
|
||||
|
||||
switch ($key[$type . 'Algorithm']['algorithm']) {
|
||||
case 'id-Ed25519':
|
||||
case 'id-Ed448':
|
||||
return self::loadEdDSA($key);
|
||||
}
|
||||
|
||||
$decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
|
||||
if (!$decoded) {
|
||||
throw new RuntimeException('Unable to decode BER');
|
||||
}
|
||||
$params = ASN1::asn1map($decoded[0], Maps\ECParameters::MAP);
|
||||
if (!$params) {
|
||||
throw new RuntimeException('Unable to decode the parameters using Maps\ECParameters');
|
||||
}
|
||||
|
||||
$components = [];
|
||||
$components['curve'] = self::loadCurveByParam($params);
|
||||
|
||||
if ($type == 'publicKey') {
|
||||
$components['QA'] = self::extractPoint("\0" . $key['publicKey'], $components['curve']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
$decoded = ASN1::decodeBER($key['privateKey']);
|
||||
if (!$decoded) {
|
||||
throw new RuntimeException('Unable to decode BER');
|
||||
}
|
||||
$key = ASN1::asn1map($decoded[0], Maps\ECPrivateKey::MAP);
|
||||
if (isset($key['parameters']) && $params != $key['parameters']) {
|
||||
throw new RuntimeException('The PKCS8 parameter field does not match the private key parameter field');
|
||||
}
|
||||
|
||||
$components['dA'] = new BigInteger($key['privateKey'], 256);
|
||||
$components['curve']->rangeCheck($components['dA']);
|
||||
$components['QA'] = isset($key['publicKey']) ?
|
||||
self::extractPoint($key['publicKey'], $components['curve']) :
|
||||
$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Break a public or private EdDSA key down into its constituent components
|
||||
*/
|
||||
private static function loadEdDSA(array $key): array
|
||||
{
|
||||
$components = [];
|
||||
|
||||
if (isset($key['privateKey'])) {
|
||||
$components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
|
||||
$expected = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($components['curve']::SIZE);
|
||||
if (substr($key['privateKey'], 0, 2) != $expected) {
|
||||
throw new RuntimeException(
|
||||
'The first two bytes of the ' .
|
||||
$key['privateKeyAlgorithm']['algorithm'] .
|
||||
' private key field should be 0x' . bin2hex($expected)
|
||||
);
|
||||
}
|
||||
$arr = $components['curve']->extractSecret(substr($key['privateKey'], 2));
|
||||
$components['dA'] = $arr['dA'];
|
||||
$components['secret'] = $arr['secret'];
|
||||
}
|
||||
|
||||
if (isset($key['publicKey'])) {
|
||||
if (!isset($components['curve'])) {
|
||||
$components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-Ed25519' ? new Ed25519() : new Ed448();
|
||||
}
|
||||
|
||||
$components['QA'] = self::extractPoint($key['publicKey'], $components['curve']);
|
||||
}
|
||||
|
||||
if (isset($key['privateKey']) && !isset($components['QA'])) {
|
||||
$components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
* @param array $options optional
|
||||
*/
|
||||
public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
return self::wrapPublicKey(
|
||||
$curve->encodePoint($publicKey),
|
||||
null,
|
||||
$curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448'
|
||||
);
|
||||
}
|
||||
|
||||
$params = new ASN1\Element(self::encodeParameters($curve, false, $options));
|
||||
|
||||
$key = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
|
||||
return self::wrapPublicKey($key, $params, 'id-ecPublicKey');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, ?string $secret = null, ?string $password = null, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve) {
|
||||
return self::wrapPrivateKey(
|
||||
chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($curve::SIZE) . $secret,
|
||||
[],
|
||||
null,
|
||||
$password,
|
||||
$curve instanceof Ed25519 ? 'id-Ed25519' : 'id-Ed448'
|
||||
);
|
||||
}
|
||||
|
||||
$publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
|
||||
$params = new ASN1\Element(self::encodeParameters($curve, false, $options));
|
||||
|
||||
$key = [
|
||||
'version' => 'ecPrivkeyVer1',
|
||||
'privateKey' => $privateKey->toBytes(),
|
||||
//'parameters' => $params,
|
||||
'publicKey' => "\0" . $publicKey,
|
||||
];
|
||||
|
||||
$key = ASN1::encodeDER($key, Maps\ECPrivateKey::MAP);
|
||||
|
||||
return self::wrapPrivateKey($key, [], $params, $password, 'id-ecPublicKey', '', $options);
|
||||
}
|
||||
}
|
||||
135
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php
Normal file
135
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/PuTTY.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PuTTY Formatted EC Key Handler
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* PuTTY Formatted EC Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class PuTTY extends Progenitor
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Public Handler
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const PUBLIC_HANDLER = 'phpseclib3\Crypt\EC\Formats\Keys\OpenSSH';
|
||||
|
||||
/**
|
||||
* Supported Key Types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $types = [
|
||||
'ecdsa-sha2-nistp256',
|
||||
'ecdsa-sha2-nistp384',
|
||||
'ecdsa-sha2-nistp521',
|
||||
'ssh-ed25519',
|
||||
];
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|false $password
|
||||
* @return array|false
|
||||
*/
|
||||
public static function load($key, $password)
|
||||
{
|
||||
$components = parent::load($key, $password);
|
||||
if (!isset($components['private'])) {
|
||||
return $components;
|
||||
}
|
||||
|
||||
$private = $components['private'];
|
||||
|
||||
$temp = Strings::base64_encode(Strings::packSSH2('s', $components['type']) . $components['public']);
|
||||
$components = OpenSSH::load($components['type'] . ' ' . $temp . ' ' . $components['comment']);
|
||||
|
||||
if ($components['curve'] instanceof TwistedEdwardsCurve) {
|
||||
if (Strings::shift($private, 4) != "\0\0\0\x20") {
|
||||
throw new RuntimeException('Length of ssh-ed25519 key should be 32');
|
||||
}
|
||||
$arr = $components['curve']->extractSecret($private);
|
||||
$components['dA'] = $arr['dA'];
|
||||
$components['secret'] = $arr['secret'];
|
||||
} else {
|
||||
[$components['dA']] = Strings::unpackSSH2('i', $private);
|
||||
$components['curve']->rangeCheck($components['dA']);
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $privateKey, BaseCurve $curve, array $publicKey, ?string $secret = null, ?string $password = null, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
$public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
|
||||
$name = $public[0];
|
||||
$public = Strings::base64_decode($public[1]);
|
||||
[, $length] = unpack('N', Strings::shift($public, 4));
|
||||
Strings::shift($public, $length);
|
||||
|
||||
// PuTTY pads private keys with a null byte per the following:
|
||||
// https://github.com/github/putty/blob/a3d14d77f566a41fc61dfdc5c2e0e384c9e6ae8b/sshecc.c#L1926
|
||||
if (!$curve instanceof TwistedEdwardsCurve) {
|
||||
$private = $privateKey->toBytes();
|
||||
if (!(strlen($privateKey->toBits()) & 7)) {
|
||||
$private = "\0$private";
|
||||
}
|
||||
}
|
||||
|
||||
$private = $curve instanceof TwistedEdwardsCurve ?
|
||||
Strings::packSSH2('s', $secret) :
|
||||
Strings::packSSH2('s', $private);
|
||||
|
||||
return self::wrapPrivateKey($public, $private, $name, $password, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param FiniteField[] $publicKey
|
||||
*/
|
||||
public static function savePublicKey(BaseCurve $curve, array $publicKey): string
|
||||
{
|
||||
$public = explode(' ', OpenSSH::savePublicKey($curve, $publicKey));
|
||||
$type = $public[0];
|
||||
$public = Strings::base64_decode($public[1]);
|
||||
[, $length] = unpack('N', Strings::shift($public, 4));
|
||||
Strings::shift($public, $length);
|
||||
|
||||
return self::wrapPublicKey($public, $type);
|
||||
}
|
||||
}
|
||||
471
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/XML.php
Normal file
471
qa-tool/htdocs/oidc/phpseclib/Crypt/EC/Formats/Keys/XML.php
Normal file
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* XML Formatted EC Key Handler
|
||||
*
|
||||
* More info:
|
||||
*
|
||||
* https://www.w3.org/TR/xmldsig-core/#sec-ECKeyValue
|
||||
* http://en.wikipedia.org/wiki/XML_Signature
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Common\Functions\Strings;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
|
||||
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
|
||||
use phpseclib3\Exception\BadConfigurationException;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnexpectedValueException;
|
||||
use phpseclib3\Exception\UnsupportedCurveException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* XML Formatted EC Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class XML
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Default namespace
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $namespace;
|
||||
|
||||
/**
|
||||
* Flag for using RFC4050 syntax
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $rfc4050 = false;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*
|
||||
* @param string|array $key
|
||||
*/
|
||||
public static function load($key, ?string $password = null): array
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if (!Strings::is_stringable($key)) {
|
||||
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
|
||||
}
|
||||
|
||||
if (!class_exists('DOMDocument')) {
|
||||
throw new BadConfigurationException('The dom extension is not setup correctly on this system');
|
||||
}
|
||||
|
||||
$use_errors = libxml_use_internal_errors(true);
|
||||
|
||||
$temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#');
|
||||
if ($temp) {
|
||||
$key = $temp;
|
||||
}
|
||||
|
||||
$temp = self::isolateNamespace($key, 'http://www.w3.org/2001/04/xmldsig-more#');
|
||||
if ($temp) {
|
||||
$key = $temp;
|
||||
}
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
if (substr($key, 0, 5) != '<?xml') {
|
||||
$key = '<xml>' . $key . '</xml>';
|
||||
}
|
||||
|
||||
if (!$dom->loadXML($key)) {
|
||||
libxml_use_internal_errors($use_errors);
|
||||
throw new UnexpectedValueException('Key does not appear to contain XML');
|
||||
}
|
||||
$xpath = new \DOMXPath($dom);
|
||||
libxml_use_internal_errors($use_errors);
|
||||
$curve = self::loadCurveByParam($xpath);
|
||||
|
||||
$pubkey = self::query($xpath, 'publickey', 'Public Key is not present');
|
||||
|
||||
$QA = self::query($xpath, 'ecdsakeyvalue')->length ?
|
||||
self::extractPointRFC4050($xpath, $curve) :
|
||||
self::extractPoint("\0" . $pubkey, $curve);
|
||||
|
||||
libxml_use_internal_errors($use_errors);
|
||||
|
||||
return compact('curve', 'QA');
|
||||
}
|
||||
|
||||
/**
|
||||
* Case-insensitive xpath query
|
||||
*
|
||||
* @param string|null $error optional
|
||||
* @param bool $decode optional
|
||||
* @return \DOMNodeList|string
|
||||
*/
|
||||
private static function query(\DOMXPath $xpath, string $name, ?string $error = null, bool $decode = true)
|
||||
{
|
||||
$query = '/';
|
||||
$names = explode('/', $name);
|
||||
foreach ($names as $name) {
|
||||
$query .= "/*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$name']";
|
||||
}
|
||||
$result = $xpath->query($query);
|
||||
if (!isset($error)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!$result->length) {
|
||||
throw new RuntimeException($error);
|
||||
}
|
||||
return $decode ? self::decodeValue($result->item(0)->textContent) : $result->item(0)->textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first element in the relevant namespace, strips the namespacing and returns the XML for that element.
|
||||
*/
|
||||
private static function isolateNamespace(string $xml, string $ns)
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
if (!$dom->loadXML($xml)) {
|
||||
return false;
|
||||
}
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$nodes = $xpath->query("//*[namespace::*[.='$ns'] and not(../namespace::*[.='$ns'])]");
|
||||
if (!$nodes->length) {
|
||||
return false;
|
||||
}
|
||||
$node = $nodes->item(0);
|
||||
$ns_name = $node->lookupPrefix($ns);
|
||||
if ($ns_name) {
|
||||
$node->removeAttributeNS($ns, $ns_name);
|
||||
}
|
||||
return $dom->saveXML($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the value
|
||||
*/
|
||||
private static function decodeValue(string $value): string
|
||||
{
|
||||
return Strings::base64_decode(str_replace(["\r", "\n", ' ', "\t"], '', $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract points from an XML document
|
||||
*
|
||||
* @return object[]
|
||||
*/
|
||||
private static function extractPointRFC4050(\DOMXPath $xpath, BaseCurve $curve): array
|
||||
{
|
||||
$x = self::query($xpath, 'publickey/x');
|
||||
$y = self::query($xpath, 'publickey/y');
|
||||
if (!$x->length || !$x->item(0)->hasAttribute('Value')) {
|
||||
throw new RuntimeException('Public Key / X coordinate not found');
|
||||
}
|
||||
if (!$y->length || !$y->item(0)->hasAttribute('Value')) {
|
||||
throw new RuntimeException('Public Key / Y coordinate not found');
|
||||
}
|
||||
$point = [
|
||||
$curve->convertInteger(new BigInteger($x->item(0)->getAttribute('Value'))),
|
||||
$curve->convertInteger(new BigInteger($y->item(0)->getAttribute('Value'))),
|
||||
];
|
||||
if (!$curve->verifyPoint($point)) {
|
||||
throw new RuntimeException('Unable to verify that point exists on curve');
|
||||
}
|
||||
return $point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
|
||||
* on the curve parameters
|
||||
*
|
||||
* @return BaseCurve|false
|
||||
*/
|
||||
private static function loadCurveByParam(\DOMXPath $xpath)
|
||||
{
|
||||
$namedCurve = self::query($xpath, 'namedcurve');
|
||||
if ($namedCurve->length == 1) {
|
||||
$oid = $namedCurve->item(0)->getAttribute('URN');
|
||||
$oid = preg_replace('#[^\d.]#', '', $oid);
|
||||
$name = array_search($oid, self::$curveOIDs);
|
||||
if ($name === false) {
|
||||
throw new UnsupportedCurveException('Curve with OID of ' . $oid . ' is not supported');
|
||||
}
|
||||
|
||||
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $name;
|
||||
if (!class_exists($curve)) {
|
||||
throw new UnsupportedCurveException('Named Curve of ' . $name . ' is not supported');
|
||||
}
|
||||
return new $curve();
|
||||
}
|
||||
|
||||
$params = self::query($xpath, 'explicitparams');
|
||||
if ($params->length) {
|
||||
return self::loadCurveByParamRFC4050($xpath);
|
||||
}
|
||||
|
||||
$params = self::query($xpath, 'ecparameters');
|
||||
if (!$params->length) {
|
||||
throw new RuntimeException('No parameters are present');
|
||||
}
|
||||
|
||||
$fieldTypes = [
|
||||
'prime-field' => ['fieldid/prime/p'],
|
||||
'gnb' => ['fieldid/gnb/m'],
|
||||
'tnb' => ['fieldid/tnb/k'],
|
||||
'pnb' => ['fieldid/pnb/k1', 'fieldid/pnb/k2', 'fieldid/pnb/k3'],
|
||||
'unknown' => [],
|
||||
];
|
||||
|
||||
foreach ($fieldTypes as $type => $queries) {
|
||||
foreach ($queries as $query) {
|
||||
$result = self::query($xpath, $query);
|
||||
if (!$result->length) {
|
||||
continue 2;
|
||||
}
|
||||
$param = preg_replace('#.*/#', '', $query);
|
||||
$$param = self::decodeValue($result->item(0)->textContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$a = self::query($xpath, 'curve/a', 'A coefficient is not present');
|
||||
$b = self::query($xpath, 'curve/b', 'B coefficient is not present');
|
||||
$base = self::query($xpath, 'base', 'Base point is not present');
|
||||
$order = self::query($xpath, 'order', 'Order is not present');
|
||||
|
||||
switch ($type) {
|
||||
case 'prime-field':
|
||||
$curve = new PrimeCurve();
|
||||
$curve->setModulo(new BigInteger($p, 256));
|
||||
$curve->setCoefficients(
|
||||
new BigInteger($a, 256),
|
||||
new BigInteger($b, 256)
|
||||
);
|
||||
$point = self::extractPoint("\0" . $base, $curve);
|
||||
$curve->setBasePoint(...$point);
|
||||
$curve->setOrder(new BigInteger($order, 256));
|
||||
return $curve;
|
||||
case 'gnb':
|
||||
case 'tnb':
|
||||
case 'pnb':
|
||||
default:
|
||||
throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of \phpseclib3\Crypt\EC\BaseCurves\Base based
|
||||
* on the curve parameters
|
||||
*
|
||||
* @return BaseCurve|false
|
||||
*/
|
||||
private static function loadCurveByParamRFC4050(\DOMXPath $xpath)
|
||||
{
|
||||
$fieldTypes = [
|
||||
'prime-field' => ['primefieldparamstype/p'],
|
||||
'unknown' => [],
|
||||
];
|
||||
|
||||
foreach ($fieldTypes as $type => $queries) {
|
||||
foreach ($queries as $query) {
|
||||
$result = self::query($xpath, $query);
|
||||
if (!$result->length) {
|
||||
continue 2;
|
||||
}
|
||||
$param = preg_replace('#.*/#', '', $query);
|
||||
$$param = $result->item(0)->textContent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$a = self::query($xpath, 'curveparamstype/a', 'A coefficient is not present', false);
|
||||
$b = self::query($xpath, 'curveparamstype/b', 'B coefficient is not present', false);
|
||||
$x = self::query($xpath, 'basepointparams/basepoint/ecpointtype/x', 'Base Point X is not present', false);
|
||||
$y = self::query($xpath, 'basepointparams/basepoint/ecpointtype/y', 'Base Point Y is not present', false);
|
||||
$order = self::query($xpath, 'order', 'Order is not present', false);
|
||||
|
||||
switch ($type) {
|
||||
case 'prime-field':
|
||||
$curve = new PrimeCurve();
|
||||
|
||||
$p = str_replace(["\r", "\n", ' ', "\t"], '', $p);
|
||||
$curve->setModulo(new BigInteger($p));
|
||||
|
||||
$a = str_replace(["\r", "\n", ' ', "\t"], '', $a);
|
||||
$b = str_replace(["\r", "\n", ' ', "\t"], '', $b);
|
||||
$curve->setCoefficients(
|
||||
new BigInteger($a),
|
||||
new BigInteger($b)
|
||||
);
|
||||
|
||||
$x = str_replace(["\r", "\n", ' ', "\t"], '', $x);
|
||||
$y = str_replace(["\r", "\n", ' ', "\t"], '', $y);
|
||||
$curve->setBasePoint(
|
||||
new BigInteger($x),
|
||||
new BigInteger($y)
|
||||
);
|
||||
|
||||
$order = str_replace(["\r", "\n", ' ', "\t"], '', $order);
|
||||
$curve->setOrder(new BigInteger($order));
|
||||
return $curve;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Field Type of ' . $type . ' is not supported');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the namespace. dsig11 is the most common one.
|
||||
*
|
||||
* Set to null to unset. Used only for creating public keys.
|
||||
*/
|
||||
public static function setNamespace(string $namespace): void
|
||||
{
|
||||
self::$namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the XML syntax specified in https://tools.ietf.org/html/rfc4050
|
||||
*/
|
||||
public static function enableRFC4050Syntax(): void
|
||||
{
|
||||
self::$rfc4050 = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the XML syntax specified in https://www.w3.org/TR/xmldsig-core/#sec-ECParameters
|
||||
*/
|
||||
public static function disableRFC4050Syntax(): void
|
||||
{
|
||||
self::$rfc4050 = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
* @param array $options optional
|
||||
*/
|
||||
public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = []): string
|
||||
{
|
||||
self::initialize_static_variables();
|
||||
|
||||
if ($curve instanceof TwistedEdwardsCurve || $curve instanceof MontgomeryCurve) {
|
||||
throw new UnsupportedCurveException('TwistedEdwards and Montgomery Curves are not supported');
|
||||
}
|
||||
|
||||
if (empty(static::$namespace)) {
|
||||
$pre = $post = '';
|
||||
} else {
|
||||
$pre = static::$namespace . ':';
|
||||
$post = ':' . static::$namespace;
|
||||
}
|
||||
|
||||
if (self::$rfc4050) {
|
||||
return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2001/04/xmldsig-more#">' . "\r\n" .
|
||||
self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
|
||||
'<' . $pre . 'PublicKey>' . "\r\n" .
|
||||
'<' . $pre . 'X Value="' . $publicKey[0] . '" />' . "\r\n" .
|
||||
'<' . $pre . 'Y Value="' . $publicKey[1] . '" />' . "\r\n" .
|
||||
'</' . $pre . 'PublicKey>' . "\r\n" .
|
||||
'</' . $pre . 'ECDSAKeyValue>';
|
||||
}
|
||||
|
||||
$publicKey = "\4" . $publicKey[0]->toBytes() . $publicKey[1]->toBytes();
|
||||
|
||||
return '<' . $pre . 'ECDSAKeyValue xmlns' . $post . '="http://www.w3.org/2009/xmldsig11#">' . "\r\n" .
|
||||
self::encodeXMLParameters($curve, $pre, $options) . "\r\n" .
|
||||
'<' . $pre . 'PublicKey>' . Strings::base64_encode($publicKey) . '</' . $pre . 'PublicKey>' . "\r\n" .
|
||||
'</' . $pre . 'ECDSAKeyValue>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Parameters
|
||||
*
|
||||
* @param array $options optional
|
||||
* @return string|false
|
||||
*/
|
||||
private static function encodeXMLParameters(BaseCurve $curve, string $pre, array $options = [])
|
||||
{
|
||||
$result = self::encodeParameters($curve, true, $options);
|
||||
|
||||
if (isset($result['namedCurve'])) {
|
||||
$namedCurve = '<' . $pre . 'NamedCurve URI="urn:oid:' . self::$curveOIDs[$result['namedCurve']] . '" />';
|
||||
return self::$rfc4050 ?
|
||||
'<DomainParameters>' . str_replace('URI', 'URN', $namedCurve) . '</DomainParameters>' :
|
||||
$namedCurve;
|
||||
}
|
||||
|
||||
if (self::$rfc4050) {
|
||||
$xml = '<' . $pre . 'ExplicitParams>' . "\r\n" .
|
||||
'<' . $pre . 'FieldParams>' . "\r\n";
|
||||
$temp = $result['specifiedCurve'];
|
||||
switch ($temp['fieldID']['fieldType']) {
|
||||
case 'prime-field':
|
||||
$xml .= '<' . $pre . 'PrimeFieldParamsType>' . "\r\n" .
|
||||
'<' . $pre . 'P>' . $temp['fieldID']['parameters'] . '</' . $pre . 'P>' . "\r\n" .
|
||||
'</' . $pre . 'PrimeFieldParamsType>' . "\r\n";
|
||||
$a = $curve->getA();
|
||||
$b = $curve->getB();
|
||||
[$x, $y] = $curve->getBasePoint();
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
|
||||
}
|
||||
$xml .= '</' . $pre . 'FieldParams>' . "\r\n" .
|
||||
'<' . $pre . 'CurveParamsType>' . "\r\n" .
|
||||
'<' . $pre . 'A>' . $a . '</' . $pre . 'A>' . "\r\n" .
|
||||
'<' . $pre . 'B>' . $b . '</' . $pre . 'B>' . "\r\n" .
|
||||
'</' . $pre . 'CurveParamsType>' . "\r\n" .
|
||||
'<' . $pre . 'BasePointParams>' . "\r\n" .
|
||||
'<' . $pre . 'BasePoint>' . "\r\n" .
|
||||
'<' . $pre . 'ECPointType>' . "\r\n" .
|
||||
'<' . $pre . 'X>' . $x . '</' . $pre . 'X>' . "\r\n" .
|
||||
'<' . $pre . 'Y>' . $y . '</' . $pre . 'Y>' . "\r\n" .
|
||||
'</' . $pre . 'ECPointType>' . "\r\n" .
|
||||
'</' . $pre . 'BasePoint>' . "\r\n" .
|
||||
'<' . $pre . 'Order>' . $curve->getOrder() . '</' . $pre . 'Order>' . "\r\n" .
|
||||
'</' . $pre . 'BasePointParams>' . "\r\n" .
|
||||
'</' . $pre . 'ExplicitParams>' . "\r\n";
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
if (isset($result['specifiedCurve'])) {
|
||||
$xml = '<' . $pre . 'ECParameters>' . "\r\n" .
|
||||
'<' . $pre . 'FieldID>' . "\r\n";
|
||||
$temp = $result['specifiedCurve'];
|
||||
switch ($temp['fieldID']['fieldType']) {
|
||||
case 'prime-field':
|
||||
$xml .= '<' . $pre . 'Prime>' . "\r\n" .
|
||||
'<' . $pre . 'P>' . Strings::base64_encode($temp['fieldID']['parameters']->toBytes()) . '</' . $pre . 'P>' . "\r\n" .
|
||||
'</' . $pre . 'Prime>' . "\r\n" ;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedCurveException('Field Type of ' . $temp['fieldID']['fieldType'] . ' is not supported');
|
||||
}
|
||||
$xml .= '</' . $pre . 'FieldID>' . "\r\n" .
|
||||
'<' . $pre . 'Curve>' . "\r\n" .
|
||||
'<' . $pre . 'A>' . Strings::base64_encode($temp['curve']['a']) . '</' . $pre . 'A>' . "\r\n" .
|
||||
'<' . $pre . 'B>' . Strings::base64_encode($temp['curve']['b']) . '</' . $pre . 'B>' . "\r\n" .
|
||||
'</' . $pre . 'Curve>' . "\r\n" .
|
||||
'<' . $pre . 'Base>' . Strings::base64_encode($temp['base']) . '</' . $pre . 'Base>' . "\r\n" .
|
||||
'<' . $pre . 'Order>' . Strings::base64_encode($temp['order']) . '</' . $pre . 'Order>' . "\r\n" .
|
||||
'</' . $pre . 'ECParameters>';
|
||||
return $xml;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* libsodium Key Handler
|
||||
*
|
||||
* Different NaCl implementations store the key differently.
|
||||
* https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/ elaborates.
|
||||
* libsodium appears to use the same format as SUPERCOP.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2015 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://phpseclib.sourceforge.net
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Crypt\EC\Formats\Keys;
|
||||
|
||||
use phpseclib3\Crypt\EC\Curves\Ed25519;
|
||||
use phpseclib3\Exception\RuntimeException;
|
||||
use phpseclib3\Exception\UnsupportedFormatException;
|
||||
use phpseclib3\Math\BigInteger;
|
||||
use phpseclib3\Math\Common\FiniteField\Integer;
|
||||
|
||||
/**
|
||||
* libsodium Key Handler
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class libsodium
|
||||
{
|
||||
use Common;
|
||||
|
||||
/**
|
||||
* Is invisible flag
|
||||
*/
|
||||
public const IS_INVISIBLE = true;
|
||||
|
||||
/**
|
||||
* Break a public or private key down into its constituent components
|
||||
*/
|
||||
public static function load(string $key, ?string $password = null): array
|
||||
{
|
||||
switch (strlen($key)) {
|
||||
case 32:
|
||||
$public = $key;
|
||||
break;
|
||||
case 64:
|
||||
$private = substr($key, 0, 32);
|
||||
$public = substr($key, -32);
|
||||
break;
|
||||
case 96:
|
||||
$public = substr($key, -32);
|
||||
if (substr($key, 32, 32) != $public) {
|
||||
throw new RuntimeException('Keys with 96 bytes should have the 2nd and 3rd set of 32 bytes match');
|
||||
}
|
||||
$private = substr($key, 0, 32);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('libsodium keys need to either be 32 bytes long, 64 bytes long or 96 bytes long');
|
||||
}
|
||||
|
||||
$curve = new Ed25519();
|
||||
$components = ['curve' => $curve];
|
||||
if (isset($private)) {
|
||||
$arr = $curve->extractSecret($private);
|
||||
$components['dA'] = $arr['dA'];
|
||||
$components['secret'] = $arr['secret'];
|
||||
}
|
||||
$components['QA'] = isset($public) ?
|
||||
self::extractPoint($public, $curve) :
|
||||
$curve->multiplyPoint($curve->getBasePoint(), $components['dA']);
|
||||
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an EC public key to the appropriate format
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePublicKey(Ed25519 $curve, array $publicKey): string
|
||||
{
|
||||
return $curve->encodePoint($publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a private key to the appropriate format.
|
||||
*
|
||||
* @param Integer[] $publicKey
|
||||
*/
|
||||
public static function savePrivateKey(BigInteger $privateKey, Ed25519 $curve, array $publicKey, ?string $secret = null, ?string $password = null): string
|
||||
{
|
||||
if (!isset($secret)) {
|
||||
throw new RuntimeException('Private Key does not have a secret set');
|
||||
}
|
||||
if (strlen($secret) != 32) {
|
||||
throw new RuntimeException('Private Key secret is not of the correct length');
|
||||
}
|
||||
if (!empty($password) && is_string($password)) {
|
||||
throw new UnsupportedFormatException('libsodium private keys do not support encryption');
|
||||
}
|
||||
return $secret . $curve->encodePoint($publicKey);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user