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