PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php

https://bitbucket.org/openemr/openemr
PHP | 1308 lines | 846 code | 100 blank | 362 comment | 189 complexity | 319920a17b9b41873a25140eb580567f MD5 | raw file
Possible License(s): Apache-2.0, AGPL-1.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, Unlicense, MPL-2.0, GPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Pure-PHP ASN.1 Parser
  4. *
  5. * PHP version 5
  6. *
  7. * ASN.1 provides the semantics for data encoded using various schemes. The most commonly
  8. * utilized scheme is DER or the "Distinguished Encoding Rules". PEM's are base64 encoded
  9. * DER blobs.
  10. *
  11. * \phpseclib\File\ASN1 decodes and encodes DER formatted messages and places them in a semantic context.
  12. *
  13. * Uses the 1988 ASN.1 syntax.
  14. *
  15. * @category File
  16. * @package ASN1
  17. * @author Jim Wigginton <terrafrost@php.net>
  18. * @copyright 2012 Jim Wigginton
  19. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  20. * @link http://phpseclib.sourceforge.net
  21. */
  22. namespace phpseclib\File;
  23. use phpseclib\File\ASN1\Element;
  24. use phpseclib\Math\BigInteger;
  25. /**
  26. * Pure-PHP ASN.1 Parser
  27. *
  28. * @package ASN1
  29. * @author Jim Wigginton <terrafrost@php.net>
  30. * @access public
  31. */
  32. class ASN1
  33. {
  34. /**#@+
  35. * Tag Classes
  36. *
  37. * @access private
  38. * @link http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=12
  39. */
  40. const CLASS_UNIVERSAL = 0;
  41. const CLASS_APPLICATION = 1;
  42. const CLASS_CONTEXT_SPECIFIC = 2;
  43. const CLASS_PRIVATE = 3;
  44. /**#@-*/
  45. /**#@+
  46. * Tag Classes
  47. *
  48. * @access private
  49. * @link http://www.obj-sys.com/asn1tutorial/node124.html
  50. */
  51. const TYPE_BOOLEAN = 1;
  52. const TYPE_INTEGER = 2;
  53. const TYPE_BIT_STRING = 3;
  54. const TYPE_OCTET_STRING = 4;
  55. const TYPE_NULL = 5;
  56. const TYPE_OBJECT_IDENTIFIER = 6;
  57. //const TYPE_OBJECT_DESCRIPTOR = 7;
  58. //const TYPE_INSTANCE_OF = 8; // EXTERNAL
  59. const TYPE_REAL = 9;
  60. const TYPE_ENUMERATED = 10;
  61. //const TYPE_EMBEDDED = 11;
  62. const TYPE_UTF8_STRING = 12;
  63. //const TYPE_RELATIVE_OID = 13;
  64. const TYPE_SEQUENCE = 16; // SEQUENCE OF
  65. const TYPE_SET = 17; // SET OF
  66. /**#@-*/
  67. /**#@+
  68. * More Tag Classes
  69. *
  70. * @access private
  71. * @link http://www.obj-sys.com/asn1tutorial/node10.html
  72. */
  73. const TYPE_NUMERIC_STRING = 18;
  74. const TYPE_PRINTABLE_STRING = 19;
  75. const TYPE_TELETEX_STRING = 20; // T61String
  76. const TYPE_VIDEOTEX_STRING = 21;
  77. const TYPE_IA5_STRING = 22;
  78. const TYPE_UTC_TIME = 23;
  79. const TYPE_GENERALIZED_TIME = 24;
  80. const TYPE_GRAPHIC_STRING = 25;
  81. const TYPE_VISIBLE_STRING = 26; // ISO646String
  82. const TYPE_GENERAL_STRING = 27;
  83. const TYPE_UNIVERSAL_STRING = 28;
  84. //const TYPE_CHARACTER_STRING = 29;
  85. const TYPE_BMP_STRING = 30;
  86. /**#@-*/
  87. /**#@+
  88. * Tag Aliases
  89. *
  90. * These tags are kinda place holders for other tags.
  91. *
  92. * @access private
  93. */
  94. const TYPE_CHOICE = -1;
  95. const TYPE_ANY = -2;
  96. /**#@-*/
  97. /**
  98. * ASN.1 object identifier
  99. *
  100. * @var array
  101. * @access private
  102. * @link http://en.wikipedia.org/wiki/Object_identifier
  103. */
  104. var $oids = array();
  105. /**
  106. * Default date format
  107. *
  108. * @var string
  109. * @access private
  110. * @link http://php.net/class.datetime
  111. */
  112. var $format = 'D, d M Y H:i:s O';
  113. /**
  114. * Default date format
  115. *
  116. * @var array
  117. * @access private
  118. * @see self::setTimeFormat()
  119. * @see self::asn1map()
  120. * @link http://php.net/class.datetime
  121. */
  122. var $encoded;
  123. /**
  124. * Filters
  125. *
  126. * If the mapping type is self::TYPE_ANY what do we actually encode it as?
  127. *
  128. * @var array
  129. * @access private
  130. * @see self::_encode_der()
  131. */
  132. var $filters;
  133. /**
  134. * Type mapping table for the ANY type.
  135. *
  136. * Structured or unknown types are mapped to a \phpseclib\File\ASN1\Element.
  137. * Unambiguous types get the direct mapping (int/real/bool).
  138. * Others are mapped as a choice, with an extra indexing level.
  139. *
  140. * @var array
  141. * @access public
  142. */
  143. var $ANYmap = array(
  144. self::TYPE_BOOLEAN => true,
  145. self::TYPE_INTEGER => true,
  146. self::TYPE_BIT_STRING => 'bitString',
  147. self::TYPE_OCTET_STRING => 'octetString',
  148. self::TYPE_NULL => 'null',
  149. self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
  150. self::TYPE_REAL => true,
  151. self::TYPE_ENUMERATED => 'enumerated',
  152. self::TYPE_UTF8_STRING => 'utf8String',
  153. self::TYPE_NUMERIC_STRING => 'numericString',
  154. self::TYPE_PRINTABLE_STRING => 'printableString',
  155. self::TYPE_TELETEX_STRING => 'teletexString',
  156. self::TYPE_VIDEOTEX_STRING => 'videotexString',
  157. self::TYPE_IA5_STRING => 'ia5String',
  158. self::TYPE_UTC_TIME => 'utcTime',
  159. self::TYPE_GENERALIZED_TIME => 'generalTime',
  160. self::TYPE_GRAPHIC_STRING => 'graphicString',
  161. self::TYPE_VISIBLE_STRING => 'visibleString',
  162. self::TYPE_GENERAL_STRING => 'generalString',
  163. self::TYPE_UNIVERSAL_STRING => 'universalString',
  164. //self::TYPE_CHARACTER_STRING => 'characterString',
  165. self::TYPE_BMP_STRING => 'bmpString'
  166. );
  167. /**
  168. * String type to character size mapping table.
  169. *
  170. * Non-convertable types are absent from this table.
  171. * size == 0 indicates variable length encoding.
  172. *
  173. * @var array
  174. * @access public
  175. */
  176. var $stringTypeSize = array(
  177. self::TYPE_UTF8_STRING => 0,
  178. self::TYPE_BMP_STRING => 2,
  179. self::TYPE_UNIVERSAL_STRING => 4,
  180. self::TYPE_PRINTABLE_STRING => 1,
  181. self::TYPE_TELETEX_STRING => 1,
  182. self::TYPE_IA5_STRING => 1,
  183. self::TYPE_VISIBLE_STRING => 1,
  184. );
  185. /**
  186. * Parse BER-encoding
  187. *
  188. * Serves a similar purpose to openssl's asn1parse
  189. *
  190. * @param string $encoded
  191. * @return array
  192. * @access public
  193. */
  194. function decodeBER($encoded)
  195. {
  196. if ($encoded instanceof Element) {
  197. $encoded = $encoded->element;
  198. }
  199. $this->encoded = $encoded;
  200. // encapsulate in an array for BC with the old decodeBER
  201. return array($this->_decode_ber($encoded));
  202. }
  203. /**
  204. * Parse BER-encoding (Helper function)
  205. *
  206. * Sometimes we want to get the BER encoding of a particular tag. $start lets us do that without having to reencode.
  207. * $encoded is passed by reference for the recursive calls done for self::TYPE_BIT_STRING and
  208. * self::TYPE_OCTET_STRING. In those cases, the indefinite length is used.
  209. *
  210. * @param string $encoded
  211. * @param int $start
  212. * @return array
  213. * @access private
  214. */
  215. function _decode_ber($encoded, $start = 0)
  216. {
  217. $current = array('start' => $start);
  218. $type = ord($this->_string_shift($encoded));
  219. $start++;
  220. $constructed = ($type >> 5) & 1;
  221. $tag = $type & 0x1F;
  222. if ($tag == 0x1F) {
  223. $tag = 0;
  224. // process septets (since the eighth bit is ignored, it's not an octet)
  225. do {
  226. $loop = ord($encoded[0]) >> 7;
  227. $tag <<= 7;
  228. $tag |= ord($this->_string_shift($encoded)) & 0x7F;
  229. $start++;
  230. } while ($loop);
  231. }
  232. // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
  233. $length = ord($this->_string_shift($encoded));
  234. $start++;
  235. if ($length == 0x80) { // indefinite length
  236. // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
  237. // immediately available." -- paragraph 8.1.3.2.c
  238. $length = strlen($encoded);
  239. } elseif ($length & 0x80) { // definite length, long form
  240. // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
  241. // support it up to four.
  242. $length&= 0x7F;
  243. $temp = $this->_string_shift($encoded, $length);
  244. // tags of indefinte length don't really have a header length; this length includes the tag
  245. $current+= array('headerlength' => $length + 2);
  246. $start+= $length;
  247. extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
  248. } else {
  249. $current+= array('headerlength' => 2);
  250. }
  251. if ($length > strlen($encoded)) {
  252. return false;
  253. }
  254. $content = $this->_string_shift($encoded, $length);
  255. // at this point $length can be overwritten. it's only accurate for definite length things as is
  256. /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
  257. built-in types. It defines an application-independent data type that must be distinguishable from all other
  258. data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
  259. have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within
  260. a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the
  261. alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this
  262. data type; the term CONTEXT-SPECIFIC does not appear.
  263. -- http://www.obj-sys.com/asn1tutorial/node12.html */
  264. $class = ($type >> 6) & 3;
  265. switch ($class) {
  266. case self::CLASS_APPLICATION:
  267. case self::CLASS_PRIVATE:
  268. case self::CLASS_CONTEXT_SPECIFIC:
  269. if (!$constructed) {
  270. return array(
  271. 'type' => $class,
  272. 'constant' => $tag,
  273. 'content' => $content,
  274. 'length' => $length + $start - $current['start']
  275. );
  276. }
  277. $newcontent = array();
  278. $remainingLength = $length;
  279. while ($remainingLength > 0) {
  280. $temp = $this->_decode_ber($content, $start);
  281. $length = $temp['length'];
  282. // end-of-content octets - see paragraph 8.1.5
  283. if (substr($content, $length, 2) == "\0\0") {
  284. $length+= 2;
  285. $start+= $length;
  286. $newcontent[] = $temp;
  287. break;
  288. }
  289. $start+= $length;
  290. $remainingLength-= $length;
  291. $newcontent[] = $temp;
  292. $this->_string_shift($content, $length);
  293. }
  294. return array(
  295. 'type' => $class,
  296. 'constant' => $tag,
  297. // the array encapsulation is for BC with the old format
  298. 'content' => $newcontent,
  299. // the only time when $content['headerlength'] isn't defined is when the length is indefinite.
  300. // the absence of $content['headerlength'] is how we know if something is indefinite or not.
  301. // technically, it could be defined to be 2 and then another indicator could be used but whatever.
  302. 'length' => $start - $current['start']
  303. ) + $current;
  304. }
  305. $current+= array('type' => $tag);
  306. // decode UNIVERSAL tags
  307. switch ($tag) {
  308. case self::TYPE_BOOLEAN:
  309. // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
  310. //if (strlen($content) != 1) {
  311. // return false;
  312. //}
  313. $current['content'] = (bool) ord($content[0]);
  314. break;
  315. case self::TYPE_INTEGER:
  316. case self::TYPE_ENUMERATED:
  317. $current['content'] = new BigInteger($content, -256);
  318. break;
  319. case self::TYPE_REAL: // not currently supported
  320. return false;
  321. case self::TYPE_BIT_STRING:
  322. // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
  323. // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
  324. // seven.
  325. if (!$constructed) {
  326. $current['content'] = $content;
  327. } else {
  328. $temp = $this->_decode_ber($content, $start);
  329. $length-= strlen($content);
  330. $last = count($temp) - 1;
  331. for ($i = 0; $i < $last; $i++) {
  332. // all subtags should be bit strings
  333. //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
  334. // return false;
  335. //}
  336. $current['content'].= substr($temp[$i]['content'], 1);
  337. }
  338. // all subtags should be bit strings
  339. //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
  340. // return false;
  341. //}
  342. $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
  343. }
  344. break;
  345. case self::TYPE_OCTET_STRING:
  346. if (!$constructed) {
  347. $current['content'] = $content;
  348. } else {
  349. $current['content'] = '';
  350. $length = 0;
  351. while (substr($content, 0, 2) != "\0\0") {
  352. $temp = $this->_decode_ber($content, $length + $start);
  353. $this->_string_shift($content, $temp['length']);
  354. // all subtags should be octet strings
  355. //if ($temp['type'] != self::TYPE_OCTET_STRING) {
  356. // return false;
  357. //}
  358. $current['content'].= $temp['content'];
  359. $length+= $temp['length'];
  360. }
  361. if (substr($content, 0, 2) == "\0\0") {
  362. $length+= 2; // +2 for the EOC
  363. }
  364. }
  365. break;
  366. case self::TYPE_NULL:
  367. // "The contents octets shall not contain any octets." -- paragraph 8.8.2
  368. //if (strlen($content)) {
  369. // return false;
  370. //}
  371. break;
  372. case self::TYPE_SEQUENCE:
  373. case self::TYPE_SET:
  374. $offset = 0;
  375. $current['content'] = array();
  376. while (strlen($content)) {
  377. // if indefinite length construction was used and we have an end-of-content string next
  378. // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2
  379. if (!isset($current['headerlength']) && substr($content, 0, 2) == "\0\0") {
  380. $length = $offset + 2; // +2 for the EOC
  381. break 2;
  382. }
  383. $temp = $this->_decode_ber($content, $start + $offset);
  384. $this->_string_shift($content, $temp['length']);
  385. $current['content'][] = $temp;
  386. $offset+= $temp['length'];
  387. }
  388. break;
  389. case self::TYPE_OBJECT_IDENTIFIER:
  390. $temp = ord($this->_string_shift($content));
  391. $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
  392. $valuen = 0;
  393. // process septets
  394. while (strlen($content)) {
  395. $temp = ord($this->_string_shift($content));
  396. $valuen <<= 7;
  397. $valuen |= $temp & 0x7F;
  398. if (~$temp & 0x80) {
  399. $current['content'].= ".$valuen";
  400. $valuen = 0;
  401. }
  402. }
  403. // the eighth bit of the last byte should not be 1
  404. //if ($temp >> 7) {
  405. // return false;
  406. //}
  407. break;
  408. /* Each character string type shall be encoded as if it had been declared:
  409. [UNIVERSAL x] IMPLICIT OCTET STRING
  410. -- X.690-0207.pdf#page=23 (paragraph 8.21.3)
  411. Per that, we're not going to do any validation. If there are any illegal characters in the string,
  412. we don't really care */
  413. case self::TYPE_NUMERIC_STRING:
  414. // 0,1,2,3,4,5,6,7,8,9, and space
  415. case self::TYPE_PRINTABLE_STRING:
  416. // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma,
  417. // hyphen, full stop, solidus, colon, equal sign, question mark
  418. case self::TYPE_TELETEX_STRING:
  419. // The Teletex character set in CCITT's T61, space, and delete
  420. // see http://en.wikipedia.org/wiki/Teletex#Character_sets
  421. case self::TYPE_VIDEOTEX_STRING:
  422. // The Videotex character set in CCITT's T.100 and T.101, space, and delete
  423. case self::TYPE_VISIBLE_STRING:
  424. // Printing character sets of international ASCII, and space
  425. case self::TYPE_IA5_STRING:
  426. // International Alphabet 5 (International ASCII)
  427. case self::TYPE_GRAPHIC_STRING:
  428. // All registered G sets, and space
  429. case self::TYPE_GENERAL_STRING:
  430. // All registered C and G sets, space and delete
  431. case self::TYPE_UTF8_STRING:
  432. // ????
  433. case self::TYPE_BMP_STRING:
  434. $current['content'] = $content;
  435. break;
  436. case self::TYPE_UTC_TIME:
  437. case self::TYPE_GENERALIZED_TIME:
  438. $current['content'] = $this->_decodeTime($content, $tag);
  439. default:
  440. }
  441. $start+= $length;
  442. // ie. length is the length of the full TLV encoding - it's not just the length of the value
  443. return $current + array('length' => $start - $current['start']);
  444. }
  445. /**
  446. * ASN.1 Map
  447. *
  448. * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
  449. *
  450. * "Special" mappings may be applied on a per tag-name basis via $special.
  451. *
  452. * @param array $decoded
  453. * @param array $mapping
  454. * @param array $special
  455. * @return array
  456. * @access public
  457. */
  458. function asn1map($decoded, $mapping, $special = array())
  459. {
  460. if (isset($mapping['explicit']) && is_array($decoded['content'])) {
  461. $decoded = $decoded['content'][0];
  462. }
  463. switch (true) {
  464. case $mapping['type'] == self::TYPE_ANY:
  465. $intype = $decoded['type'];
  466. if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) {
  467. return new Element(substr($this->encoded, $decoded['start'], $decoded['length']));
  468. }
  469. $inmap = $this->ANYmap[$intype];
  470. if (is_string($inmap)) {
  471. return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
  472. }
  473. break;
  474. case $mapping['type'] == self::TYPE_CHOICE:
  475. foreach ($mapping['children'] as $key => $option) {
  476. switch (true) {
  477. case isset($option['constant']) && $option['constant'] == $decoded['constant']:
  478. case !isset($option['constant']) && $option['type'] == $decoded['type']:
  479. $value = $this->asn1map($decoded, $option, $special);
  480. break;
  481. case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE:
  482. $v = $this->asn1map($decoded, $option, $special);
  483. if (isset($v)) {
  484. $value = $v;
  485. }
  486. }
  487. if (isset($value)) {
  488. if (isset($special[$key])) {
  489. $value = call_user_func($special[$key], $value);
  490. }
  491. return array($key => $value);
  492. }
  493. }
  494. return null;
  495. case isset($mapping['implicit']):
  496. case isset($mapping['explicit']):
  497. case $decoded['type'] == $mapping['type']:
  498. break;
  499. default:
  500. // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings,
  501. // let it through
  502. switch (true) {
  503. case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18
  504. case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30
  505. case $mapping['type'] < 18:
  506. case $mapping['type'] > 30:
  507. return null;
  508. }
  509. }
  510. if (isset($mapping['implicit'])) {
  511. $decoded['type'] = $mapping['type'];
  512. }
  513. switch ($decoded['type']) {
  514. case self::TYPE_SEQUENCE:
  515. $map = array();
  516. // ignore the min and max
  517. if (isset($mapping['min']) && isset($mapping['max'])) {
  518. $child = $mapping['children'];
  519. foreach ($decoded['content'] as $content) {
  520. if (($map[] = $this->asn1map($content, $child, $special)) === null) {
  521. return null;
  522. }
  523. }
  524. return $map;
  525. }
  526. $n = count($decoded['content']);
  527. $i = 0;
  528. foreach ($mapping['children'] as $key => $child) {
  529. $maymatch = $i < $n; // Match only existing input.
  530. if ($maymatch) {
  531. $temp = $decoded['content'][$i];
  532. if ($child['type'] != self::TYPE_CHOICE) {
  533. // Get the mapping and input class & constant.
  534. $childClass = $tempClass = self::CLASS_UNIVERSAL;
  535. $constant = null;
  536. if (isset($temp['constant'])) {
  537. $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
  538. }
  539. if (isset($child['class'])) {
  540. $childClass = $child['class'];
  541. $constant = $child['cast'];
  542. } elseif (isset($child['constant'])) {
  543. $childClass = self::CLASS_CONTEXT_SPECIFIC;
  544. $constant = $child['constant'];
  545. }
  546. if (isset($constant) && isset($temp['constant'])) {
  547. // Can only match if constants and class match.
  548. $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
  549. } else {
  550. // Can only match if no constant expected and type matches or is generic.
  551. $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
  552. }
  553. }
  554. }
  555. if ($maymatch) {
  556. // Attempt submapping.
  557. $candidate = $this->asn1map($temp, $child, $special);
  558. $maymatch = $candidate !== null;
  559. }
  560. if ($maymatch) {
  561. // Got the match: use it.
  562. if (isset($special[$key])) {
  563. $candidate = call_user_func($special[$key], $candidate);
  564. }
  565. $map[$key] = $candidate;
  566. $i++;
  567. } elseif (isset($child['default'])) {
  568. $map[$key] = $child['default']; // Use default.
  569. } elseif (!isset($child['optional'])) {
  570. return null; // Syntax error.
  571. }
  572. }
  573. // Fail mapping if all input items have not been consumed.
  574. return $i < $n ? null: $map;
  575. // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
  576. case self::TYPE_SET:
  577. $map = array();
  578. // ignore the min and max
  579. if (isset($mapping['min']) && isset($mapping['max'])) {
  580. $child = $mapping['children'];
  581. foreach ($decoded['content'] as $content) {
  582. if (($map[] = $this->asn1map($content, $child, $special)) === null) {
  583. return null;
  584. }
  585. }
  586. return $map;
  587. }
  588. for ($i = 0; $i < count($decoded['content']); $i++) {
  589. $temp = $decoded['content'][$i];
  590. $tempClass = self::CLASS_UNIVERSAL;
  591. if (isset($temp['constant'])) {
  592. $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
  593. }
  594. foreach ($mapping['children'] as $key => $child) {
  595. if (isset($map[$key])) {
  596. continue;
  597. }
  598. $maymatch = true;
  599. if ($child['type'] != self::TYPE_CHOICE) {
  600. $childClass = self::CLASS_UNIVERSAL;
  601. $constant = null;
  602. if (isset($child['class'])) {
  603. $childClass = $child['class'];
  604. $constant = $child['cast'];
  605. } elseif (isset($child['constant'])) {
  606. $childClass = self::CLASS_CONTEXT_SPECIFIC;
  607. $constant = $child['constant'];
  608. }
  609. if (isset($constant) && isset($temp['constant'])) {
  610. // Can only match if constants and class match.
  611. $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
  612. } else {
  613. // Can only match if no constant expected and type matches or is generic.
  614. $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
  615. }
  616. }
  617. if ($maymatch) {
  618. // Attempt submapping.
  619. $candidate = $this->asn1map($temp, $child, $special);
  620. $maymatch = $candidate !== null;
  621. }
  622. if (!$maymatch) {
  623. break;
  624. }
  625. // Got the match: use it.
  626. if (isset($special[$key])) {
  627. $candidate = call_user_func($special[$key], $candidate);
  628. }
  629. $map[$key] = $candidate;
  630. break;
  631. }
  632. }
  633. foreach ($mapping['children'] as $key => $child) {
  634. if (!isset($map[$key])) {
  635. if (isset($child['default'])) {
  636. $map[$key] = $child['default'];
  637. } elseif (!isset($child['optional'])) {
  638. return null;
  639. }
  640. }
  641. }
  642. return $map;
  643. case self::TYPE_OBJECT_IDENTIFIER:
  644. return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
  645. case self::TYPE_UTC_TIME:
  646. case self::TYPE_GENERALIZED_TIME:
  647. if (isset($mapping['implicit'])) {
  648. $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
  649. }
  650. return @date($this->format, $decoded['content']);
  651. case self::TYPE_BIT_STRING:
  652. if (isset($mapping['mapping'])) {
  653. $offset = ord($decoded['content'][0]);
  654. $size = (strlen($decoded['content']) - 1) * 8 - $offset;
  655. /*
  656. From X.680-0207.pdf#page=46 (21.7):
  657. "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove)
  658. arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should
  659. therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
  660. 0 bits."
  661. */
  662. $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
  663. for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
  664. $current = ord($decoded['content'][$i]);
  665. for ($j = $offset; $j < 8; $j++) {
  666. $bits[] = (bool) ($current & (1 << $j));
  667. }
  668. $offset = 0;
  669. }
  670. $values = array();
  671. $map = array_reverse($mapping['mapping']);
  672. foreach ($map as $i => $value) {
  673. if ($bits[$i]) {
  674. $values[] = $value;
  675. }
  676. }
  677. return $values;
  678. }
  679. case self::TYPE_OCTET_STRING:
  680. return base64_encode($decoded['content']);
  681. case self::TYPE_NULL:
  682. return '';
  683. case self::TYPE_BOOLEAN:
  684. return $decoded['content'];
  685. case self::TYPE_NUMERIC_STRING:
  686. case self::TYPE_PRINTABLE_STRING:
  687. case self::TYPE_TELETEX_STRING:
  688. case self::TYPE_VIDEOTEX_STRING:
  689. case self::TYPE_IA5_STRING:
  690. case self::TYPE_GRAPHIC_STRING:
  691. case self::TYPE_VISIBLE_STRING:
  692. case self::TYPE_GENERAL_STRING:
  693. case self::TYPE_UNIVERSAL_STRING:
  694. case self::TYPE_UTF8_STRING:
  695. case self::TYPE_BMP_STRING:
  696. return $decoded['content'];
  697. case self::TYPE_INTEGER:
  698. case self::TYPE_ENUMERATED:
  699. $temp = $decoded['content'];
  700. if (isset($mapping['implicit'])) {
  701. $temp = new BigInteger($decoded['content'], -256);
  702. }
  703. if (isset($mapping['mapping'])) {
  704. $temp = (int) $temp->toString();
  705. return isset($mapping['mapping'][$temp]) ?
  706. $mapping['mapping'][$temp] :
  707. false;
  708. }
  709. return $temp;
  710. }
  711. }
  712. /**
  713. * ASN.1 Encode
  714. *
  715. * DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
  716. * an ASN.1 compiler.
  717. *
  718. * "Special" mappings can be applied via $special.
  719. *
  720. * @param string $source
  721. * @param string $mapping
  722. * @param int $idx
  723. * @return string
  724. * @access public
  725. */
  726. function encodeDER($source, $mapping, $special = array())
  727. {
  728. $this->location = array();
  729. return $this->_encode_der($source, $mapping, null, $special);
  730. }
  731. /**
  732. * ASN.1 Encode (Helper function)
  733. *
  734. * @param string $source
  735. * @param string $mapping
  736. * @param int $idx
  737. * @return string
  738. * @access private
  739. */
  740. function _encode_der($source, $mapping, $idx = null, $special = array())
  741. {
  742. if ($source instanceof Element) {
  743. return $source->element;
  744. }
  745. // do not encode (implicitly optional) fields with value set to default
  746. if (isset($mapping['default']) && $source === $mapping['default']) {
  747. return '';
  748. }
  749. if (isset($idx)) {
  750. if (isset($special[$idx])) {
  751. $source = call_user_func($special[$idx], $source);
  752. }
  753. $this->location[] = $idx;
  754. }
  755. $tag = $mapping['type'];
  756. switch ($tag) {
  757. case self::TYPE_SET: // Children order is not important, thus process in sequence.
  758. case self::TYPE_SEQUENCE:
  759. $tag|= 0x20; // set the constructed bit
  760. $value = '';
  761. // ignore the min and max
  762. if (isset($mapping['min']) && isset($mapping['max'])) {
  763. $child = $mapping['children'];
  764. foreach ($source as $content) {
  765. $temp = $this->_encode_der($content, $child, null, $special);
  766. if ($temp === false) {
  767. return false;
  768. }
  769. $value.= $temp;
  770. }
  771. break;
  772. }
  773. foreach ($mapping['children'] as $key => $child) {
  774. if (!array_key_exists($key, $source)) {
  775. if (!isset($child['optional'])) {
  776. return false;
  777. }
  778. continue;
  779. }
  780. $temp = $this->_encode_der($source[$key], $child, $key, $special);
  781. if ($temp === false) {
  782. return false;
  783. }
  784. // An empty child encoding means it has been optimized out.
  785. // Else we should have at least one tag byte.
  786. if ($temp === '') {
  787. continue;
  788. }
  789. // if isset($child['constant']) is true then isset($child['optional']) should be true as well
  790. if (isset($child['constant'])) {
  791. /*
  792. From X.680-0207.pdf#page=58 (30.6):
  793. "The tagging construction specifies explicit tagging if any of the following holds:
  794. ...
  795. c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
  796. AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
  797. an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
  798. */
  799. if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
  800. $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
  801. $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
  802. } else {
  803. $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
  804. $temp = $subtag . substr($temp, 1);
  805. }
  806. }
  807. $value.= $temp;
  808. }
  809. break;
  810. case self::TYPE_CHOICE:
  811. $temp = false;
  812. foreach ($mapping['children'] as $key => $child) {
  813. if (!isset($source[$key])) {
  814. continue;
  815. }
  816. $temp = $this->_encode_der($source[$key], $child, $key, $special);
  817. if ($temp === false) {
  818. return false;
  819. }
  820. // An empty child encoding means it has been optimized out.
  821. // Else we should have at least one tag byte.
  822. if ($temp === '') {
  823. continue;
  824. }
  825. $tag = ord($temp[0]);
  826. // if isset($child['constant']) is true then isset($child['optional']) should be true as well
  827. if (isset($child['constant'])) {
  828. if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
  829. $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
  830. $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
  831. } else {
  832. $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
  833. $temp = $subtag . substr($temp, 1);
  834. }
  835. }
  836. }
  837. if (isset($idx)) {
  838. array_pop($this->location);
  839. }
  840. if ($temp && isset($mapping['cast'])) {
  841. $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
  842. }
  843. return $temp;
  844. case self::TYPE_INTEGER:
  845. case self::TYPE_ENUMERATED:
  846. if (!isset($mapping['mapping'])) {
  847. if (is_numeric($source)) {
  848. $source = new BigInteger($source);
  849. }
  850. $value = $source->toBytes(true);
  851. } else {
  852. $value = array_search($source, $mapping['mapping']);
  853. if ($value === false) {
  854. return false;
  855. }
  856. $value = new BigInteger($value);
  857. $value = $value->toBytes(true);
  858. }
  859. if (!strlen($value)) {
  860. $value = chr(0);
  861. }
  862. break;
  863. case self::TYPE_UTC_TIME:
  864. case self::TYPE_GENERALIZED_TIME:
  865. $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
  866. $format.= 'mdHis';
  867. $value = @gmdate($format, strtotime($source)) . 'Z';
  868. break;
  869. case self::TYPE_BIT_STRING:
  870. if (isset($mapping['mapping'])) {
  871. $bits = array_fill(0, count($mapping['mapping']), 0);
  872. $size = 0;
  873. for ($i = 0; $i < count($mapping['mapping']); $i++) {
  874. if (in_array($mapping['mapping'][$i], $source)) {
  875. $bits[$i] = 1;
  876. $size = $i;
  877. }
  878. }
  879. if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
  880. $size = $mapping['min'] - 1;
  881. }
  882. $offset = 8 - (($size + 1) & 7);
  883. $offset = $offset !== 8 ? $offset : 0;
  884. $value = chr($offset);
  885. for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
  886. unset($bits[$i]);
  887. }
  888. $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
  889. $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
  890. foreach ($bytes as $byte) {
  891. $value.= chr(bindec($byte));
  892. }
  893. break;
  894. }
  895. case self::TYPE_OCTET_STRING:
  896. /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
  897. the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.
  898. -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
  899. $value = base64_decode($source);
  900. break;
  901. case self::TYPE_OBJECT_IDENTIFIER:
  902. $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
  903. if ($oid === false) {
  904. user_error('Invalid OID');
  905. return false;
  906. }
  907. $value = '';
  908. $parts = explode('.', $oid);
  909. $value = chr(40 * $parts[0] + $parts[1]);
  910. for ($i = 2; $i < count($parts); $i++) {
  911. $temp = '';
  912. if (!$parts[$i]) {
  913. $temp = "\0";
  914. } else {
  915. while ($parts[$i]) {
  916. $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
  917. $parts[$i] >>= 7;
  918. }
  919. $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
  920. }
  921. $value.= $temp;
  922. }
  923. break;
  924. case self::TYPE_ANY:
  925. $loc = $this->location;
  926. if (isset($idx)) {
  927. array_pop($this->location);
  928. }
  929. switch (true) {
  930. case !isset($source):
  931. return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special);
  932. case is_int($source):
  933. case $source instanceof BigInteger:
  934. return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special);
  935. case is_float($source):
  936. return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special);
  937. case is_bool($source):
  938. return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
  939. case is_array($source) && count($source) == 1:
  940. $typename = implode('', array_keys($source));
  941. $outtype = array_search($typename, $this->ANYmap, true);
  942. if ($outtype !== false) {
  943. return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
  944. }
  945. }
  946. $filters = $this->filters;
  947. foreach ($loc as $part) {
  948. if (!isset($filters[$part])) {
  949. $filters = false;
  950. break;
  951. }
  952. $filters = $filters[$part];
  953. }
  954. if ($filters === false) {
  955. user_error('No filters defined for ' . implode('/', $loc));
  956. return false;
  957. }
  958. return $this->_encode_der($source, $filters + $mapping, null, $special);
  959. case self::TYPE_NULL:
  960. $value = '';
  961. break;
  962. case self::TYPE_NUMERIC_STRING:
  963. case self::TYPE_TELETEX_STRING:
  964. case self::TYPE_PRINTABLE_STRING:
  965. case self::TYPE_UNIVERSAL_STRING:
  966. case self::TYPE_UTF8_STRING:
  967. case self::TYPE_BMP_STRING:
  968. case self::TYPE_IA5_STRING:
  969. case self::TYPE_VISIBLE_STRING:
  970. case self::TYPE_VIDEOTEX_STRING:
  971. case self::TYPE_GRAPHIC_STRING:
  972. case self::TYPE_GENERAL_STRING:
  973. $value = $source;
  974. break;
  975. case self::TYPE_BOOLEAN:
  976. $value = $source ? "\xFF" : "\x00";
  977. break;
  978. default:
  979. user_error('Mapping provides no type definition for ' . implode('/', $this->location));
  980. return false;
  981. }
  982. if (isset($idx)) {
  983. array_pop($this->location);
  984. }
  985. if (isset($mapping['cast'])) {
  986. if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
  987. $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
  988. $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
  989. } else {
  990. $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
  991. }
  992. }
  993. return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
  994. }
  995. /**
  996. * DER-encode the length
  997. *
  998. * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
  999. * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
  1000. *
  1001. * @access private
  1002. * @param int $length
  1003. * @return string
  1004. */
  1005. function _encodeLength($length)
  1006. {
  1007. if ($length <= 0x7F) {
  1008. return chr($length);
  1009. }
  1010. $temp = ltrim(pack('N', $length), chr(0));
  1011. return pack('Ca*', 0x80 | strlen($temp), $temp);
  1012. }
  1013. /**
  1014. * BER-decode the time
  1015. *
  1016. * Called by _decode_ber() and in the case of implicit tags asn1map().
  1017. *
  1018. * @access private
  1019. * @param string $content
  1020. * @param int $tag
  1021. * @return string
  1022. */
  1023. function _decodeTime($content, $tag)
  1024. {
  1025. /* UTCTime:
  1026. http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
  1027. http://www.obj-sys.com/asn1tutorial/node15.html
  1028. GeneralizedTime:
  1029. http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
  1030. http://www.obj-sys.com/asn1tutorial/node14.html */
  1031. $pattern = $tag == self::TYPE_UTC_TIME ?
  1032. '#(..)(..)(..)(..)(..)(..)(.*)#' :
  1033. '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
  1034. preg_match($pattern, $content, $matches);
  1035. list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
  1036. if ($tag == self::TYPE_UTC_TIME) {
  1037. $year = $year >= 50 ? "19$year" : "20$year";
  1038. }
  1039. if ($timezone == 'Z') {
  1040. $mktime = 'gmmktime';
  1041. $timezone = 0;
  1042. } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
  1043. $mktime = 'gmmktime';
  1044. $timezone = 60 * $matches[3] + 3600 * $matches[2];
  1045. if ($matches[1] == '-') {
  1046. $timezone = -$timezone;
  1047. }
  1048. } else {
  1049. $mktime = 'mktime';
  1050. $timezone = 0;
  1051. }
  1052. return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
  1053. }
  1054. /**
  1055. * Set the time format
  1056. *
  1057. * Sets the time / date format for asn1map().
  1058. *
  1059. * @access public
  1060. * @param string $format
  1061. */
  1062. function setTimeFormat($format)
  1063. {
  1064. $this->format = $format;
  1065. }
  1066. /**
  1067. * Load OIDs
  1068. *
  1069. * Load the relevant OIDs for a particular ASN.1 semantic mapping.
  1070. *
  1071. * @access public
  1072. * @param array $oids
  1073. */
  1074. function loadOIDs($oids)
  1075. {
  1076. $this->oids = $oids;
  1077. }
  1078. /**
  1079. * Load filters
  1080. *
  1081. * See \phpseclib\File\X509, etc, for an example.
  1082. *
  1083. * @access public
  1084. * @param array $filters
  1085. */
  1086. function loadFilters($filters)
  1087. {
  1088. $this->filters = $filters;
  1089. }
  1090. /**
  1091. * String Shift
  1092. *
  1093. * Inspired by array_shift
  1094. *
  1095. * @param string $string
  1096. * @param int $index
  1097. * @return string
  1098. * @access private
  1099. */
  1100. function _string_shift(&$string, $index = 1)
  1101. {
  1102. $substr = substr($string, 0, $index);
  1103. $string = substr($string, $index);
  1104. return $substr;
  1105. }
  1106. /**
  1107. * String type conversion
  1108. *
  1109. * This is a lazy conversion, dealing only with character size.
  1110. * No real conversion table is used.
  1111. *
  1112. * @param string $in
  1113. * @param int $from
  1114. * @param int $to
  1115. * @return string
  1116. * @access public
  1117. */
  1118. function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
  1119. {
  1120. if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
  1121. return false;
  1122. }
  1123. $insize = $this->stringTypeSize[$from];
  1124. $outsize = $this->stringTypeSize[$to];
  1125. $inlength = strlen($in);
  1126. $out = '';
  1127. for ($i = 0; $i < $inlength;) {
  1128. if ($inlength - $i < $insize) {
  1129. return false;
  1130. }
  1131. // Get an input character as a 32-bit value.
  1132. $c = ord($in[$i++]);
  1133. switch (true) {
  1134. case $insize == 4:
  1135. $c = ($c << 8) | ord($in[$i++]);
  1136. $c = ($c << 8) | ord($in[$i++]);
  1137. case $insize == 2:
  1138. $c = ($c << 8) | ord($in[$i++]);
  1139. case $insize == 1:
  1140. break;
  1141. case ($c & 0x80) == 0x00:
  1142. break;
  1143. case ($c & 0x40) == 0x00:
  1144. return false;
  1145. default:
  1146. $bit = 6;
  1147. do {
  1148. if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
  1149. return false;

Large files files are truncated, but you can click here to view the full file