    001package org.apache.commons.ssl.asn1;
    002
    003import java.io.ByteArrayInputStream;
    004import java.io.ByteArrayOutputStream;
    005import java.io.EOFException;
    006import java.io.FilterInputStream;
    007import java.io.IOException;
    008import java.io.InputStream;
    009import java.util.Vector;
    010
    011/**
    012 * a general purpose ASN.1 decoder - note: this class differs from the
    013 * others in that it returns null after it has read the last object in
    014 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
    015 * returned.
    016 */
    017public class ASN1InputStream
    018    extends FilterInputStream
    019    implements DERTags {
    020    private static final DERObject END_OF_STREAM = new DERObject() {
    021        void encode(
    022            DEROutputStream out)
    023            throws IOException {
    024            throw new IOException("Eeek!");
    025        }
    026        public int hashCode() {
    027            return 0;
    028        }
    029        public boolean equals(
    030            Object o) {
    031            return o == this;
    032        }
    033    };
    034
    035    boolean eofFound = false;
    036    int limit = Integer.MAX_VALUE;
    037
    038    public ASN1InputStream(
    039        InputStream is) {
    040        super(is);
    041    }
    042
    043    /**
    044     * Create an ASN1InputStream based on the input byte array. The length of DER objects in
    045     * the stream is automatically limited to the length of the input array.
    046     *
    047     * @param input array containing ASN.1 encoded data.
    048     */
    049    public ASN1InputStream(
    050        byte[] input) {
    051        this(new ByteArrayInputStream(input), input.length);
    052    }
    053
    054    /**
    055     * Create an ASN1InputStream where no DER object will be longer than limit.
    056     *
    057     * @param input stream containing ASN.1 encoded data.
    058     * @param limit maximum size of a DER encoded object.
    059     */
    060    public ASN1InputStream(
    061        InputStream input,
    062        int limit) {
    063        super(input);
    064        this.limit = limit;
    065    }
    066
    067    protected int readLength()
    068        throws IOException {
    069        int length = read();
    070        if (length < 0) {
    071            throw new IOException("EOF found when length expected");
    072        }
    073
    074        if (length == 0x80) {
    075            return -1;      // indefinite-length encoding
    076        }
    077
    078        if (length > 127) {
    079            int size = length & 0x7f;
    080
    081            if (size > 4) {
    082                throw new IOException("DER length more than 4 bytes");
    083            }
    084
    085            length = 0;
    086            for (int i = 0; i < size; i++) {
    087                int next = read();
    088
    089                if (next < 0) {
    090                    throw new IOException("EOF found reading length");
    091                }
    092
    093                length = (length << 8) + next;
    094            }
    095
    096            if (length < 0) {
    097                throw new IOException("corrupted stream - negative length found");
    098            }
    099
    100            if (length >= limit)   // after all we must have read at least 1 byte
    101            {
    102                throw new IOException("corrupted stream - out of bounds length found");
    103            }
    104        }
    105
    106        return length;
    107    }
    108
    109    protected void readFully(
    110        byte[] bytes)
    111        throws IOException {
    112        int left = bytes.length;
    113        int len;
    114
    115        if (left == 0) {
    116            return;
    117        }
    118
    119        while ((len = read(bytes, bytes.length - left, left)) > 0) {
    120            if ((left -= len) == 0) {
    121                return;
    122            }
    123        }
    124
    125        if (left != 0) {
    126            throw new EOFException("EOF encountered in middle of object");
    127        }
    128    }
    129
    130    /** build an object given its tag and the number of bytes to construct it from. */
    131    protected DERObject buildObject(
    132        int tag,
    133        int tagNo,
    134        int length)
    135        throws IOException {
    136        if ((tag & APPLICATION) != 0) {
    137            return new DERApplicationSpecific(tagNo, readDefiniteLengthFully(length));
    138        }
    139
    140        boolean isConstructed = (tag & CONSTRUCTED) != 0;
    141
    142        if (isConstructed) {
    143            switch (tag) {
    144                case SEQUENCE | CONSTRUCTED:
    145                    return new DERSequence(buildDerEncodableVector(length));
    146                case SET | CONSTRUCTED:
    147                    return new DERSet(buildDerEncodableVector(length), false);
    148                case OCTET_STRING | CONSTRUCTED:
    149                    return buildDerConstructedOctetString(length);
    150                default: {
    151                    //
    152                    // with tagged object tag number is bottom 5 bits
    153                    //
    154                    if ((tag & TAGGED) != 0) {
    155                        if (length == 0)     // empty tag!
    156                        {
    157                            return new DERTaggedObject(false, tagNo, new DERSequence());
    158                        }
    159
    160                        ASN1EncodableVector v = buildDerEncodableVector(length);
    161
    162                        if (v.size() == 1) {
    163                            //
    164                            // explicitly tagged (probably!) - if it isn't we'd have to
    165                            // tell from the context
    166                            //
    167                            return new DERTaggedObject(tagNo, v.get(0));
    168                        }
    169
    170                        return new DERTaggedObject(false, tagNo, new DERSequence(v));
    171                    }
    172
    173                    return new DERUnknownTag(tag, readDefiniteLengthFully(length));
    174                }
    175            }
    176        }
    177
    178        byte[] bytes = readDefiniteLengthFully(length);
    179
    180        switch (tag) {
    181            case NULL:
    182                return DERNull.INSTANCE;
    183            case BOOLEAN:
    184                return new DERBoolean(bytes);
    185            case INTEGER:
    186                return new DERInteger(bytes);
    187            case ENUMERATED:
    188                return new DEREnumerated(bytes);
    189            case OBJECT_IDENTIFIER:
    190                return new DERObjectIdentifier(bytes);
    191            case BIT_STRING: {
    192                int padBits = bytes[0];
    193                byte[] data = new byte[bytes.length - 1];
    194
    195                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
    196
    197                return new DERBitString(data, padBits);
    198            }
    199            case NUMERIC_STRING:
    200                return new DERNumericString(bytes);
    201            case UTF8_STRING:
    202                return new DERUTF8String(bytes);
    203            case PRINTABLE_STRING:
    204                return new DERPrintableString(bytes);
    205            case IA5_STRING:
    206                return new DERIA5String(bytes);
    207            case T61_STRING:
    208                return new DERT61String(bytes);
    209            case VISIBLE_STRING:
    210                return new DERVisibleString(bytes);
    211            case GENERAL_STRING:
    212                return new DERGeneralString(bytes);
    213            case UNIVERSAL_STRING:
    214                return new DERUniversalString(bytes);
    215            case BMP_STRING:
    216                return new DERBMPString(bytes);
    217            case OCTET_STRING:
    218                return new DEROctetString(bytes);
    219            case UTC_TIME:
    220                return new DERUTCTime(bytes);
    221            case GENERALIZED_TIME:
    222                return new DERGeneralizedTime(bytes);
    223            default: {
    224                //
    225                // with tagged object tag number is bottom 5 bits
    226                //
    227                if ((tag & TAGGED) != 0) {
    228                    if (bytes.length == 0)     // empty tag!
    229                    {
    230                        return new DERTaggedObject(false, tagNo, DERNull.INSTANCE);
    231                    }
    232
    233                    //
    234                    // simple type - implicit... return an octet string
    235                    //
    236                    return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
    237                }
    238
    239                return new DERUnknownTag(tag, bytes);
    240            }
    241        }
    242    }
    243
    244    private byte[] readDefiniteLengthFully(int length)
    245        throws IOException {
    246        byte[] bytes = new byte[length];
    247        readFully(bytes);
    248        return bytes;
    249    }
    250
    251    /** read a string of bytes representing an indefinite length object. */
    252    private byte[] readIndefiniteLengthFully()
    253        throws IOException {
    254        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    255        int b, b1;
    256
    257        b1 = read();
    258
    259        while ((b = read()) >= 0) {
    260            if (b1 == 0 && b == 0) {
    261                break;
    262            }
    263
    264            bOut.write(b1);
    265            b1 = b;
    266        }
    267
    268        return bOut.toByteArray();
    269    }
    270
    271    private BERConstructedOctetString buildConstructedOctetString(DERObject sentinel)
    272        throws IOException {
    273        Vector octs = new Vector();
    274        DERObject o;
    275
    276        while ((o = readObject()) != sentinel) {
    277            octs.addElement(o);
    278        }
    279
    280        return new BERConstructedOctetString(octs);
    281    }
    282
    283    //
    284    // yes, people actually do this...
    285    //
    286    private BERConstructedOctetString buildDerConstructedOctetString(int length)
    287        throws IOException {
    288        DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
    289        ASN1InputStream aIn = new ASN1InputStream(dIn, length);
    290
    291        return aIn.buildConstructedOctetString(null);
    292    }
    293
    294    private ASN1EncodableVector buildEncodableVector(DERObject sentinel)
    295        throws IOException {
    296        ASN1EncodableVector v = new ASN1EncodableVector();
    297        DERObject o;
    298
    299        while ((o = readObject()) != sentinel) {
    300            v.add(o);
    301        }
    302
    303        return v;
    304    }
    305
    306    private ASN1EncodableVector buildDerEncodableVector(int length)
    307        throws IOException {
    308        DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
    309        ASN1InputStream aIn = new ASN1InputStream(dIn, length);
    310
    311        return aIn.buildEncodableVector(null);
    312    }
    313
    314    public DERObject readObject()
    315        throws IOException {
    316        int tag = read();
    317        if (tag == -1) {
    318            if (eofFound) {
    319                throw new EOFException("attempt to read past end of file.");
    320            }
    321
    322            eofFound = true;
    323
    324            return null;
    325        }
    326
    327        int tagNo = 0;
    328
    329        if ((tag & TAGGED) != 0 || (tag & APPLICATION) != 0) {
    330            tagNo = readTagNumber(tag);
    331        }
    332
    333        int length = readLength();
    334
    335        if (length < 0)    // indefinite length method
    336        {
    337            switch (tag) {
    338                case NULL:
    339                    return BERNull.INSTANCE;
    340                case SEQUENCE | CONSTRUCTED:
    341                    return new BERSequence(buildEncodableVector(END_OF_STREAM));
    342                case SET | CONSTRUCTED:
    343                    return new BERSet(buildEncodableVector(END_OF_STREAM), false);
    344                case OCTET_STRING | CONSTRUCTED:
    345                    return buildConstructedOctetString(END_OF_STREAM);
    346                default: {
    347                    //
    348                    // with tagged object tag number is bottom 5 bits
    349                    //
    350                    if ((tag & TAGGED) != 0) {
    351                        //
    352                        // simple type - implicit... return an octet string
    353                        //
    354                        if ((tag & CONSTRUCTED) == 0) {
    355                            byte[] bytes = readIndefiniteLengthFully();
    356
    357                            return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
    358                        }
    359
    360                        //
    361                        // either constructed or explicitly tagged
    362                        //
    363                        ASN1EncodableVector v = buildEncodableVector(END_OF_STREAM);
    364
    365                        if (v.size() == 0)     // empty tag!
    366                        {
    367                            return new DERTaggedObject(tagNo);
    368                        }
    369
    370                        if (v.size() == 1) {
    371                            //
    372                            // explicitly tagged (probably!) - if it isn't we'd have to
    373                            // tell from the context
    374                            //
    375                            return new BERTaggedObject(tagNo, v.get(0));
    376                        }
    377
    378                        return new BERTaggedObject(false, tagNo, new BERSequence(v));
    379                    }
    380
    381                    throw new IOException("unknown BER object encountered");
    382                }
    383            }
    384        } else {
    385            if (tag == 0 && length == 0)    // end of contents marker.
    386            {
    387                return END_OF_STREAM;
    388            }
    389
    390            return buildObject(tag, tagNo, length);
    391        }
    392    }
    393
    394    private int readTagNumber(int tag)
    395        throws IOException {
    396        int tagNo = tag & 0x1f;
    397
    398        if (tagNo == 0x1f) {
    399            int b = read();
    400
    401            tagNo = 0;
    402
    403            while ((b >= 0) && ((b & 0x80) != 0)) {
    404                tagNo |= (b & 0x7f);
    405                tagNo <<= 7;
    406                b = read();
    407            }
    408
    409            if (b < 0) {
    410                eofFound = true;
    411                throw new EOFException("EOF found inside tag value.");
    412            }
    413
    414            tagNo |= (b & 0x7f);
    415        }
    416
    417        return tagNo;
    418    }
    419}
    420