1 package org.bouncycastle.asn1; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 /** 8 * A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters. 9 */ 10 public class ASN1StreamParser 11 { 12 private final InputStream _in; 13 private final int _limit; 14 private final byte[][] tmpBuffers; 15 ASN1StreamParser(InputStream in)16 public ASN1StreamParser(InputStream in) 17 { 18 this(in, StreamUtil.findLimit(in)); 19 } 20 ASN1StreamParser(byte[] encoding)21 public ASN1StreamParser(byte[] encoding) 22 { 23 this(new ByteArrayInputStream(encoding), encoding.length); 24 } 25 ASN1StreamParser(InputStream in, int limit)26 public ASN1StreamParser(InputStream in, int limit) 27 { 28 this(in, limit, new byte[11][]); 29 } 30 ASN1StreamParser(InputStream in, int limit, byte[][] tmpBuffers)31 ASN1StreamParser(InputStream in, int limit, byte[][] tmpBuffers) 32 { 33 this._in = in; 34 this._limit = limit; 35 this.tmpBuffers = tmpBuffers; 36 } 37 readObject()38 public ASN1Encodable readObject() throws IOException 39 { 40 int tagHdr = _in.read(); 41 if (tagHdr < 0) 42 { 43 return null; 44 } 45 46 return implParseObject(tagHdr); 47 } 48 implParseObject(int tagHdr)49 ASN1Encodable implParseObject(int tagHdr) throws IOException 50 { 51 // 52 // turn off looking for "00" while we resolve the tag 53 // 54 set00Check(false); 55 56 // 57 // calculate tag number 58 // 59 int tagNo = ASN1InputStream.readTagNumber(_in, tagHdr); 60 61 // 62 // calculate length 63 // 64 int length = ASN1InputStream.readLength(_in, _limit, 65 tagNo == BERTags.BIT_STRING || tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE 66 || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL); 67 68 if (length < 0) // indefinite-length method 69 { 70 if (0 == (tagHdr & BERTags.CONSTRUCTED)) 71 { 72 throw new IOException("indefinite-length primitive encoding encountered"); 73 } 74 75 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); 76 ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit, tmpBuffers); 77 78 int tagClass = tagHdr & BERTags.PRIVATE; 79 if (0 != tagClass) 80 { 81 return new BERTaggedObjectParser(tagClass, tagNo, sp); 82 } 83 84 return sp.parseImplicitConstructedIL(tagNo); 85 } 86 else 87 { 88 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit); 89 90 if (0 == (tagHdr & BERTags.FLAGS)) 91 { 92 return parseImplicitPrimitive(tagNo, defIn); 93 } 94 95 ASN1StreamParser sp = new ASN1StreamParser(defIn, defIn.getLimit(), tmpBuffers); 96 97 int tagClass = tagHdr & BERTags.PRIVATE; 98 if (0 != tagClass) 99 { 100 boolean isConstructed = (tagHdr & BERTags.CONSTRUCTED) != 0; 101 102 return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp); 103 } 104 105 return sp.parseImplicitConstructedDL(tagNo); 106 } 107 } 108 loadTaggedDL(int tagClass, int tagNo, boolean constructed)109 ASN1Primitive loadTaggedDL(int tagClass, int tagNo, boolean constructed) throws IOException 110 { 111 if (!constructed) 112 { 113 byte[] contentsOctets = ((DefiniteLengthInputStream) _in).toByteArray(); 114 return ASN1TaggedObject.createPrimitive(tagClass, tagNo, contentsOctets); 115 } 116 117 ASN1EncodableVector contentsElements = readVector(); 118 return ASN1TaggedObject.createConstructedDL(tagClass, tagNo, contentsElements); 119 } 120 loadTaggedIL(int tagClass, int tagNo)121 ASN1Primitive loadTaggedIL(int tagClass, int tagNo) throws IOException 122 { 123 ASN1EncodableVector contentsElements = readVector(); 124 return ASN1TaggedObject.createConstructedIL(tagClass, tagNo, contentsElements); 125 } 126 parseImplicitConstructedDL(int univTagNo)127 ASN1Encodable parseImplicitConstructedDL(int univTagNo) throws IOException 128 { 129 switch (univTagNo) 130 { 131 case BERTags.BIT_STRING: 132 // TODO[asn1] DLConstructedBitStringParser 133 return new BERBitStringParser(this); 134 case BERTags.EXTERNAL: 135 return new DERExternalParser(this); 136 case BERTags.OCTET_STRING: 137 // TODO[asn1] DLConstructedOctetStringParser 138 return new BEROctetStringParser(this); 139 case BERTags.SET: 140 return new DLSetParser(this); 141 case BERTags.SEQUENCE: 142 return new DLSequenceParser(this); 143 default: 144 // -DM toHexString 145 throw new ASN1Exception("unknown DL object encountered: 0x" + Integer.toHexString(univTagNo)); 146 } 147 } 148 parseImplicitConstructedIL(int univTagNo)149 ASN1Encodable parseImplicitConstructedIL(int univTagNo) throws IOException 150 { 151 switch (univTagNo) 152 { 153 case BERTags.BIT_STRING: 154 return new BERBitStringParser(this); 155 case BERTags.OCTET_STRING: 156 return new BEROctetStringParser(this); 157 case BERTags.EXTERNAL: 158 // TODO[asn1] BERExternalParser 159 return new DERExternalParser(this); 160 case BERTags.SEQUENCE: 161 return new BERSequenceParser(this); 162 case BERTags.SET: 163 return new BERSetParser(this); 164 default: 165 throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(univTagNo)); 166 } 167 } 168 parseImplicitPrimitive(int univTagNo)169 ASN1Encodable parseImplicitPrimitive(int univTagNo) throws IOException 170 { 171 return parseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in); 172 } 173 parseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn)174 ASN1Encodable parseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn) throws IOException 175 { 176 // Some primitive encodings can be handled by parsers too... 177 switch (univTagNo) 178 { 179 case BERTags.BIT_STRING: 180 return new DLBitStringParser(defIn); 181 case BERTags.EXTERNAL: 182 throw new ASN1Exception("externals must use constructed encoding (see X.690 8.18)"); 183 case BERTags.OCTET_STRING: 184 return new DEROctetStringParser(defIn); 185 case BERTags.SET: 186 throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); 187 case BERTags.SEQUENCE: 188 throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); 189 } 190 191 try 192 { 193 return ASN1InputStream.createPrimitiveDERObject(univTagNo, defIn, tmpBuffers); 194 } 195 catch (IllegalArgumentException e) 196 { 197 throw new ASN1Exception("corrupted stream detected", e); 198 } 199 } 200 parseObject(int univTagNo)201 ASN1Encodable parseObject(int univTagNo) throws IOException 202 { 203 if (univTagNo < 0 || univTagNo > 30) 204 { 205 throw new IllegalArgumentException("invalid universal tag number: " + univTagNo); 206 } 207 208 int tagHdr = _in.read(); 209 if (tagHdr < 0) 210 { 211 return null; 212 } 213 214 if ((tagHdr & ~BERTags.CONSTRUCTED) != univTagNo) 215 { 216 throw new IOException("unexpected identifier encountered: " + tagHdr); 217 } 218 219 return implParseObject(tagHdr); 220 } 221 parseTaggedObject()222 ASN1TaggedObjectParser parseTaggedObject() throws IOException 223 { 224 int tagHdr = _in.read(); 225 if (tagHdr < 0) 226 { 227 return null; 228 } 229 230 int tagClass = tagHdr & BERTags.PRIVATE; 231 if (0 == tagClass) 232 { 233 throw new ASN1Exception("no tagged object found"); 234 } 235 236 return (ASN1TaggedObjectParser)implParseObject(tagHdr); 237 } 238 239 // TODO[asn1] Prefer 'loadVector' readVector()240 ASN1EncodableVector readVector() throws IOException 241 { 242 int tagHdr = _in.read(); 243 if (tagHdr < 0) 244 { 245 return new ASN1EncodableVector(0); 246 } 247 248 ASN1EncodableVector v = new ASN1EncodableVector(); 249 do 250 { 251 ASN1Encodable obj = implParseObject(tagHdr); 252 253 if (obj instanceof InMemoryRepresentable) 254 { 255 v.add(((InMemoryRepresentable) obj).getLoadedObject()); 256 } 257 else 258 { 259 v.add(obj.toASN1Primitive()); 260 } 261 } 262 while ((tagHdr = _in.read()) >= 0); 263 return v; 264 } 265 set00Check(boolean enabled)266 private void set00Check(boolean enabled) 267 { 268 if (_in instanceof IndefiniteLengthInputStream) 269 { 270 ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); 271 } 272 } 273 } 274