ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ASN1.php
Go to the documentation of this file.
1 <?php
2 
24 namespace phpseclib\File;
25 
28 
36 class ASN1
37 {
44  const CLASS_UNIVERSAL = 0;
45  const CLASS_APPLICATION = 1;
47  const CLASS_PRIVATE = 3;
56  const TYPE_BOOLEAN = 1;
57  const TYPE_INTEGER = 2;
58  const TYPE_BIT_STRING = 3;
59  const TYPE_OCTET_STRING = 4;
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
78  const TYPE_NUMERIC_STRING = 18;
80  const TYPE_TELETEX_STRING = 20; // T61String
82  const TYPE_IA5_STRING = 22;
83  const TYPE_UTC_TIME = 23;
85  const TYPE_GRAPHIC_STRING = 25;
86  const TYPE_VISIBLE_STRING = 26; // ISO646String
87  const TYPE_GENERAL_STRING = 27;
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 
131  var $encoded;
132 
142  var $filters;
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 
207  function decodeBER($encoded)
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 
230  function _decode_ber($encoded, $start = 0)
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) {
290  case self::CLASS_APPLICATION:
291  case self::CLASS_PRIVATE:
292  case self::CLASS_CONTEXT_SPECIFIC:
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) {
336  case self::TYPE_BOOLEAN:
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;
343  case self::TYPE_INTEGER:
344  case self::TYPE_ENUMERATED:
345  $current['content'] = new BigInteger($content, -256);
346  break;
347  case self::TYPE_REAL: // not currently supported
348  return false;
349  case self::TYPE_BIT_STRING:
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;
373  case self::TYPE_OCTET_STRING:
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;
400  case self::TYPE_SEQUENCE:
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;
417  case self::TYPE_OBJECT_IDENTIFIER:
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 */
443  case self::TYPE_NUMERIC_STRING:
444  // 0,1,2,3,4,5,6,7,8,9, and space
445  case self::TYPE_PRINTABLE_STRING:
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
448  case self::TYPE_TELETEX_STRING:
449  // The Teletex character set in CCITT's T61, space, and delete
450  // see http://en.wikipedia.org/wiki/Teletex#Character_sets
451  case self::TYPE_VIDEOTEX_STRING:
452  // The Videotex character set in CCITT's T.100 and T.101, space, and delete
453  case self::TYPE_VISIBLE_STRING:
454  // Printing character sets of international ASCII, and space
455  case self::TYPE_IA5_STRING:
456  // International Alphabet 5 (International ASCII)
457  case self::TYPE_GRAPHIC_STRING:
458  // All registered G sets, and space
459  case self::TYPE_GENERAL_STRING:
460  // All registered C and G sets, space and delete
461  case self::TYPE_UTF8_STRING:
462  // ????
463  case self::TYPE_BMP_STRING:
464  $current['content'] = $content;
465  break;
466  case self::TYPE_UTC_TIME:
467  case self::TYPE_GENERALIZED_TIME:
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']) {
550  case self::TYPE_SEQUENCE:
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;
698  case self::TYPE_OBJECT_IDENTIFIER:
699  return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
700  case self::TYPE_UTC_TIME:
701  case self::TYPE_GENERALIZED_TIME:
702  if (isset($mapping['implicit'])) {
703  $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
704  }
705  return @date($this->format, $decoded['content']);
706  case self::TYPE_BIT_STRING:
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  }
735  case self::TYPE_OCTET_STRING:
736  return base64_encode($decoded['content']);
737  case self::TYPE_NULL:
738  return '';
739  case self::TYPE_BOOLEAN:
740  return $decoded['content'];
741  case self::TYPE_NUMERIC_STRING:
742  case self::TYPE_PRINTABLE_STRING:
743  case self::TYPE_TELETEX_STRING:
744  case self::TYPE_VIDEOTEX_STRING:
745  case self::TYPE_IA5_STRING:
746  case self::TYPE_GRAPHIC_STRING:
747  case self::TYPE_VISIBLE_STRING:
748  case self::TYPE_GENERAL_STRING:
749  case self::TYPE_UNIVERSAL_STRING:
750  case self::TYPE_UTF8_STRING:
751  case self::TYPE_BMP_STRING:
752  return $decoded['content'];
753  case self::TYPE_INTEGER:
754  case self::TYPE_ENUMERATED:
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.
820  case self::TYPE_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;
879  case self::TYPE_CHOICE:
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;
921  case self::TYPE_INTEGER:
922  case self::TYPE_ENUMERATED:
923  if (!isset($mapping['mapping'])) {
924  if (is_numeric($source)) {
925  $source = new BigInteger($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;
940  case self::TYPE_UTC_TIME:
941  case self::TYPE_GENERALIZED_TIME:
942  $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
943  $format.= 'mdHis';
944  $value = @gmdate($format, strtotime($source)) . 'Z';
945  break;
946  case self::TYPE_BIT_STRING:
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  }
978  case self::TYPE_OCTET_STRING:
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;
985  case self::TYPE_OBJECT_IDENTIFIER:
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 
1032  $filters = $this->filters;
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;
1048  case self::TYPE_NUMERIC_STRING:
1049  case self::TYPE_TELETEX_STRING:
1050  case self::TYPE_PRINTABLE_STRING:
1051  case self::TYPE_UNIVERSAL_STRING:
1052  case self::TYPE_UTF8_STRING:
1053  case self::TYPE_BMP_STRING:
1054  case self::TYPE_IA5_STRING:
1055  case self::TYPE_VISIBLE_STRING:
1056  case self::TYPE_VIDEOTEX_STRING:
1057  case self::TYPE_GRAPHIC_STRING:
1058  case self::TYPE_GENERAL_STRING:
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 
1162  function setTimeFormat($format)
1163  {
1164  $this->format = $format;
1165  }
1166 
1175  function loadOIDs($oids)
1176  {
1177  $this->oids = $oids;
1178  }
1179 
1188  function loadFilters($filters)
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 }
const TYPE_VISIBLE_STRING
Definition: ASN1.php:86
loadFilters($filters)
Load filters.
Definition: ASN1.php:1188
const TYPE_UTC_TIME
Definition: ASN1.php:83
$size
Definition: RandomTest.php:84
const CLASS_CONTEXT_SPECIFIC
Definition: ASN1.php:46
const TYPE_NUMERIC_STRING
#-
Definition: ASN1.php:78
$type
$location
Definition: buildRTE.php:44
const TYPE_PRINTABLE_STRING
Definition: ASN1.php:79
asn1map($decoded, $mapping, $special=array())
ASN.1 Map.
Definition: ASN1.php:491
const TYPE_NULL
Definition: ASN1.php:60
$from
const TYPE_IA5_STRING
Definition: ASN1.php:82
const TYPE_CHOICE
#-
Definition: ASN1.php:100
$index
Definition: metadata.php:60
const CLASS_PRIVATE
Definition: ASN1.php:47
const CLASS_UNIVERSAL
#+ Tag Classes
Definition: ASN1.php:44
const TYPE_INTEGER
Definition: ASN1.php:57
_encode_der($source, $mapping, $idx=null, $special=array())
ASN.1 Encode (Helper function)
Definition: ASN1.php:798
const TYPE_SEQUENCE
Definition: ASN1.php:69
decodeBER($encoded)
Parse BER-encoding.
Definition: ASN1.php:207
const CLASS_APPLICATION
Definition: ASN1.php:45
convert($in, $from=self::TYPE_UTF8_STRING, $to=self::TYPE_UTF8_STRING)
String type conversion.
Definition: ASN1.php:1222
$start
Definition: bench.php:8
const TYPE_OCTET_STRING
Definition: ASN1.php:59
const TYPE_UTF8_STRING
Definition: ASN1.php:67
const TYPE_UNIVERSAL_STRING
Definition: ASN1.php:88
$mask
Definition: example_042.php:90
const TYPE_REAL
Definition: ASN1.php:64
_string_shift(&$string, $index=1)
String Shift.
Definition: ASN1.php:1203
$values
const TYPE_OBJECT_IDENTIFIER
Definition: ASN1.php:61
const TYPE_GENERALIZED_TIME
Definition: ASN1.php:84
loadOIDs($oids)
Load OIDs.
Definition: ASN1.php:1175
const TYPE_ENUMERATED
Definition: ASN1.php:65
const TYPE_GRAPHIC_STRING
Definition: ASN1.php:85
$n
Definition: RandomTest.php:85
encodeDER($source, $mapping, $special=array())
ASN.1 Encode.
Definition: ASN1.php:783
if(php_sapi_name() !='cli') $in
Definition: Utf8Test.php:37
const TYPE_TELETEX_STRING
Definition: ASN1.php:80
const TYPE_SET
Definition: ASN1.php:70
_encodeLength($length)
DER-encode the length.
Definition: ASN1.php:1095
Pure-PHP arbitrary precision integer arithmetic library.
_decode_ber($encoded, $start=0)
Parse BER-encoding (Helper function)
Definition: ASN1.php:230
const TYPE_VIDEOTEX_STRING
Definition: ASN1.php:81
_decodeTime($content, $tag)
BER-decode the time.
Definition: ASN1.php:1115
$i
Definition: disco.tpl.php:19
const TYPE_BOOLEAN
#-
Definition: ASN1.php:56
$source
Definition: linkback.php:22
const TYPE_BIT_STRING
Definition: ASN1.php:58
const TYPE_GENERAL_STRING
Definition: ASN1.php:87
$key
Definition: croninfo.php:18
const TYPE_BMP_STRING
Definition: ASN1.php:90
if(function_exists('posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
setTimeFormat($format)
Set the time format.
Definition: ASN1.php:1162
ASN.1 Element.