]> git.mxchange.org Git - friendica.git/blob - library/asn1.php
Merge pull request #6196 from annando/notices
[friendica.git] / library / asn1.php
1 <?php
2
3 // ASN.1 parsing library
4 // Attribution: http://www.krisbailey.com
5 // license: unknown
6 // modified: Mike Macgrivin mike@macgirvin.com 6-oct-2010 to support Salmon auto-discovery
7 // modified: Tobias Diekershoff 28-jul-2016 adding an intval in line 162 to make PHP7 happy
8 // from openssl public keys
9
10
11 class ASN_BASE {
12         public $asnData = null;
13         private $cursor = 0;
14         private $parent = null;
15         
16         public static $ASN_MARKERS = array(
17                 'ASN_UNIVERSAL'         => 0x00,
18                 'ASN_APPLICATION'       => 0x40,
19                 'ASN_CONTEXT'           => 0x80,
20                 'ASN_PRIVATE'           => 0xC0,
21
22                 'ASN_PRIMITIVE'         => 0x00,
23                 'ASN_CONSTRUCTOR'       => 0x20,
24
25                 'ASN_LONG_LEN'          => 0x80,
26                 'ASN_EXTENSION_ID'      => 0x1F,
27                 'ASN_BIT'               => 0x80,
28         );
29         
30         public static $ASN_TYPES = array(
31                 1       => 'ASN_BOOLEAN',
32                 2       => 'ASN_INTEGER',
33                 3       => 'ASN_BIT_STR',
34                 4       => 'ASN_OCTET_STR',
35                 5       => 'ASN_NULL',
36                 6       => 'ASN_OBJECT_ID',
37                 9       => 'ASN_REAL',
38                 10      => 'ASN_ENUMERATED',
39                 13      => 'ASN_RELATIVE_OID',
40                 48      => 'ASN_SEQUENCE',
41                 49      => 'ASN_SET',
42                 19      => 'ASN_PRINT_STR',
43                 22      => 'ASN_IA5_STR',
44                 23      => 'ASN_UTC_TIME',
45                 24      => 'ASN_GENERAL_TIME',
46         );
47
48         function __construct($v = false)
49         {
50                 if (false !== $v) {
51                         $this->asnData = $v;
52                         if (is_array($this->asnData)) {
53                                 foreach ($this->asnData as $key => $value) {
54                                         if (is_object($value)) {
55                                                 $this->asnData[$key]->setParent($this);
56                                         }
57                                 }
58                         } else {
59                                 if (is_object($this->asnData)) {
60                                         $this->asnData->setParent($this);
61                                 }
62                         }
63                 }
64         }
65         
66         public function setParent($parent)
67         {
68                 if (false !== $parent) {
69                         $this->parent = $parent;
70                 }
71         }
72         
73         /**
74          * This function will take the markers and types arrays and
75          * dynamically generate classes that extend this class for each one,
76          * and also define constants for them.
77          */
78         public static function generateSubclasses()
79         {
80                 define('ASN_BASE', 0);
81                 foreach (self::$ASN_MARKERS as $name => $bit)
82                         self::makeSubclass($name, $bit);
83                 foreach (self::$ASN_TYPES as $bit => $name)
84                         self::makeSubclass($name, $bit);
85         }
86         
87         /**
88          * Helper function for generateSubclasses()
89          */
90         public static function makeSubclass($name, $bit)
91         {
92                 define($name, $bit);
93                 eval("class ".$name." extends ASN_BASE {}");
94         }
95         
96         /**
97          * This function reset's the internal cursor used for value iteration.
98          */
99         public function reset()
100         {
101                 $this->cursor = 0;
102         }
103         
104         /**
105          * This function catches calls to get the value for the type, typeName, value, values, and data
106          * from the object.  For type calls we just return the class name or the value of the constant that
107          * is named the same as the class.
108          */
109         public function __get($name)
110         {
111                 if ('type' == $name) {
112                         // int flag of the data type
113                         return constant(get_class($this));
114                 } elseif ('typeName' == $name) {
115                         // name of the data type
116                         return get_class($this);
117                 } elseif ('value' == $name) {
118                         // will always return one value and can be iterated over with:
119                         // while ($v = $obj->value) { ...
120                         // because $this->asnData["invalid key"] will return false
121                         return is_array($this->asnData) ? $this->asnData[$this->cursor++] : $this->asnData;
122                 } elseif ('values' == $name) {
123                         // will always return an array
124                         return is_array($this->asnData) ? $this->asnData : array($this->asnData);
125                 } elseif ('data' == $name) {
126                         // will always return the raw data
127                         return $this->asnData;
128                 }
129         }
130
131         /**
132          * Parse an ASN.1 binary string.
133          * 
134          * This function takes a binary ASN.1 string and parses it into it's respective
135          * pieces and returns it.  It can optionally stop at any depth.
136          *
137          * @param       string  $string         The binary ASN.1 String
138          * @param       int     $level          The current parsing depth level
139          * @param       int     $maxLevel       The max parsing depth level
140          * @return      ASN_BASE        The array representation of the ASN.1 data contained in $string
141          */
142         public static function parseASNString($string=false, $level=1, $maxLevels=false){
143                 if (!class_exists('ASN_UNIVERSAL'))
144                         self::generateSubclasses();
145                 if ($level>$maxLevels && $maxLevels)
146                         return array(new ASN_BASE($string));
147                 $parsed = array();
148                 $endLength = strlen($string);
149                 $bigLength = $length = $type = $dtype = $p = 0;
150                 while ($p<$endLength){
151                         $type = ord($string[$p++]);
152                         $dtype = ($type & 192) >> 6;
153                         if ($type==0){ // if we are type 0, just continue
154                         } else {
155                                 $length = ord($string[$p++]);
156                                 if (($length & ASN_LONG_LEN)==ASN_LONG_LEN){
157                                         $tempLength = 0;
158                                         for ($x=0; $x<($length & (ASN_LONG_LEN-1)); $x++){
159                                                 $tempLength = @ord($string[$p++]) + ($tempLength * 256);
160                                         }
161                                         $length = $tempLength;
162                                 }
163                                 $data = substr($string, $p, intval($length));
164                                 $parsed[] = self::parseASNData($type, $data, $level, $maxLevels);
165                                 $p = $p + $length;
166                         }
167                 }
168                 return $parsed;
169         }
170
171         /**
172          * Parse an ASN.1 field value.
173          * 
174          * This function takes a binary ASN.1 value and parses it according to it's specified type
175          *
176          * @param       int     $type           The type of data being provided
177          * @param       string  $data           The raw binary data string
178          * @param       int     $level          The current parsing depth
179          * @param       int     $maxLevels      The max parsing depth
180          * @return      mixed   The data that was parsed from the raw binary data string
181          */
182         public static function parseASNData($type, $data, $level, $maxLevels){
183                 $type = $type%50; // strip out context
184                 switch ($type){
185                         default:
186                                 return new ASN_BASE($data);
187                         case ASN_BOOLEAN:
188                                 return new ASN_BOOLEAN((bool)$data);
189                         case ASN_INTEGER:
190                                 return new ASN_INTEGER(strtr(base64_encode($data),'+/','-_'));
191                         case ASN_BIT_STR:
192                                 return new ASN_BIT_STR(self::parseASNString($data, $level+1, $maxLevels));
193                         case ASN_OCTET_STR:
194                                 return new ASN_OCTET_STR($data);
195                         case ASN_NULL:
196                                 return new ASN_NULL(null);
197                         case ASN_REAL:
198                                 return new ASN_REAL($data);
199                         case ASN_ENUMERATED:
200                                 return new ASN_ENUMERATED(self::parseASNString($data, $level+1, $maxLevels));
201                         case ASN_RELATIVE_OID: // I don't really know how this works and don't have an example :-)
202                                                 // so, lets just return it ...
203                                 return new ASN_RELATIVE_OID($data);
204                         case ASN_SEQUENCE:
205                                 return new ASN_SEQUENCE(self::parseASNString($data, $level+1, $maxLevels));
206                         case ASN_SET:
207                                 return new ASN_SET(self::parseASNString($data, $level+1, $maxLevels));
208                         case ASN_PRINT_STR:
209                                 return new ASN_PRINT_STR($data);
210                         case ASN_IA5_STR:
211                                 return new ASN_IA5_STR($data);
212                         case ASN_UTC_TIME:
213                                 return new ASN_UTC_TIME($data);
214                         case ASN_GENERAL_TIME:
215                                 return new ASN_GENERAL_TIME($data);
216                         case ASN_OBJECT_ID:
217                                 return new ASN_OBJECT_ID(self::parseOID($data));
218                 }
219         }
220
221         /**
222          * Parse an ASN.1 OID value.
223          * 
224          * This takes the raw binary string that represents an OID value and parses it into its
225          * dot notation form.  example - 1.2.840.113549.1.1.5
226          * look up OID's here: http://www.oid-info.com/
227          * (the multi-byte OID section can be done in a more efficient way, I will fix it later)
228          *
229          * @param       string  $data           The raw binary data string
230          * @return      string  The OID contained in $data
231          */
232         public static function parseOID($string){
233                 $ret = floor(ord($string[0])/40).".";
234                 $ret .= (ord($string[0]) % 40);
235                 $build = array();
236                 $cs = 0;        
237                 
238                 for ($i=1; $i<strlen($string); $i++){
239                         $v = ord($string[$i]);
240                         if ($v>127){
241                                 $build[] = ord($string[$i])-ASN_BIT;
242                         } elseif ($build){
243                                 // do the build here for multibyte values
244                                 $build[] = ord($string[$i])-ASN_BIT;
245                                 // you know, it seems there should be a better way to do this...
246                                 $build = array_reverse($build);
247                                 $num = 0;
248                                 for ($x=0; $x<count($build); $x++){
249                                         $mult = $x==0?1:pow(256, $x);
250                                         if ($x+1==count($build)){
251                                                 $value = ((($build[$x] & (ASN_BIT-1)) >> $x)) * $mult;
252                                         } else {
253                                                 $value = ((($build[$x] & (ASN_BIT-1)) >> $x) ^ ($build[$x+1] << (7 - $x) & 255)) * $mult;
254                                         }
255                                         $num += $value;
256                                 }
257                                 $ret .= ".".$num;
258                                 $build = array(); // start over
259                         } else {
260                                 $ret .= ".".$v;
261                                 $build = array();
262                         }
263                 }
264                 return $ret;
265         }
266         
267         public static function printASN($x, $indent=''){
268                 if (is_object($x)) {
269                         echo $indent.$x->typeName."\n";
270                         if (ASN_NULL == $x->type) return;
271                         if (is_array($x->data)) {
272                                 while ($d = $x->value) {
273                                         echo self::printASN($d, $indent.'.  ');
274                                 }
275                                 $x->reset();
276                         } else {
277                                 echo self::printASN($x->data, $indent.'.  ');
278                         }
279                 } elseif (is_array($x)) {
280                         foreach ($x as $d) {
281                                 echo self::printASN($d, $indent);
282                         }
283                 } else {
284                         if (preg_match('/[^[:print:]]/', $x))   // if we have non-printable characters that would
285                                 $x = base64_encode($x);         // mess up the console, then print the base64 of them...
286                         echo $indent.$x."\n";
287                 }
288         }
289
290         
291 }
292