From 39a846768e82cd1439bcb165665593c9baae5ffb Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 8 Nov 2021 16:44:49 +0700 Subject: More tagged object work from bc-java --- crypto/src/asn1/Asn1TaggedObject.cs | 205 ++++++++++++++++++++++++++++-------- crypto/src/asn1/Asn1Utilities.cs | 69 ++++++++++++ crypto/src/asn1/BerTaggedObject.cs | 10 ++ crypto/src/asn1/DerTaggedObject.cs | 10 ++ 4 files changed, 248 insertions(+), 46 deletions(-) diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs index b45c0f2d0..87a66ffe0 100644 --- a/crypto/src/asn1/Asn1TaggedObject.cs +++ b/crypto/src/asn1/Asn1TaggedObject.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -12,6 +13,7 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1TaggedObject : Asn1Object, Asn1TaggedObjectParser { + // TODO[asn1] Rewrite DerApplicationSpecific in terms of Asn1TaggedObject and remove this internal static bool IsConstructed(bool isExplicit, Asn1Object obj) { if (isExplicit || obj is Asn1Sequence || obj is Asn1Set) @@ -22,34 +24,51 @@ namespace Org.BouncyCastle.Asn1 return IsConstructed(tagged.IsExplicit(), tagged.GetObject()); } - internal readonly int tagNo; - internal readonly bool explicitly; - internal readonly Asn1Encodable obj; - - static public Asn1TaggedObject GetInstance( - Asn1TaggedObject obj, - bool explicitly) - { - if (explicitly) + public static Asn1TaggedObject GetInstance(object obj) + { + if (obj == null || obj is Asn1TaggedObject) + { + return (Asn1TaggedObject)obj; + } + //else if (obj is Asn1TaggedObjectParser) + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is Asn1TaggedObject) + return (Asn1TaggedObject)asn1Object; + } + else if (obj is byte[]) { - return GetInstance(obj.GetObject()); + try + { + return CheckedCast(FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct tagged object from byte[]: " + e.Message); + } } - throw new ArgumentException("implicitly tagged tagged object"); - } + throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); + } - static public Asn1TaggedObject GetInstance( - object obj) - { - if (obj == null || obj is Asn1TaggedObject) - { - return (Asn1TaggedObject) obj; - } + public static Asn1TaggedObject GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) + { + if (Asn1Tags.ContextSpecific != taggedObject.TagClass) + throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); - throw new ArgumentException("Unknown object in GetInstance: " + Platform.GetTypeName(obj), "obj"); - } + if (!declaredExplicit) + throw new ArgumentException("this method not valid for implicitly tagged tagged objects"); - /** + return taggedObject.GetExplicitBaseTagged(); + } + + internal readonly int tagClass = Asn1Tags.ContextSpecific; + internal readonly int tagNo; + internal readonly bool explicitly; + internal readonly Asn1Encodable obj; + + /** * @param tagNo the tag number for this object. * @param obj the tagged object. */ @@ -74,22 +93,22 @@ namespace Org.BouncyCastle.Asn1 this.obj = obj; } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - Asn1TaggedObject other = asn1Object as Asn1TaggedObject; - - if (other == null) - return false; + if (asn1Object is DerApplicationSpecific) + return asn1Object.CallAsn1Equals(this); - return this.tagNo == other.tagNo - && this.explicitly == other.explicitly // TODO Should this be part of equality? - && this.GetObject().Equals(other.GetObject()); + Asn1TaggedObject that = asn1Object as Asn1TaggedObject; + return null != that + && this.tagClass == that.tagClass + && this.tagNo == that.tagNo + && this.explicitly == that.explicitly // TODO Should this be part of equality? + && this.GetObject().Equals(that.GetObject()); } protected override int Asn1GetHashCode() { - int code = tagNo.GetHashCode(); + int code = (tagClass * 7919) ^ tagNo; // TODO: actually this is wrong - the problem is that a re-encoded // object may end up with a different hashCode due to implicit @@ -105,7 +124,7 @@ namespace Org.BouncyCastle.Asn1 public int TagClass { - get { return Asn1Tags.ContextSpecific; } + get { return tagClass; } } public int TagNo @@ -113,7 +132,17 @@ namespace Org.BouncyCastle.Asn1 get { return tagNo; } } - /** + public bool HasContextTag(int tagNo) + { + return this.tagClass == Asn1Tags.ContextSpecific && this.tagNo == tagNo; + } + + public bool HasTag(int tagClass, int tagNo) + { + return this.tagClass == tagClass && this.tagNo == tagNo; + } + + /** * return whether or not the object may be explicitly tagged. *

* Note: if the object has been read from an input stream, the only @@ -133,7 +162,53 @@ namespace Org.BouncyCastle.Asn1 return false; } - /** + /** + * Return the contents of this object as a byte[] + * + * @return the encoded contents of the object. + */ + // TODO Need this public if/when DerApplicationSpecific extends Asn1TaggedObject + internal byte[] GetContents() + { + try + { + byte[] baseEncoding = obj.GetEncoded(Asn1Encoding); + if (IsExplicit()) + return baseEncoding; + + MemoryStream input = new MemoryStream(baseEncoding, false); + int tag = input.ReadByte(); + Asn1InputStream.ReadTagNumber(input, tag); + int length = Asn1InputStream.ReadLength(input, (int)(input.Length - input.Position), false); + int remaining = (int)(input.Length - input.Position); + + // For indefinite form, account for end-of-contents octets + int contentsLength = length < 0 ? remaining - 2 : remaining; + if (contentsLength < 0) + throw new InvalidOperationException("failed to get contents"); + + byte[] contents = new byte[contentsLength]; + Array.Copy(baseEncoding, baseEncoding.Length - remaining, contents, 0, contentsLength); + return contents; + } + catch (IOException e) + { + throw new InvalidOperationException("failed to get contents", e); + } + } + + /** + * Return true if the object is marked as constructed, false otherwise. + * + * @return true if constructed, otherwise false. + */ + // TODO Need this public if/when DerApplicationSpecific extends Asn1TaggedObject + internal bool IsConstructed() + { + return EncodeConstructed(); + } + + /** * return whatever was following the tag. *

* Note: tagged objects are generally context dependent if you're @@ -142,9 +217,23 @@ namespace Org.BouncyCastle.Asn1 */ public Asn1Object GetObject() { + if (Asn1Tags.ContextSpecific != TagClass) + throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); + return obj.ToAsn1Object(); } + /** + * Needed for open types, until we have better type-guided parsing support. Use sparingly for other + * purposes, and prefer {@link #getExplicitBaseTagged()}, {@link #getImplicitBaseTagged(int, int)} or + * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check for matching tag + * {@link #getTagClass() class} and {@link #getTagNo() number}. + */ + public Asn1Encodable GetBaseObject() + { + return obj; + } + /** * Needed for open types, until we have better type-guided parsing support. Use * sparingly for other purposes, and prefer {@link #getExplicitBaseTagged()} or @@ -159,23 +248,34 @@ namespace Org.BouncyCastle.Asn1 return obj; } + public Asn1TaggedObject GetExplicitBaseTagged() + { + if (!IsExplicit()) + throw new InvalidOperationException("object implicit - explicit expected."); + + return CheckedCast(obj.ToAsn1Object()); + } + /** * Return the object held in this tagged object as a parser assuming it has * the type of the passed in tag. If the object doesn't have a parser * associated with it, the base object is returned. */ - public IAsn1Convertible GetObjectParser( - int tag, - bool isExplicit) + public IAsn1Convertible GetObjectParser(int tag, bool isExplicit) { - switch (tag) + if (Asn1Tags.ContextSpecific != TagClass) + throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); + + switch (tag) { - case Asn1Tags.Set: + //case Asn1Tags.BitString: + // return Asn1BitString.GetInstance(this, isExplicit).Parser; + case Asn1Tags.OctetString: + return Asn1OctetString.GetInstance(this, isExplicit).Parser; + case Asn1Tags.Sequence: + return Asn1Sequence.GetInstance(this, isExplicit).Parser; + case Asn1Tags.Set: return Asn1Set.GetInstance(this, isExplicit).Parser; - case Asn1Tags.Sequence: - return Asn1Sequence.GetInstance(this, isExplicit).Parser; - case Asn1Tags.OctetString: - return Asn1OctetString.GetInstance(this, isExplicit).Parser; } if (isExplicit) @@ -188,7 +288,20 @@ namespace Org.BouncyCastle.Asn1 public override string ToString() { - return "[" + tagNo + "]" + obj; + return Asn1Utilities.GetTagText(tagClass, tagNo) + obj; } - } + + internal abstract string Asn1Encoding { get; } + + internal abstract Asn1Sequence RebuildConstructed(Asn1Object asn1Object); + + private static Asn1TaggedObject CheckedCast(Asn1Object asn1Object) + { + Asn1TaggedObject taggedObject = asn1Object as Asn1TaggedObject; + if (null != taggedObject) + return taggedObject; + + throw new InvalidOperationException("unexpected object: " + Platform.GetTypeName(asn1Object)); + } + } } diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs index dca2253ec..fe6db2df7 100644 --- a/crypto/src/asn1/Asn1Utilities.cs +++ b/crypto/src/asn1/Asn1Utilities.cs @@ -23,5 +23,74 @@ namespace Org.BouncyCastle.Asn1 return "[UNIVERSAL " + tagNo + "]"; } } + + /* + * Wrappers for Asn1TaggedObject.GetExplicitBaseObject + */ + + public static Asn1Encodable GetExplicitBaseObject(Asn1TaggedObject taggedObject, int tagClass, int tagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + { + string expected = GetTagText(tagClass, tagNo); + string found = GetTagText(taggedObject); + throw new InvalidOperationException("Expected " + expected + " tag but found " + found); + } + + return taggedObject.GetExplicitBaseObject(); + } + + public static Asn1Encodable GetExplicitContextBaseObject(Asn1TaggedObject taggedObject, int tagNo) + { + return GetExplicitBaseObject(taggedObject, Asn1Tags.ContextSpecific, tagNo); + } + + public static Asn1Encodable TryGetExplicitBaseObject(Asn1TaggedObject taggedObject, int tagClass, int tagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + return null; + + return taggedObject.GetExplicitBaseObject(); + } + + public static Asn1Encodable TryGetExplicitContextBaseObject(Asn1TaggedObject taggedObject, int tagNo) + { + return TryGetExplicitBaseObject(taggedObject, Asn1Tags.ContextSpecific, tagNo); + } + + + /* + * Wrappers for Asn1TaggedObject.GetExplicitBaseTagged + */ + + public static Asn1TaggedObject GetExplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + { + string expected = GetTagText(tagClass, tagNo); + string found = GetTagText(taggedObject); + throw new InvalidOperationException("Expected " + expected + " tag but found " + found); + } + + return taggedObject.GetExplicitBaseTagged(); + } + + public static Asn1TaggedObject GetExplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo) + { + return GetExplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo); + } + + public static Asn1TaggedObject TryGetExplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + return null; + + return taggedObject.GetExplicitBaseTagged(); + } + + public static Asn1TaggedObject TryGetExplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo) + { + return TryGetExplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo); + } } } diff --git a/crypto/src/asn1/BerTaggedObject.cs b/crypto/src/asn1/BerTaggedObject.cs index 164936c6f..4bf27e12f 100644 --- a/crypto/src/asn1/BerTaggedObject.cs +++ b/crypto/src/asn1/BerTaggedObject.cs @@ -47,6 +47,11 @@ namespace Org.BouncyCastle.Asn1 { } + internal override string Asn1Encoding + { + get { return Ber; } + } + internal override bool EncodeConstructed() { throw Platform.CreateNotImplementedException("BerTaggedObject.EncodeConstructed"); @@ -117,5 +122,10 @@ namespace Org.BouncyCastle.Asn1 base.Encode(asn1Out, withID); } } + + internal override Asn1Sequence RebuildConstructed(Asn1Object asn1Object) + { + return new BerSequence(asn1Object); + } } } diff --git a/crypto/src/asn1/DerTaggedObject.cs b/crypto/src/asn1/DerTaggedObject.cs index 4800eaa04..125d30ff2 100644 --- a/crypto/src/asn1/DerTaggedObject.cs +++ b/crypto/src/asn1/DerTaggedObject.cs @@ -46,6 +46,11 @@ namespace Org.BouncyCastle.Asn1 { } + internal override string Asn1Encoding + { + get { return Der; } + } + internal override bool EncodeConstructed() { throw Platform.CreateNotImplementedException("DerTaggedObject.EncodeConstructed"); @@ -78,5 +83,10 @@ namespace Org.BouncyCastle.Asn1 asn1Out.Write(bytes, 1, bytes.Length - 1); } } + + internal override Asn1Sequence RebuildConstructed(Asn1Object asn1Object) + { + return new DerSequence(asn1Object); + } } } -- cgit 1.4.1