| 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. |
| */ |
| 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); |
| } |
| } |
| |
| // TODO ASN1Exception |
| throw new RuntimeException("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 DERApplicationSpecific(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; |
| } |
| } |