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