1package org.bouncycastle.asn1;
2
3import java.io.ByteArrayInputStream;
4import java.io.IOException;
5import java.io.InputStream;
6
7public class ASN1StreamParser
8{
9    private final InputStream _in;
10    private final int         _limit;
11    private final byte[][] tmpBuffers;
12
13    public ASN1StreamParser(
14        InputStream in)
15    {
16        this(in, StreamUtil.findLimit(in));
17    }
18
19    public ASN1StreamParser(
20        InputStream in,
21        int         limit)
22    {
23        this._in = in;
24        this._limit = limit;
25
26        this.tmpBuffers = new byte[11][];
27    }
28
29    public ASN1StreamParser(
30        byte[] encoding)
31    {
32        this(new ByteArrayInputStream(encoding), encoding.length);
33    }
34
35    ASN1Encodable readIndef(int tagValue) throws IOException
36    {
37        // Note: INDEF => CONSTRUCTED
38
39        // TODO There are other tags that may be constructed (e.g. BIT_STRING)
40        switch (tagValue)
41        {
42            case BERTags.EXTERNAL:
43                return new DERExternalParser(this);
44            case BERTags.OCTET_STRING:
45                return new BEROctetStringParser(this);
46            case BERTags.SEQUENCE:
47                return new BERSequenceParser(this);
48            case BERTags.SET:
49                return new BERSetParser(this);
50            default:
51                throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue));
52        }
53    }
54
55    ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException
56    {
57        if (_in instanceof IndefiniteLengthInputStream)
58        {
59            if (!constructed)
60            {
61                throw new IOException("indefinite length primitive encoding encountered");
62            }
63
64            return readIndef(tag);
65        }
66
67        if (constructed)
68        {
69            switch (tag)
70            {
71                case BERTags.SET:
72                    return new DERSetParser(this);
73                case BERTags.SEQUENCE:
74                    return new DERSequenceParser(this);
75                case BERTags.OCTET_STRING:
76                    return new BEROctetStringParser(this);
77            }
78        }
79        else
80        {
81            switch (tag)
82            {
83                case BERTags.SET:
84                    throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
85                case BERTags.SEQUENCE:
86                    throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
87                case BERTags.OCTET_STRING:
88                    return new DEROctetStringParser((DefiniteLengthInputStream)_in);
89            }
90        }
91
92        // TODO ASN1Exception
93        throw new RuntimeException("implicit tagging not implemented");
94    }
95
96    ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException
97    {
98        if (!constructed)
99        {
100            // Note: !CONSTRUCTED => IMPLICIT
101            DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
102            return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray()));
103        }
104
105        ASN1EncodableVector v = readVector();
106
107        if (_in instanceof IndefiniteLengthInputStream)
108        {
109            return v.size() == 1
110                ?   new BERTaggedObject(true, tag, v.get(0))
111                :   new BERTaggedObject(false, tag, BERFactory.createSequence(v));
112        }
113
114        return v.size() == 1
115            ?   new DERTaggedObject(true, tag, v.get(0))
116            :   new DERTaggedObject(false, tag, DERFactory.createSequence(v));
117    }
118
119    public ASN1Encodable readObject()
120        throws IOException
121    {
122        int tag = _in.read();
123        if (tag == -1)
124        {
125            return null;
126        }
127
128        //
129        // turn of looking for "00" while we resolve the tag
130        //
131        set00Check(false);
132
133        //
134        // calculate tag number
135        //
136        int tagNo = ASN1InputStream.readTagNumber(_in, tag);
137
138        boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0;
139
140        //
141        // calculate length
142        //
143        int length = ASN1InputStream.readLength(_in, _limit);
144
145        if (length < 0) // indefinite length method
146        {
147            if (!isConstructed)
148            {
149                throw new IOException("indefinite length primitive encoding encountered");
150            }
151
152            IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
153            ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit);
154
155            if ((tag & BERTags.APPLICATION) != 0)
156            {
157                return new BERApplicationSpecificParser(tagNo, sp);
158            }
159
160            if ((tag & BERTags.TAGGED) != 0)
161            {
162                return new BERTaggedObjectParser(true, tagNo, sp);
163            }
164
165            return sp.readIndef(tagNo);
166        }
167        else
168        {
169            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
170
171            if ((tag & BERTags.APPLICATION) != 0)
172            {
173                return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray());
174            }
175
176            if ((tag & BERTags.TAGGED) != 0)
177            {
178                return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn));
179            }
180
181            if (isConstructed)
182            {
183                // TODO There are other tags that may be constructed (e.g. BIT_STRING)
184                switch (tagNo)
185                {
186                    case BERTags.OCTET_STRING:
187                        //
188                        // yes, people actually do this...
189                        //
190                        return new BEROctetStringParser(new ASN1StreamParser(defIn));
191                    case BERTags.SEQUENCE:
192                        return new DERSequenceParser(new ASN1StreamParser(defIn));
193                    case BERTags.SET:
194                        return new DERSetParser(new ASN1StreamParser(defIn));
195                    case BERTags.EXTERNAL:
196                        return new DERExternalParser(new ASN1StreamParser(defIn));
197                    default:
198                        throw new IOException("unknown tag " + tagNo + " encountered");
199                }
200            }
201
202            // Some primitive encodings can be handled by parsers too...
203            switch (tagNo)
204            {
205                case BERTags.OCTET_STRING:
206                    return new DEROctetStringParser(defIn);
207            }
208
209            try
210            {
211                return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers);
212            }
213            catch (IllegalArgumentException e)
214            {
215                throw new ASN1Exception("corrupted stream detected", e);
216            }
217        }
218    }
219
220    private void set00Check(boolean enabled)
221    {
222        if (_in instanceof IndefiniteLengthInputStream)
223        {
224            ((IndefiniteLengthInputStream)_in).setEofOn00(enabled);
225        }
226    }
227
228    ASN1EncodableVector readVector() throws IOException
229    {
230        ASN1EncodableVector v = new ASN1EncodableVector();
231
232        ASN1Encodable obj;
233        while ((obj = readObject()) != null)
234        {
235            if (obj instanceof InMemoryRepresentable)
236            {
237                v.add(((InMemoryRepresentable)obj).getLoadedObject());
238            }
239            else
240            {
241                v.add(obj.toASN1Primitive());
242            }
243        }
244
245        return v;
246    }
247}
248