summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-08 16:44:49 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-08 16:44:49 +0700
commit39a846768e82cd1439bcb165665593c9baae5ffb (patch)
tree42ae9856612671f01e78f0ef2c271c791776eaac
parentChange return type of GetExplicitBaseObject (diff)
downloadBouncyCastle.NET-ed25519-39a846768e82cd1439bcb165665593c9baae5ffb.tar.xz
More tagged object work from bc-java
-rw-r--r--crypto/src/asn1/Asn1TaggedObject.cs205
-rw-r--r--crypto/src/asn1/Asn1Utilities.cs69
-rw-r--r--crypto/src/asn1/BerTaggedObject.cs10
-rw-r--r--crypto/src/asn1/DerTaggedObject.cs10
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.
          * <p>
          * 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.
          * <p>
          * Note: tagged objects are generally context dependent if you're
@@ -142,10 +217,24 @@ 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
          * {@link #getBaseUniversal(boolean, int)} where possible. Before using, check
@@ -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);
+        }
     }
 }