001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.ByteArrayOutputStream;
005    import java.io.EOFException;
006    import java.io.FilterInputStream;
007    import java.io.IOException;
008    import java.io.InputStream;
009    import 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     */
017    public 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