123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- <?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;
- }
- }
- }
|