1 package com.android.hotspot2.asn1;
2 
3 import java.nio.ByteBuffer;
4 import java.util.Collection;
5 import java.util.HashMap;
6 import java.util.Map;
7 
8 public 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 {
sTagMap.put(TAG_UNIVZERO, Asn1Tag.UNIVZERO)53         sTagMap.put(TAG_UNIVZERO, Asn1Tag.UNIVZERO);
sTagMap.put(TAG_BOOLEAN, Asn1Tag.BOOLEAN)54         sTagMap.put(TAG_BOOLEAN, Asn1Tag.BOOLEAN);
sTagMap.put(TAG_INTEGER, Asn1Tag.INTEGER)55         sTagMap.put(TAG_INTEGER, Asn1Tag.INTEGER);
sTagMap.put(TAG_BITSTRING, Asn1Tag.BITSTRING)56         sTagMap.put(TAG_BITSTRING, Asn1Tag.BITSTRING);
sTagMap.put(TAG_OCTET_STRING, Asn1Tag.OCTET_STRING)57         sTagMap.put(TAG_OCTET_STRING, Asn1Tag.OCTET_STRING);
sTagMap.put(TAG_NULL, Asn1Tag.NULL)58         sTagMap.put(TAG_NULL, Asn1Tag.NULL);
sTagMap.put(TAG_OID, Asn1Tag.OID)59         sTagMap.put(TAG_OID, Asn1Tag.OID);
sTagMap.put(TAG_ObjectDescriptor, Asn1Tag.ObjectDescriptor)60         sTagMap.put(TAG_ObjectDescriptor, Asn1Tag.ObjectDescriptor);
sTagMap.put(TAG_EXTERNAL, Asn1Tag.EXTERNAL)61         sTagMap.put(TAG_EXTERNAL, Asn1Tag.EXTERNAL);
sTagMap.put(TAG_REAL, Asn1Tag.REAL)62         sTagMap.put(TAG_REAL, Asn1Tag.REAL);
sTagMap.put(TAG_ENUMERATED, Asn1Tag.ENUMERATED)63         sTagMap.put(TAG_ENUMERATED, Asn1Tag.ENUMERATED);
sTagMap.put(TAG_UTF8String, Asn1Tag.UTF8String)64         sTagMap.put(TAG_UTF8String, Asn1Tag.UTF8String);
sTagMap.put(TAG_RelativeOID, Asn1Tag.RelativeOID)65         sTagMap.put(TAG_RelativeOID, Asn1Tag.RelativeOID);
sTagMap.put(TAG_SEQ, Asn1Tag.SEQUENCE)66         sTagMap.put(TAG_SEQ, Asn1Tag.SEQUENCE);
sTagMap.put(TAG_SET, Asn1Tag.SET)67         sTagMap.put(TAG_SET, Asn1Tag.SET);
sTagMap.put(TAG_NumericString, Asn1Tag.NumericString)68         sTagMap.put(TAG_NumericString, Asn1Tag.NumericString);
sTagMap.put(TAG_PrintableString, Asn1Tag.PrintableString)69         sTagMap.put(TAG_PrintableString, Asn1Tag.PrintableString);
sTagMap.put(TAG_T61String, Asn1Tag.T61String)70         sTagMap.put(TAG_T61String, Asn1Tag.T61String);
sTagMap.put(TAG_VideotexString, Asn1Tag.VideotexString)71         sTagMap.put(TAG_VideotexString, Asn1Tag.VideotexString);
sTagMap.put(TAG_IA5String, Asn1Tag.IA5String)72         sTagMap.put(TAG_IA5String, Asn1Tag.IA5String);
sTagMap.put(TAG_UTCTime, Asn1Tag.UTCTime)73         sTagMap.put(TAG_UTCTime, Asn1Tag.UTCTime);
sTagMap.put(TAG_GeneralizedTime, Asn1Tag.GeneralizedTime)74         sTagMap.put(TAG_GeneralizedTime, Asn1Tag.GeneralizedTime);
sTagMap.put(TAG_GraphicString, Asn1Tag.GraphicString)75         sTagMap.put(TAG_GraphicString, Asn1Tag.GraphicString);
sTagMap.put(TAG_VisibleString, Asn1Tag.VisibleString)76         sTagMap.put(TAG_VisibleString, Asn1Tag.VisibleString);
sTagMap.put(TAG_GeneralString, Asn1Tag.GeneralString)77         sTagMap.put(TAG_GeneralString, Asn1Tag.GeneralString);
sTagMap.put(TAG_UniversalString, Asn1Tag.UniversalString)78         sTagMap.put(TAG_UniversalString, Asn1Tag.UniversalString);
sTagMap.put(TAG_BMPString, Asn1Tag.BMPString)79         sTagMap.put(TAG_BMPString, Asn1Tag.BMPString);
80     }
81 
mapTag(int tag)82     public static Asn1Tag mapTag(int tag) {
83         return sTagMap.get(tag);
84     }
85 
decode(ByteBuffer data)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 
decode(int level, Asn1Constructed parent)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 
peelOff(ByteBuffer base, int length)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 
buildScalar(int tag, Asn1Class asn1Class, int length, ByteBuffer data)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 
decodeTag(ByteBuffer data)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 
decodeLength(ByteBuffer data)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