    001package org.apache.commons.ssl.asn1;
    002
    003import java.io.ByteArrayInputStream;
    004import java.io.EOFException;
    005import java.io.IOException;
    006import java.io.InputStream;
    007
    008public class ASN1StreamParser {
    009    InputStream _in;
    010
    011    private int _limit;
    012    private boolean _eofFound;
    013
    014    public ASN1StreamParser(
    015        InputStream in) {
    016        this(in, Integer.MAX_VALUE);
    017    }
    018
    019    public ASN1StreamParser(
    020        InputStream in,
    021        int limit) {
    022        this._in = in;
    023        this._limit = limit;
    024    }
    025
    026    public ASN1StreamParser(
    027        byte[] encoding) {
    028        this(new ByteArrayInputStream(encoding), encoding.length);
    029    }
    030
    031    InputStream getParentStream() {
    032        return _in;
    033    }
    034
    035    private int readLength()
    036        throws IOException {
    037        int length = _in.read();
    038        if (length < 0) {
    039            throw new EOFException("EOF found when length expected");
    040        }
    041
    042        if (length == 0x80) {
    043            return -1;      // indefinite-length encoding
    044        }
    045
    046        if (length > 127) {
    047            int size = length & 0x7f;
    048
    049            if (size > 4) {
    050                throw new IOException("DER length more than 4 bytes");
    051            }
    052
    053            length = 0;
    054            for (int i = 0; i < size; i++) {
    055                int next = _in.read();
    056
    057                if (next < 0) {
    058                    throw new EOFException("EOF found reading length");
    059                }
    060
    061                length = (length << 8) + next;
    062            }
    063
    064            if (length < 0) {
    065                throw new IOException("corrupted stream - negative length found");
    066            }
    067
    068            if (length >= _limit)   // after all we must have read at least 1 byte
    069            {
    070                throw new IOException("corrupted stream - out of bounds length found");
    071            }
    072        }
    073
    074        return length;
    075    }
    076
    077    public DEREncodable readObject()
    078        throws IOException {
    079        int tag = _in.read();
    080        if (tag == -1) {
    081            if (_eofFound) {
    082                throw new EOFException("attempt to read past end of file.");
    083            }
    084
    085            _eofFound = true;
    086
    087            return null;
    088        }
    089
    090        //
    091        // turn of looking for "00" while we resolve the tag
    092        //
    093        set00Check(false);
    094
    095        //
    096        // calculate tag number
    097        //
    098        int baseTagNo = tag & ~DERTags.CONSTRUCTED;
    099        int tagNo = baseTagNo;
    100
    101        if ((tag & DERTags.TAGGED) != 0) {
    102            tagNo = tag & 0x1f;
    103
    104            //
    105            // with tagged object tag number is bottom 5 bits, or stored at the start of the content
    106            //
    107            if (tagNo == 0x1f) {
    108                tagNo = 0;
    109
    110                int b = _in.read();
    111
    112                while ((b >= 0) && ((b & 0x80) != 0)) {
    113                    tagNo |= (b & 0x7f);
    114                    tagNo <<= 7;
    115                    b = _in.read();
    116                }
    117
    118                if (b < 0) {
    119                    _eofFound = true;
    120
    121                    throw new EOFException("EOF encountered inside tag value.");
    122                }
    123
    124                tagNo |= (b & 0x7f);
    125            }
    126        }
    127
    128        //
    129        // calculate length
    130        //
    131        int length = readLength();
    132
    133        if (length < 0)  // indefinite length
    134        {
    135            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in);
    136
    137            switch (baseTagNo) {
    138                case DERTags.NULL:
    139                    while (indIn.read() >= 0) {
    140                        // make sure we skip to end of object
    141                    }
    142                    return BERNull.INSTANCE;
    143                case DERTags.OCTET_STRING:
    144                    return new BEROctetStringParser(new ASN1ObjectParser(tag, tagNo, indIn));
    145                case DERTags.SEQUENCE:
    146                    return new BERSequenceParser(new ASN1ObjectParser(tag, tagNo, indIn));
    147                case DERTags.SET:
    148                    return new BERSetParser(new ASN1ObjectParser(tag, tagNo, indIn));
    149                default:
    150                    return new BERTaggedObjectParser(tag, tagNo, indIn);
    151            }
    152        } else {
    153            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
    154
    155            switch (baseTagNo) {
    156                case DERTags.INTEGER:
    157                    return new DERInteger(defIn.toByteArray());
    158                case DERTags.NULL:
    159                    defIn.toByteArray(); // make sure we read to end of object bytes.
    160                    return DERNull.INSTANCE;
    161                case DERTags.OBJECT_IDENTIFIER:
    162                    return new DERObjectIdentifier(defIn.toByteArray());
    163                case DERTags.OCTET_STRING:
    164                    return new DEROctetString(defIn.toByteArray());
    165                case DERTags.SEQUENCE:
    166                    return new DERSequence(loadVector(defIn, length)).parser();
    167                case DERTags.SET:
    168                    return new DERSet(loadVector(defIn, length)).parser();
    169                default:
    170                    return new BERTaggedObjectParser(tag, tagNo, defIn);
    171            }
    172        }
    173    }
    174
    175    private void set00Check(boolean enabled) {
    176        if (_in instanceof IndefiniteLengthInputStream) {
    177            ((IndefiniteLengthInputStream) _in).setEofOn00(enabled);
    178        }
    179    }
    180
    181    private ASN1EncodableVector loadVector(InputStream in, int length)
    182        throws IOException {
    183        ASN1InputStream aIn = new ASN1InputStream(in, length);
    184        ASN1EncodableVector v = new ASN1EncodableVector();
    185
    186        DERObject obj;
    187        while ((obj = aIn.readObject()) != null) {
    188            v.add(obj);
    189        }
    190
    191        return v;
    192    }
    193}