blob: a4bb370eb2e8a04f8b76b0aa7c78a1ca63eb0b2a [file] [log] [blame]
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);
}
}
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 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;
}
}