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