first commit
This commit is contained in:
22
libraries/vendor/spomky-labs/pki-framework/LICENSE
vendored
Normal file
22
libraries/vendor/spomky-labs/pki-framework/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2019 Joni Eskelinen
|
||||
Copyright (c) 2022 Spomky-Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
277
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Component/Identifier.php
vendored
Normal file
277
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Component/Identifier.php
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Component;
|
||||
|
||||
use function array_key_exists;
|
||||
use Brick\Math\BigInteger;
|
||||
use function mb_strlen;
|
||||
use function ord;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\Encodable;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
|
||||
/**
|
||||
* Class to represent BER/DER identifier octets.
|
||||
*/
|
||||
final class Identifier implements Encodable
|
||||
{
|
||||
// Type class enumerations
|
||||
final public const CLASS_UNIVERSAL = 0b00;
|
||||
|
||||
final public const CLASS_APPLICATION = 0b01;
|
||||
|
||||
final public const CLASS_CONTEXT_SPECIFIC = 0b10;
|
||||
|
||||
final public const CLASS_PRIVATE = 0b11;
|
||||
|
||||
// P/C enumerations
|
||||
final public const PRIMITIVE = 0b0;
|
||||
|
||||
final public const CONSTRUCTED = 0b1;
|
||||
|
||||
/**
|
||||
* Mapping from type class to human readable name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const MAP_CLASS_TO_NAME = [
|
||||
self::CLASS_UNIVERSAL => 'UNIVERSAL',
|
||||
self::CLASS_APPLICATION => 'APPLICATION',
|
||||
self::CLASS_CONTEXT_SPECIFIC => 'CONTEXT SPECIFIC',
|
||||
self::CLASS_PRIVATE => 'PRIVATE',
|
||||
];
|
||||
|
||||
/**
|
||||
* Type class.
|
||||
*/
|
||||
private int $_class;
|
||||
|
||||
/**
|
||||
* Primitive or Constructed.
|
||||
*/
|
||||
private readonly int $_pc;
|
||||
|
||||
/**
|
||||
* Content type tag.
|
||||
*/
|
||||
private BigInt $_tag;
|
||||
|
||||
/**
|
||||
* @param int $class Type class
|
||||
* @param int $pc Primitive / Constructed
|
||||
* @param BigInteger|int $tag Type tag number
|
||||
*/
|
||||
private function __construct(int $class, int $pc, BigInteger|int $tag)
|
||||
{
|
||||
$this->_class = 0b11 & $class;
|
||||
$this->_pc = 0b1 & $pc;
|
||||
$this->_tag = BigInt::create($tag);
|
||||
}
|
||||
|
||||
public static function create(int $class, int $pc, BigInteger|int $tag): self
|
||||
{
|
||||
return new self($class, $pc, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode identifier component from DER data.
|
||||
*
|
||||
* @param string $data DER encoded data
|
||||
* @param null|int $offset Reference to the variable that contains offset
|
||||
* into the data where to start parsing.
|
||||
* Variable is updated to the offset next to the
|
||||
* parsed identifier. If null, start from offset 0.
|
||||
*/
|
||||
public static function fromDER(string $data, int &$offset = null): self
|
||||
{
|
||||
$idx = $offset ?? 0;
|
||||
$datalen = mb_strlen($data, '8bit');
|
||||
if ($idx >= $datalen) {
|
||||
throw new DecodeException('Invalid offset.');
|
||||
}
|
||||
$byte = ord($data[$idx++]);
|
||||
// bits 8 and 7 (class)
|
||||
// 0 = universal, 1 = application, 2 = context-specific, 3 = private
|
||||
$class = (0b11000000 & $byte) >> 6;
|
||||
// bit 6 (0 = primitive / 1 = constructed)
|
||||
$pc = (0b00100000 & $byte) >> 5;
|
||||
// bits 5 to 1 (tag number)
|
||||
$tag = (0b00011111 & $byte);
|
||||
// long-form identifier
|
||||
if ($tag === 0x1f) {
|
||||
$tag = self::decodeLongFormTag($data, $idx);
|
||||
}
|
||||
if (isset($offset)) {
|
||||
$offset = $idx;
|
||||
}
|
||||
return self::create($class, $pc, $tag);
|
||||
}
|
||||
|
||||
public function toDER(): string
|
||||
{
|
||||
$bytes = [];
|
||||
$byte = $this->_class << 6 | $this->_pc << 5;
|
||||
$tag = $this->_tag->getValue();
|
||||
if ($tag->isLessThan(0x1f)) {
|
||||
$bytes[] = $byte | $tag->toInt();
|
||||
} // long-form identifier
|
||||
else {
|
||||
$bytes[] = $byte | 0x1f;
|
||||
$octets = [];
|
||||
for (; $tag->isGreaterThan(0); $tag = $tag->shiftedRight(7)) {
|
||||
$octets[] = 0x80 | $tag->and(0x7f)->toInt();
|
||||
}
|
||||
// last octet has bit 8 set to zero
|
||||
$octets[0] &= 0x7f;
|
||||
foreach (array_reverse($octets) as $octet) {
|
||||
$bytes[] = $octet;
|
||||
}
|
||||
}
|
||||
return pack('C*', ...$bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class of the type.
|
||||
*/
|
||||
public function typeClass(): int
|
||||
{
|
||||
return $this->_class;
|
||||
}
|
||||
|
||||
public function pc(): int
|
||||
{
|
||||
return $this->_pc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag number.
|
||||
*
|
||||
* @return string Base 10 integer string
|
||||
*/
|
||||
public function tag(): string
|
||||
{
|
||||
return $this->_tag->base10();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag as an integer.
|
||||
*/
|
||||
public function intTag(): int
|
||||
{
|
||||
return $this->_tag->toInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether type is of an universal class.
|
||||
*/
|
||||
public function isUniversal(): bool
|
||||
{
|
||||
return $this->_class === self::CLASS_UNIVERSAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether type is of an application class.
|
||||
*/
|
||||
public function isApplication(): bool
|
||||
{
|
||||
return $this->_class === self::CLASS_APPLICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether type is of a context specific class.
|
||||
*/
|
||||
public function isContextSpecific(): bool
|
||||
{
|
||||
return $this->_class === self::CLASS_CONTEXT_SPECIFIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether type is of a private class.
|
||||
*/
|
||||
public function isPrivate(): bool
|
||||
{
|
||||
return $this->_class === self::CLASS_PRIVATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether content is primitive type.
|
||||
*/
|
||||
public function isPrimitive(): bool
|
||||
{
|
||||
return $this->_pc === self::PRIMITIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check hether content is constructed type.
|
||||
*/
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return $this->_pc === self::CONSTRUCTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with given type class.
|
||||
*
|
||||
* @param int $class One of `CLASS_*` enumerations
|
||||
*/
|
||||
public function withClass(int $class): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->_class = 0b11 & $class;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with given type tag.
|
||||
*
|
||||
* @param int $tag Tag number
|
||||
*/
|
||||
public function withTag(int $tag): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->_tag = BigInt::create($tag);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human readable name of the type class.
|
||||
*/
|
||||
public static function classToName(int $class): string
|
||||
{
|
||||
if (! array_key_exists($class, self::MAP_CLASS_TO_NAME)) {
|
||||
return "CLASS {$class}";
|
||||
}
|
||||
return self::MAP_CLASS_TO_NAME[$class];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse long form tag.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Reference to the variable containing offset to data
|
||||
*
|
||||
* @return BigInteger Tag number
|
||||
*/
|
||||
private static function decodeLongFormTag(string $data, int &$offset): BigInteger
|
||||
{
|
||||
$datalen = mb_strlen($data, '8bit');
|
||||
$tag = BigInteger::of(0);
|
||||
while (true) {
|
||||
if ($offset >= $datalen) {
|
||||
throw new DecodeException('Unexpected end of data while decoding long form identifier.');
|
||||
}
|
||||
$byte = ord($data[$offset++]);
|
||||
$tag = $tag->shiftedLeft(7);
|
||||
$tag = $tag->or(0x7f & $byte);
|
||||
// last byte has bit 8 set to zero
|
||||
if ((0x80 & $byte) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
204
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Component/Length.php
vendored
Normal file
204
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Component/Length.php
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Component;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function count;
|
||||
use DomainException;
|
||||
use LogicException;
|
||||
use function mb_strlen;
|
||||
use function ord;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\Encodable;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
|
||||
/**
|
||||
* Class to represent BER/DER length octets.
|
||||
*/
|
||||
final class Length implements Encodable
|
||||
{
|
||||
/**
|
||||
* Length.
|
||||
*/
|
||||
private readonly BigInt $_length;
|
||||
|
||||
/**
|
||||
* @param BigInteger|int $length Length
|
||||
* @param bool $_indefinite Whether length is indefinite
|
||||
*/
|
||||
private function __construct(
|
||||
BigInteger|int $length,
|
||||
private readonly bool $_indefinite = false
|
||||
) {
|
||||
$this->_length = BigInt::create($length);
|
||||
}
|
||||
|
||||
public static function create(BigInteger|int $length, bool $_indefinite = false): self
|
||||
{
|
||||
return new self($length, $_indefinite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode length component from DER data.
|
||||
*
|
||||
* @param string $data DER encoded data
|
||||
* @param null|int $offset Reference to the variable that contains offset
|
||||
* into the data where to start parsing.
|
||||
* Variable is updated to the offset next to the
|
||||
* parsed length component. If null, start from offset 0.
|
||||
*/
|
||||
public static function fromDER(string $data, int &$offset = null): self
|
||||
{
|
||||
$idx = $offset ?? 0;
|
||||
$datalen = mb_strlen($data, '8bit');
|
||||
if ($idx >= $datalen) {
|
||||
throw new DecodeException('Unexpected end of data while decoding length.');
|
||||
}
|
||||
$indefinite = false;
|
||||
$byte = ord($data[$idx++]);
|
||||
// bits 7 to 1
|
||||
$length = (0x7f & $byte);
|
||||
// long form
|
||||
if ((0x80 & $byte) !== 0) {
|
||||
if ($length === 0) {
|
||||
$indefinite = true;
|
||||
} else {
|
||||
if ($idx + $length > $datalen) {
|
||||
throw new DecodeException('Unexpected end of data while decoding long form length.');
|
||||
}
|
||||
$length = self::decodeLongFormLength($length, $data, $idx);
|
||||
}
|
||||
}
|
||||
if (isset($offset)) {
|
||||
$offset = $idx;
|
||||
}
|
||||
return self::create($length, $indefinite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode length from DER.
|
||||
*
|
||||
* Throws an exception if length doesn't match with expected or if data doesn't contain enough bytes.
|
||||
*
|
||||
* Requirement of definite length is relaxed contrary to the specification (sect. 10.1).
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Reference to the offset variable
|
||||
* @param null|int $expected Expected length, null to bypass checking
|
||||
* @see self::fromDER
|
||||
*/
|
||||
public static function expectFromDER(string $data, int &$offset, int $expected = null): self
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = self::fromDER($data, $idx);
|
||||
// if certain length was expected
|
||||
if (isset($expected)) {
|
||||
if ($length->isIndefinite()) {
|
||||
throw new DecodeException(sprintf('Expected length %d, got indefinite.', $expected));
|
||||
}
|
||||
if ($expected !== $length->intLength()) {
|
||||
throw new DecodeException(sprintf('Expected length %d, got %d.', $expected, $length->intLength()));
|
||||
}
|
||||
}
|
||||
// check that enough data is available
|
||||
if (! $length->isIndefinite()
|
||||
&& mb_strlen($data, '8bit') < $idx + $length->intLength()) {
|
||||
throw new DecodeException(
|
||||
sprintf(
|
||||
'Length %d overflows data, %d bytes left.',
|
||||
$length->intLength(),
|
||||
mb_strlen($data, '8bit') - $idx
|
||||
)
|
||||
);
|
||||
}
|
||||
$offset = $idx;
|
||||
return $length;
|
||||
}
|
||||
|
||||
public function toDER(): string
|
||||
{
|
||||
$bytes = [];
|
||||
if ($this->_indefinite) {
|
||||
$bytes[] = 0x80;
|
||||
} else {
|
||||
$num = $this->_length->getValue();
|
||||
// long form
|
||||
if ($num->isGreaterThan(127)) {
|
||||
$octets = [];
|
||||
for (; $num->isGreaterThan(0); $num = $num->shiftedRight(8)) {
|
||||
$octets[] = BigInteger::of(0xff)->and($num)->toInt();
|
||||
}
|
||||
$count = count($octets);
|
||||
// first octet must not be 0xff
|
||||
if ($count >= 127) {
|
||||
throw new DomainException('Too many length octets.');
|
||||
}
|
||||
$bytes[] = 0x80 | $count;
|
||||
foreach (array_reverse($octets) as $octet) {
|
||||
$bytes[] = $octet;
|
||||
}
|
||||
} // short form
|
||||
else {
|
||||
$bytes[] = $num->toInt();
|
||||
}
|
||||
}
|
||||
return pack('C*', ...$bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length.
|
||||
*
|
||||
* @return string Length as an integer string
|
||||
*/
|
||||
public function length(): string
|
||||
{
|
||||
if ($this->_indefinite) {
|
||||
throw new LogicException('Length is indefinite.');
|
||||
}
|
||||
return $this->_length->base10();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length as an integer.
|
||||
*/
|
||||
public function intLength(): int
|
||||
{
|
||||
if ($this->_indefinite) {
|
||||
throw new LogicException('Length is indefinite.');
|
||||
}
|
||||
return $this->_length->toInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether length is indefinite.
|
||||
*/
|
||||
public function isIndefinite(): bool
|
||||
{
|
||||
return $this->_indefinite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode long form length.
|
||||
*
|
||||
* @param int $length Number of octets
|
||||
* @param string $data Data
|
||||
* @param int $offset reference to the variable containing offset to the data
|
||||
*/
|
||||
private static function decodeLongFormLength(int $length, string $data, int &$offset): BigInteger
|
||||
{
|
||||
// first octet must not be 0xff (spec 8.1.3.5c)
|
||||
if ($length === 127) {
|
||||
throw new DecodeException('Invalid number of length octets.');
|
||||
}
|
||||
$num = BigInteger::of(0);
|
||||
while (--$length >= 0) {
|
||||
$byte = ord($data[$offset++]);
|
||||
$num = $num->shiftedLeft(8)
|
||||
->or($byte);
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
}
|
||||
80
libraries/vendor/spomky-labs/pki-framework/src/ASN1/DERData.php
vendored
Normal file
80
libraries/vendor/spomky-labs/pki-framework/src/ASN1/DERData.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1;
|
||||
|
||||
use BadMethodCallException;
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
|
||||
/**
|
||||
* Container for raw DER encoded data.
|
||||
*
|
||||
* May be inserted into structure without decoding first.
|
||||
*/
|
||||
final class DERData extends Element
|
||||
{
|
||||
/**
|
||||
* DER encoded data.
|
||||
*/
|
||||
private readonly string $der;
|
||||
|
||||
/**
|
||||
* Identifier of the underlying type.
|
||||
*/
|
||||
private readonly Identifier $identifier;
|
||||
|
||||
/**
|
||||
* Offset to the content in DER data.
|
||||
*/
|
||||
private int $contentOffset = 0;
|
||||
|
||||
/**
|
||||
* @param string $data DER encoded data
|
||||
*/
|
||||
private function __construct(string $data)
|
||||
{
|
||||
$this->identifier = Identifier::fromDER($data, $this->contentOffset);
|
||||
// check that length encoding is valid
|
||||
Length::expectFromDER($data, $this->contentOffset);
|
||||
$this->der = $data;
|
||||
parent::__construct($this->identifier->intTag());
|
||||
}
|
||||
|
||||
public static function create(string $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function typeClass(): int
|
||||
{
|
||||
return $this->identifier->typeClass();
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return $this->identifier->isConstructed();
|
||||
}
|
||||
|
||||
public function toDER(): string
|
||||
{
|
||||
return $this->der;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
// if there's no content payload
|
||||
if (mb_strlen($this->der, '8bit') === $this->contentOffset) {
|
||||
return '';
|
||||
}
|
||||
return mb_substr($this->der, $this->contentOffset, null, '8bit');
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
throw new BadMethodCallException(__METHOD__ . ' must be implemented in derived class.');
|
||||
}
|
||||
}
|
||||
474
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Element.php
vendored
Normal file
474
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Element.php
vendored
Normal file
@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1;
|
||||
|
||||
use function array_key_exists;
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\ConstructedString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Set;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BMPString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Boolean;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\CharacterString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Enumerated;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\EOC;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GeneralizedTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GeneralString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GraphicString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\IA5String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Integer;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NumericString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectDescriptor;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectIdentifier;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\PrintableString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Real;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\RelativeOID;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\T61String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UniversalString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTCTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTF8String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\VideotexString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\VisibleString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\StringType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\ApplicationType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\ContextSpecificType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\PrivateType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\TaggedType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\TimeType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Base class for all ASN.1 type elements.
|
||||
*/
|
||||
abstract class Element implements ElementBase
|
||||
{
|
||||
// Universal type tags
|
||||
public const TYPE_EOC = 0x00;
|
||||
|
||||
public const TYPE_BOOLEAN = 0x01;
|
||||
|
||||
public const TYPE_INTEGER = 0x02;
|
||||
|
||||
public const TYPE_BIT_STRING = 0x03;
|
||||
|
||||
public const TYPE_OCTET_STRING = 0x04;
|
||||
|
||||
public const TYPE_NULL = 0x05;
|
||||
|
||||
public const TYPE_OBJECT_IDENTIFIER = 0x06;
|
||||
|
||||
public const TYPE_OBJECT_DESCRIPTOR = 0x07;
|
||||
|
||||
public const TYPE_EXTERNAL = 0x08;
|
||||
|
||||
public const TYPE_REAL = 0x09;
|
||||
|
||||
public const TYPE_ENUMERATED = 0x0a;
|
||||
|
||||
public const TYPE_EMBEDDED_PDV = 0x0b;
|
||||
|
||||
public const TYPE_UTF8_STRING = 0x0c;
|
||||
|
||||
public const TYPE_RELATIVE_OID = 0x0d;
|
||||
|
||||
public const TYPE_SEQUENCE = 0x10;
|
||||
|
||||
public const TYPE_SET = 0x11;
|
||||
|
||||
public const TYPE_NUMERIC_STRING = 0x12;
|
||||
|
||||
public const TYPE_PRINTABLE_STRING = 0x13;
|
||||
|
||||
public const TYPE_T61_STRING = 0x14;
|
||||
|
||||
public const TYPE_VIDEOTEX_STRING = 0x15;
|
||||
|
||||
public const TYPE_IA5_STRING = 0x16;
|
||||
|
||||
public const TYPE_UTC_TIME = 0x17;
|
||||
|
||||
public const TYPE_GENERALIZED_TIME = 0x18;
|
||||
|
||||
public const TYPE_GRAPHIC_STRING = 0x19;
|
||||
|
||||
public const TYPE_VISIBLE_STRING = 0x1a;
|
||||
|
||||
public const TYPE_GENERAL_STRING = 0x1b;
|
||||
|
||||
public const TYPE_UNIVERSAL_STRING = 0x1c;
|
||||
|
||||
public const TYPE_CHARACTER_STRING = 0x1d;
|
||||
|
||||
public const TYPE_BMP_STRING = 0x1e;
|
||||
|
||||
/**
|
||||
* Pseudotype for all string types.
|
||||
*
|
||||
* May be used as an expectation parameter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const TYPE_STRING = -1;
|
||||
|
||||
/**
|
||||
* Pseudotype for all time types.
|
||||
*
|
||||
* May be used as an expectation parameter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const TYPE_TIME = -2;
|
||||
|
||||
/**
|
||||
* Pseudotype for constructed strings.
|
||||
*
|
||||
* May be used as an expectation parameter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const TYPE_CONSTRUCTED_STRING = -3;
|
||||
|
||||
/**
|
||||
* Mapping from universal type tag to implementation class name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const MAP_TAG_TO_CLASS = [
|
||||
self::TYPE_EOC => EOC::class,
|
||||
self::TYPE_BOOLEAN => Boolean::class,
|
||||
self::TYPE_INTEGER => Integer::class,
|
||||
self::TYPE_BIT_STRING => BitString::class,
|
||||
self::TYPE_OCTET_STRING => OctetString::class,
|
||||
self::TYPE_NULL => NullType::class,
|
||||
self::TYPE_OBJECT_IDENTIFIER => ObjectIdentifier::class,
|
||||
self::TYPE_OBJECT_DESCRIPTOR => ObjectDescriptor::class,
|
||||
self::TYPE_REAL => Real::class,
|
||||
self::TYPE_ENUMERATED => Enumerated::class,
|
||||
self::TYPE_UTF8_STRING => UTF8String::class,
|
||||
self::TYPE_RELATIVE_OID => RelativeOID::class,
|
||||
self::TYPE_SEQUENCE => Sequence::class,
|
||||
self::TYPE_SET => Set::class,
|
||||
self::TYPE_NUMERIC_STRING => NumericString::class,
|
||||
self::TYPE_PRINTABLE_STRING => PrintableString::class,
|
||||
self::TYPE_T61_STRING => T61String::class,
|
||||
self::TYPE_VIDEOTEX_STRING => VideotexString::class,
|
||||
self::TYPE_IA5_STRING => IA5String::class,
|
||||
self::TYPE_UTC_TIME => UTCTime::class,
|
||||
self::TYPE_GENERALIZED_TIME => GeneralizedTime::class,
|
||||
self::TYPE_GRAPHIC_STRING => GraphicString::class,
|
||||
self::TYPE_VISIBLE_STRING => VisibleString::class,
|
||||
self::TYPE_GENERAL_STRING => GeneralString::class,
|
||||
self::TYPE_UNIVERSAL_STRING => UniversalString::class,
|
||||
self::TYPE_CHARACTER_STRING => CharacterString::class,
|
||||
self::TYPE_BMP_STRING => BMPString::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping from universal type tag to human-readable name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const MAP_TYPE_TO_NAME = [
|
||||
self::TYPE_EOC => 'EOC',
|
||||
self::TYPE_BOOLEAN => 'BOOLEAN',
|
||||
self::TYPE_INTEGER => 'INTEGER',
|
||||
self::TYPE_BIT_STRING => 'BIT STRING',
|
||||
self::TYPE_OCTET_STRING => 'OCTET STRING',
|
||||
self::TYPE_NULL => 'NULL',
|
||||
self::TYPE_OBJECT_IDENTIFIER => 'OBJECT IDENTIFIER',
|
||||
self::TYPE_OBJECT_DESCRIPTOR => 'ObjectDescriptor',
|
||||
self::TYPE_EXTERNAL => 'EXTERNAL',
|
||||
self::TYPE_REAL => 'REAL',
|
||||
self::TYPE_ENUMERATED => 'ENUMERATED',
|
||||
self::TYPE_EMBEDDED_PDV => 'EMBEDDED PDV',
|
||||
self::TYPE_UTF8_STRING => 'UTF8String',
|
||||
self::TYPE_RELATIVE_OID => 'RELATIVE-OID',
|
||||
self::TYPE_SEQUENCE => 'SEQUENCE',
|
||||
self::TYPE_SET => 'SET',
|
||||
self::TYPE_NUMERIC_STRING => 'NumericString',
|
||||
self::TYPE_PRINTABLE_STRING => 'PrintableString',
|
||||
self::TYPE_T61_STRING => 'T61String',
|
||||
self::TYPE_VIDEOTEX_STRING => 'VideotexString',
|
||||
self::TYPE_IA5_STRING => 'IA5String',
|
||||
self::TYPE_UTC_TIME => 'UTCTime',
|
||||
self::TYPE_GENERALIZED_TIME => 'GeneralizedTime',
|
||||
self::TYPE_GRAPHIC_STRING => 'GraphicString',
|
||||
self::TYPE_VISIBLE_STRING => 'VisibleString',
|
||||
self::TYPE_GENERAL_STRING => 'GeneralString',
|
||||
self::TYPE_UNIVERSAL_STRING => 'UniversalString',
|
||||
self::TYPE_CHARACTER_STRING => 'CHARACTER STRING',
|
||||
self::TYPE_BMP_STRING => 'BMPString',
|
||||
self::TYPE_STRING => 'Any String',
|
||||
self::TYPE_TIME => 'Any Time',
|
||||
self::TYPE_CONSTRUCTED_STRING => 'Constructed String',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param bool $indefiniteLength Whether type shall be encoded with indefinite length.
|
||||
*/
|
||||
protected function __construct(
|
||||
protected readonly int $typeTag,
|
||||
protected bool $indefiniteLength = false
|
||||
) {
|
||||
}
|
||||
|
||||
abstract public function typeClass(): int;
|
||||
|
||||
abstract public function isConstructed(): bool;
|
||||
|
||||
/**
|
||||
* Decode element from DER data.
|
||||
*
|
||||
* @param string $data DER encoded data
|
||||
* @param null|int $offset Reference to the variable that contains offset
|
||||
* into the data where to start parsing.
|
||||
* Variable is updated to the offset next to the
|
||||
* parsed element. If null, start from offset 0.
|
||||
*/
|
||||
public static function fromDER(string $data, int &$offset = null): static
|
||||
{
|
||||
$idx = $offset ?? 0;
|
||||
// decode identifier
|
||||
$identifier = Identifier::fromDER($data, $idx);
|
||||
// determine class that implements type specific decoding
|
||||
$cls = self::determineImplClass($identifier);
|
||||
// decode remaining element
|
||||
$element = $cls::decodeFromDER($identifier, $data, $idx);
|
||||
// if called in the context of a concrete class, check
|
||||
// that decoded type matches the type of calling class
|
||||
$called_class = static::class;
|
||||
if ($called_class !== self::class) {
|
||||
if (! $element instanceof $called_class) {
|
||||
throw new UnexpectedValueException(sprintf('%s expected, got %s.', $called_class, $element::class));
|
||||
}
|
||||
}
|
||||
// update offset for the caller
|
||||
if (isset($offset)) {
|
||||
$offset = $idx;
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
public function toDER(): string
|
||||
{
|
||||
$identifier = Identifier::create(
|
||||
$this->typeClass(),
|
||||
$this->isConstructed() ? Identifier::CONSTRUCTED : Identifier::PRIMITIVE,
|
||||
$this->typeTag
|
||||
);
|
||||
$content = $this->encodedAsDER();
|
||||
if ($this->indefiniteLength) {
|
||||
$length = Length::create(0, true);
|
||||
$eoc = EOC::create();
|
||||
return $identifier->toDER() . $length->toDER() . $content . $eoc->toDER();
|
||||
}
|
||||
$length = Length::create(mb_strlen($content, '8bit'));
|
||||
return $identifier->toDER() . $length->toDER() . $content;
|
||||
}
|
||||
|
||||
public function tag(): int
|
||||
{
|
||||
return $this->typeTag;
|
||||
}
|
||||
|
||||
public function isType(int $tag): bool
|
||||
{
|
||||
// if element is context specific
|
||||
if ($this->typeClass() === Identifier::CLASS_CONTEXT_SPECIFIC) {
|
||||
return false;
|
||||
}
|
||||
// negative tags identify an abstract pseudotype
|
||||
if ($tag < 0) {
|
||||
return $this->isPseudoType($tag);
|
||||
}
|
||||
return $this->isConcreteType($tag);
|
||||
}
|
||||
|
||||
public function expectType(int $tag): ElementBase
|
||||
{
|
||||
if (! $this->isType($tag)) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf('%s expected, got %s.', self::tagToName($tag), $this->typeDescriptorString())
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isTagged(): bool
|
||||
{
|
||||
return $this instanceof TaggedType;
|
||||
}
|
||||
|
||||
public function expectTagged(?int $tag = null): TaggedType
|
||||
{
|
||||
if (! $this->isTagged()) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf('Context specific element expected, got %s.', Identifier::classToName($this->typeClass()))
|
||||
);
|
||||
}
|
||||
if (isset($tag) && $this->tag() !== $tag) {
|
||||
throw new UnexpectedValueException(sprintf('Tag %d expected, got %d.', $tag, $this->tag()));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether element has indefinite length.
|
||||
*/
|
||||
public function hasIndefiniteLength(): bool
|
||||
{
|
||||
return $this->indefiniteLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with indefinite length encoding set.
|
||||
*
|
||||
* @param bool $indefinite True for indefinite length, false for definite length
|
||||
*/
|
||||
public function withIndefiniteLength(bool $indefinite = true): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->indefiniteLength = $indefinite;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
final public function asElement(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element decorated with `UnspecifiedType` object.
|
||||
*/
|
||||
public function asUnspecified(): UnspecifiedType
|
||||
{
|
||||
return UnspecifiedType::create($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human readable name for an universal tag.
|
||||
*/
|
||||
public static function tagToName(int $tag): string
|
||||
{
|
||||
if (! array_key_exists($tag, self::MAP_TYPE_TO_NAME)) {
|
||||
return "TAG {$tag}";
|
||||
}
|
||||
return self::MAP_TYPE_TO_NAME[$tag];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content encoded in DER.
|
||||
*
|
||||
* Returns the DER encoded content without identifier and length header octets.
|
||||
*/
|
||||
abstract protected function encodedAsDER(): string;
|
||||
|
||||
/**
|
||||
* Decode type-specific element from DER.
|
||||
*
|
||||
* @param Identifier $identifier Pre-parsed identifier
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset in data to the next byte after identifier
|
||||
*/
|
||||
abstract protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase;
|
||||
|
||||
/**
|
||||
* Determine the class that implements the type.
|
||||
*
|
||||
* @return string Class name
|
||||
*/
|
||||
protected static function determineImplClass(Identifier $identifier): string
|
||||
{
|
||||
switch ($identifier->typeClass()) {
|
||||
case Identifier::CLASS_UNIVERSAL:
|
||||
$cls = self::determineUniversalImplClass($identifier->intTag());
|
||||
// constructed strings may be present in BER
|
||||
if ($identifier->isConstructed()
|
||||
&& is_subclass_of($cls, StringType::class)) {
|
||||
$cls = ConstructedString::class;
|
||||
}
|
||||
return $cls;
|
||||
case Identifier::CLASS_CONTEXT_SPECIFIC:
|
||||
return ContextSpecificType::class;
|
||||
case Identifier::CLASS_APPLICATION:
|
||||
return ApplicationType::class;
|
||||
case Identifier::CLASS_PRIVATE:
|
||||
return PrivateType::class;
|
||||
}
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
'%s %d not implemented.',
|
||||
Identifier::classToName($identifier->typeClass()),
|
||||
$identifier->tag()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the class that implements an universal type of the given tag.
|
||||
*
|
||||
* @return string Class name
|
||||
*/
|
||||
protected static function determineUniversalImplClass(int $tag): string
|
||||
{
|
||||
if (! array_key_exists($tag, self::MAP_TAG_TO_CLASS)) {
|
||||
throw new UnexpectedValueException("Universal tag {$tag} not implemented.");
|
||||
}
|
||||
return self::MAP_TAG_TO_CLASS[$tag];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get textual description of the type for debugging purposes.
|
||||
*/
|
||||
protected function typeDescriptorString(): string
|
||||
{
|
||||
if ($this->typeClass() === Identifier::CLASS_UNIVERSAL) {
|
||||
return self::tagToName($this->typeTag);
|
||||
}
|
||||
return sprintf('%s TAG %d', Identifier::classToName($this->typeClass()), $this->typeTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the element is a concrete type of given tag.
|
||||
*/
|
||||
private function isConcreteType(int $tag): bool
|
||||
{
|
||||
// if tag doesn't match
|
||||
if ($this->tag() !== $tag) {
|
||||
return false;
|
||||
}
|
||||
// if type is universal check that instance is of a correct class
|
||||
if ($this->typeClass() === Identifier::CLASS_UNIVERSAL) {
|
||||
$cls = self::determineUniversalImplClass($tag);
|
||||
if (! $this instanceof $cls) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the element is a pseudotype.
|
||||
*/
|
||||
private function isPseudoType(int $tag): bool
|
||||
{
|
||||
return match ($tag) {
|
||||
self::TYPE_STRING => $this instanceof StringType,
|
||||
self::TYPE_TIME => $this instanceof TimeType,
|
||||
self::TYPE_CONSTRUCTED_STRING => $this instanceof ConstructedString,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
14
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Exception/DecodeException.php
vendored
Normal file
14
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Exception/DecodeException.php
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown on decoding errors.
|
||||
*/
|
||||
final class DecodeException extends RuntimeException
|
||||
{
|
||||
}
|
||||
77
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/ElementBase.php
vendored
Normal file
77
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/ElementBase.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Feature;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\TaggedType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Base interface for ASN.1 type elements.
|
||||
*/
|
||||
interface ElementBase extends Encodable
|
||||
{
|
||||
/**
|
||||
* Get the class of the ASN.1 type.
|
||||
*
|
||||
* One of `Identifier::CLASS_*` constants.
|
||||
*/
|
||||
public function typeClass(): int;
|
||||
|
||||
/**
|
||||
* Check whether the element is constructed.
|
||||
*
|
||||
* Otherwise it's primitive.
|
||||
*/
|
||||
public function isConstructed(): bool;
|
||||
|
||||
/**
|
||||
* Get the tag of the element.
|
||||
*
|
||||
* Interpretation of the tag depends on the context. For example, it may represent a universal type tag or a tag of
|
||||
* an implicitly or explicitly tagged type.
|
||||
*/
|
||||
public function tag(): int;
|
||||
|
||||
/**
|
||||
* Check whether the element is a type of given tag.
|
||||
*
|
||||
* @param int $tag Type tag
|
||||
*/
|
||||
public function isType(int $tag): bool;
|
||||
|
||||
/**
|
||||
* Check whether the element is a type of a given tag.
|
||||
*
|
||||
* Throws an exception if expectation fails.
|
||||
*
|
||||
* @param int $tag Type tag
|
||||
*/
|
||||
public function expectType(int $tag): self;
|
||||
|
||||
/**
|
||||
* Check whether the element is tagged (context specific).
|
||||
*/
|
||||
public function isTagged(): bool;
|
||||
|
||||
/**
|
||||
* Check whether the element is tagged (context specific) and optionally has a given tag.
|
||||
*
|
||||
* Throws an exception if the element is not tagged or tag differs from the expected.
|
||||
*
|
||||
* @param null|int $tag Optional type tag
|
||||
*/
|
||||
public function expectTagged(?int $tag = null): TaggedType;
|
||||
|
||||
/**
|
||||
* Get the object as an abstract `Element` instance.
|
||||
*/
|
||||
public function asElement(): Element;
|
||||
|
||||
/**
|
||||
* Get the object as an `UnspecifiedType` instance.
|
||||
*/
|
||||
public function asUnspecified(): UnspecifiedType;
|
||||
}
|
||||
16
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/Encodable.php
vendored
Normal file
16
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/Encodable.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Feature;
|
||||
|
||||
/**
|
||||
* Interface for classes that may be encoded to DER.
|
||||
*/
|
||||
interface Encodable
|
||||
{
|
||||
/**
|
||||
* Encode object to DER.
|
||||
*/
|
||||
public function toDER(): string;
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/Stringable.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Feature/Stringable.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Feature;
|
||||
|
||||
/**
|
||||
* Interface for classes that may be cast to string.
|
||||
*/
|
||||
interface Stringable
|
||||
{
|
||||
/**
|
||||
* Convert object to string.
|
||||
*/
|
||||
public function __toString(): string;
|
||||
|
||||
/**
|
||||
* Get the string representation of the type.
|
||||
*/
|
||||
public function string(): string;
|
||||
}
|
||||
56
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/BaseString.php
vendored
Normal file
56
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/BaseString.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Base class for all string types.
|
||||
*/
|
||||
abstract class BaseString extends Element implements StringType, Stringable
|
||||
{
|
||||
/**
|
||||
* String value.
|
||||
*/
|
||||
private readonly string $string;
|
||||
|
||||
protected function __construct(int $typeTag, string $string)
|
||||
{
|
||||
parent::__construct($typeTag);
|
||||
if (! $this->validateString($string)) {
|
||||
throw new InvalidArgumentException(sprintf('Not a valid %s string.', self::tagToName($this->typeTag)));
|
||||
}
|
||||
$this->string = $string;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string value.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether string is valid for the concrete type.
|
||||
*/
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// Override in derived classes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
59
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/BaseTime.php
vendored
Normal file
59
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/BaseTime.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Base class for all types representing a point in time.
|
||||
*/
|
||||
abstract class BaseTime extends Element implements TimeType, Stringable
|
||||
{
|
||||
/**
|
||||
* UTC timezone.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const TZ_UTC = 'UTC';
|
||||
|
||||
protected function __construct(
|
||||
int $typeTag,
|
||||
protected readonly DateTimeImmutable $dateTime
|
||||
) {
|
||||
parent::__construct($typeTag);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from datetime string.
|
||||
*
|
||||
* @see http://php.net/manual/en/datetime.formats.php
|
||||
*
|
||||
* @param string $time Time string
|
||||
*/
|
||||
abstract public static function fromString(string $time): static;
|
||||
|
||||
/**
|
||||
* Get the date and time.
|
||||
*/
|
||||
public function dateTime(): DateTimeImmutable
|
||||
{
|
||||
return $this->dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date and time as a type specific string.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return $this->encodedAsDER();
|
||||
}
|
||||
}
|
||||
156
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/ConstructedString.php
vendored
Normal file
156
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/ConstructedString.php
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Constructed;
|
||||
|
||||
use function count;
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\StringType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Structure;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Implements constructed type of simple strings.
|
||||
*
|
||||
* Constructed strings only exist in BER encodings, and often with indefinite length. Generally constructed string must
|
||||
* contain only elements that have the same type tag as the constructing element. For example: ``` OCTET STRING (cons) {
|
||||
* OCTET STRING (prim) "ABC" OCTET STRING (prim) "DEF" } ``` Canonically this corresponds to a payload of "ABCDEF"
|
||||
* string.
|
||||
*
|
||||
* From API standpoint this can also be seen as a string type (as it implements `StringType`), and thus
|
||||
* `UnspecifiedType::asString()` method may return `ConstructedString` instances.
|
||||
*/
|
||||
final class ConstructedString extends Structure implements StringType, Stringable
|
||||
{
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from a list of string type elements.
|
||||
*
|
||||
* All strings must have the same type.
|
||||
*/
|
||||
public static function create(StringType ...$elements): self
|
||||
{
|
||||
if (count($elements) === 0) {
|
||||
throw new LogicException('No elements, unable to determine type tag.');
|
||||
}
|
||||
$tag = $elements[0]->tag();
|
||||
|
||||
return self::createWithTag($tag, ...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create from strings with a given type tag.
|
||||
*
|
||||
* Does not perform any validation on types.
|
||||
*
|
||||
* @param int $tag Type tag for the constructed string element
|
||||
* @param StringType ...$elements Any number of elements
|
||||
*/
|
||||
public static function createWithTag(int $tag, StringType ...$elements): self
|
||||
{
|
||||
foreach ($elements as $el) {
|
||||
if ($el->tag() !== $tag) {
|
||||
throw new LogicException('All elements in constructed string must have the same type.');
|
||||
}
|
||||
}
|
||||
|
||||
return new self($tag, ...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of strings in this structure.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function strings(): array
|
||||
{
|
||||
return array_map(static fn (Element $el): string => $el->string(), $this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contained strings concatenated together.
|
||||
*
|
||||
* NOTE: It's unclear how bit strings with unused bits should be concatenated.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return implode('', $this->strings());
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): self
|
||||
{
|
||||
if (! $identifier->isConstructed()) {
|
||||
throw new DecodeException('Structured element must have constructed bit set.');
|
||||
}
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx);
|
||||
if ($length->isIndefinite()) {
|
||||
$type = self::decodeIndefiniteLength($identifier->intTag(), $data, $idx);
|
||||
} else {
|
||||
$type = self::decodeDefiniteLength($identifier->intTag(), $data, $idx, $length->intLength());
|
||||
}
|
||||
$offset = $idx;
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for a definite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
* @param int $length Number of bytes to decode
|
||||
*/
|
||||
protected static function decodeDefiniteLength(int $typeTag, string $data, int &$offset, int $length): self
|
||||
{
|
||||
$idx = $offset;
|
||||
$end = $idx + $length;
|
||||
$elements = [];
|
||||
while ($idx < $end) {
|
||||
$elements[] = Element::fromDER($data, $idx);
|
||||
// check that element didn't overflow length
|
||||
if ($idx > $end) {
|
||||
throw new DecodeException("Structure's content overflows length.");
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
// return instance by static late binding
|
||||
return self::createWithTag($typeTag, ...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for an indefinite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
*/
|
||||
protected static function decodeIndefiniteLength(int $typeTag, string $data, int &$offset): self
|
||||
{
|
||||
$idx = $offset;
|
||||
$elements = [];
|
||||
$end = mb_strlen($data, '8bit');
|
||||
while (true) {
|
||||
if ($idx >= $end) {
|
||||
throw new DecodeException('Unexpected end of data while decoding indefinite length structure.');
|
||||
}
|
||||
$el = Element::fromDER($data, $idx);
|
||||
if ($el->isType(self::TYPE_EOC)) {
|
||||
break;
|
||||
}
|
||||
$elements[] = $el;
|
||||
}
|
||||
$offset = $idx;
|
||||
$type = self::createWithTag($typeTag, ...$elements);
|
||||
$type->indefiniteLength = true;
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
92
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/Sequence.php
vendored
Normal file
92
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/Sequence.php
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Constructed;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Structure;
|
||||
|
||||
/**
|
||||
* Implements *SEQUENCE* and *SEQUENCE OF* types.
|
||||
*/
|
||||
final class Sequence extends Structure
|
||||
{
|
||||
/**
|
||||
* @param Element ...$elements Any number of elements
|
||||
*/
|
||||
public static function create(Element ...$elements): self
|
||||
{
|
||||
return new self(self::TYPE_SEQUENCE, ...$elements);
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): self
|
||||
{
|
||||
if (! $identifier->isConstructed()) {
|
||||
throw new DecodeException('Structured element must have constructed bit set.');
|
||||
}
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx);
|
||||
if ($length->isIndefinite()) {
|
||||
$type = self::decodeIndefiniteLength($data, $idx);
|
||||
} else {
|
||||
$type = self::decodeDefiniteLength($data, $idx, $length->intLength());
|
||||
}
|
||||
$offset = $idx;
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for a definite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
* @param int $length Number of bytes to decode
|
||||
*/
|
||||
protected static function decodeDefiniteLength(string $data, int &$offset, int $length): self
|
||||
{
|
||||
$idx = $offset;
|
||||
$end = $idx + $length;
|
||||
$elements = [];
|
||||
while ($idx < $end) {
|
||||
$elements[] = Element::fromDER($data, $idx);
|
||||
// check that element didn't overflow length
|
||||
if ($idx > $end) {
|
||||
throw new DecodeException("Structure's content overflows length.");
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
// return instance by static late binding
|
||||
return self::create(...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for an indefinite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
*/
|
||||
protected static function decodeIndefiniteLength(string $data, int &$offset): self
|
||||
{
|
||||
$idx = $offset;
|
||||
$elements = [];
|
||||
$end = mb_strlen($data, '8bit');
|
||||
while (true) {
|
||||
if ($idx >= $end) {
|
||||
throw new DecodeException('Unexpected end of data while decoding indefinite length structure.');
|
||||
}
|
||||
$el = Element::fromDER($data, $idx);
|
||||
if ($el->isType(self::TYPE_EOC)) {
|
||||
break;
|
||||
}
|
||||
$elements[] = $el;
|
||||
}
|
||||
$offset = $idx;
|
||||
$type = self::create(...$elements);
|
||||
$type->indefiniteLength = true;
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
135
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/Set.php
vendored
Normal file
135
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Constructed/Set.php
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Constructed;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Structure;
|
||||
|
||||
/**
|
||||
* Implements *SET* and *SET OF* types.
|
||||
*/
|
||||
final class Set extends Structure
|
||||
{
|
||||
/**
|
||||
* @param Element ...$elements Any number of elements
|
||||
*/
|
||||
public static function create(Element ...$elements): self
|
||||
{
|
||||
return new self(self::TYPE_SET, ...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by canonical ascending order.
|
||||
*
|
||||
* Used for DER encoding of *SET* type.
|
||||
*/
|
||||
public function sortedSet(): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
usort(
|
||||
$obj->elements,
|
||||
function (Element $a, Element $b) {
|
||||
if ($a->typeClass() !== $b->typeClass()) {
|
||||
return $a->typeClass() < $b->typeClass() ? -1 : 1;
|
||||
}
|
||||
return $a->tag() <=> $b->tag();
|
||||
}
|
||||
);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by encoding ascending order.
|
||||
*
|
||||
* Used for DER encoding of *SET OF* type.
|
||||
*/
|
||||
public function sortedSetOf(): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
usort(
|
||||
$obj->elements,
|
||||
function (Element $a, Element $b) {
|
||||
$a_der = $a->toDER();
|
||||
$b_der = $b->toDER();
|
||||
return strcmp($a_der, $b_der);
|
||||
}
|
||||
);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
if (! $identifier->isConstructed()) {
|
||||
throw new DecodeException('Structured element must have constructed bit set.');
|
||||
}
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx);
|
||||
if ($length->isIndefinite()) {
|
||||
$type = self::decodeIndefiniteLength($data, $idx);
|
||||
} else {
|
||||
$type = self::decodeDefiniteLength($data, $idx, $length->intLength());
|
||||
}
|
||||
$offset = $idx;
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for a definite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
* @param int $length Number of bytes to decode
|
||||
*/
|
||||
protected static function decodeDefiniteLength(string $data, int &$offset, int $length): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$end = $idx + $length;
|
||||
$elements = [];
|
||||
while ($idx < $end) {
|
||||
$elements[] = Element::fromDER($data, $idx);
|
||||
// check that element didn't overflow length
|
||||
if ($idx > $end) {
|
||||
throw new DecodeException("Structure's content overflows length.");
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
// return instance by static late binding
|
||||
return self::create(...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode elements for an indefinite length.
|
||||
*
|
||||
* @param string $data DER data
|
||||
* @param int $offset Offset to data
|
||||
*/
|
||||
protected static function decodeIndefiniteLength(string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$elements = [];
|
||||
$end = mb_strlen($data, '8bit');
|
||||
while (true) {
|
||||
if ($idx >= $end) {
|
||||
throw new DecodeException('Unexpected end of data while decoding indefinite length structure.');
|
||||
}
|
||||
$el = Element::fromDER($data, $idx);
|
||||
if ($el->isType(self::TYPE_EOC)) {
|
||||
break;
|
||||
}
|
||||
$elements[] = $el;
|
||||
}
|
||||
$offset = $idx;
|
||||
$type = self::create(...$elements);
|
||||
$type->indefiniteLength = true;
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
35
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/BMPString.php
vendored
Normal file
35
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/BMPString.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *BMPString* type.
|
||||
*
|
||||
* BMP stands for Basic Multilingual Plane. This is generally an Unicode string with UCS-2 encoding.
|
||||
*/
|
||||
final class BMPString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_BMP_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// UCS-2 has fixed with of 2 octets (16 bits)
|
||||
return mb_strlen($string, '8bit') % 2 === 0;
|
||||
}
|
||||
}
|
||||
191
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/BitString.php
vendored
Normal file
191
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/BitString.php
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function chr;
|
||||
use function mb_strlen;
|
||||
use function ord;
|
||||
use OutOfBoundsException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\BaseString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *BIT STRING* type.
|
||||
*/
|
||||
final class BitString extends BaseString
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* @param string $string Content octets
|
||||
* @param int $unusedBits Number of unused bits in the last octet
|
||||
*/
|
||||
private function __construct(
|
||||
string $string,
|
||||
private readonly int $unusedBits = 0
|
||||
) {
|
||||
parent::__construct(self::TYPE_BIT_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string, int $_unusedBits = 0): self
|
||||
{
|
||||
return new self($string, $_unusedBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of bits in the string.
|
||||
*/
|
||||
public function numBits(): int
|
||||
{
|
||||
return mb_strlen($this->string(), '8bit') * 8 - $this->unusedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of unused bits in the last octet of the string.
|
||||
*/
|
||||
public function unusedBits(): int
|
||||
{
|
||||
return $this->unusedBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether bit is set.
|
||||
*
|
||||
* @param int $idx Bit index. Most significant bit of the first octet is index 0.
|
||||
*/
|
||||
public function testBit(int $idx): bool
|
||||
{
|
||||
// octet index
|
||||
$oi = (int) floor($idx / 8);
|
||||
// if octet is outside range
|
||||
if ($oi < 0 || $oi >= mb_strlen($this->string(), '8bit')) {
|
||||
throw new OutOfBoundsException('Index is out of bounds.');
|
||||
}
|
||||
// bit index
|
||||
$bi = $idx % 8;
|
||||
// if tested bit is last octet's unused bit
|
||||
if ($oi === mb_strlen($this->string(), '8bit') - 1) {
|
||||
if ($bi >= 8 - $this->unusedBits) {
|
||||
throw new OutOfBoundsException('Index refers to an unused bit.');
|
||||
}
|
||||
}
|
||||
$byte = $this->string()[$oi];
|
||||
// index 0 is the most significant bit in byte
|
||||
$mask = 0x01 << (7 - $bi);
|
||||
return (ord($byte) & $mask) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get range of bits.
|
||||
*
|
||||
* @param int $start Index of first bit
|
||||
* @param int $length Number of bits in range
|
||||
*
|
||||
* @return string Integer of $length bits
|
||||
*/
|
||||
public function range(int $start, int $length): string
|
||||
{
|
||||
if ($length === 0) {
|
||||
return '0';
|
||||
}
|
||||
if ($start + $length > $this->numBits()) {
|
||||
throw new OutOfBoundsException('Not enough bits.');
|
||||
}
|
||||
$bits = BigInteger::of(0);
|
||||
$idx = $start;
|
||||
$end = $start + $length;
|
||||
while (true) {
|
||||
$bit = $this->testBit($idx) ? 1 : 0;
|
||||
$bits = $bits->or($bit);
|
||||
if (++$idx >= $end) {
|
||||
break;
|
||||
}
|
||||
$bits = $bits->shiftedLeft(1);
|
||||
}
|
||||
return $bits->toBase(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a copy of the bit string with trailing zeroes removed.
|
||||
*/
|
||||
public function withoutTrailingZeroes(): self
|
||||
{
|
||||
// if bit string was empty
|
||||
if ($this->string() === '') {
|
||||
return self::create('');
|
||||
}
|
||||
$bits = $this->string();
|
||||
// count number of empty trailing octets
|
||||
$unused_octets = 0;
|
||||
for ($idx = mb_strlen($bits, '8bit') - 1; $idx >= 0; --$idx, ++$unused_octets) {
|
||||
if ($bits[$idx] !== "\x0") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// strip trailing octets
|
||||
if ($unused_octets !== 0) {
|
||||
$bits = mb_substr($bits, 0, -$unused_octets, '8bit');
|
||||
}
|
||||
// if bit string was full of zeroes
|
||||
if ($bits === '') {
|
||||
return self::create('');
|
||||
}
|
||||
// count number of trailing zeroes in the last octet
|
||||
$unused_bits = 0;
|
||||
$byte = ord($bits[mb_strlen($bits, '8bit') - 1]);
|
||||
while (0 === ($byte & 0x01)) {
|
||||
++$unused_bits;
|
||||
$byte >>= 1;
|
||||
}
|
||||
return self::create($bits, $unused_bits);
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
$der = chr($this->unusedBits);
|
||||
$der .= $this->string();
|
||||
if ($this->unusedBits !== 0) {
|
||||
$octet = $der[mb_strlen($der, '8bit') - 1];
|
||||
// set unused bits to zero
|
||||
$octet &= chr(0xff & ~((1 << $this->unusedBits) - 1));
|
||||
$der[mb_strlen($der, '8bit') - 1] = $octet;
|
||||
}
|
||||
return $der;
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx);
|
||||
if ($length->intLength() < 1) {
|
||||
throw new DecodeException('Bit string length must be at least 1.');
|
||||
}
|
||||
$unused_bits = ord($data[$idx++]);
|
||||
if ($unused_bits > 7) {
|
||||
throw new DecodeException('Unused bits in a bit string must be less than 8.');
|
||||
}
|
||||
$str_len = $length->intLength() - 1;
|
||||
if ($str_len !== 0) {
|
||||
$str = mb_substr($data, $idx, $str_len, '8bit');
|
||||
if ($unused_bits !== 0) {
|
||||
$mask = (1 << $unused_bits) - 1;
|
||||
if (($mask & ord($str[mb_strlen($str, '8bit') - 1])) !== 0) {
|
||||
throw new DecodeException('DER encoded bit string must have zero padding.');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$str = '';
|
||||
}
|
||||
$offset = $idx + $str_len;
|
||||
return self::create($str, $unused_bits);
|
||||
}
|
||||
}
|
||||
62
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Boolean.php
vendored
Normal file
62
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Boolean.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use function chr;
|
||||
use function ord;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *BOOLEAN* type.
|
||||
*/
|
||||
final class Boolean extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
private function __construct(
|
||||
private readonly bool $_bool
|
||||
) {
|
||||
parent::__construct(self::TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
public static function create(bool $_bool): self
|
||||
{
|
||||
return new self($_bool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value.
|
||||
*/
|
||||
public function value(): bool
|
||||
{
|
||||
return $this->_bool;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return $this->_bool ? chr(0xff) : chr(0);
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
Length::expectFromDER($data, $idx, 1);
|
||||
$byte = ord($data[$idx++]);
|
||||
if ($byte !== 0) {
|
||||
if ($byte !== 0xff) {
|
||||
throw new DecodeException('DER encoded boolean true must have all bits set to 1.');
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
return self::create($byte !== 0);
|
||||
}
|
||||
}
|
||||
26
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/CharacterString.php
vendored
Normal file
26
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/CharacterString.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *CHARACTER STRING* type.
|
||||
*/
|
||||
final class CharacterString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_CHARACTER_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
}
|
||||
49
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/EOC.php
vendored
Normal file
49
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/EOC.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *End-of-contents* type.
|
||||
*/
|
||||
final class EOC extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::TYPE_EOC);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
if (! $identifier->isPrimitive()) {
|
||||
throw new DecodeException('EOC value must be primitive.');
|
||||
}
|
||||
// EOC type has always zero length
|
||||
Length::expectFromDER($data, $idx, 0);
|
||||
$offset = $idx;
|
||||
return self::create();
|
||||
}
|
||||
}
|
||||
34
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Enumerated.php
vendored
Normal file
34
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Enumerated.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
|
||||
/**
|
||||
* Implements *ENUMERATED* type.
|
||||
*/
|
||||
final class Enumerated extends Integer
|
||||
{
|
||||
public static function create(BigInteger|int|string $number): static
|
||||
{
|
||||
return new static($number, self::TYPE_ENUMERATED);
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
$bytes = mb_substr($data, $idx, $length, '8bit');
|
||||
$idx += $length;
|
||||
$num = BigInt::fromSignedOctets($bytes)->getValue();
|
||||
$offset = $idx;
|
||||
// late static binding since enumerated extends integer type
|
||||
return self::create($num);
|
||||
}
|
||||
}
|
||||
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GeneralString.php
vendored
Normal file
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GeneralString.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *GeneralString* type.
|
||||
*/
|
||||
final class GeneralString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_GENERAL_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// allow everything
|
||||
return true;
|
||||
}
|
||||
}
|
||||
133
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GeneralizedTime.php
vendored
Normal file
133
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GeneralizedTime.php
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use function intval;
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\BaseTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use Throwable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements *GeneralizedTime* type.
|
||||
*/
|
||||
final class GeneralizedTime extends BaseTime
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* Regular expression to parse date.
|
||||
*
|
||||
* DER restricts format to UTC timezone (Z suffix).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const REGEX = '#^' .
|
||||
'(\d\d\d\d)' . // YYYY
|
||||
'(\d\d)' . // MM
|
||||
'(\d\d)' . // DD
|
||||
'(\d\d)' . // hh
|
||||
'(\d\d)' . // mm
|
||||
'(\d\d)' . // ss
|
||||
'(?:\.(\d+))?' . // frac
|
||||
'Z' . // TZ
|
||||
'$#';
|
||||
|
||||
/**
|
||||
* Cached formatted date.
|
||||
*/
|
||||
private ?string $_formatted = null;
|
||||
|
||||
private function __construct(DateTimeImmutable $dt)
|
||||
{
|
||||
parent::__construct(self::TYPE_GENERALIZED_TIME, $dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cached variables on clone.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->_formatted = null;
|
||||
}
|
||||
|
||||
public static function create(DateTimeImmutable $dt): self
|
||||
{
|
||||
return new self($dt);
|
||||
}
|
||||
|
||||
public static function fromString(string $time, ?string $tz = null): static
|
||||
{
|
||||
return new static(new DateTimeImmutable($time, self::createTimeZone($tz)));
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
if (! isset($this->_formatted)) {
|
||||
$dt = $this->dateTime->setTimezone(new DateTimeZone('UTC'));
|
||||
$this->_formatted = $dt->format('YmdHis');
|
||||
// if fractions were used
|
||||
$frac = $dt->format('u');
|
||||
if (intval($frac) !== 0) {
|
||||
$frac = rtrim($frac, '0');
|
||||
$this->_formatted .= ".{$frac}";
|
||||
}
|
||||
// timezone
|
||||
$this->_formatted .= 'Z';
|
||||
}
|
||||
return $this->_formatted;
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
$str = mb_substr($data, $idx, $length, '8bit');
|
||||
$idx += $length;
|
||||
if (preg_match(self::REGEX, $str, $match) !== 1) {
|
||||
throw new DecodeException('Invalid GeneralizedTime format.');
|
||||
}
|
||||
[, $year, $month, $day, $hour, $minute, $second] = $match;
|
||||
// if fractions match, there's at least one digit
|
||||
if (isset($match[7])) {
|
||||
$frac = $match[7];
|
||||
// DER restricts trailing zeroes in fractional seconds component
|
||||
if ($frac[mb_strlen($frac, '8bit') - 1] === '0') {
|
||||
throw new DecodeException('Fractional seconds must omit trailing zeroes.');
|
||||
}
|
||||
} else {
|
||||
$frac = '0';
|
||||
}
|
||||
$time = $year . $month . $day . $hour . $minute . $second . '.' . $frac .
|
||||
self::TZ_UTC;
|
||||
$dt = DateTimeImmutable::createFromFormat('!YmdHis.uT', $time, new DateTimeZone('UTC'));
|
||||
if ($dt === false) {
|
||||
throw new DecodeException('Failed to decode GeneralizedTime');
|
||||
}
|
||||
$offset = $idx;
|
||||
return self::create($dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `DateTimeZone` object from string.
|
||||
*/
|
||||
private static function createTimeZone(?string $tz): DateTimeZone
|
||||
{
|
||||
try {
|
||||
return new DateTimeZone($tz ?? 'UTC');
|
||||
} catch (Throwable $e) {
|
||||
throw new UnexpectedValueException('Invalid timezone.', 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GraphicString.php
vendored
Normal file
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/GraphicString.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *GraphicString* type.
|
||||
*/
|
||||
final class GraphicString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_GRAPHIC_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// allow everything
|
||||
return true;
|
||||
}
|
||||
}
|
||||
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/IA5String.php
vendored
Normal file
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/IA5String.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *IA5String* type.
|
||||
*/
|
||||
final class IA5String extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_IA5_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
return preg_match('/[^\x00-\x7f]/', $string) !== 1;
|
||||
}
|
||||
}
|
||||
108
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Integer.php
vendored
Normal file
108
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Integer.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function gettype;
|
||||
use InvalidArgumentException;
|
||||
use function is_int;
|
||||
use function is_scalar;
|
||||
use function is_string;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
|
||||
/**
|
||||
* Implements *INTEGER* type.
|
||||
*/
|
||||
class Integer extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* The number.
|
||||
*/
|
||||
private readonly BigInt $_number;
|
||||
|
||||
/**
|
||||
* @param BigInteger|int|string $number Base 10 integer
|
||||
*/
|
||||
final protected function __construct(BigInteger|int|string $number, int $typeTag)
|
||||
{
|
||||
parent::__construct($typeTag);
|
||||
if (! self::validateNumber($number)) {
|
||||
$var = is_scalar($number) ? (string) $number : gettype($number);
|
||||
throw new InvalidArgumentException("'{$var}' is not a valid number.");
|
||||
}
|
||||
$this->_number = BigInt::create($number);
|
||||
}
|
||||
|
||||
public static function create(BigInteger|int|string $number): static
|
||||
{
|
||||
return new static($number, self::TYPE_INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as a base 10.
|
||||
*
|
||||
* @return string Integer as a string
|
||||
*/
|
||||
public function number(): string
|
||||
{
|
||||
return $this->_number->base10();
|
||||
}
|
||||
|
||||
public function getValue(): BigInteger
|
||||
{
|
||||
return $this->_number->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as an integer type.
|
||||
*/
|
||||
public function intNumber(): int
|
||||
{
|
||||
return $this->_number->toInt();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return $this->_number->signedOctets();
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
$bytes = mb_substr($data, $idx, $length, '8bit');
|
||||
$idx += $length;
|
||||
$num = BigInt::fromSignedOctets($bytes)->getValue();
|
||||
$offset = $idx;
|
||||
// late static binding since enumerated extends integer type
|
||||
return static::create($num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that number is valid for this context.
|
||||
*/
|
||||
private static function validateNumber(mixed $num): bool
|
||||
{
|
||||
if (is_int($num)) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($num) && preg_match('/-?\d+/', $num) === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($num instanceof BigInteger) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
49
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/NullType.php
vendored
Normal file
49
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/NullType.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *NULL* type.
|
||||
*/
|
||||
final class NullType extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::TYPE_NULL);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
if (! $identifier->isPrimitive()) {
|
||||
throw new DecodeException('Null value must be primitive.');
|
||||
}
|
||||
// null type has always zero length
|
||||
Length::expectFromDER($data, $idx, 0);
|
||||
$offset = $idx;
|
||||
return self::create();
|
||||
}
|
||||
}
|
||||
88
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Number.php
vendored
Normal file
88
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Number.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function gettype;
|
||||
use InvalidArgumentException;
|
||||
use function is_int;
|
||||
use function is_scalar;
|
||||
use function is_string;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
use function strval;
|
||||
|
||||
abstract class Number extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* The number.
|
||||
*/
|
||||
private readonly BigInt $number;
|
||||
|
||||
/**
|
||||
* @param BigInteger|int|string $number Base 10 integer
|
||||
*/
|
||||
protected function __construct(int $tag, BigInteger|int|string $number)
|
||||
{
|
||||
parent::__construct($tag);
|
||||
if (! self::validateNumber($number)) {
|
||||
$var = is_scalar($number) ? strval($number) : gettype($number);
|
||||
throw new InvalidArgumentException(sprintf('"%s" is not a valid number.', $var));
|
||||
}
|
||||
$this->number = BigInt::create($number);
|
||||
}
|
||||
|
||||
abstract public static function create(BigInteger|int|string $number): self;
|
||||
|
||||
/**
|
||||
* Get the number as a base 10.
|
||||
*
|
||||
* @return string Integer as a string
|
||||
*/
|
||||
public function number(): string
|
||||
{
|
||||
return $this->number->base10();
|
||||
}
|
||||
|
||||
public function getValue(): BigInteger
|
||||
{
|
||||
return $this->number->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as an integer type.
|
||||
*/
|
||||
public function intNumber(): int
|
||||
{
|
||||
return $this->number->toInt();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return $this->number->signedOctets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that number is valid for this context.
|
||||
*/
|
||||
private static function validateNumber(mixed $num): bool
|
||||
{
|
||||
if (is_int($num)) {
|
||||
return true;
|
||||
}
|
||||
if (is_string($num) && preg_match('/-?\d+/', $num) === 1) {
|
||||
return true;
|
||||
}
|
||||
if ($num instanceof BigInteger) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/NumericString.php
vendored
Normal file
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/NumericString.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *NumericString* type.
|
||||
*/
|
||||
final class NumericString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_NUMERIC_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
return preg_match('/[^\d ]/', $string) !== 1;
|
||||
}
|
||||
}
|
||||
34
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/ObjectDescriptor.php
vendored
Normal file
34
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/ObjectDescriptor.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *ObjectDescriptor* type.
|
||||
*/
|
||||
final class ObjectDescriptor extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $descriptor)
|
||||
{
|
||||
parent::__construct(self::TYPE_OBJECT_DESCRIPTOR, $descriptor);
|
||||
}
|
||||
|
||||
public static function create(string $descriptor): self
|
||||
{
|
||||
return new self($descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object descriptor.
|
||||
*/
|
||||
public function descriptor(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
}
|
||||
198
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/ObjectIdentifier.php
vendored
Normal file
198
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/ObjectIdentifier.php
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function is_int;
|
||||
use function mb_strlen;
|
||||
use function ord;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use Throwable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements *OBJECT IDENTIFIER* type.
|
||||
*/
|
||||
final class ObjectIdentifier extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* Object identifier split to sub ID's.
|
||||
*
|
||||
* @var BigInteger[]
|
||||
*/
|
||||
private readonly array $subids;
|
||||
|
||||
/**
|
||||
* @param string $oid OID in dotted format
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $oid,
|
||||
?int $typeTag
|
||||
) {
|
||||
$this->subids = self::explodeDottedOID($oid);
|
||||
// if OID is non-empty
|
||||
if (count($this->subids) > 0) {
|
||||
// check that at least two nodes are set
|
||||
if (count($this->subids) < 2) {
|
||||
throw new UnexpectedValueException('OID must have at least two nodes.');
|
||||
}
|
||||
// check that root arc is in 0..2 range
|
||||
if ($this->subids[0]->isGreaterThan(2)) {
|
||||
throw new UnexpectedValueException('Root arc must be in range of 0..2.');
|
||||
}
|
||||
// if root arc is 0 or 1, second node must be in 0..39 range
|
||||
if ($this->subids[0]->isLessThan(2) && $this->subids[1]->isGreaterThanOrEqualTo(40)) {
|
||||
throw new UnexpectedValueException('Second node must be in 0..39 range for root arcs 0 and 1.');
|
||||
}
|
||||
}
|
||||
parent::__construct($typeTag ?? self::TYPE_OBJECT_IDENTIFIER);
|
||||
}
|
||||
|
||||
public static function create(string $oid, ?int $typeTag = null): self
|
||||
{
|
||||
return new self($oid, $typeTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID in dotted format.
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
$subids = $this->subids;
|
||||
// encode first two subids to one according to spec section 8.19.4
|
||||
if (count($subids) >= 2) {
|
||||
$num = $subids[0]->multipliedBy(40)->plus($subids[1]);
|
||||
array_splice($subids, 0, 2, [$num]);
|
||||
}
|
||||
return self::encodeSubIDs(...$subids);
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$len = Length::expectFromDER($data, $idx)->intLength();
|
||||
$subids = self::decodeSubIDs(mb_substr($data, $idx, $len, '8bit'));
|
||||
$idx += $len;
|
||||
// decode first subidentifier according to spec section 8.19.4
|
||||
if (isset($subids[0])) {
|
||||
if ($subids[0]->isLessThan(80)) {
|
||||
[$x, $y] = $subids[0]->quotientAndRemainder(40);
|
||||
} else {
|
||||
$x = BigInteger::of(2);
|
||||
$y = $subids[0]->minus(80);
|
||||
}
|
||||
array_splice($subids, 0, 1, [$x, $y]);
|
||||
}
|
||||
$offset = $idx;
|
||||
return self::create(self::implodeSubIDs(...$subids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode dotted OID to an array of sub ID's.
|
||||
*
|
||||
* @param string $oid OID in dotted format
|
||||
*
|
||||
* @return BigInteger[] Array of BigInteger numbers
|
||||
*/
|
||||
protected static function explodeDottedOID(string $oid): array
|
||||
{
|
||||
$subids = [];
|
||||
if ($oid !== '') {
|
||||
foreach (explode('.', $oid) as $subid) {
|
||||
try {
|
||||
$n = BigInteger::of($subid);
|
||||
$subids[] = $n;
|
||||
} catch (Throwable $e) {
|
||||
throw new UnexpectedValueException(sprintf('"%s" is not a number.', $subid), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $subids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode an array of sub IDs to dotted OID format.
|
||||
*/
|
||||
protected static function implodeSubIDs(BigInteger ...$subids): string
|
||||
{
|
||||
return implode('.', array_map(static fn ($num) => $num->toBase(10), $subids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode sub ID's to DER.
|
||||
*/
|
||||
protected static function encodeSubIDs(BigInteger ...$subids): string
|
||||
{
|
||||
$data = '';
|
||||
foreach ($subids as $subid) {
|
||||
// if number fits to one base 128 byte
|
||||
if ($subid->isLessThan(128)) {
|
||||
$data .= chr($subid->toInt());
|
||||
} else { // encode to multiple bytes
|
||||
$bytes = [];
|
||||
do {
|
||||
array_unshift($bytes, 0x7f & $subid->toInt());
|
||||
$subid = $subid->shiftedRight(7);
|
||||
} while ($subid->isGreaterThan(0));
|
||||
// all bytes except last must have bit 8 set to one
|
||||
foreach (array_splice($bytes, 0, -1) as $byte) {
|
||||
$data .= chr(0x80 | $byte);
|
||||
}
|
||||
$byte = reset($bytes);
|
||||
if (! is_int($byte)) {
|
||||
throw new RuntimeException('Encoding failed');
|
||||
}
|
||||
$data .= chr($byte);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode sub ID's from DER data.
|
||||
*
|
||||
* @return BigInteger[] Array of BigInteger numbers
|
||||
*/
|
||||
protected static function decodeSubIDs(string $data): array
|
||||
{
|
||||
$subids = [];
|
||||
$idx = 0;
|
||||
$end = mb_strlen($data, '8bit');
|
||||
while ($idx < $end) {
|
||||
$num = BigInteger::of(0);
|
||||
while (true) {
|
||||
if ($idx >= $end) {
|
||||
throw new DecodeException('Unexpected end of data.');
|
||||
}
|
||||
$byte = ord($data[$idx++]);
|
||||
$num = $num->or($byte & 0x7f);
|
||||
// bit 8 of the last octet is zero
|
||||
if (0 === ($byte & 0x80)) {
|
||||
break;
|
||||
}
|
||||
$num = $num->shiftedLeft(7);
|
||||
}
|
||||
$subids[] = $num;
|
||||
}
|
||||
return $subids;
|
||||
}
|
||||
}
|
||||
26
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/OctetString.php
vendored
Normal file
26
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/OctetString.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *OCTET STRING* type.
|
||||
*/
|
||||
final class OctetString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_OCTET_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
}
|
||||
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/PrintableString.php
vendored
Normal file
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/PrintableString.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *PrintableString* type.
|
||||
*/
|
||||
final class PrintableString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_PRINTABLE_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
$chars = preg_quote(" '()+,-./:=?]", '/');
|
||||
return preg_match('/[^A-Za-z0-9' . $chars . ']/', $string) !== 1;
|
||||
}
|
||||
}
|
||||
692
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Real.php
vendored
Normal file
692
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/Real.php
vendored
Normal file
@ -0,0 +1,692 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use const INF;
|
||||
use LogicException;
|
||||
use function mb_strlen;
|
||||
use function ord;
|
||||
use RangeException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use SpomkyLabs\Pki\ASN1\Util\BigInt;
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements *REAL* type.
|
||||
*/
|
||||
final class Real extends Element implements Stringable
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* Regex pattern to parse NR1 form number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const NR1_REGEX = '/^\s*' .
|
||||
'(?<s>[+\-])?' . // sign
|
||||
'(?<i>\d+)' . // integer
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Regex pattern to parse NR2 form number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const NR2_REGEX = '/^\s*' .
|
||||
'(?<s>[+\-])?' . // sign
|
||||
'(?<d>(?:\d+[\.,]\d*)|(?:\d*[\.,]\d+))' . // decimal number
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Regex pattern to parse NR3 form number.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const NR3_REGEX = '/^\s*' .
|
||||
'(?<ms>[+\-])?' . // mantissa sign
|
||||
'(?<m>(?:\d+[\.,]\d*)|(?:\d*[\.,]\d+))' . // mantissa
|
||||
'[Ee](?<es>[+\-])?' . // exponent sign
|
||||
'(?<e>\d+)' . // exponent
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Regex pattern to parse PHP exponent number format.
|
||||
*
|
||||
* @see http://php.net/manual/en/language.types.float.php
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const PHP_EXPONENT_DNUM = '/^' .
|
||||
'(?<ms>[+\-])?' . // sign
|
||||
'(?<m>' .
|
||||
'\d+' . // LNUM
|
||||
'|' .
|
||||
'(?:\d*\.\d+|\d+\.\d*)' . // DNUM
|
||||
')[eE]' .
|
||||
'(?<es>[+\-])?(?<e>\d+)' . // exponent
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Exponent when value is positive or negative infinite.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const INF_EXPONENT = 2047;
|
||||
|
||||
/**
|
||||
* Exponent bias for IEEE 754 double precision float.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const EXP_BIAS = -1023;
|
||||
|
||||
/**
|
||||
* Signed integer mantissa.
|
||||
*/
|
||||
private readonly BigInt $_mantissa;
|
||||
|
||||
/**
|
||||
* Signed integer exponent.
|
||||
*/
|
||||
private readonly BigInt $_exponent;
|
||||
|
||||
/**
|
||||
* Abstract value base.
|
||||
*
|
||||
* Must be 2 or 10.
|
||||
*/
|
||||
private readonly int $_base;
|
||||
|
||||
/**
|
||||
* Whether to encode strictly in DER.
|
||||
*/
|
||||
private bool $_strictDer;
|
||||
|
||||
/**
|
||||
* Number as a native float.
|
||||
*
|
||||
* @internal Lazily initialized
|
||||
*/
|
||||
private ?float $_float = null;
|
||||
|
||||
/**
|
||||
* @param BigInteger|int|string $mantissa Integer mantissa
|
||||
* @param BigInteger|int|string $exponent Integer exponent
|
||||
* @param int $base Base, 2 or 10
|
||||
*/
|
||||
private function __construct(BigInteger|int|string $mantissa, BigInteger|int|string $exponent, int $base = 10)
|
||||
{
|
||||
if ($base !== 10 && $base !== 2) {
|
||||
throw new UnexpectedValueException('Base must be 2 or 10.');
|
||||
}
|
||||
parent::__construct(self::TYPE_REAL);
|
||||
$this->_strictDer = true;
|
||||
$this->_mantissa = BigInt::create($mantissa);
|
||||
$this->_exponent = BigInt::create($exponent);
|
||||
$this->_base = $base;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return sprintf('%g', $this->floatVal());
|
||||
}
|
||||
|
||||
public static function create(
|
||||
BigInteger|int|string $mantissa,
|
||||
BigInteger|int|string $exponent,
|
||||
int $base = 10
|
||||
): self {
|
||||
return new self($mantissa, $exponent, $base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create base 2 real number from float.
|
||||
*/
|
||||
public static function fromFloat(float $number): self
|
||||
{
|
||||
if (is_infinite($number)) {
|
||||
return self::fromInfinite($number);
|
||||
}
|
||||
if (is_nan($number)) {
|
||||
throw new UnexpectedValueException('NaN values not supported.');
|
||||
}
|
||||
[$m, $e] = self::parse754Double(pack('E', $number));
|
||||
return self::create($m, $e, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create base 10 real number from string.
|
||||
*
|
||||
* @param string $number Real number in base-10 textual form
|
||||
*/
|
||||
public static function fromString(string $number): self
|
||||
{
|
||||
[$m, $e] = self::parseString($number);
|
||||
return self::create($m, $e, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with strict DER flag set or unset.
|
||||
*
|
||||
* @param bool $strict whether to encode strictly in DER
|
||||
*/
|
||||
public function withStrictDER(bool $strict): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->_strictDer = $strict;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mantissa.
|
||||
*/
|
||||
public function mantissa(): BigInt
|
||||
{
|
||||
return $this->_mantissa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the exponent.
|
||||
*/
|
||||
public function exponent(): BigInt
|
||||
{
|
||||
return $this->_exponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the base.
|
||||
*/
|
||||
public function base(): int
|
||||
{
|
||||
return $this->_base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number as a float.
|
||||
*/
|
||||
public function floatVal(): float
|
||||
{
|
||||
if (! isset($this->_float)) {
|
||||
$m = $this->_mantissa->toInt();
|
||||
$e = $this->_exponent->toInt();
|
||||
$this->_float = (float) ($m * $this->_base ** $e);
|
||||
}
|
||||
return $this->_float;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number as a NR3 form string conforming to DER rules.
|
||||
*/
|
||||
public function nr3Val(): string
|
||||
{
|
||||
// convert to base 10
|
||||
if ($this->_base === 2) {
|
||||
[$m, $e] = self::parseString(sprintf('%15E', $this->floatVal()));
|
||||
} else {
|
||||
$m = $this->_mantissa->getValue();
|
||||
$e = $this->_exponent->getValue();
|
||||
}
|
||||
$zero = BigInteger::of(0);
|
||||
$ten = BigInteger::of(10);
|
||||
|
||||
// shift trailing zeroes from the mantissa to the exponent
|
||||
// (X.690 07-2002, section 11.3.2.4)
|
||||
while (! $m->isEqualTo($zero) && $m->mod($ten)->isEqualTo($zero)) {
|
||||
$m = $m->dividedBy($ten);
|
||||
$e = $e->plus(1);
|
||||
}
|
||||
// if exponent is zero, it must be prefixed with a "+" sign
|
||||
// (X.690 07-2002, section 11.3.2.6)
|
||||
if ($e->isEqualTo(0)) {
|
||||
$es = '+';
|
||||
} else {
|
||||
$es = $e->isLessThan(0) ? '-' : '';
|
||||
}
|
||||
return sprintf('%s.E%s%s', $m->toBase(10), $es, $e->abs()->toBase(10));
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
$infExponent = BigInteger::of(self::INF_EXPONENT);
|
||||
if ($this->_exponent->getValue()->isEqualTo($infExponent)) {
|
||||
return $this->encodeSpecial();
|
||||
}
|
||||
// if the real value is the value zero, there shall be no contents
|
||||
// octets in the encoding. (X.690 07-2002, section 8.5.2)
|
||||
if ($this->_mantissa->getValue()->toBase(10) === '0') {
|
||||
return '';
|
||||
}
|
||||
if ($this->_base === 10) {
|
||||
return $this->encodeDecimal();
|
||||
}
|
||||
return $this->encodeBinary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode in binary format.
|
||||
*/
|
||||
protected function encodeBinary(): string
|
||||
{
|
||||
/** @var BigInteger $m */
|
||||
/** @var BigInteger $e */
|
||||
/** @var int $sign */
|
||||
[$base, $sign, $m, $e] = $this->prepareBinaryEncoding();
|
||||
$zero = BigInteger::of(0);
|
||||
$byte = 0x80;
|
||||
if ($sign < 0) {
|
||||
$byte |= 0x40;
|
||||
}
|
||||
// normalization: mantissa must be 0 or odd
|
||||
if ($base === 2) {
|
||||
// while last bit is zero
|
||||
while ($m->isGreaterThan(0) && $m->and(0x01)->isEqualTo($zero)) {
|
||||
$m = $m->shiftedRight(1);
|
||||
$e = $e->plus(1);
|
||||
}
|
||||
} elseif ($base === 8) {
|
||||
$byte |= 0x10;
|
||||
// while last 3 bits are zero
|
||||
while ($m->isGreaterThan(0) && $m->and(0x07)->isEqualTo($zero)) {
|
||||
$m = $m->shiftedRight(3);
|
||||
$e = $e->plus(1);
|
||||
}
|
||||
} else { // base === 16
|
||||
$byte |= 0x20;
|
||||
// while last 4 bits are zero
|
||||
while ($m->isGreaterThan(0) && $m->and(0x0f)->isEqualTo($zero)) {
|
||||
$m = $m->shiftedRight(4);
|
||||
$e = $e->plus(1);
|
||||
}
|
||||
}
|
||||
// scale factor
|
||||
$scale = 0;
|
||||
while ($m->isGreaterThan(0) && $m->and(0x01)->isEqualTo($zero)) {
|
||||
$m = $m->shiftedRight(1);
|
||||
++$scale;
|
||||
}
|
||||
$byte |= ($scale & 0x03) << 2;
|
||||
// encode exponent
|
||||
$exp_bytes = (BigInt::create($e))->signedOctets();
|
||||
$exp_len = mb_strlen($exp_bytes, '8bit');
|
||||
if ($exp_len > 0xff) {
|
||||
throw new RangeException('Exponent encoding is too long.');
|
||||
}
|
||||
if ($exp_len <= 3) {
|
||||
$byte |= ($exp_len - 1) & 0x03;
|
||||
$bytes = chr($byte);
|
||||
} else {
|
||||
$byte |= 0x03;
|
||||
$bytes = chr($byte) . chr($exp_len);
|
||||
}
|
||||
$bytes .= $exp_bytes;
|
||||
// encode mantissa
|
||||
$bytes .= (BigInt::create($m))->unsignedOctets();
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode in decimal format.
|
||||
*/
|
||||
protected function encodeDecimal(): string
|
||||
{
|
||||
// encode in NR3 decimal encoding
|
||||
return chr(0x03) . $this->nr3Val();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode special value.
|
||||
*/
|
||||
protected function encodeSpecial(): string
|
||||
{
|
||||
return match ($this->_mantissa->toInt()) {
|
||||
1 => chr(0x40),
|
||||
-1 => chr(0x41),
|
||||
default => throw new LogicException('Invalid special value.'),
|
||||
};
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
// if length is zero, value is zero (spec 8.5.2)
|
||||
if ($length === 0) {
|
||||
$obj = self::create(0, 0, 10);
|
||||
} else {
|
||||
$bytes = mb_substr($data, $idx, $length, '8bit');
|
||||
$byte = ord($bytes[0]);
|
||||
if ((0x80 & $byte) !== 0) { // bit 8 = 1
|
||||
$obj = self::decodeBinaryEncoding($bytes);
|
||||
} elseif ($byte >> 6 === 0x00) { // bit 8 = 0, bit 7 = 0
|
||||
$obj = self::decodeDecimalEncoding($bytes);
|
||||
} else { // bit 8 = 0, bit 7 = 1
|
||||
$obj = self::decodeSpecialRealValue($bytes);
|
||||
}
|
||||
}
|
||||
$offset = $idx + $length;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode binary encoding.
|
||||
*/
|
||||
protected static function decodeBinaryEncoding(string $data): self
|
||||
{
|
||||
$byte = ord($data[0]);
|
||||
// bit 7 is set if mantissa is negative
|
||||
$neg = (bool) (0x40 & $byte);
|
||||
$base = match (($byte >> 4) & 0x03) {
|
||||
0b00 => 2,
|
||||
0b01 => 8,
|
||||
0b10 => 16,
|
||||
default => throw new DecodeException('Reserved REAL binary encoding base not supported.'),
|
||||
};
|
||||
// scaling factor in bits 4 and 3
|
||||
$scale = ($byte >> 2) & 0x03;
|
||||
$idx = 1;
|
||||
// content length in bits 2 and 1
|
||||
$len = ($byte & 0x03) + 1;
|
||||
// if both bits are set, the next octet encodes the length
|
||||
if ($len > 3) {
|
||||
if (mb_strlen($data, '8bit') < 2) {
|
||||
throw new DecodeException('Unexpected end of data while decoding REAL exponent length.');
|
||||
}
|
||||
$len = ord($data[1]);
|
||||
$idx = 2;
|
||||
}
|
||||
if (mb_strlen($data, '8bit') < $idx + $len) {
|
||||
throw new DecodeException('Unexpected end of data while decoding REAL exponent.');
|
||||
}
|
||||
// decode exponent
|
||||
$octets = mb_substr($data, $idx, $len, '8bit');
|
||||
$exp = BigInt::fromSignedOctets($octets)->getValue();
|
||||
if ($base === 8) {
|
||||
$exp = $exp->multipliedBy(3);
|
||||
} elseif ($base === 16) {
|
||||
$exp = $exp->multipliedBy(4);
|
||||
}
|
||||
if (mb_strlen($data, '8bit') <= $idx + $len) {
|
||||
throw new DecodeException('Unexpected end of data while decoding REAL mantissa.');
|
||||
}
|
||||
// decode mantissa
|
||||
$octets = mb_substr($data, $idx + $len, null, '8bit');
|
||||
$n = BigInt::fromUnsignedOctets($octets)->getValue();
|
||||
$n = $n->multipliedBy(2 ** $scale);
|
||||
if ($neg) {
|
||||
$n = $n->negated();
|
||||
}
|
||||
return self::create($n, $exp, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode decimal encoding.
|
||||
*/
|
||||
protected static function decodeDecimalEncoding(string $data): self
|
||||
{
|
||||
$nr = ord($data[0]) & 0x3f;
|
||||
if (! in_array($nr, [1, 2, 3], true)) {
|
||||
throw new DecodeException('Unsupported decimal encoding form.');
|
||||
}
|
||||
$str = mb_substr($data, 1, null, '8bit');
|
||||
return self::fromString($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode special encoding.
|
||||
*/
|
||||
protected static function decodeSpecialRealValue(string $data): self
|
||||
{
|
||||
if (mb_strlen($data, '8bit') !== 1) {
|
||||
throw new DecodeException('SpecialRealValue must have one content octet.');
|
||||
}
|
||||
$byte = ord($data[0]);
|
||||
if ($byte === 0x40) { // positive infinity
|
||||
return self::fromInfinite(INF);
|
||||
}
|
||||
if ($byte === 0x41) { // negative infinity
|
||||
return self::fromInfinite(-INF);
|
||||
}
|
||||
throw new DecodeException('Invalid SpecialRealValue encoding.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare value for binary encoding.
|
||||
*
|
||||
* @return array<int|BigInteger> (int) base, (int) sign, (BigInteger) mantissa and (BigInteger) exponent
|
||||
*/
|
||||
protected function prepareBinaryEncoding(): array
|
||||
{
|
||||
$base = 2;
|
||||
$m = $this->_mantissa->getValue();
|
||||
$ms = $m->getSign();
|
||||
$m = BigInteger::of($m->abs());
|
||||
$e = $this->_exponent->getValue();
|
||||
$es = $e->getSign();
|
||||
$e = BigInteger::of($e->abs());
|
||||
$zero = BigInteger::of(0);
|
||||
$three = BigInteger::of(3);
|
||||
$four = BigInteger::of(4);
|
||||
// DER uses only base 2 binary encoding
|
||||
if (! $this->_strictDer) {
|
||||
if ($e->mod($four)->isEqualTo($zero)) {
|
||||
$base = 16;
|
||||
$e = $e->dividedBy(4);
|
||||
} elseif ($e->mod($three)->isEqualTo($zero)) {
|
||||
$base = 8;
|
||||
$e = $e->dividedBy(3);
|
||||
}
|
||||
}
|
||||
return [$base, $ms, $m, $e->multipliedBy($es)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from INF or -INF.
|
||||
*/
|
||||
private static function fromInfinite(float $inf): self
|
||||
{
|
||||
return self::create($inf === -INF ? -1 : 1, self::INF_EXPONENT, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse IEEE 754 big endian formatted double precision float to base 2 mantissa and exponent.
|
||||
*
|
||||
* @param string $octets 64 bits
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parse754Double(string $octets): array
|
||||
{
|
||||
$n = BigInteger::fromBytes($octets, false);
|
||||
// sign bit
|
||||
$neg = $n->testBit(63);
|
||||
// 11 bits of biased exponent
|
||||
$exponentMask = BigInteger::fromBase('7ff0000000000000', 16);
|
||||
$exp = $n->and($exponentMask)
|
||||
->shiftedRight(52)
|
||||
->plus(self::EXP_BIAS);
|
||||
|
||||
// 52 bits of mantissa
|
||||
$mantissaMask = BigInteger::fromBase('fffffffffffff', 16);
|
||||
$man = $n->and($mantissaMask);
|
||||
// zero, ASN.1 doesn't differentiate -0 from +0
|
||||
$zero = BigInteger::of(0);
|
||||
if ($exp->isEqualTo(self::EXP_BIAS) && $man->isEqualTo($zero)) {
|
||||
return [BigInteger::of(0), BigInteger::of(0)];
|
||||
}
|
||||
// denormalized value, shift binary point
|
||||
if ($exp->isEqualTo(self::EXP_BIAS)) {
|
||||
$exp = $exp->plus(1);
|
||||
} // normalized value, insert implicit leading one before the binary point
|
||||
else {
|
||||
$man = $man->or(BigInteger::of(1)->shiftedLeft(52));
|
||||
}
|
||||
|
||||
// find the last fraction bit that is set
|
||||
$last = 0;
|
||||
while (! $man->testBit($last) && $last !== 52) {
|
||||
$last++;
|
||||
}
|
||||
|
||||
$bits_for_fraction = 52 - $last;
|
||||
// adjust mantissa and exponent so that we have integer values
|
||||
$man = $man->shiftedRight($last);
|
||||
$exp = $exp->minus($bits_for_fraction);
|
||||
// negate mantissa if number was negative
|
||||
if ($neg) {
|
||||
$man = $man->negated();
|
||||
}
|
||||
return [$man, $exp];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse textual REAL number to base 10 mantissa and exponent.
|
||||
*
|
||||
* @param string $str Number
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parseString(string $str): array
|
||||
{
|
||||
// PHP exponent format
|
||||
if (preg_match(self::PHP_EXPONENT_DNUM, $str, $match) === 1) {
|
||||
[$m, $e] = self::parsePHPExponentMatch($match);
|
||||
} // NR3 format
|
||||
elseif (preg_match(self::NR3_REGEX, $str, $match) === 1) {
|
||||
[$m, $e] = self::parseNR3Match($match);
|
||||
} // NR2 format
|
||||
elseif (preg_match(self::NR2_REGEX, $str, $match) === 1) {
|
||||
[$m, $e] = self::parseNR2Match($match);
|
||||
} // NR1 format
|
||||
elseif (preg_match(self::NR1_REGEX, $str, $match) === 1) {
|
||||
[$m, $e] = self::parseNR1Match($match);
|
||||
} // invalid number
|
||||
else {
|
||||
throw new UnexpectedValueException("{$str} could not be parsed to REAL.");
|
||||
}
|
||||
// normalize so that mantissa has no trailing zeroes
|
||||
$zero = BigInteger::of(0);
|
||||
$ten = BigInteger::of(10);
|
||||
while (! $m->isEqualTo($zero) && $m->mod($ten)->isEqualTo($zero)) {
|
||||
$m = $m->dividedBy($ten);
|
||||
$e = $e->plus(1);
|
||||
}
|
||||
return [$m, $e];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse PHP form float to base 10 mantissa and exponent.
|
||||
*
|
||||
* @param array<string> $match Regexp match
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parsePHPExponentMatch(array $match): array
|
||||
{
|
||||
// mantissa sign
|
||||
$ms = $match['ms'] === '-' ? -1 : 1;
|
||||
$m_parts = explode('.', $match['m']);
|
||||
// integer part of the mantissa
|
||||
$int = ltrim($m_parts[0], '0');
|
||||
// exponent sign
|
||||
$es = $match['es'] === '-' ? -1 : 1;
|
||||
// signed exponent
|
||||
$e = BigInteger::of($match['e'])->multipliedBy($es);
|
||||
// if mantissa had fractional part
|
||||
if (count($m_parts) === 2) {
|
||||
$frac = rtrim($m_parts[1], '0');
|
||||
$e = $e->minus(mb_strlen($frac, '8bit'));
|
||||
$int .= $frac;
|
||||
}
|
||||
$m = BigInteger::of($int)->multipliedBy($ms);
|
||||
return [$m, $e];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse NR3 form number to base 10 mantissa and exponent.
|
||||
*
|
||||
* @param array<string> $match Regexp match
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parseNR3Match(array $match): array
|
||||
{
|
||||
// mantissa sign
|
||||
$ms = $match['ms'] === '-' ? -1 : 1;
|
||||
// explode mantissa to integer and fraction parts
|
||||
[$int, $frac] = explode('.', str_replace(',', '.', $match['m']));
|
||||
$int = ltrim($int, '0');
|
||||
$frac = rtrim($frac, '0');
|
||||
// exponent sign
|
||||
$es = $match['es'] === '-' ? -1 : 1;
|
||||
// signed exponent
|
||||
$e = BigInteger::of($match['e'])->multipliedBy($es);
|
||||
// shift exponent by the number of base 10 fractions
|
||||
$e = $e->minus(mb_strlen($frac, '8bit'));
|
||||
// insert fractions to integer part and produce signed mantissa
|
||||
$int .= $frac;
|
||||
if ($int === '') {
|
||||
$int = '0';
|
||||
}
|
||||
$m = BigInteger::of($int)->multipliedBy($ms);
|
||||
return [$m, $e];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse NR2 form number to base 10 mantissa and exponent.
|
||||
*
|
||||
* @param array<string> $match Regexp match
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parseNR2Match(array $match): array
|
||||
{
|
||||
$sign = $match['s'] === '-' ? -1 : 1;
|
||||
// explode decimal number to integer and fraction parts
|
||||
[$int, $frac] = explode('.', str_replace(',', '.', $match['d']));
|
||||
$int = ltrim($int, '0');
|
||||
$frac = rtrim($frac, '0');
|
||||
// shift exponent by the number of base 10 fractions
|
||||
$e = BigInteger::of(0);
|
||||
$e = $e->minus(mb_strlen($frac, '8bit'));
|
||||
// insert fractions to integer part and produce signed mantissa
|
||||
$int .= $frac;
|
||||
if ($int === '') {
|
||||
$int = '0';
|
||||
}
|
||||
$m = BigInteger::of($int)->multipliedBy($sign);
|
||||
return [$m, $e];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse NR1 form number to base 10 mantissa and exponent.
|
||||
*
|
||||
* @param array<string> $match Regexp match
|
||||
*
|
||||
* @return BigInteger[] Tuple of mantissa and exponent
|
||||
*/
|
||||
private static function parseNR1Match(array $match): array
|
||||
{
|
||||
$sign = $match['s'] === '-' ? -1 : 1;
|
||||
$int = ltrim($match['i'], '0');
|
||||
if ($int === '') {
|
||||
$int = '0';
|
||||
}
|
||||
$m = BigInteger::of($int)->multipliedBy($sign);
|
||||
return [$m, BigInteger::of(0)];
|
||||
}
|
||||
}
|
||||
163
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/RelativeOID.php
vendored
Normal file
163
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/RelativeOID.php
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function chr;
|
||||
use function is_int;
|
||||
use function ord;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
use Throwable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements *RELATIVE-OID* type.
|
||||
*/
|
||||
final class RelativeOID extends Element
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* Object identifier split to sub ID's.
|
||||
*
|
||||
* @var BigInteger[]
|
||||
*/
|
||||
private readonly array $subids;
|
||||
|
||||
/**
|
||||
* @param string $oid OID in dotted format
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $oid
|
||||
) {
|
||||
parent::__construct(self::TYPE_RELATIVE_OID);
|
||||
$this->subids = self::explodeDottedOID($oid);
|
||||
}
|
||||
|
||||
public static function create(string $oid): self
|
||||
{
|
||||
return new self($oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID in dotted format.
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return self::encodeSubIDs(...$this->subids);
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$len = Length::expectFromDER($data, $idx)->intLength();
|
||||
$subids = self::decodeSubIDs(mb_substr($data, $idx, $len, '8bit'));
|
||||
$offset = $idx + $len;
|
||||
return self::create(self::implodeSubIDs(...$subids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode dotted OID to an array of sub ID's.
|
||||
*
|
||||
* @param string $oid OID in dotted format
|
||||
*
|
||||
* @return BigInteger[] Array of BigInteger numbers
|
||||
*/
|
||||
protected static function explodeDottedOID(string $oid): array
|
||||
{
|
||||
$subids = [];
|
||||
if ($oid !== '') {
|
||||
foreach (explode('.', $oid) as $subid) {
|
||||
try {
|
||||
$n = BigInteger::of($subid);
|
||||
$subids[] = $n;
|
||||
} catch (Throwable $e) {
|
||||
throw new UnexpectedValueException(sprintf('"%s" is not a number.', $subid), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $subids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode an array of sub IDs to dotted OID format.
|
||||
*/
|
||||
protected static function implodeSubIDs(BigInteger ...$subids): string
|
||||
{
|
||||
return implode('.', array_map(static fn ($num) => $num->toBase(10), $subids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode sub ID's to DER.
|
||||
*/
|
||||
protected static function encodeSubIDs(BigInteger ...$subids): string
|
||||
{
|
||||
$data = '';
|
||||
foreach ($subids as $subid) {
|
||||
// if number fits to one base 128 byte
|
||||
if ($subid->isLessThan(128)) {
|
||||
$data .= chr($subid->toInt());
|
||||
} else { // encode to multiple bytes
|
||||
$bytes = [];
|
||||
do {
|
||||
array_unshift($bytes, 0x7f & $subid->toInt());
|
||||
$subid = $subid->shiftedRight(7);
|
||||
} while ($subid->isGreaterThan(0));
|
||||
// all bytes except last must have bit 8 set to one
|
||||
foreach (array_splice($bytes, 0, -1) as $byte) {
|
||||
$data .= chr(0x80 | $byte);
|
||||
}
|
||||
$byte = reset($bytes);
|
||||
if (! is_int($byte)) {
|
||||
throw new RuntimeException('Encoding failed');
|
||||
}
|
||||
$data .= chr($byte);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode sub ID's from DER data.
|
||||
*
|
||||
* @return BigInteger[] Array of BigInteger numbers
|
||||
*/
|
||||
protected static function decodeSubIDs(string $data): array
|
||||
{
|
||||
$subids = [];
|
||||
$idx = 0;
|
||||
$end = mb_strlen($data, '8bit');
|
||||
while ($idx < $end) {
|
||||
$num = BigInteger::of(0);
|
||||
while (true) {
|
||||
if ($idx >= $end) {
|
||||
throw new DecodeException('Unexpected end of data.');
|
||||
}
|
||||
$byte = ord($data[$idx++]);
|
||||
$num = $num->or($byte & 0x7f);
|
||||
// bit 8 of the last octet is zero
|
||||
if (0 === ($byte & 0x80)) {
|
||||
break;
|
||||
}
|
||||
$num = $num->shiftedLeft(7);
|
||||
}
|
||||
$subids[] = $num;
|
||||
}
|
||||
return $subids;
|
||||
}
|
||||
}
|
||||
33
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/T61String.php
vendored
Normal file
33
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/T61String.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *T61String* type.
|
||||
*/
|
||||
final class T61String extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_T61_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// allow everything since there's literally
|
||||
// thousands of allowed characters (16 bit composed characters)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
81
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UTCTime.php
vendored
Normal file
81
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UTCTime.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\BaseTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *UTCTime* type.
|
||||
*/
|
||||
final class UTCTime extends BaseTime
|
||||
{
|
||||
use UniversalClass;
|
||||
use PrimitiveType;
|
||||
|
||||
/**
|
||||
* Regular expression to parse date.
|
||||
*
|
||||
* DER restricts format to UTC timezone (Z suffix).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const REGEX = '#^' .
|
||||
'(\d\d)' . // YY
|
||||
'(\d\d)' . // MM
|
||||
'(\d\d)' . // DD
|
||||
'(\d\d)' . // hh
|
||||
'(\d\d)' . // mm
|
||||
'(\d\d)' . // ss
|
||||
'Z' . // TZ
|
||||
'$#';
|
||||
|
||||
private function __construct(DateTimeImmutable $dt)
|
||||
{
|
||||
parent::__construct(self::TYPE_UTC_TIME, $dt);
|
||||
}
|
||||
|
||||
public static function create(DateTimeImmutable $dt): self
|
||||
{
|
||||
return new self($dt);
|
||||
}
|
||||
|
||||
public static function fromString(string $time): static
|
||||
{
|
||||
return new static(new DateTimeImmutable($time, new DateTimeZone('UTC')));
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
$dt = $this->dateTime->setTimezone(new DateTimeZone('UTC'));
|
||||
return $dt->format('ymdHis\\Z');
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
$str = mb_substr($data, $idx, $length, '8bit');
|
||||
$idx += $length;
|
||||
if (preg_match(self::REGEX, $str, $match) !== 1) {
|
||||
throw new DecodeException('Invalid UTCTime format.');
|
||||
}
|
||||
[, $year, $month, $day, $hour, $minute, $second] = $match;
|
||||
$time = $year . $month . $day . $hour . $minute . $second . self::TZ_UTC;
|
||||
$dt = DateTimeImmutable::createFromFormat('!ymdHisT', $time, new DateTimeZone('UTC'));
|
||||
if ($dt === false) {
|
||||
throw new DecodeException('Failed to decode UTCTime');
|
||||
}
|
||||
$offset = $idx;
|
||||
return self::create($dt);
|
||||
}
|
||||
}
|
||||
33
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UTF8String.php
vendored
Normal file
33
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UTF8String.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *UTF8String* type.
|
||||
*
|
||||
* UTF8String* is an Unicode string with UTF-8 encoding.
|
||||
*/
|
||||
final class UTF8String extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_UTF8_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
return mb_check_encoding($string, 'UTF-8');
|
||||
}
|
||||
}
|
||||
38
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UniversalString.php
vendored
Normal file
38
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/UniversalString.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *UniversalString* type.
|
||||
*
|
||||
* Universal string is an Unicode string with UCS-4 encoding.
|
||||
*/
|
||||
final class UniversalString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_UNIVERSAL_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// UCS-4 has fixed with of 4 octets (32 bits)
|
||||
if (mb_strlen($string, '8bit') % 4 !== 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/VideotexString.php
vendored
Normal file
32
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/VideotexString.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *VideotexString* type.
|
||||
*/
|
||||
final class VideotexString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_VIDEOTEX_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
// allow everything
|
||||
return true;
|
||||
}
|
||||
}
|
||||
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/VisibleString.php
vendored
Normal file
31
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Primitive/VisibleString.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Primitive;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\PrimitiveString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UniversalClass;
|
||||
|
||||
/**
|
||||
* Implements *VisibleString* type.
|
||||
*/
|
||||
final class VisibleString extends PrimitiveString
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
private function __construct(string $string)
|
||||
{
|
||||
parent::__construct(self::TYPE_VISIBLE_STRING, $string);
|
||||
}
|
||||
|
||||
public static function create(string $string): self
|
||||
{
|
||||
return new self($string);
|
||||
}
|
||||
|
||||
protected function validateString(string $string): bool
|
||||
{
|
||||
return preg_match('/[^\x20-\x7e]/', $string) !== 1;
|
||||
}
|
||||
}
|
||||
40
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/PrimitiveString.php
vendored
Normal file
40
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/PrimitiveString.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
|
||||
/**
|
||||
* Base class for primitive strings.
|
||||
*
|
||||
* Used by types that don't require special processing of the encoded string data.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class PrimitiveString extends BaseString
|
||||
{
|
||||
use PrimitiveType;
|
||||
|
||||
abstract public static function create(string $string): self;
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): static
|
||||
{
|
||||
$idx = $offset;
|
||||
if (! $identifier->isPrimitive()) {
|
||||
throw new DecodeException('DER encoded string must be primitive.');
|
||||
}
|
||||
$length = Length::expectFromDER($data, $idx)->intLength();
|
||||
$str = $length === 0 ? '' : mb_substr($data, $idx, $length, '8bit');
|
||||
$offset = $idx + $length;
|
||||
try {
|
||||
return static::create($str);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecodeException($e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/PrimitiveType.php
vendored
Normal file
19
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/PrimitiveType.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
/**
|
||||
* Trait for primitive types.
|
||||
*/
|
||||
trait PrimitiveType
|
||||
{
|
||||
/**
|
||||
* @see \Sop\ASN1\Feature\ElementBase::isConstructed()
|
||||
*/
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
16
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/StringType.php
vendored
Normal file
16
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/StringType.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\Stringable;
|
||||
|
||||
/**
|
||||
* Interface to mark types that correspond to ASN.1 specification's character strings. That being all simple strings and
|
||||
* time types.
|
||||
*/
|
||||
interface StringType extends ElementBase, Stringable
|
||||
{
|
||||
}
|
||||
281
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Structure.php
vendored
Normal file
281
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Structure.php
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection ALL */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use ArrayIterator;
|
||||
use function count;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use LogicException;
|
||||
use OutOfBoundsException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
|
||||
/**
|
||||
* Base class for the constructed types.
|
||||
*/
|
||||
abstract class Structure extends Element implements Countable, IteratorAggregate
|
||||
{
|
||||
use UniversalClass;
|
||||
|
||||
/**
|
||||
* Array of elements in the structure.
|
||||
*
|
||||
* @var Element[]
|
||||
*/
|
||||
protected array $elements;
|
||||
|
||||
/**
|
||||
* Lookup table for the tagged elements.
|
||||
*
|
||||
* @var null|Element[]
|
||||
*/
|
||||
private ?array $taggedMap = null;
|
||||
|
||||
/**
|
||||
* Cache variable of elements wrapped into `UnspecifiedType` objects.
|
||||
*
|
||||
* @var null|UnspecifiedType[]
|
||||
*/
|
||||
private ?array $unspecifiedTypes = null;
|
||||
|
||||
/**
|
||||
* @param ElementBase ...$elements Any number of elements
|
||||
*/
|
||||
protected function __construct(int $typeTag, ElementBase ...$elements)
|
||||
{
|
||||
parent::__construct($typeTag);
|
||||
$this->elements = array_map(static fn (ElementBase $el) => $el->asElement(), $elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone magic method.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
// clear cache-variables
|
||||
$this->taggedMap = null;
|
||||
$this->unspecifiedTypes = null;
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explode DER structure to DER encoded components that it contains.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function explodeDER(string $data): array
|
||||
{
|
||||
$offset = 0;
|
||||
$identifier = Identifier::fromDER($data, $offset);
|
||||
if (! $identifier->isConstructed()) {
|
||||
throw new DecodeException('Element is not constructed.');
|
||||
}
|
||||
$length = Length::expectFromDER($data, $offset);
|
||||
if ($length->isIndefinite()) {
|
||||
throw new DecodeException('Explode not implemented for indefinite length encoding.');
|
||||
}
|
||||
$end = $offset + $length->intLength();
|
||||
$parts = [];
|
||||
while ($offset < $end) {
|
||||
// start of the element
|
||||
$idx = $offset;
|
||||
// skip identifier
|
||||
Identifier::fromDER($data, $offset);
|
||||
// decode element length
|
||||
$length = Length::expectFromDER($data, $offset)->intLength();
|
||||
// extract der encoding of the element
|
||||
$parts[] = mb_substr($data, $idx, $offset - $idx + $length, '8bit');
|
||||
// update offset over content
|
||||
$offset += $length;
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with an element at the given index replaced by another.
|
||||
*
|
||||
* @param int $idx Element index
|
||||
* @param Element $el New element to insert into the structure
|
||||
*/
|
||||
public function withReplaced(int $idx, Element $el): self
|
||||
{
|
||||
if (! isset($this->elements[$idx])) {
|
||||
throw new OutOfBoundsException("Structure doesn't have element at index {$idx}.");
|
||||
}
|
||||
$obj = clone $this;
|
||||
$obj->elements[$idx] = $el;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with an element inserted before the given index.
|
||||
*
|
||||
* @param int $idx Element index
|
||||
* @param Element $el New element to insert into the structure
|
||||
*/
|
||||
public function withInserted(int $idx, Element $el): self
|
||||
{
|
||||
if (count($this->elements) < $idx || $idx < 0) {
|
||||
throw new OutOfBoundsException("Index {$idx} is out of bounds.");
|
||||
}
|
||||
$obj = clone $this;
|
||||
array_splice($obj->elements, $idx, 0, [$el]);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with an element appended to the end.
|
||||
*
|
||||
* @param Element $el Element to insert into the structure
|
||||
*/
|
||||
public function withAppended(Element $el): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
array_push($obj->elements, $el);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with an element prepended in the beginning.
|
||||
*
|
||||
* @param Element $el Element to insert into the structure
|
||||
*/
|
||||
public function withPrepended(Element $el): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
array_unshift($obj->elements, $el);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with an element at the given index removed.
|
||||
*
|
||||
* @param int $idx Element index
|
||||
*/
|
||||
public function withoutElement(int $idx): self
|
||||
{
|
||||
if (! isset($this->elements[$idx])) {
|
||||
throw new OutOfBoundsException("Structure doesn't have element at index {$idx}.");
|
||||
}
|
||||
$obj = clone $this;
|
||||
array_splice($obj->elements, $idx, 1);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get elements in the structure.
|
||||
*
|
||||
* @return UnspecifiedType[]
|
||||
*/
|
||||
public function elements(): array
|
||||
{
|
||||
if (! isset($this->unspecifiedTypes)) {
|
||||
$this->unspecifiedTypes = array_map(
|
||||
static fn (Element $el) => UnspecifiedType::create($el),
|
||||
$this->elements
|
||||
);
|
||||
}
|
||||
return $this->unspecifiedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the structure has an element at the given index, optionally satisfying given tag expectation.
|
||||
*
|
||||
* @param int $idx Index 0..n
|
||||
* @param null|int $expectedTag Optional type tag expectation
|
||||
*/
|
||||
public function has(int $idx, ?int $expectedTag = null): bool
|
||||
{
|
||||
if (! isset($this->elements[$idx])) {
|
||||
return false;
|
||||
}
|
||||
if (isset($expectedTag)) {
|
||||
if (! $this->elements[$idx]->isType($expectedTag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element at the given index, optionally checking that the element has a given tag.
|
||||
*
|
||||
* @param int $idx Index 0..n
|
||||
*/
|
||||
public function at(int $idx): UnspecifiedType
|
||||
{
|
||||
if (! isset($this->elements[$idx])) {
|
||||
throw new OutOfBoundsException("Structure doesn't have an element at index {$idx}.");
|
||||
}
|
||||
return UnspecifiedType::create($this->elements[$idx]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the structure contains a context specific element with a given tag.
|
||||
*
|
||||
* @param int $tag Tag number
|
||||
*/
|
||||
public function hasTagged(int $tag): bool
|
||||
{
|
||||
// lazily build lookup map
|
||||
if (! isset($this->taggedMap)) {
|
||||
$this->taggedMap = [];
|
||||
foreach ($this->elements as $element) {
|
||||
if ($element->isTagged()) {
|
||||
$this->taggedMap[$element->tag()] = $element;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isset($this->taggedMap[$tag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a context specific element tagged with a given tag.
|
||||
*/
|
||||
public function getTagged(int $tag): TaggedType
|
||||
{
|
||||
if (! $this->hasTagged($tag)) {
|
||||
throw new LogicException("No tagged element for tag {$tag}.");
|
||||
}
|
||||
return $this->taggedMap[$tag];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the `UnspecifiedElement` objects.
|
||||
*
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->elements());
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
$data = '';
|
||||
foreach ($this->elements as $element) {
|
||||
$data .= $element->toDER();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ApplicationType.php
vendored
Normal file
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ApplicationType.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
/**
|
||||
* Intermediate class to store DER data of an application specific type.
|
||||
*/
|
||||
final class ApplicationType extends DERTaggedType
|
||||
{
|
||||
}
|
||||
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ContextSpecificType.php
vendored
Normal file
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ContextSpecificType.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
/**
|
||||
* Intermediate class to store DER data of context specific type.
|
||||
*/
|
||||
final class ContextSpecificType extends DERTaggedType
|
||||
{
|
||||
}
|
||||
107
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/DERTaggedType.php
vendored
Normal file
107
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/DERTaggedType.php
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Length;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\TaggedType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Intermediate class to store tagged DER data.
|
||||
*
|
||||
* `implicit($tag)` or `explicit()` method is used to decode the actual element, which is only known by the abstract
|
||||
* syntax of data structure.
|
||||
*
|
||||
* May be encoded back to complete DER encoding.
|
||||
*/
|
||||
class DERTaggedType extends TaggedType implements ExplicitTagging, ImplicitTagging
|
||||
{
|
||||
/**
|
||||
* @param Identifier $_identifier Pre-parsed identifier
|
||||
* @param string $_data DER data
|
||||
* @param int $_offset Offset to next byte after identifier
|
||||
* @param int $_valueOffset Offset to content
|
||||
* @param int $_valueLength Content length
|
||||
*/
|
||||
final private function __construct(
|
||||
private readonly Identifier $_identifier,
|
||||
private readonly string $_data,
|
||||
private readonly int $_offset,
|
||||
private readonly int $_valueOffset,
|
||||
private readonly int $_valueLength,
|
||||
bool $indefinite_length
|
||||
) {
|
||||
parent::__construct($_identifier->intTag(), $indefinite_length);
|
||||
}
|
||||
|
||||
public static function create(
|
||||
Identifier $_identifier,
|
||||
string $_data,
|
||||
int $_offset,
|
||||
int $_valueOffset,
|
||||
int $_valueLength,
|
||||
bool $indefinite_length
|
||||
): static {
|
||||
return new static($_identifier, $_data, $_offset, $_valueOffset, $_valueLength, $indefinite_length);
|
||||
}
|
||||
|
||||
public function typeClass(): int
|
||||
{
|
||||
return $this->_identifier->typeClass();
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return $this->_identifier->isConstructed();
|
||||
}
|
||||
|
||||
public function implicit(int $tag, int $class = Identifier::CLASS_UNIVERSAL): UnspecifiedType
|
||||
{
|
||||
$identifier = $this->_identifier->withClass($class)
|
||||
->withTag($tag);
|
||||
$cls = self::determineImplClass($identifier);
|
||||
$idx = $this->_offset;
|
||||
/** @var ElementBase $element */
|
||||
$element = $cls::decodeFromDER($identifier, $this->_data, $idx);
|
||||
return $element->asUnspecified();
|
||||
}
|
||||
|
||||
public function explicit(): UnspecifiedType
|
||||
{
|
||||
$idx = $this->_valueOffset;
|
||||
return Element::fromDER($this->_data, $idx)->asUnspecified();
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
$idx = $offset;
|
||||
$length = Length::expectFromDER($data, $idx);
|
||||
// offset to inner value
|
||||
$value_offset = $idx;
|
||||
if ($length->isIndefinite()) {
|
||||
if ($identifier->isPrimitive()) {
|
||||
throw new DecodeException('Primitive type with indefinite length is not supported.');
|
||||
}
|
||||
// EOC consists of two octets.
|
||||
$value_length = $idx - $value_offset - 2;
|
||||
} else {
|
||||
$value_length = $length->intLength();
|
||||
$idx += $value_length;
|
||||
}
|
||||
// late static binding since ApplicationType and PrivateType extend this class
|
||||
$type = static::create($identifier, $data, $offset, $value_offset, $value_length, $length->isIndefinite());
|
||||
$offset = $idx;
|
||||
return $type;
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
return mb_substr($this->_data, $this->_valueOffset, $this->_valueLength, '8bit');
|
||||
}
|
||||
}
|
||||
19
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ExplicitTagging.php
vendored
Normal file
19
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ExplicitTagging.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Interface for classes providing explicit tagging.
|
||||
*/
|
||||
interface ExplicitTagging extends ElementBase
|
||||
{
|
||||
/**
|
||||
* Get explicitly tagged wrapped element.
|
||||
*/
|
||||
public function explicit(): UnspecifiedType;
|
||||
}
|
||||
45
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ExplicitlyTaggedType.php
vendored
Normal file
45
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ExplicitlyTaggedType.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use BadMethodCallException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Implements explicit tagging mode.
|
||||
*
|
||||
* Explicit tagging wraps a type by prepending a tag. Underlying DER encoding is not changed.
|
||||
*/
|
||||
final class ExplicitlyTaggedType extends TaggedTypeWrap implements ExplicitTagging
|
||||
{
|
||||
public static function create(int $tag, Element $element, int $class = Identifier::CLASS_CONTEXT_SPECIFIC): self
|
||||
{
|
||||
return new self($element, $class, $tag);
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function explicit(): UnspecifiedType
|
||||
{
|
||||
return $this->element->asUnspecified();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
// get the full encoding of the wrapped element
|
||||
return $this->element->toDER();
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
throw new BadMethodCallException(__METHOD__ . ' must be implemented in derived class.');
|
||||
}
|
||||
}
|
||||
23
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ImplicitTagging.php
vendored
Normal file
23
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ImplicitTagging.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Interface for classes providing implicit tagging.
|
||||
*/
|
||||
interface ImplicitTagging extends ElementBase
|
||||
{
|
||||
/**
|
||||
* Get implicitly tagged wrapped element.
|
||||
*
|
||||
* @param int $tag Tag of the element
|
||||
* @param int $class Expected type class of the element
|
||||
*/
|
||||
public function implicit(int $tag, int $class = Identifier::CLASS_UNIVERSAL): UnspecifiedType;
|
||||
}
|
||||
58
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ImplicitlyTaggedType.php
vendored
Normal file
58
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/ImplicitlyTaggedType.php
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use BadMethodCallException;
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements implicit tagging mode.
|
||||
*
|
||||
* Implicit tagging changes the tag of the tagged type. This changes the DER encoding of the type, and hence the
|
||||
* abstract syntax must be known when decoding the data.
|
||||
*/
|
||||
final class ImplicitlyTaggedType extends TaggedTypeWrap implements ImplicitTagging
|
||||
{
|
||||
public static function create(int $tag, Element $element, int $class = Identifier::CLASS_CONTEXT_SPECIFIC): self
|
||||
{
|
||||
return new self($element, $class, $tag);
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
// depends on the underlying type
|
||||
return $this->element->isConstructed();
|
||||
}
|
||||
|
||||
public function implicit(int $tag, int $class = Identifier::CLASS_UNIVERSAL): UnspecifiedType
|
||||
{
|
||||
$this->element->expectType($tag);
|
||||
if ($this->element->typeClass() !== $class) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf(
|
||||
'Type class %s expected, got %s.',
|
||||
Identifier::classToName($class),
|
||||
Identifier::classToName($this->element->typeClass())
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this->element->asUnspecified();
|
||||
}
|
||||
|
||||
protected function encodedAsDER(): string
|
||||
{
|
||||
// get only the content of the wrapped element.
|
||||
return $this->element->encodedAsDER();
|
||||
}
|
||||
|
||||
protected static function decodeFromDER(Identifier $identifier, string $data, int &$offset): ElementBase
|
||||
{
|
||||
throw new BadMethodCallException(__METHOD__ . ' must be implemented in derived class.');
|
||||
}
|
||||
}
|
||||
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/PrivateType.php
vendored
Normal file
12
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/PrivateType.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
/**
|
||||
* Intermediate class to store DER data of a private tagging type.
|
||||
*/
|
||||
final class PrivateType extends DERTaggedType
|
||||
{
|
||||
}
|
||||
27
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/TaggedTypeWrap.php
vendored
Normal file
27
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/Tagged/TaggedTypeWrap.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type\Tagged;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\TaggedType;
|
||||
|
||||
/**
|
||||
* Base class to wrap inner element for tagging.
|
||||
*/
|
||||
abstract class TaggedTypeWrap extends TaggedType
|
||||
{
|
||||
protected function __construct(
|
||||
protected readonly Element $element,
|
||||
private readonly int $class,
|
||||
int $typeTag
|
||||
) {
|
||||
parent::__construct($typeTag);
|
||||
}
|
||||
|
||||
public function typeClass(): int
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
}
|
||||
78
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/TaggedType.php
vendored
Normal file
78
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/TaggedType.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\ExplicitTagging;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\ImplicitTagging;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Base class for context-specific types.
|
||||
*/
|
||||
abstract class TaggedType extends Element
|
||||
{
|
||||
/**
|
||||
* Check whether element supports explicit tagging.
|
||||
*
|
||||
* @param null|int $expectedTag Optional outer tag expectation
|
||||
*/
|
||||
public function expectExplicit(?int $expectedTag = null): ExplicitTagging
|
||||
{
|
||||
$el = $this;
|
||||
if (! $el instanceof ExplicitTagging) {
|
||||
throw new UnexpectedValueException("Element doesn't implement explicit tagging.");
|
||||
}
|
||||
if (isset($expectedTag)) {
|
||||
$el->expectTagged($expectedTag);
|
||||
}
|
||||
return $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped inner element employing explicit tagging.
|
||||
*
|
||||
* @param null|int $expectedTag Optional outer tag expectation
|
||||
*/
|
||||
public function asExplicit(?int $expectedTag = null): UnspecifiedType
|
||||
{
|
||||
return $this->expectExplicit($expectedTag)
|
||||
->explicit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether element supports implicit tagging.
|
||||
*
|
||||
* @param null|int $expectedTag Optional outer tag expectation
|
||||
*/
|
||||
public function expectImplicit(?int $expectedTag = null): ImplicitTagging
|
||||
{
|
||||
$el = $this;
|
||||
if (! $el instanceof ImplicitTagging) {
|
||||
throw new UnexpectedValueException("Element doesn't implement implicit tagging.");
|
||||
}
|
||||
if (isset($expectedTag)) {
|
||||
$el->expectTagged($expectedTag);
|
||||
}
|
||||
return $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped inner element employing implicit tagging.
|
||||
*
|
||||
* @param int $tag Type tag of the inner element
|
||||
* @param null|int $expectedTag Optional outer tag expectation
|
||||
* @param int $expectedClass Optional inner type class expectation
|
||||
*/
|
||||
public function asImplicit(
|
||||
int $tag,
|
||||
?int $expectedTag = null,
|
||||
int $expectedClass = Identifier::CLASS_UNIVERSAL
|
||||
): UnspecifiedType {
|
||||
return $this->expectImplicit($expectedTag)
|
||||
->implicit($tag, $expectedClass);
|
||||
}
|
||||
}
|
||||
18
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/TimeType.php
vendored
Normal file
18
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/TimeType.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
/**
|
||||
* Interface to mark types that encode a time as a string.
|
||||
*/
|
||||
interface TimeType extends StringType
|
||||
{
|
||||
/**
|
||||
* Get the date and time.
|
||||
*/
|
||||
public function dateTime(): DateTimeImmutable;
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/UniversalClass.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/UniversalClass.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
|
||||
/**
|
||||
* Trait for types of universal class.
|
||||
*/
|
||||
trait UniversalClass
|
||||
{
|
||||
/**
|
||||
* @see \Sop\ASN1\Feature\ElementBase::typeClass()
|
||||
*/
|
||||
public function typeClass(): int
|
||||
{
|
||||
return Identifier::CLASS_UNIVERSAL;
|
||||
}
|
||||
}
|
||||
518
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/UnspecifiedType.php
vendored
Normal file
518
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Type/UnspecifiedType.php
vendored
Normal file
@ -0,0 +1,518 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Type;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Component\Identifier;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\ConstructedString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Set;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BMPString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Boolean;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\CharacterString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Enumerated;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GeneralizedTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GeneralString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\GraphicString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\IA5String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Integer;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NumericString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectDescriptor;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectIdentifier;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\PrintableString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Real;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\RelativeOID;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\T61String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UniversalString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTCTime;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTF8String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\VideotexString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\VisibleString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\ApplicationType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Tagged\PrivateType;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Decorator class to wrap an element without already knowing the specific underlying type.
|
||||
*
|
||||
* Provides accessor methods to test the underlying type and return a type hinted instance of the concrete element.
|
||||
*/
|
||||
final class UnspecifiedType implements ElementBase
|
||||
{
|
||||
private function __construct(
|
||||
private readonly Element $element
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(Element $element): self
|
||||
{
|
||||
return new self($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from DER data.
|
||||
*
|
||||
* @param string $data DER encoded data
|
||||
*/
|
||||
public static function fromDER(string $data): self
|
||||
{
|
||||
return Element::fromDER($data)->asUnspecified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from `ElementBase` interface.
|
||||
*/
|
||||
public static function fromElementBase(ElementBase $el): self
|
||||
{
|
||||
// if element is already wrapped
|
||||
if ($el instanceof self) {
|
||||
return $el;
|
||||
}
|
||||
return self::create($el->asElement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a context specific tagged type.
|
||||
*/
|
||||
public function asTagged(): TaggedType
|
||||
{
|
||||
if (! $this->element instanceof TaggedType) {
|
||||
throw new UnexpectedValueException('Tagged element expected, got ' . $this->typeDescriptorString());
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an application specific type.
|
||||
*/
|
||||
public function asApplication(): ApplicationType
|
||||
{
|
||||
if (! $this->element instanceof ApplicationType) {
|
||||
throw new UnexpectedValueException('Application type expected, got ' . $this->typeDescriptorString());
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a private tagged type.
|
||||
*/
|
||||
public function asPrivate(): PrivateType
|
||||
{
|
||||
if (! $this->element instanceof PrivateType) {
|
||||
throw new UnexpectedValueException('Private type expected, got ' . $this->typeDescriptorString());
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a boolean type.
|
||||
*/
|
||||
public function asBoolean(): Boolean
|
||||
{
|
||||
if (! $this->element instanceof Boolean) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_BOOLEAN));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an integer type.
|
||||
*/
|
||||
public function asInteger(): Integer
|
||||
{
|
||||
if (! $this->element instanceof Integer) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_INTEGER));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a bit string type.
|
||||
*/
|
||||
public function asBitString(): BitString
|
||||
{
|
||||
if (! $this->element instanceof BitString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_BIT_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an octet string type.
|
||||
*/
|
||||
public function asOctetString(): OctetString
|
||||
{
|
||||
if (! $this->element instanceof OctetString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_OCTET_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a null type.
|
||||
*/
|
||||
public function asNull(): NullType
|
||||
{
|
||||
if (! $this->element instanceof NullType) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_NULL));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an object identifier type.
|
||||
*/
|
||||
public function asObjectIdentifier(): ObjectIdentifier
|
||||
{
|
||||
if (! $this->element instanceof ObjectIdentifier) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_OBJECT_IDENTIFIER));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an object descriptor type.
|
||||
*/
|
||||
public function asObjectDescriptor(): ObjectDescriptor
|
||||
{
|
||||
if (! $this->element instanceof ObjectDescriptor) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_OBJECT_DESCRIPTOR));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a real type.
|
||||
*/
|
||||
public function asReal(): Real
|
||||
{
|
||||
if (! $this->element instanceof Real) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_REAL));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an enumerated type.
|
||||
*/
|
||||
public function asEnumerated(): Enumerated
|
||||
{
|
||||
if (! $this->element instanceof Enumerated) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_ENUMERATED));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a UTF8 string type.
|
||||
*/
|
||||
public function asUTF8String(): UTF8String
|
||||
{
|
||||
if (! $this->element instanceof UTF8String) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_UTF8_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a relative OID type.
|
||||
*/
|
||||
public function asRelativeOID(): RelativeOID
|
||||
{
|
||||
if (! $this->element instanceof RelativeOID) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_RELATIVE_OID));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a sequence type.
|
||||
*/
|
||||
public function asSequence(): Sequence
|
||||
{
|
||||
if (! $this->element instanceof Sequence) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_SEQUENCE));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a set type.
|
||||
*/
|
||||
public function asSet(): Set
|
||||
{
|
||||
if (! $this->element instanceof Set) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_SET));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a numeric string type.
|
||||
*/
|
||||
public function asNumericString(): NumericString
|
||||
{
|
||||
if (! $this->element instanceof NumericString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_NUMERIC_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a printable string type.
|
||||
*/
|
||||
public function asPrintableString(): PrintableString
|
||||
{
|
||||
if (! $this->element instanceof PrintableString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_PRINTABLE_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a T61 string type.
|
||||
*/
|
||||
public function asT61String(): T61String
|
||||
{
|
||||
if (! $this->element instanceof T61String) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_T61_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a videotex string type.
|
||||
*/
|
||||
public function asVideotexString(): VideotexString
|
||||
{
|
||||
if (! $this->element instanceof VideotexString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_VIDEOTEX_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a IA5 string type.
|
||||
*/
|
||||
public function asIA5String(): IA5String
|
||||
{
|
||||
if (! $this->element instanceof IA5String) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_IA5_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as an UTC time type.
|
||||
*/
|
||||
public function asUTCTime(): UTCTime
|
||||
{
|
||||
if (! $this->element instanceof UTCTime) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_UTC_TIME));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a generalized time type.
|
||||
*/
|
||||
public function asGeneralizedTime(): GeneralizedTime
|
||||
{
|
||||
if (! $this->element instanceof GeneralizedTime) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_GENERALIZED_TIME));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a graphic string type.
|
||||
*/
|
||||
public function asGraphicString(): GraphicString
|
||||
{
|
||||
if (! $this->element instanceof GraphicString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_GRAPHIC_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a visible string type.
|
||||
*/
|
||||
public function asVisibleString(): VisibleString
|
||||
{
|
||||
if (! $this->element instanceof VisibleString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_VISIBLE_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a general string type.
|
||||
*/
|
||||
public function asGeneralString(): GeneralString
|
||||
{
|
||||
if (! $this->element instanceof GeneralString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_GENERAL_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a universal string type.
|
||||
*/
|
||||
public function asUniversalString(): UniversalString
|
||||
{
|
||||
if (! $this->element instanceof UniversalString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_UNIVERSAL_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a character string type.
|
||||
*/
|
||||
public function asCharacterString(): CharacterString
|
||||
{
|
||||
if (! $this->element instanceof CharacterString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_CHARACTER_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a BMP string type.
|
||||
*/
|
||||
public function asBMPString(): BMPString
|
||||
{
|
||||
if (! $this->element instanceof BMPString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_BMP_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as a constructed string type.
|
||||
*/
|
||||
public function asConstructedString(): ConstructedString
|
||||
{
|
||||
if (! $this->element instanceof ConstructedString) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_CONSTRUCTED_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as any string type.
|
||||
*/
|
||||
public function asString(): StringType
|
||||
{
|
||||
if (! $this->element instanceof StringType) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_STRING));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wrapped element as any time type.
|
||||
*/
|
||||
public function asTime(): TimeType
|
||||
{
|
||||
if (! $this->element instanceof TimeType) {
|
||||
throw new UnexpectedValueException($this->generateExceptionMessage(Element::TYPE_TIME));
|
||||
}
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
public function asElement(): Element
|
||||
{
|
||||
return $this->element;
|
||||
}
|
||||
|
||||
public function asUnspecified(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toDER(): string
|
||||
{
|
||||
return $this->element->toDER();
|
||||
}
|
||||
|
||||
public function typeClass(): int
|
||||
{
|
||||
return $this->element->typeClass();
|
||||
}
|
||||
|
||||
public function tag(): int
|
||||
{
|
||||
return $this->element->tag();
|
||||
}
|
||||
|
||||
public function isConstructed(): bool
|
||||
{
|
||||
return $this->element->isConstructed();
|
||||
}
|
||||
|
||||
public function isType(int $tag): bool
|
||||
{
|
||||
return $this->element->isType($tag);
|
||||
}
|
||||
|
||||
public function isTagged(): bool
|
||||
{
|
||||
return $this->element->isTagged();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Consider using any of the `as*` accessor methods instead.
|
||||
*/
|
||||
public function expectType(int $tag): ElementBase
|
||||
{
|
||||
return $this->element->expectType($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Consider using `asTagged()` method instead and chaining
|
||||
* with `TaggedType::asExplicit()` or `TaggedType::asImplicit()`.
|
||||
*/
|
||||
public function expectTagged(?int $tag = null): TaggedType
|
||||
{
|
||||
return $this->element->expectTagged($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate message for exceptions thrown by `as*` methods.
|
||||
*
|
||||
* @param int $tag Type tag of the expected element
|
||||
*/
|
||||
private function generateExceptionMessage(int $tag): string
|
||||
{
|
||||
return sprintf('%s expected, got %s.', Element::tagToName($tag), $this->typeDescriptorString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get textual description of the wrapped element for debugging purposes.
|
||||
*/
|
||||
private function typeDescriptorString(): string
|
||||
{
|
||||
$type_cls = $this->element->typeClass();
|
||||
$tag = $this->element->tag();
|
||||
$str = $this->element->isConstructed() ? 'constructed ' : 'primitive ';
|
||||
if ($type_cls === Identifier::CLASS_UNIVERSAL) {
|
||||
$str .= Element::tagToName($tag);
|
||||
} else {
|
||||
$str .= Identifier::classToName($type_cls) . " TAG {$tag}";
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
125
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Util/BigInt.php
vendored
Normal file
125
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Util/BigInt.php
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Util;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use InvalidArgumentException;
|
||||
use function mb_strlen;
|
||||
use Stringable;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class to wrap an integer of arbirtary length.
|
||||
*/
|
||||
final class BigInt implements Stringable
|
||||
{
|
||||
/**
|
||||
* Number as a BigInteger object.
|
||||
*/
|
||||
private readonly BigInteger $value;
|
||||
|
||||
/**
|
||||
* Number as a base 10 integer string.
|
||||
*
|
||||
* @internal Lazily initialized
|
||||
*/
|
||||
private ?string $number = null;
|
||||
|
||||
/**
|
||||
* Number as an integer type.
|
||||
*
|
||||
* @internal Lazily initialized
|
||||
*/
|
||||
private ?int $_intNum = null;
|
||||
|
||||
private function __construct(BigInteger|int|string $num)
|
||||
{
|
||||
// convert to BigInteger object
|
||||
if (! $num instanceof BigInteger) {
|
||||
try {
|
||||
$num = BigInteger::of($num);
|
||||
} catch (Throwable) {
|
||||
throw new InvalidArgumentException('Unable to convert to integer.');
|
||||
}
|
||||
}
|
||||
$this->value = $num;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->base10();
|
||||
}
|
||||
|
||||
public static function create(BigInteger|int|string $num): self
|
||||
{
|
||||
return new self($num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from an arbitrary length of octets as an unsigned integer.
|
||||
*/
|
||||
public static function fromUnsignedOctets(string $octets): self
|
||||
{
|
||||
if (mb_strlen($octets, '8bit') === 0) {
|
||||
throw new InvalidArgumentException('Empty octets.');
|
||||
}
|
||||
return self::create(BigInteger::fromBytes($octets, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from an arbitrary length of octets as an signed integer having two's complement encoding.
|
||||
*/
|
||||
public static function fromSignedOctets(string $octets): self
|
||||
{
|
||||
if (mb_strlen($octets, '8bit') === 0) {
|
||||
throw new InvalidArgumentException('Empty octets.');
|
||||
}
|
||||
|
||||
return self::create(BigInteger::fromBytes($octets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as a base 10 integer string.
|
||||
*/
|
||||
public function base10(): string
|
||||
{
|
||||
if ($this->number === null) {
|
||||
$this->number = $this->value->toBase(10);
|
||||
}
|
||||
return $this->number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as an integer.
|
||||
*/
|
||||
public function toInt(): int
|
||||
{
|
||||
if (! isset($this->_intNum)) {
|
||||
$this->_intNum = $this->value->toInt();
|
||||
}
|
||||
return $this->_intNum;
|
||||
}
|
||||
|
||||
public function getValue(): BigInteger
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as an unsigned integer encoded in binary.
|
||||
*/
|
||||
public function unsignedOctets(): string
|
||||
{
|
||||
return $this->value->toBytes(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number as a signed integer encoded in two's complement binary.
|
||||
*/
|
||||
public function signedOctets(): string
|
||||
{
|
||||
return $this->value->toBytes();
|
||||
}
|
||||
}
|
||||
147
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Util/Flags.php
vendored
Normal file
147
libraries/vendor/spomky-labs/pki-framework/src/ASN1/Util/Flags.php
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\ASN1\Util;
|
||||
|
||||
use function assert;
|
||||
use Brick\Math\BigInteger;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function ord;
|
||||
use OutOfBoundsException;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString;
|
||||
|
||||
/**
|
||||
* Class to handle a bit string as a field of flags.
|
||||
*/
|
||||
final class Flags
|
||||
{
|
||||
/**
|
||||
* Flag octets.
|
||||
*/
|
||||
private string $_flags;
|
||||
|
||||
/**
|
||||
* @param BigInteger|int|string $flags Flags
|
||||
* @param int $_width The number of flags. If width is larger than
|
||||
* number of bits in $flags, zeroes are prepended
|
||||
* to flag field.
|
||||
*/
|
||||
private function __construct(
|
||||
BigInteger|int|string $flags,
|
||||
private readonly int $_width
|
||||
) {
|
||||
if ($_width === 0) {
|
||||
$this->_flags = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate number of unused bits in last octet
|
||||
$last_octet_bits = $_width % 8;
|
||||
$unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0;
|
||||
// mask bits outside bitfield width
|
||||
$num = BigInteger::of($flags);
|
||||
$mask = BigInteger::of(1)->shiftedLeft($_width)->minus(1);
|
||||
$num = $num->and($mask);
|
||||
|
||||
// shift towards MSB if needed
|
||||
$data = $num->shiftedLeft($unused_bits)
|
||||
->toBytes(false);
|
||||
$octets = unpack('C*', $data);
|
||||
assert(is_array($octets), new RuntimeException('unpack() failed'));
|
||||
$bits = count($octets) * 8;
|
||||
// pad with zeroes
|
||||
while ($bits < $_width) {
|
||||
array_unshift($octets, 0);
|
||||
$bits += 8;
|
||||
}
|
||||
$this->_flags = pack('C*', ...$octets);
|
||||
}
|
||||
|
||||
public static function create(BigInteger|int|string $flags, int $_width): self
|
||||
{
|
||||
return new self($flags, $_width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from `BitString`.
|
||||
*/
|
||||
public static function fromBitString(BitString $bs, int $width): self
|
||||
{
|
||||
$num_bits = $bs->numBits();
|
||||
$data = $bs->string();
|
||||
$num = $data === '' ? BigInteger::of(0) : BigInteger::fromBytes($bs->string(), false);
|
||||
$num = $num->shiftedRight($bs->unusedBits());
|
||||
if ($num_bits < $width) {
|
||||
$num = $num->shiftedLeft($width - $num_bits);
|
||||
}
|
||||
return self::create($num, $width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a bit at given index is set.
|
||||
*
|
||||
* Index 0 is the leftmost bit.
|
||||
*/
|
||||
public function test(int $idx): bool
|
||||
{
|
||||
if ($idx >= $this->_width) {
|
||||
throw new OutOfBoundsException('Index is out of bounds.');
|
||||
}
|
||||
// octet index
|
||||
$oi = (int) floor($idx / 8);
|
||||
$byte = $this->_flags[$oi];
|
||||
// bit index
|
||||
$bi = $idx % 8;
|
||||
// index 0 is the most significant bit in byte
|
||||
$mask = 0x01 << (7 - $bi);
|
||||
return (ord($byte) & $mask) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flags as an octet string.
|
||||
*
|
||||
* Zeroes are appended to the last octet if width is not divisible by 8.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return $this->_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flags as a base 10 integer.
|
||||
*
|
||||
* @return string Integer as a string
|
||||
*/
|
||||
public function number(): string
|
||||
{
|
||||
$num = BigInteger::fromBytes($this->_flags, false);
|
||||
$last_octet_bits = $this->_width % 8;
|
||||
$unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0;
|
||||
$num = $num->shiftedRight($unused_bits);
|
||||
return $num->toBase(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flags as an integer.
|
||||
*/
|
||||
public function intNumber(): int
|
||||
{
|
||||
$num = BigInt::create($this->number());
|
||||
return $num->toInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get flags as a `BitString` object.
|
||||
*
|
||||
* Unused bits are set accordingly. Trailing zeroes are not stripped.
|
||||
*/
|
||||
public function bitString(): BitString
|
||||
{
|
||||
$last_octet_bits = $this->_width % 8;
|
||||
$unused_bits = $last_octet_bits !== 0 ? 8 - $last_octet_bits : 0;
|
||||
return BitString::create($this->_flags, $unused_bits);
|
||||
}
|
||||
}
|
||||
90
libraries/vendor/spomky-labs/pki-framework/src/CryptoBridge/Crypto.php
vendored
Normal file
90
libraries/vendor/spomky-labs/pki-framework/src/CryptoBridge/Crypto.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoBridge;
|
||||
|
||||
use function defined;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\CryptoBridge\Crypto\OpenSSLCrypto;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\CipherAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PrivateKeyInfo;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PublicKeyInfo;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Signature\Signature;
|
||||
|
||||
/**
|
||||
* Base class for crypto engine implementations.
|
||||
*/
|
||||
abstract class Crypto
|
||||
{
|
||||
/**
|
||||
* Sign data with given algorithm using given private key.
|
||||
*
|
||||
* @param string $data Data to sign
|
||||
* @param PrivateKeyInfo $privkey_info Private key
|
||||
* @param SignatureAlgorithmIdentifier $algo Signature algorithm
|
||||
*/
|
||||
abstract public function sign(
|
||||
string $data,
|
||||
PrivateKeyInfo $privkey_info,
|
||||
SignatureAlgorithmIdentifier $algo
|
||||
): Signature;
|
||||
|
||||
/**
|
||||
* Verify signature with given algorithm using given public key.
|
||||
*
|
||||
* @param string $data Data to verify
|
||||
* @param Signature $signature Signature
|
||||
* @param PublicKeyInfo $pubkey_info Public key
|
||||
* @param SignatureAlgorithmIdentifier $algo Signature algorithm
|
||||
*
|
||||
* @return bool True if signature matches
|
||||
*/
|
||||
abstract public function verify(
|
||||
string $data,
|
||||
Signature $signature,
|
||||
PublicKeyInfo $pubkey_info,
|
||||
SignatureAlgorithmIdentifier $algo
|
||||
): bool;
|
||||
|
||||
/**
|
||||
* Encrypt data with given algorithm using given key.
|
||||
*
|
||||
* Padding must be added by the caller. Initialization vector is taken from the algorithm identifier if available.
|
||||
*
|
||||
* @param string $data Plaintext
|
||||
* @param string $key Encryption key
|
||||
* @param CipherAlgorithmIdentifier $algo Encryption algorithm
|
||||
*
|
||||
* @return string Ciphertext
|
||||
*/
|
||||
abstract public function encrypt(string $data, string $key, CipherAlgorithmIdentifier $algo): string;
|
||||
|
||||
/**
|
||||
* Decrypt data with given algorithm using given key.
|
||||
*
|
||||
* Possible padding is not removed and must be handled by the caller. Initialization vector is taken from the
|
||||
* algorithm identifier if available.
|
||||
*
|
||||
* @param string $data Ciphertext
|
||||
* @param string $key Encryption key
|
||||
* @param CipherAlgorithmIdentifier $algo Encryption algorithm
|
||||
*
|
||||
* @return string Plaintext
|
||||
*/
|
||||
abstract public function decrypt(string $data, string $key, CipherAlgorithmIdentifier $algo): string;
|
||||
|
||||
/**
|
||||
* Get default supported crypto implementation.
|
||||
*/
|
||||
public static function getDefault(): self
|
||||
{
|
||||
if (defined('OPENSSL_VERSION_NUMBER')) {
|
||||
return new OpenSSLCrypto();
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new RuntimeException('No crypto implementation available.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
234
libraries/vendor/spomky-labs/pki-framework/src/CryptoBridge/Crypto/OpenSSLCrypto.php
vendored
Normal file
234
libraries/vendor/spomky-labs/pki-framework/src/CryptoBridge/Crypto/OpenSSLCrypto.php
vendored
Normal file
@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoBridge\Crypto;
|
||||
|
||||
use function array_key_exists;
|
||||
use function mb_strlen;
|
||||
use const OPENSSL_ALGO_MD4;
|
||||
use const OPENSSL_ALGO_MD5;
|
||||
use const OPENSSL_ALGO_SHA1;
|
||||
use const OPENSSL_ALGO_SHA224;
|
||||
use const OPENSSL_ALGO_SHA256;
|
||||
use const OPENSSL_ALGO_SHA384;
|
||||
use const OPENSSL_ALGO_SHA512;
|
||||
use const OPENSSL_RAW_DATA;
|
||||
use const OPENSSL_ZERO_PADDING;
|
||||
use RuntimeException;
|
||||
use SpomkyLabs\Pki\CryptoBridge\Crypto;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\BlockCipherAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\CipherAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\RC2CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PrivateKeyInfo;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Asymmetric\PublicKeyInfo;
|
||||
use SpomkyLabs\Pki\CryptoTypes\Signature\Signature;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Crypto engine using OpenSSL extension.
|
||||
*/
|
||||
final class OpenSSLCrypto extends Crypto
|
||||
{
|
||||
/**
|
||||
* Mapping from algorithm OID to OpenSSL signature method identifier.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private const MAP_DIGEST_OID = [
|
||||
AlgorithmIdentifier::OID_MD4_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_MD4,
|
||||
AlgorithmIdentifier::OID_MD5_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_MD5,
|
||||
AlgorithmIdentifier::OID_SHA1_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_SHA1,
|
||||
AlgorithmIdentifier::OID_SHA224_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_SHA224,
|
||||
AlgorithmIdentifier::OID_SHA256_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_SHA256,
|
||||
AlgorithmIdentifier::OID_SHA384_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_SHA384,
|
||||
AlgorithmIdentifier::OID_SHA512_WITH_RSA_ENCRYPTION => OPENSSL_ALGO_SHA512,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA1 => OPENSSL_ALGO_SHA1,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA224 => OPENSSL_ALGO_SHA224,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA256 => OPENSSL_ALGO_SHA256,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA384 => OPENSSL_ALGO_SHA384,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA512 => OPENSSL_ALGO_SHA512,
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping from algorithm OID to OpenSSL cipher method name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const MAP_CIPHER_OID = [
|
||||
AlgorithmIdentifier::OID_DES_CBC => 'des-cbc',
|
||||
AlgorithmIdentifier::OID_DES_EDE3_CBC => 'des-ede3-cbc',
|
||||
AlgorithmIdentifier::OID_AES_128_CBC => 'aes-128-cbc',
|
||||
AlgorithmIdentifier::OID_AES_192_CBC => 'aes-192-cbc',
|
||||
AlgorithmIdentifier::OID_AES_256_CBC => 'aes-256-cbc',
|
||||
];
|
||||
|
||||
public function sign(
|
||||
string $data,
|
||||
PrivateKeyInfo $privkey_info,
|
||||
SignatureAlgorithmIdentifier $algo
|
||||
): Signature {
|
||||
$this->_checkSignatureAlgoAndKey($algo, $privkey_info->algorithmIdentifier());
|
||||
$result = openssl_sign($data, $signature, (string) $privkey_info->toPEM(), $this->_algoToDigest($algo));
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('openssl_sign() failed: ' . $this->_getLastError());
|
||||
}
|
||||
return Signature::fromSignatureData($signature, $algo);
|
||||
}
|
||||
|
||||
public function verify(
|
||||
string $data,
|
||||
Signature $signature,
|
||||
PublicKeyInfo $pubkey_info,
|
||||
SignatureAlgorithmIdentifier $algo
|
||||
): bool {
|
||||
$this->_checkSignatureAlgoAndKey($algo, $pubkey_info->algorithmIdentifier());
|
||||
$result = openssl_verify(
|
||||
$data,
|
||||
$signature->bitString()
|
||||
->string(),
|
||||
(string) $pubkey_info->toPEM(),
|
||||
$this->_algoToDigest($algo)
|
||||
);
|
||||
if ($result === -1) {
|
||||
throw new RuntimeException('openssl_verify() failed: ' . $this->_getLastError());
|
||||
}
|
||||
return $result === 1;
|
||||
}
|
||||
|
||||
public function encrypt(string $data, string $key, CipherAlgorithmIdentifier $algo): string
|
||||
{
|
||||
$this->_checkCipherKeySize($algo, $key);
|
||||
$iv = $algo->initializationVector();
|
||||
$result = openssl_encrypt(
|
||||
$data,
|
||||
$this->_algoToCipher($algo),
|
||||
$key,
|
||||
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
|
||||
$iv
|
||||
);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('openssl_encrypt() failed: ' . $this->_getLastError());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function decrypt(string $data, string $key, CipherAlgorithmIdentifier $algo): string
|
||||
{
|
||||
$this->_checkCipherKeySize($algo, $key);
|
||||
$iv = $algo->initializationVector();
|
||||
$result = openssl_decrypt(
|
||||
$data,
|
||||
$this->_algoToCipher($algo),
|
||||
$key,
|
||||
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
|
||||
$iv
|
||||
);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('openssl_decrypt() failed: ' . $this->_getLastError());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate cipher algorithm key size.
|
||||
*/
|
||||
protected function _checkCipherKeySize(CipherAlgorithmIdentifier $algo, string $key): void
|
||||
{
|
||||
if ($algo instanceof BlockCipherAlgorithmIdentifier) {
|
||||
if (mb_strlen($key, '8bit') !== $algo->keySize()) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf(
|
||||
'Key length for %s must be %d, %d given.',
|
||||
$algo->name(),
|
||||
$algo->keySize(),
|
||||
mb_strlen($key, '8bit')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last OpenSSL error message.
|
||||
*/
|
||||
protected function _getLastError(): ?string
|
||||
{
|
||||
// pump error message queue
|
||||
$msg = null;
|
||||
while (false !== ($err = openssl_error_string())) {
|
||||
$msg = $err;
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that given signature algorithm supports key of given type.
|
||||
*
|
||||
* @param SignatureAlgorithmIdentifier $sig_algo Signature algorithm
|
||||
* @param AlgorithmIdentifier $key_algo Key algorithm
|
||||
*/
|
||||
protected function _checkSignatureAlgoAndKey(
|
||||
SignatureAlgorithmIdentifier $sig_algo,
|
||||
AlgorithmIdentifier $key_algo
|
||||
): void {
|
||||
if (! $sig_algo->supportsKeyAlgorithm($key_algo)) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf(
|
||||
'Signature algorithm %s does not support key algorithm %s.',
|
||||
$sig_algo->name(),
|
||||
$key_algo->name()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenSSL digest method for given signature algorithm identifier.
|
||||
*/
|
||||
protected function _algoToDigest(SignatureAlgorithmIdentifier $algo): int
|
||||
{
|
||||
$oid = $algo->oid();
|
||||
if (! array_key_exists($oid, self::MAP_DIGEST_OID)) {
|
||||
throw new UnexpectedValueException(sprintf('Digest method %s not supported.', $algo->name()));
|
||||
}
|
||||
return self::MAP_DIGEST_OID[$oid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenSSL cipher method for given cipher algorithm identifier.
|
||||
*/
|
||||
protected function _algoToCipher(CipherAlgorithmIdentifier $algo): string
|
||||
{
|
||||
$oid = $algo->oid();
|
||||
if (array_key_exists($oid, self::MAP_CIPHER_OID)) {
|
||||
return self::MAP_CIPHER_OID[$oid];
|
||||
}
|
||||
if ($oid === AlgorithmIdentifier::OID_RC2_CBC) {
|
||||
if (! $algo instanceof RC2CBCAlgorithmIdentifier) {
|
||||
throw new UnexpectedValueException('Not an RC2-CBC algorithm.');
|
||||
}
|
||||
return $this->_rc2AlgoToCipher($algo);
|
||||
}
|
||||
throw new UnexpectedValueException(sprintf('Cipher method %s not supported.', $algo->name()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenSSL cipher method for given RC2 algorithm identifier.
|
||||
*/
|
||||
protected function _rc2AlgoToCipher(RC2CBCAlgorithmIdentifier $algo): string
|
||||
{
|
||||
return match ($algo->effectiveKeyBits()) {
|
||||
128 => 'rc2-cbc',
|
||||
64 => 'rc2-64-cbc',
|
||||
40 => 'rc2-40-cbc',
|
||||
default => throw new UnexpectedValueException($algo->effectiveKeyBits() . ' bit RC2 not supported.'),
|
||||
};
|
||||
}
|
||||
}
|
||||
138
libraries/vendor/spomky-labs/pki-framework/src/CryptoEncoding/PEM.php
vendored
Normal file
138
libraries/vendor/spomky-labs/pki-framework/src/CryptoEncoding/PEM.php
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoEncoding;
|
||||
|
||||
use function is_string;
|
||||
use RuntimeException;
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Implements PEM file encoding and decoding.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc7468
|
||||
*/
|
||||
final class PEM implements Stringable
|
||||
{
|
||||
// well-known PEM types
|
||||
final public const TYPE_CERTIFICATE = 'CERTIFICATE';
|
||||
|
||||
final public const TYPE_CRL = 'X509 CRL';
|
||||
|
||||
final public const TYPE_CERTIFICATE_REQUEST = 'CERTIFICATE REQUEST';
|
||||
|
||||
final public const TYPE_ATTRIBUTE_CERTIFICATE = 'ATTRIBUTE CERTIFICATE';
|
||||
|
||||
final public const TYPE_PRIVATE_KEY = 'PRIVATE KEY';
|
||||
|
||||
final public const TYPE_PUBLIC_KEY = 'PUBLIC KEY';
|
||||
|
||||
final public const TYPE_ENCRYPTED_PRIVATE_KEY = 'ENCRYPTED PRIVATE KEY';
|
||||
|
||||
final public const TYPE_RSA_PRIVATE_KEY = 'RSA PRIVATE KEY';
|
||||
|
||||
final public const TYPE_RSA_PUBLIC_KEY = 'RSA PUBLIC KEY';
|
||||
|
||||
final public const TYPE_EC_PRIVATE_KEY = 'EC PRIVATE KEY';
|
||||
|
||||
final public const TYPE_PKCS7 = 'PKCS7';
|
||||
|
||||
final public const TYPE_CMS = 'CMS';
|
||||
|
||||
/**
|
||||
* Regular expression to match PEM block.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const PEM_REGEX = '/' .
|
||||
/* line start */
|
||||
'(?:^|[\r\n])' .
|
||||
/* header */
|
||||
'-----BEGIN (.+?)-----[\r\n]+' .
|
||||
/* payload */
|
||||
'(.+?)' .
|
||||
/* trailer */
|
||||
'[\r\n]+-----END \\1-----' .
|
||||
'/ms';
|
||||
|
||||
/**
|
||||
* @param string $type Content type
|
||||
* @param string $data Payload
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $type,
|
||||
private readonly string $data
|
||||
) {
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
|
||||
public static function create(string $_type, string $_data): self
|
||||
{
|
||||
return new self($_type, $_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from a PEM-formatted string.
|
||||
*/
|
||||
public static function fromString(string $str): self
|
||||
{
|
||||
if (preg_match(self::PEM_REGEX, $str, $match) !== 1) {
|
||||
throw new UnexpectedValueException('Not a PEM formatted string.');
|
||||
}
|
||||
$payload = preg_replace('/\s+/', '', $match[2]);
|
||||
if (! is_string($payload)) {
|
||||
throw new UnexpectedValueException('Failed to decode PEM data.');
|
||||
}
|
||||
$data = base64_decode($payload, true);
|
||||
if ($data === false) {
|
||||
throw new UnexpectedValueException('Failed to decode PEM data.');
|
||||
}
|
||||
return self::create($match[1], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from a file.
|
||||
*
|
||||
* @param string $filename Path to file
|
||||
*/
|
||||
public static function fromFile(string $filename): self
|
||||
{
|
||||
if (! is_readable($filename)) {
|
||||
throw new RuntimeException("Failed to read {$filename}.");
|
||||
}
|
||||
$str = file_get_contents($filename);
|
||||
if ($str === false) {
|
||||
throw new RuntimeException("Failed to read {$filename}.");
|
||||
}
|
||||
return self::fromString($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content type.
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function data(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode to PEM string.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return "-----BEGIN {$this->type}-----\n" .
|
||||
trim(chunk_split(base64_encode($this->data), 64, "\n")) . "\n" .
|
||||
"-----END {$this->type}-----";
|
||||
}
|
||||
}
|
||||
155
libraries/vendor/spomky-labs/pki-framework/src/CryptoEncoding/PEMBundle.php
vendored
Normal file
155
libraries/vendor/spomky-labs/pki-framework/src/CryptoEncoding/PEMBundle.php
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoEncoding;
|
||||
|
||||
use ArrayIterator;
|
||||
use function count;
|
||||
use Countable;
|
||||
use function is_string;
|
||||
use IteratorAggregate;
|
||||
use LogicException;
|
||||
use const PREG_SET_ORDER;
|
||||
use RuntimeException;
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Container for multiple PEM objects.
|
||||
*
|
||||
* The order of PEMs shall be retained, eg. when read from a file.
|
||||
*/
|
||||
final class PEMBundle implements Countable, IteratorAggregate, Stringable
|
||||
{
|
||||
/**
|
||||
* Array of PEM objects.
|
||||
*
|
||||
* @var PEM[]
|
||||
*/
|
||||
private array $pems;
|
||||
|
||||
private function __construct(PEM ...$pems)
|
||||
{
|
||||
$this->pems = $pems;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->string();
|
||||
}
|
||||
|
||||
public static function create(PEM ...$pems): self
|
||||
{
|
||||
return new self(...$pems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from a string.
|
||||
*/
|
||||
public static function fromString(string $str): self
|
||||
{
|
||||
$hasMatches = preg_match_all(PEM::PEM_REGEX, $str, $matches, PREG_SET_ORDER);
|
||||
if ($hasMatches === false || $hasMatches === 0) {
|
||||
throw new UnexpectedValueException('No PEM blocks.');
|
||||
}
|
||||
$pems = array_map(
|
||||
static function ($match) {
|
||||
$payload = preg_replace('/\s+/', '', $match[2]);
|
||||
if (! is_string($payload)) {
|
||||
throw new UnexpectedValueException('Failed to decode PEM data.');
|
||||
}
|
||||
$data = base64_decode($payload, true);
|
||||
if ($data === false) {
|
||||
throw new UnexpectedValueException('Failed to decode PEM data.');
|
||||
}
|
||||
return PEM::create($match[1], $data);
|
||||
},
|
||||
$matches
|
||||
);
|
||||
return self::create(...$pems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from a file.
|
||||
*/
|
||||
public static function fromFile(string $filename): self
|
||||
{
|
||||
if (! is_readable($filename)) {
|
||||
throw new RuntimeException("Failed to read {$filename}.");
|
||||
}
|
||||
$str = file_get_contents($filename);
|
||||
if ($str === false) {
|
||||
throw new RuntimeException("Failed to read {$filename}.");
|
||||
}
|
||||
return self::fromString($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with PEM objects appended.
|
||||
*/
|
||||
public function withPEMs(PEM ...$pems): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->pems = array_merge($obj->pems, $pems);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all PEMs in a bundle.
|
||||
*
|
||||
* @return PEM[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->pems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first PEM in a bundle.
|
||||
*/
|
||||
public function first(): PEM
|
||||
{
|
||||
if (count($this->pems) === 0) {
|
||||
throw new LogicException('No PEMs.');
|
||||
}
|
||||
return $this->pems[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last PEM in a bundle.
|
||||
*/
|
||||
public function last(): PEM
|
||||
{
|
||||
if (count($this->pems) === 0) {
|
||||
throw new LogicException('No PEMs.');
|
||||
}
|
||||
return $this->pems[count($this->pems) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->pems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for PEMs.
|
||||
*
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->pems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode bundle to a string of contiguous PEM blocks.
|
||||
*/
|
||||
public function string(): string
|
||||
{
|
||||
return implode("\n", array_map(static fn (PEM $pem) => $pem->string(), $this->pems));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\AlgorithmIdentifierType;
|
||||
|
||||
/**
|
||||
* Implements AlgorithmIdentifier ASN.1 type.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2898#appendix-C
|
||||
* @see https://tools.ietf.org/html/rfc3447#appendix-C
|
||||
*/
|
||||
abstract class AlgorithmIdentifier implements AlgorithmIdentifierType
|
||||
{
|
||||
// RSA encryption
|
||||
final public const OID_RSA_ENCRYPTION = '1.2.840.113549.1.1.1';
|
||||
|
||||
// RSA signature algorithms
|
||||
final public const OID_MD2_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.2';
|
||||
|
||||
final public const OID_MD4_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.3';
|
||||
|
||||
final public const OID_MD5_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.4';
|
||||
|
||||
final public const OID_SHA1_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.5';
|
||||
|
||||
final public const OID_SHA256_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.11';
|
||||
|
||||
final public const OID_SHA384_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.12';
|
||||
|
||||
final public const OID_SHA512_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.13';
|
||||
|
||||
final public const OID_SHA224_WITH_RSA_ENCRYPTION = '1.2.840.113549.1.1.14';
|
||||
|
||||
// Elliptic Curve signature algorithms
|
||||
final public const OID_ECDSA_WITH_SHA1 = '1.2.840.10045.4.1';
|
||||
|
||||
final public const OID_ECDSA_WITH_SHA224 = '1.2.840.10045.4.3.1';
|
||||
|
||||
final public const OID_ECDSA_WITH_SHA256 = '1.2.840.10045.4.3.2';
|
||||
|
||||
final public const OID_ECDSA_WITH_SHA384 = '1.2.840.10045.4.3.3';
|
||||
|
||||
final public const OID_ECDSA_WITH_SHA512 = '1.2.840.10045.4.3.4';
|
||||
|
||||
// Elliptic Curve public key
|
||||
final public const OID_EC_PUBLIC_KEY = '1.2.840.10045.2.1';
|
||||
|
||||
// Elliptic curve / algorithm pairs from RFC 8410
|
||||
final public const OID_X25519 = '1.3.101.110';
|
||||
|
||||
final public const OID_X448 = '1.3.101.111';
|
||||
|
||||
final public const OID_ED25519 = '1.3.101.112';
|
||||
|
||||
final public const OID_ED448 = '1.3.101.113';
|
||||
|
||||
// Cipher algorithms
|
||||
final public const OID_DES_CBC = '1.3.14.3.2.7';
|
||||
|
||||
final public const OID_RC2_CBC = '1.2.840.113549.3.2';
|
||||
|
||||
final public const OID_DES_EDE3_CBC = '1.2.840.113549.3.7';
|
||||
|
||||
final public const OID_AES_128_CBC = '2.16.840.1.101.3.4.1.2';
|
||||
|
||||
final public const OID_AES_192_CBC = '2.16.840.1.101.3.4.1.22';
|
||||
|
||||
final public const OID_AES_256_CBC = '2.16.840.1.101.3.4.1.42';
|
||||
|
||||
// HMAC-SHA-1 from RFC 8018
|
||||
final public const OID_HMAC_WITH_SHA1 = '1.2.840.113549.2.7';
|
||||
|
||||
// HMAC algorithms from RFC 4231
|
||||
final public const OID_HMAC_WITH_SHA224 = '1.2.840.113549.2.8';
|
||||
|
||||
final public const OID_HMAC_WITH_SHA256 = '1.2.840.113549.2.9';
|
||||
|
||||
final public const OID_HMAC_WITH_SHA384 = '1.2.840.113549.2.10';
|
||||
|
||||
final public const OID_HMAC_WITH_SHA512 = '1.2.840.113549.2.11';
|
||||
|
||||
// Message digest algorithms
|
||||
final public const OID_MD5 = '1.2.840.113549.2.5';
|
||||
|
||||
final public const OID_SHA1 = '1.3.14.3.2.26';
|
||||
|
||||
final public const OID_SHA224 = '2.16.840.1.101.3.4.2.4';
|
||||
|
||||
final public const OID_SHA256 = '2.16.840.1.101.3.4.2.1';
|
||||
|
||||
final public const OID_SHA384 = '2.16.840.1.101.3.4.2.2';
|
||||
|
||||
final public const OID_SHA512 = '2.16.840.1.101.3.4.2.3';
|
||||
|
||||
protected function __construct(
|
||||
protected readonly string $oid
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Sequence $seq): self
|
||||
{
|
||||
return AlgorithmIdentifierFactory::create()->parse($seq);
|
||||
}
|
||||
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
public function toASN1(): Sequence
|
||||
{
|
||||
$elements = [ObjectIdentifier::create($this->oid)];
|
||||
$params = $this->paramsASN1();
|
||||
if (isset($params)) {
|
||||
$elements[] = $params;
|
||||
}
|
||||
return Sequence::create(...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get algorithm identifier parameters as ASN.1.
|
||||
*
|
||||
* If type allows parameters to be omitted, return null.
|
||||
*/
|
||||
abstract protected function paramsASN1(): ?Element;
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier;
|
||||
|
||||
use function array_key_exists;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\ECPublicKeyAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\Ed25519AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\Ed448AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\RSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\X25519AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric\X448AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\AES128CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\AES192CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\AES256CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\DESCBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\DESEDE3CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher\RC2CBCAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA1AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA224AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA256AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA384AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA512AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\MD5AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\SHA1AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\SHA224AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\SHA256AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\SHA384AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash\SHA512AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\ECDSAWithSHA1AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\ECDSAWithSHA224AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\ECDSAWithSHA256AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\ECDSAWithSHA384AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\ECDSAWithSHA512AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\MD2WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\MD4WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\MD5WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA1WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA224WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA256WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA384WithRSAEncryptionAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Signature\SHA512WithRSAEncryptionAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Factory class to parse AlgorithmIdentifier ASN.1 types to specific algorithm identifier objects.
|
||||
*
|
||||
* Additional providers may be added to the process to support algorithm identifiers that are implemented in external
|
||||
* libraries.
|
||||
*/
|
||||
final class AlgorithmIdentifierFactory
|
||||
{
|
||||
/**
|
||||
* Mapping for algorithm identifiers provided by this library.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const MAP_OID_TO_CLASS = [
|
||||
AlgorithmIdentifier::OID_RSA_ENCRYPTION => RSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_EC_PUBLIC_KEY => ECPublicKeyAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_X25519 => X25519AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_X448 => X448AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ED25519 => Ed25519AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ED448 => Ed448AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_DES_CBC => DESCBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_DES_EDE3_CBC => DESEDE3CBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_RC2_CBC => RC2CBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_AES_128_CBC => AES128CBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_AES_192_CBC => AES192CBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_AES_256_CBC => AES256CBCAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_HMAC_WITH_SHA1 => HMACWithSHA1AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_HMAC_WITH_SHA224 => HMACWithSHA224AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_HMAC_WITH_SHA256 => HMACWithSHA256AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_HMAC_WITH_SHA384 => HMACWithSHA384AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_HMAC_WITH_SHA512 => HMACWithSHA512AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_MD5 => MD5AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA1 => SHA1AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA224 => SHA224AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA256 => SHA256AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA384 => SHA384AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA512 => SHA512AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_MD2_WITH_RSA_ENCRYPTION => MD2WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_MD4_WITH_RSA_ENCRYPTION => MD4WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_MD5_WITH_RSA_ENCRYPTION => MD5WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA1_WITH_RSA_ENCRYPTION => SHA1WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA224_WITH_RSA_ENCRYPTION => SHA224WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA256_WITH_RSA_ENCRYPTION => SHA256WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA384_WITH_RSA_ENCRYPTION => SHA384WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_SHA512_WITH_RSA_ENCRYPTION => SHA512WithRSAEncryptionAlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA1 => ECDSAWithSHA1AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA224 => ECDSAWithSHA224AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA256 => ECDSAWithSHA256AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA384 => ECDSAWithSHA384AlgorithmIdentifier::class,
|
||||
AlgorithmIdentifier::OID_ECDSA_WITH_SHA512 => ECDSAWithSHA512AlgorithmIdentifier::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Additional algorithm identifier providers.
|
||||
*
|
||||
* @var AlgorithmIdentifierProvider[]
|
||||
*/
|
||||
private readonly array $_additionalProviders;
|
||||
|
||||
/**
|
||||
* @param AlgorithmIdentifierProvider ...$providers Additional providers
|
||||
*/
|
||||
private function __construct(AlgorithmIdentifierProvider ...$providers)
|
||||
{
|
||||
$this->_additionalProviders = $providers;
|
||||
}
|
||||
|
||||
public static function create(AlgorithmIdentifierProvider ...$providers): self
|
||||
{
|
||||
return new self(...$providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of a class that implements algorithm identifier for given OID.
|
||||
*
|
||||
* @param string $oid Object identifier in dotted format
|
||||
*
|
||||
* @return null|string Fully qualified class name or null if not supported
|
||||
*/
|
||||
public function getClass(string $oid): ?string
|
||||
{
|
||||
// if OID is provided by this factory
|
||||
if (array_key_exists($oid, self::MAP_OID_TO_CLASS)) {
|
||||
return self::MAP_OID_TO_CLASS[$oid];
|
||||
}
|
||||
// try additional providers
|
||||
foreach ($this->_additionalProviders as $provider) {
|
||||
if ($provider->supportsOID($oid)) {
|
||||
return $provider->getClassByOID($oid);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse AlgorithmIdentifier from an ASN.1 sequence.
|
||||
*/
|
||||
public function parse(Sequence $seq): AlgorithmIdentifier
|
||||
{
|
||||
$oid = $seq->at(0)
|
||||
->asObjectIdentifier()
|
||||
->oid();
|
||||
$params = $seq->has(1) ? $seq->at(1) : null;
|
||||
$cls = $this->getClass($oid);
|
||||
if ($cls !== null) {
|
||||
return $cls::fromASN1Params($params);
|
||||
}
|
||||
|
||||
return GenericAlgorithmIdentifier::create($oid, $params);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Interface to provide lookup from OID to class name of specific algorithm identifier type implementations.
|
||||
*
|
||||
* This allows AlgorithmIdentifier types to be implemented in external libraries and to use AlgorithmIdentifierFactory
|
||||
* to resolve them.
|
||||
*/
|
||||
interface AlgorithmIdentifierProvider
|
||||
{
|
||||
/**
|
||||
* Check whether this provider supports algorithm identifier of given OID.
|
||||
*
|
||||
* @param string $oid Object identifier in dotted format
|
||||
*/
|
||||
public function supportsOID(string $oid): bool;
|
||||
|
||||
/**
|
||||
* Get the name of a class that implements algorithm identifier for given OID.
|
||||
*
|
||||
* @param string $oid Object identifier in dotted format
|
||||
*
|
||||
* @return string Fully qualified name of a class that extends
|
||||
* SpecificAlgorithmIdentifier
|
||||
*/
|
||||
public function getClassByOID(string $oid): string;
|
||||
}
|
||||
@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectIdentifier;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\AsymmetricCryptoAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
From RFC 5480 - 2.1.1. Unrestricted Algorithm Identifier and Parameters:
|
||||
|
||||
The parameter for id-ecPublicKey is as follows and MUST always be
|
||||
present:
|
||||
|
||||
ECParameters ::= CHOICE {
|
||||
namedCurve OBJECT IDENTIFIER
|
||||
-- implicitCurve NULL
|
||||
-- specifiedCurve SpecifiedECDomain
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the elliptic curve public key.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc5480#section-2.1.1
|
||||
*/
|
||||
final class ECPublicKeyAlgorithmIdentifier extends SpecificAlgorithmIdentifier implements AsymmetricCryptoAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* prime192v1/secp192r1 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.1
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME192V1 = '1.2.840.10045.3.1.1';
|
||||
|
||||
/**
|
||||
* prime192v2 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.2
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME192V2 = '1.2.840.10045.3.1.2';
|
||||
|
||||
/**
|
||||
* prime192v3 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.3
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME192V3 = '1.2.840.10045.3.1.3';
|
||||
|
||||
/**
|
||||
* prime239v1 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.4
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME239V1 = '1.2.840.10045.3.1.4';
|
||||
|
||||
/**
|
||||
* prime239v2 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.5
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME239V2 = '1.2.840.10045.3.1.5';
|
||||
|
||||
/**
|
||||
* prime239v3 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.6
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME239V3 = '1.2.840.10045.3.1.6';
|
||||
|
||||
/**
|
||||
* prime256v1/secp256r1 curve OID.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.10045.3.1.7
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_PRIME256V1 = '1.2.840.10045.3.1.7';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp112r1.
|
||||
*
|
||||
* @see http://www.oid-info.com/get/1.3.132.0.6
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP112R1 = '1.3.132.0.6';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp112r2.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.7
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP112R2 = '1.3.132.0.7';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp128r1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.28
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP128R1 = '1.3.132.0.28';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp128r2.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.29
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP128R2 = '1.3.132.0.29';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp160k1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.9
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP160K1 = '1.3.132.0.9';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp160r1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.8
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP160R1 = '1.3.132.0.8';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp160r2.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.30
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP160R2 = '1.3.132.0.30';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp192k1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.31
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP192K1 = '1.3.132.0.31';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp224k1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.32
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP224K1 = '1.3.132.0.32';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp224r1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.33
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP224R1 = '1.3.132.0.33';
|
||||
|
||||
/**
|
||||
* "SEC 2" recommended elliptic curve domain - secp256k1.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.10
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP256K1 = '1.3.132.0.10';
|
||||
|
||||
/**
|
||||
* National Institute of Standards and Technology (NIST) 384-bit elliptic curve.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.34
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP384R1 = '1.3.132.0.34';
|
||||
|
||||
/**
|
||||
* National Institute of Standards and Technology (NIST) 512-bit elliptic curve.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.132.0.35
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const CURVE_SECP521R1 = '1.3.132.0.35';
|
||||
|
||||
/**
|
||||
* Mapping from curve OID to field bit size.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
final public const MAP_CURVE_TO_SIZE = [
|
||||
self::CURVE_PRIME192V1 => 192,
|
||||
self::CURVE_PRIME192V2 => 192,
|
||||
self::CURVE_PRIME192V3 => 192,
|
||||
self::CURVE_PRIME239V1 => 239,
|
||||
self::CURVE_PRIME239V2 => 239,
|
||||
self::CURVE_PRIME239V3 => 239,
|
||||
self::CURVE_PRIME256V1 => 256,
|
||||
self::CURVE_SECP112R1 => 112,
|
||||
self::CURVE_SECP112R2 => 112,
|
||||
self::CURVE_SECP128R1 => 128,
|
||||
self::CURVE_SECP128R2 => 128,
|
||||
self::CURVE_SECP160K1 => 160,
|
||||
self::CURVE_SECP160R1 => 160,
|
||||
self::CURVE_SECP160R2 => 160,
|
||||
self::CURVE_SECP192K1 => 192,
|
||||
self::CURVE_SECP224K1 => 224,
|
||||
self::CURVE_SECP224R1 => 224,
|
||||
self::CURVE_SECP256K1 => 256,
|
||||
self::CURVE_SECP384R1 => 384,
|
||||
self::CURVE_SECP521R1 => 521,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $namedCurve Curve identifier
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $namedCurve
|
||||
) {
|
||||
parent::__construct(self::OID_EC_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
public static function create(string $namedCurve): self
|
||||
{
|
||||
return new self($namedCurve);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'ecPublicKey';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$named_curve = $params->asObjectIdentifier()
|
||||
->oid();
|
||||
return self::create($named_curve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID of the named curve.
|
||||
*/
|
||||
public function namedCurve(): string
|
||||
{
|
||||
return $this->namedCurve;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectIdentifier
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return ObjectIdentifier::create($this->namedCurve);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Edwards-curve Digital Signature Algorithm (EdDSA) with curve25519.
|
||||
*
|
||||
* Same algorithm identifier is used for public and private keys as well as for signatures.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.101.112
|
||||
* @see https://tools.ietf.org/html/rfc8420#appendix-A.1
|
||||
*/
|
||||
final class Ed25519AlgorithmIdentifier extends RFC8410EdAlgorithmIdentifier
|
||||
{
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_ED25519);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if ($params !== null) {
|
||||
throw new UnexpectedValueException('Parameters must be absent.');
|
||||
}
|
||||
return self::create();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'id-Ed25519';
|
||||
}
|
||||
|
||||
public function supportsKeyAlgorithm(AlgorithmIdentifier $algo): bool
|
||||
{
|
||||
return $algo->oid() === self::OID_ED25519;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Edwards-curve Digital Signature Algorithm (EdDSA) with curve448.
|
||||
*
|
||||
* Same algorithm identifier is used for public and private keys as well as for signatures.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.101.113
|
||||
* @see https://tools.ietf.org/html/rfc8420#appendix-A.2
|
||||
*/
|
||||
final class Ed448AlgorithmIdentifier extends RFC8410EdAlgorithmIdentifier
|
||||
{
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_ED448);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if ($params !== null) {
|
||||
throw new UnexpectedValueException('Parameters must be absent.');
|
||||
}
|
||||
return self::create();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'id-Ed448';
|
||||
}
|
||||
|
||||
public function supportsKeyAlgorithm(AlgorithmIdentifier $algo): bool
|
||||
{
|
||||
return $algo->oid() === self::OID_ED448;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\AsymmetricCryptoAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\SignatureAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/*
|
||||
From RFC 8410:
|
||||
|
||||
For all of the OIDs, the parameters MUST be absent.
|
||||
|
||||
It is possible to find systems that require the parameters to be
|
||||
present. This can be due to either a defect in the original 1997
|
||||
syntax or a programming error where developers never got input where
|
||||
this was not true. The optimal solution is to fix these systems;
|
||||
where this is not possible, the problem needs to be restricted to
|
||||
that subsystem and not propagated to the Internet.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Edwards-curve Digital Signature Algorithm (EdDSA) identifiers specified by RFC 8410.
|
||||
*
|
||||
* Same algorithm identifier is used for public and private keys as well as for signatures.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc8410#section-3
|
||||
* @see https://tools.ietf.org/html/rfc8410#section-6
|
||||
*/
|
||||
abstract class RFC8410EdAlgorithmIdentifier extends SpecificAlgorithmIdentifier implements AsymmetricCryptoAlgorithmIdentifier, SignatureAlgorithmIdentifier
|
||||
{
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\AsymmetricCryptoAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/*
|
||||
From RFC 8410:
|
||||
|
||||
For all of the OIDs, the parameters MUST be absent.
|
||||
|
||||
It is possible to find systems that require the parameters to be
|
||||
present. This can be due to either a defect in the original 1997
|
||||
syntax or a programming error where developers never got input where
|
||||
this was not true. The optimal solution is to fix these systems;
|
||||
where this is not possible, the problem needs to be restricted to
|
||||
that subsystem and not propagated to the Internet.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Diffie-Hellman operations specified by RFC 8410.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc8410#section-3
|
||||
*/
|
||||
abstract class RFC8410XAlgorithmIdentifier extends SpecificAlgorithmIdentifier implements AsymmetricCryptoAlgorithmIdentifier
|
||||
{
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\AsymmetricCryptoAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
From RFC 3447:
|
||||
|
||||
When rsaEncryption is used in an AlgorithmIdentifier the
|
||||
parameters MUST be present and MUST be NULL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for RSA encryption.
|
||||
*
|
||||
* @see http://www.oid-info.com/get/1.2.840.113549.1.1.1
|
||||
* @see https://tools.ietf.org/html/rfc3447#appendix-C
|
||||
*/
|
||||
final class RSAEncryptionAlgorithmIdentifier extends SpecificAlgorithmIdentifier implements AsymmetricCryptoAlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_RSA_ENCRYPTION);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'rsaEncryption';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$params->asNull();
|
||||
return self::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NullType
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return NullType::create();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Diffie-Hellman operation with curve25519.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.101.110
|
||||
*/
|
||||
final class X25519AlgorithmIdentifier extends RFC8410XAlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_X25519);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if ($params !== null) {
|
||||
throw new UnexpectedValueException('Parameters must be absent.');
|
||||
}
|
||||
return self::create();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'id-X25519';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Asymmetric;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for the Diffie-Hellman operation with curve448.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.101.111
|
||||
*/
|
||||
final class X448AlgorithmIdentifier extends RFC8410XAlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_X448);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if ($params !== null) {
|
||||
throw new UnexpectedValueException('Parameters must be absent.');
|
||||
}
|
||||
return self::create();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'id-X448';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for AES with 128-bit key in CBC mode.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3565.html#section-4.1
|
||||
* @see http://www.alvestrand.no/objectid/2.16.840.1.101.3.4.1.2.html
|
||||
* @see http://www.oid-info.com/get/2.16.840.1.101.3.4.1.2
|
||||
*/
|
||||
final class AES128CBCAlgorithmIdentifier extends AESCBCAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param string $iv Initialization vector
|
||||
*/
|
||||
protected function __construct(string $iv)
|
||||
{
|
||||
parent::__construct(self::OID_AES_128_CBC, $iv);
|
||||
}
|
||||
|
||||
public static function create(string $iv): self
|
||||
{
|
||||
return new self($iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
return self::create($iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'aes128-CBC';
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for AES with 192-bit key in CBC mode.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3565.html#section-4.1
|
||||
* @see http://www.alvestrand.no/objectid/2.16.840.1.101.3.4.1.22.html
|
||||
* @see http://www.oid-info.com/get/2.16.840.1.101.3.4.1.22
|
||||
*/
|
||||
final class AES192CBCAlgorithmIdentifier extends AESCBCAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param null|string $iv Initialization vector
|
||||
*/
|
||||
protected function __construct(?string $iv = null)
|
||||
{
|
||||
parent::__construct(self::OID_AES_192_CBC, $iv);
|
||||
}
|
||||
|
||||
public static function create(?string $iv = null): self
|
||||
{
|
||||
return new self($iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
return self::create($iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'aes192-CBC';
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for AES with 256-bit key in CBC mode.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3565.html#section-4.1
|
||||
* @see http://www.alvestrand.no/objectid/2.16.840.1.101.3.4.1.42.html
|
||||
* @see http://www.oid-info.com/get/2.16.840.1.101.3.4.1.42
|
||||
*/
|
||||
final class AES256CBCAlgorithmIdentifier extends AESCBCAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param null|string $iv Initialization vector
|
||||
*/
|
||||
protected function __construct(?string $iv = null)
|
||||
{
|
||||
parent::__construct(self::OID_AES_256_CBC, $iv);
|
||||
}
|
||||
|
||||
public static function create(?string $iv = null): self
|
||||
{
|
||||
return new self($iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
return new static($iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'aes256-CBC';
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
|
||||
/*
|
||||
From RFC 3565 - 4.1. AES Algorithm Identifiers and Parameters:
|
||||
|
||||
The AlgorithmIdentifier parameters field MUST be present, and the parameter field MUST contain a AES-IV:
|
||||
|
||||
AES-IV ::= OCTET STRING (SIZE(16))
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for AES-CBC algorithm identifiers.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc3565.html#section-4.1
|
||||
*/
|
||||
abstract class AESCBCAlgorithmIdentifier extends BlockCipherAlgorithmIdentifier
|
||||
{
|
||||
public function blockSize(): int
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
public function ivSize(): int
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
protected function paramsASN1(): OctetString
|
||||
{
|
||||
if (! isset($this->initializationVector)) {
|
||||
throw new LogicException('IV not set.');
|
||||
}
|
||||
return OctetString::create($this->initializationVector);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
/**
|
||||
* Base class for block cipher algorithm identifiers.
|
||||
*/
|
||||
abstract class BlockCipherAlgorithmIdentifier extends CipherAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* Get block size in bytes.
|
||||
*/
|
||||
abstract public function blockSize(): int;
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use function mb_strlen;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Base class for cipher algorithm identifiers.
|
||||
*/
|
||||
abstract class CipherAlgorithmIdentifier extends SpecificAlgorithmIdentifier
|
||||
{
|
||||
protected function __construct(
|
||||
string $oid,
|
||||
protected string $initializationVector
|
||||
) {
|
||||
$this->_checkIVSize($initializationVector);
|
||||
parent::__construct($oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key size in bytes.
|
||||
*/
|
||||
abstract public function keySize(): int;
|
||||
|
||||
/**
|
||||
* Get the initialization vector size in bytes.
|
||||
*/
|
||||
abstract public function ivSize(): int;
|
||||
|
||||
/**
|
||||
* Get initialization vector.
|
||||
*/
|
||||
public function initializationVector(): string
|
||||
{
|
||||
return $this->initializationVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copy of the object with given initialization vector.
|
||||
*
|
||||
* @param string $iv Initialization vector or null to remove
|
||||
*/
|
||||
public function withInitializationVector(string $iv): self
|
||||
{
|
||||
$this->_checkIVSize($iv);
|
||||
$obj = clone $this;
|
||||
$obj->initializationVector = $iv;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that initialization vector size is valid for the cipher.
|
||||
*/
|
||||
protected function _checkIVSize(string $iv): void
|
||||
{
|
||||
if (mb_strlen($iv, '8bit') !== $this->ivSize()) {
|
||||
throw new UnexpectedValueException('Invalid IV size.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
RFC 2898 defines parameters as follows:
|
||||
|
||||
{OCTET STRING (SIZE(8)) IDENTIFIED BY desCBC}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for DES cipher in CBC mode.
|
||||
*
|
||||
* @see http://www.alvestrand.no/objectid/1.3.14.3.2.7.html
|
||||
* @see http://www.oid-info.com/get/1.3.14.3.2.7
|
||||
* @see https://tools.ietf.org/html/rfc2898#appendix-C
|
||||
*/
|
||||
final class DESCBCAlgorithmIdentifier extends BlockCipherAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param null|string $iv Initialization vector
|
||||
*/
|
||||
private function __construct(?string $iv)
|
||||
{
|
||||
$this->_checkIVSize($iv);
|
||||
parent::__construct(self::OID_DES_CBC, $iv);
|
||||
}
|
||||
|
||||
public static function create(?string $iv = null): self
|
||||
{
|
||||
return new self($iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'desCBC';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
return self::create($iv);
|
||||
}
|
||||
|
||||
public function blockSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function ivSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OctetString
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
if (! isset($this->initializationVector)) {
|
||||
throw new LogicException('IV not set.');
|
||||
}
|
||||
return OctetString::create($this->initializationVector);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
RFC 2898 defines parameters as follows:
|
||||
|
||||
{OCTET STRING (SIZE(8)) IDENTIFIED BY des-EDE3-CBC}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for Triple-DES cipher in CBC mode.
|
||||
*
|
||||
* @see http://www.alvestrand.no/objectid/1.2.840.113549.3.7.html
|
||||
* @see http://oid-info.com/get/1.2.840.113549.3.7
|
||||
* @see https://tools.ietf.org/html/rfc2898#appendix-C
|
||||
* @see https://tools.ietf.org/html/rfc2630#section-12.4.1
|
||||
*/
|
||||
final class DESEDE3CBCAlgorithmIdentifier extends BlockCipherAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param null|string $iv Initialization vector
|
||||
*/
|
||||
private function __construct(?string $iv)
|
||||
{
|
||||
parent::__construct(self::OID_DES_EDE3_CBC, $iv);
|
||||
$this->_checkIVSize($iv);
|
||||
}
|
||||
|
||||
public static function create(?string $iv = null): self
|
||||
{
|
||||
return new self($iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'des-EDE3-CBC';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
return self::create($iv);
|
||||
}
|
||||
|
||||
public function blockSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
|
||||
public function ivSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OctetString
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
if (! isset($this->initializationVector)) {
|
||||
throw new LogicException('IV not set.');
|
||||
}
|
||||
return OctetString::create($this->initializationVector);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Cipher;
|
||||
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\Integer;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\OctetString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
Parameters may be seen in various forms. This implementation attemts
|
||||
to take them all into consideration.
|
||||
|
||||
# RFC 2268 - A Description of the RC2(r) Encryption Algorithm
|
||||
RC2-CBCParameter ::= CHOICE {
|
||||
iv IV,
|
||||
params SEQUENCE {
|
||||
version RC2Version,
|
||||
iv IV
|
||||
}
|
||||
}
|
||||
|
||||
# RFC 2898 - PKCS #5: Password-Based Cryptography Specification Version 2.0
|
||||
RC2-CBC-Parameter ::= SEQUENCE {
|
||||
rc2ParameterVersion INTEGER OPTIONAL,
|
||||
iv OCTET STRING (SIZE(8))
|
||||
}
|
||||
|
||||
# RFC 3370 - Cryptographic Message Syntax (CMS) Algorithms
|
||||
RC2CBCParameter ::= SEQUENCE {
|
||||
rc2ParameterVersion INTEGER,
|
||||
iv OCTET STRING } -- exactly 8 octets
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm identifier for RC2 cipher in CBC mode.
|
||||
*
|
||||
* @see http://www.alvestrand.no/objectid/1.2.840.113549.3.2.html
|
||||
* @see http://www.oid-info.com/get/1.2.840.113549.3.2
|
||||
* @see https://tools.ietf.org/html/rfc2268#section-6
|
||||
* @see https://tools.ietf.org/html/rfc3370#section-5.2
|
||||
* @see https://tools.ietf.org/html/rfc2898#appendix-C
|
||||
*/
|
||||
final class RC2CBCAlgorithmIdentifier extends BlockCipherAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* RFC 2268 translation table for effective key bits.
|
||||
*
|
||||
* This table maps effective key bytes from 0..255 to version number.
|
||||
*
|
||||
* @var array<int> ekb => version
|
||||
*/
|
||||
private const EKB_TABLE = [
|
||||
0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a,
|
||||
0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
|
||||
0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b,
|
||||
0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
|
||||
0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda,
|
||||
0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
|
||||
0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8,
|
||||
0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
|
||||
0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17,
|
||||
0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
|
||||
0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72,
|
||||
0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
|
||||
0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd,
|
||||
0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
|
||||
0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b,
|
||||
0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
|
||||
0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77,
|
||||
0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
|
||||
0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3,
|
||||
0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
|
||||
0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e,
|
||||
0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
|
||||
0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d,
|
||||
0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
|
||||
0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46,
|
||||
0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
|
||||
0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97,
|
||||
0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
|
||||
0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef,
|
||||
0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
|
||||
0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf,
|
||||
0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param int $effectiveKeyBits Number of effective key bits
|
||||
* @param null|string $iv Initialization vector
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly int $effectiveKeyBits,
|
||||
?string $iv
|
||||
) {
|
||||
parent::__construct(self::OID_RC2_CBC, $iv);
|
||||
$this->_checkIVSize($iv);
|
||||
}
|
||||
|
||||
public static function create(int $_effectiveKeyBits = 64, ?string $iv = null): self
|
||||
{
|
||||
return new self($_effectiveKeyBits, $iv);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'rc2-cbc';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (! isset($params)) {
|
||||
throw new UnexpectedValueException('No parameters.');
|
||||
}
|
||||
$key_bits = 32;
|
||||
// rfc2268 a choice containing only IV
|
||||
if ($params->isType(Element::TYPE_OCTET_STRING)) {
|
||||
$iv = $params->asOctetString()
|
||||
->string();
|
||||
} else {
|
||||
$seq = $params->asSequence();
|
||||
$idx = 0;
|
||||
// version is optional in rfc2898
|
||||
if ($seq->has($idx, Element::TYPE_INTEGER)) {
|
||||
$version = $seq->at($idx++)
|
||||
->asInteger()
|
||||
->intNumber();
|
||||
$key_bits = self::_versionToEKB($version);
|
||||
}
|
||||
// IV is present in all variants
|
||||
$iv = $seq->at($idx)
|
||||
->asOctetString()
|
||||
->string();
|
||||
}
|
||||
return self::create($key_bits, $iv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of effective key bits.
|
||||
*/
|
||||
public function effectiveKeyBits(): int
|
||||
{
|
||||
return $this->effectiveKeyBits;
|
||||
}
|
||||
|
||||
public function blockSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function keySize(): int
|
||||
{
|
||||
return (int) round($this->effectiveKeyBits / 8);
|
||||
}
|
||||
|
||||
public function ivSize(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Sequence
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
if ($this->effectiveKeyBits >= 256) {
|
||||
$version = $this->effectiveKeyBits;
|
||||
} else {
|
||||
$version = self::EKB_TABLE[$this->effectiveKeyBits];
|
||||
}
|
||||
if (! isset($this->initializationVector)) {
|
||||
throw new LogicException('IV not set.');
|
||||
}
|
||||
return Sequence::create(Integer::create($version), OctetString::create($this->initializationVector));
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate version number to number of effective key bits.
|
||||
*/
|
||||
private static function _versionToEKB(int $version): int
|
||||
{
|
||||
static $lut;
|
||||
if ($version > 255) {
|
||||
return $version;
|
||||
}
|
||||
if (! isset($lut)) {
|
||||
$lut = array_flip(self::EKB_TABLE);
|
||||
}
|
||||
return $lut[$version];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
|
||||
/**
|
||||
* Base interface for algorithm identifiers.
|
||||
*/
|
||||
interface AlgorithmIdentifierType
|
||||
{
|
||||
/**
|
||||
* Get the object identifier of the algorithm.
|
||||
*
|
||||
* @return string Object identifier in dotted format
|
||||
*/
|
||||
public function oid(): string;
|
||||
|
||||
/**
|
||||
* Get a human-readable name of the algorithm.
|
||||
*/
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Sequence;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for asymmetric cryptography algorithms.
|
||||
*/
|
||||
interface AsymmetricCryptoAlgorithmIdentifier extends AlgorithmIdentifierType
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for encryption algorithms.
|
||||
*/
|
||||
interface EncryptionAlgorithmIdentifier extends AlgorithmIdentifierType
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for hash functions.
|
||||
*/
|
||||
interface HashAlgorithmIdentifier extends AlgorithmIdentifierType
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for pseudorandom functions.
|
||||
*/
|
||||
interface PRFAlgorithmIdentifier extends AlgorithmIdentifierType
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature;
|
||||
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Algorithm identifier for signature algorithms.
|
||||
*/
|
||||
interface SignatureAlgorithmIdentifier extends AlgorithmIdentifierType
|
||||
{
|
||||
/**
|
||||
* Check whether signature algorithm supports given key algorithm.
|
||||
*/
|
||||
public function supportsKeyAlgorithm(AlgorithmIdentifier $algo): bool;
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* Generic algorithm identifier to hold parameters as ASN.1 objects.
|
||||
*/
|
||||
final class GenericAlgorithmIdentifier extends AlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param string $oid Algorithm OID
|
||||
* @param null|UnspecifiedType $params Parameters
|
||||
*/
|
||||
private function __construct(
|
||||
string $oid,
|
||||
private readonly ?UnspecifiedType $params
|
||||
) {
|
||||
parent::__construct($oid);
|
||||
}
|
||||
|
||||
public static function create(string $oid, ?UnspecifiedType $params = null): self
|
||||
{
|
||||
return new self($oid, $params);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
public function parameters(): ?UnspecifiedType
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return $this->params?->asElement();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\HashAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\PRFAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/*
|
||||
Per RFC 2898 this algorithm identifier has no parameters:
|
||||
|
||||
algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::=
|
||||
{algorithm id-hmacWithSHA1, parameters NULL : NULL}
|
||||
*/
|
||||
|
||||
/**
|
||||
* HMAC-SHA-1 algorithm identifier.
|
||||
*
|
||||
* @see http://www.alvestrand.no/objectid/1.2.840.113549.2.7.html
|
||||
* @see http://www.oid-info.com/get/1.2.840.113549.2.7
|
||||
* @see https://tools.ietf.org/html/rfc2898#appendix-C
|
||||
*/
|
||||
final class HMACWithSHA1AlgorithmIdentifier extends SpecificAlgorithmIdentifier implements HashAlgorithmIdentifier, PRFAlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_HMAC_WITH_SHA1);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'hmacWithSHA1';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
if (isset($params)) {
|
||||
throw new UnexpectedValueException('Parameters must be omitted.');
|
||||
}
|
||||
return self::create();
|
||||
}
|
||||
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* HMAC with SHA-224 algorithm identifier.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4231#section-3.1
|
||||
*/
|
||||
final class HMACWithSHA224AlgorithmIdentifier extends RFC4231HMACAlgorithmIdentifier
|
||||
{
|
||||
private function __construct(?Element $params)
|
||||
{
|
||||
parent::__construct(self::OID_HMAC_WITH_SHA224, $params);
|
||||
}
|
||||
|
||||
public static function create(?Element $params = null): self
|
||||
{
|
||||
return new self($params);
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): self
|
||||
{
|
||||
/*
|
||||
* RFC 4231 states that the "parameter" component SHOULD be present
|
||||
* but have type NULL.
|
||||
*/
|
||||
return self::create($params?->asNull());
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'hmacWithSHA224';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* HMAC with SHA-256 algorithm identifier.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4231#section-3.1
|
||||
*/
|
||||
final class HMACWithSHA256AlgorithmIdentifier extends RFC4231HMACAlgorithmIdentifier
|
||||
{
|
||||
private function __construct(?Element $params)
|
||||
{
|
||||
parent::__construct(self::OID_HMAC_WITH_SHA256, $params);
|
||||
}
|
||||
|
||||
public static function create(?Element $params = null): self
|
||||
{
|
||||
return new self($params);
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): self
|
||||
{
|
||||
/*
|
||||
* RFC 4231 states that the "parameter" component SHOULD be present
|
||||
* but have type NULL.
|
||||
*/
|
||||
return self::create($params?->asNull());
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'hmacWithSHA256';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* HMAC with SHA-384 algorithm identifier.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4231#section-3.1
|
||||
*/
|
||||
final class HMACWithSHA384AlgorithmIdentifier extends RFC4231HMACAlgorithmIdentifier
|
||||
{
|
||||
private function __construct(?Element $params)
|
||||
{
|
||||
parent::__construct(self::OID_HMAC_WITH_SHA384, $params);
|
||||
}
|
||||
|
||||
public static function create(?Element $params = null): self
|
||||
{
|
||||
return new self($params);
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): self
|
||||
{
|
||||
/*
|
||||
* RFC 4231 states that the "parameter" component SHOULD be present
|
||||
* but have type NULL.
|
||||
*/
|
||||
return new self($params?->asNull());
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'hmacWithSHA384';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
|
||||
/**
|
||||
* HMAC with SHA-512 algorithm identifier.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4231#section-3.1
|
||||
*/
|
||||
final class HMACWithSHA512AlgorithmIdentifier extends RFC4231HMACAlgorithmIdentifier
|
||||
{
|
||||
private function __construct(?Element $params)
|
||||
{
|
||||
parent::__construct(self::OID_HMAC_WITH_SHA512, $params);
|
||||
}
|
||||
|
||||
public static function create(?Element $params = null): self
|
||||
{
|
||||
return new self($params);
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): self
|
||||
{
|
||||
/*
|
||||
* RFC 4231 states that the "parameter" component SHOULD be present
|
||||
* but have type NULL.
|
||||
*/
|
||||
return self::create($params?->asNull());
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'hmacWithSHA512';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\HashAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/*
|
||||
From RFC 1321 - 1. Executive Summary
|
||||
|
||||
In the X.509 type AlgorithmIdentifier, the parameters for MD5
|
||||
should have type NULL.
|
||||
|
||||
From RFC 3370 - 2.2 MD5
|
||||
|
||||
The AlgorithmIdentifier parameters field MUST be present, and the
|
||||
parameters field MUST contain NULL. Implementations MAY accept the
|
||||
MD5 AlgorithmIdentifiers with absent parameters as well as NULL
|
||||
parameters.
|
||||
*/
|
||||
|
||||
/**
|
||||
* MD5 algorithm identifier.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.2.840.113549.2.5
|
||||
* @see https://tools.ietf.org/html/rfc1321#section-1
|
||||
* @see https://tools.ietf.org/html/rfc3370#section-2.2
|
||||
*/
|
||||
final class MD5AlgorithmIdentifier extends SpecificAlgorithmIdentifier implements HashAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* Parameters.
|
||||
*/
|
||||
private ?NullType $params;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_MD5);
|
||||
$this->params = NullType::create();
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'md5';
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): static
|
||||
{
|
||||
$obj = static::create();
|
||||
// if parameters field is present, it must be null type
|
||||
if (isset($params)) {
|
||||
$obj->params = $params->asNull();
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|NullType
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\HashAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\PRFAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* Base class for HMAC algorithm identifiers specified in RFC 4231.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4231#section-3.1
|
||||
*/
|
||||
abstract class RFC4231HMACAlgorithmIdentifier extends SpecificAlgorithmIdentifier implements HashAlgorithmIdentifier, PRFAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* @param Element|null $params Parameters stored for re-encoding.
|
||||
*/
|
||||
protected function __construct(
|
||||
string $oid,
|
||||
protected ?Element $params
|
||||
) {
|
||||
parent::__construct($oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|NullType
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\NullType;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Feature\HashAlgorithmIdentifier;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/*
|
||||
From RFC 3370 - 2.1 SHA-1
|
||||
|
||||
The AlgorithmIdentifier parameters field is OPTIONAL. If present,
|
||||
the parameters field MUST contain a NULL. Implementations MUST
|
||||
accept SHA-1 AlgorithmIdentifiers with absent parameters.
|
||||
Implementations MUST accept SHA-1 AlgorithmIdentifiers with NULL
|
||||
parameters. Implementations SHOULD generate SHA-1
|
||||
AlgorithmIdentifiers with absent parameters.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SHA-1 algorithm identifier.
|
||||
*
|
||||
* @see http://oid-info.com/get/1.3.14.3.2.26
|
||||
* @see https://tools.ietf.org/html/rfc3370#section-2.1
|
||||
*/
|
||||
final class SHA1AlgorithmIdentifier extends SpecificAlgorithmIdentifier implements HashAlgorithmIdentifier
|
||||
{
|
||||
/**
|
||||
* Parameters.
|
||||
*/
|
||||
private ?NullType $params;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_SHA1);
|
||||
$this->params = null;
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'sha1';
|
||||
}
|
||||
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): static
|
||||
{
|
||||
$obj = static::create();
|
||||
// if parameters field is present, it must be null type
|
||||
if (isset($params)) {
|
||||
$obj->params = $params->asNull();
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|NullType
|
||||
*/
|
||||
protected function paramsASN1(): ?Element
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* SHA-224 algorithm identifier.
|
||||
*
|
||||
* @see http://oid-info.com/get/2.16.840.1.101.3.4.2.4
|
||||
* @see https://tools.ietf.org/html/rfc3874#section-4
|
||||
* @see https://tools.ietf.org/html/rfc4055#section-2.1
|
||||
* @see https://tools.ietf.org/html/rfc5754#section-2.1
|
||||
*/
|
||||
final class SHA224AlgorithmIdentifier extends SHA2AlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_SHA224);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
$obj = new static();
|
||||
// if parameters field is present, it must be null type
|
||||
if (isset($params)) {
|
||||
$obj->_params = $params->asNull();
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'sha224';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\Hash;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
|
||||
|
||||
/**
|
||||
* SHA-256 algorithm identifier.
|
||||
*
|
||||
* @see http://oid-info.com/get/2.16.840.1.101.3.4.2.1
|
||||
* @see https://tools.ietf.org/html/rfc4055#section-2.1
|
||||
* @see https://tools.ietf.org/html/rfc5754#section-2.2
|
||||
*/
|
||||
final class SHA256AlgorithmIdentifier extends SHA2AlgorithmIdentifier
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
parent::__construct(self::OID_SHA256);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1Params(?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
|
||||
{
|
||||
$obj = new static();
|
||||
// if parameters field is present, it must be null type
|
||||
if (isset($params)) {
|
||||
$obj->_params = $params->asNull();
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'sha256';
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user