Home | History | Annotate | Download | only in asn1
      1 package org.bouncycastle.asn1;
      2 
      3 import java.io.ByteArrayInputStream;
      4 import java.io.ByteArrayOutputStream;
      5 import java.io.EOFException;
      6 import java.io.FilterInputStream;
      7 import java.io.IOException;
      8 import java.io.InputStream;
      9 import java.util.Vector;
     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 DERObject END_OF_STREAM = new DERObject()
     22     {
     23         void encode(
     24             DEROutputStream out)
     25         throws IOException
     26         {
     27             throw new IOException("Eeek!");
     28         }
     29         public int hashCode()
     30         {
     31             return 0;
     32         }
     33         public boolean equals(
     34             Object o)
     35         {
     36             return o == this;
     37         }
     38     };
     39 
     40     boolean eofFound = false;
     41     int     limit = Integer.MAX_VALUE;
     42 
     43     public ASN1InputStream(
     44         InputStream is)
     45     {
     46         super(is);
     47     }
     48 
     49     /**
     50      * Create an ASN1InputStream based on the input byte array. The length of DER objects in
     51      * the stream is automatically limited to the length of the input array.
     52      *
     53      * @param input array containing ASN.1 encoded data.
     54      */
     55     public ASN1InputStream(
     56         byte[] input)
     57     {
     58         this(new ByteArrayInputStream(input), input.length);
     59     }
     60 
     61     /**
     62      * Create an ASN1InputStream where no DER object will be longer than limit.
     63      *
     64      * @param input stream containing ASN.1 encoded data.
     65      * @param limit maximum size of a DER encoded object.
     66      */
     67     public ASN1InputStream(
     68         InputStream input,
     69         int         limit)
     70     {
     71         super(input);
     72         this.limit = limit;
     73     }
     74 
     75     protected int readLength()
     76         throws IOException
     77     {
     78         int length = read();
     79         if (length < 0)
     80         {
     81             throw new IOException("EOF found when length expected");
     82         }
     83 
     84         if (length == 0x80)
     85         {
     86             return -1;      // indefinite-length encoding
     87         }
     88 
     89         if (length > 127)
     90         {
     91             int size = length & 0x7f;
     92 
     93             if (size > 4)
     94             {
     95                 throw new IOException("DER length more than 4 bytes");
     96             }
     97 
     98             length = 0;
     99             for (int i = 0; i < size; i++)
    100             {
    101                 int next = read();
    102 
    103                 if (next < 0)
    104                 {
    105                     throw new IOException("EOF found reading length");
    106                 }
    107 
    108                 length = (length << 8) + next;
    109             }
    110 
    111             if (length < 0)
    112             {
    113                 throw new IOException("corrupted steam - negative length found");
    114             }
    115 
    116             if (length >= limit)   // after all we must have read at least 1 byte
    117             {
    118                 throw new IOException("corrupted steam - out of bounds length found");
    119             }
    120         }
    121 
    122         return length;
    123     }
    124 
    125     protected void readFully(
    126         byte[]  bytes)
    127         throws IOException
    128     {
    129         int     left = bytes.length;
    130         int     len;
    131 
    132         if (left == 0)
    133         {
    134             return;
    135         }
    136 
    137         while ((len = read(bytes, bytes.length - left, left)) > 0)
    138         {
    139             if ((left -= len) == 0)
    140             {
    141                 return;
    142             }
    143         }
    144 
    145         if (left != 0)
    146         {
    147             throw new EOFException("EOF encountered in middle of object");
    148         }
    149     }
    150 
    151     /**
    152      * build an object given its tag and a byte stream to construct it
    153      * from.
    154      */
    155     protected DERObject buildObject(
    156         int       tag,
    157         int       tagNo,
    158         byte[]    bytes)
    159         throws IOException
    160     {
    161         if ((tag & APPLICATION) != 0)
    162         {
    163             return new DERApplicationSpecific(tag, bytes);
    164         }
    165 
    166         switch (tag)
    167         {
    168         case NULL:
    169             // BEGIN android-changed
    170             return DERNull.THE_ONE;
    171             //END android-changed
    172         case SEQUENCE | CONSTRUCTED:
    173             ASN1InputStream         aIn = new ASN1InputStream(bytes);
    174             ASN1EncodableVector     v = new ASN1EncodableVector();
    175 
    176             DERObject   obj = aIn.readObject();
    177 
    178             while (obj != null)
    179             {
    180                 v.add(obj);
    181                 obj = aIn.readObject();
    182             }
    183 
    184             return new DERSequence(v);
    185         case SET | CONSTRUCTED:
    186             aIn = new ASN1InputStream(bytes);
    187             v = new ASN1EncodableVector();
    188 
    189             obj = aIn.readObject();
    190 
    191             while (obj != null)
    192             {
    193                 v.add(obj);
    194                 obj = aIn.readObject();
    195             }
    196 
    197             return new DERSet(v, false);
    198         case BOOLEAN:
    199             // BEGIN android-changed
    200             return DERBoolean.getInstance(bytes);
    201             // END android-changed
    202         case INTEGER:
    203             return new DERInteger(bytes);
    204         case ENUMERATED:
    205             return new DEREnumerated(bytes);
    206         case OBJECT_IDENTIFIER:
    207             return new DERObjectIdentifier(bytes);
    208         case BIT_STRING:
    209             int     padBits = bytes[0];
    210             byte[]  data = new byte[bytes.length - 1];
    211 
    212             System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
    213 
    214             return new DERBitString(data, padBits);
    215         case NUMERIC_STRING:
    216             return new DERNumericString(bytes);
    217         case UTF8_STRING:
    218             return new DERUTF8String(bytes);
    219         case PRINTABLE_STRING:
    220             return new DERPrintableString(bytes);
    221         case IA5_STRING:
    222             return new DERIA5String(bytes);
    223         case T61_STRING:
    224             return new DERT61String(bytes);
    225         case VISIBLE_STRING:
    226             return new DERVisibleString(bytes);
    227         case GENERAL_STRING:
    228             return new DERGeneralString(bytes);
    229         case UNIVERSAL_STRING:
    230             return new DERUniversalString(bytes);
    231         case BMP_STRING:
    232             return new DERBMPString(bytes);
    233         case OCTET_STRING:
    234             return new DEROctetString(bytes);
    235         case OCTET_STRING | CONSTRUCTED:
    236             return buildDerConstructedOctetString(bytes);
    237         case UTC_TIME:
    238             return new DERUTCTime(bytes);
    239         case GENERALIZED_TIME:
    240             return new DERGeneralizedTime(bytes);
    241         default:
    242             //
    243             // with tagged object tag number is bottom 5 bits
    244             //
    245 
    246             if ((tag & TAGGED) != 0)
    247             {
    248                 if (bytes.length == 0)        // empty tag!
    249                 {
    250                     if ((tag & CONSTRUCTED) == 0)
    251                     {
    252                         // BEGIN android-changed
    253                         return new DERTaggedObject(false, tagNo, DERNull.THE_ONE);
    254                         // END android-changed
    255                     }
    256                     else
    257                     {
    258                         return new DERTaggedObject(false, tagNo, new DERSequence());
    259                     }
    260                 }
    261 
    262                 //
    263                 // simple type - implicit... return an octet string
    264                 //
    265                 if ((tag & CONSTRUCTED) == 0)
    266                 {
    267                     return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
    268                 }
    269 
    270                 aIn = new ASN1InputStream(bytes);
    271 
    272                 DEREncodable dObj = aIn.readObject();
    273 
    274                 //
    275                 // explicitly tagged (probably!) - if it isn't we'd have to
    276                 // tell from the context
    277                 //
    278                 if (aIn.available() == 0)
    279                 {
    280                     return new DERTaggedObject(tagNo, dObj);
    281                 }
    282 
    283                 //
    284                 // another implicit object, we'll create a sequence...
    285                 //
    286                 v = new ASN1EncodableVector();
    287 
    288                 while (dObj != null)
    289                 {
    290                     v.add(dObj);
    291                     dObj = aIn.readObject();
    292                 }
    293 
    294                 return new DERTaggedObject(false, tagNo, new DERSequence(v));
    295             }
    296 
    297             return new DERUnknownTag(tag, bytes);
    298         }
    299     }
    300 
    301     /**
    302      * read a string of bytes representing an indefinite length object.
    303      */
    304     private byte[] readIndefiniteLengthFully()
    305         throws IOException
    306     {
    307         ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
    308         int                     b, b1;
    309 
    310         b1 = read();
    311 
    312         while ((b = read()) >= 0)
    313         {
    314             if (b1 == 0 && b == 0)
    315             {
    316                 break;
    317             }
    318 
    319             bOut.write(b1);
    320             b1 = b;
    321         }
    322 
    323         return bOut.toByteArray();
    324     }
    325 
    326     private BERConstructedOctetString buildConstructedOctetString()
    327         throws IOException
    328     {
    329         Vector               octs = new Vector();
    330 
    331         for (;;)
    332         {
    333             DERObject        o = readObject();
    334 
    335             if (o == END_OF_STREAM)
    336             {
    337                 break;
    338             }
    339 
    340             octs.addElement(o);
    341         }
    342 
    343         return new BERConstructedOctetString(octs);
    344     }
    345 
    346     //
    347     // yes, people actually do this...
    348     //
    349     private BERConstructedOctetString buildDerConstructedOctetString(byte[] input)
    350         throws IOException
    351     {
    352         Vector               octs = new Vector();
    353         ASN1InputStream      aIn = new ASN1InputStream(input);
    354         DERObject            o;
    355 
    356         while ((o = aIn.readObject()) != null)
    357         {
    358             octs.addElement(o);
    359         }
    360 
    361         return new BERConstructedOctetString(octs);
    362     }
    363 
    364     public DERObject readObject()
    365         throws IOException
    366     {
    367         int tag = read();
    368         if (tag == -1)
    369         {
    370             if (eofFound)
    371             {
    372                 throw new EOFException("attempt to read past end of file.");
    373             }
    374 
    375             eofFound = true;
    376 
    377             return null;
    378         }
    379 
    380         int tagNo = 0;
    381 
    382         if ((tag & TAGGED) != 0)
    383         {
    384             tagNo = readTagNumber(tag);
    385         }
    386 
    387         int     length = readLength();
    388 
    389         if (length < 0)    // indefinite length method
    390         {
    391             switch (tag)
    392             {
    393             case NULL:
    394                 // BEGIN android-changed
    395                 return BERNull.THE_ONE;
    396                 // END android-changed
    397             case SEQUENCE | CONSTRUCTED:
    398                 ASN1EncodableVector  v = new ASN1EncodableVector();
    399 
    400                 for (;;)
    401                 {
    402                     DERObject   obj = readObject();
    403 
    404                     if (obj == END_OF_STREAM)
    405                     {
    406                         break;
    407                     }
    408 
    409                     v.add(obj);
    410                 }
    411                 return new BERSequence(v);
    412             case SET | CONSTRUCTED:
    413                 v = new ASN1EncodableVector();
    414 
    415                 for (;;)
    416                 {
    417                     DERObject   obj = readObject();
    418 
    419                     if (obj == END_OF_STREAM)
    420                     {
    421                         break;
    422                     }
    423 
    424                     v.add(obj);
    425                 }
    426                 return new BERSet(v, false);
    427             case OCTET_STRING | CONSTRUCTED:
    428                 return buildConstructedOctetString();
    429             default:
    430                 //
    431                 // with tagged object tag number is bottom 5 bits
    432                 //
    433                 if ((tag & TAGGED) != 0)
    434                 {
    435                     //
    436                     // simple type - implicit... return an octet string
    437                     //
    438                     if ((tag & CONSTRUCTED) == 0)
    439                     {
    440                         byte[]  bytes = readIndefiniteLengthFully();
    441 
    442                         return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
    443                     }
    444 
    445                     //
    446                     // either constructed or explicitly tagged
    447                     //
    448                     DERObject        dObj = readObject();
    449 
    450                     if (dObj == END_OF_STREAM)     // empty tag!
    451                     {
    452                         return new DERTaggedObject(tagNo);
    453                     }
    454 
    455                     DERObject       next = readObject();
    456 
    457                     //
    458                     // explicitly tagged (probably!) - if it isn't we'd have to
    459                     // tell from the context
    460                     //
    461                     if (next == END_OF_STREAM)
    462                     {
    463                         return new BERTaggedObject(tagNo, dObj);
    464                     }
    465 
    466                     //
    467                     // another implicit object, we'll create a sequence...
    468                     //
    469                     v = new ASN1EncodableVector();
    470 
    471                     v.add(dObj);
    472 
    473                     do
    474                     {
    475                         v.add(next);
    476                         next = readObject();
    477                     }
    478                     while (next != END_OF_STREAM);
    479 
    480                     return new BERTaggedObject(false, tagNo, new BERSequence(v));
    481                 }
    482 
    483                 throw new IOException("unknown BER object encountered");
    484             }
    485         }
    486         else
    487         {
    488             if (tag == 0 && length == 0)    // end of contents marker.
    489             {
    490                 return END_OF_STREAM;
    491             }
    492 
    493             byte[]  bytes = new byte[length];
    494 
    495             readFully(bytes);
    496 
    497             return buildObject(tag, tagNo, bytes);
    498         }
    499     }
    500 
    501     private int readTagNumber(int tag)
    502         throws IOException
    503     {
    504         int tagNo = tag & 0x1f;
    505 
    506         if (tagNo == 0x1f)
    507         {
    508             int b = read();
    509 
    510             tagNo = 0;
    511 
    512             while ((b >= 0) && ((b & 0x80) != 0))
    513             {
    514                 tagNo |= (b & 0x7f);
    515                 tagNo <<= 7;
    516                 b = read();
    517             }
    518 
    519             if (b < 0)
    520             {
    521                 eofFound = true;
    522                 throw new EOFException("EOF found inside tag value.");
    523             }
    524 
    525             tagNo |= (b & 0x7f);
    526         }
    527 
    528         return tagNo;
    529     }
    530 }
    531 
    532