primo commit
This commit is contained in:
180
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Attribute.php
vendored
Normal file
180
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Attribute.php
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Set;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Implements *Attribute* ASN.1 type.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.Attribute
|
||||
*/
|
||||
final class Attribute implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Attribute type.
|
||||
*/
|
||||
private readonly AttributeType $type;
|
||||
|
||||
/**
|
||||
* Attribute values.
|
||||
*
|
||||
* @var AttributeValue[]
|
||||
*/
|
||||
private readonly array $values;
|
||||
|
||||
/**
|
||||
* @param AttributeType $type Attribute type
|
||||
* @param AttributeValue ...$values Attribute values
|
||||
*/
|
||||
private function __construct(AttributeType $type, AttributeValue ...$values)
|
||||
{
|
||||
// check that attribute values have correct oid
|
||||
foreach ($values as $value) {
|
||||
if ($value->oid() !== $type->oid()) {
|
||||
throw new LogicException('Attribute OID mismatch.');
|
||||
}
|
||||
}
|
||||
$this->type = $type;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
public static function create(AttributeType $type, AttributeValue ...$values): self
|
||||
{
|
||||
return new self($type, ...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Sequence $seq): self
|
||||
{
|
||||
$type = AttributeType::fromASN1($seq->at(0)->asObjectIdentifier());
|
||||
$values = array_map(
|
||||
static fn (UnspecifiedType $el) => AttributeValue::fromASN1ByOID($type->oid(), $el),
|
||||
$seq->at(1)
|
||||
->asSet()
|
||||
->elements()
|
||||
);
|
||||
return self::create($type, ...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to initialize from attribute values.
|
||||
*
|
||||
* @param AttributeValue ...$values One or more values
|
||||
*/
|
||||
public static function fromAttributeValues(AttributeValue ...$values): self
|
||||
{
|
||||
// we need at least one value to determine OID
|
||||
if (count($values) === 0) {
|
||||
throw new LogicException('No values.');
|
||||
}
|
||||
$oid = reset($values)
|
||||
->oid();
|
||||
return self::create(AttributeType::create($oid), ...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first value of the attribute.
|
||||
*/
|
||||
public function first(): AttributeValue
|
||||
{
|
||||
if (count($this->values) === 0) {
|
||||
throw new LogicException('Attribute contains no values.');
|
||||
}
|
||||
return $this->values[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all values.
|
||||
*
|
||||
* @return AttributeValue[]
|
||||
*/
|
||||
public function values(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Sequence
|
||||
{
|
||||
$values = array_map(static fn (AttributeValue $value) => $value->toASN1(), $this->values);
|
||||
$valueset = Set::create(...$values);
|
||||
return Sequence::create($this->type->toASN1(), $valueset->sortedSetOf());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast attribute values to another AttributeValue class.
|
||||
*
|
||||
* This method is generally used to cast UnknownAttributeValue values to specific objects when class is declared
|
||||
* outside this package.
|
||||
*
|
||||
* The new class must be derived from AttributeValue and have the same OID as current attribute values.
|
||||
*
|
||||
* @param string $cls AttributeValue class name
|
||||
*/
|
||||
public function castValues(string $cls): self
|
||||
{
|
||||
// check that target class derives from AttributeValue
|
||||
if (! is_subclass_of($cls, AttributeValue::class)) {
|
||||
throw new LogicException(sprintf('%s must be derived from %s.', $cls, AttributeValue::class));
|
||||
}
|
||||
$values = array_map(
|
||||
function (AttributeValue $value) use ($cls) {
|
||||
/** @var AttributeValue $cls Class name as a string */
|
||||
$value = $cls::fromSelf($value);
|
||||
if ($value->oid() !== $this->oid()) {
|
||||
throw new LogicException('Attribute OID mismatch.');
|
||||
}
|
||||
return $value;
|
||||
},
|
||||
$this->values
|
||||
);
|
||||
return self::fromAttributeValues(...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute type.
|
||||
*/
|
||||
public function type(): AttributeType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID of the attribute.
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->type->oid();
|
||||
}
|
||||
}
|
||||
525
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeType.php
vendored
Normal file
525
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeType.php
vendored
Normal file
@ -0,0 +1,525 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1;
|
||||
|
||||
use OutOfBoundsException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\ObjectIdentifier;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\PrintableString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTF8String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\StringType;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Implements *AttributeType* ASN.1 type.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.AttributeType
|
||||
*/
|
||||
final class AttributeType
|
||||
{
|
||||
// OID's from 2.5.4 arc
|
||||
final public const OID_OBJECT_CLASS = '2.5.4.0';
|
||||
|
||||
final public const OID_ALIASED_ENTRY_NAME = '2.5.4.1';
|
||||
|
||||
final public const OID_KNOWLEDGE_INFORMATION = '2.5.4.2';
|
||||
|
||||
final public const OID_COMMON_NAME = '2.5.4.3';
|
||||
|
||||
final public const OID_SURNAME = '2.5.4.4';
|
||||
|
||||
final public const OID_SERIAL_NUMBER = '2.5.4.5';
|
||||
|
||||
final public const OID_COUNTRY_NAME = '2.5.4.6';
|
||||
|
||||
final public const OID_LOCALITY_NAME = '2.5.4.7';
|
||||
|
||||
final public const OID_STATE_OR_PROVINCE_NAME = '2.5.4.8';
|
||||
|
||||
final public const OID_STREET_ADDRESS = '2.5.4.9';
|
||||
|
||||
final public const OID_ORGANIZATION_NAME = '2.5.4.10';
|
||||
|
||||
final public const OID_ORGANIZATIONAL_UNIT_NAME = '2.5.4.11';
|
||||
|
||||
final public const OID_TITLE = '2.5.4.12';
|
||||
|
||||
final public const OID_DESCRIPTION = '2.5.4.13';
|
||||
|
||||
final public const OID_SEARCH_GUIDE = '2.5.4.14';
|
||||
|
||||
final public const OID_BUSINESS_CATEGORY = '2.5.4.15';
|
||||
|
||||
final public const OID_POSTAL_ADDRESS = '2.5.4.16';
|
||||
|
||||
final public const OID_POSTAL_CODE = '2.5.4.17';
|
||||
|
||||
final public const OID_POST_OFFICE_BOX = '2.5.4.18';
|
||||
|
||||
final public const OID_PHYSICAL_DELIVERY_OFFICE_NAME = '2.5.4.19';
|
||||
|
||||
final public const OID_TELEPHONE_NUMBER = '2.5.4.20';
|
||||
|
||||
final public const OID_TELEX_NUMBER = '2.5.4.21';
|
||||
|
||||
final public const OID_TELETEX_TERMINAL_IDENTIFIER = '2.5.4.22';
|
||||
|
||||
final public const OID_FACSIMILE_TELEPHONE_NUMBER = '2.5.4.23';
|
||||
|
||||
final public const OID_X121_ADDRESS = '2.5.4.24';
|
||||
|
||||
final public const OID_INTERNATIONAL_ISDN_NUMBER = '2.5.4.25';
|
||||
|
||||
final public const OID_REGISTERED_ADDRESS = '2.5.4.26';
|
||||
|
||||
final public const OID_DESTINATION_INDICATOR = '2.5.4.27';
|
||||
|
||||
final public const OID_PREFERRED_DELIVERY_METHOD = '2.5.4.28';
|
||||
|
||||
final public const OID_PRESENTATION_ADDRESS = '2.5.4.29';
|
||||
|
||||
final public const OID_SUPPORTED_APPLICATION_CONTEXT = '2.5.4.30';
|
||||
|
||||
final public const OID_MEMBER = '2.5.4.31';
|
||||
|
||||
final public const OID_OWNER = '2.5.4.32';
|
||||
|
||||
final public const OID_ROLE_OCCUPANT = '2.5.4.33';
|
||||
|
||||
final public const OID_SEE_ALSO = '2.5.4.34';
|
||||
|
||||
final public const OID_USER_PASSWORD = '2.5.4.35';
|
||||
|
||||
final public const OID_USER_CERTIFICATE = '2.5.4.36';
|
||||
|
||||
final public const OID_CA_CERTIFICATE = '2.5.4.37';
|
||||
|
||||
final public const OID_AUTHORITY_REVOCATION_LIST = '2.5.4.38';
|
||||
|
||||
final public const OID_CERTIFICATE_REVOCATION_LIST = '2.5.4.39';
|
||||
|
||||
final public const OID_CROSS_CERTIFICATE_PAIR = '2.5.4.40';
|
||||
|
||||
final public const OID_NAME = '2.5.4.41';
|
||||
|
||||
final public const OID_GIVEN_NAME = '2.5.4.42';
|
||||
|
||||
final public const OID_INITIALS = '2.5.4.43';
|
||||
|
||||
final public const OID_GENERATION_QUALIFIER = '2.5.4.44';
|
||||
|
||||
final public const OID_UNIQUE_IDENTIFIER = '2.5.4.45';
|
||||
|
||||
final public const OID_DN_QUALIFIER = '2.5.4.46';
|
||||
|
||||
final public const OID_ENHANCED_SEARCH_GUIDE = '2.5.4.47';
|
||||
|
||||
final public const OID_PROTOCOL_INFORMATION = '2.5.4.48';
|
||||
|
||||
final public const OID_DISTINGUISHED_NAME = '2.5.4.49';
|
||||
|
||||
final public const OID_UNIQUE_MEMBER = '2.5.4.50';
|
||||
|
||||
final public const OID_HOUSE_IDENTIFIER = '2.5.4.51';
|
||||
|
||||
final public const OID_SUPPORTED_ALGORITHMS = '2.5.4.52';
|
||||
|
||||
final public const OID_DELTA_REVOCATION_LIST = '2.5.4.53';
|
||||
|
||||
final public const OID_DMD_NAME = '2.5.4.54';
|
||||
|
||||
final public const OID_CLEARANCE = '2.5.4.55';
|
||||
|
||||
final public const OID_DEFAULT_DIR_QOP = '2.5.4.56';
|
||||
|
||||
final public const OID_ATTRIBUTE_INTEGRITY_INFO = '2.5.4.57';
|
||||
|
||||
final public const OID_ATTRIBUTE_CERTIFICATE = '2.5.4.58';
|
||||
|
||||
final public const OID_ATTRIBUTE_CERTIFICATE_REVOCATION_LIST = '2.5.4.59';
|
||||
|
||||
final public const OID_CONF_KEY_INFO = '2.5.4.60';
|
||||
|
||||
final public const OID_AA_CERTIFICATE = '2.5.4.61';
|
||||
|
||||
final public const OID_ATTRIBUTE_DESCRIPTOR_CERTIFICATE = '2.5.4.62';
|
||||
|
||||
final public const OID_ATTRIBUTE_AUTHORITY_REVOCATION_LIST = '2.5.4.63';
|
||||
|
||||
final public const OID_FAMILY_INFORMATION = '2.5.4.64';
|
||||
|
||||
final public const OID_PSEUDONYM = '2.5.4.65';
|
||||
|
||||
final public const OID_COMMUNICATIONS_SERVICE = '2.5.4.66';
|
||||
|
||||
final public const OID_COMMUNICATIONS_NETWORK = '2.5.4.67';
|
||||
|
||||
final public const OID_CERTIFICATION_PRACTICE_STMT = '2.5.4.68';
|
||||
|
||||
final public const OID_CERTIFICATE_POLICY = '2.5.4.69';
|
||||
|
||||
final public const OID_PKI_PATH = '2.5.4.70';
|
||||
|
||||
final public const OID_PRIV_POLICY = '2.5.4.71';
|
||||
|
||||
final public const OID_ROLE = '2.5.4.72';
|
||||
|
||||
final public const OID_DELEGATION_PATH = '2.5.4.73';
|
||||
|
||||
final public const OID_PROT_PRIV_POLICY = '2.5.4.74';
|
||||
|
||||
final public const OID_XML_PRIVILEGE_INFO = '2.5.4.75';
|
||||
|
||||
final public const OID_XML_PRIV_POLICY = '2.5.4.76';
|
||||
|
||||
final public const OID_UUID_PAIR = '2.5.4.77';
|
||||
|
||||
final public const OID_TAG_OID = '2.5.4.78';
|
||||
|
||||
final public const OID_UII_FORMAT = '2.5.4.79';
|
||||
|
||||
final public const OID_UII_IN_URH = '2.5.4.80';
|
||||
|
||||
final public const OID_CONTENT_URL = '2.5.4.81';
|
||||
|
||||
final public const OID_PERMISSION = '2.5.4.82';
|
||||
|
||||
final public const OID_URI = '2.5.4.83';
|
||||
|
||||
final public const OID_PWD_ATTRIBUTE = '2.5.4.84';
|
||||
|
||||
final public const OID_USER_PWD = '2.5.4.85';
|
||||
|
||||
final public const OID_URN = '2.5.4.86';
|
||||
|
||||
final public const OID_URL = '2.5.4.87';
|
||||
|
||||
final public const OID_UTM_COORDINATES = '2.5.4.88';
|
||||
|
||||
final public const OID_URNC = '2.5.4.89';
|
||||
|
||||
final public const OID_UII = '2.5.4.90';
|
||||
|
||||
final public const OID_EPC = '2.5.4.91';
|
||||
|
||||
final public const OID_TAG_AFI = '2.5.4.92';
|
||||
|
||||
final public const OID_EPC_FORMAT = '2.5.4.93';
|
||||
|
||||
final public const OID_EPC_IN_URN = '2.5.4.94';
|
||||
|
||||
final public const OID_LDAP_URL = '2.5.4.95';
|
||||
|
||||
final public const OID_TAG_LOCATION = '2.5.4.96';
|
||||
|
||||
final public const OID_ORGANIZATION_IDENTIFIER = '2.5.4.97';
|
||||
|
||||
// Miscellany attribute OID's
|
||||
final public const OID_CLEARANCE_X501 = '2.5.1.5.55';
|
||||
|
||||
/**
|
||||
* Default ASN.1 string types for attributes.
|
||||
*
|
||||
* Attributes not mapped here shall use UTF8String as a default type.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private const MAP_ATTR_TO_STR_TYPE = [
|
||||
self::OID_DN_QUALIFIER => Element::TYPE_PRINTABLE_STRING,
|
||||
self::OID_COUNTRY_NAME => Element::TYPE_PRINTABLE_STRING,
|
||||
self::OID_SERIAL_NUMBER => Element::TYPE_PRINTABLE_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* OID to attribute names mapping.
|
||||
*
|
||||
* First name is the primary name. If there's more than one name, others may be used as an alias.
|
||||
*
|
||||
* Generated using ldap-attribs.py.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, array<string>>
|
||||
*/
|
||||
private const MAP_OID_TO_NAME = [
|
||||
'0.9.2342.19200300.100.1.1' => ['uid', 'userid'],
|
||||
'0.9.2342.19200300.100.1.2' => ['textEncodedORAddress'],
|
||||
'0.9.2342.19200300.100.1.3' => ['mail', 'rfc822Mailbox'],
|
||||
'0.9.2342.19200300.100.1.4' => ['info'],
|
||||
'0.9.2342.19200300.100.1.5' => ['drink', 'favouriteDrink'],
|
||||
'0.9.2342.19200300.100.1.6' => ['roomNumber'],
|
||||
'0.9.2342.19200300.100.1.7' => ['photo'],
|
||||
'0.9.2342.19200300.100.1.8' => ['userClass'],
|
||||
'0.9.2342.19200300.100.1.9' => ['host'],
|
||||
'0.9.2342.19200300.100.1.10' => ['manager'],
|
||||
'0.9.2342.19200300.100.1.11' => ['documentIdentifier'],
|
||||
'0.9.2342.19200300.100.1.12' => ['documentTitle'],
|
||||
'0.9.2342.19200300.100.1.13' => ['documentVersion'],
|
||||
'0.9.2342.19200300.100.1.14' => ['documentAuthor'],
|
||||
'0.9.2342.19200300.100.1.15' => ['documentLocation'],
|
||||
'0.9.2342.19200300.100.1.20' => ['homePhone', 'homeTelephoneNumber'],
|
||||
'0.9.2342.19200300.100.1.21' => ['secretary'],
|
||||
'0.9.2342.19200300.100.1.22' => ['otherMailbox'],
|
||||
'0.9.2342.19200300.100.1.25' => ['dc', 'domainComponent'],
|
||||
'0.9.2342.19200300.100.1.26' => ['aRecord'],
|
||||
'0.9.2342.19200300.100.1.27' => ['mDRecord'],
|
||||
'0.9.2342.19200300.100.1.28' => ['mXRecord'],
|
||||
'0.9.2342.19200300.100.1.29' => ['nSRecord'],
|
||||
'0.9.2342.19200300.100.1.30' => ['sOARecord'],
|
||||
'0.9.2342.19200300.100.1.31' => ['cNAMERecord'],
|
||||
'0.9.2342.19200300.100.1.37' => ['associatedDomain'],
|
||||
'0.9.2342.19200300.100.1.38' => ['associatedName'],
|
||||
'0.9.2342.19200300.100.1.39' => ['homePostalAddress'],
|
||||
'0.9.2342.19200300.100.1.40' => ['personalTitle'],
|
||||
'0.9.2342.19200300.100.1.41' => ['mobile', 'mobileTelephoneNumber'],
|
||||
'0.9.2342.19200300.100.1.42' => ['pager', 'pagerTelephoneNumber'],
|
||||
'0.9.2342.19200300.100.1.43' => ['co', 'friendlyCountryName'],
|
||||
'0.9.2342.19200300.100.1.44' => ['uniqueIdentifier'],
|
||||
'0.9.2342.19200300.100.1.45' => ['organizationalStatus'],
|
||||
'0.9.2342.19200300.100.1.46' => ['janetMailbox'],
|
||||
'0.9.2342.19200300.100.1.47' => ['mailPreferenceOption'],
|
||||
'0.9.2342.19200300.100.1.48' => ['buildingName'],
|
||||
'0.9.2342.19200300.100.1.49' => ['dSAQuality'],
|
||||
'0.9.2342.19200300.100.1.50' => ['singleLevelQuality'],
|
||||
'0.9.2342.19200300.100.1.51' => ['subtreeMinimumQuality'],
|
||||
'0.9.2342.19200300.100.1.52' => ['subtreeMaximumQuality'],
|
||||
'0.9.2342.19200300.100.1.53' => ['personalSignature'],
|
||||
'0.9.2342.19200300.100.1.54' => ['dITRedirect'],
|
||||
'0.9.2342.19200300.100.1.55' => ['audio'],
|
||||
'0.9.2342.19200300.100.1.56' => ['documentPublisher'],
|
||||
'0.9.2342.19200300.100.1.60' => ['jpegPhoto'],
|
||||
'1.2.840.113549.1.9.1' => ['email', 'emailAddress', 'pkcs9email'],
|
||||
'1.2.840.113556.1.2.102' => ['memberOf'],
|
||||
'1.3.6.1.1.1.1.0' => ['uidNumber'],
|
||||
'1.3.6.1.1.1.1.1' => ['gidNumber'],
|
||||
'1.3.6.1.1.1.1.2' => ['gecos'],
|
||||
'1.3.6.1.1.1.1.3' => ['homeDirectory'],
|
||||
'1.3.6.1.1.1.1.4' => ['loginShell'],
|
||||
'1.3.6.1.1.1.1.5' => ['shadowLastChange'],
|
||||
'1.3.6.1.1.1.1.6' => ['shadowMin'],
|
||||
'1.3.6.1.1.1.1.7' => ['shadowMax'],
|
||||
'1.3.6.1.1.1.1.8' => ['shadowWarning'],
|
||||
'1.3.6.1.1.1.1.9' => ['shadowInactive'],
|
||||
'1.3.6.1.1.1.1.10' => ['shadowExpire'],
|
||||
'1.3.6.1.1.1.1.11' => ['shadowFlag'],
|
||||
'1.3.6.1.1.1.1.12' => ['memberUid'],
|
||||
'1.3.6.1.1.1.1.13' => ['memberNisNetgroup'],
|
||||
'1.3.6.1.1.1.1.14' => ['nisNetgroupTriple'],
|
||||
'1.3.6.1.1.1.1.15' => ['ipServicePort'],
|
||||
'1.3.6.1.1.1.1.16' => ['ipServiceProtocol'],
|
||||
'1.3.6.1.1.1.1.17' => ['ipProtocolNumber'],
|
||||
'1.3.6.1.1.1.1.18' => ['oncRpcNumber'],
|
||||
'1.3.6.1.1.1.1.19' => ['ipHostNumber'],
|
||||
'1.3.6.1.1.1.1.20' => ['ipNetworkNumber'],
|
||||
'1.3.6.1.1.1.1.21' => ['ipNetmaskNumber'],
|
||||
'1.3.6.1.1.1.1.22' => ['macAddress'],
|
||||
'1.3.6.1.1.1.1.23' => ['bootParameter'],
|
||||
'1.3.6.1.1.1.1.24' => ['bootFile'],
|
||||
'1.3.6.1.1.1.1.26' => ['nisMapName'],
|
||||
'1.3.6.1.1.1.1.27' => ['nisMapEntry'],
|
||||
'1.3.6.1.1.4' => ['vendorName'],
|
||||
'1.3.6.1.1.5' => ['vendorVersion'],
|
||||
'1.3.6.1.1.16.4' => ['entryUUID'],
|
||||
'1.3.6.1.1.20' => ['entryDN'],
|
||||
'2.5.4.0' => ['objectClass'],
|
||||
'2.5.4.1' => ['aliasedObjectName', 'aliasedEntryName'],
|
||||
'2.5.4.2' => ['knowledgeInformation'],
|
||||
'2.5.4.3' => ['cn', 'commonName'],
|
||||
'2.5.4.4' => ['sn', 'surname'],
|
||||
'2.5.4.5' => ['serialNumber'],
|
||||
'2.5.4.6' => ['c', 'countryName'],
|
||||
'2.5.4.7' => ['l', 'localityName'],
|
||||
'2.5.4.8' => ['st', 'stateOrProvinceName'],
|
||||
'2.5.4.9' => ['street', 'streetAddress'],
|
||||
'2.5.4.10' => ['o', 'organizationName'],
|
||||
'2.5.4.11' => ['ou', 'organizationalUnitName'],
|
||||
'2.5.4.12' => ['title'],
|
||||
'2.5.4.13' => ['description'],
|
||||
'2.5.4.14' => ['searchGuide'],
|
||||
'2.5.4.15' => ['businessCategory'],
|
||||
'2.5.4.16' => ['postalAddress'],
|
||||
'2.5.4.17' => ['postalCode'],
|
||||
'2.5.4.18' => ['postOfficeBox'],
|
||||
'2.5.4.19' => ['physicalDeliveryOfficeName'],
|
||||
'2.5.4.20' => ['telephoneNumber'],
|
||||
'2.5.4.21' => ['telexNumber'],
|
||||
'2.5.4.22' => ['teletexTerminalIdentifier'],
|
||||
'2.5.4.23' => ['facsimileTelephoneNumber', 'fax'],
|
||||
'2.5.4.24' => ['x121Address'],
|
||||
'2.5.4.25' => ['internationaliSDNNumber'],
|
||||
'2.5.4.26' => ['registeredAddress'],
|
||||
'2.5.4.27' => ['destinationIndicator'],
|
||||
'2.5.4.28' => ['preferredDeliveryMethod'],
|
||||
'2.5.4.29' => ['presentationAddress'],
|
||||
'2.5.4.30' => ['supportedApplicationContext'],
|
||||
'2.5.4.31' => ['member'],
|
||||
'2.5.4.32' => ['owner'],
|
||||
'2.5.4.33' => ['roleOccupant'],
|
||||
'2.5.4.34' => ['seeAlso'],
|
||||
'2.5.4.35' => ['userPassword'],
|
||||
'2.5.4.36' => ['userCertificate'],
|
||||
'2.5.4.37' => ['cACertificate'],
|
||||
'2.5.4.38' => ['authorityRevocationList'],
|
||||
'2.5.4.39' => ['certificateRevocationList'],
|
||||
'2.5.4.40' => ['crossCertificatePair'],
|
||||
'2.5.4.41' => ['name'],
|
||||
'2.5.4.42' => ['givenName', 'gn'],
|
||||
'2.5.4.43' => ['initials'],
|
||||
'2.5.4.44' => ['generationQualifier'],
|
||||
'2.5.4.45' => ['x500UniqueIdentifier'],
|
||||
'2.5.4.46' => ['dnQualifier'],
|
||||
'2.5.4.47' => ['enhancedSearchGuide'],
|
||||
'2.5.4.48' => ['protocolInformation'],
|
||||
'2.5.4.49' => ['distinguishedName'],
|
||||
'2.5.4.50' => ['uniqueMember'],
|
||||
'2.5.4.51' => ['houseIdentifier'],
|
||||
'2.5.4.52' => ['supportedAlgorithms'],
|
||||
'2.5.4.53' => ['deltaRevocationList'],
|
||||
'2.5.4.54' => ['dmdName'],
|
||||
'2.5.4.65' => ['pseudonym'],
|
||||
'2.5.18.1' => ['createTimestamp'],
|
||||
'2.5.18.2' => ['modifyTimestamp'],
|
||||
'2.5.18.3' => ['creatorsName'],
|
||||
'2.5.18.4' => ['modifiersName'],
|
||||
'2.5.18.5' => ['administrativeRole'],
|
||||
'2.5.18.6' => ['subtreeSpecification'],
|
||||
'2.5.18.9' => ['hasSubordinates'],
|
||||
'2.5.18.10' => ['subschemaSubentry'],
|
||||
'2.5.21.1' => ['dITStructureRules'],
|
||||
'2.5.21.2' => ['dITContentRules'],
|
||||
'2.5.21.4' => ['matchingRules'],
|
||||
'2.5.21.5' => ['attributeTypes'],
|
||||
'2.5.21.6' => ['objectClasses'],
|
||||
'2.5.21.7' => ['nameForms'],
|
||||
'2.5.21.8' => ['matchingRuleUse'],
|
||||
'2.5.21.9' => ['structuralObjectClass'],
|
||||
'2.16.840.1.113730.3.1.1' => ['carLicense'],
|
||||
'2.16.840.1.113730.3.1.2' => ['departmentNumber'],
|
||||
'2.16.840.1.113730.3.1.3' => ['employeeNumber'],
|
||||
'2.16.840.1.113730.3.1.4' => ['employeeType'],
|
||||
'2.16.840.1.113730.3.1.34' => ['ref'],
|
||||
'2.16.840.1.113730.3.1.39' => ['preferredLanguage'],
|
||||
'2.16.840.1.113730.3.1.40' => ['userSMIMECertificate'],
|
||||
'2.16.840.1.113730.3.1.216' => ['userPKCS12'],
|
||||
'2.16.840.1.113730.3.1.241' => ['displayName'],
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $_oid OID in dotted format
|
||||
*/
|
||||
private function __construct(
|
||||
protected string $_oid
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(string $oid): self
|
||||
{
|
||||
return new self($oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(ObjectIdentifier $oi): self
|
||||
{
|
||||
return self::create($oi->oid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from attribute name.
|
||||
*/
|
||||
public static function fromName(string $name): self
|
||||
{
|
||||
$oid = self::attrNameToOID($name);
|
||||
return self::create($oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID of the attribute.
|
||||
*
|
||||
* @return string OID in dotted format
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->_oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the attribute.
|
||||
*/
|
||||
public function typeName(): string
|
||||
{
|
||||
if (array_key_exists($this->_oid, self::MAP_OID_TO_NAME)) {
|
||||
return self::MAP_OID_TO_NAME[$this->_oid][0];
|
||||
}
|
||||
return $this->_oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 element.
|
||||
*/
|
||||
public function toASN1(): ObjectIdentifier
|
||||
{
|
||||
return ObjectIdentifier::create($this->_oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert attribute name to OID.
|
||||
*
|
||||
* @param string $name Primary attribute name or an alias
|
||||
*
|
||||
* @return string OID in dotted format
|
||||
*/
|
||||
public static function attrNameToOID(string $name): string
|
||||
{
|
||||
// if already in OID form
|
||||
if (preg_match('/^[0-9]+(?:\.[0-9]+)*$/', $name) === 1) {
|
||||
return $name;
|
||||
}
|
||||
$map = self::_oidReverseMap();
|
||||
$k = mb_strtolower($name, '8bit');
|
||||
if (! isset($map[$k])) {
|
||||
throw new OutOfBoundsException("No OID for {$name}.");
|
||||
}
|
||||
return $map[$k];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ASN.1 string for given attribute type.
|
||||
*
|
||||
* @param string $oid Attribute OID
|
||||
* @param string $str String
|
||||
*/
|
||||
public static function asn1StringForType(string $oid, string $str): StringType
|
||||
{
|
||||
if (! array_key_exists($oid, self::MAP_ATTR_TO_STR_TYPE)) {
|
||||
return UTF8String::create($str);
|
||||
}
|
||||
return PrintableString::create($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name to OID lookup map.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private static function _oidReverseMap(): array
|
||||
{
|
||||
static $map;
|
||||
if (! isset($map)) {
|
||||
$map = [];
|
||||
// for each attribute type
|
||||
foreach (self::MAP_OID_TO_NAME as $oid => $names) {
|
||||
// for primary name and aliases
|
||||
foreach ($names as $name) {
|
||||
$map[mb_strtolower($name, '8bit')] = $oid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
}
|
||||
115
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeTypeAndValue.php
vendored
Normal file
115
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeTypeAndValue.php
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use Stringable;
|
||||
|
||||
/**
|
||||
* Implements *AttributeTypeAndValue* ASN.1 type.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.AttributeTypeAndValue
|
||||
*/
|
||||
final class AttributeTypeAndValue implements Stringable
|
||||
{
|
||||
/**
|
||||
* @param AttributeType $type Attribute type
|
||||
* @param AttributeValue $value Attribute value
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly AttributeType $type,
|
||||
private readonly AttributeValue $value
|
||||
) {
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public static function create(AttributeType $type, AttributeValue $value): self
|
||||
{
|
||||
return new self($type, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Sequence $seq): self
|
||||
{
|
||||
$type = AttributeType::fromASN1($seq->at(0)->asObjectIdentifier());
|
||||
$value = AttributeValue::fromASN1ByOID($type->oid(), $seq->at(1));
|
||||
return self::create($type, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to initialize from attribute value.
|
||||
*
|
||||
* @param AttributeValue $value Attribute value
|
||||
*/
|
||||
public static function fromAttributeValue(AttributeValue $value): self
|
||||
{
|
||||
return self::create(AttributeType::create($value->oid()), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value.
|
||||
*/
|
||||
public function value(): AttributeValue
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Sequence
|
||||
{
|
||||
return Sequence::create($this->type->toASN1(), $this->value->toASN1());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attributeTypeAndValue string conforming to RFC 2253.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2253#section-2.3
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->type->typeName() . '=' . $this->value->rfc2253String();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether attribute is semantically equal to other.
|
||||
*
|
||||
* @param AttributeTypeAndValue $other Object to compare to
|
||||
*/
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
// check that attribute types match
|
||||
if ($this->oid() !== $other->oid()) {
|
||||
return false;
|
||||
}
|
||||
$matcher = $this->value->equalityMatchingRule();
|
||||
|
||||
return $matcher->compare($this->value->stringValue(), $other->value->stringValue()) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute type.
|
||||
*/
|
||||
public function type(): AttributeType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OID of the attribute.
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->type->oid();
|
||||
}
|
||||
}
|
||||
146
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/AttributeValue.php
vendored
Normal file
146
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/AttributeValue.php
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\Attribute;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeTypeAndValue;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\MatchingRule;
|
||||
use Stringable;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Base class for attribute values.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.AttributeValue
|
||||
*/
|
||||
abstract class AttributeValue implements Stringable
|
||||
{
|
||||
/**
|
||||
* Mapping from attribute type OID to attribute value class name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const MAP_OID_TO_CLASS = [
|
||||
AttributeType::OID_COMMON_NAME => CommonNameValue::class,
|
||||
AttributeType::OID_SURNAME => SurnameValue::class,
|
||||
AttributeType::OID_SERIAL_NUMBER => SerialNumberValue::class,
|
||||
AttributeType::OID_COUNTRY_NAME => CountryNameValue::class,
|
||||
AttributeType::OID_LOCALITY_NAME => LocalityNameValue::class,
|
||||
AttributeType::OID_STATE_OR_PROVINCE_NAME => StateOrProvinceNameValue::class,
|
||||
AttributeType::OID_ORGANIZATION_NAME => OrganizationNameValue::class,
|
||||
AttributeType::OID_ORGANIZATIONAL_UNIT_NAME => OrganizationalUnitNameValue::class,
|
||||
AttributeType::OID_TITLE => TitleValue::class,
|
||||
AttributeType::OID_DESCRIPTION => DescriptionValue::class,
|
||||
AttributeType::OID_NAME => NameValue::class,
|
||||
AttributeType::OID_GIVEN_NAME => GivenNameValue::class,
|
||||
AttributeType::OID_PSEUDONYM => PseudonymValue::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $oid OID of the attribute type.
|
||||
*/
|
||||
protected function __construct(
|
||||
protected string $oid
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as an UTF-8 encoded string.
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->_transcodedString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 element.
|
||||
*/
|
||||
abstract public function toASN1(): Element;
|
||||
|
||||
/**
|
||||
* Get attribute value as a string.
|
||||
*/
|
||||
abstract public function stringValue(): string;
|
||||
|
||||
/**
|
||||
* Get matching rule for equality comparison.
|
||||
*/
|
||||
abstract public function equalityMatchingRule(): MatchingRule;
|
||||
|
||||
/**
|
||||
* Get attribute value as a string conforming to RFC 2253.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2253#section-2.4
|
||||
*/
|
||||
abstract public function rfc2253String(): string;
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
abstract public static function fromASN1(UnspecifiedType $el): self;
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1 with given OID hint.
|
||||
*
|
||||
* @param string $oid Attribute's OID
|
||||
*/
|
||||
public static function fromASN1ByOID(string $oid, UnspecifiedType $el): self
|
||||
{
|
||||
if (! array_key_exists($oid, self::MAP_OID_TO_CLASS)) {
|
||||
return new UnknownAttributeValue($oid, $el->asElement());
|
||||
}
|
||||
$cls = self::MAP_OID_TO_CLASS[$oid];
|
||||
return $cls::fromASN1($el);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from another AttributeValue.
|
||||
*
|
||||
* This method is generally used to cast UnknownAttributeValue to specific object when class is declared outside
|
||||
* this package.
|
||||
*
|
||||
* @param self $obj Instance of AttributeValue
|
||||
*/
|
||||
public static function fromSelf(self $obj): self
|
||||
{
|
||||
return static::fromASN1($obj->toASN1()->asUnspecified());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute type's OID.
|
||||
*/
|
||||
public function oid(): string
|
||||
{
|
||||
return $this->oid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Attribute object with this as a single value.
|
||||
*/
|
||||
public function toAttribute(): Attribute
|
||||
{
|
||||
return Attribute::fromAttributeValues($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AttributeTypeAndValue object with this as a value.
|
||||
*/
|
||||
public function toAttributeTypeAndValue(): AttributeTypeAndValue
|
||||
{
|
||||
return AttributeTypeAndValue::fromAttributeValue($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attribute value as an UTF-8 string conforming to RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.1
|
||||
*/
|
||||
abstract protected function _transcodedString(): string;
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/CommonNameValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/CommonNameValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'commonName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.commonName
|
||||
*/
|
||||
final class CommonNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_COMMON_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
35
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/CountryNameValue.php
vendored
Normal file
35
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/CountryNameValue.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\PrintableStringValue;
|
||||
|
||||
/**
|
||||
* 'countryName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.countryName
|
||||
*/
|
||||
final class CountryNameValue extends PrintableStringValue
|
||||
{
|
||||
/**
|
||||
* @param string $value String value
|
||||
*/
|
||||
protected function __construct(string $value)
|
||||
{
|
||||
parent::__construct(AttributeType::OID_COUNTRY_NAME, $value);
|
||||
}
|
||||
|
||||
public static function create(string $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function fromASN1(UnspecifiedType $el): self
|
||||
{
|
||||
return self::create($el->asPrintableString()->string());
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/DescriptionValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/DescriptionValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'description' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.description
|
||||
*/
|
||||
final class DescriptionValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_DESCRIPTION, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
148
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/Feature/DirectoryString.php
vendored
Normal file
148
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/Feature/DirectoryString.php
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\BMPString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\PrintableString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\T61String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UniversalString;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\UTF8String;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use SpomkyLabs\Pki\X501\DN\DNParser;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\CaseIgnoreMatch;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\MatchingRule;
|
||||
use SpomkyLabs\Pki\X501\StringPrep\TranscodeStep;
|
||||
use UnexpectedValueException;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Base class for attribute values having *(Unbounded)DirectoryString* as a syntax.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.UnboundedDirectoryString
|
||||
*/
|
||||
abstract class DirectoryString extends AttributeValue
|
||||
{
|
||||
/**
|
||||
* Teletex string syntax.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const TELETEX = Element::TYPE_T61_STRING;
|
||||
|
||||
/**
|
||||
* Printable string syntax.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const PRINTABLE = Element::TYPE_PRINTABLE_STRING;
|
||||
|
||||
/**
|
||||
* BMP string syntax.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const BMP = Element::TYPE_BMP_STRING;
|
||||
|
||||
/**
|
||||
* Universal string syntax.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const UNIVERSAL = Element::TYPE_UNIVERSAL_STRING;
|
||||
|
||||
/**
|
||||
* UTF-8 string syntax.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
final public const UTF8 = Element::TYPE_UTF8_STRING;
|
||||
|
||||
/**
|
||||
* Mapping from syntax enumeration to ASN.1 class name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const MAP_TAG_TO_CLASS = [
|
||||
self::TELETEX => T61String::class,
|
||||
self::PRINTABLE => PrintableString::class,
|
||||
self::UNIVERSAL => UniversalString::class,
|
||||
self::UTF8 => UTF8String::class,
|
||||
self::BMP => BMPString::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $_string String value
|
||||
* @param int $_stringTag Syntax choice
|
||||
*/
|
||||
final protected function __construct(
|
||||
string $oid,
|
||||
protected string $_string,
|
||||
protected int $_stringTag
|
||||
) {
|
||||
parent::__construct($oid);
|
||||
}
|
||||
|
||||
abstract public static function create(string $value, int $string_tag = self::UTF8): static;
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function fromASN1(UnspecifiedType $el): AttributeValue
|
||||
{
|
||||
$tag = $el->tag();
|
||||
// validate tag
|
||||
self::_tagToASN1Class($tag);
|
||||
return static::create($el->asString()->string(), $tag);
|
||||
}
|
||||
|
||||
public function toASN1(): Element
|
||||
{
|
||||
$cls = self::_tagToASN1Class($this->_stringTag);
|
||||
return $cls::create($this->_string);
|
||||
}
|
||||
|
||||
public function stringValue(): string
|
||||
{
|
||||
return $this->_string;
|
||||
}
|
||||
|
||||
public function equalityMatchingRule(): MatchingRule
|
||||
{
|
||||
return CaseIgnoreMatch::create($this->_stringTag);
|
||||
}
|
||||
|
||||
public function rfc2253String(): string
|
||||
{
|
||||
// TeletexString is encoded as binary
|
||||
if ($this->_stringTag === self::TELETEX) {
|
||||
return $this->_transcodedString();
|
||||
}
|
||||
return DNParser::escapeString($this->_transcodedString());
|
||||
}
|
||||
|
||||
protected function _transcodedString(): string
|
||||
{
|
||||
return TranscodeStep::create($this->_stringTag)
|
||||
->apply($this->_string)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ASN.1 class name for given DirectoryString type tag.
|
||||
*/
|
||||
private static function _tagToASN1Class(int $tag): string
|
||||
{
|
||||
if (! array_key_exists($tag, self::MAP_TAG_TO_CLASS)) {
|
||||
throw new UnexpectedValueException(
|
||||
sprintf('Type %s is not valid DirectoryString.', Element::tagToName($tag))
|
||||
);
|
||||
}
|
||||
return self::MAP_TAG_TO_CLASS[$tag];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\PrintableString;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use SpomkyLabs\Pki\X501\DN\DNParser;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\CaseIgnoreMatch;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\MatchingRule;
|
||||
|
||||
/**
|
||||
* Base class for attribute values having *PrintableString* syntax.
|
||||
*/
|
||||
abstract class PrintableStringValue extends AttributeValue
|
||||
{
|
||||
/**
|
||||
* @param string $_string String value
|
||||
*/
|
||||
protected function __construct(
|
||||
string $oid,
|
||||
protected string $_string
|
||||
) {
|
||||
parent::__construct($oid);
|
||||
}
|
||||
|
||||
public function toASN1(): Element
|
||||
{
|
||||
return PrintableString::create($this->_string);
|
||||
}
|
||||
|
||||
public function stringValue(): string
|
||||
{
|
||||
return $this->_string;
|
||||
}
|
||||
|
||||
public function equalityMatchingRule(): MatchingRule
|
||||
{
|
||||
// default to caseIgnoreMatch
|
||||
return CaseIgnoreMatch::create(Element::TYPE_PRINTABLE_STRING);
|
||||
}
|
||||
|
||||
public function rfc2253String(): string
|
||||
{
|
||||
return DNParser::escapeString($this->_transcodedString());
|
||||
}
|
||||
|
||||
protected function _transcodedString(): string
|
||||
{
|
||||
// PrintableString maps directly to UTF-8
|
||||
return $this->_string;
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/GivenNameValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/GivenNameValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'givenName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.givenName
|
||||
*/
|
||||
final class GivenNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_GIVEN_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/LocalityNameValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/LocalityNameValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'localityName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.localityName
|
||||
*/
|
||||
final class LocalityNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_LOCALITY_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/NameValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/NameValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'name' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.name
|
||||
*/
|
||||
final class NameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'organizationName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.organizationName
|
||||
*/
|
||||
final class OrganizationNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_ORGANIZATION_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'organizationalUnitName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.organizationalUnitName
|
||||
*/
|
||||
final class OrganizationalUnitNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_ORGANIZATIONAL_UNIT_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/PseudonymValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/PseudonymValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'pseudonym' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.pseudonym
|
||||
*/
|
||||
final class PseudonymValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_PSEUDONYM, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
35
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/SerialNumberValue.php
vendored
Normal file
35
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/SerialNumberValue.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\PrintableStringValue;
|
||||
|
||||
/**
|
||||
* 'serialNumber' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.serialNumber
|
||||
*/
|
||||
final class SerialNumberValue extends PrintableStringValue
|
||||
{
|
||||
/**
|
||||
* @param string $value String value
|
||||
*/
|
||||
protected function __construct(string $value)
|
||||
{
|
||||
parent::__construct(AttributeType::OID_SERIAL_NUMBER, $value);
|
||||
}
|
||||
|
||||
public static function create(string $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function fromASN1(UnspecifiedType $el): self
|
||||
{
|
||||
return self::create($el->asPrintableString()->string());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'stateOrProvinceName' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.stateOrProvinceName
|
||||
*/
|
||||
final class StateOrProvinceNameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_STATE_OR_PROVINCE_NAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/SurnameValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/SurnameValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'surname' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.surname
|
||||
*/
|
||||
final class SurnameValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_SURNAME, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/TitleValue.php
vendored
Normal file
21
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/AttributeValue/TitleValue.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\Feature\DirectoryString;
|
||||
|
||||
/**
|
||||
* 'title' attribute value.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2012/SelectedAttributeTypes.html#SelectedAttributeTypes.title
|
||||
*/
|
||||
final class TitleValue extends DirectoryString
|
||||
{
|
||||
public static function create(string $value, int $string_tag = DirectoryString::UTF8): static
|
||||
{
|
||||
return new static(AttributeType::OID_TITLE, $value, $string_tag);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\AttributeValue;
|
||||
|
||||
use BadMethodCallException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\DN\DNParser;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\BinaryMatch;
|
||||
use SpomkyLabs\Pki\X501\MatchingRule\MatchingRule;
|
||||
use SpomkyLabs\Pki\X501\StringPrep\TranscodeStep;
|
||||
|
||||
/**
|
||||
* Class to hold ASN.1 structure of an unimplemented attribute value.
|
||||
*/
|
||||
final class UnknownAttributeValue extends AttributeValue
|
||||
{
|
||||
/**
|
||||
* @param Element $_element ASN.1 element.
|
||||
*/
|
||||
protected function __construct(
|
||||
string $oid,
|
||||
protected Element $_element
|
||||
) {
|
||||
parent::__construct($oid);
|
||||
$this->oid = $oid;
|
||||
}
|
||||
|
||||
public static function create(string $oid, Element $_element): self
|
||||
{
|
||||
return new self($oid, $_element);
|
||||
}
|
||||
|
||||
public function toASN1(): Element
|
||||
{
|
||||
return $this->_element;
|
||||
}
|
||||
|
||||
public function stringValue(): string
|
||||
{
|
||||
// if value is encoded as a string type
|
||||
if ($this->_element->isType(Element::TYPE_STRING)) {
|
||||
return $this->_element->asUnspecified()
|
||||
->asString()
|
||||
->string();
|
||||
}
|
||||
// return DER encoding as a hexstring (see RFC2253 section 2.4)
|
||||
return '#' . bin2hex($this->_element->toDER());
|
||||
}
|
||||
|
||||
public function equalityMatchingRule(): MatchingRule
|
||||
{
|
||||
return new BinaryMatch();
|
||||
}
|
||||
|
||||
public function rfc2253String(): string
|
||||
{
|
||||
$str = $this->_transcodedString();
|
||||
// if value has a string representation
|
||||
if ($this->_element->isType(Element::TYPE_STRING)) {
|
||||
$str = DNParser::escapeString($str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function fromASN1(UnspecifiedType $el): AttributeValue
|
||||
{
|
||||
throw new BadMethodCallException('ASN.1 parsing must be implemented in a concrete class.');
|
||||
}
|
||||
|
||||
protected function _transcodedString(): string
|
||||
{
|
||||
// if transcoding is defined for the value type
|
||||
if (TranscodeStep::isTypeSupported($this->_element->tag())) {
|
||||
$step = TranscodeStep::create($this->_element->tag());
|
||||
return $step->apply($this->stringValue());
|
||||
}
|
||||
return $this->stringValue();
|
||||
}
|
||||
}
|
||||
187
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/AttributeCollection.php
vendored
Normal file
187
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/AttributeCollection.php
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\Collection;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Structure;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\Attribute;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeType;
|
||||
use UnexpectedValueException;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Base class for X.501 attribute containers.
|
||||
*
|
||||
* Implements methods for Countable and IteratorAggregate interfaces.
|
||||
*/
|
||||
abstract class AttributeCollection implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Array of attributes.
|
||||
*
|
||||
* Always with consecutive indices.
|
||||
*
|
||||
* @var Attribute[]
|
||||
*/
|
||||
protected array $_attributes;
|
||||
|
||||
/**
|
||||
* @param Attribute ...$attribs List of attributes
|
||||
*/
|
||||
final private function __construct(Attribute ...$attribs)
|
||||
{
|
||||
$this->_attributes = $attribs;
|
||||
}
|
||||
|
||||
public static function create(Attribute ...$attribs): static
|
||||
{
|
||||
return new static(...$attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether attribute is present.
|
||||
*
|
||||
* @param string $name OID or attribute name
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return $this->_findFirst($name) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first attribute by OID or attribute name.
|
||||
*
|
||||
* @param string $name OID or attribute name
|
||||
*/
|
||||
public function firstOf(string $name): Attribute
|
||||
{
|
||||
$attr = $this->_findFirst($name);
|
||||
if ($attr === null) {
|
||||
throw new UnexpectedValueException("No {$name} attribute.");
|
||||
}
|
||||
return $attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all attributes of given name.
|
||||
*
|
||||
* @param string $name OID or attribute name
|
||||
*
|
||||
* @return Attribute[]
|
||||
*/
|
||||
public function allOf(string $name): array
|
||||
{
|
||||
$oid = AttributeType::attrNameToOID($name);
|
||||
return array_values(array_filter($this->_attributes, fn (Attribute $attr) => $attr->oid() === $oid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all attributes.
|
||||
*
|
||||
* @return Attribute[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with additional attributes added.
|
||||
*
|
||||
* @param Attribute ...$attribs List of attributes to add
|
||||
*/
|
||||
public function withAdditional(Attribute ...$attribs): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
foreach ($attribs as $attr) {
|
||||
$obj->_attributes[] = $attr;
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with single unique attribute added.
|
||||
*
|
||||
* All previous attributes of the same type are removed.
|
||||
*
|
||||
* @param Attribute $attr Attribute to add
|
||||
*/
|
||||
public function withUnique(Attribute $attr): static
|
||||
{
|
||||
$attribs = array_values(array_filter($this->_attributes, fn (Attribute $a) => $a->oid() !== $attr->oid()));
|
||||
$attribs[] = $attr;
|
||||
$obj = clone $this;
|
||||
$obj->_attributes = $attribs;
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of attributes.
|
||||
*
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for attributes.
|
||||
*
|
||||
* @return ArrayIterator|Attribute[]
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->_attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first attribute of given name or OID.
|
||||
*
|
||||
* @param string $name OID or attribute name
|
||||
*/
|
||||
protected function _findFirst(string $name): ?Attribute
|
||||
{
|
||||
$oid = AttributeType::attrNameToOID($name);
|
||||
foreach ($this->_attributes as $attr) {
|
||||
if ($attr->oid() === $oid) {
|
||||
return $attr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1 constructed element.
|
||||
*
|
||||
* @param Structure $struct ASN.1 structure
|
||||
*/
|
||||
protected static function _fromASN1Structure(Structure $struct): static
|
||||
{
|
||||
return static::create(...array_map(
|
||||
static fn (UnspecifiedType $el) => static::_castAttributeValues(
|
||||
Attribute::fromASN1($el->asSequence())
|
||||
),
|
||||
$struct->elements()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast Attribute's AttributeValues to implementation specific objects.
|
||||
*
|
||||
* Overridden in derived classes.
|
||||
*
|
||||
* @param Attribute $attribute Attribute to cast
|
||||
*/
|
||||
protected static function _castAttributeValues(Attribute $attribute): Attribute
|
||||
{
|
||||
// pass through by default
|
||||
return $attribute;
|
||||
}
|
||||
}
|
||||
46
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/SequenceOfAttributes.php
vendored
Normal file
46
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/SequenceOfAttributes.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\Collection;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\X501\ASN1\Attribute;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
|
||||
/**
|
||||
* Implements *Attributes* ASN.1 type as a *SEQUENCE OF Attribute*.
|
||||
*
|
||||
* Used in *AttributeCertificateInfo*.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc5755#section-4.1
|
||||
* @see https://tools.ietf.org/html/rfc5755#section-4.2.7
|
||||
*/
|
||||
class SequenceOfAttributes extends AttributeCollection
|
||||
{
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Sequence $seq): self
|
||||
{
|
||||
return static::_fromASN1Structure($seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from attribute values.
|
||||
*
|
||||
* @param AttributeValue ...$values List of attribute values
|
||||
*/
|
||||
public static function fromAttributeValues(AttributeValue ...$values): static
|
||||
{
|
||||
return static::create(...array_map(static fn (AttributeValue $value) => $value->toAttribute(), $values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Sequence
|
||||
{
|
||||
return Sequence::create(...array_map(static fn (Attribute $attr) => $attr->toASN1(), $this->_attributes));
|
||||
}
|
||||
}
|
||||
47
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/SetOfAttributes.php
vendored
Normal file
47
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Collection/SetOfAttributes.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1\Collection;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Set;
|
||||
use SpomkyLabs\Pki\X501\ASN1\Attribute;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
|
||||
/**
|
||||
* Implements *Attributes* ASN.1 type as a *SET OF Attribute*.
|
||||
*
|
||||
* Used in *CertificationRequestInfo* and *OneAsymmetricKey*.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2986#section-4
|
||||
* @see https://tools.ietf.org/html/rfc5958#section-2
|
||||
*/
|
||||
class SetOfAttributes extends AttributeCollection
|
||||
{
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Set $set): static
|
||||
{
|
||||
return static::_fromASN1Structure($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from attribute values.
|
||||
*
|
||||
* @param AttributeValue ...$values List of attribute values
|
||||
*/
|
||||
public static function fromAttributeValues(AttributeValue ...$values): static
|
||||
{
|
||||
return static::create(...array_map(static fn (AttributeValue $value) => $value->toAttribute(), $values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Set
|
||||
{
|
||||
$set = Set::create(...array_map(static fn (Attribute $attr) => $attr->toASN1(), $this->_attributes));
|
||||
return $set->sortedSetOf();
|
||||
}
|
||||
}
|
||||
193
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Name.php
vendored
Normal file
193
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/Name.php
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use RangeException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use SpomkyLabs\Pki\X501\DN\DNParser;
|
||||
use Stringable;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Implements *Name* ASN.1 type.
|
||||
*
|
||||
* Since *Name* is a CHOICE only supporting *RDNSequence* type, this class implements *RDNSequence* semantics as well.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.Name
|
||||
*/
|
||||
final class Name implements Countable, IteratorAggregate, Stringable
|
||||
{
|
||||
/**
|
||||
* Relative distinguished name components.
|
||||
*
|
||||
* @var RDN[]
|
||||
*/
|
||||
private readonly array $rdns;
|
||||
|
||||
/**
|
||||
* @param RDN ...$rdns RDN components
|
||||
*/
|
||||
private function __construct(RDN ...$rdns)
|
||||
{
|
||||
$this->rdns = $rdns;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public static function create(RDN ...$rdns): self
|
||||
{
|
||||
return new self(...$rdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Sequence $seq): self
|
||||
{
|
||||
$rdns = array_map(static fn (UnspecifiedType $el) => RDN::fromASN1($el->asSet()), $seq->elements());
|
||||
return self::create(...$rdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from distinguished name string.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc1779
|
||||
*/
|
||||
public static function fromString(string $str): self
|
||||
{
|
||||
$rdns = [];
|
||||
foreach (DNParser::parseString($str) as $nameComponent) {
|
||||
$attribs = [];
|
||||
foreach ($nameComponent as [$name, $val]) {
|
||||
$type = AttributeType::fromName($name);
|
||||
// hexstrings are parsed to ASN.1 elements
|
||||
if ($val instanceof Element) {
|
||||
$el = $val;
|
||||
} else {
|
||||
$el = AttributeType::asn1StringForType($type->oid(), $val);
|
||||
}
|
||||
$value = AttributeValue::fromASN1ByOID($type->oid(), $el->asUnspecified());
|
||||
$attribs[] = AttributeTypeAndValue::create($type, $value);
|
||||
}
|
||||
$rdns[] = RDN::create(...$attribs);
|
||||
}
|
||||
return self::create(...$rdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Sequence
|
||||
{
|
||||
$elements = array_map(static fn (RDN $rdn) => $rdn->toASN1(), $this->rdns);
|
||||
return Sequence::create(...$elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distinguised name string conforming to RFC 2253.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2253#section-2.1
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
$parts = array_map(static fn (RDN $rdn) => $rdn->toString(), array_reverse($this->rdns));
|
||||
return implode(',', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether name is semantically equal to other.
|
||||
*
|
||||
* Comparison conforms to RFC 4518 string preparation algorithm.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518
|
||||
*
|
||||
* @param Name $other Object to compare to
|
||||
*/
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
// if RDN count doesn't match
|
||||
if (count($this) !== count($other)) {
|
||||
return false;
|
||||
}
|
||||
for ($i = count($this) - 1; $i >= 0; --$i) {
|
||||
$rdn1 = $this->rdns[$i];
|
||||
$rdn2 = $other->rdns[$i];
|
||||
if (! $rdn1->equals($rdn2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all RDN objects.
|
||||
*
|
||||
* @return RDN[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->rdns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first AttributeValue of given type.
|
||||
*
|
||||
* Relative name components shall be traversed in encoding order, which is reversed in regards to the string
|
||||
* representation. Multi-valued RDN with multiple attributes of the requested type is ambiguous and shall throw an
|
||||
* exception.
|
||||
*
|
||||
* @param string $name Attribute OID or name
|
||||
*/
|
||||
public function firstValueOf(string $name): AttributeValue
|
||||
{
|
||||
$oid = AttributeType::attrNameToOID($name);
|
||||
foreach ($this->rdns as $rdn) {
|
||||
$tvs = $rdn->allOf($oid);
|
||||
if (count($tvs) > 1) {
|
||||
throw new RangeException("RDN with multiple {$name} attributes.");
|
||||
}
|
||||
if (count($tvs) === 1) {
|
||||
return $tvs[0]->value();
|
||||
}
|
||||
}
|
||||
throw new RangeException("Attribute {$name} not found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->rdns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of attributes of given type.
|
||||
*
|
||||
* @param string $name Attribute OID or name
|
||||
*/
|
||||
public function countOfType(string $name): int
|
||||
{
|
||||
$oid = AttributeType::attrNameToOID($name);
|
||||
return array_sum(array_map(static fn (RDN $rdn): int => count($rdn->allOf($oid)), $this->rdns));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->rdns);
|
||||
}
|
||||
}
|
||||
167
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/RDN.php
vendored
Normal file
167
libraries/vendor/spomky-labs/pki-framework/src/X501/ASN1/RDN.php
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\ASN1;
|
||||
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Constructed\Set;
|
||||
use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType;
|
||||
use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue;
|
||||
use Stringable;
|
||||
use UnexpectedValueException;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Implements *RelativeDistinguishedName* ASN.1 type.
|
||||
*
|
||||
* @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.RelativeDistinguishedName
|
||||
*/
|
||||
final class RDN implements Countable, IteratorAggregate, Stringable
|
||||
{
|
||||
/**
|
||||
* Attributes.
|
||||
*
|
||||
* @var AttributeTypeAndValue[]
|
||||
*/
|
||||
private readonly array $_attribs;
|
||||
|
||||
/**
|
||||
* @param AttributeTypeAndValue ...$attribs One or more attributes
|
||||
*/
|
||||
private function __construct(AttributeTypeAndValue ...$attribs)
|
||||
{
|
||||
if (count($attribs) === 0) {
|
||||
throw new UnexpectedValueException('RDN must have at least one AttributeTypeAndValue.');
|
||||
}
|
||||
$this->_attribs = $attribs;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public static function create(AttributeTypeAndValue ...$attribs): self
|
||||
{
|
||||
return new self(...$attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to initialize RDN from AttributeValue objects.
|
||||
*
|
||||
* @param AttributeValue ...$values One or more attributes
|
||||
*/
|
||||
public static function fromAttributeValues(AttributeValue ...$values): self
|
||||
{
|
||||
$attribs = array_map(
|
||||
static fn (AttributeValue $value) => AttributeTypeAndValue::create(AttributeType::create(
|
||||
$value->oid()
|
||||
), $value),
|
||||
$values
|
||||
);
|
||||
return self::create(...$attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize from ASN.1.
|
||||
*/
|
||||
public static function fromASN1(Set $set): self
|
||||
{
|
||||
$attribs = array_map(
|
||||
static fn (UnspecifiedType $el) => AttributeTypeAndValue::fromASN1($el->asSequence()),
|
||||
$set->elements()
|
||||
);
|
||||
return self::create(...$attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ASN.1 structure.
|
||||
*/
|
||||
public function toASN1(): Set
|
||||
{
|
||||
$elements = array_map(static fn (AttributeTypeAndValue $tv) => $tv->toASN1(), $this->_attribs);
|
||||
return Set::create(...$elements)->sortedSetOf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name-component string conforming to RFC 2253.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2253#section-2.2
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
$parts = array_map(static fn (AttributeTypeAndValue $tv) => $tv->toString(), $this->_attribs);
|
||||
return implode('+', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether RDN is semantically equal to other.
|
||||
*
|
||||
* @param RDN $other Object to compare to
|
||||
*/
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
// if attribute count doesn't match
|
||||
if (count($this) !== count($other)) {
|
||||
return false;
|
||||
}
|
||||
$attribs1 = $this->_attribs;
|
||||
$attribs2 = $other->_attribs;
|
||||
// if there's multiple attributes, sort using SET OF rules
|
||||
if (count($attribs1) > 1) {
|
||||
$attribs1 = self::fromASN1($this->toASN1())->_attribs;
|
||||
$attribs2 = self::fromASN1($other->toASN1())->_attribs;
|
||||
}
|
||||
for ($i = count($attribs1) - 1; $i >= 0; --$i) {
|
||||
$tv1 = $attribs1[$i];
|
||||
$tv2 = $attribs2[$i];
|
||||
if (! $tv1->equals($tv2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all AttributeTypeAndValue objects.
|
||||
*
|
||||
* @return AttributeTypeAndValue[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->_attribs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all AttributeTypeAndValue objects of the given attribute type.
|
||||
*
|
||||
* @param string $name Attribute OID or name
|
||||
*
|
||||
* @return AttributeTypeAndValue[]
|
||||
*/
|
||||
public function allOf(string $name): array
|
||||
{
|
||||
$oid = AttributeType::attrNameToOID($name);
|
||||
$attribs = array_filter($this->_attribs, static fn (AttributeTypeAndValue $tv) => $tv->oid() === $oid);
|
||||
return array_values($attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \Countable::count()
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->_attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \IteratorAggregate::getIterator()
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->_attribs);
|
||||
}
|
||||
}
|
||||
363
libraries/vendor/spomky-labs/pki-framework/src/X501/DN/DNParser.php
vendored
Normal file
363
libraries/vendor/spomky-labs/pki-framework/src/X501/DN/DNParser.php
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\DN;
|
||||
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Exception\DecodeException;
|
||||
use SpomkyLabs\Pki\ASN1\Feature\ElementBase;
|
||||
use UnexpectedValueException;
|
||||
use function mb_strlen;
|
||||
|
||||
/**
|
||||
* Distinguished Name parsing conforming to RFC 2253 and RFC 1779.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc1779
|
||||
* @see https://tools.ietf.org/html/rfc2253
|
||||
*/
|
||||
final class DNParser
|
||||
{
|
||||
/**
|
||||
* RFC 2253 special characters.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
final public const SPECIAL_CHARS = ',=+<>#;';
|
||||
|
||||
/**
|
||||
* DN string length.
|
||||
*/
|
||||
private readonly int $_len;
|
||||
|
||||
/**
|
||||
* @param string $_dn Distinguised name
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $_dn
|
||||
) {
|
||||
$this->_len = mb_strlen($_dn, '8bit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse distinguished name string to name-components.
|
||||
*
|
||||
* @return array<array<string>>
|
||||
*/
|
||||
public static function parseString(string $dn): array
|
||||
{
|
||||
$parser = new self($dn);
|
||||
return $parser->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a AttributeValue string conforming to RFC 2253.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc2253#section-2.4
|
||||
*/
|
||||
public static function escapeString(string $str): string
|
||||
{
|
||||
// one of the characters ",", "+", """, "\", "<", ">" or ";"
|
||||
$str = preg_replace('/([,\+"\\\<\>;])/u', '\\\\$1', $str);
|
||||
// a space character occurring at the end of the string
|
||||
$str = preg_replace('/( )$/u', '\\\\$1', (string) $str);
|
||||
// a space or "#" character occurring at the beginning of the string
|
||||
$str = preg_replace('/^([ #])/u', '\\\\$1', (string) $str);
|
||||
// implementation specific special characters
|
||||
$str = preg_replace_callback(
|
||||
'/([\pC])/u',
|
||||
function ($m) {
|
||||
$octets = mb_str_split(bin2hex($m[1]), 2, '8bit');
|
||||
return implode('', array_map(static fn ($octet) => '\\' . mb_strtoupper($octet, '8bit'), $octets));
|
||||
},
|
||||
(string) $str
|
||||
);
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse DN to name-components.
|
||||
*
|
||||
* @return array<array<string>>
|
||||
*/
|
||||
private function parse(): array
|
||||
{
|
||||
$offset = 0;
|
||||
$name = $this->_parseName($offset);
|
||||
if ($offset < $this->_len) {
|
||||
$remains = mb_substr($this->_dn, $offset, null, '8bit');
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
'Parser finished before the end of string, remaining: %s',
|
||||
$remains
|
||||
));
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'name'.
|
||||
*
|
||||
* name-component *("," name-component)
|
||||
*
|
||||
* @return array<array<string>> Array of name-components
|
||||
*/
|
||||
private function _parseName(int &$offset): array
|
||||
{
|
||||
$idx = $offset;
|
||||
$names = [];
|
||||
while ($idx < $this->_len) {
|
||||
$names[] = $this->_parseNameComponent($idx);
|
||||
if ($idx >= $this->_len) {
|
||||
break;
|
||||
}
|
||||
$this->_skipWs($idx);
|
||||
if ($this->_dn[$idx] !== ',' && $this->_dn[$idx] !== ';') {
|
||||
break;
|
||||
}
|
||||
++$idx;
|
||||
$this->_skipWs($idx);
|
||||
}
|
||||
$offset = $idx;
|
||||
return array_reverse($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'name-component'.
|
||||
*
|
||||
* attributeTypeAndValue *("+" attributeTypeAndValue)
|
||||
*
|
||||
* @return array<array<string, string|ElementBase>> Array of [type, value] tuples
|
||||
*/
|
||||
private function _parseNameComponent(int &$offset): array
|
||||
{
|
||||
$idx = $offset;
|
||||
$tvpairs = [];
|
||||
while ($idx < $this->_len) {
|
||||
$tvpairs[] = $this->_parseAttrTypeAndValue($idx);
|
||||
$this->_skipWs($idx);
|
||||
if ($idx >= $this->_len || $this->_dn[$idx] !== '+') {
|
||||
break;
|
||||
}
|
||||
++$idx;
|
||||
$this->_skipWs($idx);
|
||||
}
|
||||
$offset = $idx;
|
||||
return $tvpairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'attributeTypeAndValue'.
|
||||
*
|
||||
* attributeType "=" attributeValue
|
||||
*
|
||||
* @return array<string, string|ElementBase> A tuple of [type, value]. Value may be either a string or
|
||||
* an Element, if it's encoded as hexstring.
|
||||
*/
|
||||
private function _parseAttrTypeAndValue(int &$offset): array
|
||||
{
|
||||
$idx = $offset;
|
||||
$type = $this->_parseAttrType($idx);
|
||||
$this->_skipWs($idx);
|
||||
if ($idx >= $this->_len || $this->_dn[$idx++] !== '=') {
|
||||
throw new UnexpectedValueException('Invalid type and value pair.');
|
||||
}
|
||||
$this->_skipWs($idx);
|
||||
// hexstring
|
||||
if ($idx < $this->_len && $this->_dn[$idx] === '#') {
|
||||
++$idx;
|
||||
$data = $this->_parseAttrHexValue($idx);
|
||||
try {
|
||||
$value = Element::fromDER($data);
|
||||
} catch (DecodeException $e) {
|
||||
throw new UnexpectedValueException('Invalid DER encoding from hexstring.', 0, $e);
|
||||
}
|
||||
} else {
|
||||
$value = $this->_parseAttrStringValue($idx);
|
||||
}
|
||||
$offset = $idx;
|
||||
return [$type, $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'attributeType'.
|
||||
*
|
||||
* (ALPHA 1*keychar) / oid
|
||||
*/
|
||||
private function _parseAttrType(int &$offset): string
|
||||
{
|
||||
$idx = $offset;
|
||||
// dotted OID
|
||||
$type = $this->_regexMatch('/^(?:oid\.)?([0-9]+(?:\.[0-9]+)*)/i', $idx);
|
||||
if ($type === null) {
|
||||
// name
|
||||
$type = $this->_regexMatch('/^[a-z][a-z0-9\-]*/i', $idx);
|
||||
if ($type === null) {
|
||||
throw new UnexpectedValueException('Invalid attribute type.');
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'attributeValue' of string type.
|
||||
*/
|
||||
private function _parseAttrStringValue(int &$offset): string
|
||||
{
|
||||
$idx = $offset;
|
||||
if ($idx >= $this->_len) {
|
||||
return '';
|
||||
}
|
||||
if ($this->_dn[$idx] === '"') { // quoted string
|
||||
$val = $this->_parseQuotedAttrString($idx);
|
||||
} else { // string
|
||||
$val = $this->_parseAttrString($idx);
|
||||
}
|
||||
$offset = $idx;
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse plain 'attributeValue' string.
|
||||
*/
|
||||
private function _parseAttrString(int &$offset): string
|
||||
{
|
||||
$idx = $offset;
|
||||
$val = '';
|
||||
$wsidx = null;
|
||||
while ($idx < $this->_len) {
|
||||
$c = $this->_dn[$idx];
|
||||
// pair (escape sequence)
|
||||
if ($c === '\\') {
|
||||
++$idx;
|
||||
$val .= $this->_parsePairAfterSlash($idx);
|
||||
$wsidx = null;
|
||||
continue;
|
||||
}
|
||||
if ($c === '"') {
|
||||
throw new UnexpectedValueException('Unexpected quotation.');
|
||||
}
|
||||
if (mb_strpos(self::SPECIAL_CHARS, $c, 0, '8bit') !== false) {
|
||||
break;
|
||||
}
|
||||
// keep track of the first consecutive whitespace
|
||||
if ($c === ' ') {
|
||||
if ($wsidx === null) {
|
||||
$wsidx = $idx;
|
||||
}
|
||||
} else {
|
||||
$wsidx = null;
|
||||
}
|
||||
// stringchar
|
||||
$val .= $c;
|
||||
++$idx;
|
||||
}
|
||||
// if there was non-escaped whitespace in the end of the value
|
||||
if ($wsidx !== null) {
|
||||
$val = mb_substr($val, 0, -($idx - $wsidx), '8bit');
|
||||
}
|
||||
$offset = $idx;
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse quoted 'attributeValue' string.
|
||||
*
|
||||
* @param int $offset Offset to starting quote
|
||||
*/
|
||||
private function _parseQuotedAttrString(int &$offset): string
|
||||
{
|
||||
$idx = $offset + 1;
|
||||
$val = '';
|
||||
while ($idx < $this->_len) {
|
||||
$c = $this->_dn[$idx];
|
||||
if ($c === '\\') { // pair
|
||||
++$idx;
|
||||
$val .= $this->_parsePairAfterSlash($idx);
|
||||
continue;
|
||||
}
|
||||
if ($c === '"') {
|
||||
++$idx;
|
||||
break;
|
||||
}
|
||||
$val .= $c;
|
||||
++$idx;
|
||||
}
|
||||
$offset = $idx;
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'attributeValue' of binary type.
|
||||
*/
|
||||
private function _parseAttrHexValue(int &$offset): string
|
||||
{
|
||||
$idx = $offset;
|
||||
$hexstr = $this->_regexMatch('/^(?:[0-9a-f]{2})+/i', $idx);
|
||||
if ($hexstr === null) {
|
||||
throw new UnexpectedValueException('Invalid hexstring.');
|
||||
}
|
||||
$data = hex2bin($hexstr);
|
||||
$offset = $idx;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 'pair' after leading slash.
|
||||
*/
|
||||
private function _parsePairAfterSlash(int &$offset): string
|
||||
{
|
||||
$idx = $offset;
|
||||
if ($idx >= $this->_len) {
|
||||
throw new UnexpectedValueException('Unexpected end of escape sequence.');
|
||||
}
|
||||
$c = $this->_dn[$idx++];
|
||||
// special | \ | " | SPACE
|
||||
if (mb_strpos(self::SPECIAL_CHARS . '\\" ', $c, 0, '8bit') !== false) {
|
||||
$val = $c;
|
||||
} else { // hexpair
|
||||
if ($idx >= $this->_len) {
|
||||
throw new UnexpectedValueException('Unexpected end of hexpair.');
|
||||
}
|
||||
$val = @hex2bin($c . $this->_dn[$idx++]);
|
||||
if ($val === false) {
|
||||
throw new UnexpectedValueException('Invalid hexpair.');
|
||||
}
|
||||
}
|
||||
$offset = $idx;
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match DN to pattern and extract the last capture group.
|
||||
*
|
||||
* Updates offset to fully matched pattern.
|
||||
*
|
||||
* @return null|string Null if pattern doesn't match
|
||||
*/
|
||||
private function _regexMatch(string $pattern, int &$offset): ?string
|
||||
{
|
||||
$idx = $offset;
|
||||
if (preg_match($pattern, mb_substr($this->_dn, $idx, null, '8bit'), $match) !== 1) {
|
||||
return null;
|
||||
}
|
||||
$idx += mb_strlen($match[0], '8bit');
|
||||
$offset = $idx;
|
||||
return end($match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip consecutive spaces.
|
||||
*/
|
||||
private function _skipWs(int &$offset): void
|
||||
{
|
||||
$idx = $offset;
|
||||
while ($idx < $this->_len) {
|
||||
if ($this->_dn[$idx] !== ' ') {
|
||||
break;
|
||||
}
|
||||
++$idx;
|
||||
}
|
||||
$offset = $idx;
|
||||
}
|
||||
}
|
||||
18
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/BinaryMatch.php
vendored
Normal file
18
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/BinaryMatch.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\MatchingRule;
|
||||
|
||||
/**
|
||||
* Implements binary matching rule.
|
||||
*
|
||||
* Generally used only by UnknownAttribute and custom attributes.
|
||||
*/
|
||||
final class BinaryMatch extends MatchingRule
|
||||
{
|
||||
public function compare(string $assertion, string $value): ?bool
|
||||
{
|
||||
return strcmp($assertion, $value) === 0;
|
||||
}
|
||||
}
|
||||
28
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/CaseExactMatch.php
vendored
Normal file
28
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/CaseExactMatch.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\MatchingRule;
|
||||
|
||||
use SpomkyLabs\Pki\X501\StringPrep\StringPreparer;
|
||||
|
||||
/**
|
||||
* Implements 'caseExactMatch' matching rule.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4517#section-4.2.4
|
||||
*/
|
||||
final class CaseExactMatch extends StringPrepMatchingRule
|
||||
{
|
||||
/**
|
||||
* @param int $stringType ASN.1 string type tag
|
||||
*/
|
||||
private function __construct(int $stringType)
|
||||
{
|
||||
parent::__construct(StringPreparer::forStringType($stringType));
|
||||
}
|
||||
|
||||
public static function create(int $stringType): self
|
||||
{
|
||||
return new self($stringType);
|
||||
}
|
||||
}
|
||||
30
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/CaseIgnoreMatch.php
vendored
Normal file
30
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/CaseIgnoreMatch.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\MatchingRule;
|
||||
|
||||
use SpomkyLabs\Pki\X501\StringPrep\StringPreparer;
|
||||
|
||||
/**
|
||||
* Implements 'caseIgnoreMatch' matching rule.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4517#section-4.2.11
|
||||
*/
|
||||
final class CaseIgnoreMatch extends StringPrepMatchingRule
|
||||
{
|
||||
/**
|
||||
* @param int $stringType ASN.1 string type tag
|
||||
*/
|
||||
private function __construct(int $stringType)
|
||||
{
|
||||
parent::__construct(
|
||||
StringPreparer::forStringType($stringType)->withCaseFolding(true)
|
||||
);
|
||||
}
|
||||
|
||||
public static function create(int $stringType): self
|
||||
{
|
||||
return new self($stringType);
|
||||
}
|
||||
}
|
||||
24
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/MatchingRule.php
vendored
Normal file
24
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/MatchingRule.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\MatchingRule;
|
||||
|
||||
/**
|
||||
* Base class for attribute matching rules.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4517#section-4
|
||||
*/
|
||||
abstract class MatchingRule
|
||||
{
|
||||
/**
|
||||
* Compare attribute value to assertion.
|
||||
*
|
||||
* @param string $assertion Value to assert
|
||||
* @param string $value Attribute value
|
||||
*
|
||||
* @return null|bool True if value matches. Null shall be returned if match
|
||||
* evaluates to Undefined.
|
||||
*/
|
||||
abstract public function compare(string $assertion, string $value): ?bool;
|
||||
}
|
||||
25
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/StringPrepMatchingRule.php
vendored
Normal file
25
libraries/vendor/spomky-labs/pki-framework/src/X501/MatchingRule/StringPrepMatchingRule.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\MatchingRule;
|
||||
|
||||
use SpomkyLabs\Pki\X501\StringPrep\StringPreparer;
|
||||
|
||||
/**
|
||||
* Base class for matching rules employing string preparement semantics.
|
||||
*/
|
||||
abstract class StringPrepMatchingRule extends MatchingRule
|
||||
{
|
||||
protected function __construct(
|
||||
private readonly StringPreparer $preparer
|
||||
) {
|
||||
}
|
||||
|
||||
public function compare(string $assertion, string $value): ?bool
|
||||
{
|
||||
$assertion = $this->preparer->prepare($assertion);
|
||||
$value = $this->preparer->prepare($value);
|
||||
return strcmp($assertion, $value) === 0;
|
||||
}
|
||||
}
|
||||
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/CheckBidiStep.php
vendored
Normal file
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/CheckBidiStep.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
/**
|
||||
* Implements 'Check bidi' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.5
|
||||
*/
|
||||
final class CheckBidiStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* @param string $string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
// @todo Implement
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
/**
|
||||
* Implements 'Insignificant Space Handling' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* This variant handles input strings that are non-substring assertion values.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.6.1
|
||||
*/
|
||||
final class InsignificantNonSubstringSpaceStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* @param string $string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
// if value contains no non-space characters
|
||||
if (preg_match('/^\p{Zs}*$/u', $string) === 1) {
|
||||
return ' ';
|
||||
}
|
||||
// trim leading and trailing spaces
|
||||
$string = preg_replace('/^\p{Zs}+/u', '', $string);
|
||||
$string = preg_replace('/\p{Zs}+$/u', '', (string) $string);
|
||||
// convert inner space sequences to two U+0020 characters
|
||||
$string = preg_replace('/\p{Zs}+/u', ' ', (string) $string);
|
||||
return " {$string} ";
|
||||
}
|
||||
}
|
||||
40
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/MapStep.php
vendored
Normal file
40
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/MapStep.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
use const MB_CASE_LOWER;
|
||||
|
||||
/**
|
||||
* Implements 'Map' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.2
|
||||
*/
|
||||
final class MapStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* @param bool $fold Whether to apply case folding
|
||||
*/
|
||||
private function __construct(
|
||||
protected bool $fold
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(bool $fold = false): self
|
||||
{
|
||||
return new self($fold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
// @todo Implement character mappings
|
||||
if ($this->fold) {
|
||||
$string = mb_convert_case($string, MB_CASE_LOWER, 'UTF-8');
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
23
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/NormalizeStep.php
vendored
Normal file
23
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/NormalizeStep.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
use Normalizer;
|
||||
|
||||
/**
|
||||
* Implements 'Normalize' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.3
|
||||
*/
|
||||
final class NormalizeStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* @param string $string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
return normalizer_normalize($string, Normalizer::NFKC);
|
||||
}
|
||||
}
|
||||
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/PrepareStep.php
vendored
Normal file
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/PrepareStep.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
/**
|
||||
* Interface for string preparation steps of Internationalized String Preparation algorithm specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2
|
||||
*/
|
||||
interface PrepareStep
|
||||
{
|
||||
/**
|
||||
* Apply string preparation step.
|
||||
*
|
||||
* @param string $string String to prepare
|
||||
*
|
||||
* @return string Prepared string
|
||||
*/
|
||||
public function apply(string $string): string;
|
||||
}
|
||||
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/ProhibitStep.php
vendored
Normal file
22
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/ProhibitStep.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
/**
|
||||
* Implements 'Prohibit' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.4
|
||||
*/
|
||||
final class ProhibitStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* @param string $string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
// @todo Implement
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
78
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/StringPreparer.php
vendored
Normal file
78
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/StringPreparer.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
/**
|
||||
* Implement Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518
|
||||
*/
|
||||
final class StringPreparer
|
||||
{
|
||||
final public const STEP_TRANSCODE = 1;
|
||||
|
||||
final public const STEP_MAP = 2;
|
||||
|
||||
final public const STEP_NORMALIZE = 3;
|
||||
|
||||
final public const STEP_PROHIBIT = 4;
|
||||
|
||||
final public const STEP_CHECK_BIDI = 5;
|
||||
|
||||
final public const STEP_INSIGNIFICANT_CHARS = 6;
|
||||
|
||||
/**
|
||||
* @param PrepareStep[] $_steps Preparation steps to apply
|
||||
*/
|
||||
private function __construct(
|
||||
/**
|
||||
* Preparation steps.
|
||||
*/
|
||||
protected array $_steps
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default instance for given string type.
|
||||
*
|
||||
* @param int $string_type ASN.1 string type tag.
|
||||
*/
|
||||
public static function forStringType(int $string_type): self
|
||||
{
|
||||
$steps = [
|
||||
self::STEP_TRANSCODE => TranscodeStep::create($string_type),
|
||||
self::STEP_MAP => MapStep::create(),
|
||||
self::STEP_NORMALIZE => new NormalizeStep(),
|
||||
self::STEP_PROHIBIT => new ProhibitStep(),
|
||||
self::STEP_CHECK_BIDI => new CheckBidiStep(),
|
||||
// @todo Vary by string type
|
||||
self::STEP_INSIGNIFICANT_CHARS => new InsignificantNonSubstringSpaceStep(),
|
||||
];
|
||||
return new self($steps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get self with case folding set.
|
||||
*
|
||||
* @param bool $fold True to apply case folding
|
||||
*/
|
||||
public function withCaseFolding(bool $fold): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
$obj->_steps[self::STEP_MAP] = MapStep::create($fold);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare string.
|
||||
*/
|
||||
public function prepare(string $string): string
|
||||
{
|
||||
foreach ($this->_steps as $step) {
|
||||
$string = $step->apply($string);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
82
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/TranscodeStep.php
vendored
Normal file
82
libraries/vendor/spomky-labs/pki-framework/src/X501/StringPrep/TranscodeStep.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SpomkyLabs\Pki\X501\StringPrep;
|
||||
|
||||
use LogicException;
|
||||
use SpomkyLabs\Pki\ASN1\Element;
|
||||
use SpomkyLabs\Pki\ASN1\Type\Primitive\T61String;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* Implements 'Transcode' step of the Internationalized String Preparation as specified by RFC 4518.
|
||||
*
|
||||
* @see https://tools.ietf.org/html/rfc4518#section-2.1
|
||||
*/
|
||||
final class TranscodeStep implements PrepareStep
|
||||
{
|
||||
/**
|
||||
* Supported ASN.1 types.
|
||||
*
|
||||
* @var array<int>
|
||||
*/
|
||||
private const SUPPORTED_TYPES = [
|
||||
Element::TYPE_UTF8_STRING,
|
||||
Element::TYPE_PRINTABLE_STRING,
|
||||
Element::TYPE_BMP_STRING,
|
||||
Element::TYPE_UNIVERSAL_STRING,
|
||||
Element::TYPE_T61_STRING,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param int $_type ASN.1 type tag of the string
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly int $_type
|
||||
) {
|
||||
}
|
||||
|
||||
public static function create(int $_type): self
|
||||
{
|
||||
return new self($_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether transcoding from given ASN.1 type tag is supported.
|
||||
*
|
||||
* @param int $type ASN.1 type tag
|
||||
*/
|
||||
public static function isTypeSupported(int $type): bool
|
||||
{
|
||||
return in_array($type, self::SUPPORTED_TYPES, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string String to prepare
|
||||
*
|
||||
* @return string UTF-8 encoded string
|
||||
*/
|
||||
public function apply(string $string): string
|
||||
{
|
||||
switch ($this->_type) {
|
||||
// UTF-8 string as is
|
||||
case Element::TYPE_UTF8_STRING:
|
||||
// PrintableString maps directly to UTF-8
|
||||
case Element::TYPE_PRINTABLE_STRING:
|
||||
return $string;
|
||||
// UCS-2 to UTF-8
|
||||
case Element::TYPE_BMP_STRING:
|
||||
return mb_convert_encoding($string, 'UTF-8', 'UCS-2BE');
|
||||
// UCS-4 to UTF-8
|
||||
case Element::TYPE_UNIVERSAL_STRING:
|
||||
return mb_convert_encoding($string, 'UTF-8', 'UCS-4BE');
|
||||
// TeletexString mapping is a local matter.
|
||||
// We take a shortcut here and encode it as a hexstring.
|
||||
case Element::TYPE_T61_STRING:
|
||||
$el = T61String::create($string);
|
||||
return '#' . bin2hex($el->toDER());
|
||||
}
|
||||
throw new LogicException(sprintf('Unsupported string type %s.', Element::tagToName($this->_type)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user