package org.bouncycastle.asn1;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters.
/** * A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters. */
public class ASN1StreamParser { private final InputStream _in; private final int _limit; private final byte[][] tmpBuffers; public ASN1StreamParser( InputStream in) { this(in, StreamUtil.findLimit(in)); } public ASN1StreamParser( InputStream in, int limit) { this._in = in; this._limit = limit; this.tmpBuffers = new byte[11][]; } public ASN1StreamParser( byte[] encoding) { this(new ByteArrayInputStream(encoding), encoding.length); } ASN1Encodable readIndef(int tagValue) throws IOException { // Note: INDEF => CONSTRUCTED // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagValue) { case BERTags.EXTERNAL: return new DERExternalParser(this); case BERTags.OCTET_STRING: return new BEROctetStringParser(this); case BERTags.SEQUENCE: return new BERSequenceParser(this); case BERTags.SET: return new BERSetParser(this); default: throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue)); } } ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException { if (_in instanceof IndefiniteLengthInputStream) { if (!constructed) { throw new IOException("indefinite-length primitive encoding encountered"); } return readIndef(tag); } if (constructed) { switch (tag) { case BERTags.SET: return new DERSetParser(this); case BERTags.SEQUENCE: return new DERSequenceParser(this); case BERTags.OCTET_STRING: return new BEROctetStringParser(this); } } else { switch (tag) { case BERTags.SET: throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); case BERTags.SEQUENCE: throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); case BERTags.OCTET_STRING: return new DEROctetStringParser((DefiniteLengthInputStream)_in); } } throw new ASN1Exception("implicit tagging not implemented"); } ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException { if (!constructed) { // Note: !CONSTRUCTED => IMPLICIT DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray())); } ASN1EncodableVector v = readVector(); if (_in instanceof IndefiniteLengthInputStream) { return v.size() == 1 ? new BERTaggedObject(true, tag, v.get(0)) : new BERTaggedObject(false, tag, BERFactory.createSequence(v)); } return v.size() == 1 ? new DERTaggedObject(true, tag, v.get(0)) : new DERTaggedObject(false, tag, DERFactory.createSequence(v)); } public ASN1Encodable readObject() throws IOException { int tag = _in.read(); if (tag == -1) { return null; } // // turn of looking for "00" while we resolve the tag // set00Check(false); // // calculate tag number // int tagNo = ASN1InputStream.readTagNumber(_in, tag); boolean isConstructed = (tag & BERTags.CONSTRUCTED) != 0; // // calculate length // int length = ASN1InputStream.readLength(_in, _limit); if (length < 0) // indefinite-length method { if (!isConstructed) { throw new IOException("indefinite-length primitive encoding encountered"); } IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); if ((tag & BERTags.APPLICATION) != 0) { return new BERApplicationSpecificParser(tagNo, sp); } if ((tag & BERTags.TAGGED) != 0) { return new BERTaggedObjectParser(true, tagNo, sp); } return sp.readIndef(tagNo); } else { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); if ((tag & BERTags.APPLICATION) != 0) { return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); } if ((tag & BERTags.TAGGED) != 0) { return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn)); } if (isConstructed) { // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagNo) { case BERTags.OCTET_STRING: // // yes, people actually do this... // return new BEROctetStringParser(new ASN1StreamParser(defIn)); case BERTags.SEQUENCE: return new DERSequenceParser(new ASN1StreamParser(defIn)); case BERTags.SET: return new DERSetParser(new ASN1StreamParser(defIn)); case BERTags.EXTERNAL: return new DERExternalParser(new ASN1StreamParser(defIn)); default: throw new IOException("unknown tag " + tagNo + " encountered"); } } // Some primitive encodings can be handled by parsers too... switch (tagNo) { case BERTags.OCTET_STRING: return new DEROctetStringParser(defIn); } try { return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers); } catch (IllegalArgumentException e) { throw new ASN1Exception("corrupted stream detected", e); } } } private void set00Check(boolean enabled) { if (_in instanceof IndefiniteLengthInputStream) { ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); } } ASN1EncodableVector readVector() throws IOException { ASN1EncodableVector v = new ASN1EncodableVector(); ASN1Encodable obj; while ((obj = readObject()) != null) { if (obj instanceof InMemoryRepresentable) { v.add(((InMemoryRepresentable)obj).getLoadedObject()); } else { v.add(obj.toASN1Primitive()); } } return v; } }