updated plugin WP-WebAuthn
version 1.3.1
This commit is contained in:
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Cose\Key;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use FG\ASN1\ExplicitlyTaggedObject;
|
||||
use FG\ASN1\Universal\BitString;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\ObjectIdentifier;
|
||||
use FG\ASN1\Universal\OctetString;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
|
||||
class Ec2Key extends Key
|
||||
{
|
||||
public const CURVE_P256 = 1;
|
||||
public const CURVE_P256K = 8;
|
||||
public const CURVE_P384 = 2;
|
||||
public const CURVE_P521 = 3;
|
||||
|
||||
public const DATA_CURVE = -1;
|
||||
public const DATA_X = -2;
|
||||
public const DATA_Y = -3;
|
||||
public const DATA_D = -4;
|
||||
|
||||
private const SUPPORTED_CURVES = [
|
||||
self::CURVE_P256,
|
||||
self::CURVE_P256K,
|
||||
self::CURVE_P384,
|
||||
self::CURVE_P521,
|
||||
];
|
||||
|
||||
private const NAMED_CURVE_OID = [
|
||||
self::CURVE_P256 => '1.2.840.10045.3.1.7', // NIST P-256 / secp256r1
|
||||
self::CURVE_P256K => '1.3.132.0.10', // NIST P-256K / secp256k1
|
||||
self::CURVE_P384 => '1.3.132.0.34', // NIST P-384 / secp384r1
|
||||
self::CURVE_P521 => '1.3.132.0.35', // NIST P-521 / secp521r1
|
||||
];
|
||||
|
||||
private const CURVE_KEY_LENGTH = [
|
||||
self::CURVE_P256 => 32,
|
||||
self::CURVE_P256K => 32,
|
||||
self::CURVE_P384 => 48,
|
||||
self::CURVE_P521 => 66,
|
||||
];
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
Assertion::eq($data[self::TYPE], self::TYPE_EC2, 'Invalid EC2 key. The key type does not correspond to an EC2 key');
|
||||
Assertion::keyExists($data, self::DATA_CURVE, 'Invalid EC2 key. The curve is missing');
|
||||
Assertion::keyExists($data, self::DATA_X, 'Invalid EC2 key. The x coordinate is missing');
|
||||
Assertion::keyExists($data, self::DATA_Y, 'Invalid EC2 key. The y coordinate is missing');
|
||||
Assertion::length($data[self::DATA_X], self::CURVE_KEY_LENGTH[$data[self::DATA_CURVE]], 'Invalid length for x coordinate', null, '8bit');
|
||||
Assertion::length($data[self::DATA_Y], self::CURVE_KEY_LENGTH[$data[self::DATA_CURVE]], 'Invalid length for y coordinate', null, '8bit');
|
||||
Assertion::inArray((int) $data[self::DATA_CURVE], self::SUPPORTED_CURVES, 'The curve is not supported');
|
||||
}
|
||||
|
||||
public function toPublic(): self
|
||||
{
|
||||
$data = $this->getData();
|
||||
unset($data[self::DATA_D]);
|
||||
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function x(): string
|
||||
{
|
||||
return $this->get(self::DATA_X);
|
||||
}
|
||||
|
||||
public function y(): string
|
||||
{
|
||||
return $this->get(self::DATA_Y);
|
||||
}
|
||||
|
||||
public function isPrivate(): bool
|
||||
{
|
||||
return array_key_exists(self::DATA_D, $this->getData());
|
||||
}
|
||||
|
||||
public function d(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private');
|
||||
|
||||
return $this->get(self::DATA_D);
|
||||
}
|
||||
|
||||
public function curve(): int
|
||||
{
|
||||
return (int) $this->get(self::DATA_CURVE);
|
||||
}
|
||||
|
||||
public function asPEM(): string
|
||||
{
|
||||
if ($this->isPrivate()) {
|
||||
$der = new Sequence(
|
||||
new Integer(1),
|
||||
new OctetString(bin2hex($this->d())),
|
||||
new ExplicitlyTaggedObject(0, new ObjectIdentifier($this->getCurveOid())),
|
||||
new ExplicitlyTaggedObject(1, new BitString(bin2hex($this->getUncompressedCoordinates())))
|
||||
);
|
||||
|
||||
return $this->pem('EC PRIVATE KEY', $der->getBinary());
|
||||
}
|
||||
|
||||
$der = new Sequence(
|
||||
new Sequence(
|
||||
new ObjectIdentifier('1.2.840.10045.2.1'),
|
||||
new ObjectIdentifier($this->getCurveOid())
|
||||
),
|
||||
new BitString(bin2hex($this->getUncompressedCoordinates()))
|
||||
);
|
||||
|
||||
return $this->pem('PUBLIC KEY', $der->getBinary());
|
||||
}
|
||||
|
||||
public function getUncompressedCoordinates(): string
|
||||
{
|
||||
return "\x04".$this->x().$this->y();
|
||||
}
|
||||
|
||||
private function getCurveOid(): string
|
||||
{
|
||||
return self::NAMED_CURVE_OID[$this->curve()];
|
||||
}
|
||||
|
||||
private function pem(string $type, string $der): string
|
||||
{
|
||||
return sprintf("-----BEGIN %s-----\n", mb_strtoupper($type)).
|
||||
chunk_split(base64_encode($der), 64, "\n").
|
||||
sprintf("-----END %s-----\n", mb_strtoupper($type));
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Cose\Key;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
|
||||
class Key
|
||||
{
|
||||
public const TYPE = 1;
|
||||
public const TYPE_OKP = 1;
|
||||
public const TYPE_EC2 = 2;
|
||||
public const TYPE_RSA = 3;
|
||||
public const TYPE_OCT = 4;
|
||||
public const KID = 2;
|
||||
public const ALG = 3;
|
||||
public const KEY_OPS = 4;
|
||||
public const BASE_IV = 5;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
Assertion::keyExists($data, self::TYPE, 'Invalid key: the type is not defined');
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public static function createFromData(array $data): self
|
||||
{
|
||||
Assertion::keyExists($data, self::TYPE, 'Invalid key: the type is not defined');
|
||||
switch ($data[self::TYPE]) {
|
||||
case 1:
|
||||
return new OkpKey($data);
|
||||
case 2:
|
||||
return new Ec2Key($data);
|
||||
case 3:
|
||||
return new RsaKey($data);
|
||||
case 4:
|
||||
return new SymmetricKey($data);
|
||||
default:
|
||||
return new self($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->data[self::TYPE];
|
||||
}
|
||||
|
||||
public function alg(): int
|
||||
{
|
||||
return (int) $this->get(self::ALG);
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function has(int $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(int $key)
|
||||
{
|
||||
Assertion::keyExists($this->data, $key, sprintf('The key has no data at index %d', $key));
|
||||
|
||||
return $this->data[$key];
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Cose\Key;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
|
||||
class OkpKey extends Key
|
||||
{
|
||||
public const CURVE_X25519 = 4;
|
||||
public const CURVE_X448 = 5;
|
||||
public const CURVE_ED25519 = 6;
|
||||
public const CURVE_ED448 = 7;
|
||||
|
||||
public const DATA_CURVE = -1;
|
||||
public const DATA_X = -2;
|
||||
public const DATA_D = -4;
|
||||
|
||||
private const SUPPORTED_CURVES = [
|
||||
self::CURVE_X25519,
|
||||
self::CURVE_X448,
|
||||
self::CURVE_ED25519,
|
||||
self::CURVE_ED448,
|
||||
];
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
Assertion::eq($data[self::TYPE], self::TYPE_OKP, 'Invalid OKP key. The key type does not correspond to an OKP key');
|
||||
Assertion::keyExists($data, self::DATA_CURVE, 'Invalid EC2 key. The curve is missing');
|
||||
Assertion::keyExists($data, self::DATA_X, 'Invalid OKP key. The x coordinate is missing');
|
||||
Assertion::inArray((int) $data[self::DATA_CURVE], self::SUPPORTED_CURVES, 'The curve is not supported');
|
||||
}
|
||||
|
||||
public function x(): string
|
||||
{
|
||||
return $this->get(self::DATA_X);
|
||||
}
|
||||
|
||||
public function isPrivate(): bool
|
||||
{
|
||||
return array_key_exists(self::DATA_D, $this->getData());
|
||||
}
|
||||
|
||||
public function d(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private');
|
||||
|
||||
return $this->get(self::DATA_D);
|
||||
}
|
||||
|
||||
public function curve(): int
|
||||
{
|
||||
return (int) $this->get(self::DATA_CURVE);
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Cose\Key;
|
||||
|
||||
use function array_key_exists;
|
||||
use Assert\Assertion;
|
||||
use Brick\Math\BigInteger;
|
||||
use FG\ASN1\Universal\BitString;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\NullObject;
|
||||
use FG\ASN1\Universal\ObjectIdentifier;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class RsaKey extends Key
|
||||
{
|
||||
public const DATA_N = -1;
|
||||
public const DATA_E = -2;
|
||||
public const DATA_D = -3;
|
||||
public const DATA_P = -4;
|
||||
public const DATA_Q = -5;
|
||||
public const DATA_DP = -6;
|
||||
public const DATA_DQ = -7;
|
||||
public const DATA_QI = -8;
|
||||
public const DATA_OTHER = -9;
|
||||
public const DATA_RI = -10;
|
||||
public const DATA_DI = -11;
|
||||
public const DATA_TI = -12;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
Assertion::eq($data[self::TYPE], self::TYPE_RSA, 'Invalid RSA key. The key type does not correspond to a RSA key');
|
||||
Assertion::keyExists($data, self::DATA_N, 'Invalid RSA key. The modulus is missing');
|
||||
Assertion::keyExists($data, self::DATA_E, 'Invalid RSA key. The exponent is missing');
|
||||
}
|
||||
|
||||
public function n(): string
|
||||
{
|
||||
return $this->get(self::DATA_N);
|
||||
}
|
||||
|
||||
public function e(): string
|
||||
{
|
||||
return $this->get(self::DATA_E);
|
||||
}
|
||||
|
||||
public function d(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_D);
|
||||
}
|
||||
|
||||
public function p(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_P);
|
||||
}
|
||||
|
||||
public function q(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_Q);
|
||||
}
|
||||
|
||||
public function dP(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_DP);
|
||||
}
|
||||
|
||||
public function dQ(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_DQ);
|
||||
}
|
||||
|
||||
public function QInv(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_QI);
|
||||
}
|
||||
|
||||
public function other(): array
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_OTHER);
|
||||
}
|
||||
|
||||
public function rI(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_RI);
|
||||
}
|
||||
|
||||
public function dI(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_DI);
|
||||
}
|
||||
|
||||
public function tI(): string
|
||||
{
|
||||
Assertion::true($this->isPrivate(), 'The key is not private.');
|
||||
|
||||
return $this->get(self::DATA_TI);
|
||||
}
|
||||
|
||||
public function hasPrimes(): bool
|
||||
{
|
||||
return $this->has(self::DATA_P) && $this->has(self::DATA_Q);
|
||||
}
|
||||
|
||||
public function primes(): array
|
||||
{
|
||||
return [
|
||||
$this->p(),
|
||||
$this->q(),
|
||||
];
|
||||
}
|
||||
|
||||
public function hasExponents(): bool
|
||||
{
|
||||
return $this->has(self::DATA_DP) && $this->has(self::DATA_DQ);
|
||||
}
|
||||
|
||||
public function exponents(): array
|
||||
{
|
||||
return [
|
||||
$this->dP(),
|
||||
$this->dQ(),
|
||||
];
|
||||
}
|
||||
|
||||
public function hasCoefficient(): bool
|
||||
{
|
||||
return $this->has(self::DATA_QI);
|
||||
}
|
||||
|
||||
public function isPublic(): bool
|
||||
{
|
||||
return !$this->isPrivate();
|
||||
}
|
||||
|
||||
public function isPrivate(): bool
|
||||
{
|
||||
return array_key_exists(self::DATA_D, $this->getData());
|
||||
}
|
||||
|
||||
public function asPem(): string
|
||||
{
|
||||
Assertion::false($this->isPrivate(), 'Unsupported for private keys.');
|
||||
$bitSring = new Sequence(
|
||||
new Integer($this->fromBase64ToInteger($this->n())),
|
||||
new Integer($this->fromBase64ToInteger($this->e()))
|
||||
);
|
||||
|
||||
$der = new Sequence(
|
||||
new Sequence(
|
||||
new ObjectIdentifier('1.2.840.113549.1.1.1'),
|
||||
new NullObject()
|
||||
),
|
||||
new BitString(bin2hex($bitSring->getBinary()))
|
||||
);
|
||||
|
||||
return $this->pem('PUBLIC KEY', $der->getBinary());
|
||||
}
|
||||
|
||||
private function fromBase64ToInteger(string $value): string
|
||||
{
|
||||
$data = unpack('H*', $value);
|
||||
if (false === $data) {
|
||||
throw new InvalidArgumentException('Unable to convert to an integer');
|
||||
}
|
||||
|
||||
$hex = current($data);
|
||||
|
||||
return BigInteger::fromBase($hex, 16)->toBase(10);
|
||||
}
|
||||
|
||||
private function pem(string $type, string $der): string
|
||||
{
|
||||
return sprintf("-----BEGIN %s-----\n", mb_strtoupper($type)).
|
||||
chunk_split(base64_encode($der), 64, "\n").
|
||||
sprintf("-----END %s-----\n", mb_strtoupper($type));
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2021 Spomky-Labs
|
||||
*
|
||||
* This software may be modified and distributed under the terms
|
||||
* of the MIT license. See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Cose\Key;
|
||||
|
||||
use Assert\Assertion;
|
||||
|
||||
class SymmetricKey extends Key
|
||||
{
|
||||
public const DATA_K = -1;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
Assertion::eq($data[self::TYPE], self::TYPE_OCT, 'Invalid symmetric key. The key type does not correspond to a symmetric key');
|
||||
Assertion::keyExists($data, self::DATA_K, 'Invalid symmetric key. The parameter "k" is missing');
|
||||
}
|
||||
|
||||
public function k(): string
|
||||
{
|
||||
return $this->get(self::DATA_K);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user