001package org.apache.commons.ssl.org.bouncycastle.asn1;
002
003import java.io.ByteArrayInputStream;
004import java.io.IOException;
005import java.io.InputStream;
006
007/**
008 * A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters.
009 */
010public class ASN1StreamParser
011{
012    private final InputStream _in;
013    private final int         _limit;
014    private final byte[][] tmpBuffers;
015
016    public ASN1StreamParser(
017        InputStream in)
018    {
019        this(in, StreamUtil.findLimit(in));
020    }
021
022    public ASN1StreamParser(
023        InputStream in,
024        int         limit)
025    {
026        this._in = in;
027        this._limit = limit;
028
029        this.tmpBuffers = new byte[11][];
030    }
031
032    public ASN1StreamParser(
033        byte[] encoding)
034    {
035        this(new ByteArrayInputStream(encoding), encoding.length);
036    }
037
038    ASN1Encodable readIndef(int tagValue) throws IOException
039    {
040        // Note: INDEF => CONSTRUCTED
041
042        // TODO There are other tags that may be constructed (e.g. BIT_STRING)
043        switch (tagValue)
044        {
045            case BERTags.EXTERNAL:
046                return new DERExternalParser(this);
047            case BERTags.OCTET_STRING:
048                return new BEROctetStringParser(this);
049            case BERTags.SEQUENCE:
050                return new BERSequenceParser(this);
051            case BERTags.SET:
052                return new BERSetParser(this);
053            default:
054                throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
055        }
056    }
057
058    ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
059    {
060        if (_in instanceof IndefiniteLengthInputStream)
061        {
062            if (!constructed)
063            {
064                throw new IOException("indefinite length primitive encoding encountered");
065            }
066            
067            return readIndef(tag);
068        }
069
070        if (constructed)
071        {
072            switch (tag)
073            {
074                case BERTags.SET:
075                    return new DERSetParser(this);
076                case BERTags.SEQUENCE:
077                    return new DERSequenceParser(this);
078                case BERTags.OCTET_STRING:
079                    return new BEROctetStringParser(this);
080            }
081        }
082        else
083        {
084            switch (tag)
085            {
086                case BERTags.SET:
087                    throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
088                case BERTags.SEQUENCE:
089                    throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
090                case BERTags.OCTET_STRING:
091                    return new DEROctetStringParser((DefiniteLengthInputStream)_in);
092            }
093        }
094
095        // TODO ASN1Exception
096        throw new RuntimeException("implicit tagging not implemented");
097    }
098
099    ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
100    {
101        if (!constructed)
102        {
103            // Note: !CONSTRUCTED => IMPLICIT
104            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
105            return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
106        }
107
108        ASN1EncodableVector v = readVector();
109
110        if (_in instanceof IndefiniteLengthInputStream)
111        {
112            return v.size() == 1
113                ?   new BERTaggedObject(true, tag, v.get(0))
114                :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
115        }
116
117        return v.size() == 1
118            ?   new DERTaggedObject(true, tag, v.get(0))
119            :   new DERTaggedObject(false, tag, DERFactory.createSequence(v));
120    }
121
122    public ASN1Encodable readObject()
123        throws IOException
124    {
125        int tag = _in.read();
126        if (tag == -1)
127        {
128            return null;
129        }
130
131        //
132        // turn of looking for "00" while we resolve the tag
133        //
134        set00Check(false);
135
136        //
137        // calculate tag number
138        //
139        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
140
141        boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
142
143        //
144        // calculate length
145        //
146        int length = ASN1InputStream.readLength(_in, _limit);
147
148        if (length < 0) // indefinite length method
149        {
150            if (!isConstructed)
151            {
152                throw new IOException("indefinite length primitive encoding encountered");
153            }
154
155            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
156            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
157
158            if ((tag & BERTags.APPLICATION) != 0)
159            {
160                return new BERApplicationSpecificParser(tagNo, sp);
161            }
162
163            if ((tag & BERTags.TAGGED) != 0)
164            {
165                return new BERTaggedObjectParser(true, tagNo, sp);
166            }
167
168            return sp.readIndef(tagNo);
169        }
170        else
171        {
172            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
173
174            if ((tag & BERTags.APPLICATION) != 0)
175            {
176                return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
177            }
178
179            if ((tag & BERTags.TAGGED) != 0)
180            {
181                return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
182            }
183
184            if (isConstructed)
185            {
186                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
187                switch (tagNo)
188                {
189                    case BERTags.OCTET_STRING:
190                        //
191                        // yes, people actually do this...
192                        //
193                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
194                    case BERTags.SEQUENCE:
195                        return new DERSequenceParser(new ASN1StreamParser(defIn));
196                    case BERTags.SET:
197                        return new DERSetParser(new ASN1StreamParser(defIn));
198                    case BERTags.EXTERNAL:
199                        return new DERExternalParser(new ASN1StreamParser(defIn));
200                    default:
201                        throw new IOException("unknown tag " + tagNo + " encountered");
202                }
203            }
204
205            // Some primitive encodings can be handled by parsers too...
206            switch (tagNo)
207            {
208                case BERTags.OCTET_STRING:
209                    return new DEROctetStringParser(defIn);
210            }
211
212            try
213            {
214                return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
215            }
216            catch (IllegalArgumentException e)
217            {
218                throw new ASN1Exception("corrupted stream detected", e);
219            }
220        }
221    }
222
223    private void set00Check(boolean enabled)
224    {
225        if (_in instanceof IndefiniteLengthInputStream)
226        {
227            ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
228        }
229    }
230
231    ASN1EncodableVector readVector() throws IOException
232    {
233        ASN1EncodableVector v = new ASN1EncodableVector();
234
235        ASN1Encodable obj;
236        while ((obj = readObject()) != null)
237        {
238            if (obj instanceof InMemoryRepresentable)
239            {
240                v.add(((InMemoryRepresentable)obj).getLoadedObject());
241            }
242            else
243            {
244                v.add(obj.toASN1Primitive());
245            }
246        }
247
248        return v;
249    }
250}