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