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