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 BERTags 20 { 21 private final int limit; 22 private final boolean lazyEvaluate; 23 24 private final byte[][] tmpBuffers; 25 ASN1InputStream( InputStream is)26 public ASN1InputStream( 27 InputStream is) 28 { 29 this(is, StreamUtil.findLimit(is)); 30 } 31 32 /** 33 * Create an ASN1InputStream based on the input byte array. The length of DER objects in 34 * the stream is automatically limited to the length of the input array. 35 * 36 * @param input array containing ASN.1 encoded data. 37 */ ASN1InputStream( byte[] input)38 public ASN1InputStream( 39 byte[] input) 40 { 41 this(new ByteArrayInputStream(input), input.length); 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 * @param lazyEvaluate true if parsing inside constructed objects can be delayed. 50 */ ASN1InputStream( byte[] input, boolean lazyEvaluate)51 public ASN1InputStream( 52 byte[] input, 53 boolean lazyEvaluate) 54 { 55 this(new ByteArrayInputStream(input), input.length, lazyEvaluate); 56 } 57 58 /** 59 * Create an ASN1InputStream where no DER object will be longer than limit. 60 * 61 * @param input stream containing ASN.1 encoded data. 62 * @param limit maximum size of a DER encoded object. 63 */ ASN1InputStream( InputStream input, int limit)64 public ASN1InputStream( 65 InputStream input, 66 int limit) 67 { 68 this(input, limit, false); 69 } 70 71 /** 72 * Create an ASN1InputStream where no DER object will be longer than limit, and constructed 73 * objects such as sequences will be parsed lazily. 74 * 75 * @param input stream containing ASN.1 encoded data. 76 * @param lazyEvaluate true if parsing inside constructed objects can be delayed. 77 */ ASN1InputStream( InputStream input, boolean lazyEvaluate)78 public ASN1InputStream( 79 InputStream input, 80 boolean lazyEvaluate) 81 { 82 this(input, StreamUtil.findLimit(input), lazyEvaluate); 83 } 84 85 /** 86 * Create an ASN1InputStream where no DER object will be longer than limit, and constructed 87 * objects such as sequences will be parsed lazily. 88 * 89 * @param input stream containing ASN.1 encoded data. 90 * @param limit maximum size of a DER encoded object. 91 * @param lazyEvaluate true if parsing inside constructed objects can be delayed. 92 */ ASN1InputStream( InputStream input, int limit, boolean lazyEvaluate)93 public ASN1InputStream( 94 InputStream input, 95 int limit, 96 boolean lazyEvaluate) 97 { 98 super(input); 99 this.limit = limit; 100 this.lazyEvaluate = lazyEvaluate; 101 this.tmpBuffers = new byte[11][]; 102 } 103 getLimit()104 int getLimit() 105 { 106 return limit; 107 } 108 readLength()109 protected int readLength() 110 throws IOException 111 { 112 return readLength(this, limit); 113 } 114 readFully( byte[] bytes)115 protected void readFully( 116 byte[] bytes) 117 throws IOException 118 { 119 if (Streams.readFully(this, bytes) != bytes.length) 120 { 121 throw new EOFException("EOF encountered in middle of object"); 122 } 123 } 124 125 /** 126 * build an object given its tag and the number of bytes to construct it from. 127 * 128 * @param tag the full tag details. 129 * @param tagNo the tagNo defined. 130 * @param length the length of the object. 131 * @return the resulting primitive. 132 * @throws java.io.IOException on processing exception. 133 */ buildObject( int tag, int tagNo, int length)134 protected ASN1Primitive buildObject( 135 int tag, 136 int tagNo, 137 int length) 138 throws IOException 139 { 140 boolean isConstructed = (tag & CONSTRUCTED) != 0; 141 142 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length); 143 144 if ((tag & APPLICATION) != 0) 145 { 146 return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); 147 } 148 149 if ((tag & TAGGED) != 0) 150 { 151 return new ASN1StreamParser(defIn).readTaggedObject(isConstructed, tagNo); 152 } 153 154 if (isConstructed) 155 { 156 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 157 switch (tagNo) 158 { 159 case OCTET_STRING: 160 // 161 // yes, people actually do this... 162 // 163 ASN1EncodableVector v = buildDEREncodableVector(defIn); 164 ASN1OctetString[] strings = new ASN1OctetString[v.size()]; 165 166 for (int i = 0; i != strings.length; i++) 167 { 168 strings[i] = (ASN1OctetString)v.get(i); 169 } 170 171 return new BEROctetString(strings); 172 case SEQUENCE: 173 if (lazyEvaluate) 174 { 175 return new LazyEncodedSequence(defIn.toByteArray()); 176 } 177 else 178 { 179 return DERFactory.createSequence(buildDEREncodableVector(defIn)); 180 } 181 case SET: 182 return DERFactory.createSet(buildDEREncodableVector(defIn)); 183 case EXTERNAL: 184 return new DERExternal(buildDEREncodableVector(defIn)); 185 default: 186 throw new IOException("unknown tag " + tagNo + " encountered"); 187 } 188 } 189 190 return createPrimitiveDERObject(tagNo, defIn, tmpBuffers); 191 } 192 buildEncodableVector()193 ASN1EncodableVector buildEncodableVector() 194 throws IOException 195 { 196 ASN1EncodableVector v = new ASN1EncodableVector(); 197 ASN1Primitive o; 198 199 while ((o = readObject()) != null) 200 { 201 v.add(o); 202 } 203 204 return v; 205 } 206 buildDEREncodableVector( DefiniteLengthInputStream dIn)207 ASN1EncodableVector buildDEREncodableVector( 208 DefiniteLengthInputStream dIn) throws IOException 209 { 210 return new ASN1InputStream(dIn).buildEncodableVector(); 211 } 212 readObject()213 public ASN1Primitive readObject() 214 throws IOException 215 { 216 int tag = read(); 217 if (tag <= 0) 218 { 219 if (tag == 0) 220 { 221 throw new IOException("unexpected end-of-contents marker"); 222 } 223 224 return null; 225 } 226 227 // 228 // calculate tag number 229 // 230 int tagNo = readTagNumber(this, tag); 231 232 boolean isConstructed = (tag & CONSTRUCTED) != 0; 233 234 // 235 // calculate length 236 // 237 int length = readLength(); 238 239 if (length < 0) // indefinite-length method 240 { 241 if (!isConstructed) 242 { 243 throw new IOException("indefinite-length primitive encoding encountered"); 244 } 245 246 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit); 247 ASN1StreamParser sp = new ASN1StreamParser(indIn, limit); 248 249 if ((tag & APPLICATION) != 0) 250 { 251 return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject(); 252 } 253 254 if ((tag & TAGGED) != 0) 255 { 256 return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject(); 257 } 258 259 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 260 switch (tagNo) 261 { 262 case OCTET_STRING: 263 return new BEROctetStringParser(sp).getLoadedObject(); 264 case SEQUENCE: 265 return new BERSequenceParser(sp).getLoadedObject(); 266 case SET: 267 return new BERSetParser(sp).getLoadedObject(); 268 case EXTERNAL: 269 return new DERExternalParser(sp).getLoadedObject(); 270 default: 271 throw new IOException("unknown BER object encountered"); 272 } 273 } 274 else 275 { 276 try 277 { 278 return buildObject(tag, tagNo, length); 279 } 280 catch (IllegalArgumentException e) 281 { 282 throw new ASN1Exception("corrupted stream detected", e); 283 } 284 } 285 } 286 readTagNumber(InputStream s, int tag)287 static int readTagNumber(InputStream s, int tag) 288 throws IOException 289 { 290 int tagNo = tag & 0x1f; 291 292 // 293 // with tagged object tag number is bottom 5 bits, or stored at the start of the content 294 // 295 if (tagNo == 0x1f) 296 { 297 tagNo = 0; 298 299 int b = s.read(); 300 301 // X.690-0207 8.1.2.4.2 302 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." 303 if ((b & 0x7f) == 0) // Note: -1 will pass 304 { 305 throw new IOException("corrupted stream - invalid high tag number found"); 306 } 307 308 while ((b >= 0) && ((b & 0x80) != 0)) 309 { 310 tagNo |= (b & 0x7f); 311 tagNo <<= 7; 312 b = s.read(); 313 } 314 315 if (b < 0) 316 { 317 throw new EOFException("EOF found inside tag value."); 318 } 319 320 tagNo |= (b & 0x7f); 321 } 322 323 return tagNo; 324 } 325 readLength(InputStream s, int limit)326 static int readLength(InputStream s, int limit) 327 throws IOException 328 { 329 int length = s.read(); 330 if (length < 0) 331 { 332 throw new EOFException("EOF found when length expected"); 333 } 334 335 if (length == 0x80) 336 { 337 return -1; // indefinite-length encoding 338 } 339 340 if (length > 127) 341 { 342 int size = length & 0x7f; 343 344 // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here 345 if (size > 4) 346 { 347 throw new IOException("DER length more than 4 bytes: " + size); 348 } 349 350 length = 0; 351 for (int i = 0; i < size; i++) 352 { 353 int next = s.read(); 354 355 if (next < 0) 356 { 357 throw new EOFException("EOF found reading length"); 358 } 359 360 length = (length << 8) + next; 361 } 362 363 if (length < 0) 364 { 365 throw new IOException("corrupted stream - negative length found"); 366 } 367 368 if (length >= limit) // after all we must have read at least 1 byte 369 { 370 throw new IOException("corrupted stream - out of bounds length found"); 371 } 372 } 373 374 return length; 375 } 376 getBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)377 private static byte[] getBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers) 378 throws IOException 379 { 380 int len = defIn.getRemaining(); 381 if (defIn.getRemaining() < tmpBuffers.length) 382 { 383 byte[] buf = tmpBuffers[len]; 384 385 if (buf == null) 386 { 387 buf = tmpBuffers[len] = new byte[len]; 388 } 389 390 Streams.readFully(defIn, buf); 391 392 return buf; 393 } 394 else 395 { 396 return defIn.toByteArray(); 397 } 398 } 399 getBMPCharBuffer(DefiniteLengthInputStream defIn)400 private static char[] getBMPCharBuffer(DefiniteLengthInputStream defIn) 401 throws IOException 402 { 403 int len = defIn.getRemaining() / 2; 404 char[] buf = new char[len]; 405 int totalRead = 0; 406 while (totalRead < len) 407 { 408 int ch1 = defIn.read(); 409 if (ch1 < 0) 410 { 411 break; 412 } 413 int ch2 = defIn.read(); 414 if (ch2 < 0) 415 { 416 break; 417 } 418 buf[totalRead++] = (char)((ch1 << 8) | (ch2 & 0xff)); 419 } 420 421 return buf; 422 } 423 createPrimitiveDERObject( int tagNo, DefiniteLengthInputStream defIn, byte[][] tmpBuffers)424 static ASN1Primitive createPrimitiveDERObject( 425 int tagNo, 426 DefiniteLengthInputStream defIn, 427 byte[][] tmpBuffers) 428 throws IOException 429 { 430 switch (tagNo) 431 { 432 case BIT_STRING: 433 return ASN1BitString.fromInputStream(defIn.getRemaining(), defIn); 434 case BMP_STRING: 435 return new DERBMPString(getBMPCharBuffer(defIn)); 436 case BOOLEAN: 437 return ASN1Boolean.fromOctetString(getBuffer(defIn, tmpBuffers)); 438 case ENUMERATED: 439 return ASN1Enumerated.fromOctetString(getBuffer(defIn, tmpBuffers)); 440 case GENERALIZED_TIME: 441 return new ASN1GeneralizedTime(defIn.toByteArray()); 442 case GENERAL_STRING: 443 return new DERGeneralString(defIn.toByteArray()); 444 case IA5_STRING: 445 return new DERIA5String(defIn.toByteArray()); 446 case INTEGER: 447 return new ASN1Integer(defIn.toByteArray(), false); 448 case NULL: 449 return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?) 450 case NUMERIC_STRING: 451 return new DERNumericString(defIn.toByteArray()); 452 case OBJECT_IDENTIFIER: 453 return ASN1ObjectIdentifier.fromOctetString(getBuffer(defIn, tmpBuffers)); 454 case OCTET_STRING: 455 return new DEROctetString(defIn.toByteArray()); 456 case PRINTABLE_STRING: 457 return new DERPrintableString(defIn.toByteArray()); 458 case T61_STRING: 459 return new DERT61String(defIn.toByteArray()); 460 case UNIVERSAL_STRING: 461 return new DERUniversalString(defIn.toByteArray()); 462 case UTC_TIME: 463 return new ASN1UTCTime(defIn.toByteArray()); 464 case UTF8_STRING: 465 return new DERUTF8String(defIn.toByteArray()); 466 case VISIBLE_STRING: 467 return new DERVisibleString(defIn.toByteArray()); 468 case GRAPHIC_STRING: 469 return new DERGraphicString(defIn.toByteArray()); 470 case VIDEOTEX_STRING: 471 return new DERVideotexString(defIn.toByteArray()); 472 default: 473 throw new IOException("unknown tag " + tagNo + " encountered"); 474 } 475 } 476 } 477