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