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}