Use local copy of latest bndlib code for pre-release testing purposes
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/BER.java b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
new file mode 100644
index 0000000..4416dd4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
@@ -0,0 +1,472 @@
+package aQute.libg.asn1;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+public class BER implements Types {
+ DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss\\Z");
+
+ final DataInputStream xin;
+ long position;
+
+ public BER(InputStream in) {
+ this.xin = new DataInputStream(in);
+ }
+
+ public void dump(PrintStream out) throws Exception {
+ int type = readByte();
+ long length = readLength();
+ if (type == -1 || length == -1)
+ throw new EOFException("Empty file");
+ dump(out, type, length, "");
+ }
+
+ void dump(PrintStream out, int type, long length, String indent)
+ throws Exception {
+ int clss = type >> 6;
+ int nmbr = type & 0x1F;
+ boolean cnst = (type & 0x20) != 0;
+
+ String tag = "[" + nmbr + "]";
+ if (clss == 0)
+ tag = TAGS[nmbr];
+
+ if (cnst) {
+ System.err.printf("%5d %s %s %s%n", length, indent, CLASSES[clss],
+ tag);
+ while (length > 1) {
+ long atStart = getPosition();
+ int t2 = read();
+ long l2 = readLength();
+ dump(out, t2, l2, indent + " ");
+ length -= getPosition() - atStart;
+ }
+ } else {
+ assert length < Integer.MAX_VALUE;
+ assert length >= 0;
+ byte[] data = new byte[(int) length];
+ readFully(data);
+ String summary;
+
+ switch (nmbr) {
+ case BOOLEAN:
+ assert length == 1;
+ summary = data[0] != 0 ? "true" : "false";
+ break;
+
+ case INTEGER:
+ long n = toLong(data);
+ summary = n + "";
+ break;
+
+ case UTF8_STRING:
+ case IA5STRING:
+ case VISIBLE_STRING:
+ case UNIVERSAL_STRING:
+ case PRINTABLE_STRING:
+ case UTCTIME:
+ summary = new String(data, "UTF-8");
+ break;
+
+ case OBJECT_IDENTIFIER:
+ summary = readOID(data);
+ break;
+
+ case GENERALIZED_TIME:
+ case GRAPHIC_STRING:
+ case GENERAL_STRING:
+ case CHARACTER_STRING:
+
+ case REAL:
+ case EOC:
+ case BIT_STRING:
+ case OCTET_STRING:
+ case NULL:
+ case OBJECT_DESCRIPTOR:
+ case EXTERNAL:
+ case ENUMERATED:
+ case EMBEDDED_PDV:
+ case RELATIVE_OID:
+ case NUMERIC_STRING:
+ case T61_STRING:
+ case VIDEOTEX_STRING:
+ case BMP_STRING:
+ default:
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 10 && i < data.length; i++) {
+ sb.append(Integer.toHexString(data[i]));
+ }
+ if (data.length > 10) {
+ sb.append("...");
+ }
+ summary = sb.toString();
+ break;
+ }
+ out.printf("%5d %s %s %s %s\n", length, indent, CLASSES[clss], tag,
+ summary);
+ }
+ }
+
+ long toLong(byte[] data) {
+ if (data[0] < 0) {
+ for (int i = 0; i < data.length; i++)
+ data[i] = (byte) (0xFF ^ data[i]);
+
+ return -(toLong(data) + 1);
+ }
+ long n = 0;
+ for (int i = 0; i < data.length; i++) {
+ n = n * 256 + data[i];
+ }
+ return n;
+ }
+
+ /**
+ * 8.1.3.3 For the definite form, the length octets shall consist of one or
+ * more octets, and shall represent the number of octets in the contents
+ * octets using either the short form (see 8.1.3.4) or the long form (see
+ * 8.1.3.5) as a sender's option. NOTE – The short form can only be used if
+ * the number of octets in the contents octets is less than or equal to 127.
+ * 8.1.3.4 In the short form, the length octets shall consist of a single
+ * octet in which bit 8 is zero and bits 7 to 1 encode the number of octets
+ * in the contents octets (which may be zero), as an unsigned binary integer
+ * with bit 7 as the most significant bit. EXAMPLE L = 38 can be encoded as
+ * 001001102 8.1.3.5 In the long form, the length octets shall consist of an
+ * initial octet and one or more subsequent octets. The initial octet shall
+ * be encoded as follows: a) bit 8 shall be one; b) bits 7 to 1 shall encode
+ * the number of subsequent octets in the length octets, as an unsigned
+ * binary integer with bit 7 as the most significant bit; c) the value
+ * 111111112 shall not be used. ISO/IEC 8825-1:2003 (E) NOTE 1 – This
+ * restriction is introduced for possible future extension. Bits 8 to 1 of
+ * the first subsequent octet, followed by bits 8 to 1 of the second
+ * subsequent octet, followed in turn by bits 8 to 1 of each further octet
+ * up to and including the last subsequent octet, shall be the encoding of
+ * an unsigned binary integer equal to the number of octets in the contents
+ * octets, with bit 8 of the first subsequent octet as the most significant
+ * bit. EXAMPLE L = 201 can be encoded as: 100000012 110010012 NOTE 2 – In
+ * the long form, it is a sender's option whether to use more length octets
+ * than the minimum necessary. 8.1.3.6 For the indefinite form, the length
+ * octets indicate that the contents octets are terminated by
+ * end-of-contents octets (see 8.1.5), and shall consist of a single octet.
+ * 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to 1
+ * set to zero. 8.1.3.6.2 If this form of length is used, then
+ * end-of-contents octets (see 8.1.5) shall be present in the encoding
+ * following the contents octets. 8.1.4 Contents octets The contents octets
+ * shall consist of zero, one or more octets, and shall encode the data
+ * value as specified in subsequent clauses. NOTE – The contents octets
+ * depend on the type of the data value; subsequent clauses follow the same
+ * sequence as the definition of types in ASN.1. 8.1.5 End-of-contents
+ * octets The end-of-contents octets shall be present if the length is
+ * encoded as specified in 8.1.3.6, otherwise they shall not be present. The
+ * end-of-contents octets shall consist of two zero octets. NOTE – The
+ * end-of-contents octets can be considered as the encoding of a value whose
+ * tag is universal class, whose form is primitive, whose number of the tag
+ * is zero, and whose contents are absent, thus:
+ *
+ * End-of-contents Length Contents 0016 0016 Absent
+ *
+ * @return
+ */
+ private long readLength() throws IOException {
+ long n = readByte();
+ if (n > 0) {
+ // short form
+ return n;
+ }
+ // long form
+ int count = (int) (n & 0x7F);
+ if (count == 0) {
+ // indefinite form
+ return 0;
+ }
+ n = 0;
+ while (count-- > 0) {
+ n = n * 256 + read();
+ }
+ return n;
+ }
+
+ private int readByte() throws IOException {
+ position++;
+ return xin.readByte();
+ }
+
+ private void readFully(byte[] data) throws IOException {
+ position += data.length;
+ xin.readFully(data);
+ }
+
+ private long getPosition() {
+ return position;
+ }
+
+ private int read() throws IOException {
+ position++;
+ return xin.read();
+ }
+
+ String readOID(byte[] data) {
+ StringBuilder sb = new StringBuilder();
+ sb.append((0xFF & data[0]) / 40);
+ sb.append(".");
+ sb.append((0xFF & data[0]) % 40);
+
+ int i = 0;
+ while (++i < data.length) {
+ int n = 0;
+ while (data[i] < 0) {
+ n = n * 128 + (0x7F & data[i]);
+ i++;
+ }
+ n = n * 128 + data[i];
+ sb.append(".");
+ sb.append(n);
+ }
+
+ return sb.toString();
+ }
+
+ int getPayloadLength(PDU pdu) throws Exception {
+ switch (pdu.getTag() & 0x1F) {
+ case EOC:
+ return 1;
+
+ case BOOLEAN:
+ return 1;
+
+ case INTEGER:
+ return size(pdu.getInt());
+
+ case UTF8_STRING:
+ String s = pdu.getString();
+ byte[] encoded = s.getBytes("UTF-8");
+ return encoded.length;
+
+ case IA5STRING:
+ case VISIBLE_STRING:
+ case UNIVERSAL_STRING:
+ case PRINTABLE_STRING:
+ case GENERALIZED_TIME:
+ case GRAPHIC_STRING:
+ case GENERAL_STRING:
+ case CHARACTER_STRING:
+ case UTCTIME:
+ case NUMERIC_STRING: {
+ String str = pdu.getString();
+ encoded = str.getBytes("ASCII");
+ return encoded.length;
+ }
+
+ case OBJECT_IDENTIFIER:
+ case REAL:
+ case BIT_STRING:
+ return pdu.getBytes().length;
+
+ case OCTET_STRING:
+ case NULL:
+ case OBJECT_DESCRIPTOR:
+ case EXTERNAL:
+ case ENUMERATED:
+ case EMBEDDED_PDV:
+ case RELATIVE_OID:
+ case T61_STRING:
+ case VIDEOTEX_STRING:
+ case BMP_STRING:
+ return pdu.getBytes().length;
+
+ default:
+ throw new IllegalArgumentException("Invalid type: " + pdu);
+ }
+ }
+
+ int size(long value) {
+ if (value < 128)
+ return 1;
+
+ if (value <= 0xFF)
+ return 2;
+
+ if (value <= 0xFFFF)
+ return 3;
+
+ if (value <= 0xFFFFFF)
+ return 4;
+
+ if (value <= 0xFFFFFFFF)
+ return 5;
+
+ if (value <= 0xFFFFFFFFFFL)
+ return 6;
+
+ if (value <= 0xFFFFFFFFFFFFL)
+ return 7;
+
+ if (value <= 0xFFFFFFFFFFFFFFL)
+ return 8;
+
+ if (value <= 0xFFFFFFFFFFFFFFFFL)
+ return 9;
+
+ throw new IllegalArgumentException("length too long");
+ }
+
+ public void write(OutputStream out, PDU pdu) throws Exception {
+ byte id = 0;
+
+ switch (pdu.getClss()) {
+ case UNIVERSAL:
+ id |= 0;
+ break;
+ case APPLICATION:
+ id |= 0x40;
+ break;
+ case CONTEXT:
+ id |= 0x80;
+ break;
+ case PRIVATE:
+ id |= 0xC0;
+ break;
+ }
+
+ if (pdu.isConstructed())
+ id |= 0x20;
+
+ int tag = pdu.getTag();
+ if (tag >= 0 && tag < 31) {
+ id |= tag;
+ } else {
+ throw new UnsupportedOperationException("Cant do tags > 30");
+ }
+
+ out.write(id);
+
+ int length = getPayloadLength(pdu);
+ int size = size(length);
+ if (size == 1) {
+ out.write(length);
+ } else {
+ out.write(size);
+ while (--size >= 0) {
+ byte data = (byte) ((length >> (size * 8)) & 0xFF);
+ out.write(data);
+ }
+ }
+ writePayload(out, pdu);
+ }
+
+ void writePayload(OutputStream out, PDU pdu) throws Exception {
+ switch (pdu.getTag()) {
+ case EOC:
+ out.write(0);
+ break;
+
+ case BOOLEAN:
+ if (pdu.getBoolean())
+ out.write(-1);
+ else
+ out.write(0);
+ break;
+
+ case ENUMERATED:
+ case INTEGER: {
+ long value = pdu.getInt();
+ int size = size(value);
+ for (int i = size; i >= 0; i--) {
+ byte b = (byte) ((value >> (i * 8)) & 0xFF);
+ out.write(b);
+ }
+ }
+
+ case BIT_STRING: {
+ byte bytes[] = pdu.getBytes();
+ int unused = bytes[0];
+ assert unused <= 7;
+ int[] mask = { 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1 };
+ bytes[bytes.length - 1] &= (byte) mask[unused];
+ out.write(bytes);
+ break;
+ }
+
+ case RELATIVE_OID:
+ case OBJECT_IDENTIFIER: {
+ int[] oid = pdu.getOID();
+ assert oid.length > 2;
+ assert oid[0] < 4;
+ assert oid[1] < 40;
+ byte top = (byte) (oid[0] * 40 + oid[1]);
+ out.write(top);
+ for (int i = 2; i < oid.length; i++) {
+ putOid(out,oid[i]);
+ }
+ break;
+ }
+
+ case OCTET_STRING: {
+ byte bytes[] = pdu.getBytes();
+ out.write(bytes);
+ break;
+ }
+
+ case NULL:
+ break;
+
+ case BMP_STRING:
+ case GRAPHIC_STRING:
+ case VISIBLE_STRING:
+ case GENERAL_STRING:
+ case UNIVERSAL_STRING:
+ case CHARACTER_STRING:
+ case NUMERIC_STRING:
+ case PRINTABLE_STRING:
+ case VIDEOTEX_STRING:
+ case T61_STRING:
+ case REAL:
+ case EMBEDDED_PDV:
+ case EXTERNAL:
+ throw new UnsupportedEncodingException("dont know real, embedded PDV or external");
+
+ case UTF8_STRING: {
+ String s = pdu.getString();
+ byte [] data = s.getBytes("UTF-8");
+ out.write(data);
+ break;
+ }
+
+ case OBJECT_DESCRIPTOR:
+ case IA5STRING:
+ String s = pdu.getString();
+ byte [] data = s.getBytes("ASCII");
+ out.write(data);
+ break;
+
+
+ case SEQUENCE:
+ case SET: {
+ PDU pdus[] = pdu.getChildren();
+ for ( PDU p : pdus ) {
+ write(out, p);
+ }
+ }
+
+
+ case UTCTIME:
+ case GENERALIZED_TIME:
+ Date date = pdu.getDate();
+ String ss= df.format(date);
+ byte d[] = ss.getBytes("ASCII");
+ out.write(d);
+ break;
+
+ }
+ }
+
+
+ private void putOid(OutputStream out, int i) throws IOException {
+ if (i > 127) {
+ putOid(out, i >> 7);
+ out.write(0x80 + (i & 0x7F));
+ } else
+ out.write(i & 0x7F);
+ }
+}