[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/File/ -> ASN1.php (source)

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