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