Subversion Repositories uuid_mac_utils

Rev

Rev 10 | Rev 13 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OidDerConverter.class.php, Version 1.1; Based on version 1.11 of oid.c
10 daniel-mar 5
 * Copyright 2014-2022 Daniel Marschall, ViaThinkSoft
2 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
 
21
// Note: Leading zeros are permitted in dotted notation.
22
 
23
// TODO: define output format as parameter; don't force the user to use hexStrToArray
24
 
25
class OidDerConverter {
26
 
27
        /**
28
         * @arg $str "\x12\x23" or "12 34"
29
         * @return array(0x12, 0x23)
30
         */
31
        // Doesn't need to be public, but it is a nice handy function, especially in the testcases
32
        public static function hexStrToArray($str) {
33
                $out = array();
34
 
35
                $str = str_replace('\x', ' ', $str);
36
                $str = trim($str);
37
                $ary = explode(' ', $str);
38
 
39
                foreach ($ary as &$a) {
40
                        $out[] = hexdec($a);
41
                }
42
 
43
                return $out;
44
        }
45
 
46
        /**
10 daniel-mar 47
         * @return string|false Outputs .<oid> for an absolute OID and <oid> for a relative OID.
2 daniel-mar 48
         */
49
        public static function derToOID($abBinary, $verbose=false) {
50
                $output_oid = array();
51
                $output_absolute = true;
52
 
53
                if (count($abBinary) < 3) {
54
                        if ($verbose) fprintf(STDERR, "Encoded OID must have at least three bytes!\n");
55
                        return false;
56
                }
57
 
58
                $nBinary = count($abBinary);
59
                $ll = gmp_init(0);
60
                $fOK = false;
61
                $fSub = 0; // Subtract value from next number output. Used when encoding {2 48} and up
62
 
63
                // 0 = Universal Class Identifier Tag (can be more than 1 byte, but not in our case)
64
                // 1 = Length part (may have more than 1 byte!)
65
                // 2 = First two arc encoding
66
                // 3 = Encoding of arc three and higher
67
                $part = 0;
68
 
69
                $lengthbyte_count = 0;
70
                $lengthbyte_pos = 0;
71
                $lengthfinished = false;
72
 
73
                $arcBeginning = true;
74
 
10 daniel-mar 75
                $isRelative = null; // to avoid that phpstan complains
76
 
2 daniel-mar 77
                foreach ($abBinary as $nn => &$pb) {
78
                        if ($part == 0) { // Class Tag
79
                                // Leading octet
80
                                // Bit 7 / Bit 6 = Universal (00), Application (01), Context (10), Private(11)
81
                                // Bit 5 = Primitive (0), Constructed (1)
82
                                // Bit 4..0 = 00000 .. 11110 => Tag 0..30, 11111 for Tag > 30 (following bytes with the highest bit as "more" bit)
83
                                // --> We don't need to respect 11111 (class-tag encodes in more than 1 octet)
84
                                //     as we terminate when the tag is not of type OID or RELATEIVE-OID
85
                                // See page 396 of "ASN.1 - Communication between Heterogeneous Systems" by Olivier Dubuisson.
86
 
87
                                // Class: 8. - 7. bit
88
                                // 0 (00) = Universal
89
                                // 1 (01) = Application
90
                                // 2 (10) = Context
91
                                // 3 (11) = Private
92
                                $cl = (($pb & 0xC0) >> 6) & 0x03;
93
                                if ($cl != 0) {
94
                                        if ($verbose) fprintf(STDERR, "Error at type: The OID tags are only defined as UNIVERSAL class tags.\n");
95
                                        return false;
96
                                }
97
 
98
                                // Primitive/Constructed: 6. bit
99
                                // 0 = Primitive
100
                                // 1 = Constructed
101
                                $pc = $pb & 0x20;
102
                                if ($pc != 0) {
103
                                        if ($verbose) fprintf(STDERR, "Error at type: OIDs must be primitive, not constructed.\n");
104
                                        return false;
105
                                }
106
 
107
                                // Tag number: 5. - 1. bit
108
                                $tag = $pb & 0x1F;
109
                                if ($tag == 0x0D) {
110
                                        $isRelative = true;
111
                                } else if ($tag == 0x06) {
112
                                        $isRelative = false;
113
                                } else {
114
                                        if ($verbose) fprintf(STDERR, "Error at type: The tag number is neither an absolute OID (0x06) nor a relative OID (0x0D).\n");
115
                                        return false;
116
                                }
117
 
118
                                // Output
119
                                $output_absolute = !$isRelative;
120
 
121
                                $part++;
122
                        } else if ($part == 1) { // Length
123
                                // Find out the length and save it into $ll
124
 
125
                                // [length] is encoded as follows:
126
                                //  0x00 .. 0x7F = The actual length is in this byte, followed by [data].
127
                                //  0x80 + n     = The length of [data] is spread over the following 'n' bytes. (0 < n < 0x7F)
128
                                //  0x80         = "indefinite length" (only constructed form) -- Invalid
129
                                //  0xFF         = Reserved for further implementations -- Invalid
130
                                //  See page 396 of "ASN.1 - Communication between Heterogeneous Systems" by Olivier Dubuisson.
131
 
132
                                if ($nn == 1) { // The first length byte
133
                                        $lengthbyte_pos = 0;
134
                                        if (($pb & 0x80) != 0) {
135
                                                // 0x80 + n => The length is spread over the following 'n' bytes
136
                                                $lengthfinished = false;
137
                                                $lengthbyte_count = $pb & 0x7F;
138
                                                if ($lengthbyte_count == 0x00) {
139
                                                        if ($verbose) fprintf(STDERR, "Length value 0x80 is invalid (\"indefinite length\") for primitive types.\n");
140
                                                        return false;
141
                                                } else if ($lengthbyte_count == 0x7F) {
142
                                                        if ($verbose) fprintf(STDERR, "Length value 0xFF is reserved for further extensions.\n");
143
                                                        return false;
144
                                                }
145
                                                $fOK = false;
146
                                        } else {
147
                                                // 0x01 .. 0x7F => The actual length
148
 
149
                                                if ($pb == 0x00) {
150
                                                        if ($verbose) fprintf(STDERR, "Length value 0x00 is invalid for an OID.\n");
151
                                                        return false;
152
                                                }
153
 
154
                                                $ll = gmp_init($pb);
155
                                                $lengthfinished = true;
156
                                                $lengthbyte_count = 0;
157
                                                $fOK = true;
158
                                        }
159
                                } else {
160
                                        if ($lengthbyte_count > $lengthbyte_pos) {
161
                                                $ll = gmp_mul($ll, 0x100);
162
                                                $ll = gmp_add($ll, $pb);
163
                                                $lengthbyte_pos++;
164
                                        }
165
 
166
                                        if ($lengthbyte_count == $lengthbyte_pos) {
167
                                                $lengthfinished = true;
168
                                                $fOK = true;
169
                                        }
170
                                }
171
 
172
                                if ($lengthfinished) { // The length is now in $ll
173
                                        if (gmp_cmp($ll, $nBinary - 2 - $lengthbyte_count) != 0) {
174
                                                if ($verbose) fprintf(STDERR, "Invalid length (%d entered, but %s expected)\n", $nBinary - 2, gmp_strval($ll, 10));
175
                                                return false;
176
                                        }
177
                                        $ll = gmp_init(0); // reset for later usage
178
                                        $fOK = true;
179
                                        $part++;
180
                                        if ($isRelative) $part++; // Goto step 3!
181
                                }
182
                        } else if ($part == 2) { // First two arcs
183
                                $first = $pb / 40;
184
                                $second = $pb % 40;
185
                                if ($first > 2) {
186
                                        $first = 2;
187
                                        $output_oid[] = $first;
188
                                        $arcBeginning = true;
189
 
190
                                        if (($pb & 0x80) != 0) {
191
                                                // 2.48 and up => 2+ octets
192
                                                // Output in "part 3"
193
 
11 daniel-mar 194
                                                if ($pb == 0x80) {
2 daniel-mar 195
                                                        if ($verbose) fprintf(STDERR, "Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)\n");
196
                                                        return false;
197
                                                } else {
198
                                                        $arcBeginning = false;
199
                                                }
200
 
201
                                                $ll = gmp_add($ll, ($pb & 0x7F));
202
                                                $fSub = 80;
203
                                                $fOK = false;
204
                                        } else {
205
                                                // 2.0 till 2.47 => 1 octet
206
                                                $second = $pb - 80;
207
                                                $output_oid[] = $second;
208
                                                $arcBeginning = true;
209
                                                $fOK = true;
210
                                                $ll = gmp_init(0);
211
                                        }
212
                                } else {
213
                                        // 0.0 till 0.37 => 1 octet
214
                                        // 1.0 till 1.37 => 1 octet
215
                                        $output_oid[] = $first;
216
                                        $output_oid[] = $second;
217
                                        $arcBeginning = true;
218
                                        $fOK = true;
219
                                        $ll = gmp_init(0);
220
                                }
221
                                $part++;
222
                        } else if ($part == 3) { // Arc three and higher
223
                                if (($pb & 0x80) != 0) {
224
                                        if ($arcBeginning && ($pb == 0x80)) {
225
                                                if ($verbose) fprintf(STDERR, "Encoding error. Illegal 0x80 paddings. (See Rec. ITU-T X.690, clause 8.19.2)\n");
226
                                                return false;
227
                                        } else {
228
                                                $arcBeginning = false;
229
                                        }
230
 
231
                                        $ll = gmp_mul($ll, 0x80);
232
                                        $ll = gmp_add($ll, ($pb & 0x7F));
233
                                        $fOK = false;
234
                                } else {
235
                                        $fOK = true;
236
                                        $ll = gmp_mul($ll, 0x80);
237
                                        $ll = gmp_add($ll, $pb);
238
                                        $ll = gmp_sub($ll, $fSub);
239
                                        $output_oid[] = gmp_strval($ll, 10);
240
 
241
                                        // Happens only if 0x80 paddings are allowed
242
                                        // $fOK = gmp_cmp($ll, 0) >= 0;
243
                                        $ll = gmp_init(0);
244
                                        $fSub = 0;
245
                                        $arcBeginning = true;
246
                                }
247
                        }
248
                }
249
 
250
                if (!$fOK) {
251
                        if ($verbose) fprintf(STDERR, "Encoding error. The OID is not constructed properly.\n");
252
                        return false;
253
                }
254
 
255
                return ($output_absolute ? '.' : '').implode('.', $output_oid);
256
        }
257
 
258
        // Doesn't need to be public, but it is a nice handy function, especially in the testcases
259
        public static function hexarrayToStr($ary, $nCHex=false) {
260
                $str = '';
261
                if ($ary === false) return false;
262
                foreach ($ary as &$a) {
263
                        if ($nCHex) {
264
                                $str .= sprintf("\"\\x%02X", $a);
265
                        } else {
266
                                $str .= sprintf("%02X ", $a);
267
                        }
268
                }
269
                return trim($str);
270
        }
271
 
272
        public static function oidToDER($oid, $isRelative=false, $verbose=false) {
273
                if ($oid[0] == '.') { // MIB notation
274
                        $oid = substr($oid, 1);
275
                        $isRelative = false;
276
                }
277
 
278
                $cl = 0x00; // Class. Always UNIVERSAL (00)
279
 
280
                // Tag for Universal Class
281
                if ($isRelative) {
282
                        $cl |= 0x0D;
283
                } else {
284
                        $cl |= 0x06;
285
                }
286
 
287
                $arcs = explode('.', $oid);
288
 
289
                $b = 0;
290
                $isjoint = false;
291
                $abBinary = array();
292
 
293
                if ((!$isRelative) && (count($arcs) < 2)) {
294
                        if ($verbose) fprintf(STDERR, "Encoding error. The minimum depth of an encodeable absolute OID is 2. (e.g. 2.999)\n");
295
                        return false;
296
                }
297
 
298
                foreach ($arcs as $n => &$arc) {
299
                        if (!preg_match('@^\d+$@', $arc)) {
300
                                if ($verbose) fprintf(STDERR, "Encoding error. Arc '%s' is invalid.\n", $arc);
301
                                return false;
302
                        }
303
 
304
                        $l = gmp_init($arc, 10);
305
 
306
                        if ((!$isRelative) && ($n == 0)) {
307
                                if (gmp_cmp($l, 2) > 0) {
308
                                        if ($verbose) fprintf(STDERR, "Encoding error. The top arc is limited to 0, 1 and 2.\n");
309
                                        return false;
310
                                }
311
                                $b += 40 * gmp_intval($l);
312
                                $isjoint = gmp_cmp($l, 2) == 0;
313
                        } else if ((!$isRelative) && ($n == 1)) {
314
                                if ((!$isjoint) && (gmp_cmp($l, 39) > 0)) {
315
                                        if ($verbose) fprintf(STDERR, "Encoding error. The second arc is limited to 0..39 for root arcs 0 and 1.\n");
316
                                        return false;
317
                                }
318
 
319
                                if (gmp_cmp($l, 47) > 0) {
320
                                        $l = gmp_add($l, 80);
321
                                        self::makeBase128($l, 1, $abBinary);
322
                                } else {
323
                                        $b += gmp_intval($l);
324
                                        $abBinary[] = $b;
325
                                }
326
                        } else {
327
                                self::makeBase128($l, 1, $abBinary);
328
                        }
329
                }
330
 
331
                $output = array();
332
 
333
                // Write class-tag
334
                $output[] = $cl;
335
 
336
                // Write length
337
                $nBinary = count($abBinary);
338
                if ($nBinary <= 0x7F) {
339
                        $output[] = $nBinary;
340
                } else {
341
                        $lengthCount = 0;
342
                        $nBinaryWork = $nBinary;
343
                        do {
344
                                $lengthCount++;
345
                                $nBinaryWork /= 0x100;
346
                        } while ($nBinaryWork > 0);
347
 
348
                        if ($lengthCount >= 0x7F) {
349
                                if ($verbose) fprintf(STDERR, "The length cannot be encoded.\n");
350
                                return false;
351
                        }
352
 
353
                        $output[] = 0x80 + $lengthCount;
354
 
355
                        $nBinaryWork = $nBinary;
356
                        do {
10 daniel-mar 357
                                $output[] = $nBinaryWork & 0xFF;
2 daniel-mar 358
                                $nBinaryWork /= 0x100;
359
                        } while ($nBinaryWork > 0);
360
                }
361
 
362
                foreach ($abBinary as $b) {
363
                        $output[] = $b;
364
                }
365
 
366
                return $output;
367
        }
368
 
369
        protected static function makeBase128($l, $first, &$abBinary) {
370
                if (gmp_cmp($l, 127) > 0) {
371
                        $l2 = gmp_div($l, 128);
372
                        self::makeBase128($l2 , 0, $abBinary);
373
                }
374
                $l = gmp_mod($l, 128);
375
 
376
                if ($first) {
377
                        $abBinary[] = gmp_intval($l);
378
                } else {
379
                        $abBinary[] = 0x80 | gmp_intval($l);
380
                }
381
        }
382
}