[securemail] Update Composer dependency ahead of release
[friendica-addons.git/.git] / securemail / vendor / phpseclib / phpseclib / phpseclib / File / ASN1.php
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
22 namespace phpseclib3\File;
23
24 use phpseclib3\Common\Functions\Strings;
25 use phpseclib3\File\ASN1\Element;
26 use phpseclib3\Math\BigInteger;
27
28 /**
29  * Pure-PHP ASN.1 Parser
30  *
31  * @author  Jim Wigginton <terrafrost@php.net>
32  */
33 abstract 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 }