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( 17 InputStream in) 18 { 19 this(in, StreamUtil.findLimit(in)); 20 } 21 ASN1StreamParser( InputStream in, int limit)22 public ASN1StreamParser( 23 InputStream in, 24 int limit) 25 { 26 this._in = in; 27 this._limit = limit; 28 29 this.tmpBuffers = new byte[11][]; 30 } 31 ASN1StreamParser( byte[] encoding)32 public ASN1StreamParser( 33 byte[] encoding) 34 { 35 this(new ByteArrayInputStream(encoding), encoding.length); 36 } 37 readIndef(int tagValue)38 ASN1Encodable readIndef(int tagValue) throws IOException 39 { 40 // Note: INDEF => CONSTRUCTED 41 42 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 43 switch (tagValue) 44 { 45 case BERTags.EXTERNAL: 46 return new DERExternalParser(this); 47 case BERTags.OCTET_STRING: 48 return new BEROctetStringParser(this); 49 case BERTags.SEQUENCE: 50 return new BERSequenceParser(this); 51 case BERTags.SET: 52 return new BERSetParser(this); 53 default: 54 throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue)); 55 } 56 } 57 readImplicit(boolean constructed, int tag)58 ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException 59 { 60 if (_in instanceof IndefiniteLengthInputStream) 61 { 62 if (!constructed) 63 { 64 throw new IOException("indefinite-length primitive encoding encountered"); 65 } 66 67 return readIndef(tag); 68 } 69 70 if (constructed) 71 { 72 switch (tag) 73 { 74 case BERTags.SET: 75 return new DLSetParser(this); 76 case BERTags.SEQUENCE: 77 return new DLSequenceParser(this); 78 case BERTags.OCTET_STRING: 79 return new BEROctetStringParser(this); 80 } 81 } 82 else 83 { 84 switch (tag) 85 { 86 case BERTags.SET: 87 throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); 88 case BERTags.SEQUENCE: 89 throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); 90 case BERTags.OCTET_STRING: 91 return new DEROctetStringParser((DefiniteLengthInputStream)_in); 92 } 93 } 94 95 throw new ASN1Exception("implicit tagging not implemented"); 96 } 97 readTaggedObject(boolean constructed, int tag)98 ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException 99 { 100 if (!constructed) 101 { 102 // Note: !CONSTRUCTED => IMPLICIT 103 DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; 104 return new DLTaggedObject(false, tag, new DEROctetString(defIn.toByteArray())); 105 } 106 107 ASN1EncodableVector v = readVector(); 108 109 if (_in instanceof IndefiniteLengthInputStream) 110 { 111 return v.size() == 1 112 ? new BERTaggedObject(true, tag, v.get(0)) 113 : new BERTaggedObject(false, tag, BERFactory.createSequence(v)); 114 } 115 116 return v.size() == 1 117 ? new DLTaggedObject(true, tag, v.get(0)) 118 : new DLTaggedObject(false, tag, DLFactory.createSequence(v)); 119 } 120 readObject()121 public ASN1Encodable readObject() 122 throws IOException 123 { 124 int tag = _in.read(); 125 if (tag == -1) 126 { 127 return null; 128 } 129 130 // 131 // turn of looking for "00" while we resolve the tag 132 // 133 set00Check(false); 134 135 // 136 // calculate tag number 137 // 138 int tagNo = ASN1InputStream.readTagNumber(_in, tag); 139 140 boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0; 141 142 // 143 // calculate length 144 // 145 int length = ASN1InputStream.readLength(_in, _limit, 146 tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL); 147 148 if (length < 0) // indefinite-length method 149 { 150 if (!isConstructed) 151 { 152 throw new IOException("indefinite-length primitive encoding encountered"); 153 } 154 155 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); 156 ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); 157 158 if ((tag & BERTags.APPLICATION) != 0) 159 { 160 return new BERApplicationSpecificParser(tagNo, sp); 161 } 162 163 if ((tag & BERTags.TAGGED) != 0) 164 { 165 return new BERTaggedObjectParser(true, tagNo, sp); 166 } 167 168 return sp.readIndef(tagNo); 169 } 170 else 171 { 172 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit); 173 174 if ((tag & BERTags.APPLICATION) != 0) 175 { 176 return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); 177 } 178 179 if ((tag & BERTags.TAGGED) != 0) 180 { 181 return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn)); 182 } 183 184 if (isConstructed) 185 { 186 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 187 switch (tagNo) 188 { 189 case BERTags.OCTET_STRING: 190 // 191 // yes, people actually do this... 192 // 193 return new BEROctetStringParser(new ASN1StreamParser(defIn)); 194 case BERTags.SEQUENCE: 195 return new DLSequenceParser(new ASN1StreamParser(defIn)); 196 case BERTags.SET: 197 return new DLSetParser(new ASN1StreamParser(defIn)); 198 case BERTags.EXTERNAL: 199 return new DERExternalParser(new ASN1StreamParser(defIn)); 200 default: 201 throw new IOException("unknown tag " + tagNo + " encountered"); 202 } 203 } 204 205 // Some primitive encodings can be handled by parsers too... 206 switch (tagNo) 207 { 208 case BERTags.OCTET_STRING: 209 return new DEROctetStringParser(defIn); 210 } 211 212 try 213 { 214 return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers); 215 } 216 catch (IllegalArgumentException e) 217 { 218 throw new ASN1Exception("corrupted stream detected", e); 219 } 220 } 221 } 222 set00Check(boolean enabled)223 private void set00Check(boolean enabled) 224 { 225 if (_in instanceof IndefiniteLengthInputStream) 226 { 227 ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); 228 } 229 } 230 readVector()231 ASN1EncodableVector readVector() throws IOException 232 { 233 ASN1Encodable obj = readObject(); 234 if (null == obj) 235 { 236 return new ASN1EncodableVector(0); 237 } 238 239 ASN1EncodableVector v = new ASN1EncodableVector(); 240 do 241 { 242 if (obj instanceof InMemoryRepresentable) 243 { 244 v.add(((InMemoryRepresentable)obj).getLoadedObject()); 245 } 246 else 247 { 248 v.add(obj.toASN1Primitive()); 249 } 250 } 251 while ((obj = readObject()) != null); 252 return v; 253 } 254 } 255