340 lines
11 KiB
PHP
340 lines
11 KiB
PHP
![]() |
<?php
|
||
|
/*
|
||
|
* This file is part of the PHPASN1 library.
|
||
|
*
|
||
|
* Copyright © Friedrich Große <friedrich.grosse@gmail.com>
|
||
|
*
|
||
|
* For the full copyright and license information, please view the LICENSE
|
||
|
* file that was distributed with this source code.
|
||
|
*/
|
||
|
|
||
|
namespace FG\ASN1;
|
||
|
|
||
|
use Exception;
|
||
|
|
||
|
/**
|
||
|
* The Identifier encodes the ASN.1 tag (class and number) of the type of a data value.
|
||
|
*
|
||
|
* Every identifier whose number is in the range 0 to 30 has the following structure:
|
||
|
*
|
||
|
* Bits: 8 7 6 5 4 3 2 1
|
||
|
* | Class | P/C | Tag number |
|
||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
*
|
||
|
* Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private).
|
||
|
* Bit 6 encoded whether this type is primitive or constructed
|
||
|
* The remaining bits 5 - 1 encode the tag number
|
||
|
*/
|
||
|
class Identifier
|
||
|
{
|
||
|
const CLASS_UNIVERSAL = 0x00;
|
||
|
const CLASS_APPLICATION = 0x01;
|
||
|
const CLASS_CONTEXT_SPECIFIC = 0x02;
|
||
|
const CLASS_PRIVATE = 0x03;
|
||
|
|
||
|
const EOC = 0x00; // unsupported for now
|
||
|
const BOOLEAN = 0x01;
|
||
|
const INTEGER = 0x02;
|
||
|
const BITSTRING = 0x03;
|
||
|
const OCTETSTRING = 0x04;
|
||
|
const NULL = 0x05;
|
||
|
const OBJECT_IDENTIFIER = 0x06;
|
||
|
const OBJECT_DESCRIPTOR = 0x07;
|
||
|
const EXTERNAL = 0x08; // unsupported for now
|
||
|
const REAL = 0x09; // unsupported for now
|
||
|
const ENUMERATED = 0x0A;
|
||
|
const EMBEDDED_PDV = 0x0B; // unsupported for now
|
||
|
const UTF8_STRING = 0x0C;
|
||
|
const RELATIVE_OID = 0x0D;
|
||
|
// value 0x0E and 0x0F are reserved for future use
|
||
|
|
||
|
const SEQUENCE = 0x30;
|
||
|
const SET = 0x31;
|
||
|
const NUMERIC_STRING = 0x12;
|
||
|
const PRINTABLE_STRING = 0x13;
|
||
|
const T61_STRING = 0x14; // sometimes referred to as TeletextString
|
||
|
const VIDEOTEXT_STRING = 0x15;
|
||
|
const IA5_STRING = 0x16;
|
||
|
const UTC_TIME = 0x17;
|
||
|
const GENERALIZED_TIME = 0x18;
|
||
|
const GRAPHIC_STRING = 0x19;
|
||
|
const VISIBLE_STRING = 0x1A;
|
||
|
const GENERAL_STRING = 0x1B;
|
||
|
const UNIVERSAL_STRING = 0x1C;
|
||
|
const CHARACTER_STRING = 0x1D; // Unrestricted character type
|
||
|
const BMP_STRING = 0x1E;
|
||
|
|
||
|
const LONG_FORM = 0x1F;
|
||
|
const IS_CONSTRUCTED = 0x20;
|
||
|
|
||
|
/**
|
||
|
* Creates an identifier. Short form identifiers are returned as integers
|
||
|
* for BC, long form identifiers will be returned as a string of octets.
|
||
|
*
|
||
|
* @param int $class
|
||
|
* @param bool $isConstructed
|
||
|
* @param int $tagNumber
|
||
|
*
|
||
|
* @throws Exception if the given arguments are invalid
|
||
|
*
|
||
|
* @return int|string
|
||
|
*/
|
||
|
public static function create($class, $isConstructed, $tagNumber)
|
||
|
{
|
||
|
if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) {
|
||
|
throw new Exception(sprintf('Invalid class %d given', $class));
|
||
|
}
|
||
|
|
||
|
if (!is_bool($isConstructed)) {
|
||
|
throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)");
|
||
|
}
|
||
|
|
||
|
$tagNumber = self::makeNumeric($tagNumber);
|
||
|
if ($tagNumber < 0) {
|
||
|
throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber));
|
||
|
}
|
||
|
|
||
|
if ($tagNumber < self::LONG_FORM) {
|
||
|
return ($class << 6) | ($isConstructed << 5) | $tagNumber;
|
||
|
}
|
||
|
|
||
|
$firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM;
|
||
|
|
||
|
// Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4
|
||
|
return chr($firstOctet).Base128::encode($tagNumber);
|
||
|
}
|
||
|
|
||
|
public static function isConstructed($identifierOctet)
|
||
|
{
|
||
|
return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED;
|
||
|
}
|
||
|
|
||
|
public static function isLongForm($identifierOctet)
|
||
|
{
|
||
|
return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the name of the mapped ASN.1 type with a preceding "ASN.1 ".
|
||
|
*
|
||
|
* Example: ASN.1 Octet String
|
||
|
*
|
||
|
* @see Identifier::getShortName()
|
||
|
*
|
||
|
* @param int|string $identifier
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function getName($identifier)
|
||
|
{
|
||
|
$identifierOctet = self::makeNumeric($identifier);
|
||
|
|
||
|
$typeName = static::getShortName($identifier);
|
||
|
|
||
|
if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) {
|
||
|
$typeName = "ASN.1 {$typeName}";
|
||
|
}
|
||
|
|
||
|
return $typeName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the short version of the type name.
|
||
|
*
|
||
|
* If the given identifier octet can be mapped to a known universal type this will
|
||
|
* return its name. Else Identifier::getClassDescription() is used to retrieve
|
||
|
* information about the identifier.
|
||
|
*
|
||
|
* @see Identifier::getName()
|
||
|
* @see Identifier::getClassDescription()
|
||
|
*
|
||
|
* @param int|string $identifier
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function getShortName($identifier)
|
||
|
{
|
||
|
$identifierOctet = self::makeNumeric($identifier);
|
||
|
|
||
|
switch ($identifierOctet) {
|
||
|
case self::EOC:
|
||
|
return 'End-of-contents octet';
|
||
|
case self::BOOLEAN:
|
||
|
return 'Boolean';
|
||
|
case self::INTEGER:
|
||
|
return 'Integer';
|
||
|
case self::BITSTRING:
|
||
|
return 'Bit String';
|
||
|
case self::OCTETSTRING:
|
||
|
return 'Octet String';
|
||
|
case self::NULL:
|
||
|
return 'NULL';
|
||
|
case self::OBJECT_IDENTIFIER:
|
||
|
return 'Object Identifier';
|
||
|
case self::OBJECT_DESCRIPTOR:
|
||
|
return 'Object Descriptor';
|
||
|
case self::EXTERNAL:
|
||
|
return 'External Type';
|
||
|
case self::REAL:
|
||
|
return 'Real';
|
||
|
case self::ENUMERATED:
|
||
|
return 'Enumerated';
|
||
|
case self::EMBEDDED_PDV:
|
||
|
return 'Embedded PDV';
|
||
|
case self::UTF8_STRING:
|
||
|
return 'UTF8 String';
|
||
|
case self::RELATIVE_OID:
|
||
|
return 'Relative OID';
|
||
|
case self::SEQUENCE:
|
||
|
return 'Sequence';
|
||
|
case self::SET:
|
||
|
return 'Set';
|
||
|
case self::NUMERIC_STRING:
|
||
|
return 'Numeric String';
|
||
|
case self::PRINTABLE_STRING:
|
||
|
return 'Printable String';
|
||
|
case self::T61_STRING:
|
||
|
return 'T61 String';
|
||
|
case self::VIDEOTEXT_STRING:
|
||
|
return 'Videotext String';
|
||
|
case self::IA5_STRING:
|
||
|
return 'IA5 String';
|
||
|
case self::UTC_TIME:
|
||
|
return 'UTC Time';
|
||
|
case self::GENERALIZED_TIME:
|
||
|
return 'Generalized Time';
|
||
|
case self::GRAPHIC_STRING:
|
||
|
return 'Graphic String';
|
||
|
case self::VISIBLE_STRING:
|
||
|
return 'Visible String';
|
||
|
case self::GENERAL_STRING:
|
||
|
return 'General String';
|
||
|
case self::UNIVERSAL_STRING:
|
||
|
return 'Universal String';
|
||
|
case self::CHARACTER_STRING:
|
||
|
return 'Character String';
|
||
|
case self::BMP_STRING:
|
||
|
return 'BMP String';
|
||
|
|
||
|
case 0x0E:
|
||
|
return 'RESERVED (0x0E)';
|
||
|
case 0x0F:
|
||
|
return 'RESERVED (0x0F)';
|
||
|
|
||
|
case self::LONG_FORM:
|
||
|
default:
|
||
|
$classDescription = self::getClassDescription($identifier);
|
||
|
|
||
|
if (is_int($identifier)) {
|
||
|
$identifier = chr($identifier);
|
||
|
}
|
||
|
|
||
|
return "$classDescription (0x".strtoupper(bin2hex($identifier)).')';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a textual description of the information encoded in a given identifier octet.
|
||
|
*
|
||
|
* The first three (most significant) bytes are evaluated to determine if this is a
|
||
|
* constructed or primitive type and if it is either universal, application, context-specific or
|
||
|
* private.
|
||
|
*
|
||
|
* Example:
|
||
|
* Constructed context-specific
|
||
|
* Primitive universal
|
||
|
*
|
||
|
* @param int|string $identifier
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function getClassDescription($identifier)
|
||
|
{
|
||
|
$identifierOctet = self::makeNumeric($identifier);
|
||
|
|
||
|
if (self::isConstructed($identifierOctet)) {
|
||
|
$classDescription = 'Constructed ';
|
||
|
} else {
|
||
|
$classDescription = 'Primitive ';
|
||
|
}
|
||
|
$classBits = $identifierOctet >> 6;
|
||
|
switch ($classBits) {
|
||
|
case self::CLASS_UNIVERSAL:
|
||
|
$classDescription .= 'universal';
|
||
|
break;
|
||
|
case self::CLASS_APPLICATION:
|
||
|
$classDescription .= 'application';
|
||
|
break;
|
||
|
case self::CLASS_CONTEXT_SPECIFIC:
|
||
|
$tagNumber = self::getTagNumber($identifier);
|
||
|
$classDescription = "[$tagNumber] Context-specific";
|
||
|
break;
|
||
|
case self::CLASS_PRIVATE:
|
||
|
$classDescription .= 'private';
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return "INVALID IDENTIFIER OCTET: {$identifierOctet}";
|
||
|
}
|
||
|
|
||
|
return $classDescription;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int|string $identifier
|
||
|
*
|
||
|
* @return int
|
||
|
*/
|
||
|
public static function getTagNumber($identifier)
|
||
|
{
|
||
|
$firstOctet = self::makeNumeric($identifier);
|
||
|
$tagNumber = $firstOctet & self::LONG_FORM;
|
||
|
|
||
|
if ($tagNumber < self::LONG_FORM) {
|
||
|
return $tagNumber;
|
||
|
}
|
||
|
|
||
|
if (is_numeric($identifier)) {
|
||
|
$identifier = chr($identifier);
|
||
|
}
|
||
|
return Base128::decode(substr($identifier, 1));
|
||
|
}
|
||
|
|
||
|
public static function isUniversalClass($identifier)
|
||
|
{
|
||
|
$identifier = self::makeNumeric($identifier);
|
||
|
|
||
|
return $identifier >> 6 == self::CLASS_UNIVERSAL;
|
||
|
}
|
||
|
|
||
|
public static function isApplicationClass($identifier)
|
||
|
{
|
||
|
$identifier = self::makeNumeric($identifier);
|
||
|
|
||
|
return $identifier >> 6 == self::CLASS_APPLICATION;
|
||
|
}
|
||
|
|
||
|
public static function isContextSpecificClass($identifier)
|
||
|
{
|
||
|
$identifier = self::makeNumeric($identifier);
|
||
|
|
||
|
return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC;
|
||
|
}
|
||
|
|
||
|
public static function isPrivateClass($identifier)
|
||
|
{
|
||
|
$identifier = self::makeNumeric($identifier);
|
||
|
|
||
|
return $identifier >> 6 == self::CLASS_PRIVATE;
|
||
|
}
|
||
|
|
||
|
private static function makeNumeric($identifierOctet)
|
||
|
{
|
||
|
if (!is_numeric($identifierOctet)) {
|
||
|
return ord($identifierOctet);
|
||
|
} else {
|
||
|
return $identifierOctet;
|
||
|
}
|
||
|
}
|
||
|
}
|