ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ASN1.php
Go to the documentation of this file.
1<?php
2
24namespace phpseclib\File;
25
28
36class ASN1
37{
44 const CLASS_UNIVERSAL = 0;
47 const CLASS_PRIVATE = 3;
56 const TYPE_BOOLEAN = 1;
57 const TYPE_INTEGER = 2;
58 const TYPE_BIT_STRING = 3;
60 const TYPE_NULL = 5;
62 //const TYPE_OBJECT_DESCRIPTOR = 7;
63 //const TYPE_INSTANCE_OF = 8; // EXTERNAL
64 const TYPE_REAL = 9;
65 const TYPE_ENUMERATED = 10;
66 //const TYPE_EMBEDDED = 11;
67 const TYPE_UTF8_STRING = 12;
68 //const TYPE_RELATIVE_OID = 13;
69 const TYPE_SEQUENCE = 16; // SEQUENCE OF
70 const TYPE_SET = 17; // SET OF
80 const TYPE_TELETEX_STRING = 20; // T61String
82 const TYPE_IA5_STRING = 22;
83 const TYPE_UTC_TIME = 23;
86 const TYPE_VISIBLE_STRING = 26; // ISO646String
89 //const TYPE_CHARACTER_STRING = 29;
90 const TYPE_BMP_STRING = 30;
100 const TYPE_CHOICE = -1;
101 const TYPE_ANY = -2;
111 var $oids = array();
112
120 var $format = 'D, d M Y H:i:s O';
121
132
143
154 var $ANYmap = array(
155 self::TYPE_BOOLEAN => true,
156 self::TYPE_INTEGER => true,
157 self::TYPE_BIT_STRING => 'bitString',
158 self::TYPE_OCTET_STRING => 'octetString',
159 self::TYPE_NULL => 'null',
160 self::TYPE_OBJECT_IDENTIFIER => 'objectIdentifier',
161 self::TYPE_REAL => true,
162 self::TYPE_ENUMERATED => 'enumerated',
163 self::TYPE_UTF8_STRING => 'utf8String',
164 self::TYPE_NUMERIC_STRING => 'numericString',
165 self::TYPE_PRINTABLE_STRING => 'printableString',
166 self::TYPE_TELETEX_STRING => 'teletexString',
167 self::TYPE_VIDEOTEX_STRING => 'videotexString',
168 self::TYPE_IA5_STRING => 'ia5String',
169 self::TYPE_UTC_TIME => 'utcTime',
170 self::TYPE_GENERALIZED_TIME => 'generalTime',
171 self::TYPE_GRAPHIC_STRING => 'graphicString',
172 self::TYPE_VISIBLE_STRING => 'visibleString',
173 self::TYPE_GENERAL_STRING => 'generalString',
174 self::TYPE_UNIVERSAL_STRING => 'universalString',
175 //self::TYPE_CHARACTER_STRING => 'characterString',
176 self::TYPE_BMP_STRING => 'bmpString'
177 );
178
188 var $stringTypeSize = array(
189 self::TYPE_UTF8_STRING => 0,
190 self::TYPE_BMP_STRING => 2,
191 self::TYPE_UNIVERSAL_STRING => 4,
192 self::TYPE_PRINTABLE_STRING => 1,
193 self::TYPE_TELETEX_STRING => 1,
194 self::TYPE_IA5_STRING => 1,
195 self::TYPE_VISIBLE_STRING => 1,
196 );
197
208 {
209 if ($encoded instanceof Element) {
210 $encoded = $encoded->element;
211 }
212
213 $this->encoded = $encoded;
214 // encapsulate in an array for BC with the old decodeBER
215 return array($this->_decode_ber($encoded));
216 }
217
231 {
232 $current = array('start' => $start);
233
234 $type = ord($this->_string_shift($encoded));
235 $start++;
236
237 $constructed = ($type >> 5) & 1;
238
239 $tag = $type & 0x1F;
240 if ($tag == 0x1F) {
241 $tag = 0;
242 // process septets (since the eighth bit is ignored, it's not an octet)
243 do {
244 $loop = ord($encoded[0]) >> 7;
245 $tag <<= 7;
246 $tag |= ord($this->_string_shift($encoded)) & 0x7F;
247 $start++;
248 } while ($loop);
249 }
250
251 // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
252 $length = ord($this->_string_shift($encoded));
253 $start++;
254 if ($length == 0x80) { // indefinite length
255 // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all
256 // immediately available." -- paragraph 8.1.3.2.c
257 $length = strlen($encoded);
258 } elseif ($length & 0x80) { // definite length, long form
259 // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only
260 // support it up to four.
261 $length&= 0x7F;
262 $temp = $this->_string_shift($encoded, $length);
263 // tags of indefinte length don't really have a header length; this length includes the tag
264 $current+= array('headerlength' => $length + 2);
265 $start+= $length;
266 extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
267 } else {
268 $current+= array('headerlength' => 2);
269 }
270
271 if ($length > strlen($encoded)) {
272 return false;
273 }
274
275 $content = $this->_string_shift($encoded, $length);
276
277 // at this point $length can be overwritten. it's only accurate for definite length things as is
278
279 /* Class is UNIVERSAL, APPLICATION, PRIVATE, or CONTEXT-SPECIFIC. The UNIVERSAL class is restricted to the ASN.1
280 built-in types. It defines an application-independent data type that must be distinguishable from all other
281 data types. The other three classes are user defined. The APPLICATION class distinguishes data types that
282 have a wide, scattered use within a particular presentation context. PRIVATE distinguishes data types within
283 a particular organization or country. CONTEXT-SPECIFIC distinguishes members of a sequence or set, the
284 alternatives of a CHOICE, or universally tagged set members. Only the class number appears in braces for this
285 data type; the term CONTEXT-SPECIFIC does not appear.
286
287 -- http://www.obj-sys.com/asn1tutorial/node12.html */
288 $class = ($type >> 6) & 3;
289 switch ($class) {
293 if (!$constructed) {
294 return array(
295 'type' => $class,
296 'constant' => $tag,
297 'content' => $content,
298 'length' => $length + $start - $current['start']
299 );
300 }
301
302 $newcontent = array();
303 $remainingLength = $length;
304 while ($remainingLength > 0) {
305 $temp = $this->_decode_ber($content, $start);
306 $length = $temp['length'];
307 // end-of-content octets - see paragraph 8.1.5
308 if (substr($content, $length, 2) == "\0\0") {
309 $length+= 2;
310 $start+= $length;
311 $newcontent[] = $temp;
312 break;
313 }
314 $start+= $length;
315 $remainingLength-= $length;
316 $newcontent[] = $temp;
317 $this->_string_shift($content, $length);
318 }
319
320 return array(
321 'type' => $class,
322 'constant' => $tag,
323 // the array encapsulation is for BC with the old format
324 'content' => $newcontent,
325 // the only time when $content['headerlength'] isn't defined is when the length is indefinite.
326 // the absence of $content['headerlength'] is how we know if something is indefinite or not.
327 // technically, it could be defined to be 2 and then another indicator could be used but whatever.
328 'length' => $start - $current['start']
329 ) + $current;
330 }
331
332 $current+= array('type' => $tag);
333
334 // decode UNIVERSAL tags
335 switch ($tag) {
337 // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
338 //if (strlen($content) != 1) {
339 // return false;
340 //}
341 $current['content'] = (bool) ord($content[0]);
342 break;
345 $current['content'] = new BigInteger($content, -256);
346 break;
347 case self::TYPE_REAL: // not currently supported
348 return false;
350 // The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
351 // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
352 // seven.
353 if (!$constructed) {
354 $current['content'] = $content;
355 } else {
356 $temp = $this->_decode_ber($content, $start);
357 $length-= strlen($content);
358 $last = count($temp) - 1;
359 for ($i = 0; $i < $last; $i++) {
360 // all subtags should be bit strings
361 //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
362 // return false;
363 //}
364 $current['content'].= substr($temp[$i]['content'], 1);
365 }
366 // all subtags should be bit strings
367 //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
368 // return false;
369 //}
370 $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
371 }
372 break;
374 if (!$constructed) {
375 $current['content'] = $content;
376 } else {
377 $current['content'] = '';
378 $length = 0;
379 while (substr($content, 0, 2) != "\0\0") {
380 $temp = $this->_decode_ber($content, $length + $start);
381 $this->_string_shift($content, $temp['length']);
382 // all subtags should be octet strings
383 //if ($temp['type'] != self::TYPE_OCTET_STRING) {
384 // return false;
385 //}
386 $current['content'].= $temp['content'];
387 $length+= $temp['length'];
388 }
389 if (substr($content, 0, 2) == "\0\0") {
390 $length+= 2; // +2 for the EOC
391 }
392 }
393 break;
394 case self::TYPE_NULL:
395 // "The contents octets shall not contain any octets." -- paragraph 8.8.2
396 //if (strlen($content)) {
397 // return false;
398 //}
399 break;
401 case self::TYPE_SET:
402 $offset = 0;
403 $current['content'] = array();
404 while (strlen($content)) {
405 // if indefinite length construction was used and we have an end-of-content string next
406 // 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
407 if (!isset($current['headerlength']) && substr($content, 0, 2) == "\0\0") {
408 $length = $offset + 2; // +2 for the EOC
409 break 2;
410 }
411 $temp = $this->_decode_ber($content, $start + $offset);
412 $this->_string_shift($content, $temp['length']);
413 $current['content'][] = $temp;
414 $offset+= $temp['length'];
415 }
416 break;
418 $temp = ord($this->_string_shift($content));
419 $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
420 $valuen = 0;
421 // process septets
422 while (strlen($content)) {
423 $temp = ord($this->_string_shift($content));
424 $valuen <<= 7;
425 $valuen |= $temp & 0x7F;
426 if (~$temp & 0x80) {
427 $current['content'].= ".$valuen";
428 $valuen = 0;
429 }
430 }
431 // the eighth bit of the last byte should not be 1
432 //if ($temp >> 7) {
433 // return false;
434 //}
435 break;
436 /* Each character string type shall be encoded as if it had been declared:
437 [UNIVERSAL x] IMPLICIT OCTET STRING
438
439 -- X.690-0207.pdf#page=23 (paragraph 8.21.3)
440
441 Per that, we're not going to do any validation. If there are any illegal characters in the string,
442 we don't really care */
444 // 0,1,2,3,4,5,6,7,8,9, and space
446 // Upper and lower case letters, digits, space, apostrophe, left/right parenthesis, plus sign, comma,
447 // hyphen, full stop, solidus, colon, equal sign, question mark
449 // The Teletex character set in CCITT's T61, space, and delete
450 // see http://en.wikipedia.org/wiki/Teletex#Character_sets
452 // The Videotex character set in CCITT's T.100 and T.101, space, and delete
454 // Printing character sets of international ASCII, and space
456 // International Alphabet 5 (International ASCII)
458 // All registered G sets, and space
460 // All registered C and G sets, space and delete
462 // ????
464 $current['content'] = $content;
465 break;
468 $current['content'] = $this->_decodeTime($content, $tag);
469 default:
470 }
471
472 $start+= $length;
473
474 // ie. length is the length of the full TLV encoding - it's not just the length of the value
475 return $current + array('length' => $start - $current['start']);
476 }
477
491 function asn1map($decoded, $mapping, $special = array())
492 {
493 if (isset($mapping['explicit']) && is_array($decoded['content'])) {
494 $decoded = $decoded['content'][0];
495 }
496
497 switch (true) {
498 case $mapping['type'] == self::TYPE_ANY:
499 $intype = $decoded['type'];
500 if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) {
501 return new Element(substr($this->encoded, $decoded['start'], $decoded['length']));
502 }
503 $inmap = $this->ANYmap[$intype];
504 if (is_string($inmap)) {
505 return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
506 }
507 break;
508 case $mapping['type'] == self::TYPE_CHOICE:
509 foreach ($mapping['children'] as $key => $option) {
510 switch (true) {
511 case isset($option['constant']) && $option['constant'] == $decoded['constant']:
512 case !isset($option['constant']) && $option['type'] == $decoded['type']:
513 $value = $this->asn1map($decoded, $option, $special);
514 break;
515 case !isset($option['constant']) && $option['type'] == self::TYPE_CHOICE:
516 $v = $this->asn1map($decoded, $option, $special);
517 if (isset($v)) {
518 $value = $v;
519 }
520 }
521 if (isset($value)) {
522 if (isset($special[$key])) {
523 $value = call_user_func($special[$key], $value);
524 }
525 return array($key => $value);
526 }
527 }
528 return null;
529 case isset($mapping['implicit']):
530 case isset($mapping['explicit']):
531 case $decoded['type'] == $mapping['type']:
532 break;
533 default:
534 // if $decoded['type'] and $mapping['type'] are both strings, but different types of strings,
535 // let it through
536 switch (true) {
537 case $decoded['type'] < 18: // self::TYPE_NUMERIC_STRING == 18
538 case $decoded['type'] > 30: // self::TYPE_BMP_STRING == 30
539 case $mapping['type'] < 18:
540 case $mapping['type'] > 30:
541 return null;
542 }
543 }
544
545 if (isset($mapping['implicit'])) {
546 $decoded['type'] = $mapping['type'];
547 }
548
549 switch ($decoded['type']) {
551 $map = array();
552
553 // ignore the min and max
554 if (isset($mapping['min']) && isset($mapping['max'])) {
555 $child = $mapping['children'];
556 foreach ($decoded['content'] as $content) {
557 if (($map[] = $this->asn1map($content, $child, $special)) === null) {
558 return null;
559 }
560 }
561
562 return $map;
563 }
564
565 $n = count($decoded['content']);
566 $i = 0;
567
568 foreach ($mapping['children'] as $key => $child) {
569 $maymatch = $i < $n; // Match only existing input.
570 if ($maymatch) {
571 $temp = $decoded['content'][$i];
572
573 if ($child['type'] != self::TYPE_CHOICE) {
574 // Get the mapping and input class & constant.
575 $childClass = $tempClass = self::CLASS_UNIVERSAL;
576 $constant = null;
577 if (isset($temp['constant'])) {
578 $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
579 }
580 if (isset($child['class'])) {
581 $childClass = $child['class'];
582 $constant = $child['cast'];
583 } elseif (isset($child['constant'])) {
584 $childClass = self::CLASS_CONTEXT_SPECIFIC;
585 $constant = $child['constant'];
586 }
587
588 if (isset($constant) && isset($temp['constant'])) {
589 // Can only match if constants and class match.
590 $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
591 } else {
592 // Can only match if no constant expected and type matches or is generic.
593 $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
594 }
595 }
596 }
597
598 if ($maymatch) {
599 // Attempt submapping.
600 $candidate = $this->asn1map($temp, $child, $special);
601 $maymatch = $candidate !== null;
602 }
603
604 if ($maymatch) {
605 // Got the match: use it.
606 if (isset($special[$key])) {
607 $candidate = call_user_func($special[$key], $candidate);
608 }
609 $map[$key] = $candidate;
610 $i++;
611 } elseif (isset($child['default'])) {
612 $map[$key] = $child['default']; // Use default.
613 } elseif (!isset($child['optional'])) {
614 return null; // Syntax error.
615 }
616 }
617
618 // Fail mapping if all input items have not been consumed.
619 return $i < $n ? null: $map;
620
621 // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
622 case self::TYPE_SET:
623 $map = array();
624
625 // ignore the min and max
626 if (isset($mapping['min']) && isset($mapping['max'])) {
627 $child = $mapping['children'];
628 foreach ($decoded['content'] as $content) {
629 if (($map[] = $this->asn1map($content, $child, $special)) === null) {
630 return null;
631 }
632 }
633
634 return $map;
635 }
636
637 for ($i = 0; $i < count($decoded['content']); $i++) {
638 $temp = $decoded['content'][$i];
639 $tempClass = self::CLASS_UNIVERSAL;
640 if (isset($temp['constant'])) {
641 $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
642 }
643
644 foreach ($mapping['children'] as $key => $child) {
645 if (isset($map[$key])) {
646 continue;
647 }
648 $maymatch = true;
649 if ($child['type'] != self::TYPE_CHOICE) {
650 $childClass = self::CLASS_UNIVERSAL;
651 $constant = null;
652 if (isset($child['class'])) {
653 $childClass = $child['class'];
654 $constant = $child['cast'];
655 } elseif (isset($child['constant'])) {
656 $childClass = self::CLASS_CONTEXT_SPECIFIC;
657 $constant = $child['constant'];
658 }
659
660 if (isset($constant) && isset($temp['constant'])) {
661 // Can only match if constants and class match.
662 $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
663 } else {
664 // Can only match if no constant expected and type matches or is generic.
665 $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], self::TYPE_ANY, self::TYPE_CHOICE)) !== false;
666 }
667 }
668
669 if ($maymatch) {
670 // Attempt submapping.
671 $candidate = $this->asn1map($temp, $child, $special);
672 $maymatch = $candidate !== null;
673 }
674
675 if (!$maymatch) {
676 break;
677 }
678
679 // Got the match: use it.
680 if (isset($special[$key])) {
681 $candidate = call_user_func($special[$key], $candidate);
682 }
683 $map[$key] = $candidate;
684 break;
685 }
686 }
687
688 foreach ($mapping['children'] as $key => $child) {
689 if (!isset($map[$key])) {
690 if (isset($child['default'])) {
691 $map[$key] = $child['default'];
692 } elseif (!isset($child['optional'])) {
693 return null;
694 }
695 }
696 }
697 return $map;
699 return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
702 if (isset($mapping['implicit'])) {
703 $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
704 }
705 return @date($this->format, $decoded['content']);
707 if (isset($mapping['mapping'])) {
708 $offset = ord($decoded['content'][0]);
709 $size = (strlen($decoded['content']) - 1) * 8 - $offset;
710 /*
711 From X.680-0207.pdf#page=46 (21.7):
712
713 "When a "NamedBitList" is used in defining a bitstring type ASN.1 encoding rules are free to add (or remove)
714 arbitrarily any trailing 0 bits to (or from) values that are being encoded or decoded. Application designers should
715 therefore ensure that different semantics are not associated with such values which differ only in the number of trailing
716 0 bits."
717 */
718 $bits = count($mapping['mapping']) == $size ? array() : array_fill(0, count($mapping['mapping']) - $size, false);
719 for ($i = strlen($decoded['content']) - 1; $i > 0; $i--) {
720 $current = ord($decoded['content'][$i]);
721 for ($j = $offset; $j < 8; $j++) {
722 $bits[] = (bool) ($current & (1 << $j));
723 }
724 $offset = 0;
725 }
726 $values = array();
727 $map = array_reverse($mapping['mapping']);
728 foreach ($map as $i => $value) {
729 if ($bits[$i]) {
730 $values[] = $value;
731 }
732 }
733 return $values;
734 }
736 return base64_encode($decoded['content']);
737 case self::TYPE_NULL:
738 return '';
740 return $decoded['content'];
752 return $decoded['content'];
755 $temp = $decoded['content'];
756 if (isset($mapping['implicit'])) {
757 $temp = new BigInteger($decoded['content'], -256);
758 }
759 if (isset($mapping['mapping'])) {
760 $temp = (int) $temp->toString();
761 return isset($mapping['mapping'][$temp]) ?
762 $mapping['mapping'][$temp] :
763 false;
764 }
765 return $temp;
766 }
767 }
768
783 function encodeDER($source, $mapping, $special = array())
784 {
785 $this->location = array();
786 return $this->_encode_der($source, $mapping, null, $special);
787 }
788
798 function _encode_der($source, $mapping, $idx = null, $special = array())
799 {
800 if ($source instanceof Element) {
801 return $source->element;
802 }
803
804 // do not encode (implicitly optional) fields with value set to default
805 if (isset($mapping['default']) && $source === $mapping['default']) {
806 return '';
807 }
808
809 if (isset($idx)) {
810 if (isset($special[$idx])) {
811 $source = call_user_func($special[$idx], $source);
812 }
813 $this->location[] = $idx;
814 }
815
816 $tag = $mapping['type'];
817
818 switch ($tag) {
819 case self::TYPE_SET: // Children order is not important, thus process in sequence.
821 $tag|= 0x20; // set the constructed bit
822 $value = '';
823
824 // ignore the min and max
825 if (isset($mapping['min']) && isset($mapping['max'])) {
826 $child = $mapping['children'];
827
828 foreach ($source as $content) {
829 $temp = $this->_encode_der($content, $child, null, $special);
830 if ($temp === false) {
831 return false;
832 }
833 $value.= $temp;
834 }
835 break;
836 }
837
838 foreach ($mapping['children'] as $key => $child) {
839 if (!array_key_exists($key, $source)) {
840 if (!isset($child['optional'])) {
841 return false;
842 }
843 continue;
844 }
845
846 $temp = $this->_encode_der($source[$key], $child, $key, $special);
847 if ($temp === false) {
848 return false;
849 }
850
851 // An empty child encoding means it has been optimized out.
852 // Else we should have at least one tag byte.
853 if ($temp === '') {
854 continue;
855 }
856
857 // if isset($child['constant']) is true then isset($child['optional']) should be true as well
858 if (isset($child['constant'])) {
859 /*
860 From X.680-0207.pdf#page=58 (30.6):
861
862 "The tagging construction specifies explicit tagging if any of the following holds:
863 ...
864 c) the "Tag Type" alternative is used and the value of "TagDefault" for the module is IMPLICIT TAGS or
865 AUTOMATIC TAGS, but the type defined by "Type" is an untagged choice type, an untagged open type, or
866 an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
867 */
868 if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
869 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
870 $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
871 } else {
872 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
873 $temp = $subtag . substr($temp, 1);
874 }
875 }
876 $value.= $temp;
877 }
878 break;
880 $temp = false;
881
882 foreach ($mapping['children'] as $key => $child) {
883 if (!isset($source[$key])) {
884 continue;
885 }
886
887 $temp = $this->_encode_der($source[$key], $child, $key, $special);
888 if ($temp === false) {
889 return false;
890 }
891
892 // An empty child encoding means it has been optimized out.
893 // Else we should have at least one tag byte.
894 if ($temp === '') {
895 continue;
896 }
897
898 $tag = ord($temp[0]);
899
900 // if isset($child['constant']) is true then isset($child['optional']) should be true as well
901 if (isset($child['constant'])) {
902 if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
903 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
904 $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp;
905 } else {
906 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
907 $temp = $subtag . substr($temp, 1);
908 }
909 }
910 }
911
912 if (isset($idx)) {
913 array_pop($this->location);
914 }
915
916 if ($temp && isset($mapping['cast'])) {
917 $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']);
918 }
919
920 return $temp;
923 if (!isset($mapping['mapping'])) {
924 if (is_numeric($source)) {
926 }
927 $value = $source->toBytes(true);
928 } else {
929 $value = array_search($source, $mapping['mapping']);
930 if ($value === false) {
931 return false;
932 }
933 $value = new BigInteger($value);
934 $value = $value->toBytes(true);
935 }
936 if (!strlen($value)) {
937 $value = chr(0);
938 }
939 break;
942 $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
943 $format.= 'mdHis';
944 $value = @gmdate($format, strtotime($source)) . 'Z';
945 break;
947 if (isset($mapping['mapping'])) {
948 $bits = array_fill(0, count($mapping['mapping']), 0);
949 $size = 0;
950 for ($i = 0; $i < count($mapping['mapping']); $i++) {
951 if (in_array($mapping['mapping'][$i], $source)) {
952 $bits[$i] = 1;
953 $size = $i;
954 }
955 }
956
957 if (isset($mapping['min']) && $mapping['min'] >= 1 && $size < $mapping['min']) {
958 $size = $mapping['min'] - 1;
959 }
960
961 $offset = 8 - (($size + 1) & 7);
962 $offset = $offset !== 8 ? $offset : 0;
963
964 $value = chr($offset);
965
966 for ($i = $size + 1; $i < count($mapping['mapping']); $i++) {
967 unset($bits[$i]);
968 }
969
970 $bits = implode('', array_pad($bits, $size + $offset + 1, 0));
971 $bytes = explode(' ', rtrim(chunk_split($bits, 8, ' ')));
972 foreach ($bytes as $byte) {
973 $value.= chr(bindec($byte));
974 }
975
976 break;
977 }
979 /* The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit,
980 the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven.
981
982 -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
983 $value = base64_decode($source);
984 break;
986 $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
987 if ($oid === false) {
988 user_error('Invalid OID');
989 return false;
990 }
991 $value = '';
992 $parts = explode('.', $oid);
993 $value = chr(40 * $parts[0] + $parts[1]);
994 for ($i = 2; $i < count($parts); $i++) {
995 $temp = '';
996 if (!$parts[$i]) {
997 $temp = "\0";
998 } else {
999 while ($parts[$i]) {
1000 $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
1001 $parts[$i] >>= 7;
1002 }
1003 $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
1004 }
1005 $value.= $temp;
1006 }
1007 break;
1008 case self::TYPE_ANY:
1009 $loc = $this->location;
1010 if (isset($idx)) {
1011 array_pop($this->location);
1012 }
1013
1014 switch (true) {
1015 case !isset($source):
1016 return $this->_encode_der(null, array('type' => self::TYPE_NULL) + $mapping, null, $special);
1017 case is_int($source):
1018 case $source instanceof BigInteger:
1019 return $this->_encode_der($source, array('type' => self::TYPE_INTEGER) + $mapping, null, $special);
1020 case is_float($source):
1021 return $this->_encode_der($source, array('type' => self::TYPE_REAL) + $mapping, null, $special);
1022 case is_bool($source):
1023 return $this->_encode_der($source, array('type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
1024 case is_array($source) && count($source) == 1:
1025 $typename = implode('', array_keys($source));
1026 $outtype = array_search($typename, $this->ANYmap, true);
1027 if ($outtype !== false) {
1028 return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, null, $special);
1029 }
1030 }
1031
1033 foreach ($loc as $part) {
1034 if (!isset($filters[$part])) {
1035 $filters = false;
1036 break;
1037 }
1038 $filters = $filters[$part];
1039 }
1040 if ($filters === false) {
1041 user_error('No filters defined for ' . implode('/', $loc));
1042 return false;
1043 }
1044 return $this->_encode_der($source, $filters + $mapping, null, $special);
1045 case self::TYPE_NULL:
1046 $value = '';
1047 break;
1059 $value = $source;
1060 break;
1061 case self::TYPE_BOOLEAN:
1062 $value = $source ? "\xFF" : "\x00";
1063 break;
1064 default:
1065 user_error('Mapping provides no type definition for ' . implode('/', $this->location));
1066 return false;
1067 }
1068
1069 if (isset($idx)) {
1070 array_pop($this->location);
1071 }
1072
1073 if (isset($mapping['cast'])) {
1074 if (isset($mapping['explicit']) || $mapping['type'] == self::TYPE_CHOICE) {
1075 $value = chr($tag) . $this->_encodeLength(strlen($value)) . $value;
1076 $tag = ($mapping['class'] << 6) | 0x20 | $mapping['cast'];
1077 } else {
1078 $tag = ($mapping['class'] << 6) | (ord($temp[0]) & 0x20) | $mapping['cast'];
1079 }
1080 }
1081
1082 return chr($tag) . $this->_encodeLength(strlen($value)) . $value;
1083 }
1084
1095 function _encodeLength($length)
1096 {
1097 if ($length <= 0x7F) {
1098 return chr($length);
1099 }
1100
1101 $temp = ltrim(pack('N', $length), chr(0));
1102 return pack('Ca*', 0x80 | strlen($temp), $temp);
1103 }
1104
1115 function _decodeTime($content, $tag)
1116 {
1117 /* UTCTime:
1118 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
1119 http://www.obj-sys.com/asn1tutorial/node15.html
1120
1121 GeneralizedTime:
1122 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
1123 http://www.obj-sys.com/asn1tutorial/node14.html */
1124
1125 $pattern = $tag == self::TYPE_UTC_TIME ?
1126 '#(..)(..)(..)(..)(..)(..)(.*)#' :
1127 '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
1128
1129 preg_match($pattern, $content, $matches);
1130
1131 list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
1132
1133 if ($tag == self::TYPE_UTC_TIME) {
1134 $year = $year >= 50 ? "19$year" : "20$year";
1135 }
1136
1137 if ($timezone == 'Z') {
1138 $mktime = 'gmmktime';
1139 $timezone = 0;
1140 } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
1141 $mktime = 'gmmktime';
1142 $timezone = 60 * $matches[3] + 3600 * $matches[2];
1143 if ($matches[1] == '-') {
1144 $timezone = -$timezone;
1145 }
1146 } else {
1147 $mktime = 'mktime';
1148 $timezone = 0;
1149 }
1150
1151 return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
1152 }
1153
1163 {
1164 $this->format = $format;
1165 }
1166
1175 function loadOIDs($oids)
1176 {
1177 $this->oids = $oids;
1178 }
1179
1189 {
1190 $this->filters = $filters;
1191 }
1192
1203 function _string_shift(&$string, $index = 1)
1204 {
1205 $substr = substr($string, 0, $index);
1206 $string = substr($string, $index);
1207 return $substr;
1208 }
1209
1222 function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
1223 {
1224 if (!isset($this->stringTypeSize[$from]) || !isset($this->stringTypeSize[$to])) {
1225 return false;
1226 }
1227 $insize = $this->stringTypeSize[$from];
1228 $outsize = $this->stringTypeSize[$to];
1229 $inlength = strlen($in);
1230 $out = '';
1231
1232 for ($i = 0; $i < $inlength;) {
1233 if ($inlength - $i < $insize) {
1234 return false;
1235 }
1236
1237 // Get an input character as a 32-bit value.
1238 $c = ord($in[$i++]);
1239 switch (true) {
1240 case $insize == 4:
1241 $c = ($c << 8) | ord($in[$i++]);
1242 $c = ($c << 8) | ord($in[$i++]);
1243 case $insize == 2:
1244 $c = ($c << 8) | ord($in[$i++]);
1245 case $insize == 1:
1246 break;
1247 case ($c & 0x80) == 0x00:
1248 break;
1249 case ($c & 0x40) == 0x00:
1250 return false;
1251 default:
1252 $bit = 6;
1253 do {
1254 if ($bit > 25 || $i >= $inlength || (ord($in[$i]) & 0xC0) != 0x80) {
1255 return false;
1256 }
1257 $c = ($c << 6) | (ord($in[$i++]) & 0x3F);
1258 $bit += 5;
1259 $mask = 1 << $bit;
1260 } while ($c & $bit);
1261 $c &= $mask - 1;
1262 break;
1263 }
1264
1265 // Convert and append the character to output string.
1266 $v = '';
1267 switch (true) {
1268 case $outsize == 4:
1269 $v .= chr($c & 0xFF);
1270 $c >>= 8;
1271 $v .= chr($c & 0xFF);
1272 $c >>= 8;
1273 case $outsize == 2:
1274 $v .= chr($c & 0xFF);
1275 $c >>= 8;
1276 case $outsize == 1:
1277 $v .= chr($c & 0xFF);
1278 $c >>= 8;
1279 if ($c) {
1280 return false;
1281 }
1282 break;
1283 case ($c & 0x80000000) != 0:
1284 return false;
1285 case $c >= 0x04000000:
1286 $v .= chr(0x80 | ($c & 0x3F));
1287 $c = ($c >> 6) | 0x04000000;
1288 case $c >= 0x00200000:
1289 $v .= chr(0x80 | ($c & 0x3F));
1290 $c = ($c >> 6) | 0x00200000;
1291 case $c >= 0x00010000:
1292 $v .= chr(0x80 | ($c & 0x3F));
1293 $c = ($c >> 6) | 0x00010000;
1294 case $c >= 0x00000800:
1295 $v .= chr(0x80 | ($c & 0x3F));
1296 $c = ($c >> 6) | 0x00000800;
1297 case $c >= 0x00000080:
1298 $v .= chr(0x80 | ($c & 0x3F));
1299 $c = ($c >> 6) | 0x000000C0;
1300 default:
1301 $v .= chr($c);
1302 break;
1303 }
1304 $out .= strrev($v);
1305 }
1306 return $out;
1307 }
1308}
$n
Definition: RandomTest.php:85
$size
Definition: RandomTest.php:84
if(php_sapi_name() !='cli') $in
Definition: Utf8Test.php:37
$source
Definition: linkback.php:22
$location
Definition: buildRTE.php:44
An exception for terminatinating execution or to throw for unit testing.
const TYPE_PRINTABLE_STRING
Definition: ASN1.php:79
const CLASS_APPLICATION
Definition: ASN1.php:45
const TYPE_GRAPHIC_STRING
Definition: ASN1.php:85
encodeDER($source, $mapping, $special=array())
ASN.1 Encode.
Definition: ASN1.php:783
const TYPE_OBJECT_IDENTIFIER
Definition: ASN1.php:61
const TYPE_IA5_STRING
Definition: ASN1.php:82
const TYPE_OCTET_STRING
Definition: ASN1.php:59
const TYPE_TELETEX_STRING
Definition: ASN1.php:80
const TYPE_BMP_STRING
Definition: ASN1.php:90
setTimeFormat($format)
Set the time format.
Definition: ASN1.php:1162
const TYPE_SEQUENCE
Definition: ASN1.php:69
const TYPE_REAL
Definition: ASN1.php:64
asn1map($decoded, $mapping, $special=array())
ASN.1 Map.
Definition: ASN1.php:491
_encode_der($source, $mapping, $idx=null, $special=array())
ASN.1 Encode (Helper function)
Definition: ASN1.php:798
const TYPE_SET
Definition: ASN1.php:70
loadFilters($filters)
Load filters.
Definition: ASN1.php:1188
decodeBER($encoded)
Parse BER-encoding.
Definition: ASN1.php:207
const CLASS_PRIVATE
Definition: ASN1.php:47
const TYPE_GENERAL_STRING
Definition: ASN1.php:87
const TYPE_BIT_STRING
Definition: ASN1.php:58
const TYPE_GENERALIZED_TIME
Definition: ASN1.php:84
const TYPE_UNIVERSAL_STRING
Definition: ASN1.php:88
_string_shift(&$string, $index=1)
String Shift.
Definition: ASN1.php:1203
const TYPE_VISIBLE_STRING
Definition: ASN1.php:86
loadOIDs($oids)
Load OIDs.
Definition: ASN1.php:1175
convert($in, $from=self::TYPE_UTF8_STRING, $to=self::TYPE_UTF8_STRING)
String type conversion.
Definition: ASN1.php:1222
const TYPE_UTF8_STRING
Definition: ASN1.php:67
const TYPE_VIDEOTEX_STRING
Definition: ASN1.php:81
_decode_ber($encoded, $start=0)
Parse BER-encoding (Helper function)
Definition: ASN1.php:230
const CLASS_CONTEXT_SPECIFIC
Definition: ASN1.php:46
const CLASS_UNIVERSAL
#+ Tag Classes
Definition: ASN1.php:44
const TYPE_CHOICE
#-
Definition: ASN1.php:100
const TYPE_UTC_TIME
Definition: ASN1.php:83
const TYPE_NUMERIC_STRING
#-
Definition: ASN1.php:78
const TYPE_NULL
Definition: ASN1.php:60
const TYPE_ENUMERATED
Definition: ASN1.php:65
const TYPE_BOOLEAN
#-
Definition: ASN1.php:56
_encodeLength($length)
DER-encode the length.
Definition: ASN1.php:1095
_decodeTime($content, $tag)
BER-decode the time.
Definition: ASN1.php:1115
const TYPE_INTEGER
Definition: ASN1.php:57
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$mask
Definition: example_042.php:90
if(function_exists( 'posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
$index
Definition: metadata.php:60
Pure-PHP ASN.1 Parser.
Pure-PHP arbitrary precision integer arithmetic library.
$type
$from
$values
$start
Definition: bench.php:8