From fe0abc8e4f4eb407a7fff3e5d567a51ce49a4baf Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 20 Nov 2021 18:44:07 +0700 Subject: ASN.1: Tagged object parser improvements - add DLTaggedObjectParser - refactoring of Asn1InputStream/Asn1StreamParser --- crypto/BouncyCastle.Android.csproj | 1 + crypto/BouncyCastle.csproj | 1 + crypto/BouncyCastle.iOS.csproj | 1 + crypto/crypto.csproj | 5 + crypto/src/asn1/ASN1StreamParser.cs | 289 +++++++++++++----------- crypto/src/asn1/ASN1TaggedObjectParser.cs | 9 + crypto/src/asn1/Asn1InputStream.cs | 25 +- crypto/src/asn1/Asn1TaggedObject.cs | 56 ++--- crypto/src/asn1/Asn1Tags.cs | 2 + crypto/src/asn1/Asn1Utilities.cs | 36 +++ crypto/src/asn1/BERTaggedObjectParser.cs | 58 +++-- crypto/src/asn1/BerApplicationSpecificParser.cs | 6 +- crypto/src/asn1/DLTaggedObjectParser.cs | 81 +++++++ crypto/src/asn1/DerApplicationSpecific.cs | 7 +- crypto/src/asn1/cms/ContentInfoParser.cs | 24 +- crypto/test/src/asn1/test/InputStreamTest.cs | 2 +- 16 files changed, 390 insertions(+), 213 deletions(-) create mode 100644 crypto/src/asn1/DLTaggedObjectParser.cs diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index cdc4843bd..198ec093e 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -144,6 +144,7 @@ + diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index bbcfd9324..849dd3202 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -138,6 +138,7 @@ + diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 415992207..456a15b19 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -139,6 +139,7 @@ + diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 72fcfe5b9..a588a961a 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -578,6 +578,11 @@ SubType = "Code" BuildAction = "Compile" /> + CONSTRUCTED - - // TODO There are other tags that may be constructed (e.g. BIT_STRING) - 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" + tagNo.ToString("X")); - } - } - - internal IAsn1Convertible ReadImplicit(bool constructed, int tagNo) - { - if (_in is IndefiniteLengthInputStream) - { - if (!constructed) - throw new IOException("indefinite-length primitive encoding encountered"); - - return ReadIndef(tagNo); - } - - if (constructed) - { - switch (tagNo) - { - case Asn1Tags.Set: - return new DerSetParser(this); - case Asn1Tags.Sequence: - return new DerSequenceParser(this); - case Asn1Tags.OctetString: - return new BerOctetStringParser(this); - } - } - else - { - 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); - } - } - - throw new Asn1Exception("implicit tagging not implemented"); - } - - internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed) - { - if (!constructed) - { - // Note: !CONSTRUCTED => IMPLICIT - byte[] contentsOctets = ((DefiniteLengthInputStream)_in).ToArray(); - return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets); - } - - bool isIL = (_in is IndefiniteLengthInputStream); - Asn1EncodableVector contentsElements = ReadVector(); - return Asn1TaggedObject.CreateConstructed(tagClass, tagNo, isIL, contentsElements); - } - public virtual IAsn1Convertible ReadObject() { int tagHdr = _in.ReadByte(); - if (tagHdr == -1) + if (tagHdr < 0) return null; - // turn off looking for "00" while we resolve the tag - Set00Check(false); + return ImplParseObject(tagHdr); + } + + internal IAsn1Convertible ImplParseObject(int tagHdr) + { + // turn off looking for "00" while we resolve the tag + Set00Check(false); // // calculate tag number // int tagNo = Asn1InputStream.ReadTagNumber(_in, tagHdr); - 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.BitString || tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || + tagNo == Asn1Tags.Set || tagNo == Asn1Tags.External); if (length < 0) // indefinite-length method { - if (!isConstructed) - throw new IOException("indefinite-length primitive encoding encountered"); + if (0 == (tagHdr & Asn1Tags.Constructed)) + throw new IOException("indefinite-length primitive encoding encountered"); IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); Asn1StreamParser sp = new Asn1StreamParser(indIn, _limit, tmpBuffers); @@ -144,86 +75,184 @@ namespace Org.BouncyCastle.Asn1 if (Asn1Tags.Application == tagClass) return new BerApplicationSpecificParser(tagNo, sp); - return new BerTaggedObjectParser(tagClass, tagNo, true, sp); + return new BerTaggedObjectParser(tagClass, tagNo, sp); } - return sp.ReadIndef(tagNo); + return sp.ParseImplicitConstructedIL(tagNo); } else { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit); + if (0 == (tagHdr & Asn1Tags.Flags)) + return ParseImplicitPrimitive(tagNo, defIn); + + Asn1StreamParser sp = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers); + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) { - Asn1StreamParser sub = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers); + bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0; + // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecific types removed. if (Asn1Tags.Application == tagClass) - return (DLApplicationSpecific)sub.ReadTaggedObject(tagClass, tagNo, isConstructed); - - return new BerTaggedObjectParser(tagClass, tagNo, isConstructed, sub); - } - - if (!isConstructed) - { - // Some primitive encodings can be handled by parsers too... - switch (tagNo) { - case Asn1Tags.OctetString: - return new DerOctetStringParser(defIn); + // This cast is ensuring the current user-expected return type. + return (DLApplicationSpecific)sp.LoadTaggedDL(tagClass, tagNo, isConstructed); } - try - { - return Asn1InputStream.CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); - } - catch (ArgumentException e) - { - throw new Asn1Exception("corrupted stream detected", e); - } + return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp); } - Asn1StreamParser sp = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers); - - // TODO There are other tags that may be constructed (e.g. BitString) - switch (tagNo) - { - 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"); - } + return sp.ParseImplicitConstructedDL(tagNo); } } - private void Set00Check( - bool enabled) - { - if (_in is IndefiniteLengthInputStream) - { - ((IndefiniteLengthInputStream) _in).SetEofOn00(enabled); - } - } + internal Asn1Object LoadTaggedDL(int tagClass, int tagNo, bool constructed) + { + if (!constructed) + { + byte[] contentsOctets = ((DefiniteLengthInputStream)_in).ToArray(); + return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets); + } + + Asn1EncodableVector contentsElements = ReadVector(); + return Asn1TaggedObject.CreateConstructedDL(tagClass, tagNo, contentsElements); + } + + internal Asn1Object LoadTaggedIL(int tagClass, int tagNo) + { + Asn1EncodableVector contentsElements = ReadVector(); + return Asn1TaggedObject.CreateConstructedIL(tagClass, tagNo, contentsElements); + } + + internal IAsn1Convertible ParseImplicitConstructedDL(int univTagNo) + { + switch (univTagNo) + { + // TODO[asn1] DLConstructedBitStringParser + //case Asn1Tags.BitString: + // return new BerBitStringParser(this); + case Asn1Tags.External: + return new DerExternalParser(this); + case Asn1Tags.OctetString: + // TODO[asn1] DLConstructedOctetStringParser + return new BerOctetStringParser(this); + case Asn1Tags.Set: + return new DerSetParser(this); + case Asn1Tags.Sequence: + return new DerSequenceParser(this); + default: + throw new Asn1Exception("unknown DL object encountered: 0x" + univTagNo.ToString("X")); + } + } + + internal IAsn1Convertible ParseImplicitConstructedIL(int univTagNo) + { + switch (univTagNo) + { + // TODO[asn1] BerBitStringParser + //case Asn1Tags.BitString: + // return new BerBitStringParser(this); + case Asn1Tags.External: + // TODO[asn1] BERExternalParser + 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" + univTagNo.ToString("X")); + } + } + + internal IAsn1Convertible ParseImplicitPrimitive(int univTagNo) + { + return ParseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in); + } + internal IAsn1Convertible ParseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn) + { + // Some primitive encodings can be handled by parsers too... + switch (univTagNo) + { + // TODO[asn1] DLBitStringParser + //case Asn1Tags.BitString: + // return new DLBitStringParser(defIn); + case Asn1Tags.External: + throw new Asn1Exception("externals must use constructed encoding (see X.690 8.18)"); + case Asn1Tags.OctetString: + return new DerOctetStringParser(defIn); + 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)"); + } + + try + { + return Asn1InputStream.CreatePrimitiveDerObject(univTagNo, defIn, tmpBuffers); + } + catch (ArgumentException e) + { + throw new Asn1Exception("corrupted stream detected", e); + } + } + + internal IAsn1Convertible ParseObject(int univTagNo) + { + if (univTagNo < 0 || univTagNo > 30) + throw new ArgumentException("invalid universal tag number: " + univTagNo, "univTagNo"); + + int tagHdr = _in.ReadByte(); + if (tagHdr < 0) + return null; + + if ((tagHdr & ~Asn1Tags.Constructed) != univTagNo) + throw new IOException("unexpected identifier encountered: " + tagHdr); + + return ImplParseObject(tagHdr); + } + + internal Asn1TaggedObjectParser ParseTaggedObject() + { + int tagHdr = _in.ReadByte(); + if (tagHdr< 0) + return null; + + int tagClass = tagHdr & Asn1Tags.Private; + if (0 == tagClass) + throw new Asn1Exception("no tagged object found"); + + return (Asn1TaggedObjectParser)ImplParseObject(tagHdr); + } + + // TODO[asn1] Prefer 'LoadVector' internal Asn1EncodableVector ReadVector() { - IAsn1Convertible obj = ReadObject(); - if (null == obj) + int tagHdr = _in.ReadByte(); + if (tagHdr < 0) return new Asn1EncodableVector(0); Asn1EncodableVector v = new Asn1EncodableVector(); do { + IAsn1Convertible obj = ImplParseObject(tagHdr); + v.Add(obj.ToAsn1Object()); } - while ((obj = ReadObject()) != null); + while ((tagHdr = _in.ReadByte()) >= 0); return v; } + + private void Set00Check(bool enabled) + { + if (_in is IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).SetEofOn00(enabled); + } + } } } diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs index d125ddc1a..68b83f290 100644 --- a/crypto/src/asn1/ASN1TaggedObjectParser.cs +++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs @@ -21,6 +21,15 @@ namespace Org.BouncyCastle.Asn1 /// IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo); + /// Needed for open types, until we have better type-guided parsing support. + /// + /// Use sparingly for other purposes, and prefer or + /// where possible. Before using, check for matching tag + /// class and number. + /// + /// + IAsn1Convertible ParseExplicitBaseObject(); + /// Asn1TaggedObjectParser ParseExplicitBaseTagged(); diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs index d03c7a6b5..8ddfc022b 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs @@ -75,16 +75,19 @@ namespace Org.BouncyCastle.Asn1 */ private Asn1Object BuildObject(int tagHdr, int tagNo, int length) { - bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0; + // TODO[asn1] Special-case zero length first? DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit); + if (0 == (tagHdr & Asn1Tags.Flags)) + return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); + int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) - return ReadTaggedObject(tagClass, tagNo, isConstructed, defIn); - - if (!isConstructed) - return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); + { + bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0; + return ReadTaggedObjectDL(tagClass, tagNo, isConstructed, defIn); + } switch (tagNo) { @@ -103,7 +106,7 @@ namespace Org.BouncyCastle.Asn1 } } - internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn) + internal Asn1Object ReadTaggedObjectDL(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn) { if (!constructed) { @@ -111,9 +114,8 @@ namespace Org.BouncyCastle.Asn1 return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets); } - bool isIL = false; Asn1EncodableVector contentsElements = ReadVector(defIn); - return Asn1TaggedObject.CreateConstructed(tagClass, tagNo, isIL, contentsElements); + return Asn1TaggedObject.CreateConstructedDL(tagClass, tagNo, contentsElements); } internal virtual Asn1EncodableVector ReadVector() @@ -187,11 +189,13 @@ namespace Org.BouncyCastle.Asn1 int tagClass = tagHdr & Asn1Tags.Private; if (0 != tagClass) - return sp.ReadTaggedObject(tagClass, tagNo, true); + return sp.LoadTaggedIL(tagClass, tagNo); - // TODO There are other tags that may be constructed (e.g. BitString) switch (tagNo) { + // TODO[asn1] BerBitStringParser + //case Asn1Tags.BitString: + // return BerBitStringParser.Parse(sp); case Asn1Tags.OctetString: return BerOctetStringParser.Parse(sp); case Asn1Tags.Sequence: @@ -199,6 +203,7 @@ namespace Org.BouncyCastle.Asn1 case Asn1Tags.Set: return BerSetParser.Parse(sp); case Asn1Tags.External: + // TODO[asn1] BerExternalParser return DerExternalParser.Parse(sp); default: throw new IOException("unknown BER object encountered"); diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs index c7c0db6fc..ce5e9e3f2 100644 --- a/crypto/src/asn1/Asn1TaggedObject.cs +++ b/crypto/src/asn1/Asn1TaggedObject.cs @@ -396,6 +396,11 @@ namespace Org.BouncyCastle.Asn1 return asn1Object; } + public IAsn1Convertible ParseExplicitBaseObject() + { + return GetExplicitBaseObject(); + } + public Asn1TaggedObjectParser ParseExplicitBaseTagged() { return GetExplicitBaseTagged(); @@ -417,38 +422,37 @@ namespace Org.BouncyCastle.Asn1 internal abstract Asn1TaggedObject ReplaceTag(int tagClass, int tagNo); - internal static Asn1Object CreateConstructed(int tagClass, int tagNo, bool isIL, - Asn1EncodableVector contentsElements) + internal static Asn1Object CreateConstructedDL(int tagClass, int tagNo, Asn1EncodableVector contentsElements) { bool maybeExplicit = (contentsElements.Count == 1); - if (isIL) - { - Asn1TaggedObject taggedObject = maybeExplicit - ? new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) - : new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements)); + Asn1TaggedObject taggedObject = maybeExplicit + ? new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) + : new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements)); - switch (tagClass) - { - case Asn1Tags.Application: - return new BerApplicationSpecific(taggedObject); - default: - return taggedObject; - } - } - else + switch (tagClass) { - Asn1TaggedObject taggedObject = maybeExplicit - ? new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) - : new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements)); + case Asn1Tags.Application: + return new DLApplicationSpecific(taggedObject); + default: + return taggedObject; + } + } - switch (tagClass) - { - case Asn1Tags.Application: - return new DLApplicationSpecific(taggedObject); - default: - return taggedObject; - } + internal static Asn1Object CreateConstructedIL(int tagClass, int tagNo, Asn1EncodableVector contentsElements) + { + bool maybeExplicit = (contentsElements.Count == 1); + + Asn1TaggedObject taggedObject = maybeExplicit + ? new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) + : new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements)); + + switch (tagClass) + { + case Asn1Tags.Application: + return new BerApplicationSpecific(taggedObject); + default: + return taggedObject; } } diff --git a/crypto/src/asn1/Asn1Tags.cs b/crypto/src/asn1/Asn1Tags.cs index 692acdf50..670fb7d96 100644 --- a/crypto/src/asn1/Asn1Tags.cs +++ b/crypto/src/asn1/Asn1Tags.cs @@ -45,5 +45,7 @@ namespace Org.BouncyCastle.Asn1 public const int Tagged = 0x80; public const int ContextSpecific = 0x80; public const int Private = 0xC0; + + public const int Flags = 0xE0; } } diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs index 8a6d931c0..5605d4c54 100644 --- a/crypto/src/asn1/Asn1Utilities.cs +++ b/crypto/src/asn1/Asn1Utilities.cs @@ -289,5 +289,41 @@ namespace Org.BouncyCastle.Asn1 return TryParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo); } + + + /* + * Wrappers for Asn1TaggedObjectParser.ParseExplicitBaseObject + */ + + /// + public static IAsn1Convertible ParseExplicitBaseObject(Asn1TaggedObjectParser taggedObjectParser, int tagClass, + int tagNo) + { + return CheckTag(taggedObjectParser, tagClass, tagNo).ParseExplicitBaseObject(); + } + + /// + public static IAsn1Convertible ParseExplicitContextBaseObject(Asn1TaggedObjectParser taggedObjectParser, + int tagNo) + { + return ParseExplicitBaseObject(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo); + } + + /// + public static IAsn1Convertible TryParseExplicitBaseObject(Asn1TaggedObjectParser taggedObjectParser, + int tagClass, int tagNo) + { + if (!taggedObjectParser.HasTag(tagClass, tagNo)) + return null; + + return taggedObjectParser.ParseExplicitBaseObject(); + } + + /// + public static IAsn1Convertible TryParseExplicitContextBaseObject(Asn1TaggedObjectParser taggedObjectParser, + int tagNo) + { + return TryParseExplicitBaseObject(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo); + } } } diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs index a54e3ce38..c87f6cf9a 100644 --- a/crypto/src/asn1/BERTaggedObjectParser.cs +++ b/crypto/src/asn1/BERTaggedObjectParser.cs @@ -3,25 +3,24 @@ using System.IO; namespace Org.BouncyCastle.Asn1 { + [Obsolete("Will be made non-public. Test for and use only Asn1TaggedObjectParser.")] public class BerTaggedObjectParser : Asn1TaggedObjectParser { - private readonly int m_tagClass; - private readonly int m_tagNo; - private readonly bool m_constructed; - private readonly Asn1StreamParser m_parser; + internal readonly int m_tagClass; + internal readonly int m_tagNo; + internal readonly Asn1StreamParser m_parser; - internal BerTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser) + internal BerTaggedObjectParser(int tagClass, int tagNo, Asn1StreamParser parser) { - this.m_tagClass = tagClass; - this.m_tagNo = tagNo; - this.m_constructed = constructed; - this.m_parser = parser; + m_tagClass = tagClass; + m_tagNo = tagNo; + m_parser = parser; } - public bool IsConstructed + public virtual bool IsConstructed { - get { return m_constructed; } + get { return true; } } public int TagClass @@ -53,40 +52,39 @@ namespace Org.BouncyCastle.Asn1 return ParseBaseUniversal(declaredExplicit, baseTagNo); } - public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo) + public virtual IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo) { if (declaredExplicit) - { - if (!m_constructed) - throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + return m_parser.ParseObject(baseTagNo); - // TODO[asn1] Alternate parser method specific to this case - return m_parser.ReadObject(); - } - - return m_parser.ReadImplicit(m_constructed, baseTagNo); + return m_parser.ParseImplicitConstructedIL(baseTagNo); } - public Asn1TaggedObjectParser ParseExplicitBaseTagged() + public virtual IAsn1Convertible ParseExplicitBaseObject() { - if (!m_constructed) - throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + return m_parser.ReadObject(); + } - // TODO[asn1] Alternate parser method specific to this case - return (Asn1TaggedObjectParser)m_parser.ReadObject(); + public virtual Asn1TaggedObjectParser ParseExplicitBaseTagged() + { + return m_parser.ParseTaggedObject(); } - public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) + public virtual Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) { - return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser); + // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecificParser types removed. + if (Asn1Tags.Application == baseTagClass) + return new BerApplicationSpecificParser(baseTagNo, m_parser); + + return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_parser); } - public Asn1Object ToAsn1Object() + public virtual Asn1Object ToAsn1Object() { try { - return m_parser.ReadTaggedObject(TagClass, TagNo, IsConstructed); - } + return m_parser.LoadTaggedIL(TagClass, TagNo); + } catch (IOException e) { throw new Asn1ParsingException(e.Message); diff --git a/crypto/src/asn1/BerApplicationSpecificParser.cs b/crypto/src/asn1/BerApplicationSpecificParser.cs index 899e76ca5..75b7e7632 100644 --- a/crypto/src/asn1/BerApplicationSpecificParser.cs +++ b/crypto/src/asn1/BerApplicationSpecificParser.cs @@ -6,14 +6,14 @@ namespace Org.BouncyCastle.Asn1 : BerTaggedObjectParser, IAsn1ApplicationSpecificParser { internal BerApplicationSpecificParser(int tagNo, Asn1StreamParser parser) - : base(Asn1Tags.Application, tagNo, true, parser) + : base(Asn1Tags.Application, tagNo, parser) { } public IAsn1Convertible ReadObject() { // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser - return ParseBaseUniversal(true, -1); - } + return ParseExplicitBaseObject(); + } } } diff --git a/crypto/src/asn1/DLTaggedObjectParser.cs b/crypto/src/asn1/DLTaggedObjectParser.cs new file mode 100644 index 000000000..307a6342a --- /dev/null +++ b/crypto/src/asn1/DLTaggedObjectParser.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Parser for definite-length tagged objects. + */ + internal class DLTaggedObjectParser + : BerTaggedObjectParser + { + private readonly bool m_constructed; + + internal DLTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser) + : base(tagClass, tagNo, parser) + { + m_constructed = constructed; + } + + public override bool IsConstructed + { + get { return m_constructed; } + } + + public override IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo) + { + if (declaredExplicit) + { + if (!m_constructed) + throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + + return m_parser.ParseObject(baseTagNo); + } + + return m_constructed + ? m_parser.ParseImplicitConstructedDL(baseTagNo) + : m_parser.ParseImplicitPrimitive(baseTagNo); + } + + public override IAsn1Convertible ParseExplicitBaseObject() + { + if (!m_constructed) + throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + + return m_parser.ReadObject(); + } + + public override Asn1TaggedObjectParser ParseExplicitBaseTagged() + { + if (!m_constructed) + throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + + return m_parser.ParseTaggedObject(); + } + + public override Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) + { + // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecificParser types removed. + if (Asn1Tags.Application == baseTagClass) + { + // This cast is ensuring the current user-expected return type. + return (DLApplicationSpecific)m_parser.LoadTaggedDL(baseTagClass, baseTagNo, m_constructed); + } + + return new DLTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser); + } + + public override Asn1Object ToAsn1Object() + { + try + { + return m_parser.LoadTaggedDL(TagClass, TagNo, m_constructed); + } + catch (IOException e) + { + throw new Asn1ParsingException(e.Message); + } + } + } +} + diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index 92443b917..d474034ed 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -126,6 +126,11 @@ namespace Org.BouncyCastle.Asn1 return m_taggedObject.ParseBaseUniversal(declaredExplicit, baseTagNo); } + public IAsn1Convertible ParseExplicitBaseObject() + { + return TaggedObject.ParseExplicitBaseObject(); + } + public Asn1TaggedObjectParser ParseExplicitBaseTagged() { return m_taggedObject.ParseExplicitBaseTagged(); @@ -160,7 +165,7 @@ namespace Org.BouncyCastle.Asn1 public IAsn1Convertible ReadObject() { // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser - return m_taggedObject.GetBaseObject(); + return ParseExplicitBaseObject(); } public int TagClass diff --git a/crypto/src/asn1/cms/ContentInfoParser.cs b/crypto/src/asn1/cms/ContentInfoParser.cs index 541cc0f59..750d8ba7a 100644 --- a/crypto/src/asn1/cms/ContentInfoParser.cs +++ b/crypto/src/asn1/cms/ContentInfoParser.cs @@ -13,28 +13,28 @@ namespace Org.BouncyCastle.Asn1.Cms */ public class ContentInfoParser { - private DerObjectIdentifier contentType; - private Asn1TaggedObjectParser content; + private readonly DerObjectIdentifier m_contentType; + private readonly Asn1TaggedObjectParser m_content; - public ContentInfoParser( - Asn1SequenceParser seq) + public ContentInfoParser(Asn1SequenceParser seq) { - contentType = (DerObjectIdentifier)seq.ReadObject(); - content = (Asn1TaggedObjectParser)seq.ReadObject(); + m_contentType = (DerObjectIdentifier)seq.ReadObject(); + m_content = (Asn1TaggedObjectParser)seq.ReadObject(); } public DerObjectIdentifier ContentType { - get { return contentType; } + get { return m_contentType; } } - public IAsn1Convertible GetContent( - int tag) + public IAsn1Convertible GetContent(int tag) { - if (content == null) + if (null == m_content) return null; - return content.GetObjectParser(tag, true); - } + // TODO[cms] Ideally we could enforce the claimed tag + //return Asn1Utilities.ParseContextBaseUniversal(content, 0, true, tag); + return Asn1Utilities.ParseExplicitContextBaseObject(m_content, 0); + } } } diff --git a/crypto/test/src/asn1/test/InputStreamTest.cs b/crypto/test/src/asn1/test/InputStreamTest.cs index ab5200905..8f29b41ac 100644 --- a/crypto/test/src/asn1/test/InputStreamTest.cs +++ b/crypto/test/src/asn1/test/InputStreamTest.cs @@ -77,7 +77,7 @@ namespace Org.BouncyCastle.Asn1.Tests // TODO Test data has length issues too; needs to be reworked //DoTestWithByteArray(classCast1, "unknown object encountered: Org.BouncyCastle.Asn1.DerApplicationSpecific"); - DoTestWithByteArray(classCast2, "unknown object encountered: Org.BouncyCastle.Asn1.BerTaggedObjectParser"); + DoTestWithByteArray(classCast2, "unknown object encountered: Org.BouncyCastle.Asn1.DLTaggedObjectParser"); DoTestWithByteArray(classCast3, "unknown object encountered in constructed OCTET STRING: Org.BouncyCastle.Asn1.DLTaggedObject"); // TODO Error dependent on parser choices; needs to be reworked -- cgit 1.4.1