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