blob: bde15aa95058bd2f473f15c21e8d98d6655a6f96 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.libg.asn1;
2
3import java.io.*;
4import java.text.*;
5import java.util.*;
6
7public class BER implements Types {
Stuart McCulloch2286f232012-06-15 13:27:53 +00008 DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss\\Z");
Stuart McCullochbb014372012-06-07 21:57:32 +00009
Stuart McCulloch2286f232012-06-15 13:27:53 +000010 final DataInputStream xin;
11 long position;
Stuart McCullochbb014372012-06-07 21:57:32 +000012
Stuart McCulloch2286f232012-06-15 13:27:53 +000013 public BER(InputStream in) {
14 this.xin = new DataInputStream(in);
15 }
Stuart McCullochbb014372012-06-07 21:57:32 +000016
Stuart McCulloch2286f232012-06-15 13:27:53 +000017 public void dump(PrintStream out) throws Exception {
18 int type = readByte();
19 long length = readLength();
20 if (type == -1 || length == -1)
21 throw new EOFException("Empty file");
22 dump(out, type, length, "");
23 }
Stuart McCullochbb014372012-06-07 21:57:32 +000024
Stuart McCulloch2286f232012-06-15 13:27:53 +000025 void dump(PrintStream out, int type, long length, String indent) throws Exception {
26 int clss = type >> 6;
27 int nmbr = type & 0x1F;
28 boolean cnst = (type & 0x20) != 0;
Stuart McCullochbb014372012-06-07 21:57:32 +000029
Stuart McCulloch2286f232012-06-15 13:27:53 +000030 String tag = "[" + nmbr + "]";
31 if (clss == 0)
32 tag = TAGS[nmbr];
Stuart McCullochbb014372012-06-07 21:57:32 +000033
Stuart McCulloch2286f232012-06-15 13:27:53 +000034 if (cnst) {
35 System.err.printf("%5d %s %s %s%n", length, indent, CLASSES[clss], tag);
36 while (length > 1) {
37 long atStart = getPosition();
38 int t2 = read();
39 long l2 = readLength();
40 dump(out, t2, l2, indent + " ");
41 length -= getPosition() - atStart;
42 }
43 } else {
44 assert length < Integer.MAX_VALUE;
45 assert length >= 0;
46 byte[] data = new byte[(int) length];
47 readFully(data);
48 String summary;
Stuart McCullochbb014372012-06-07 21:57:32 +000049
Stuart McCulloch2286f232012-06-15 13:27:53 +000050 switch (nmbr) {
51 case BOOLEAN :
52 assert length == 1;
53 summary = data[0] != 0 ? "true" : "false";
54 break;
Stuart McCullochbb014372012-06-07 21:57:32 +000055
Stuart McCulloch2286f232012-06-15 13:27:53 +000056 case INTEGER :
57 long n = toLong(data);
58 summary = n + "";
59 break;
Stuart McCullochbb014372012-06-07 21:57:32 +000060
Stuart McCulloch2286f232012-06-15 13:27:53 +000061 case UTF8_STRING :
62 case IA5STRING :
63 case VISIBLE_STRING :
64 case UNIVERSAL_STRING :
65 case PRINTABLE_STRING :
66 case UTCTIME :
67 summary = new String(data, "UTF-8");
68 break;
Stuart McCullochbb014372012-06-07 21:57:32 +000069
Stuart McCulloch2286f232012-06-15 13:27:53 +000070 case OBJECT_IDENTIFIER :
71 summary = readOID(data);
72 break;
Stuart McCullochbb014372012-06-07 21:57:32 +000073
Stuart McCulloch2286f232012-06-15 13:27:53 +000074 case GENERALIZED_TIME :
75 case GRAPHIC_STRING :
76 case GENERAL_STRING :
77 case CHARACTER_STRING :
Stuart McCullochbb014372012-06-07 21:57:32 +000078
Stuart McCulloch2286f232012-06-15 13:27:53 +000079 case REAL :
80 case EOC :
81 case BIT_STRING :
82 case OCTET_STRING :
83 case NULL :
84 case OBJECT_DESCRIPTOR :
85 case EXTERNAL :
86 case ENUMERATED :
87 case EMBEDDED_PDV :
88 case RELATIVE_OID :
89 case NUMERIC_STRING :
90 case T61_STRING :
91 case VIDEOTEX_STRING :
92 case BMP_STRING :
93 default :
94 StringBuilder sb = new StringBuilder();
95 for (int i = 0; i < 10 && i < data.length; i++) {
96 sb.append(Integer.toHexString(data[i]));
97 }
98 if (data.length > 10) {
99 sb.append("...");
100 }
101 summary = sb.toString();
102 break;
103 }
104 out.printf("%5d %s %s %s %s\n", length, indent, CLASSES[clss], tag, summary);
105 }
106 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000107
Stuart McCulloch2286f232012-06-15 13:27:53 +0000108 long toLong(byte[] data) {
109 if (data[0] < 0) {
110 for (int i = 0; i < data.length; i++)
111 data[i] = (byte) (0xFF ^ data[i]);
Stuart McCullochbb014372012-06-07 21:57:32 +0000112
Stuart McCulloch2286f232012-06-15 13:27:53 +0000113 return -(toLong(data) + 1);
114 }
115 long n = 0;
116 for (int i = 0; i < data.length; i++) {
117 n = n * 256 + data[i];
118 }
119 return n;
120 }
121
122 /**
123 * 8.1.3.3 For the definite form, the length octets shall consist of one or
124 * more octets, and shall represent the number of octets in the contents
125 * octets using either the short form (see 8.1.3.4) or the long form (see
126 * 8.1.3.5) as a sender's option. NOTE – The short form can only be used if
127 * the number of octets in the contents octets is less than or equal to 127.
128 * 8.1.3.4 In the short form, the length octets shall consist of a single
129 * octet in which bit 8 is zero and bits 7 to 1 encode the number of octets
130 * in the contents octets (which may be zero), as an unsigned binary integer
131 * with bit 7 as the most significant bit. EXAMPLE L = 38 can be encoded as
132 * 001001102 8.1.3.5 In the long form, the length octets shall consist of an
133 * initial octet and one or more subsequent octets. The initial octet shall
134 * be encoded as follows: a) bit 8 shall be one; b) bits 7 to 1 shall encode
135 * the number of subsequent octets in the length octets, as an unsigned
136 * binary integer with bit 7 as the most significant bit; c) the value
137 * 111111112 shall not be used. ISO/IEC 8825-1:2003 (E) NOTE 1 – This
138 * restriction is introduced for possible future extension. Bits 8 to 1 of
139 * the first subsequent octet, followed by bits 8 to 1 of the second
140 * subsequent octet, followed in turn by bits 8 to 1 of each further octet
141 * up to and including the last subsequent octet, shall be the encoding of
142 * an unsigned binary integer equal to the number of octets in the contents
143 * octets, with bit 8 of the first subsequent octet as the most significant
144 * bit. EXAMPLE L = 201 can be encoded as: 100000012 110010012 NOTE 2 – In
145 * the long form, it is a sender's option whether to use more length octets
146 * than the minimum necessary. 8.1.3.6 For the indefinite form, the length
147 * octets indicate that the contents octets are terminated by
148 * end-of-contents octets (see 8.1.5), and shall consist of a single octet.
149 * 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to 1
150 * set to zero. 8.1.3.6.2 If this form of length is used, then
151 * end-of-contents octets (see 8.1.5) shall be present in the encoding
152 * following the contents octets. 8.1.4 Contents octets The contents octets
153 * shall consist of zero, one or more octets, and shall encode the data
154 * value as specified in subsequent clauses. NOTE – The contents octets
155 * depend on the type of the data value; subsequent clauses follow the same
156 * sequence as the definition of types in ASN.1. 8.1.5 End-of-contents
157 * octets The end-of-contents octets shall be present if the length is
158 * encoded as specified in 8.1.3.6, otherwise they shall not be present. The
159 * end-of-contents octets shall consist of two zero octets. NOTE – The
160 * end-of-contents octets can be considered as the encoding of a value whose
161 * tag is universal class, whose form is primitive, whose number of the tag
162 * is zero, and whose contents are absent, thus: End-of-contents Length
163 * Contents 0016 0016 Absent
164 *
165 * @return
166 */
167 private long readLength() throws IOException {
168 long n = readByte();
169 if (n > 0) {
170 // short form
171 return n;
172 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000173 // long form
174 int count = (int) (n & 0x7F);
175 if (count == 0) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000176 // indefinite form
177 return 0;
Stuart McCullochbb014372012-06-07 21:57:32 +0000178 }
179 n = 0;
180 while (count-- > 0) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000181 n = n * 256 + read();
Stuart McCullochbb014372012-06-07 21:57:32 +0000182 }
183 return n;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000184 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000185
Stuart McCulloch2286f232012-06-15 13:27:53 +0000186 private int readByte() throws IOException {
187 position++;
188 return xin.readByte();
189 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000190
Stuart McCulloch2286f232012-06-15 13:27:53 +0000191 private void readFully(byte[] data) throws IOException {
192 position += data.length;
193 xin.readFully(data);
194 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000195
Stuart McCulloch2286f232012-06-15 13:27:53 +0000196 private long getPosition() {
197 return position;
198 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000199
Stuart McCulloch2286f232012-06-15 13:27:53 +0000200 private int read() throws IOException {
201 position++;
202 return xin.read();
203 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000204
Stuart McCulloch2286f232012-06-15 13:27:53 +0000205 String readOID(byte[] data) {
206 StringBuilder sb = new StringBuilder();
207 sb.append((0xFF & data[0]) / 40);
208 sb.append(".");
209 sb.append((0xFF & data[0]) % 40);
Stuart McCullochbb014372012-06-07 21:57:32 +0000210
Stuart McCulloch2286f232012-06-15 13:27:53 +0000211 int i = 0;
212 while (++i < data.length) {
213 int n = 0;
214 while (data[i] < 0) {
215 n = n * 128 + (0x7F & data[i]);
216 i++;
217 }
218 n = n * 128 + data[i];
219 sb.append(".");
220 sb.append(n);
221 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000222
Stuart McCulloch2286f232012-06-15 13:27:53 +0000223 return sb.toString();
224 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000225
Stuart McCulloch2286f232012-06-15 13:27:53 +0000226 int getPayloadLength(PDU pdu) throws Exception {
227 switch (pdu.getTag() & 0x1F) {
228 case EOC :
229 return 1;
Stuart McCullochbb014372012-06-07 21:57:32 +0000230
Stuart McCulloch2286f232012-06-15 13:27:53 +0000231 case BOOLEAN :
232 return 1;
Stuart McCullochbb014372012-06-07 21:57:32 +0000233
Stuart McCulloch2286f232012-06-15 13:27:53 +0000234 case INTEGER :
235 return size(pdu.getInt());
Stuart McCullochbb014372012-06-07 21:57:32 +0000236
Stuart McCulloch2286f232012-06-15 13:27:53 +0000237 case UTF8_STRING :
238 String s = pdu.getString();
239 byte[] encoded = s.getBytes("UTF-8");
240 return encoded.length;
Stuart McCullochbb014372012-06-07 21:57:32 +0000241
Stuart McCulloch2286f232012-06-15 13:27:53 +0000242 case IA5STRING :
243 case VISIBLE_STRING :
244 case UNIVERSAL_STRING :
245 case PRINTABLE_STRING :
246 case GENERALIZED_TIME :
247 case GRAPHIC_STRING :
248 case GENERAL_STRING :
249 case CHARACTER_STRING :
250 case UTCTIME :
251 case NUMERIC_STRING : {
252 String str = pdu.getString();
253 encoded = str.getBytes("ASCII");
254 return encoded.length;
255 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000256
Stuart McCulloch2286f232012-06-15 13:27:53 +0000257 case OBJECT_IDENTIFIER :
258 case REAL :
259 case BIT_STRING :
260 return pdu.getBytes().length;
Stuart McCullochbb014372012-06-07 21:57:32 +0000261
Stuart McCulloch2286f232012-06-15 13:27:53 +0000262 case OCTET_STRING :
263 case NULL :
264 case OBJECT_DESCRIPTOR :
265 case EXTERNAL :
266 case ENUMERATED :
267 case EMBEDDED_PDV :
268 case RELATIVE_OID :
269 case T61_STRING :
270 case VIDEOTEX_STRING :
271 case BMP_STRING :
272 return pdu.getBytes().length;
Stuart McCullochbb014372012-06-07 21:57:32 +0000273
Stuart McCulloch2286f232012-06-15 13:27:53 +0000274 default :
275 throw new IllegalArgumentException("Invalid type: " + pdu);
276 }
277 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000278
Stuart McCulloch2286f232012-06-15 13:27:53 +0000279 int size(long value) {
280 if (value < 128)
281 return 1;
Stuart McCullochbb014372012-06-07 21:57:32 +0000282
Stuart McCulloch2286f232012-06-15 13:27:53 +0000283 if (value <= 0xFF)
284 return 2;
Stuart McCullochbb014372012-06-07 21:57:32 +0000285
Stuart McCulloch2286f232012-06-15 13:27:53 +0000286 if (value <= 0xFFFF)
287 return 3;
Stuart McCullochbb014372012-06-07 21:57:32 +0000288
Stuart McCulloch2286f232012-06-15 13:27:53 +0000289 if (value <= 0xFFFFFF)
290 return 4;
Stuart McCullochbb014372012-06-07 21:57:32 +0000291
Stuart McCulloch2286f232012-06-15 13:27:53 +0000292 if (value <= 0xFFFFFFFF)
293 return 5;
Stuart McCullochbb014372012-06-07 21:57:32 +0000294
Stuart McCulloch2286f232012-06-15 13:27:53 +0000295 if (value <= 0xFFFFFFFFFFL)
296 return 6;
Stuart McCullochbb014372012-06-07 21:57:32 +0000297
Stuart McCulloch2286f232012-06-15 13:27:53 +0000298 if (value <= 0xFFFFFFFFFFFFL)
299 return 7;
Stuart McCullochbb014372012-06-07 21:57:32 +0000300
Stuart McCulloch2286f232012-06-15 13:27:53 +0000301 if (value <= 0xFFFFFFFFFFFFFFL)
302 return 8;
Stuart McCullochbb014372012-06-07 21:57:32 +0000303
Stuart McCulloch2286f232012-06-15 13:27:53 +0000304 if (value <= 0xFFFFFFFFFFFFFFFFL)
305 return 9;
Stuart McCullochbb014372012-06-07 21:57:32 +0000306
Stuart McCulloch2286f232012-06-15 13:27:53 +0000307 throw new IllegalArgumentException("length too long");
308 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000309
Stuart McCulloch2286f232012-06-15 13:27:53 +0000310 public void write(OutputStream out, PDU pdu) throws Exception {
311 byte id = 0;
Stuart McCullochbb014372012-06-07 21:57:32 +0000312
Stuart McCulloch2286f232012-06-15 13:27:53 +0000313 switch (pdu.getClss()) {
314 case UNIVERSAL :
315 id |= 0;
316 break;
317 case APPLICATION :
318 id |= 0x40;
319 break;
320 case CONTEXT :
321 id |= 0x80;
322 break;
323 case PRIVATE :
324 id |= 0xC0;
325 break;
326 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000327
Stuart McCulloch2286f232012-06-15 13:27:53 +0000328 if (pdu.isConstructed())
329 id |= 0x20;
Stuart McCullochbb014372012-06-07 21:57:32 +0000330
Stuart McCulloch2286f232012-06-15 13:27:53 +0000331 int tag = pdu.getTag();
332 if (tag >= 0 && tag < 31) {
333 id |= tag;
334 } else {
335 throw new UnsupportedOperationException("Cant do tags > 30");
336 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000337
Stuart McCulloch2286f232012-06-15 13:27:53 +0000338 out.write(id);
Stuart McCullochbb014372012-06-07 21:57:32 +0000339
Stuart McCulloch2286f232012-06-15 13:27:53 +0000340 int length = getPayloadLength(pdu);
341 int size = size(length);
342 if (size == 1) {
343 out.write(length);
344 } else {
345 out.write(size);
346 while (--size >= 0) {
347 byte data = (byte) ((length >> (size * 8)) & 0xFF);
348 out.write(data);
349 }
350 }
351 writePayload(out, pdu);
352 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000353
Stuart McCulloch2286f232012-06-15 13:27:53 +0000354 void writePayload(OutputStream out, PDU pdu) throws Exception {
355 switch (pdu.getTag()) {
356 case EOC :
357 out.write(0);
358 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000359
Stuart McCulloch2286f232012-06-15 13:27:53 +0000360 case BOOLEAN :
361 if (pdu.getBoolean())
362 out.write(-1);
363 else
364 out.write(0);
365 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000366
Stuart McCulloch2286f232012-06-15 13:27:53 +0000367 case ENUMERATED :
368 case INTEGER : {
369 long value = pdu.getInt();
370 int size = size(value);
371 for (int i = size; i >= 0; i--) {
372 byte b = (byte) ((value >> (i * 8)) & 0xFF);
373 out.write(b);
374 }
375 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000376
Stuart McCulloch2286f232012-06-15 13:27:53 +0000377 case BIT_STRING : {
378 byte bytes[] = pdu.getBytes();
379 int unused = bytes[0];
380 assert unused <= 7;
381 int[] mask = {
382 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1
383 };
384 bytes[bytes.length - 1] &= (byte) mask[unused];
385 out.write(bytes);
386 break;
387 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000388
Stuart McCulloch2286f232012-06-15 13:27:53 +0000389 case RELATIVE_OID :
390 case OBJECT_IDENTIFIER : {
391 int[] oid = pdu.getOID();
392 assert oid.length > 2;
393 assert oid[0] < 4;
394 assert oid[1] < 40;
395 byte top = (byte) (oid[0] * 40 + oid[1]);
396 out.write(top);
397 for (int i = 2; i < oid.length; i++) {
398 putOid(out, oid[i]);
399 }
400 break;
401 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000402
Stuart McCulloch2286f232012-06-15 13:27:53 +0000403 case OCTET_STRING : {
404 byte bytes[] = pdu.getBytes();
405 out.write(bytes);
406 break;
407 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000408
Stuart McCulloch2286f232012-06-15 13:27:53 +0000409 case NULL :
410 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000411
Stuart McCulloch2286f232012-06-15 13:27:53 +0000412 case BMP_STRING :
413 case GRAPHIC_STRING :
414 case VISIBLE_STRING :
415 case GENERAL_STRING :
416 case UNIVERSAL_STRING :
417 case CHARACTER_STRING :
418 case NUMERIC_STRING :
419 case PRINTABLE_STRING :
420 case VIDEOTEX_STRING :
421 case T61_STRING :
422 case REAL :
423 case EMBEDDED_PDV :
424 case EXTERNAL :
425 throw new UnsupportedEncodingException("dont know real, embedded PDV or external");
Stuart McCullochbb014372012-06-07 21:57:32 +0000426
Stuart McCulloch2286f232012-06-15 13:27:53 +0000427 case UTF8_STRING : {
428 String s = pdu.getString();
429 byte[] data = s.getBytes("UTF-8");
430 out.write(data);
431 break;
432 }
433
434 case OBJECT_DESCRIPTOR :
435 case IA5STRING :
436 String s = pdu.getString();
437 byte[] data = s.getBytes("ASCII");
438 out.write(data);
439 break;
440
441 case SEQUENCE :
442 case SET : {
443 PDU pdus[] = pdu.getChildren();
444 for (PDU p : pdus) {
445 write(out, p);
446 }
447 }
448
449 case UTCTIME :
450 case GENERALIZED_TIME :
451 Date date = pdu.getDate();
452 String ss = df.format(date);
453 byte d[] = ss.getBytes("ASCII");
454 out.write(d);
455 break;
456
457 }
458 }
459
460 private void putOid(OutputStream out, int i) throws IOException {
461 if (i > 127) {
462 putOid(out, i >> 7);
463 out.write(0x80 + (i & 0x7F));
464 } else
465 out.write(i & 0x7F);
466 }
Stuart McCullochbb014372012-06-07 21:57:32 +0000467}