first commit
This commit is contained in:
21
libraries/vendor/spomky-labs/cbor-php/LICENSE
vendored
Normal file
21
libraries/vendor/spomky-labs/cbor-php/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 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.
|
||||
32
libraries/vendor/spomky-labs/cbor-php/src/AbstractCBORObject.php
vendored
Normal file
32
libraries/vendor/spomky-labs/cbor-php/src/AbstractCBORObject.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use function chr;
|
||||
use Stringable;
|
||||
|
||||
abstract class AbstractCBORObject implements CBORObject, Stringable
|
||||
{
|
||||
public function __construct(
|
||||
private int $majorType,
|
||||
protected int $additionalInformation
|
||||
) {
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return chr($this->majorType << 5 | $this->additionalInformation);
|
||||
}
|
||||
|
||||
public function getMajorType(): int
|
||||
{
|
||||
return $this->majorType;
|
||||
}
|
||||
|
||||
public function getAdditionalInformation(): int
|
||||
{
|
||||
return $this->additionalInformation;
|
||||
}
|
||||
}
|
||||
56
libraries/vendor/spomky-labs/cbor-php/src/ByteStringObject.php
vendored
Normal file
56
libraries/vendor/spomky-labs/cbor-php/src/ByteStringObject.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\ByteStringObjectTest
|
||||
*/
|
||||
final class ByteStringObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_BYTE_STRING;
|
||||
|
||||
private string $value;
|
||||
|
||||
private ?string $length = null;
|
||||
|
||||
public function __construct(string $data)
|
||||
{
|
||||
[$additionalInformation, $length] = LengthCalculator::getLengthOfString($data);
|
||||
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
$this->length = $length;
|
||||
$this->value = $data;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->length !== null) {
|
||||
$result .= $this->length;
|
||||
}
|
||||
|
||||
return $result . $this->value;
|
||||
}
|
||||
|
||||
public static function create(string $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return mb_strlen($this->value, '8bit');
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
94
libraries/vendor/spomky-labs/cbor-php/src/CBORObject.php
vendored
Normal file
94
libraries/vendor/spomky-labs/cbor-php/src/CBORObject.php
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
interface CBORObject
|
||||
{
|
||||
public const MAJOR_TYPE_UNSIGNED_INTEGER = 0b000;
|
||||
|
||||
public const MAJOR_TYPE_NEGATIVE_INTEGER = 0b001;
|
||||
|
||||
public const MAJOR_TYPE_BYTE_STRING = 0b010;
|
||||
|
||||
public const MAJOR_TYPE_TEXT_STRING = 0b011;
|
||||
|
||||
public const MAJOR_TYPE_LIST = 0b100;
|
||||
|
||||
public const MAJOR_TYPE_MAP = 0b101;
|
||||
|
||||
public const MAJOR_TYPE_TAG = 0b110;
|
||||
|
||||
public const MAJOR_TYPE_OTHER_TYPE = 0b111;
|
||||
|
||||
public const LENGTH_1_BYTE = 0b00011000;
|
||||
|
||||
public const LENGTH_2_BYTES = 0b00011001;
|
||||
|
||||
public const LENGTH_4_BYTES = 0b00011010;
|
||||
|
||||
public const LENGTH_8_BYTES = 0b00011011;
|
||||
|
||||
public const LENGTH_INDEFINITE = 0b00011111;
|
||||
|
||||
public const FUTURE_USE_1 = 0b00011100;
|
||||
|
||||
public const FUTURE_USE_2 = 0b00011101;
|
||||
|
||||
public const FUTURE_USE_3 = 0b00011110;
|
||||
|
||||
public const OBJECT_FALSE = 20;
|
||||
|
||||
public const OBJECT_TRUE = 21;
|
||||
|
||||
public const OBJECT_NULL = 22;
|
||||
|
||||
public const OBJECT_UNDEFINED = 23;
|
||||
|
||||
public const OBJECT_SIMPLE_VALUE = 24;
|
||||
|
||||
public const OBJECT_HALF_PRECISION_FLOAT = 25;
|
||||
|
||||
public const OBJECT_SINGLE_PRECISION_FLOAT = 26;
|
||||
|
||||
public const OBJECT_DOUBLE_PRECISION_FLOAT = 27;
|
||||
|
||||
public const OBJECT_BREAK = 0b00011111;
|
||||
|
||||
public const TAG_STANDARD_DATETIME = 0;
|
||||
|
||||
public const TAG_EPOCH_DATETIME = 1;
|
||||
|
||||
public const TAG_UNSIGNED_BIG_NUM = 2;
|
||||
|
||||
public const TAG_NEGATIVE_BIG_NUM = 3;
|
||||
|
||||
public const TAG_DECIMAL_FRACTION = 4;
|
||||
|
||||
public const TAG_BIG_FLOAT = 5;
|
||||
|
||||
public const TAG_ENCODED_BASE64_URL = 21;
|
||||
|
||||
public const TAG_ENCODED_BASE64 = 22;
|
||||
|
||||
public const TAG_ENCODED_BASE16 = 23;
|
||||
|
||||
public const TAG_ENCODED_CBOR = 24;
|
||||
|
||||
public const TAG_URI = 32;
|
||||
|
||||
public const TAG_BASE64_URL = 33;
|
||||
|
||||
public const TAG_BASE64 = 34;
|
||||
|
||||
public const TAG_MIME = 36;
|
||||
|
||||
public const TAG_CBOR = 55799;
|
||||
|
||||
public function __toString(): string;
|
||||
|
||||
public function getMajorType(): int;
|
||||
|
||||
public function getAdditionalInformation(): int;
|
||||
}
|
||||
239
libraries/vendor/spomky-labs/cbor-php/src/Decoder.php
vendored
Normal file
239
libraries/vendor/spomky-labs/cbor-php/src/Decoder.php
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use CBOR\OtherObject\BreakObject;
|
||||
use CBOR\OtherObject\DoublePrecisionFloatObject;
|
||||
use CBOR\OtherObject\FalseObject;
|
||||
use CBOR\OtherObject\HalfPrecisionFloatObject;
|
||||
use CBOR\OtherObject\NullObject;
|
||||
use CBOR\OtherObject\OtherObjectManager;
|
||||
use CBOR\OtherObject\OtherObjectManagerInterface;
|
||||
use CBOR\OtherObject\SimpleObject;
|
||||
use CBOR\OtherObject\SinglePrecisionFloatObject;
|
||||
use CBOR\OtherObject\TrueObject;
|
||||
use CBOR\OtherObject\UndefinedObject;
|
||||
use CBOR\Tag\Base16EncodingTag;
|
||||
use CBOR\Tag\Base64EncodingTag;
|
||||
use CBOR\Tag\Base64Tag;
|
||||
use CBOR\Tag\Base64UrlEncodingTag;
|
||||
use CBOR\Tag\Base64UrlTag;
|
||||
use CBOR\Tag\BigFloatTag;
|
||||
use CBOR\Tag\CBOREncodingTag;
|
||||
use CBOR\Tag\CBORTag;
|
||||
use CBOR\Tag\DatetimeTag;
|
||||
use CBOR\Tag\DecimalFractionTag;
|
||||
use CBOR\Tag\MimeTag;
|
||||
use CBOR\Tag\NegativeBigIntegerTag;
|
||||
use CBOR\Tag\TagManager;
|
||||
use CBOR\Tag\TagManagerInterface;
|
||||
use CBOR\Tag\TimestampTag;
|
||||
use CBOR\Tag\UnsignedBigIntegerTag;
|
||||
use CBOR\Tag\UriTag;
|
||||
use InvalidArgumentException;
|
||||
use function ord;
|
||||
use RuntimeException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
final class Decoder implements DecoderInterface
|
||||
{
|
||||
private TagManagerInterface $tagObjectManager;
|
||||
|
||||
private OtherObjectManagerInterface $otherTypeManager;
|
||||
|
||||
public function __construct(
|
||||
?TagManagerInterface $tagObjectManager = null,
|
||||
?OtherObjectManagerInterface $otherTypeManager = null
|
||||
) {
|
||||
$this->tagObjectManager = $tagObjectManager ?? $this->generateTagManager();
|
||||
$this->otherTypeManager = $otherTypeManager ?? $this->generateOtherObjectManager();
|
||||
}
|
||||
|
||||
public static function create(
|
||||
?TagManagerInterface $tagObjectManager = null,
|
||||
?OtherObjectManagerInterface $otherTypeManager = null
|
||||
): self {
|
||||
return new self($tagObjectManager, $otherTypeManager);
|
||||
}
|
||||
|
||||
public function decode(Stream $stream): CBORObject
|
||||
{
|
||||
return $this->process($stream, false);
|
||||
}
|
||||
|
||||
private function process(Stream $stream, bool $breakable): CBORObject
|
||||
{
|
||||
$ib = ord($stream->read(1));
|
||||
$mt = $ib >> 5;
|
||||
$ai = $ib & 0b00011111;
|
||||
$val = null;
|
||||
switch ($ai) {
|
||||
case CBORObject::LENGTH_1_BYTE: //24
|
||||
case CBORObject::LENGTH_2_BYTES: //25
|
||||
case CBORObject::LENGTH_4_BYTES: //26
|
||||
case CBORObject::LENGTH_8_BYTES: //27
|
||||
$val = $stream->read(2 ** ($ai & 0b00000111));
|
||||
break;
|
||||
case CBORObject::FUTURE_USE_1: //28
|
||||
case CBORObject::FUTURE_USE_2: //29
|
||||
case CBORObject::FUTURE_USE_3: //30
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Cannot parse the data. Found invalid Additional Information "%s" (%d).',
|
||||
str_pad(decbin($ai), 8, '0', STR_PAD_LEFT),
|
||||
$ai
|
||||
));
|
||||
case CBORObject::LENGTH_INDEFINITE: //31
|
||||
return $this->processInfinite($stream, $mt, $breakable);
|
||||
}
|
||||
|
||||
return $this->processFinite($stream, $mt, $ai, $val);
|
||||
}
|
||||
|
||||
private function processFinite(Stream $stream, int $mt, int $ai, ?string $val): CBORObject
|
||||
{
|
||||
switch ($mt) {
|
||||
case CBORObject::MAJOR_TYPE_UNSIGNED_INTEGER: //0
|
||||
return UnsignedIntegerObject::createObjectForValue($ai, $val);
|
||||
case CBORObject::MAJOR_TYPE_NEGATIVE_INTEGER: //1
|
||||
return NegativeIntegerObject::createObjectForValue($ai, $val);
|
||||
case CBORObject::MAJOR_TYPE_BYTE_STRING: //2
|
||||
$length = $val === null ? $ai : Utils::binToInt($val);
|
||||
|
||||
return ByteStringObject::create($stream->read($length));
|
||||
case CBORObject::MAJOR_TYPE_TEXT_STRING: //3
|
||||
$length = $val === null ? $ai : Utils::binToInt($val);
|
||||
|
||||
return TextStringObject::create($stream->read($length));
|
||||
case CBORObject::MAJOR_TYPE_LIST: //4
|
||||
$object = ListObject::create();
|
||||
$nbItems = $val === null ? $ai : Utils::binToInt($val);
|
||||
for ($i = 0; $i < $nbItems; ++$i) {
|
||||
$object->add($this->process($stream, false));
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_MAP: //5
|
||||
$object = MapObject::create();
|
||||
$nbItems = $val === null ? $ai : Utils::binToInt($val);
|
||||
for ($i = 0; $i < $nbItems; ++$i) {
|
||||
$object->add($this->process($stream, false), $this->process($stream, false));
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_TAG: //6
|
||||
return $this->tagObjectManager->createObjectForValue($ai, $val, $this->process($stream, false));
|
||||
case CBORObject::MAJOR_TYPE_OTHER_TYPE: //7
|
||||
return $this->otherTypeManager->createObjectForValue($ai, $val);
|
||||
default:
|
||||
throw new RuntimeException(sprintf(
|
||||
'Unsupported major type "%s" (%d).',
|
||||
str_pad(decbin($mt), 5, '0', STR_PAD_LEFT),
|
||||
$mt
|
||||
)); // Should never append
|
||||
}
|
||||
}
|
||||
|
||||
private function processInfinite(Stream $stream, int $mt, bool $breakable): CBORObject
|
||||
{
|
||||
switch ($mt) {
|
||||
case CBORObject::MAJOR_TYPE_BYTE_STRING: //2
|
||||
$object = IndefiniteLengthByteStringObject::create();
|
||||
while (! ($it = $this->process($stream, true)) instanceof BreakObject) {
|
||||
if (! $it instanceof ByteStringObject) {
|
||||
throw new RuntimeException(
|
||||
'Unable to parse the data. Infinite Byte String object can only get Byte String objects.'
|
||||
);
|
||||
}
|
||||
$object->add($it);
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_TEXT_STRING: //3
|
||||
$object = IndefiniteLengthTextStringObject::create();
|
||||
while (! ($it = $this->process($stream, true)) instanceof BreakObject) {
|
||||
if (! $it instanceof TextStringObject) {
|
||||
throw new RuntimeException(
|
||||
'Unable to parse the data. Infinite Text String object can only get Text String objects.'
|
||||
);
|
||||
}
|
||||
$object->add($it);
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_LIST: //4
|
||||
$object = IndefiniteLengthListObject::create();
|
||||
$it = $this->process($stream, true);
|
||||
while (! $it instanceof BreakObject) {
|
||||
$object->add($it);
|
||||
$it = $this->process($stream, true);
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_MAP: //5
|
||||
$object = IndefiniteLengthMapObject::create();
|
||||
while (! ($it = $this->process($stream, true)) instanceof BreakObject) {
|
||||
$object->add($it, $this->process($stream, false));
|
||||
}
|
||||
|
||||
return $object;
|
||||
case CBORObject::MAJOR_TYPE_OTHER_TYPE: //7
|
||||
if (! $breakable) {
|
||||
throw new InvalidArgumentException('Cannot parse the data. No enclosing indefinite.');
|
||||
}
|
||||
|
||||
return BreakObject::create();
|
||||
case CBORObject::MAJOR_TYPE_UNSIGNED_INTEGER: //0
|
||||
case CBORObject::MAJOR_TYPE_NEGATIVE_INTEGER: //1
|
||||
case CBORObject::MAJOR_TYPE_TAG: //6
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Cannot parse the data. Found infinite length for Major Type "%s" (%d).',
|
||||
str_pad(decbin($mt), 5, '0', STR_PAD_LEFT),
|
||||
$mt
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private function generateTagManager(): TagManagerInterface
|
||||
{
|
||||
return TagManager::create()
|
||||
->add(DatetimeTag::class)
|
||||
->add(TimestampTag::class)
|
||||
|
||||
->add(UnsignedBigIntegerTag::class)
|
||||
->add(NegativeBigIntegerTag::class)
|
||||
|
||||
->add(DecimalFractionTag::class)
|
||||
->add(BigFloatTag::class)
|
||||
|
||||
->add(Base64UrlEncodingTag::class)
|
||||
->add(Base64EncodingTag::class)
|
||||
->add(Base16EncodingTag::class)
|
||||
->add(CBOREncodingTag::class)
|
||||
|
||||
->add(UriTag::class)
|
||||
->add(Base64UrlTag::class)
|
||||
->add(Base64Tag::class)
|
||||
->add(MimeTag::class)
|
||||
|
||||
->add(CBORTag::class)
|
||||
;
|
||||
}
|
||||
|
||||
private function generateOtherObjectManager(): OtherObjectManagerInterface
|
||||
{
|
||||
return OtherObjectManager::create()
|
||||
->add(BreakObject::class)
|
||||
->add(SimpleObject::class)
|
||||
->add(FalseObject::class)
|
||||
->add(TrueObject::class)
|
||||
->add(NullObject::class)
|
||||
->add(UndefinedObject::class)
|
||||
->add(HalfPrecisionFloatObject::class)
|
||||
->add(SinglePrecisionFloatObject::class)
|
||||
->add(DoublePrecisionFloatObject::class)
|
||||
;
|
||||
}
|
||||
}
|
||||
10
libraries/vendor/spomky-labs/cbor-php/src/DecoderInterface.php
vendored
Normal file
10
libraries/vendor/spomky-labs/cbor-php/src/DecoderInterface.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
interface DecoderInterface
|
||||
{
|
||||
public function decode(Stream $stream): CBORObject;
|
||||
}
|
||||
84
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthByteStringObject.php
vendored
Normal file
84
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthByteStringObject.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\IndefiniteLengthByteStringObjectTest
|
||||
*/
|
||||
final class IndefiniteLengthByteStringObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_BYTE_STRING;
|
||||
|
||||
private const ADDITIONAL_INFORMATION = self::LENGTH_INDEFINITE;
|
||||
|
||||
/**
|
||||
* @var ByteStringObject[]
|
||||
*/
|
||||
private array $chunks = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
foreach ($this->chunks as $chunk) {
|
||||
$result .= $chunk->__toString();
|
||||
}
|
||||
|
||||
return $result . "\xFF";
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function add(ByteStringObject $chunk): self
|
||||
{
|
||||
$this->chunks[] = $chunk;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function append(string $chunk): self
|
||||
{
|
||||
$this->add(ByteStringObject::create($chunk));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($this->chunks as $chunk) {
|
||||
$result .= $chunk->getValue();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
$length = 0;
|
||||
foreach ($this->chunks as $chunk) {
|
||||
$length += $chunk->getLength();
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($this->chunks as $chunk) {
|
||||
$result .= $chunk->normalize();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
137
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthListObject.php
vendored
Normal file
137
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthListObject.php
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @phpstan-implements ArrayAccess<int, CBORObject>
|
||||
* @phpstan-implements IteratorAggregate<int, CBORObject>
|
||||
* @final
|
||||
*/
|
||||
class IndefiniteLengthListObject extends AbstractCBORObject implements IteratorAggregate, Normalizable, ArrayAccess
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_LIST;
|
||||
|
||||
private const ADDITIONAL_INFORMATION = self::LENGTH_INDEFINITE;
|
||||
|
||||
/**
|
||||
* @var CBORObject[]
|
||||
*/
|
||||
private array $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
foreach ($this->data as $object) {
|
||||
$result .= (string) $object;
|
||||
}
|
||||
|
||||
return $result . "\xFF";
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function normalize(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (CBORObject $object) => $object instanceof Normalizable ? $object->normalize() : $object,
|
||||
$this->data
|
||||
);
|
||||
}
|
||||
|
||||
public function add(CBORObject $item): self
|
||||
{
|
||||
$this->data[] = $item;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(int $index): bool
|
||||
{
|
||||
return array_key_exists($index, $this->data);
|
||||
}
|
||||
|
||||
public function remove(int $index): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
return $this;
|
||||
}
|
||||
unset($this->data[$index]);
|
||||
$this->data = array_values($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(int $index): CBORObject
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
return $this->data[$index];
|
||||
}
|
||||
|
||||
public function set(int $index, CBORObject $object): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
$this->data[$index] = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int, CBORObject>
|
||||
*/
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset): CBORObject
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
$this->add($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
}
|
||||
149
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthMapObject.php
vendored
Normal file
149
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthMapObject.php
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @phpstan-implements ArrayAccess<int, CBORObject>
|
||||
* @phpstan-implements IteratorAggregate<int, MapItem>
|
||||
* @final
|
||||
*/
|
||||
class IndefiniteLengthMapObject extends AbstractCBORObject implements IteratorAggregate, Normalizable, ArrayAccess
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_MAP;
|
||||
|
||||
private const ADDITIONAL_INFORMATION = self::LENGTH_INDEFINITE;
|
||||
|
||||
/**
|
||||
* @var MapItem[]
|
||||
*/
|
||||
private array $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
foreach ($this->data as $object) {
|
||||
$result .= (string) $object->getKey();
|
||||
$result .= (string) $object->getValue();
|
||||
}
|
||||
|
||||
return $result . "\xFF";
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function add(CBORObject $key, CBORObject $value): self
|
||||
{
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
$this->data[$key->normalize()] = MapItem::create($key, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(int|string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
|
||||
public function remove(int|string $index): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
return $this;
|
||||
}
|
||||
unset($this->data[$index]);
|
||||
$this->data = array_values($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(int|string $index): CBORObject
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
return $this->data[$index]->getValue();
|
||||
}
|
||||
|
||||
public function set(MapItem $object): self
|
||||
{
|
||||
$key = $object->getKey();
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
|
||||
$this->data[$key->normalize()] = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int, MapItem>
|
||||
*/
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function normalize(): array
|
||||
{
|
||||
return array_reduce($this->data, static function (array $carry, MapItem $item): array {
|
||||
$key = $item->getKey();
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
$valueObject = $item->getValue();
|
||||
$carry[$key->normalize()] = $valueObject instanceof Normalizable ? $valueObject->normalize() : $valueObject;
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset): CBORObject
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
if (! $offset instanceof CBORObject) {
|
||||
throw new InvalidArgumentException('Invalid key');
|
||||
}
|
||||
if (! $value instanceof CBORObject) {
|
||||
throw new InvalidArgumentException('Invalid value');
|
||||
}
|
||||
|
||||
$this->set(MapItem::create($offset, $value));
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
}
|
||||
84
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthTextStringObject.php
vendored
Normal file
84
libraries/vendor/spomky-labs/cbor-php/src/IndefiniteLengthTextStringObject.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\IndefiniteLengthTextStringObjectTest
|
||||
*/
|
||||
final class IndefiniteLengthTextStringObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_TEXT_STRING;
|
||||
|
||||
private const ADDITIONAL_INFORMATION = self::LENGTH_INDEFINITE;
|
||||
|
||||
/**
|
||||
* @var TextStringObject[]
|
||||
*/
|
||||
private array $data = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
foreach ($this->data as $object) {
|
||||
$result .= (string) $object;
|
||||
}
|
||||
|
||||
return $result . "\xFF";
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function add(TextStringObject $chunk): self
|
||||
{
|
||||
$this->data[] = $chunk;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function append(string $chunk): self
|
||||
{
|
||||
$this->add(TextStringObject::create($chunk));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($this->data as $object) {
|
||||
$result .= $object->getValue();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
$length = 0;
|
||||
foreach ($this->data as $object) {
|
||||
$length += $object->getLength();
|
||||
}
|
||||
|
||||
return $length;
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
$result = '';
|
||||
foreach ($this->data as $object) {
|
||||
$result .= $object->normalize();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
65
libraries/vendor/spomky-labs/cbor-php/src/LengthCalculator.php
vendored
Normal file
65
libraries/vendor/spomky-labs/cbor-php/src/LengthCalculator.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use function chr;
|
||||
use function count;
|
||||
use InvalidArgumentException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
final class LengthCalculator
|
||||
{
|
||||
/**
|
||||
* @return array{int, null|string}
|
||||
*/
|
||||
public static function getLengthOfString(string $data): array
|
||||
{
|
||||
$length = mb_strlen($data, '8bit');
|
||||
|
||||
return self::computeLength($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $data
|
||||
*
|
||||
* @return array{int, null|string}
|
||||
*/
|
||||
public static function getLengthOfArray(array $data): array
|
||||
{
|
||||
$length = count($data);
|
||||
|
||||
return self::computeLength($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{int, null|string}
|
||||
*/
|
||||
private static function computeLength(int $length): array
|
||||
{
|
||||
return match (true) {
|
||||
$length <= 23 => [$length, null],
|
||||
$length <= 0xFF => [24, chr($length)],
|
||||
$length <= 0xFFFF => [25, self::hex2bin(dechex($length))],
|
||||
$length <= 0xFFFFFFFF => [26, self::hex2bin(dechex($length))],
|
||||
BigInteger::of($length)->isLessThan(BigInteger::fromBase('FFFFFFFFFFFFFFFF', 16)) => [
|
||||
27,
|
||||
self::hex2bin(dechex($length)),
|
||||
],
|
||||
default => [31, null],
|
||||
};
|
||||
}
|
||||
|
||||
private static function hex2bin(string $data): string
|
||||
{
|
||||
$data = str_pad($data, (int) (2 ** ceil(log(mb_strlen($data, '8bit'), 2))), '0', STR_PAD_LEFT);
|
||||
$result = hex2bin($data);
|
||||
if ($result === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the data');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
165
libraries/vendor/spomky-labs/cbor-php/src/ListObject.php
vendored
Normal file
165
libraries/vendor/spomky-labs/cbor-php/src/ListObject.php
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use function count;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @phpstan-implements ArrayAccess<int, CBORObject>
|
||||
* @phpstan-implements IteratorAggregate<int, CBORObject>
|
||||
* @see \CBOR\Test\ListObjectTest
|
||||
*/
|
||||
class ListObject extends AbstractCBORObject implements Countable, IteratorAggregate, Normalizable, ArrayAccess
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_LIST;
|
||||
|
||||
/**
|
||||
* @var CBORObject[]
|
||||
*/
|
||||
private array $data;
|
||||
|
||||
private ?string $length = null;
|
||||
|
||||
/**
|
||||
* @param CBORObject[] $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
[$additionalInformation, $length] = LengthCalculator::getLengthOfArray($data);
|
||||
array_map(static function ($item): void {
|
||||
if (! $item instanceof CBORObject) {
|
||||
throw new InvalidArgumentException('The list must contain only CBORObject objects.');
|
||||
}
|
||||
}, $data);
|
||||
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
$this->data = array_values($data);
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->length !== null) {
|
||||
$result .= $this->length;
|
||||
}
|
||||
foreach ($this->data as $object) {
|
||||
$result .= (string) $object;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CBORObject[] $data
|
||||
*/
|
||||
public static function create(array $data = []): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function add(CBORObject $object): self
|
||||
{
|
||||
$this->data[] = $object;
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(int $index): bool
|
||||
{
|
||||
return array_key_exists($index, $this->data);
|
||||
}
|
||||
|
||||
public function remove(int $index): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
return $this;
|
||||
}
|
||||
unset($this->data[$index]);
|
||||
$this->data = array_values($this->data);
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(int $index): CBORObject
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
return $this->data[$index];
|
||||
}
|
||||
|
||||
public function set(int $index, CBORObject $object): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
$this->data[$index] = $object;
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function normalize(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (CBORObject $object) => $object instanceof Normalizable ? $object->normalize() : $object,
|
||||
$this->data
|
||||
);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int, CBORObject>
|
||||
*/
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset): CBORObject
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
if ($offset === null) {
|
||||
$this->add($value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
}
|
||||
29
libraries/vendor/spomky-labs/cbor-php/src/MapItem.php
vendored
Normal file
29
libraries/vendor/spomky-labs/cbor-php/src/MapItem.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
class MapItem
|
||||
{
|
||||
public function __construct(
|
||||
private CBORObject $key,
|
||||
private CBORObject $value
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(CBORObject $key, CBORObject $value): self
|
||||
{
|
||||
return new self($key, $value);
|
||||
}
|
||||
|
||||
public function getKey(): CBORObject
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getValue(): CBORObject
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
180
libraries/vendor/spomky-labs/cbor-php/src/MapObject.php
vendored
Normal file
180
libraries/vendor/spomky-labs/cbor-php/src/MapObject.php
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use function array_key_exists;
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use function count;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* @phpstan-implements ArrayAccess<int, CBORObject>
|
||||
* @phpstan-implements IteratorAggregate<int, MapItem>
|
||||
*/
|
||||
final class MapObject extends AbstractCBORObject implements Countable, IteratorAggregate, Normalizable, ArrayAccess
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_MAP;
|
||||
|
||||
/**
|
||||
* @var MapItem[]
|
||||
*/
|
||||
private array $data;
|
||||
|
||||
private ?string $length = null;
|
||||
|
||||
/**
|
||||
* @param MapItem[] $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
[$additionalInformation, $length] = LengthCalculator::getLengthOfArray($data);
|
||||
array_map(static function ($item): void {
|
||||
if (! $item instanceof MapItem) {
|
||||
throw new InvalidArgumentException('The list must contain only MapItem objects.');
|
||||
}
|
||||
}, $data);
|
||||
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
$this->data = $data;
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->length !== null) {
|
||||
$result .= $this->length;
|
||||
}
|
||||
foreach ($this->data as $object) {
|
||||
$result .= $object->getKey()
|
||||
->__toString()
|
||||
;
|
||||
$result .= $object->getValue()
|
||||
->__toString()
|
||||
;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MapItem[] $data
|
||||
*/
|
||||
public static function create(array $data = []): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function add(CBORObject $key, CBORObject $value): self
|
||||
{
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
$this->data[$key->normalize()] = MapItem::create($key, $value);
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function has(int|string $key): bool
|
||||
{
|
||||
return array_key_exists($key, $this->data);
|
||||
}
|
||||
|
||||
public function remove(int|string $index): self
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
return $this;
|
||||
}
|
||||
unset($this->data[$index]);
|
||||
$this->data = array_values($this->data);
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get(int|string $index): CBORObject
|
||||
{
|
||||
if (! $this->has($index)) {
|
||||
throw new InvalidArgumentException('Index not found.');
|
||||
}
|
||||
|
||||
return $this->data[$index]->getValue();
|
||||
}
|
||||
|
||||
public function set(MapItem $object): self
|
||||
{
|
||||
$key = $object->getKey();
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
|
||||
$this->data[$key->normalize()] = $object;
|
||||
[$this->additionalInformation, $this->length] = LengthCalculator::getLengthOfArray($this->data);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int, MapItem>
|
||||
*/
|
||||
public function getIterator(): Iterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int|string, mixed>
|
||||
*/
|
||||
public function normalize(): array
|
||||
{
|
||||
return array_reduce($this->data, static function (array $carry, MapItem $item): array {
|
||||
$key = $item->getKey();
|
||||
if (! $key instanceof Normalizable) {
|
||||
throw new InvalidArgumentException('Invalid key. Shall be normalizable');
|
||||
}
|
||||
$valueObject = $item->getValue();
|
||||
$carry[$key->normalize()] = $valueObject instanceof Normalizable ? $valueObject->normalize() : $valueObject;
|
||||
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
public function offsetGet($offset): CBORObject
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
if (! $offset instanceof CBORObject) {
|
||||
throw new InvalidArgumentException('Invalid key');
|
||||
}
|
||||
if (! $value instanceof CBORObject) {
|
||||
throw new InvalidArgumentException('Invalid value');
|
||||
}
|
||||
|
||||
$this->set(MapItem::create($offset, $value));
|
||||
}
|
||||
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
$this->remove($offset);
|
||||
}
|
||||
}
|
||||
112
libraries/vendor/spomky-labs/cbor-php/src/NegativeIntegerObject.php
vendored
Normal file
112
libraries/vendor/spomky-labs/cbor-php/src/NegativeIntegerObject.php
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use InvalidArgumentException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
final class NegativeIntegerObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_NEGATIVE_INTEGER;
|
||||
|
||||
public function __construct(
|
||||
int $additionalInformation,
|
||||
private ?string $data
|
||||
) {
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->data !== null) {
|
||||
$result .= $this->data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function createObjectForValue(int $additionalInformation, ?string $data): self
|
||||
{
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public static function create(int $value): self
|
||||
{
|
||||
return self::createFromString((string) $value);
|
||||
}
|
||||
|
||||
public static function createFromString(string $value): self
|
||||
{
|
||||
$integer = BigInteger::of($value);
|
||||
|
||||
return self::createBigInteger($integer);
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
if ($this->data === null) {
|
||||
return (string) (-1 - $this->additionalInformation);
|
||||
}
|
||||
|
||||
$result = Utils::binToBigInteger($this->data);
|
||||
$minusOne = BigInteger::of(-1);
|
||||
|
||||
return $minusOne->minus($result)
|
||||
->toBase(10)
|
||||
;
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
private static function createBigInteger(BigInteger $integer): self
|
||||
{
|
||||
if ($integer->isGreaterThanOrEqualTo(BigInteger::zero())) {
|
||||
throw new InvalidArgumentException('The value must be a negative integer.');
|
||||
}
|
||||
|
||||
$minusOne = BigInteger::of(-1);
|
||||
$computed_value = $minusOne->minus($integer);
|
||||
|
||||
switch (true) {
|
||||
case $computed_value->isLessThan(BigInteger::of(24)):
|
||||
$ai = $computed_value->toInt();
|
||||
$data = null;
|
||||
break;
|
||||
case $computed_value->isLessThan(BigInteger::fromBase('FF', 16)):
|
||||
$ai = 24;
|
||||
$data = self::hex2bin(str_pad($computed_value->toBase(16), 2, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
case $computed_value->isLessThan(BigInteger::fromBase('FFFF', 16)):
|
||||
$ai = 25;
|
||||
$data = self::hex2bin(str_pad($computed_value->toBase(16), 4, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
case $computed_value->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
|
||||
$ai = 26;
|
||||
$data = self::hex2bin(str_pad($computed_value->toBase(16), 8, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(
|
||||
'Out of range. Please use NegativeBigIntegerTag tag with ByteStringObject object instead.'
|
||||
);
|
||||
}
|
||||
|
||||
return new self($ai, $data);
|
||||
}
|
||||
|
||||
private static function hex2bin(string $data): string
|
||||
{
|
||||
$result = hex2bin($data);
|
||||
if ($result === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the data');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
13
libraries/vendor/spomky-labs/cbor-php/src/Normalizable.php
vendored
Normal file
13
libraries/vendor/spomky-labs/cbor-php/src/Normalizable.php
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
interface Normalizable
|
||||
{
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function normalize();
|
||||
}
|
||||
34
libraries/vendor/spomky-labs/cbor-php/src/OtherObject.php
vendored
Normal file
34
libraries/vendor/spomky-labs/cbor-php/src/OtherObject.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use CBOR\OtherObject\OtherObjectInterface;
|
||||
|
||||
abstract class OtherObject extends AbstractCBORObject implements OtherObjectInterface
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_OTHER_TYPE;
|
||||
|
||||
public function __construct(
|
||||
int $additionalInformation,
|
||||
protected ?string $data
|
||||
) {
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->data !== null) {
|
||||
$result .= $this->data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
30
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/BreakObject.php
vendored
Normal file
30
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/BreakObject.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\OtherObject as Base;
|
||||
|
||||
final class BreakObject extends Base
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::OBJECT_BREAK, null);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_BREAK];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
77
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/DoublePrecisionFloatObject.php
vendored
Normal file
77
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/DoublePrecisionFloatObject.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
use CBOR\Utils;
|
||||
use const INF;
|
||||
use InvalidArgumentException;
|
||||
use const NAN;
|
||||
|
||||
final class DoublePrecisionFloatObject extends Base implements Normalizable
|
||||
{
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_DOUBLE_PRECISION_FLOAT];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public static function create(string $value): self
|
||||
{
|
||||
if (mb_strlen($value, '8bit') !== 8) {
|
||||
throw new InvalidArgumentException('The value is not a valid double precision floating point');
|
||||
}
|
||||
|
||||
return new self(self::OBJECT_DOUBLE_PRECISION_FLOAT, $value);
|
||||
}
|
||||
|
||||
public function normalize(): float|int
|
||||
{
|
||||
$exponent = $this->getExponent();
|
||||
$mantissa = $this->getMantissa();
|
||||
$sign = $this->getSign();
|
||||
|
||||
if ($exponent === 0) {
|
||||
$val = $mantissa * 2 ** (-(1022 + 52));
|
||||
} elseif ($exponent !== 0b11111111111) {
|
||||
$val = ($mantissa + (1 << 52)) * 2 ** ($exponent - (1023 + 52));
|
||||
} else {
|
||||
$val = $mantissa === 0 ? INF : NAN;
|
||||
}
|
||||
|
||||
return $sign * $val;
|
||||
}
|
||||
|
||||
public function getExponent(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->shiftedRight(52)->and(Utils::hexToBigInteger('7ff'))->toInt();
|
||||
}
|
||||
|
||||
public function getMantissa(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('fffffffffffff'))->toInt();
|
||||
}
|
||||
|
||||
public function getSign(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
$sign = Utils::binToBigInteger($data)->shiftedRight(63);
|
||||
|
||||
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/FalseObject.php
vendored
Normal file
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/FalseObject.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
|
||||
final class FalseObject extends Base implements Normalizable
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::OBJECT_FALSE, null);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_FALSE];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function normalize(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
26
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/GenericObject.php
vendored
Normal file
26
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/GenericObject.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\OtherObject as Base;
|
||||
use InvalidArgumentException;
|
||||
use function ord;
|
||||
|
||||
final class GenericObject extends Base
|
||||
{
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
if ($data !== null && ord($data) < 32) {
|
||||
throw new InvalidArgumentException('Invalid simple value. Content data should not be present.');
|
||||
}
|
||||
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
}
|
||||
77
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/HalfPrecisionFloatObject.php
vendored
Normal file
77
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/HalfPrecisionFloatObject.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
use CBOR\Utils;
|
||||
use const INF;
|
||||
use InvalidArgumentException;
|
||||
use const NAN;
|
||||
|
||||
final class HalfPrecisionFloatObject extends Base implements Normalizable
|
||||
{
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_HALF_PRECISION_FLOAT];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public static function create(string $value): self
|
||||
{
|
||||
if (mb_strlen($value, '8bit') !== 2) {
|
||||
throw new InvalidArgumentException('The value is not a valid half precision floating point');
|
||||
}
|
||||
|
||||
return new self(self::OBJECT_HALF_PRECISION_FLOAT, $value);
|
||||
}
|
||||
|
||||
public function normalize(): float|int
|
||||
{
|
||||
$exponent = $this->getExponent();
|
||||
$mantissa = $this->getMantissa();
|
||||
$sign = $this->getSign();
|
||||
|
||||
if ($exponent === 0) {
|
||||
$val = $mantissa * 2 ** (-24);
|
||||
} elseif ($exponent !== 0b11111) {
|
||||
$val = ($mantissa + (1 << 10)) * 2 ** ($exponent - 25);
|
||||
} else {
|
||||
$val = $mantissa === 0 ? INF : NAN;
|
||||
}
|
||||
|
||||
return $sign * $val;
|
||||
}
|
||||
|
||||
public function getExponent(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->shiftedRight(10)->and(Utils::hexToBigInteger('1f'))->toInt();
|
||||
}
|
||||
|
||||
public function getMantissa(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('3ff'))->toInt();
|
||||
}
|
||||
|
||||
public function getSign(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
$sign = Utils::binToBigInteger($data)->shiftedRight(15);
|
||||
|
||||
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/NullObject.php
vendored
Normal file
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/NullObject.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
|
||||
final class NullObject extends Base implements Normalizable
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::OBJECT_NULL, null);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_NULL];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function normalize(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
17
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectInterface.php
vendored
Normal file
17
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectInterface.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
|
||||
interface OtherObjectInterface extends CBORObject
|
||||
{
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public static function supportedAdditionalInformation(): array;
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): self;
|
||||
}
|
||||
47
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectManager.php
vendored
Normal file
47
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectManager.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use function array_key_exists;
|
||||
use CBOR\OtherObject;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class OtherObjectManager implements OtherObjectManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $classes = [];
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function add(string $class): self
|
||||
{
|
||||
foreach ($class::supportedAdditionalInformation() as $ai) {
|
||||
if ($ai < 0) {
|
||||
throw new InvalidArgumentException('Invalid additional information.');
|
||||
}
|
||||
$this->classes[$ai] = $class;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClassForValue(int $value): string
|
||||
{
|
||||
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericObject::class;
|
||||
}
|
||||
|
||||
public function createObjectForValue(int $value, ?string $data): OtherObjectInterface
|
||||
{
|
||||
/** @var OtherObject $class */
|
||||
$class = $this->getClassForValue($value);
|
||||
|
||||
return $class::createFromLoadedData($value, $data);
|
||||
}
|
||||
}
|
||||
10
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectManagerInterface.php
vendored
Normal file
10
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/OtherObjectManagerInterface.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
interface OtherObjectManagerInterface
|
||||
{
|
||||
public function createObjectForValue(int $value, ?string $data): OtherObjectInterface;
|
||||
}
|
||||
72
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/SimpleObject.php
vendored
Normal file
72
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/SimpleObject.php
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
use CBOR\Utils;
|
||||
use function chr;
|
||||
use InvalidArgumentException;
|
||||
use function ord;
|
||||
|
||||
final class SimpleObject extends Base implements Normalizable
|
||||
{
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return array_merge(range(0, 19), [24]);
|
||||
}
|
||||
|
||||
public static function create(int $value): self|FalseObject|TrueObject|NullObject|UndefinedObject
|
||||
{
|
||||
switch (true) {
|
||||
case $value >= 0 && $value <= 19:
|
||||
return new self($value, null);
|
||||
case $value === 20:
|
||||
return FalseObject::create();
|
||||
case $value === 21:
|
||||
return TrueObject::create();
|
||||
case $value === 22:
|
||||
return NullObject::create();
|
||||
case $value === 23:
|
||||
return UndefinedObject::create();
|
||||
case $value <= 31:
|
||||
throw new InvalidArgumentException('Invalid simple value. Shall be between 32 and 255.');
|
||||
case $value <= 255:
|
||||
return new self(24, chr($value));
|
||||
default:
|
||||
throw new InvalidArgumentException('The value is not a valid simple value.');
|
||||
}
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
if ($additionalInformation === 24) {
|
||||
if ($data === null) {
|
||||
throw new InvalidArgumentException('Invalid simple value. Content data is missing.');
|
||||
}
|
||||
if (mb_strlen($data, '8bit') !== 1) {
|
||||
throw new InvalidArgumentException('Invalid simple value. Content data is too long.');
|
||||
}
|
||||
if (ord($data) < 32) {
|
||||
throw new InvalidArgumentException('Invalid simple value. Content data must be between 32 and 255.');
|
||||
}
|
||||
} elseif ($additionalInformation < 20) {
|
||||
if ($data !== null) {
|
||||
throw new InvalidArgumentException('Invalid simple value. Content data should not be present.');
|
||||
}
|
||||
}
|
||||
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public function normalize(): int
|
||||
{
|
||||
if ($this->data === null) {
|
||||
return $this->getAdditionalInformation();
|
||||
}
|
||||
|
||||
return Utils::binToInt($this->data);
|
||||
}
|
||||
}
|
||||
76
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/SinglePrecisionFloatObject.php
vendored
Normal file
76
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/SinglePrecisionFloatObject.php
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use CBOR\OtherObject as Base;
|
||||
use CBOR\Utils;
|
||||
use const INF;
|
||||
use InvalidArgumentException;
|
||||
use const NAN;
|
||||
|
||||
final class SinglePrecisionFloatObject extends Base
|
||||
{
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_SINGLE_PRECISION_FLOAT];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public static function create(string $value): self
|
||||
{
|
||||
if (mb_strlen($value, '8bit') !== 4) {
|
||||
throw new InvalidArgumentException('The value is not a valid single precision floating point');
|
||||
}
|
||||
|
||||
return new self(self::OBJECT_SINGLE_PRECISION_FLOAT, $value);
|
||||
}
|
||||
|
||||
public function normalize(): float|int
|
||||
{
|
||||
$exponent = $this->getExponent();
|
||||
$mantissa = $this->getMantissa();
|
||||
$sign = $this->getSign();
|
||||
|
||||
if ($exponent === 0) {
|
||||
$val = $mantissa * 2 ** (-(126 + 23));
|
||||
} elseif ($exponent !== 0b11111111) {
|
||||
$val = ($mantissa + (1 << 23)) * 2 ** ($exponent - (127 + 23));
|
||||
} else {
|
||||
$val = $mantissa === 0 ? INF : NAN;
|
||||
}
|
||||
|
||||
return $sign * $val;
|
||||
}
|
||||
|
||||
public function getExponent(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->shiftedRight(23)->and(Utils::hexToBigInteger('ff'))->toInt();
|
||||
}
|
||||
|
||||
public function getMantissa(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
|
||||
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('7fffff'))->toInt();
|
||||
}
|
||||
|
||||
public function getSign(): int
|
||||
{
|
||||
$data = $this->data;
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
$sign = Utils::binToBigInteger($data)->shiftedRight(31);
|
||||
|
||||
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/TrueObject.php
vendored
Normal file
36
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/TrueObject.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject as Base;
|
||||
|
||||
final class TrueObject extends Base implements Normalizable
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::OBJECT_TRUE, null);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_TRUE];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function normalize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
30
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/UndefinedObject.php
vendored
Normal file
30
libraries/vendor/spomky-labs/cbor-php/src/OtherObject/UndefinedObject.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\OtherObject;
|
||||
|
||||
use CBOR\OtherObject as Base;
|
||||
|
||||
final class UndefinedObject extends Base
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(self::OBJECT_UNDEFINED, null);
|
||||
}
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public static function supportedAdditionalInformation(): array
|
||||
{
|
||||
return [self::OBJECT_UNDEFINED];
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
}
|
||||
10
libraries/vendor/spomky-labs/cbor-php/src/Stream.php
vendored
Normal file
10
libraries/vendor/spomky-labs/cbor-php/src/Stream.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
interface Stream
|
||||
{
|
||||
public function read(int $length): string;
|
||||
}
|
||||
76
libraries/vendor/spomky-labs/cbor-php/src/StringStream.php
vendored
Normal file
76
libraries/vendor/spomky-labs/cbor-php/src/StringStream.php
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
final class StringStream implements Stream
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $resource;
|
||||
|
||||
public function __construct(string $data)
|
||||
{
|
||||
$resource = fopen('php://memory', 'rb+');
|
||||
if ($resource === false) {
|
||||
throw new RuntimeException('Unable to open the memory');
|
||||
}
|
||||
$result = fwrite($resource, $data);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Unable to write the memory');
|
||||
}
|
||||
$result = rewind($resource);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Unable to rewind the memory');
|
||||
}
|
||||
$this->resource = $resource;
|
||||
}
|
||||
|
||||
public static function create(string $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function read(int $length): string
|
||||
{
|
||||
if ($length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$alreadyRead = 0;
|
||||
$data = '';
|
||||
while ($alreadyRead < $length) {
|
||||
$left = $length - $alreadyRead;
|
||||
$sizeToRead = $left < 1024 && $left > 0 ? $left : 1024;
|
||||
$newData = fread($this->resource, $sizeToRead);
|
||||
$alreadyRead += $sizeToRead;
|
||||
|
||||
if ($newData === false) {
|
||||
throw new RuntimeException('Unable to read the memory');
|
||||
}
|
||||
if (mb_strlen($newData, '8bit') < $sizeToRead) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Out of range. Expected: %d, read: %d.',
|
||||
$length,
|
||||
mb_strlen($data, '8bit')
|
||||
));
|
||||
}
|
||||
$data .= $newData;
|
||||
}
|
||||
|
||||
if (mb_strlen($data, '8bit') !== $length) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Out of range. Expected: %d, read: %d.',
|
||||
$length,
|
||||
mb_strlen($data, '8bit')
|
||||
));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
74
libraries/vendor/spomky-labs/cbor-php/src/Tag.php
vendored
Normal file
74
libraries/vendor/spomky-labs/cbor-php/src/Tag.php
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use CBOR\Tag\TagInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
abstract class Tag extends AbstractCBORObject implements TagInterface
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_TAG;
|
||||
|
||||
public function __construct(
|
||||
int $additionalInformation,
|
||||
protected ?string $data,
|
||||
protected CBORObject $object
|
||||
) {
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->data !== null) {
|
||||
$result .= $this->data;
|
||||
}
|
||||
|
||||
return $result . $this->object;
|
||||
}
|
||||
|
||||
public function getData(): ?string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getValue(): CBORObject
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{int, null|string}
|
||||
*/
|
||||
protected static function determineComponents(int $tag): array
|
||||
{
|
||||
switch (true) {
|
||||
case $tag < 0:
|
||||
throw new InvalidArgumentException('The value must be a positive integer.');
|
||||
case $tag < 24:
|
||||
return [$tag, null];
|
||||
case $tag < 0xFF:
|
||||
return [24, self::hex2bin(dechex($tag))];
|
||||
case $tag < 0xFFFF:
|
||||
return [25, self::hex2bin(dechex($tag))];
|
||||
case $tag < 0xFFFFFFFF:
|
||||
return [26, self::hex2bin(dechex($tag))];
|
||||
default:
|
||||
throw new InvalidArgumentException(
|
||||
'Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static function hex2bin(string $data): string
|
||||
{
|
||||
$result = hex2bin($data);
|
||||
if ($result === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the data');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base16EncodingTag.php
vendored
Normal file
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base16EncodingTag.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Tag;
|
||||
|
||||
final class Base16EncodingTag extends Tag
|
||||
{
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_ENCODED_BASE16;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_ENCODED_BASE16);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64EncodingTag.php
vendored
Normal file
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64EncodingTag.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Tag;
|
||||
|
||||
final class Base64EncodingTag extends Tag
|
||||
{
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_ENCODED_BASE64;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_ENCODED_BASE64);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64Tag.php
vendored
Normal file
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64Tag.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthTextStringObject;
|
||||
use CBOR\Tag;
|
||||
use CBOR\TextStringObject;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class Base64Tag extends Tag
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof TextStringObject && ! $object instanceof IndefiniteLengthTextStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Text String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_BASE64;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_BASE64);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64UrlEncodingTag.php
vendored
Normal file
28
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64UrlEncodingTag.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Tag;
|
||||
|
||||
final class Base64UrlEncodingTag extends Tag
|
||||
{
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_ENCODED_BASE64_URL;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_ENCODED_BASE64_URL);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64UrlTag.php
vendored
Normal file
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/Base64UrlTag.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthTextStringObject;
|
||||
use CBOR\Tag;
|
||||
use CBOR\TextStringObject;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class Base64UrlTag extends Tag
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof TextStringObject && ! $object instanceof IndefiniteLengthTextStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Text String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_BASE64_URL;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_BASE64_URL);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
83
libraries/vendor/spomky-labs/cbor-php/src/Tag/BigFloatTag.php
vendored
Normal file
83
libraries/vendor/spomky-labs/cbor-php/src/Tag/BigFloatTag.php
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\ListObject;
|
||||
use CBOR\NegativeIntegerObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\UnsignedIntegerObject;
|
||||
use function count;
|
||||
use function extension_loaded;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
final class BigFloatTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! extension_loaded('bcmath')) {
|
||||
throw new RuntimeException('The extension "bcmath" is required to use this tag');
|
||||
}
|
||||
|
||||
if (! $object instanceof ListObject || count($object) !== 2) {
|
||||
throw new InvalidArgumentException(
|
||||
'This tag only accepts a ListObject object that contains an exponent and a mantissa.'
|
||||
);
|
||||
}
|
||||
$e = $object->get(0);
|
||||
if (! $e instanceof UnsignedIntegerObject && ! $e instanceof NegativeIntegerObject) {
|
||||
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
|
||||
}
|
||||
$m = $object->get(1);
|
||||
if (! $m instanceof UnsignedIntegerObject && ! $m instanceof NegativeIntegerObject && ! $m instanceof NegativeBigIntegerTag && ! $m instanceof UnsignedBigIntegerTag) {
|
||||
throw new InvalidArgumentException(
|
||||
'The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.'
|
||||
);
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_BIG_FLOAT;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_BIG_FLOAT);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Tag
|
||||
{
|
||||
$object = ListObject::create()
|
||||
->add($e)
|
||||
->add($m)
|
||||
;
|
||||
|
||||
return self::create($object);
|
||||
}
|
||||
|
||||
public function normalize()
|
||||
{
|
||||
/** @var ListObject $object */
|
||||
$object = $this->object;
|
||||
/** @var UnsignedIntegerObject|NegativeIntegerObject $e */
|
||||
$e = $object->get(0);
|
||||
/** @var UnsignedIntegerObject|NegativeIntegerObject|NegativeBigIntegerTag|UnsignedBigIntegerTag $m */
|
||||
$m = $object->get(1);
|
||||
|
||||
return rtrim(bcmul($m->normalize(), bcpow('2', $e->normalize(), 100), 100), '0');
|
||||
}
|
||||
}
|
||||
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/CBOREncodingTag.php
vendored
Normal file
40
libraries/vendor/spomky-labs/cbor-php/src/Tag/CBOREncodingTag.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\ByteStringObject;
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthByteStringObject;
|
||||
use CBOR\Tag;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class CBOREncodingTag extends Tag
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof ByteStringObject && ! $object instanceof IndefiniteLengthByteStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_ENCODED_CBOR;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_ENCODED_CBOR);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
}
|
||||
37
libraries/vendor/spomky-labs/cbor-php/src/Tag/CBORTag.php
vendored
Normal file
37
libraries/vendor/spomky-labs/cbor-php/src/Tag/CBORTag.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
|
||||
final class CBORTag extends Tag implements Normalizable
|
||||
{
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_CBOR;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_CBOR);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|CBORObject|null
|
||||
*/
|
||||
public function normalize()
|
||||
{
|
||||
return $this->object instanceof Normalizable ? $this->object->normalize() : $this->object;
|
||||
}
|
||||
}
|
||||
63
libraries/vendor/spomky-labs/cbor-php/src/Tag/DatetimeTag.php
vendored
Normal file
63
libraries/vendor/spomky-labs/cbor-php/src/Tag/DatetimeTag.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthTextStringObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\TextStringObject;
|
||||
use const DATE_RFC3339;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\Tag\DatetimeTagTest
|
||||
*/
|
||||
final class DatetimeTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof TextStringObject && ! $object instanceof IndefiniteLengthTextStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
|
||||
}
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_STANDARD_DATETIME;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_STANDARD_DATETIME);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): DateTimeInterface
|
||||
{
|
||||
/** @var TextStringObject|IndefiniteLengthTextStringObject $object */
|
||||
$object = $this->object;
|
||||
$result = DateTimeImmutable::createFromFormat(DATE_RFC3339, $object->normalize());
|
||||
if ($result !== false) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$formatted = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $object->normalize());
|
||||
if ($formatted === false) {
|
||||
throw new InvalidArgumentException('Invalid data. Cannot be converted into a datetime object');
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
}
|
||||
82
libraries/vendor/spomky-labs/cbor-php/src/Tag/DecimalFractionTag.php
vendored
Normal file
82
libraries/vendor/spomky-labs/cbor-php/src/Tag/DecimalFractionTag.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\ListObject;
|
||||
use CBOR\NegativeIntegerObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\UnsignedIntegerObject;
|
||||
use function count;
|
||||
use function extension_loaded;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
final class DecimalFractionTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! extension_loaded('bcmath')) {
|
||||
throw new RuntimeException('The extension "bcmath" is required to use this tag');
|
||||
}
|
||||
if (! $object instanceof ListObject || count($object) !== 2) {
|
||||
throw new InvalidArgumentException(
|
||||
'This tag only accepts a ListObject object that contains an exponent and a mantissa.'
|
||||
);
|
||||
}
|
||||
$e = $object->get(0);
|
||||
if (! $e instanceof UnsignedIntegerObject && ! $e instanceof NegativeIntegerObject) {
|
||||
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
|
||||
}
|
||||
$m = $object->get(1);
|
||||
if (! $m instanceof UnsignedIntegerObject && ! $m instanceof NegativeIntegerObject && ! $m instanceof NegativeBigIntegerTag && ! $m instanceof UnsignedBigIntegerTag) {
|
||||
throw new InvalidArgumentException(
|
||||
'The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.'
|
||||
);
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): self
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_DECIMAL_FRACTION);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_DECIMAL_FRACTION;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Tag
|
||||
{
|
||||
$object = ListObject::create()
|
||||
->add($e)
|
||||
->add($m)
|
||||
;
|
||||
|
||||
return self::create($object);
|
||||
}
|
||||
|
||||
public function normalize()
|
||||
{
|
||||
/** @var ListObject $object */
|
||||
$object = $this->object;
|
||||
/** @var UnsignedIntegerObject|NegativeIntegerObject $e */
|
||||
$e = $object->get(0);
|
||||
/** @var UnsignedIntegerObject|NegativeIntegerObject|NegativeBigIntegerTag|UnsignedBigIntegerTag $m */
|
||||
$m = $object->get(1);
|
||||
|
||||
return rtrim(bcmul($m->normalize(), bcpow('10', $e->normalize(), 100), 100), '0');
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/cbor-php/src/Tag/GenericTag.php
vendored
Normal file
21
libraries/vendor/spomky-labs/cbor-php/src/Tag/GenericTag.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Tag;
|
||||
|
||||
final class GenericTag extends Tag
|
||||
{
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
}
|
||||
52
libraries/vendor/spomky-labs/cbor-php/src/Tag/MimeTag.php
vendored
Normal file
52
libraries/vendor/spomky-labs/cbor-php/src/Tag/MimeTag.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthTextStringObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\TextStringObject;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\Tag\MimeTagTest
|
||||
*/
|
||||
final class MimeTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof TextStringObject && ! $object instanceof IndefiniteLengthTextStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_MIME;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_MIME);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
/** @var TextStringObject|IndefiniteLengthTextStringObject $object */
|
||||
$object = $this->object;
|
||||
|
||||
return $object->normalize();
|
||||
}
|
||||
}
|
||||
54
libraries/vendor/spomky-labs/cbor-php/src/Tag/NegativeBigIntegerTag.php
vendored
Normal file
54
libraries/vendor/spomky-labs/cbor-php/src/Tag/NegativeBigIntegerTag.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use CBOR\ByteStringObject;
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthByteStringObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class NegativeBigIntegerTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof ByteStringObject && ! $object instanceof IndefiniteLengthByteStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_NEGATIVE_BIG_NUM;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_NEGATIVE_BIG_NUM);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
/** @var ByteStringObject|IndefiniteLengthByteStringObject $object */
|
||||
$object = $this->object;
|
||||
$integer = BigInteger::fromBase(bin2hex($object->getValue()), 16);
|
||||
$minusOne = BigInteger::of(-1);
|
||||
|
||||
return $minusOne->minus($integer)
|
||||
->toBase(10)
|
||||
;
|
||||
}
|
||||
}
|
||||
20
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagInterface.php
vendored
Normal file
20
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagInterface.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
|
||||
interface TagInterface extends CBORObject
|
||||
{
|
||||
public static function getTagId(): int;
|
||||
|
||||
public function getValue(): CBORObject;
|
||||
|
||||
public static function createFromLoadedData(
|
||||
int $additionalInformation,
|
||||
?string $data,
|
||||
CBORObject $object
|
||||
): self;
|
||||
}
|
||||
52
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagManager.php
vendored
Normal file
52
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagManager.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use function array_key_exists;
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\Tag;
|
||||
use CBOR\Utils;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class TagManager implements TagManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $classes = [];
|
||||
|
||||
public static function create(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
public function add(string $class): self
|
||||
{
|
||||
if ($class::getTagId() < 0) {
|
||||
throw new InvalidArgumentException('Invalid tag ID.');
|
||||
}
|
||||
$this->classes[$class::getTagId()] = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClassForValue(int $value): string
|
||||
{
|
||||
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericTag::class;
|
||||
}
|
||||
|
||||
public function createObjectForValue(int $additionalInformation, ?string $data, CBORObject $object): TagInterface
|
||||
{
|
||||
$value = $additionalInformation;
|
||||
if ($additionalInformation >= 24) {
|
||||
Utils::assertString($data, 'Invalid data');
|
||||
$value = Utils::binToInt($data);
|
||||
}
|
||||
/** @var Tag $class */
|
||||
$class = $this->getClassForValue($value);
|
||||
|
||||
return $class::createFromLoadedData($additionalInformation, $data, $object);
|
||||
}
|
||||
}
|
||||
12
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagManagerInterface.php
vendored
Normal file
12
libraries/vendor/spomky-labs/cbor-php/src/Tag/TagManagerInterface.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
|
||||
interface TagManagerInterface
|
||||
{
|
||||
public function createObjectForValue(int $additionalInformation, ?string $data, CBORObject $object): TagInterface;
|
||||
}
|
||||
82
libraries/vendor/spomky-labs/cbor-php/src/Tag/TimestampTag.php
vendored
Normal file
82
libraries/vendor/spomky-labs/cbor-php/src/Tag/TimestampTag.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\NegativeIntegerObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\OtherObject\DoublePrecisionFloatObject;
|
||||
use CBOR\OtherObject\HalfPrecisionFloatObject;
|
||||
use CBOR\OtherObject\SinglePrecisionFloatObject;
|
||||
use CBOR\Tag;
|
||||
use CBOR\UnsignedIntegerObject;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
use const STR_PAD_RIGHT;
|
||||
|
||||
final class TimestampTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof UnsignedIntegerObject && ! $object instanceof NegativeIntegerObject && ! $object instanceof HalfPrecisionFloatObject && ! $object instanceof SinglePrecisionFloatObject && ! $object instanceof DoublePrecisionFloatObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts integer-based or float-based objects.');
|
||||
}
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_EPOCH_DATETIME;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_EPOCH_DATETIME);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): DateTimeInterface
|
||||
{
|
||||
$object = $this->object;
|
||||
|
||||
switch (true) {
|
||||
case $object instanceof UnsignedIntegerObject:
|
||||
case $object instanceof NegativeIntegerObject:
|
||||
$formatted = DateTimeImmutable::createFromFormat('U', $object->normalize());
|
||||
|
||||
break;
|
||||
case $object instanceof HalfPrecisionFloatObject:
|
||||
case $object instanceof SinglePrecisionFloatObject:
|
||||
case $object instanceof DoublePrecisionFloatObject:
|
||||
$value = (string) $object->normalize();
|
||||
$parts = explode('.', $value);
|
||||
if (isset($parts[1])) {
|
||||
if (mb_strlen($parts[1], '8bit') > 6) {
|
||||
$parts[1] = mb_substr($parts[1], 0, 6, '8bit');
|
||||
} else {
|
||||
$parts[1] = str_pad($parts[1], 6, '0', STR_PAD_RIGHT);
|
||||
}
|
||||
}
|
||||
$formatted = DateTimeImmutable::createFromFormat('U.u', implode('.', $parts));
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Unable to normalize the object');
|
||||
}
|
||||
|
||||
if ($formatted === false) {
|
||||
throw new InvalidArgumentException('Invalid data. Cannot be converted into a datetime object');
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
}
|
||||
}
|
||||
50
libraries/vendor/spomky-labs/cbor-php/src/Tag/UnsignedBigIntegerTag.php
vendored
Normal file
50
libraries/vendor/spomky-labs/cbor-php/src/Tag/UnsignedBigIntegerTag.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\ByteStringObject;
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthByteStringObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\Utils;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class UnsignedBigIntegerTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof ByteStringObject && ! $object instanceof IndefiniteLengthByteStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_UNSIGNED_BIG_NUM;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_UNSIGNED_BIG_NUM);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
/** @var ByteStringObject|IndefiniteLengthByteStringObject $object */
|
||||
$object = $this->object;
|
||||
|
||||
return Utils::hexToString($object->normalize());
|
||||
}
|
||||
}
|
||||
49
libraries/vendor/spomky-labs/cbor-php/src/Tag/UriTag.php
vendored
Normal file
49
libraries/vendor/spomky-labs/cbor-php/src/Tag/UriTag.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR\Tag;
|
||||
|
||||
use CBOR\CBORObject;
|
||||
use CBOR\IndefiniteLengthTextStringObject;
|
||||
use CBOR\Normalizable;
|
||||
use CBOR\Tag;
|
||||
use CBOR\TextStringObject;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class UriTag extends Tag implements Normalizable
|
||||
{
|
||||
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
|
||||
{
|
||||
if (! $object instanceof TextStringObject && ! $object instanceof IndefiniteLengthTextStringObject) {
|
||||
throw new InvalidArgumentException('This tag only accepts a Text String object.');
|
||||
}
|
||||
|
||||
parent::__construct($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function getTagId(): int
|
||||
{
|
||||
return self::TAG_URI;
|
||||
}
|
||||
|
||||
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Tag
|
||||
{
|
||||
return new self($additionalInformation, $data, $object);
|
||||
}
|
||||
|
||||
public static function create(CBORObject $object): Tag
|
||||
{
|
||||
[$ai, $data] = self::determineComponents(self::TAG_URI);
|
||||
|
||||
return new self($ai, $data, $object);
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
/** @var TextStringObject|IndefiniteLengthTextStringObject $object */
|
||||
$object = $this->object;
|
||||
|
||||
return $object->normalize();
|
||||
}
|
||||
}
|
||||
56
libraries/vendor/spomky-labs/cbor-php/src/TextStringObject.php
vendored
Normal file
56
libraries/vendor/spomky-labs/cbor-php/src/TextStringObject.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
/**
|
||||
* @see \CBOR\Test\TextStringObjectTest
|
||||
*/
|
||||
final class TextStringObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_TEXT_STRING;
|
||||
|
||||
private ?string $length = null;
|
||||
|
||||
private string $data;
|
||||
|
||||
public function __construct(string $data)
|
||||
{
|
||||
[$additionalInformation, $length] = LengthCalculator::getLengthOfString($data);
|
||||
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
$this->data = $data;
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->length !== null) {
|
||||
$result .= $this->length;
|
||||
}
|
||||
|
||||
return $result . $this->data;
|
||||
}
|
||||
|
||||
public static function create(string $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return mb_strlen($this->data, 'utf8');
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
118
libraries/vendor/spomky-labs/cbor-php/src/UnsignedIntegerObject.php
vendored
Normal file
118
libraries/vendor/spomky-labs/cbor-php/src/UnsignedIntegerObject.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use InvalidArgumentException;
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
final class UnsignedIntegerObject extends AbstractCBORObject implements Normalizable
|
||||
{
|
||||
private const MAJOR_TYPE = self::MAJOR_TYPE_UNSIGNED_INTEGER;
|
||||
|
||||
public function __construct(
|
||||
int $additionalInformation,
|
||||
private ?string $data
|
||||
) {
|
||||
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$result = parent::__toString();
|
||||
if ($this->data !== null) {
|
||||
$result .= $this->data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function createObjectForValue(int $additionalInformation, ?string $data): self
|
||||
{
|
||||
return new self($additionalInformation, $data);
|
||||
}
|
||||
|
||||
public static function create(int $value): self
|
||||
{
|
||||
return self::createFromString((string) $value);
|
||||
}
|
||||
|
||||
public static function createFromHex(string $value): self
|
||||
{
|
||||
$integer = BigInteger::fromBase($value, 16);
|
||||
|
||||
return self::createBigInteger($integer);
|
||||
}
|
||||
|
||||
public static function createFromString(string $value): self
|
||||
{
|
||||
$integer = BigInteger::of($value);
|
||||
|
||||
return self::createBigInteger($integer);
|
||||
}
|
||||
|
||||
public function getMajorType(): int
|
||||
{
|
||||
return self::MAJOR_TYPE;
|
||||
}
|
||||
|
||||
public function getValue(): string
|
||||
{
|
||||
if ($this->data === null) {
|
||||
return (string) $this->additionalInformation;
|
||||
}
|
||||
|
||||
$integer = BigInteger::fromBase(bin2hex($this->data), 16);
|
||||
|
||||
return $integer->toBase(10);
|
||||
}
|
||||
|
||||
public function normalize(): string
|
||||
{
|
||||
return $this->getValue();
|
||||
}
|
||||
|
||||
private static function createBigInteger(BigInteger $integer): self
|
||||
{
|
||||
if ($integer->isLessThan(BigInteger::zero())) {
|
||||
throw new InvalidArgumentException('The value must be a positive integer.');
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $integer->isLessThan(BigInteger::of(24)):
|
||||
$ai = $integer->toInt();
|
||||
$data = null;
|
||||
break;
|
||||
case $integer->isLessThan(BigInteger::fromBase('FF', 16)):
|
||||
$ai = 24;
|
||||
$data = self::hex2bin(str_pad($integer->toBase(16), 2, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
case $integer->isLessThan(BigInteger::fromBase('FFFF', 16)):
|
||||
$ai = 25;
|
||||
$data = self::hex2bin(str_pad($integer->toBase(16), 4, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
case $integer->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
|
||||
$ai = 26;
|
||||
$data = self::hex2bin(str_pad($integer->toBase(16), 8, '0', STR_PAD_LEFT));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(
|
||||
'Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.'
|
||||
);
|
||||
}
|
||||
|
||||
return new self($ai, $data);
|
||||
}
|
||||
|
||||
private static function hex2bin(string $data): string
|
||||
{
|
||||
$result = hex2bin($data);
|
||||
if ($result === false) {
|
||||
throw new InvalidArgumentException('Unable to convert the data');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
60
libraries/vendor/spomky-labs/cbor-php/src/Utils.php
vendored
Normal file
60
libraries/vendor/spomky-labs/cbor-php/src/Utils.php
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CBOR;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
use InvalidArgumentException;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class Utils
|
||||
{
|
||||
public static function binToInt(string $value): int
|
||||
{
|
||||
return self::binToBigInteger($value)->toInt();
|
||||
}
|
||||
|
||||
public static function binToBigInteger(string $value): BigInteger
|
||||
{
|
||||
return self::hexToBigInteger(bin2hex($value));
|
||||
}
|
||||
|
||||
public static function hexToInt(string $value): int
|
||||
{
|
||||
return self::hexToBigInteger($value)->toInt();
|
||||
}
|
||||
|
||||
public static function hexToBigInteger(string $value): BigInteger
|
||||
{
|
||||
return BigInteger::fromBase($value, 16);
|
||||
}
|
||||
|
||||
public static function hexToString(string $value): string
|
||||
{
|
||||
return BigInteger::fromBase(bin2hex($value), 16)->toBase(10);
|
||||
}
|
||||
|
||||
public static function decode(string $data): string
|
||||
{
|
||||
$decoded = base64_decode(strtr($data, '-_', '+/'), true);
|
||||
if ($decoded === false) {
|
||||
throw new InvalidArgumentException('Invalid data provided');
|
||||
}
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $data
|
||||
*/
|
||||
public static function assertString($data, ?string $message = null): void
|
||||
{
|
||||
if (! is_string($data)) {
|
||||
throw new InvalidArgumentException($message ?? '');
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user