first commit
This commit is contained in:
163
libraries/vendor/web-token/jwt-core/Util/BigInteger.php
vendored
Normal file
163
libraries/vendor/web-token/jwt-core/Util/BigInteger.php
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use Brick\Math\BigInteger as BrickBigInteger;
|
||||
use function chr;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BigInteger
|
||||
{
|
||||
private function __construct(
|
||||
private readonly BrickBigInteger $value
|
||||
) {
|
||||
}
|
||||
|
||||
public static function createFromBinaryString(string $value): self
|
||||
{
|
||||
$res = unpack('H*', $value);
|
||||
if ($res === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the value');
|
||||
}
|
||||
$data = current($res);
|
||||
|
||||
return new self(BrickBigInteger::fromBase($data, 16));
|
||||
}
|
||||
|
||||
public static function createFromDecimal(int $value): self
|
||||
{
|
||||
return new self(BrickBigInteger::of($value));
|
||||
}
|
||||
|
||||
public static function createFromBigInteger(BrickBigInteger $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BigInteger to a binary string.
|
||||
*/
|
||||
public function toBytes(): string
|
||||
{
|
||||
if ($this->value->isEqualTo(BrickBigInteger::zero())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$temp = $this->value->toBase(16);
|
||||
$temp = 0 !== (mb_strlen($temp, '8bit') & 1) ? '0' . $temp : $temp;
|
||||
$temp = hex2bin($temp);
|
||||
if ($temp === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the value into bytes');
|
||||
}
|
||||
|
||||
return ltrim($temp, chr(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two BigIntegers.
|
||||
*/
|
||||
public function add(self $y): self
|
||||
{
|
||||
$value = $this->value->plus($y->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts two BigIntegers.
|
||||
*/
|
||||
public function subtract(self $y): self
|
||||
{
|
||||
$value = $this->value->minus($y->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies two BigIntegers.
|
||||
*/
|
||||
public function multiply(self $x): self
|
||||
{
|
||||
$value = $this->value->multipliedBy($x->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides two BigIntegers.
|
||||
*/
|
||||
public function divide(self $x): self
|
||||
{
|
||||
$value = $this->value->dividedBy($x->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation.
|
||||
*/
|
||||
public function modPow(self $e, self $n): self
|
||||
{
|
||||
$value = $this->value->modPow($e->value, $n->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs modular exponentiation.
|
||||
*/
|
||||
public function mod(self $d): self
|
||||
{
|
||||
$value = $this->value->mod($d->value);
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public function modInverse(self $m): self
|
||||
{
|
||||
return new self($this->value->modInverse($m->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two numbers.
|
||||
*/
|
||||
public function compare(self $y): int
|
||||
{
|
||||
return $this->value->compareTo($y->value);
|
||||
}
|
||||
|
||||
public function equals(self $y): bool
|
||||
{
|
||||
return $this->value->isEqualTo($y->value);
|
||||
}
|
||||
|
||||
public static function random(self $y): self
|
||||
{
|
||||
return new self(BrickBigInteger::randomRange(0, $y->value));
|
||||
}
|
||||
|
||||
public function gcd(self $y): self
|
||||
{
|
||||
return new self($this->value->gcd($y->value));
|
||||
}
|
||||
|
||||
public function lowerThan(self $y): bool
|
||||
{
|
||||
return $this->value->isLessThan($y->value);
|
||||
}
|
||||
|
||||
public function isEven(): bool
|
||||
{
|
||||
return $this->value->isEven();
|
||||
}
|
||||
|
||||
public function get(): BrickBigInteger
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
328
libraries/vendor/web-token/jwt-core/Util/ECKey.php
vendored
Normal file
328
libraries/vendor/web-token/jwt-core/Util/ECKey.php
vendored
Normal file
@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use function extension_loaded;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
use const OPENSSL_KEYTYPE_EC;
|
||||
use ParagonIE\ConstantTime\Base64UrlSafe;
|
||||
use const PHP_EOL;
|
||||
use RuntimeException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ECKey
|
||||
{
|
||||
public static function convertToPEM(JWK $jwk): string
|
||||
{
|
||||
if ($jwk->has('d')) {
|
||||
return self::convertPrivateKeyToPEM($jwk);
|
||||
}
|
||||
|
||||
return self::convertPublicKeyToPEM($jwk);
|
||||
}
|
||||
|
||||
public static function convertPublicKeyToPEM(JWK $jwk): string
|
||||
{
|
||||
$der = match ($jwk->get('crv')) {
|
||||
'P-256' => self::p256PublicKey(),
|
||||
'secp256k1' => self::p256KPublicKey(),
|
||||
'P-384' => self::p384PublicKey(),
|
||||
'P-521' => self::p521PublicKey(),
|
||||
default => throw new InvalidArgumentException('Unsupported curve.'),
|
||||
};
|
||||
$der .= self::getKey($jwk);
|
||||
$pem = '-----BEGIN PUBLIC KEY-----' . PHP_EOL;
|
||||
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
|
||||
|
||||
return $pem . ('-----END PUBLIC KEY-----' . PHP_EOL);
|
||||
}
|
||||
|
||||
public static function convertPrivateKeyToPEM(JWK $jwk): string
|
||||
{
|
||||
$der = match ($jwk->get('crv')) {
|
||||
'P-256' => self::p256PrivateKey($jwk),
|
||||
'secp256k1' => self::p256KPrivateKey($jwk),
|
||||
'P-384' => self::p384PrivateKey($jwk),
|
||||
'P-521' => self::p521PrivateKey($jwk),
|
||||
default => throw new InvalidArgumentException('Unsupported curve.'),
|
||||
};
|
||||
$der .= self::getKey($jwk);
|
||||
$pem = '-----BEGIN EC PRIVATE KEY-----' . PHP_EOL;
|
||||
$pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
|
||||
|
||||
return $pem . ('-----END EC PRIVATE KEY-----' . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a EC key with the given curve and additional values.
|
||||
*
|
||||
* @param string $curve The curve
|
||||
* @param array $values values to configure the key
|
||||
*/
|
||||
public static function createECKey(string $curve, array $values = []): JWK
|
||||
{
|
||||
$jwk = self::createECKeyUsingOpenSSL($curve);
|
||||
$values = array_merge($values, $jwk);
|
||||
|
||||
return new JWK($values);
|
||||
}
|
||||
|
||||
private static function getNistCurveSize(string $curve): int
|
||||
{
|
||||
return match ($curve) {
|
||||
'P-256', 'secp256k1' => 256,
|
||||
'P-384' => 384,
|
||||
'P-521' => 521,
|
||||
default => throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)),
|
||||
};
|
||||
}
|
||||
|
||||
private static function createECKeyUsingOpenSSL(string $curve): array
|
||||
{
|
||||
if (! extension_loaded('openssl')) {
|
||||
throw new RuntimeException('Please install the OpenSSL extension');
|
||||
}
|
||||
$key = openssl_pkey_new([
|
||||
'curve_name' => self::getOpensslCurveName($curve),
|
||||
'private_key_type' => OPENSSL_KEYTYPE_EC,
|
||||
]);
|
||||
if ($key === false) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$result = openssl_pkey_export($key, $out);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$res = openssl_pkey_get_private($out);
|
||||
if ($res === false) {
|
||||
throw new RuntimeException('Unable to create the key');
|
||||
}
|
||||
$details = openssl_pkey_get_details($res);
|
||||
if ($details === false) {
|
||||
throw new InvalidArgumentException('Unable to get the key details');
|
||||
}
|
||||
$nistCurveSize = self::getNistCurveSize($curve);
|
||||
|
||||
return [
|
||||
'kty' => 'EC',
|
||||
'crv' => $curve,
|
||||
'd' => Base64UrlSafe::encodeUnpadded(
|
||||
str_pad((string) $details['ec']['d'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)
|
||||
),
|
||||
'x' => Base64UrlSafe::encodeUnpadded(
|
||||
str_pad((string) $details['ec']['x'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)
|
||||
),
|
||||
'y' => Base64UrlSafe::encodeUnpadded(
|
||||
str_pad((string) $details['ec']['y'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private static function getOpensslCurveName(string $curve): string
|
||||
{
|
||||
return match ($curve) {
|
||||
'P-256' => 'prime256v1',
|
||||
'secp256k1' => 'secp256k1',
|
||||
'P-384' => 'secp384r1',
|
||||
'P-521' => 'secp521r1',
|
||||
default => throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve)),
|
||||
};
|
||||
}
|
||||
|
||||
private static function p256PublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'3059' // SEQUENCE, length 89
|
||||
. '3013' // SEQUENCE, length 19
|
||||
. '0607' // OID, length 7
|
||||
. '2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
. '0608' // OID, length 8
|
||||
. '2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve
|
||||
. '0342' // BIT STRING, length 66
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p256KPublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'3056' // SEQUENCE, length 86
|
||||
. '3010' // SEQUENCE, length 16
|
||||
. '0607' // OID, length 7
|
||||
. '2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
. '0605' // OID, length 8
|
||||
. '2B8104000A' // 1.3.132.0.10 secp256k1
|
||||
. '0342' // BIT STRING, length 66
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p384PublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'3076' // SEQUENCE, length 118
|
||||
. '3010' // SEQUENCE, length 16
|
||||
. '0607' // OID, length 7
|
||||
. '2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
. '0605' // OID, length 5
|
||||
. '2b81040022' // 1.3.132.0.34 = P-384 Curve
|
||||
. '0362' // BIT STRING, length 98
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p521PublicKey(): string
|
||||
{
|
||||
return pack(
|
||||
'H*',
|
||||
'30819b' // SEQUENCE, length 154
|
||||
. '3010' // SEQUENCE, length 16
|
||||
. '0607' // OID, length 7
|
||||
. '2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
|
||||
. '0605' // OID, length 5
|
||||
. '2b81040023' // 1.3.132.0.35 = P-521 Curve
|
||||
. '038186' // BIT STRING, length 134
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p256PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = $jwk->get('d');
|
||||
if (! is_string($d)) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
$d = unpack('H*', str_pad(Base64UrlSafe::decode($d), 32, "\0", STR_PAD_LEFT));
|
||||
if (! is_array($d) || ! isset($d[1])) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3077' // SEQUENCE, length 87+length($d)=32
|
||||
. '020101' // INTEGER, 1
|
||||
. '0420' // OCTET STRING, length($d) = 32
|
||||
. $d[1]
|
||||
. 'a00a' // TAGGED OBJECT #0, length 10
|
||||
. '0608' // OID, length 8
|
||||
. '2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve
|
||||
. 'a144' // TAGGED OBJECT #1, length 68
|
||||
. '0342' // BIT STRING, length 66
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p256KPrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = $jwk->get('d');
|
||||
if (! is_string($d)) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
$d = unpack('H*', str_pad(Base64UrlSafe::decode($d), 32, "\0", STR_PAD_LEFT));
|
||||
if (! is_array($d) || ! isset($d[1])) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3074' // SEQUENCE, length 84+length($d)=32
|
||||
. '020101' // INTEGER, 1
|
||||
. '0420' // OCTET STRING, length($d) = 32
|
||||
. $d[1]
|
||||
. 'a007' // TAGGED OBJECT #0, length 7
|
||||
. '0605' // OID, length 5
|
||||
. '2b8104000a' // 1.3.132.0.10 secp256k1
|
||||
. 'a144' // TAGGED OBJECT #1, length 68
|
||||
. '0342' // BIT STRING, length 66
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p384PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = $jwk->get('d');
|
||||
if (! is_string($d)) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
$d = unpack('H*', str_pad(Base64UrlSafe::decode($d), 48, "\0", STR_PAD_LEFT));
|
||||
if (! is_array($d) || ! isset($d[1])) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3081a4' // SEQUENCE, length 116 + length($d)=48
|
||||
. '020101' // INTEGER, 1
|
||||
. '0430' // OCTET STRING, length($d) = 30
|
||||
. $d[1]
|
||||
. 'a007' // TAGGED OBJECT #0, length 7
|
||||
. '0605' // OID, length 5
|
||||
. '2b81040022' // 1.3.132.0.34 = P-384 Curve
|
||||
. 'a164' // TAGGED OBJECT #1, length 100
|
||||
. '0362' // BIT STRING, length 98
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function p521PrivateKey(JWK $jwk): string
|
||||
{
|
||||
$d = $jwk->get('d');
|
||||
if (! is_string($d)) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
$d = unpack('H*', str_pad(Base64UrlSafe::decode($d), 66, "\0", STR_PAD_LEFT));
|
||||
if (! is_array($d) || ! isset($d[1])) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
|
||||
return pack(
|
||||
'H*',
|
||||
'3081dc' // SEQUENCE, length 154 + length($d)=66
|
||||
. '020101' // INTEGER, 1
|
||||
. '0442' // OCTET STRING, length(d) = 66
|
||||
. $d[1]
|
||||
. 'a007' // TAGGED OBJECT #0, length 7
|
||||
. '0605' // OID, length 5
|
||||
. '2b81040023' // 1.3.132.0.35 = P-521 Curve
|
||||
. 'a18189' // TAGGED OBJECT #1, length 137
|
||||
. '038186' // BIT STRING, length 134
|
||||
. '00' // prepend with NUL - pubkey will follow
|
||||
);
|
||||
}
|
||||
|
||||
private static function getKey(JWK $jwk): string
|
||||
{
|
||||
$crv = $jwk->get('crv');
|
||||
if (! is_string($crv)) {
|
||||
throw new InvalidArgumentException('Unable to get the curve');
|
||||
}
|
||||
$nistCurveSize = self::getNistCurveSize($crv);
|
||||
$length = (int) ceil($nistCurveSize / 8);
|
||||
$x = $jwk->get('x');
|
||||
if (! is_string($x)) {
|
||||
throw new InvalidArgumentException('Unable to get the public key');
|
||||
}
|
||||
$y = $jwk->get('y');
|
||||
if (! is_string($y)) {
|
||||
throw new InvalidArgumentException('Unable to get the public key');
|
||||
}
|
||||
$binX = ltrim(Base64UrlSafe::decode($x), "\0");
|
||||
$binY = ltrim(Base64UrlSafe::decode($y), "\0");
|
||||
|
||||
return "\04"
|
||||
. str_pad($binX, $length, "\0", STR_PAD_LEFT)
|
||||
. str_pad($binY, $length, "\0", STR_PAD_LEFT)
|
||||
;
|
||||
}
|
||||
}
|
||||
131
libraries/vendor/web-token/jwt-core/Util/ECSignature.php
vendored
Normal file
131
libraries/vendor/web-token/jwt-core/Util/ECSignature.php
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ECSignature
|
||||
{
|
||||
private const ASN1_SEQUENCE = '30';
|
||||
|
||||
private const ASN1_INTEGER = '02';
|
||||
|
||||
private const ASN1_MAX_SINGLE_BYTE = 128;
|
||||
|
||||
private const ASN1_LENGTH_2BYTES = '81';
|
||||
|
||||
private const ASN1_BIG_INTEGER_LIMIT = '7f';
|
||||
|
||||
private const ASN1_NEGATIVE_INTEGER = '00';
|
||||
|
||||
private const BYTE_SIZE = 2;
|
||||
|
||||
public static function toAsn1(string $signature, int $length): string
|
||||
{
|
||||
$signature = bin2hex($signature);
|
||||
|
||||
if (self::octetLength($signature) !== $length) {
|
||||
throw new InvalidArgumentException('Invalid signature length.');
|
||||
}
|
||||
|
||||
$pointR = self::preparePositiveInteger(mb_substr($signature, 0, $length, '8bit'));
|
||||
$pointS = self::preparePositiveInteger(mb_substr($signature, $length, null, '8bit'));
|
||||
|
||||
$lengthR = self::octetLength($pointR);
|
||||
$lengthS = self::octetLength($pointS);
|
||||
|
||||
$totalLength = $lengthR + $lengthS + self::BYTE_SIZE + self::BYTE_SIZE;
|
||||
$lengthPrefix = $totalLength > self::ASN1_MAX_SINGLE_BYTE ? self::ASN1_LENGTH_2BYTES : '';
|
||||
|
||||
$bin = hex2bin(
|
||||
self::ASN1_SEQUENCE
|
||||
. $lengthPrefix . dechex($totalLength)
|
||||
. self::ASN1_INTEGER . dechex($lengthR) . $pointR
|
||||
. self::ASN1_INTEGER . dechex($lengthS) . $pointS
|
||||
);
|
||||
if (! is_string($bin)) {
|
||||
throw new InvalidArgumentException('Unable to parse the data');
|
||||
}
|
||||
|
||||
return $bin;
|
||||
}
|
||||
|
||||
public static function fromAsn1(string $signature, int $length): string
|
||||
{
|
||||
$message = bin2hex($signature);
|
||||
$position = 0;
|
||||
|
||||
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_SEQUENCE) {
|
||||
throw new InvalidArgumentException('Invalid data. Should start with a sequence.');
|
||||
}
|
||||
|
||||
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) === self::ASN1_LENGTH_2BYTES) {
|
||||
$position += self::BYTE_SIZE;
|
||||
}
|
||||
|
||||
$pointR = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
|
||||
$pointS = self::retrievePositiveInteger(self::readAsn1Integer($message, $position));
|
||||
|
||||
$bin = hex2bin(str_pad($pointR, $length, '0', STR_PAD_LEFT) . str_pad($pointS, $length, '0', STR_PAD_LEFT));
|
||||
if (! is_string($bin)) {
|
||||
throw new InvalidArgumentException('Unable to parse the data');
|
||||
}
|
||||
|
||||
return $bin;
|
||||
}
|
||||
|
||||
private static function octetLength(string $data): int
|
||||
{
|
||||
return (int) (mb_strlen($data, '8bit') / self::BYTE_SIZE);
|
||||
}
|
||||
|
||||
private static function preparePositiveInteger(string $data): string
|
||||
{
|
||||
if (mb_substr($data, 0, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
return self::ASN1_NEGATIVE_INTEGER . $data;
|
||||
}
|
||||
|
||||
while (mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit') === 0
|
||||
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') <= self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
$data = mb_substr($data, 2, null, '8bit');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function readAsn1Content(string $message, int &$position, int $length): string
|
||||
{
|
||||
$content = mb_substr($message, $position, $length, '8bit');
|
||||
$position += $length;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private static function readAsn1Integer(string $message, int &$position): string
|
||||
{
|
||||
if (self::readAsn1Content($message, $position, self::BYTE_SIZE) !== self::ASN1_INTEGER) {
|
||||
throw new InvalidArgumentException('Invalid data. Should contain an integer.');
|
||||
}
|
||||
|
||||
$length = (int) hexdec(self::readAsn1Content($message, $position, self::BYTE_SIZE));
|
||||
|
||||
return self::readAsn1Content($message, $position, $length * self::BYTE_SIZE);
|
||||
}
|
||||
|
||||
private static function retrievePositiveInteger(string $data): string
|
||||
{
|
||||
while (mb_strpos($data, self::ASN1_NEGATIVE_INTEGER, 0, '8bit') === 0
|
||||
&& mb_substr($data, 2, self::BYTE_SIZE, '8bit') > self::ASN1_BIG_INTEGER_LIMIT) {
|
||||
$data = mb_substr($data, 2, null, '8bit');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
61
libraries/vendor/web-token/jwt-core/Util/Hash.php
vendored
Normal file
61
libraries/vendor/web-token/jwt-core/Util/Hash.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Hash
|
||||
{
|
||||
private function __construct(
|
||||
private readonly string $hash,
|
||||
private readonly int $length,
|
||||
private readonly string $t
|
||||
) {
|
||||
}
|
||||
|
||||
public static function sha1(): self
|
||||
{
|
||||
return new self('sha1', 20, "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14");
|
||||
}
|
||||
|
||||
public static function sha256(): self
|
||||
{
|
||||
return new self('sha256', 32, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20");
|
||||
}
|
||||
|
||||
public static function sha384(): self
|
||||
{
|
||||
return new self('sha384', 48, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30");
|
||||
}
|
||||
|
||||
public static function sha512(): self
|
||||
{
|
||||
return new self('sha512', 64, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40");
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return $this->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the HMAC.
|
||||
*/
|
||||
public function hash(string $text): string
|
||||
{
|
||||
return hash($this->hash, $text, true);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
public function t(): string
|
||||
{
|
||||
return $this->t;
|
||||
}
|
||||
}
|
||||
28
libraries/vendor/web-token/jwt-core/Util/JsonConverter.php
vendored
Normal file
28
libraries/vendor/web-token/jwt-core/Util/JsonConverter.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use const JSON_UNESCAPED_UNICODE;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class JsonConverter
|
||||
{
|
||||
public static function encode(mixed $payload): string
|
||||
{
|
||||
try {
|
||||
return json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $throwable) {
|
||||
throw new RuntimeException('Invalid content.', $throwable->getCode(), $throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static function decode(string $payload): mixed
|
||||
{
|
||||
return json_decode($payload, true, 512, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
116
libraries/vendor/web-token/jwt-core/Util/KeyChecker.php
vendored
Normal file
116
libraries/vendor/web-token/jwt-core/Util/KeyChecker.php
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use function in_array;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use Jose\Component\Core\JWK;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class KeyChecker
|
||||
{
|
||||
public static function checkKeyUsage(JWK $key, string $usage): void
|
||||
{
|
||||
if ($key->has('use')) {
|
||||
self::checkUsage($key, $usage);
|
||||
}
|
||||
if ($key->has('key_ops')) {
|
||||
self::checkOperation($key, $usage);
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkKeyAlgorithm(JWK $key, string $algorithm): void
|
||||
{
|
||||
if (! $key->has('alg')) {
|
||||
return;
|
||||
}
|
||||
$alg = $key->get('alg');
|
||||
if (! is_string($alg)) {
|
||||
throw new InvalidArgumentException('Invalid algorithm.');
|
||||
}
|
||||
if ($alg !== $algorithm) {
|
||||
throw new InvalidArgumentException(sprintf('Key is only allowed for algorithm "%s".', $alg));
|
||||
}
|
||||
}
|
||||
|
||||
private static function checkOperation(JWK $key, string $usage): void
|
||||
{
|
||||
$ops = $key->get('key_ops');
|
||||
if (! is_array($ops)) {
|
||||
throw new InvalidArgumentException('Invalid key parameter "key_ops". Should be a list of key operations');
|
||||
}
|
||||
|
||||
switch ($usage) {
|
||||
case 'verification':
|
||||
if (! in_array('verify', $ops, true)) {
|
||||
throw new InvalidArgumentException('Key cannot be used to verify a signature');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'signature':
|
||||
if (! in_array('sign', $ops, true)) {
|
||||
throw new InvalidArgumentException('Key cannot be used to sign');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'encryption':
|
||||
if (! in_array('encrypt', $ops, true) && ! in_array('wrapKey', $ops, true) && ! in_array(
|
||||
'deriveKey',
|
||||
$ops,
|
||||
true
|
||||
)) {
|
||||
throw new InvalidArgumentException('Key cannot be used to encrypt');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'decryption':
|
||||
if (! in_array('decrypt', $ops, true) && ! in_array('unwrapKey', $ops, true) && ! in_array(
|
||||
'deriveBits',
|
||||
$ops,
|
||||
true
|
||||
)) {
|
||||
throw new InvalidArgumentException('Key cannot be used to decrypt');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Unsupported key usage.');
|
||||
}
|
||||
}
|
||||
|
||||
private static function checkUsage(JWK $key, string $usage): void
|
||||
{
|
||||
$use = $key->get('use');
|
||||
|
||||
switch ($usage) {
|
||||
case 'verification':
|
||||
case 'signature':
|
||||
if ($use !== 'sig') {
|
||||
throw new InvalidArgumentException('Key cannot be used to sign or verify a signature.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'encryption':
|
||||
case 'decryption':
|
||||
if ($use !== 'enc') {
|
||||
throw new InvalidArgumentException('Key cannot be used to encrypt or decrypt.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException('Unsupported key usage.');
|
||||
}
|
||||
}
|
||||
}
|
||||
244
libraries/vendor/web-token/jwt-core/Util/RSAKey.php
vendored
Normal file
244
libraries/vendor/web-token/jwt-core/Util/RSAKey.php
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jose\Component\Core\Util;
|
||||
|
||||
use function array_key_exists;
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use function is_array;
|
||||
use Jose\Component\Core\JWK;
|
||||
use ParagonIE\ConstantTime\Base64UrlSafe;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Integer;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\CryptoEncoding\PEM;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\RSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RSA\RSAPrivateKey;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\RSA\RSAPublicKey;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RSAKey
|
||||
{
|
||||
private null|Sequence $sequence = null;
|
||||
|
||||
private readonly array $values;
|
||||
|
||||
private BigInteger $modulus;
|
||||
|
||||
private int $modulus_length;
|
||||
|
||||
private BigInteger $public_exponent;
|
||||
|
||||
private ?BigInteger $private_exponent = null;
|
||||
|
||||
/**
|
||||
* @var BigInteger[]
|
||||
*/
|
||||
private array $primes = [];
|
||||
|
||||
/**
|
||||
* @var BigInteger[]
|
||||
*/
|
||||
private array $exponents = [];
|
||||
|
||||
private ?BigInteger $coefficient = null;
|
||||
|
||||
private function __construct(JWK $data)
|
||||
{
|
||||
$this->values = $data->all();
|
||||
$this->populateBigIntegers();
|
||||
}
|
||||
|
||||
public static function createFromJWK(JWK $jwk): self
|
||||
{
|
||||
return new self($jwk);
|
||||
}
|
||||
|
||||
public function getModulus(): BigInteger
|
||||
{
|
||||
return $this->modulus;
|
||||
}
|
||||
|
||||
public function getModulusLength(): int
|
||||
{
|
||||
return $this->modulus_length;
|
||||
}
|
||||
|
||||
public function getExponent(): BigInteger
|
||||
{
|
||||
$d = $this->getPrivateExponent();
|
||||
if ($d !== null) {
|
||||
return $d;
|
||||
}
|
||||
|
||||
return $this->getPublicExponent();
|
||||
}
|
||||
|
||||
public function getPublicExponent(): BigInteger
|
||||
{
|
||||
return $this->public_exponent;
|
||||
}
|
||||
|
||||
public function getPrivateExponent(): ?BigInteger
|
||||
{
|
||||
return $this->private_exponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BigInteger[]
|
||||
*/
|
||||
public function getPrimes(): array
|
||||
{
|
||||
return $this->primes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BigInteger[]
|
||||
*/
|
||||
public function getExponents(): array
|
||||
{
|
||||
return $this->exponents;
|
||||
}
|
||||
|
||||
public function getCoefficient(): ?BigInteger
|
||||
{
|
||||
return $this->coefficient;
|
||||
}
|
||||
|
||||
public function isPublic(): bool
|
||||
{
|
||||
return ! array_key_exists('d', $this->values);
|
||||
}
|
||||
|
||||
public static function toPublic(self $private): self
|
||||
{
|
||||
$data = $private->toArray();
|
||||
$keys = ['p', 'd', 'q', 'dp', 'dq', 'qi'];
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return new self(new JWK($data));
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
public function toPEM(): string
|
||||
{
|
||||
if (array_key_exists('d', $this->values)) {
|
||||
$this->sequence = Sequence::create(
|
||||
Integer::create(0),
|
||||
RSAEncryptionAlgorithmIdentifier::create()->toASN1(),
|
||||
OctetString::create(
|
||||
RSAPrivateKey::create(
|
||||
$this->fromBase64ToInteger($this->values['n']),
|
||||
$this->fromBase64ToInteger($this->values['e']),
|
||||
$this->fromBase64ToInteger($this->values['d']),
|
||||
isset($this->values['p']) ? $this->fromBase64ToInteger($this->values['p']) : '0',
|
||||
isset($this->values['q']) ? $this->fromBase64ToInteger($this->values['q']) : '0',
|
||||
isset($this->values['dp']) ? $this->fromBase64ToInteger($this->values['dp']) : '0',
|
||||
isset($this->values['dq']) ? $this->fromBase64ToInteger($this->values['dq']) : '0',
|
||||
isset($this->values['qi']) ? $this->fromBase64ToInteger($this->values['qi']) : '0',
|
||||
)->toDER()
|
||||
)
|
||||
);
|
||||
|
||||
return PEM::create(PEM::TYPE_PRIVATE_KEY, $this->sequence->toDER())
|
||||
->string();
|
||||
}
|
||||
$this->sequence = Sequence::create(
|
||||
RSAEncryptionAlgorithmIdentifier::create()->toASN1(),
|
||||
BitString::create(
|
||||
RSAPublicKey::create(
|
||||
$this->fromBase64ToInteger($this->values['n']),
|
||||
$this->fromBase64ToInteger($this->values['e'])
|
||||
)->toDER()
|
||||
)
|
||||
);
|
||||
|
||||
return PEM::create(PEM::TYPE_PUBLIC_KEY, $this->sequence->toDER())
|
||||
->string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exponentiate with or without Chinese Remainder Theorem. Operation with primes 'p' and 'q' is appox. 2x faster.
|
||||
*/
|
||||
public static function exponentiate(self $key, BigInteger $c): BigInteger
|
||||
{
|
||||
if ($c->compare(BigInteger::createFromDecimal(0)) < 0 || $c->compare($key->getModulus()) > 0) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if ($key->isPublic() || $key->getCoefficient() === null || count($key->getPrimes()) === 0 || count(
|
||||
$key->getExponents()
|
||||
) === 0) {
|
||||
return $c->modPow($key->getExponent(), $key->getModulus());
|
||||
}
|
||||
|
||||
$p = $key->getPrimes()[0];
|
||||
$q = $key->getPrimes()[1];
|
||||
$dP = $key->getExponents()[0];
|
||||
$dQ = $key->getExponents()[1];
|
||||
$qInv = $key->getCoefficient();
|
||||
|
||||
$m1 = $c->modPow($dP, $p);
|
||||
$m2 = $c->modPow($dQ, $q);
|
||||
$h = $qInv->multiply($m1->subtract($m2)->add($p))
|
||||
->mod($p);
|
||||
|
||||
return $m2->add($h->multiply($q));
|
||||
}
|
||||
|
||||
private function populateBigIntegers(): void
|
||||
{
|
||||
$this->modulus = $this->convertBase64StringToBigInteger($this->values['n']);
|
||||
$this->modulus_length = mb_strlen($this->getModulus()->toBytes(), '8bit');
|
||||
$this->public_exponent = $this->convertBase64StringToBigInteger($this->values['e']);
|
||||
|
||||
if (! $this->isPublic()) {
|
||||
$this->private_exponent = $this->convertBase64StringToBigInteger($this->values['d']);
|
||||
|
||||
if (array_key_exists('p', $this->values) && array_key_exists('q', $this->values)) {
|
||||
$this->primes = [
|
||||
$this->convertBase64StringToBigInteger($this->values['p']),
|
||||
$this->convertBase64StringToBigInteger($this->values['q']),
|
||||
];
|
||||
if (array_key_exists('dp', $this->values) && array_key_exists('dq', $this->values) && array_key_exists(
|
||||
'qi',
|
||||
$this->values
|
||||
)) {
|
||||
$this->exponents = [
|
||||
$this->convertBase64StringToBigInteger($this->values['dp']),
|
||||
$this->convertBase64StringToBigInteger($this->values['dq']),
|
||||
];
|
||||
$this->coefficient = $this->convertBase64StringToBigInteger($this->values['qi']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function convertBase64StringToBigInteger(string $value): BigInteger
|
||||
{
|
||||
return BigInteger::createFromBinaryString(Base64UrlSafe::decode($value));
|
||||
}
|
||||
|
||||
private function fromBase64ToInteger(string $value): string
|
||||
{
|
||||
$unpacked = unpack('H*', Base64UrlSafe::decode($value));
|
||||
if (! is_array($unpacked) || count($unpacked) === 0) {
|
||||
throw new InvalidArgumentException('Unable to get the private key');
|
||||
}
|
||||
|
||||
return \Brick\Math\BigInteger::fromBase(current($unpacked), 16)->toBase(10);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user