diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-11 19:13:58 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-11 19:13:58 +0700 |
commit | 4c0a0bb815aa796c9765938434f756330663a76f (patch) | |
tree | 868a7018f8deb2df1c7773378cce6c0fc387d3e0 | |
parent | Add DLTaggedObject and use from parser (diff) | |
download | BouncyCastle.NET-ed25519-4c0a0bb815aa796c9765938434f756330663a76f.tar.xz |
Improved parsing of tagged objects
-rw-r--r-- | crypto/src/asn1/ASN1StreamParser.cs | 146 | ||||
-rw-r--r-- | crypto/src/asn1/ASN1TaggedObjectParser.cs | 2 | ||||
-rw-r--r-- | crypto/src/asn1/Asn1InputStream.cs | 180 | ||||
-rw-r--r-- | crypto/src/asn1/BERTaggedObjectParser.cs | 60 |
4 files changed, 196 insertions, 192 deletions
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs index 55b5bc656..5c6854e1d 100644 --- a/crypto/src/asn1/ASN1StreamParser.cs +++ b/crypto/src/asn1/ASN1StreamParser.cs @@ -35,91 +35,102 @@ namespace Org.BouncyCastle.Asn1 this.tmpBuffers = tmpBuffers; } - internal IAsn1Convertible ReadIndef(int tagValue) + internal IAsn1Convertible ReadIndef(int tagNo) { // Note: INDEF => CONSTRUCTED // TODO There are other tags that may be constructed (e.g. BIT_STRING) - switch (tagValue) + switch (tagNo) { - case Asn1Tags.External: - return new DerExternalParser(this); - case Asn1Tags.OctetString: - return new BerOctetStringParser(this); - case Asn1Tags.Sequence: - return new BerSequenceParser(this); - case Asn1Tags.Set: - return new BerSetParser(this); - default: - throw new Asn1Exception("unknown BER object encountered: 0x" + tagValue.ToString("X")); + case Asn1Tags.External: + return new DerExternalParser(this); + case Asn1Tags.OctetString: + return new BerOctetStringParser(this); + case Asn1Tags.Sequence: + return new BerSequenceParser(this); + case Asn1Tags.Set: + return new BerSetParser(this); + default: + throw new Asn1Exception("unknown BER object encountered: 0x" + tagNo.ToString("X")); } } - internal IAsn1Convertible ReadImplicit(bool constructed, int tag) + internal IAsn1Convertible ReadImplicit(bool constructed, int tagNo) { if (_in is IndefiniteLengthInputStream) { if (!constructed) throw new IOException("indefinite-length primitive encoding encountered"); - return ReadIndef(tag); + return ReadIndef(tagNo); } if (constructed) { - switch (tag) + switch (tagNo) { - case Asn1Tags.Set: - return new DerSetParser(this); - case Asn1Tags.Sequence: - return new DerSequenceParser(this); - case Asn1Tags.OctetString: - return new BerOctetStringParser(this); + case Asn1Tags.Set: + return new DerSetParser(this); + case Asn1Tags.Sequence: + return new DerSequenceParser(this); + case Asn1Tags.OctetString: + return new BerOctetStringParser(this); } } else { - switch (tag) + switch (tagNo) { - case Asn1Tags.Set: - throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); - case Asn1Tags.Sequence: - throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); - case Asn1Tags.OctetString: - return new DerOctetStringParser((DefiniteLengthInputStream)_in); + case Asn1Tags.Set: + throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); + case Asn1Tags.Sequence: + throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); + case Asn1Tags.OctetString: + return new DerOctetStringParser((DefiniteLengthInputStream)_in); } } throw new Asn1Exception("implicit tagging not implemented"); } - internal Asn1Object ReadTaggedObject(bool constructed, int tag) + internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed) { - if (!constructed) + if (!constructed) { - // Note: !CONSTRUCTED => IMPLICIT - DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; - return new DLTaggedObject(false, tag, new DerOctetString(defIn.ToArray())); + // Note: !CONSTRUCTED => IMPLICIT + DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; + byte[] contentsOctets = defIn.ToArray(); + + if (Asn1Tags.Application == tagClass) + return new DerApplicationSpecific(false, tagNo, contentsOctets); + + return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets)); } - Asn1EncodableVector v = ReadVector(); + Asn1EncodableVector contentsElements = ReadVector(); if (_in is IndefiniteLengthInputStream) { - return v.Count == 1 - ? new BerTaggedObject(true, tag, v[0]) - : new BerTaggedObject(false, tag, BerSequence.FromVector(v)); + if (Asn1Tags.Application == tagClass) + return new BerApplicationSpecific(tagNo, contentsElements); + + return contentsElements.Count == 1 + ? new BerTaggedObject(true, tagNo, contentsElements[0]) + : new BerTaggedObject(false, tagNo, BerSequence.FromVector(contentsElements)); } - return v.Count == 1 - ? new DLTaggedObject(true, tag, v[0]) - : new DLTaggedObject(false, tag, DLSequence.FromVector(v)); + if (Asn1Tags.Application == tagClass) + return new DerApplicationSpecific(tagNo, contentsElements); + + return contentsElements.Count == 1 + ? new DLTaggedObject(true, tagNo, contentsElements[0]) + : new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements)); } public virtual IAsn1Convertible ReadObject() { - int tag = _in.ReadByte(); - if (tag == -1) + int tagHdr = _in.ReadByte(); + if (tagHdr == -1) return null; // turn off looking for "00" while we resolve the tag @@ -128,15 +139,16 @@ namespace Org.BouncyCastle.Asn1 // // calculate tag number // - int tagNo = Asn1InputStream.ReadTagNumber(_in, tag); + int tagNo = Asn1InputStream.ReadTagNumber(_in, tagHdr); - bool isConstructed = (tag & Asn1Tags.Constructed) != 0; + bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0; // // calculate length // int length = Asn1InputStream.ReadLength(_in, _limit, - tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set || tagNo == Asn1Tags.External); + tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set || + tagNo == Asn1Tags.External); if (length < 0) // indefinite-length method { @@ -146,13 +158,13 @@ namespace Org.BouncyCastle.Asn1 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); Asn1StreamParser sp = new Asn1StreamParser(indIn, _limit, tmpBuffers); - int tagClass = tag & Asn1Tags.Private; + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) { - if ((tag & Asn1Tags.Application) != 0) + if (Asn1Tags.Application == tagClass) return new BerApplicationSpecificParser(tagNo, sp); - return new BerTaggedObjectParser(true, tagNo, sp); + return new BerTaggedObjectParser(tagClass, tagNo, true, sp); } return sp.ReadIndef(tagNo); @@ -161,14 +173,15 @@ namespace Org.BouncyCastle.Asn1 { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit); - int tagClass = tag & Asn1Tags.Private; + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) { - if ((tag & Asn1Tags.Application) != 0) - return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray()); + Asn1StreamParser sub = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers); + + if (Asn1Tags.Application == tagClass) + return (DerApplicationSpecific)sub.ReadTaggedObject(tagClass, tagNo, isConstructed); - return new BerTaggedObjectParser(isConstructed, tagNo, - new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers)); + return new BerTaggedObjectParser(tagClass, tagNo, isConstructed, sub); } if (!isConstructed) @@ -176,8 +189,8 @@ namespace Org.BouncyCastle.Asn1 // Some primitive encodings can be handled by parsers too... switch (tagNo) { - case Asn1Tags.OctetString: - return new DerOctetStringParser(defIn); + case Asn1Tags.OctetString: + return new DerOctetStringParser(defIn); } try @@ -195,19 +208,16 @@ namespace Org.BouncyCastle.Asn1 // TODO There are other tags that may be constructed (e.g. BitString) switch (tagNo) { - case Asn1Tags.OctetString: - // - // yes, people actually do this... - // - return new BerOctetStringParser(sp); - case Asn1Tags.Sequence: - return new DerSequenceParser(sp); - case Asn1Tags.Set: - return new DerSetParser(sp); - case Asn1Tags.External: - return new DerExternalParser(sp); - default: - throw new IOException("unknown tag " + tagNo + " encountered"); + case Asn1Tags.OctetString: + return new BerOctetStringParser(sp); + case Asn1Tags.Sequence: + return new DerSequenceParser(sp); + case Asn1Tags.Set: + return new DerSetParser(sp); + case Asn1Tags.External: + return new DerExternalParser(sp); + default: + throw new IOException("unknown tag " + tagNo + " encountered"); } } } diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs index 32327a269..dd031ad07 100644 --- a/crypto/src/asn1/ASN1TaggedObjectParser.cs +++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs @@ -3,6 +3,8 @@ namespace Org.BouncyCastle.Asn1 public interface Asn1TaggedObjectParser : IAsn1Convertible { + int TagClass { get; } + int TagNo { get; } IAsn1Convertible GetObjectParser(int tag, bool isExplicit); diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs index 27c583338..6e097e86d 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using Org.BouncyCastle.Utilities; @@ -74,23 +73,15 @@ namespace Org.BouncyCastle.Asn1 /** * build an object given its tag and the number of bytes to construct it from. */ - private Asn1Object BuildObject( - int tag, - int tagNo, - int length) + private Asn1Object BuildObject(int tagHdr, int tagNo, int length) { - bool isConstructed = (tag & Asn1Tags.Constructed) != 0; + bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0; DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit); - int tagClass = tag & Asn1Tags.Private; + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) - { - if ((tag & Asn1Tags.Application) != 0) - return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray()); - - return new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers).ReadTaggedObject(isConstructed, tagNo); - } + return ReadTaggedObject(tagClass, tagNo, isConstructed, defIn); if (!isConstructed) return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); @@ -98,16 +89,9 @@ namespace Org.BouncyCastle.Asn1 switch (tagNo) { case Asn1Tags.BitString: - { return BuildConstructedBitString(ReadVector(defIn)); - } case Asn1Tags.OctetString: - { - // - // yes, people actually do this... - // return BuildConstructedOctetString(ReadVector(defIn)); - } case Asn1Tags.Sequence: return CreateDLSequence(defIn); case Asn1Tags.Set: @@ -119,6 +103,29 @@ namespace Org.BouncyCastle.Asn1 } } + internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn) + { + if (!constructed) + { + // Note: !CONSTRUCTED => IMPLICIT + byte[] contentsOctets = defIn.ToArray(); + + if (Asn1Tags.Application == tagClass) + return new DerApplicationSpecific(false, tagNo, contentsOctets); + + return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets)); + } + + Asn1EncodableVector contentsElements = ReadVector(defIn); + + if (Asn1Tags.Application == tagClass) + return new DerApplicationSpecific(tagNo, contentsElements); + + return contentsElements.Count == 1 + ? new DLTaggedObject(true, tagNo, contentsElements[0]) + : new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements)); + } + internal virtual Asn1EncodableVector ReadVector() { Asn1Object o = ReadObject(); @@ -155,16 +162,16 @@ namespace Org.BouncyCastle.Asn1 public Asn1Object ReadObject() { - int tag = ReadByte(); - if (tag <= 0) + int tagHdr = ReadByte(); + if (tagHdr <= 0) { - if (tag == 0) + if (tagHdr == 0) throw new IOException("unexpected end-of-contents marker"); return null; } - int tagNo = ReadTagNumber(this, tag); + int tagNo = ReadTagNumber(this, tagHdr); int length = ReadLength(this, limit, false); if (length >= 0) @@ -172,7 +179,7 @@ namespace Org.BouncyCastle.Asn1 // definite-length try { - return BuildObject(tag, tagNo, length); + return BuildObject(tagHdr, tagNo, length); } catch (ArgumentException e) { @@ -182,20 +189,15 @@ namespace Org.BouncyCastle.Asn1 // indefinite-length - if (0 == (tag & Asn1Tags.Constructed)) + if (0 == (tagHdr & Asn1Tags.Constructed)) throw new IOException("indefinite-length primitive encoding encountered"); IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit); Asn1StreamParser sp = new Asn1StreamParser(indIn, limit, tmpBuffers); - int tagClass = tag & Asn1Tags.Private; + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) - { - if ((tag & Asn1Tags.Application) != 0) - return new BerApplicationSpecificParser(tagNo, sp).ToAsn1Object(); - - return new BerTaggedObjectParser(true, tagNo, sp).ToAsn1Object(); - } + return sp.ReadTaggedObject(tagClass, tagNo, true); // TODO There are other tags that may be constructed (e.g. BitString) switch (tagNo) @@ -254,9 +256,9 @@ namespace Org.BouncyCastle.Asn1 get { return limit; } } - internal static int ReadTagNumber(Stream s, int tag) + internal static int ReadTagNumber(Stream s, int tagHdr) { - int tagNo = tag & 0x1f; + int tagNo = tagHdr & 0x1f; // // with tagged object tag number is bottom 5 bits, or stored at the start of the content @@ -403,71 +405,69 @@ namespace Org.BouncyCastle.Asn1 return str; } - internal static Asn1Object CreatePrimitiveDerObject( - int tagNo, - DefiniteLengthInputStream defIn, - byte[][] tmpBuffers) + internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInputStream defIn, + byte[][] tmpBuffers) { switch (tagNo) { - case Asn1Tags.BmpString: - return new DerBmpString(GetBmpCharBuffer(defIn)); - case Asn1Tags.Boolean: - return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers)); - case Asn1Tags.Enumerated: - return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers)); - case Asn1Tags.ObjectIdentifier: - // TODO Ideally only clone if we used a buffer - return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true); + case Asn1Tags.BmpString: + return new DerBmpString(GetBmpCharBuffer(defIn)); + case Asn1Tags.Boolean: + return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers)); + case Asn1Tags.Enumerated: + return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers)); + case Asn1Tags.ObjectIdentifier: + // TODO Ideally only clone if we used a buffer + return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true); } byte[] bytes = defIn.ToArray(); switch (tagNo) { - case Asn1Tags.BitString: - return DerBitString.CreatePrimitive(bytes); - case Asn1Tags.GeneralizedTime: - return new DerGeneralizedTime(bytes); - case Asn1Tags.GeneralString: - return new DerGeneralString(bytes); - case Asn1Tags.GraphicString: - return DerGraphicString.CreatePrimitive(bytes); - case Asn1Tags.IA5String: - return new DerIA5String(bytes); - case Asn1Tags.Integer: - return new DerInteger(bytes, false); - case Asn1Tags.Null: - { - if (0 != bytes.Length) - throw new InvalidOperationException("malformed NULL encoding encountered"); + case Asn1Tags.BitString: + return DerBitString.CreatePrimitive(bytes); + case Asn1Tags.GeneralizedTime: + return new DerGeneralizedTime(bytes); + case Asn1Tags.GeneralString: + return new DerGeneralString(bytes); + case Asn1Tags.GraphicString: + return DerGraphicString.CreatePrimitive(bytes); + case Asn1Tags.IA5String: + return new DerIA5String(bytes); + case Asn1Tags.Integer: + return new DerInteger(bytes, false); + case Asn1Tags.Null: + { + if (0 != bytes.Length) + throw new InvalidOperationException("malformed NULL encoding encountered"); - return DerNull.Instance; - } - case Asn1Tags.NumericString: - return new DerNumericString(bytes); - case Asn1Tags.ObjectDescriptor: - return Asn1ObjectDescriptor.CreatePrimitive(bytes); - case Asn1Tags.OctetString: - return new DerOctetString(bytes); - case Asn1Tags.PrintableString: - return new DerPrintableString(bytes); - case Asn1Tags.RelativeOid: - return Asn1RelativeOid.CreatePrimitive(bytes, false); - case Asn1Tags.T61String: - return new DerT61String(bytes); - case Asn1Tags.UniversalString: - return new DerUniversalString(bytes); - case Asn1Tags.UtcTime: - return new DerUtcTime(bytes); - case Asn1Tags.Utf8String: - return new DerUtf8String(bytes); - case Asn1Tags.VideotexString: - return new DerVideotexString(bytes); - case Asn1Tags.VisibleString: - return new DerVisibleString(bytes); - default: - throw new IOException("unknown tag " + tagNo + " encountered"); + return DerNull.Instance; + } + case Asn1Tags.NumericString: + return new DerNumericString(bytes); + case Asn1Tags.ObjectDescriptor: + return Asn1ObjectDescriptor.CreatePrimitive(bytes); + case Asn1Tags.OctetString: + return new DerOctetString(bytes); + case Asn1Tags.PrintableString: + return new DerPrintableString(bytes); + case Asn1Tags.RelativeOid: + return Asn1RelativeOid.CreatePrimitive(bytes, false); + case Asn1Tags.T61String: + return new DerT61String(bytes); + case Asn1Tags.UniversalString: + return new DerUniversalString(bytes); + case Asn1Tags.UtcTime: + return new DerUtcTime(bytes); + case Asn1Tags.Utf8String: + return new DerUtf8String(bytes); + case Asn1Tags.VideotexString: + return new DerVideotexString(bytes); + case Asn1Tags.VisibleString: + return new DerVisibleString(bytes); + default: + throw new IOException("unknown tag " + tagNo + " encountered"); } } } diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs index 354437a6a..55ad36d46 100644 --- a/crypto/src/asn1/BERTaggedObjectParser.cs +++ b/crypto/src/asn1/BERTaggedObjectParser.cs @@ -1,66 +1,58 @@ using System; using System.IO; -using Org.BouncyCastle.Utilities; - namespace Org.BouncyCastle.Asn1 { public class BerTaggedObjectParser : Asn1TaggedObjectParser { - private bool _constructed; - private int _tagNumber; - private Asn1StreamParser _parser; - - [Obsolete] - internal BerTaggedObjectParser( - int baseTag, - int tagNumber, - Stream contentStream) - : this((baseTag & Asn1Tags.Constructed) != 0, tagNumber, new Asn1StreamParser(contentStream)) - { - } + private readonly int m_tagClass; + private readonly int m_tagNo; + private readonly bool m_constructed; + private readonly Asn1StreamParser m_parser; - internal BerTaggedObjectParser( - bool constructed, - int tagNumber, - Asn1StreamParser parser) + internal BerTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser) { - _constructed = constructed; - _tagNumber = tagNumber; - _parser = parser; + this.m_tagClass = tagClass; + this.m_tagNo = tagNo; + this.m_constructed = constructed; + this.m_parser = parser; } public bool IsConstructed { - get { return _constructed; } + get { return m_constructed; } } + public int TagClass + { + get { return m_tagClass; } + } + public int TagNo { - get { return _tagNumber; } + get { return m_tagNo; } } - public IAsn1Convertible GetObjectParser( - int tag, - bool isExplicit) + public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit) { - if (isExplicit) - { - if (!_constructed) - throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + if (Asn1Tags.ContextSpecific != TagClass) + throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags"); - return _parser.ReadObject(); - } + if (!declaredExplicit) + return m_parser.ReadImplicit(m_constructed, baseTagNo); + + if (!m_constructed) + throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); - return _parser.ReadImplicit(_constructed, tag); + return m_parser.ReadObject(); } public Asn1Object ToAsn1Object() { try { - return _parser.ReadTaggedObject(_constructed, _tagNumber); + return m_parser.ReadTaggedObject(TagClass, TagNo, IsConstructed); } catch (IOException e) { |