Identifier.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <?php
  2. /*
  3. * This file is part of the PHPASN1 library.
  4. *
  5. * Copyright © Friedrich Große <friedrich.grosse@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FG\ASN1;
  11. use Exception;
  12. /**
  13. * The Identifier encodes the ASN.1 tag (class and number) of the type of a data value.
  14. *
  15. * Every identifier whose number is in the range 0 to 30 has the following structure:
  16. *
  17. * Bits: 8 7 6 5 4 3 2 1
  18. * | Class | P/C | Tag number |
  19. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  20. *
  21. * Bits 8 and 7 define the class of this type ( Universal, Application, Context-specific or Private).
  22. * Bit 6 encoded whether this type is primitive or constructed
  23. * The remaining bits 5 - 1 encode the tag number
  24. */
  25. class Identifier
  26. {
  27. const CLASS_UNIVERSAL = 0x00;
  28. const CLASS_APPLICATION = 0x01;
  29. const CLASS_CONTEXT_SPECIFIC = 0x02;
  30. const CLASS_PRIVATE = 0x03;
  31. const EOC = 0x00; // unsupported for now
  32. const BOOLEAN = 0x01;
  33. const INTEGER = 0x02;
  34. const BITSTRING = 0x03;
  35. const OCTETSTRING = 0x04;
  36. const NULL = 0x05;
  37. const OBJECT_IDENTIFIER = 0x06;
  38. const OBJECT_DESCRIPTOR = 0x07;
  39. const EXTERNAL = 0x08; // unsupported for now
  40. const REAL = 0x09; // unsupported for now
  41. const ENUMERATED = 0x0A;
  42. const EMBEDDED_PDV = 0x0B; // unsupported for now
  43. const UTF8_STRING = 0x0C;
  44. const RELATIVE_OID = 0x0D;
  45. // value 0x0E and 0x0F are reserved for future use
  46. const SEQUENCE = 0x30;
  47. const SET = 0x31;
  48. const NUMERIC_STRING = 0x12;
  49. const PRINTABLE_STRING = 0x13;
  50. const T61_STRING = 0x14; // sometimes referred to as TeletextString
  51. const VIDEOTEXT_STRING = 0x15;
  52. const IA5_STRING = 0x16;
  53. const UTC_TIME = 0x17;
  54. const GENERALIZED_TIME = 0x18;
  55. const GRAPHIC_STRING = 0x19;
  56. const VISIBLE_STRING = 0x1A;
  57. const GENERAL_STRING = 0x1B;
  58. const UNIVERSAL_STRING = 0x1C;
  59. const CHARACTER_STRING = 0x1D; // Unrestricted character type
  60. const BMP_STRING = 0x1E;
  61. const LONG_FORM = 0x1F;
  62. const IS_CONSTRUCTED = 0x20;
  63. /**
  64. * Creates an identifier. Short form identifiers are returned as integers
  65. * for BC, long form identifiers will be returned as a string of octets.
  66. *
  67. * @param int $class
  68. * @param bool $isConstructed
  69. * @param int $tagNumber
  70. *
  71. * @throws Exception if the given arguments are invalid
  72. *
  73. * @return int|string
  74. */
  75. public static function create($class, $isConstructed, $tagNumber)
  76. {
  77. if (!is_numeric($class) || $class < self::CLASS_UNIVERSAL || $class > self::CLASS_PRIVATE) {
  78. throw new Exception(sprintf('Invalid class %d given', $class));
  79. }
  80. if (!is_bool($isConstructed)) {
  81. throw new Exception("\$isConstructed must be a boolean value ($isConstructed given)");
  82. }
  83. $tagNumber = self::makeNumeric($tagNumber);
  84. if ($tagNumber < 0) {
  85. throw new Exception(sprintf('Invalid $tagNumber %d given. You can only use positive integers.', $tagNumber));
  86. }
  87. if ($tagNumber < self::LONG_FORM) {
  88. return ($class << 6) | ($isConstructed << 5) | $tagNumber;
  89. }
  90. $firstOctet = ($class << 6) | ($isConstructed << 5) | self::LONG_FORM;
  91. // Tag numbers formatted in long form are base-128 encoded. See X.609#8.1.2.4
  92. return chr($firstOctet).Base128::encode($tagNumber);
  93. }
  94. public static function isConstructed($identifierOctet)
  95. {
  96. return ($identifierOctet & self::IS_CONSTRUCTED) === self::IS_CONSTRUCTED;
  97. }
  98. public static function isLongForm($identifierOctet)
  99. {
  100. return ($identifierOctet & self::LONG_FORM) === self::LONG_FORM;
  101. }
  102. /**
  103. * Return the name of the mapped ASN.1 type with a preceding "ASN.1 ".
  104. *
  105. * Example: ASN.1 Octet String
  106. *
  107. * @see Identifier::getShortName()
  108. *
  109. * @param int|string $identifier
  110. *
  111. * @return string
  112. */
  113. public static function getName($identifier)
  114. {
  115. $identifierOctet = self::makeNumeric($identifier);
  116. $typeName = static::getShortName($identifier);
  117. if (($identifierOctet & self::LONG_FORM) < self::LONG_FORM) {
  118. $typeName = "ASN.1 {$typeName}";
  119. }
  120. return $typeName;
  121. }
  122. /**
  123. * Return the short version of the type name.
  124. *
  125. * If the given identifier octet can be mapped to a known universal type this will
  126. * return its name. Else Identifier::getClassDescription() is used to retrieve
  127. * information about the identifier.
  128. *
  129. * @see Identifier::getName()
  130. * @see Identifier::getClassDescription()
  131. *
  132. * @param int|string $identifier
  133. *
  134. * @return string
  135. */
  136. public static function getShortName($identifier)
  137. {
  138. $identifierOctet = self::makeNumeric($identifier);
  139. switch ($identifierOctet) {
  140. case self::EOC:
  141. return 'End-of-contents octet';
  142. case self::BOOLEAN:
  143. return 'Boolean';
  144. case self::INTEGER:
  145. return 'Integer';
  146. case self::BITSTRING:
  147. return 'Bit String';
  148. case self::OCTETSTRING:
  149. return 'Octet String';
  150. case self::NULL:
  151. return 'NULL';
  152. case self::OBJECT_IDENTIFIER:
  153. return 'Object Identifier';
  154. case self::OBJECT_DESCRIPTOR:
  155. return 'Object Descriptor';
  156. case self::EXTERNAL:
  157. return 'External Type';
  158. case self::REAL:
  159. return 'Real';
  160. case self::ENUMERATED:
  161. return 'Enumerated';
  162. case self::EMBEDDED_PDV:
  163. return 'Embedded PDV';
  164. case self::UTF8_STRING:
  165. return 'UTF8 String';
  166. case self::RELATIVE_OID:
  167. return 'Relative OID';
  168. case self::SEQUENCE:
  169. return 'Sequence';
  170. case self::SET:
  171. return 'Set';
  172. case self::NUMERIC_STRING:
  173. return 'Numeric String';
  174. case self::PRINTABLE_STRING:
  175. return 'Printable String';
  176. case self::T61_STRING:
  177. return 'T61 String';
  178. case self::VIDEOTEXT_STRING:
  179. return 'Videotext String';
  180. case self::IA5_STRING:
  181. return 'IA5 String';
  182. case self::UTC_TIME:
  183. return 'UTC Time';
  184. case self::GENERALIZED_TIME:
  185. return 'Generalized Time';
  186. case self::GRAPHIC_STRING:
  187. return 'Graphic String';
  188. case self::VISIBLE_STRING:
  189. return 'Visible String';
  190. case self::GENERAL_STRING:
  191. return 'General String';
  192. case self::UNIVERSAL_STRING:
  193. return 'Universal String';
  194. case self::CHARACTER_STRING:
  195. return 'Character String';
  196. case self::BMP_STRING:
  197. return 'BMP String';
  198. case 0x0E:
  199. return 'RESERVED (0x0E)';
  200. case 0x0F:
  201. return 'RESERVED (0x0F)';
  202. case self::LONG_FORM:
  203. default:
  204. $classDescription = self::getClassDescription($identifier);
  205. if (is_int($identifier)) {
  206. $identifier = chr($identifier);
  207. }
  208. return "$classDescription (0x".strtoupper(bin2hex($identifier)).')';
  209. }
  210. }
  211. /**
  212. * Returns a textual description of the information encoded in a given identifier octet.
  213. *
  214. * The first three (most significant) bytes are evaluated to determine if this is a
  215. * constructed or primitive type and if it is either universal, application, context-specific or
  216. * private.
  217. *
  218. * Example:
  219. * Constructed context-specific
  220. * Primitive universal
  221. *
  222. * @param int|string $identifier
  223. *
  224. * @return string
  225. */
  226. public static function getClassDescription($identifier)
  227. {
  228. $identifierOctet = self::makeNumeric($identifier);
  229. if (self::isConstructed($identifierOctet)) {
  230. $classDescription = 'Constructed ';
  231. } else {
  232. $classDescription = 'Primitive ';
  233. }
  234. $classBits = $identifierOctet >> 6;
  235. switch ($classBits) {
  236. case self::CLASS_UNIVERSAL:
  237. $classDescription .= 'universal';
  238. break;
  239. case self::CLASS_APPLICATION:
  240. $classDescription .= 'application';
  241. break;
  242. case self::CLASS_CONTEXT_SPECIFIC:
  243. $tagNumber = self::getTagNumber($identifier);
  244. $classDescription = "[$tagNumber] Context-specific";
  245. break;
  246. case self::CLASS_PRIVATE:
  247. $classDescription .= 'private';
  248. break;
  249. default:
  250. return "INVALID IDENTIFIER OCTET: {$identifierOctet}";
  251. }
  252. return $classDescription;
  253. }
  254. /**
  255. * @param int|string $identifier
  256. *
  257. * @return int
  258. */
  259. public static function getTagNumber($identifier)
  260. {
  261. $firstOctet = self::makeNumeric($identifier);
  262. $tagNumber = $firstOctet & self::LONG_FORM;
  263. if ($tagNumber < self::LONG_FORM) {
  264. return $tagNumber;
  265. }
  266. if (is_numeric($identifier)) {
  267. $identifier = chr($identifier);
  268. }
  269. return Base128::decode(substr($identifier, 1));
  270. }
  271. public static function isUniversalClass($identifier)
  272. {
  273. $identifier = self::makeNumeric($identifier);
  274. return $identifier >> 6 == self::CLASS_UNIVERSAL;
  275. }
  276. public static function isApplicationClass($identifier)
  277. {
  278. $identifier = self::makeNumeric($identifier);
  279. return $identifier >> 6 == self::CLASS_APPLICATION;
  280. }
  281. public static function isContextSpecificClass($identifier)
  282. {
  283. $identifier = self::makeNumeric($identifier);
  284. return $identifier >> 6 == self::CLASS_CONTEXT_SPECIFIC;
  285. }
  286. public static function isPrivateClass($identifier)
  287. {
  288. $identifier = self::makeNumeric($identifier);
  289. return $identifier >> 6 == self::CLASS_PRIVATE;
  290. }
  291. private static function makeNumeric($identifierOctet)
  292. {
  293. if (!is_numeric($identifierOctet)) {
  294. return ord($identifierOctet);
  295. } else {
  296. return $identifierOctet;
  297. }
  298. }
  299. }