Home | History | Annotate | Download | only in asn1
      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