1 package org.bouncycastle.asn1; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.EOFException; 5 import java.io.FilterInputStream; 6 import java.io.IOException; 7 import java.io.InputStream; 8 9 import org.bouncycastle.util.io.Streams; 10 11 /** 12 * a general purpose ASN.1 decoder - note: this class differs from the 13 * others in that it returns null after it has read the last object in 14 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is 15 * returned. 16 */ 17 public class ASN1InputStream 18 extends FilterInputStream 19 implements DERTags 20 { 21 private final int limit; 22 private final boolean lazyEvaluate; 23 24 static int findLimit(InputStream in) 25 { 26 if (in instanceof LimitedInputStream) 27 { 28 return ((LimitedInputStream)in).getRemaining(); 29 } 30 else if (in instanceof ByteArrayInputStream) 31 { 32 return ((ByteArrayInputStream)in).available(); 33 } 34 35 return Integer.MAX_VALUE; 36 } 37 38 public ASN1InputStream( 39 InputStream is) 40 { 41 this(is, findLimit(is)); 42 } 43 44 /** 45 * Create an ASN1InputStream based on the input byte array. The length of DER objects in 46 * the stream is automatically limited to the length of the input array. 47 * 48 * @param input array containing ASN.1 encoded data. 49 */ 50 public ASN1InputStream( 51 byte[] input) 52 { 53 this(new ByteArrayInputStream(input), input.length); 54 } 55 56 /** 57 * Create an ASN1InputStream based on the input byte array. The length of DER objects in 58 * the stream is automatically limited to the length of the input array. 59 * 60 * @param input array containing ASN.1 encoded data. 61 * @param lazyEvaluate true if parsing inside constructed objects can be delayed. 62 */ 63 public ASN1InputStream( 64 byte[] input, 65 boolean lazyEvaluate) 66 { 67 this(new ByteArrayInputStream(input), input.length, lazyEvaluate); 68 } 69 70 /** 71 * Create an ASN1InputStream where no DER object will be longer than limit. 72 * 73 * @param input stream containing ASN.1 encoded data. 74 * @param limit maximum size of a DER encoded object. 75 */ 76 public ASN1InputStream( 77 InputStream input, 78 int limit) 79 { 80 this(input, limit, false); 81 } 82 83 /** 84 * Create an ASN1InputStream where no DER object will be longer than limit, and constructed 85 * objects such as sequences will be parsed lazily. 86 * 87 * @param input stream containing ASN.1 encoded data. 88 * @param limit maximum size of a DER encoded object. 89 * @param lazyEvaluate true if parsing inside constructed objects can be delayed. 90 */ 91 public ASN1InputStream( 92 InputStream input, 93 int limit, 94 boolean lazyEvaluate) 95 { 96 super(input); 97 this.limit = limit; 98 this.lazyEvaluate = lazyEvaluate; 99 } 100 101 protected int readLength() 102 throws IOException 103 { 104 return readLength(this, limit); 105 } 106 107 protected void readFully( 108 byte[] bytes) 109 throws IOException 110 { 111 if (Streams.readFully(this, bytes) != bytes.length) 112 { 113 throw new EOFException("EOF encountered in middle of object"); 114 } 115 } 116 117 /** 118 * build an object given its tag and the number of bytes to construct it from. 119 */ 120 protected DERObject buildObject( 121 int tag, 122 int tagNo, 123 int length) 124 throws IOException 125 { 126 boolean isConstructed = (tag & CONSTRUCTED) != 0; 127 128 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length); 129 130 if ((tag & APPLICATION) != 0) 131 { 132 return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); 133 } 134 135 if ((tag & TAGGED) != 0) 136 { 137 return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo); 138 } 139 140 if (isConstructed) 141 { 142 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 143 switch (tagNo) 144 { 145 case OCTET_STRING: 146 // 147 // yes, people actually do this... 148 // 149 return new BERConstructedOctetString(buildDEREncodableVector(defIn).v); 150 case SEQUENCE: 151 if (lazyEvaluate) 152 { 153 return new LazyDERSequence(defIn.toByteArray()); 154 } 155 else 156 { 157 return DERFactory.createSequence(buildDEREncodableVector(defIn)); 158 } 159 case SET: 160 return DERFactory.createSet(buildDEREncodableVector(defIn), false); 161 case EXTERNAL: 162 return new DERExternal(buildDEREncodableVector(defIn)); 163 default: 164 return new DERUnknownTag(true, tagNo, defIn.toByteArray()); 165 } 166 } 167 168 return createPrimitiveDERObject(tagNo, defIn.toByteArray()); 169 } 170 171 ASN1EncodableVector buildEncodableVector() 172 throws IOException 173 { 174 ASN1EncodableVector v = new ASN1EncodableVector(); 175 DERObject o; 176 177 while ((o = readObject()) != null) 178 { 179 v.add(o); 180 } 181 182 return v; 183 } 184 185 ASN1EncodableVector buildDEREncodableVector( 186 DefiniteLengthInputStream dIn) throws IOException 187 { 188 return new ASN1InputStream(dIn).buildEncodableVector(); 189 } 190 191 public DERObject readObject() 192 throws IOException 193 { 194 int tag = read(); 195 if (tag <= 0) 196 { 197 if (tag == 0) 198 { 199 throw new IOException("unexpected end-of-contents marker"); 200 } 201 202 return null; 203 } 204 205 // 206 // calculate tag number 207 // 208 int tagNo = readTagNumber(this, tag); 209 210 boolean isConstructed = (tag & CONSTRUCTED) != 0; 211 212 // 213 // calculate length 214 // 215 int length = readLength(); 216 217 if (length < 0) // indefinite length method 218 { 219 if (!isConstructed) 220 { 221 throw new IOException("indefinite length primitive encoding encountered"); 222 } 223 224 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit); 225 ASN1StreamParser sp = new ASN1StreamParser(indIn, limit); 226 227 if ((tag & APPLICATION) != 0) 228 { 229 return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject(); 230 } 231 232 if ((tag & TAGGED) != 0) 233 { 234 return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject(); 235 } 236 237 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 238 switch (tagNo) 239 { 240 case OCTET_STRING: 241 return new BEROctetStringParser(sp).getLoadedObject(); 242 case SEQUENCE: 243 return new BERSequenceParser(sp).getLoadedObject(); 244 case SET: 245 return new BERSetParser(sp).getLoadedObject(); 246 case EXTERNAL: 247 return new DERExternalParser(sp).getLoadedObject(); 248 default: 249 throw new IOException("unknown BER object encountered"); 250 } 251 } 252 else 253 { 254 try 255 { 256 return buildObject(tag, tagNo, length); 257 } 258 catch (IllegalArgumentException e) 259 { 260 throw new ASN1Exception("corrupted stream detected", e); 261 } 262 } 263 } 264 265 static int readTagNumber(InputStream s, int tag) 266 throws IOException 267 { 268 int tagNo = tag & 0x1f; 269 270 // 271 // with tagged object tag number is bottom 5 bits, or stored at the start of the content 272 // 273 if (tagNo == 0x1f) 274 { 275 tagNo = 0; 276 277 int b = s.read(); 278 279 // X.690-0207 8.1.2.4.2 280 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." 281 if ((b & 0x7f) == 0) // Note: -1 will pass 282 { 283 throw new IOException("corrupted stream - invalid high tag number found"); 284 } 285 286 while ((b >= 0) && ((b & 0x80) != 0)) 287 { 288 tagNo |= (b & 0x7f); 289 tagNo <<= 7; 290 b = s.read(); 291 } 292 293 if (b < 0) 294 { 295 throw new EOFException("EOF found inside tag value."); 296 } 297 298 tagNo |= (b & 0x7f); 299 } 300 301 return tagNo; 302 } 303 304 static int readLength(InputStream s, int limit) 305 throws IOException 306 { 307 int length = s.read(); 308 if (length < 0) 309 { 310 throw new EOFException("EOF found when length expected"); 311 } 312 313 if (length == 0x80) 314 { 315 return -1; // indefinite-length encoding 316 } 317 318 if (length > 127) 319 { 320 int size = length & 0x7f; 321 322 // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here 323 if (size > 4) 324 { 325 throw new IOException("DER length more than 4 bytes: " + size); 326 } 327 328 length = 0; 329 for (int i = 0; i < size; i++) 330 { 331 int next = s.read(); 332 333 if (next < 0) 334 { 335 throw new EOFException("EOF found reading length"); 336 } 337 338 length = (length << 8) + next; 339 } 340 341 if (length < 0) 342 { 343 throw new IOException("corrupted stream - negative length found"); 344 } 345 346 if (length >= limit) // after all we must have read at least 1 byte 347 { 348 throw new IOException("corrupted stream - out of bounds length found"); 349 } 350 } 351 352 return length; 353 } 354 355 static DERObject createPrimitiveDERObject( 356 int tagNo, 357 byte[] bytes) 358 { 359 switch (tagNo) 360 { 361 case BIT_STRING: 362 return DERBitString.fromOctetString(bytes); 363 case BMP_STRING: 364 return new DERBMPString(bytes); 365 case BOOLEAN: 366 // BEGIN android-changed 367 return DERBoolean.getInstance(bytes); 368 // END android-changed 369 case ENUMERATED: 370 return new ASN1Enumerated(bytes); 371 case GENERALIZED_TIME: 372 return new ASN1GeneralizedTime(bytes); 373 case GENERAL_STRING: 374 return new DERGeneralString(bytes); 375 case IA5_STRING: 376 return new DERIA5String(bytes); 377 case INTEGER: 378 return new ASN1Integer(bytes); 379 case NULL: 380 return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?) 381 case NUMERIC_STRING: 382 return new DERNumericString(bytes); 383 case OBJECT_IDENTIFIER: 384 return new ASN1ObjectIdentifier(bytes); 385 case OCTET_STRING: 386 return new DEROctetString(bytes); 387 case PRINTABLE_STRING: 388 return new DERPrintableString(bytes); 389 case T61_STRING: 390 return new DERT61String(bytes); 391 case UNIVERSAL_STRING: 392 return new DERUniversalString(bytes); 393 case UTC_TIME: 394 return new ASN1UTCTime(bytes); 395 case UTF8_STRING: 396 return new DERUTF8String(bytes); 397 case VISIBLE_STRING: 398 return new DERVisibleString(bytes); 399 default: 400 return new DERUnknownTag(false, tagNo, bytes); 401 } 402 } 403 } 404