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