Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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