1package com.android.hotspot2.asn1;
2
3import java.nio.ByteBuffer;
4import java.util.Collection;
5import java.util.HashMap;
6import java.util.Map;
7
8public class Asn1Decoder {
9    public static final int TAG_UNIVZERO = 0x00;
10    public static final int TAG_BOOLEAN = 0x01;
11    public static final int TAG_INTEGER = 0x02;
12    public static final int TAG_BITSTRING = 0x03;
13    public static final int TAG_OCTET_STRING = 0x04;
14    public static final int TAG_NULL = 0x05;
15    public static final int TAG_OID = 0x06;
16    public static final int TAG_ObjectDescriptor = 0x07;
17    public static final int TAG_EXTERNAL = 0x08;
18    public static final int TAG_REAL = 0x09;
19    public static final int TAG_ENUMERATED = 0x0a;
20    public static final int TAG_UTF8String = 0x0c;      // * (*) are X.509 DirectoryString's
21    public static final int TAG_RelativeOID = 0x0d;
22    public static final int TAG_SEQ = 0x10;             //   30 if constructed
23    public static final int TAG_SET = 0x11;
24    public static final int TAG_NumericString = 0x12;   //   [UNIVERSAL 18]
25    public static final int TAG_PrintableString = 0x13; // * [UNIVERSAL 19]
26    public static final int TAG_T61String = 0x14;       // * TeletexString [UNIVERSAL 20]
27    public static final int TAG_VideotexString = 0x15;  //   [UNIVERSAL 21]
28    public static final int TAG_IA5String = 0x16;       //   [UNIVERSAL 22]
29    public static final int TAG_UTCTime = 0x17;
30    public static final int TAG_GeneralizedTime = 0x18;
31    public static final int TAG_GraphicString = 0x19;   //   [UNIVERSAL 25]
32    public static final int TAG_VisibleString = 0x1a;   //   ISO64String [UNIVERSAL 26]
33    public static final int TAG_GeneralString = 0x1b;   //   [UNIVERSAL 27]
34    public static final int TAG_UniversalString = 0x1c; // * [UNIVERSAL 28]
35    public static final int TAG_BMPString = 0x1e;       // * [UNIVERSAL 30]
36
37    public static final int IntOverflow = 0xffff0000;
38    public static final int MoreBit = 0x80;
39    public static final int MoreData = 0x7f;
40    public static final int ConstructedBit = 0x20;
41    public static final int ClassShift = 6;
42    public static final int ClassMask = 0x3;
43    public static final int MoreWidth = 7;
44    public static final int ByteWidth = 8;
45    public static final int ByteMask = 0xff;
46    public static final int ContinuationTag = 31;
47
48    public static final int IndefiniteLength = -1;
49
50    private static final Map<Integer, Asn1Tag> sTagMap = new HashMap<>();
51
52    static {
53        sTagMap.put(TAG_UNIVZERO, Asn1Tag.UNIVZERO);
54        sTagMap.put(TAG_BOOLEAN, Asn1Tag.BOOLEAN);
55        sTagMap.put(TAG_INTEGER, Asn1Tag.INTEGER);
56        sTagMap.put(TAG_BITSTRING, Asn1Tag.BITSTRING);
57        sTagMap.put(TAG_OCTET_STRING, Asn1Tag.OCTET_STRING);
58        sTagMap.put(TAG_NULL, Asn1Tag.NULL);
59        sTagMap.put(TAG_OID, Asn1Tag.OID);
60        sTagMap.put(TAG_ObjectDescriptor, Asn1Tag.ObjectDescriptor);
61        sTagMap.put(TAG_EXTERNAL, Asn1Tag.EXTERNAL);
62        sTagMap.put(TAG_REAL, Asn1Tag.REAL);
63        sTagMap.put(TAG_ENUMERATED, Asn1Tag.ENUMERATED);
64        sTagMap.put(TAG_UTF8String, Asn1Tag.UTF8String);
65        sTagMap.put(TAG_RelativeOID, Asn1Tag.RelativeOID);
66        sTagMap.put(TAG_SEQ, Asn1Tag.SEQUENCE);
67        sTagMap.put(TAG_SET, Asn1Tag.SET);
68        sTagMap.put(TAG_NumericString, Asn1Tag.NumericString);
69        sTagMap.put(TAG_PrintableString, Asn1Tag.PrintableString);
70        sTagMap.put(TAG_T61String, Asn1Tag.T61String);
71        sTagMap.put(TAG_VideotexString, Asn1Tag.VideotexString);
72        sTagMap.put(TAG_IA5String, Asn1Tag.IA5String);
73        sTagMap.put(TAG_UTCTime, Asn1Tag.UTCTime);
74        sTagMap.put(TAG_GeneralizedTime, Asn1Tag.GeneralizedTime);
75        sTagMap.put(TAG_GraphicString, Asn1Tag.GraphicString);
76        sTagMap.put(TAG_VisibleString, Asn1Tag.VisibleString);
77        sTagMap.put(TAG_GeneralString, Asn1Tag.GeneralString);
78        sTagMap.put(TAG_UniversalString, Asn1Tag.UniversalString);
79        sTagMap.put(TAG_BMPString, Asn1Tag.BMPString);
80    }
81
82    public static Asn1Tag mapTag(int tag) {
83        return sTagMap.get(tag);
84    }
85
86    public static Collection<Asn1Object> decode(ByteBuffer data) throws DecodeException {
87        Asn1Constructed root =
88                new Asn1Constructed(0, null, data.remaining(), data, data.position());
89        decode(0, root);
90        return root.getChildren();
91    }
92
93    private static void decode(int level, Asn1Constructed parent) throws DecodeException {
94        ByteBuffer data = parent.getPayload();
95        while (data.hasRemaining()) {
96            int tagPosition = data.position();
97            int propMask = data.get(tagPosition) & ByteMask;
98            if (propMask == 0 && parent.isIndefiniteLength() && data.get(tagPosition + 1) == 0) {
99                parent.setEndOfData(tagPosition);
100                return;
101            }
102            Asn1Class asn1Class = Asn1Class.values()[(propMask >> ClassShift) & ClassMask];
103            boolean constructed = (propMask & ConstructedBit) != 0;
104
105            int tag = decodeTag(data);
106            int length = decodeLength(data);
107
108            if (constructed) {
109                ByteBuffer payload = peelOff(data, length);
110                Asn1Constructed root =
111                        new Asn1Constructed(tag, asn1Class, length, payload, tagPosition);
112                decode(level + 1, root);
113                if (length == IndefiniteLength) {
114                    data.position(root.getEndOfData() + 2);     // advance past '00'
115                }
116                parent.addChild(root);
117            } else {
118                if (asn1Class != Asn1Class.Universal) {
119                    parent.addChild(new Asn1Octets(tag, asn1Class, length, data));
120                } else {
121                    parent.addChild(buildScalar(tag, asn1Class, length, data));
122                }
123            }
124        }
125    }
126
127    private static ByteBuffer peelOff(ByteBuffer base, int length) {
128        ByteBuffer copy = base.duplicate();
129        if (length == IndefiniteLength) {
130            return copy;
131        }
132        copy.limit(copy.position() + length);
133        base.position(base.position() + length);
134        return copy;
135    }
136
137    private static Asn1Object buildScalar(int tag, Asn1Class asn1Class, int length, ByteBuffer data)
138            throws DecodeException {
139        switch (tag) {
140            case TAG_BOOLEAN:
141                return new Asn1Boolean(tag, asn1Class, length, data);
142            case TAG_INTEGER:
143            case TAG_ENUMERATED:
144                return new Asn1Integer(tag, asn1Class, length, data);
145            case TAG_BITSTRING:
146                int bitResidual = data.get() & ByteMask;
147                return new Asn1Octets(tag, asn1Class, length, data, bitResidual);
148            case TAG_OCTET_STRING:
149                return new Asn1Octets(tag, asn1Class, length, data);
150            case TAG_OID:
151                return new Asn1Oid(tag, asn1Class, length, data);
152            case TAG_UTF8String:
153            case TAG_NumericString:
154            case TAG_PrintableString:
155            case TAG_T61String:
156            case TAG_VideotexString:
157            case TAG_IA5String:
158            case TAG_GraphicString:
159            case TAG_VisibleString:
160            case TAG_GeneralString:
161            case TAG_UniversalString:
162            case TAG_BMPString:
163                return new Asn1String(tag, asn1Class, length, data);
164            case TAG_GeneralizedTime:
165            case TAG_UTCTime:
166                // Should really be a dedicated time object
167                return new Asn1String(tag, asn1Class, length, data);
168            default:
169                return new Asn1Octets(tag, asn1Class, length, data);
170        }
171    }
172
173    private static int decodeTag(ByteBuffer data) throws DecodeException {
174        int tag;
175        byte tag0 = data.get();
176
177        if ((tag = (tag0 & ContinuationTag)) == ContinuationTag) {
178            int tagByte;
179            tag = 0;
180            while (((tagByte = data.get() & ByteMask) & MoreBit) != 0) {
181                tag = (tag << MoreWidth) | (tagByte & MoreData);
182                if ((tag & IntOverflow) != 0)
183                    throw new DecodeException("Tag overflow", data.position());
184            }
185            tag = (tag << MoreWidth) | tagByte;
186        }
187        return tag;
188    }
189
190    private static int decodeLength(ByteBuffer data) throws DecodeException {
191        int length;
192        int lenlen = data.get() & ByteMask;
193
194        if ((lenlen & MoreBit) == 0)    // One byte encoding
195            length = lenlen;
196        else {
197            lenlen &= MoreData;
198            if (lenlen == 0) {
199                return IndefiniteLength;
200            }
201            length = 0;
202            while (lenlen-- > 0) {
203                length = (length << ByteWidth) | (data.get() & ByteMask);
204                if ((length & IntOverflow) != 0 && lenlen > 0)
205                    throw new DecodeException("Length overflow", data.position());
206            }
207        }
208        return length;
209    }
210
211}
212