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