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