001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.EOFException;
005    import java.io.IOException;
006    import java.io.InputStream;
007    
008    public class ASN1StreamParser {
009        InputStream _in;
010    
011        private int _limit;
012        private boolean _eofFound;
013    
014        public ASN1StreamParser(
015            InputStream in) {
016            this(in, Integer.MAX_VALUE);
017        }
018    
019        public ASN1StreamParser(
020            InputStream in,
021            int limit) {
022            this._in = in;
023            this._limit = limit;
024        }
025    
026        public ASN1StreamParser(
027            byte[] encoding) {
028            this(new ByteArrayInputStream(encoding), encoding.length);
029        }
030    
031        InputStream getParentStream() {
032            return _in;
033        }
034    
035        private int readLength()
036            throws IOException {
037            int length = _in.read();
038            if (length < 0) {
039                throw new EOFException("EOF found when length expected");
040            }
041    
042            if (length == 0x80) {
043                return -1;      // indefinite-length encoding
044            }
045    
046            if (length > 127) {
047                int size = length & 0x7f;
048    
049                if (size > 4) {
050                    throw new IOException("DER length more than 4 bytes");
051                }
052    
053                length = 0;
054                for (int i = 0; i < size; i++) {
055                    int next = _in.read();
056    
057                    if (next < 0) {
058                        throw new EOFException("EOF found reading length");
059                    }
060    
061                    length = (length << 8) + next;
062                }
063    
064                if (length < 0) {
065                    throw new IOException("corrupted stream - negative length found");
066                }
067    
068                if (length >= _limit)   // after all we must have read at least 1 byte
069                {
070                    throw new IOException("corrupted stream - out of bounds length found");
071                }
072            }
073    
074            return length;
075        }
076    
077        public DEREncodable readObject()
078            throws IOException {
079            int tag = _in.read();
080            if (tag == -1) {
081                if (_eofFound) {
082                    throw new EOFException("attempt to read past end of file.");
083                }
084    
085                _eofFound = true;
086    
087                return null;
088            }
089    
090            //
091            // turn of looking for "00" while we resolve the tag
092            //
093            set00Check(false);
094    
095            //
096            // calculate tag number
097            //
098            int baseTagNo = tag & ~DERTags.CONSTRUCTED;
099            int tagNo = baseTagNo;
100    
101            if ((tag & DERTags.TAGGED) != 0) {
102                tagNo = tag & 0x1f;
103    
104                //
105                // with tagged object tag number is bottom 5 bits, or stored at the start of the content
106                //
107                if (tagNo == 0x1f) {
108                    tagNo = 0;
109    
110                    int b = _in.read();
111    
112                    while ((b >= 0) && ((b & 0x80) != 0)) {
113                        tagNo |= (b & 0x7f);
114                        tagNo <<= 7;
115                        b = _in.read();
116                    }
117    
118                    if (b < 0) {
119                        _eofFound = true;
120    
121                        throw new EOFException("EOF encountered inside tag value.");
122                    }
123    
124                    tagNo |= (b & 0x7f);
125                }
126            }
127    
128            //
129            // calculate length
130            //
131            int length = readLength();
132    
133            if (length < 0)  // indefinite length
134            {
135                IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in);
136    
137                switch (baseTagNo) {
138                    case DERTags.NULL:
139                        while (indIn.read() >= 0) {
140                            // make sure we skip to end of object
141                        }
142                        return BERNull.INSTANCE;
143                    case DERTags.OCTET_STRING:
144                        return new BEROctetStringParser(new ASN1ObjectParser(tag, tagNo, indIn));
145                    case DERTags.SEQUENCE:
146                        return new BERSequenceParser(new ASN1ObjectParser(tag, tagNo, indIn));
147                    case DERTags.SET:
148                        return new BERSetParser(new ASN1ObjectParser(tag, tagNo, indIn));
149                    default:
150                        return new BERTaggedObjectParser(tag, tagNo, indIn);
151                }
152            } else {
153                DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
154    
155                switch (baseTagNo) {
156                    case DERTags.INTEGER:
157                        return new DERInteger(defIn.toByteArray());
158                    case DERTags.NULL:
159                        defIn.toByteArray(); // make sure we read to end of object bytes.
160                        return DERNull.INSTANCE;
161                    case DERTags.OBJECT_IDENTIFIER:
162                        return new DERObjectIdentifier(defIn.toByteArray());
163                    case DERTags.OCTET_STRING:
164                        return new DEROctetString(defIn.toByteArray());
165                    case DERTags.SEQUENCE:
166                        return new DERSequence(loadVector(defIn, length)).parser();
167                    case DERTags.SET:
168                        return new DERSet(loadVector(defIn, length)).parser();
169                    default:
170                        return new BERTaggedObjectParser(tag, tagNo, defIn);
171                }
172            }
173        }
174    
175        private void set00Check(boolean enabled) {
176            if (_in instanceof IndefiniteLengthInputStream) {
177                ((IndefiniteLengthInputStream) _in).setEofOn00(enabled);
178            }
179        }
180    
181        private ASN1EncodableVector loadVector(InputStream in, int length)
182            throws IOException {
183            ASN1InputStream aIn = new ASN1InputStream(in, length);
184            ASN1EncodableVector v = new ASN1EncodableVector();
185    
186            DERObject obj;
187            while ((obj = aIn.readObject()) != null) {
188                v.add(obj);
189            }
190    
191            return v;
192        }
193    }