From 1ec8938ad4d5810c3f50957b85c7fe9a1e6a00b7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 18 Nov 2021 16:31:12 +0700 Subject: ASN:1 tagged object parser updates from bc-java --- crypto/BouncyCastle.Android.csproj | 1 + crypto/BouncyCastle.csproj | 1 + crypto/BouncyCastle.iOS.csproj | 1 + crypto/crypto.csproj | 5 + crypto/src/asn1/ASN1OctetStringParser.cs | 5 +- crypto/src/asn1/ASN1TaggedObjectParser.cs | 22 +++- crypto/src/asn1/Asn1BitStringParser.cs | 32 ++++++ crypto/src/asn1/Asn1TaggedObject.cs | 39 +++++-- crypto/src/asn1/Asn1Utilities.cs | 133 +++++++++++++++++++++- crypto/src/asn1/BERTaggedObjectParser.cs | 49 ++++++-- crypto/src/asn1/BerApplicationSpecificParser.cs | 20 +--- crypto/src/asn1/DerApplicationSpecific.cs | 48 +++++++- crypto/src/asn1/DerBitString.cs | 23 +++- crypto/src/asn1/IAsn1ApplicationSpecificParser.cs | 5 +- 14 files changed, 337 insertions(+), 47 deletions(-) create mode 100644 crypto/src/asn1/Asn1BitStringParser.cs diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index c13fd13aa..cdc4843bd 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -63,6 +63,7 @@ + diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 9745dca60..bbcfd9324 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -57,6 +57,7 @@ + diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index f718b9238..415992207 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -58,6 +58,7 @@ + diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 69a44f9dc..72fcfe5b9 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -143,6 +143,11 @@ SubType = "Code" BuildAction = "Compile" /> + Return the content of the OCTET STRING as a . + /// A represnting the OCTET STRING's content. + Stream GetOctetStream(); } } diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs index dd031ad07..d125ddc1a 100644 --- a/crypto/src/asn1/ASN1TaggedObjectParser.cs +++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs @@ -1,3 +1,6 @@ +using System; +using System.IO; + namespace Org.BouncyCastle.Asn1 { public interface Asn1TaggedObjectParser @@ -7,6 +10,21 @@ namespace Org.BouncyCastle.Asn1 int TagNo { get; } - IAsn1Convertible GetObjectParser(int tag, bool isExplicit); - } + bool HasContextTag(int tagNo); + + bool HasTag(int tagClass, int tagNo); + + /// + [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")] + IAsn1Convertible GetObjectParser(int tag, bool isExplicit); + + /// + IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo); + + /// + Asn1TaggedObjectParser ParseExplicitBaseTagged(); + + /// + Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo); + } } diff --git a/crypto/src/asn1/Asn1BitStringParser.cs b/crypto/src/asn1/Asn1BitStringParser.cs new file mode 100644 index 000000000..76af06d73 --- /dev/null +++ b/crypto/src/asn1/Asn1BitStringParser.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public interface Asn1BitStringParser + : IAsn1Convertible + { + /// Return a representing the contents of the BIT STRING. The final byte, if any, + /// may include pad bits. See . + /// A with its source as the BIT STRING content. + /// + Stream GetBitStream(); + + /// Return a representing the contents of the BIT STRING, where the content is + /// expected to be octet-aligned (this will be automatically checked during parsing). + /// A with its source as the BIT STRING content. + /// + Stream GetOctetStream(); + + /// Return the number of pad bits, if any, in the final byte, if any, read from + /// . + /// + /// This number is in the range zero to seven. That number of the least significant bits of the final byte, if + /// any, are not part of the contents and should be ignored. NOTE: Must be called AFTER the stream has been + /// fully processed. (Does not need to be called if was used instead of + /// . + /// + /// The number of pad bits. In the range zero to seven. + int PadBits { get; } + } +} diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs index 66835ac14..c7c0db6fc 100644 --- a/crypto/src/asn1/Asn1TaggedObject.cs +++ b/crypto/src/asn1/Asn1TaggedObject.cs @@ -368,28 +368,43 @@ namespace Org.BouncyCastle.Asn1 * the type of the passed in tag. If the object doesn't have a parser * associated with it, the base object is returned. */ + [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")] public IAsn1Convertible GetObjectParser(int tag, bool isExplicit) { if (Asn1Tags.ContextSpecific != TagClass) throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); - switch (tag) - { - //case Asn1Tags.BitString: - // return Asn1BitString.GetInstance(this, isExplicit).Parser; + return ParseBaseUniversal(isExplicit, tag); + } + + public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo) + { + Asn1Object asn1Object = GetBaseUniversal(declaredExplicit, baseTagNo); + + switch (baseTagNo) + { + case Asn1Tags.BitString: + return ((DerBitString)asn1Object).Parser; case Asn1Tags.OctetString: - return Asn1OctetString.GetInstance(this, isExplicit).Parser; + return ((Asn1OctetString)asn1Object).Parser; case Asn1Tags.Sequence: - return Asn1Sequence.GetInstance(this, isExplicit).Parser; + return ((Asn1Sequence)asn1Object).Parser; case Asn1Tags.Set: - return Asn1Set.GetInstance(this, isExplicit).Parser; - } + return ((Asn1Set)asn1Object).Parser; + } - if (isExplicit) - return obj.ToAsn1Object(); + return asn1Object; + } - throw Platform.CreateNotImplementedException("implicit tagging for tag: " + tag); - } + public Asn1TaggedObjectParser ParseExplicitBaseTagged() + { + return GetExplicitBaseTagged(); + } + + public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) + { + return GetImplicitBaseTagged(baseTagClass, baseTagNo); + } public override string ToString() { diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs index 7b014ef00..8a6d931c0 100644 --- a/crypto/src/asn1/Asn1Utilities.cs +++ b/crypto/src/asn1/Asn1Utilities.cs @@ -1,4 +1,5 @@ using System; +using System.IO; namespace Org.BouncyCastle.Asn1 { @@ -15,6 +16,18 @@ namespace Org.BouncyCastle.Asn1 return taggedObject; } + internal static Asn1TaggedObjectParser CheckTag(Asn1TaggedObjectParser taggedObjectParser, int tagClass, + int tagNo) + { + if (!taggedObjectParser.HasTag(tagClass, tagNo)) + { + string expected = GetTagText(tagClass, tagNo); + string found = GetTagText(taggedObjectParser); + throw new InvalidOperationException("Expected " + expected + " tag but found " + found); + } + return taggedObjectParser; + } + internal static string GetTagText(Asn1Tag tag) { @@ -26,6 +39,11 @@ namespace Org.BouncyCastle.Asn1 return GetTagText(taggedObject.TagClass, taggedObject.TagNo); } + public static string GetTagText(Asn1TaggedObjectParser taggedObjectParser) + { + return GetTagText(taggedObjectParser.TagClass, taggedObjectParser.TagNo); + } + public static string GetTagText(int tagClass, int tagNo) { switch (tagClass) @@ -118,9 +136,7 @@ namespace Org.BouncyCastle.Asn1 int baseTagClass, int baseTagNo) { if (!taggedObject.HasTag(tagClass, tagNo)) - { return null; - } return taggedObject.GetImplicitBaseTagged(baseTagClass, baseTagNo); } @@ -152,9 +168,7 @@ namespace Org.BouncyCastle.Asn1 bool declaredExplicit, int baseTagNo) { if (!taggedObject.HasTag(tagClass, tagNo)) - { return null; - } return taggedObject.GetBaseUniversal(declaredExplicit, baseTagNo); } @@ -164,5 +178,116 @@ namespace Org.BouncyCastle.Asn1 { return TryGetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo); } + + + /* + * Wrappers for Asn1TaggedObjectParser.ParseExplicitBaseTagged + */ + + /// + public static Asn1TaggedObjectParser ParseExplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagClass, int tagNo) + { + return CheckTag(taggedObjectParser, tagClass, tagNo).ParseExplicitBaseTagged(); + } + + /// + public static Asn1TaggedObjectParser ParseExplicitContextBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagNo) + { + return ParseExplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo); + } + + /// + public static Asn1TaggedObjectParser TryParseExplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagClass, int tagNo) + { + if (!taggedObjectParser.HasTag(tagClass, tagNo)) + return null; + + return taggedObjectParser.ParseExplicitBaseTagged(); + } + + /// + public static Asn1TaggedObjectParser TryParseExplicitContextBaseTagged( + Asn1TaggedObjectParser taggedObjectParser, int tagNo) + { + return TryParseExplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo); + } + + + /* + * Wrappers for Asn1TaggedObjectParser.ParseImplicitBaseTagged + */ + + /// + public static Asn1TaggedObjectParser ParseImplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagClass, int tagNo, int baseTagClass, int baseTagNo) + { + return CheckTag(taggedObjectParser, tagClass, tagNo).ParseImplicitBaseTagged(baseTagClass, baseTagNo); + } + + /// + public static Asn1TaggedObjectParser ParseImplicitContextBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagNo, int baseTagClass, int baseTagNo) + { + return ParseImplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, baseTagClass, + baseTagNo); + } + + /// + public static Asn1TaggedObjectParser TryParseImplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser, + int tagClass, int tagNo, int baseTagClass, int baseTagNo) + { + if (!taggedObjectParser.HasTag(tagClass, tagNo)) + return null; + + return taggedObjectParser.ParseImplicitBaseTagged(baseTagClass, baseTagNo); + } + + /// + public static Asn1TaggedObjectParser TryParseImplicitContextBaseTagged( + Asn1TaggedObjectParser taggedObjectParser, int tagNo, int baseTagClass, int baseTagNo) + { + return TryParseImplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, baseTagClass, + baseTagNo); + } + + + /* + * Wrappers for Asn1TaggedObjectParser.ParseBaseUniversal + */ + + /// + public static IAsn1Convertible ParseBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagClass, + int tagNo, bool declaredExplicit, int baseTagNo) + { + return CheckTag(taggedObjectParser, tagClass, tagNo).ParseBaseUniversal(declaredExplicit, baseTagNo); + } + + /// + public static IAsn1Convertible ParseContextBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagNo, + bool declaredExplicit, int baseTagNo) + { + return ParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo); + } + + /// + public static IAsn1Convertible TryParseBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagClass, + int tagNo, bool declaredExplicit, int baseTagNo) + { + if (!taggedObjectParser.HasTag(tagClass, tagNo)) + return null; + + return taggedObjectParser.ParseBaseUniversal(declaredExplicit, baseTagNo); + } + + /// + public static IAsn1Convertible TryParseContextBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, + int tagNo, bool declaredExplicit, int baseTagNo) + { + return TryParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, + baseTagNo); + } } } diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs index 55ad36d46..a54e3ce38 100644 --- a/crypto/src/asn1/BERTaggedObjectParser.cs +++ b/crypto/src/asn1/BERTaggedObjectParser.cs @@ -34,21 +34,54 @@ namespace Org.BouncyCastle.Asn1 get { return m_tagNo; } } - public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit) + public bool HasContextTag(int tagNo) + { + return m_tagClass == Asn1Tags.ContextSpecific && m_tagNo == tagNo; + } + + public bool HasTag(int tagClass, int tagNo) + { + return m_tagClass == tagClass && m_tagNo == tagNo; + } + + [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")] + public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit) { if (Asn1Tags.ContextSpecific != TagClass) throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags"); - if (!declaredExplicit) - return m_parser.ReadImplicit(m_constructed, baseTagNo); + return ParseBaseUniversal(declaredExplicit, baseTagNo); + } - if (!m_constructed) - throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + public 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.ReadObject(); - } + // TODO[asn1] Alternate parser method specific to this case + return m_parser.ReadObject(); + } + + return m_parser.ReadImplicit(m_constructed, baseTagNo); + } + + public Asn1TaggedObjectParser ParseExplicitBaseTagged() + { + if (!m_constructed) + throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)"); + + // TODO[asn1] Alternate parser method specific to this case + return (Asn1TaggedObjectParser)m_parser.ReadObject(); + } + + public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) + { + return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser); + } - public Asn1Object ToAsn1Object() + public Asn1Object ToAsn1Object() { try { diff --git a/crypto/src/asn1/BerApplicationSpecificParser.cs b/crypto/src/asn1/BerApplicationSpecificParser.cs index 7d2c4b3e8..899e76ca5 100644 --- a/crypto/src/asn1/BerApplicationSpecificParser.cs +++ b/crypto/src/asn1/BerApplicationSpecificParser.cs @@ -3,27 +3,17 @@ using System; namespace Org.BouncyCastle.Asn1 { public class BerApplicationSpecificParser - : IAsn1ApplicationSpecificParser + : BerTaggedObjectParser, IAsn1ApplicationSpecificParser { - private readonly int tag; - private readonly Asn1StreamParser parser; - - internal BerApplicationSpecificParser( - int tag, - Asn1StreamParser parser) + internal BerApplicationSpecificParser(int tagNo, Asn1StreamParser parser) + : base(Asn1Tags.Application, tagNo, true, parser) { - this.tag = tag; - this.parser = parser; } public IAsn1Convertible ReadObject() { - return parser.ReadObject(); - } - - public Asn1Object ToAsn1Object() - { - return new BerApplicationSpecific(tag, parser.ReadVector()); + // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser + return ParseBaseUniversal(true, -1); } } } diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index 15a4cdcc2..92443b917 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1 * Base class for an application specific object */ public class DerApplicationSpecific - : Asn1Object + : Asn1Object, IAsn1ApplicationSpecificParser { public static DerApplicationSpecific GetInstance(object obj) { @@ -116,17 +116,63 @@ namespace Org.BouncyCastle.Asn1 return m_taggedObject.GetBaseUniversal(false, tagNo); } + public IAsn1Convertible GetObjectParser(int tag, bool isExplicit) + { + throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags"); + } + + public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo) + { + return m_taggedObject.ParseBaseUniversal(declaredExplicit, baseTagNo); + } + + public Asn1TaggedObjectParser ParseExplicitBaseTagged() + { + return m_taggedObject.ParseExplicitBaseTagged(); + } + + public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo) + { + return m_taggedObject.ParseImplicitBaseTagged(baseTagClass, baseTagNo); + } + public bool HasApplicationTag(int tagNo) { return m_taggedObject.HasTag(Asn1Tags.Application, tagNo); } + public bool HasContextTag(int tagNo) + { + return false; + } + + public bool HasTag(int tagClass, int tagNo) + { + return m_taggedObject.HasTag(tagClass, tagNo); + } + [Obsolete("Will be removed")] public bool IsConstructed() { return m_taggedObject.IsConstructed(); } + public IAsn1Convertible ReadObject() + { + // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser + return m_taggedObject.GetBaseObject(); + } + + public int TagClass + { + get { return m_taggedObject.TagClass; } + } + + public int TagNo + { + get { return m_taggedObject.TagNo; } + } + /** * DerApplicationSpecific uses an internal Asn1TaggedObject for the * implementation, and will soon be deprecated in favour of using diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs index 4596fbbc8..d3836b740 100644 --- a/crypto/src/asn1/DerBitString.cs +++ b/crypto/src/asn1/DerBitString.cs @@ -9,8 +9,8 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1 { public class DerBitString - : DerStringBase - { + : DerStringBase, Asn1BitStringParser + { internal class Meta : Asn1UniversalType { internal static readonly Asn1UniversalType Instance = new Meta(); @@ -309,6 +309,25 @@ namespace Org.BouncyCastle.Asn1 return thisLastDer == thatLastDer; } + public Stream GetBitStream() + { + return new MemoryStream(contents, 1, contents.Length - 1, false); + } + + public Stream GetOctetStream() + { + int padBits = contents[0] & 0xFF; + if (0 != padBits) + throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits); + + return GetBitStream(); + } + + public Asn1BitStringParser Parser + { + get { return this; } + } + public override string GetString() { byte[] str = GetDerEncoded(); diff --git a/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs b/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs index 89cf64c70..f8dec2791 100644 --- a/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs +++ b/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs @@ -2,9 +2,10 @@ using System; namespace Org.BouncyCastle.Asn1 { + [Obsolete("Test for Asn1TaggedObjectParser with TagClass of Asn1Tags.Application instead")] public interface IAsn1ApplicationSpecificParser - : IAsn1Convertible - { + : Asn1TaggedObjectParser + { IAsn1Convertible ReadObject(); } } -- cgit 1.4.1