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