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