001package org.apache.commons.ssl.asn1;
002
003import java.io.ByteArrayInputStream;
004import java.io.ByteArrayOutputStream;
005import java.io.EOFException;
006import java.io.FilterInputStream;
007import java.io.IOException;
008import java.io.InputStream;
009import java.util.Vector;
010
011/**
012 * a general purpose ASN.1 decoder - note: this class differs from the
013 * others in that it returns null after it has read the last object in
014 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
015 * returned.
016 */
017public class ASN1InputStream
018    extends FilterInputStream
019    implements DERTags {
020    private static final DERObject END_OF_STREAM = new DERObject() {
021        void encode(
022            DEROutputStream out)
023            throws IOException {
024            throw new IOException("Eeek!");
025        }
026        public int hashCode() {
027            return 0;
028        }
029        public boolean equals(
030            Object o) {
031            return o == this;
032        }
033    };
034
035    boolean eofFound = false;
036    int limit = Integer.MAX_VALUE;
037
038    public ASN1InputStream(
039        InputStream is) {
040        super(is);
041    }
042
043    /**
044     * Create an ASN1InputStream based on the input byte array. The length of DER objects in
045     * the stream is automatically limited to the length of the input array.
046     *
047     * @param input array containing ASN.1 encoded data.
048     */
049    public ASN1InputStream(
050        byte[] input) {
051        this(new ByteArrayInputStream(input), input.length);
052    }
053
054    /**
055     * Create an ASN1InputStream where no DER object will be longer than limit.
056     *
057     * @param input stream containing ASN.1 encoded data.
058     * @param limit maximum size of a DER encoded object.
059     */
060    public ASN1InputStream(
061        InputStream input,
062        int limit) {
063        super(input);
064        this.limit = limit;
065    }
066
067    protected int readLength()
068        throws IOException {
069        int length = read();
070        if (length < 0) {
071            throw new IOException("EOF found when length expected");
072        }
073
074        if (length == 0x80) {
075            return -1;      // indefinite-length encoding
076        }
077
078        if (length > 127) {
079            int size = length & 0x7f;
080
081            if (size > 4) {
082                throw new IOException("DER length more than 4 bytes");
083            }
084
085            length = 0;
086            for (int i = 0; i < size; i++) {
087                int next = read();
088
089                if (next < 0) {
090                    throw new IOException("EOF found reading length");
091                }
092
093                length = (length << 8) + next;
094            }
095
096            if (length < 0) {
097                throw new IOException("corrupted stream - negative length found");
098            }
099
100            if (length >= limit)   // after all we must have read at least 1 byte
101            {
102                throw new IOException("corrupted stream - out of bounds length found");
103            }
104        }
105
106        return length;
107    }
108
109    protected void readFully(
110        byte[] bytes)
111        throws IOException {
112        int left = bytes.length;
113        int len;
114
115        if (left == 0) {
116            return;
117        }
118
119        while ((len = read(bytes, bytes.length - left, left)) > 0) {
120            if ((left -= len) == 0) {
121                return;
122            }
123        }
124
125        if (left != 0) {
126            throw new EOFException("EOF encountered in middle of object");
127        }
128    }
129
130    /** build an object given its tag and the number of bytes to construct it from. */
131    protected DERObject buildObject(
132        int tag,
133        int tagNo,
134        int length)
135        throws IOException {
136        if ((tag & APPLICATION) != 0) {
137            return new DERApplicationSpecific(tagNo, readDefiniteLengthFully(length));
138        }
139
140        boolean isConstructed = (tag & CONSTRUCTED) != 0;
141
142        if (isConstructed) {
143            switch (tag) {
144                case SEQUENCE | CONSTRUCTED:
145                    return new DERSequence(buildDerEncodableVector(length));
146                case SET | CONSTRUCTED:
147                    return new DERSet(buildDerEncodableVector(length), false);
148                case OCTET_STRING | CONSTRUCTED:
149                    return buildDerConstructedOctetString(length);
150                default: {
151                    //
152                    // with tagged object tag number is bottom 5 bits
153                    //
154                    if ((tag & TAGGED) != 0) {
155                        if (length == 0)     // empty tag!
156                        {
157                            return new DERTaggedObject(false, tagNo, new DERSequence());
158                        }
159
160                        ASN1EncodableVector v = buildDerEncodableVector(length);
161
162                        if (v.size() == 1) {
163                            //
164                            // explicitly tagged (probably!) - if it isn't we'd have to
165                            // tell from the context
166                            //
167                            return new DERTaggedObject(tagNo, v.get(0));
168                        }
169
170                        return new DERTaggedObject(false, tagNo, new DERSequence(v));
171                    }
172
173                    return new DERUnknownTag(tag, readDefiniteLengthFully(length));
174                }
175            }
176        }
177
178        byte[] bytes = readDefiniteLengthFully(length);
179
180        switch (tag) {
181            case NULL:
182                return DERNull.INSTANCE;
183            case BOOLEAN:
184                return new DERBoolean(bytes);
185            case INTEGER:
186                return new DERInteger(bytes);
187            case ENUMERATED:
188                return new DEREnumerated(bytes);
189            case OBJECT_IDENTIFIER:
190                return new DERObjectIdentifier(bytes);
191            case BIT_STRING: {
192                int padBits = bytes[0];
193                byte[] data = new byte[bytes.length - 1];
194
195                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
196
197                return new DERBitString(data, padBits);
198            }
199            case NUMERIC_STRING:
200                return new DERNumericString(bytes);
201            case UTF8_STRING:
202                return new DERUTF8String(bytes);
203            case PRINTABLE_STRING:
204                return new DERPrintableString(bytes);
205            case IA5_STRING:
206                return new DERIA5String(bytes);
207            case T61_STRING:
208                return new DERT61String(bytes);
209            case VISIBLE_STRING:
210                return new DERVisibleString(bytes);
211            case GENERAL_STRING:
212                return new DERGeneralString(bytes);
213            case UNIVERSAL_STRING:
214                return new DERUniversalString(bytes);
215            case BMP_STRING:
216                return new DERBMPString(bytes);
217            case OCTET_STRING:
218                return new DEROctetString(bytes);
219            case UTC_TIME:
220                return new DERUTCTime(bytes);
221            case GENERALIZED_TIME:
222                return new DERGeneralizedTime(bytes);
223            default: {
224                //
225                // with tagged object tag number is bottom 5 bits
226                //
227                if ((tag & TAGGED) != 0) {
228                    if (bytes.length == 0)     // empty tag!
229                    {
230                        return new DERTaggedObject(false, tagNo, DERNull.INSTANCE);
231                    }
232
233                    //
234                    // simple type - implicit... return an octet string
235                    //
236                    return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
237                }
238
239                return new DERUnknownTag(tag, bytes);
240            }
241        }
242    }
243
244    private byte[] readDefiniteLengthFully(int length)
245        throws IOException {
246        byte[] bytes = new byte[length];
247        readFully(bytes);
248        return bytes;
249    }
250
251    /** read a string of bytes representing an indefinite length object. */
252    private byte[] readIndefiniteLengthFully()
253        throws IOException {
254        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
255        int b, b1;
256
257        b1 = read();
258
259        while ((b = read()) >= 0) {
260            if (b1 == 0 && b == 0) {
261                break;
262            }
263
264            bOut.write(b1);
265            b1 = b;
266        }
267
268        return bOut.toByteArray();
269    }
270
271    private BERConstructedOctetString buildConstructedOctetString(DERObject sentinel)
272        throws IOException {
273        Vector octs = new Vector();
274        DERObject o;
275
276        while ((o = readObject()) != sentinel) {
277            octs.addElement(o);
278        }
279
280        return new BERConstructedOctetString(octs);
281    }
282
283    //
284    // yes, people actually do this...
285    //
286    private BERConstructedOctetString buildDerConstructedOctetString(int length)
287        throws IOException {
288        DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
289        ASN1InputStream aIn = new ASN1InputStream(dIn, length);
290
291        return aIn.buildConstructedOctetString(null);
292    }
293
294    private ASN1EncodableVector buildEncodableVector(DERObject sentinel)
295        throws IOException {
296        ASN1EncodableVector v = new ASN1EncodableVector();
297        DERObject o;
298
299        while ((o = readObject()) != sentinel) {
300            v.add(o);
301        }
302
303        return v;
304    }
305
306    private ASN1EncodableVector buildDerEncodableVector(int length)
307        throws IOException {
308        DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
309        ASN1InputStream aIn = new ASN1InputStream(dIn, length);
310
311        return aIn.buildEncodableVector(null);
312    }
313
314    public DERObject readObject()
315        throws IOException {
316        int tag = read();
317        if (tag == -1) {
318            if (eofFound) {
319                throw new EOFException("attempt to read past end of file.");
320            }
321
322            eofFound = true;
323
324            return null;
325        }
326
327        int tagNo = 0;
328
329        if ((tag & TAGGED) != 0 || (tag & APPLICATION) != 0) {
330            tagNo = readTagNumber(tag);
331        }
332
333        int length = readLength();
334
335        if (length < 0)    // indefinite length method
336        {
337            switch (tag) {
338                case NULL:
339                    return BERNull.INSTANCE;
340                case SEQUENCE | CONSTRUCTED:
341                    return new BERSequence(buildEncodableVector(END_OF_STREAM));
342                case SET | CONSTRUCTED:
343                    return new BERSet(buildEncodableVector(END_OF_STREAM), false);
344                case OCTET_STRING | CONSTRUCTED:
345                    return buildConstructedOctetString(END_OF_STREAM);
346                default: {
347                    //
348                    // with tagged object tag number is bottom 5 bits
349                    //
350                    if ((tag & TAGGED) != 0) {
351                        //
352                        // simple type - implicit... return an octet string
353                        //
354                        if ((tag & CONSTRUCTED) == 0) {
355                            byte[] bytes = readIndefiniteLengthFully();
356
357                            return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
358                        }
359
360                        //
361                        // either constructed or explicitly tagged
362                        //
363                        ASN1EncodableVector v = buildEncodableVector(END_OF_STREAM);
364
365                        if (v.size() == 0)     // empty tag!
366                        {
367                            return new DERTaggedObject(tagNo);
368                        }
369
370                        if (v.size() == 1) {
371                            //
372                            // explicitly tagged (probably!) - if it isn't we'd have to
373                            // tell from the context
374                            //
375                            return new BERTaggedObject(tagNo, v.get(0));
376                        }
377
378                        return new BERTaggedObject(false, tagNo, new BERSequence(v));
379                    }
380
381                    throw new IOException("unknown BER object encountered");
382                }
383            }
384        } else {
385            if (tag == 0 && length == 0)    // end of contents marker.
386            {
387                return END_OF_STREAM;
388            }
389
390            return buildObject(tag, tagNo, length);
391        }
392    }
393
394    private int readTagNumber(int tag)
395        throws IOException {
396        int tagNo = tag & 0x1f;
397
398        if (tagNo == 0x1f) {
399            int b = read();
400
401            tagNo = 0;
402
403            while ((b >= 0) && ((b & 0x80) != 0)) {
404                tagNo |= (b & 0x7f);
405                tagNo <<= 7;
406                b = read();
407            }
408
409            if (b < 0) {
410                eofFound = true;
411                throw new EOFException("EOF found inside tag value.");
412            }
413
414            tagNo |= (b & 0x7f);
415        }
416
417        return tagNo;
418    }
419}
420