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