summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-03-05 14:45:49 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-03-05 14:45:49 +0700
commit6b59dbbece3b1509b83b8479259a3c6059e9a60b (patch)
tree3dca66ea9a1e8aa02ac6099cde5844155783bd7b
parentRefactor Asn1Set classes (diff)
downloadBouncyCastle.NET-ed25519-6b59dbbece3b1509b83b8479259a3c6059e9a60b.tar.xz
Support 'leaveOpen' in Asn1 streams
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs43
-rw-r--r--crypto/src/asn1/Asn1OutputStream.cs46
-rw-r--r--crypto/src/asn1/BEROctetStringGenerator.cs4
-rw-r--r--crypto/src/asn1/DerOutputStream.cs4
-rw-r--r--crypto/src/asn1/LazyASN1InputStream.cs19
-rw-r--r--crypto/src/asn1/util/Asn1Dump.cs10
-rw-r--r--crypto/src/cms/CMSUtils.cs25
-rw-r--r--crypto/src/tls/TlsUtilities.cs17
-rw-r--r--crypto/src/x509/X509CertPairParser.cs11
-rw-r--r--crypto/src/x509/X509CertificateParser.cs5
-rw-r--r--crypto/src/x509/X509CrlParser.cs11
-rw-r--r--crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs32
-rw-r--r--crypto/test/src/asn1/test/MiscTest.cs35
-rw-r--r--crypto/test/src/asn1/test/ParsingTest.cs43
-rw-r--r--crypto/test/src/cms/test/SignedDataTest.cs20
15 files changed, 205 insertions, 120 deletions
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 33db1e6d7..e250fe500 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -19,6 +19,7 @@ namespace Org.BouncyCastle.Asn1
         : FilterStream
     {
         private readonly int limit;
+        private readonly bool m_leaveOpen;
 
         internal byte[][] tmpBuffers;
 
@@ -36,11 +37,6 @@ namespace Org.BouncyCastle.Asn1
             return int.MaxValue;
         }
 
-        public Asn1InputStream(Stream input)
-            : this(input, FindLimit(input))
-        {
-        }
-
         /**
          * Create an ASN1InputStream based on the input byte array. The length of DER objects in
          * the stream is automatically limited to the length of the input array.
@@ -52,6 +48,11 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
+        public Asn1InputStream(Stream input)
+            : this(input, FindLimit(input))
+        {
+        }
+
         /**
          * Create an ASN1InputStream where no DER object will be longer than limit.
          *
@@ -59,22 +60,41 @@ namespace Org.BouncyCastle.Asn1
          * @param limit maximum size of a DER encoded object.
          */
         public Asn1InputStream(Stream input, int limit)
-            : this(input, limit, new byte[16][])
+            : this(input, limit, false)
+        {
+        }
+
+        public Asn1InputStream(Stream input, int limit, bool leaveOpen)
+            : this(input, limit, leaveOpen, new byte[16][])
         {
         }
 
-        internal Asn1InputStream(Stream input, int limit, byte[][] tmpBuffers)
+        internal Asn1InputStream(Stream input, int limit, bool leaveOpen, byte[][] tmpBuffers)
             : base(input)
         {
+            if (!input.CanRead)
+                throw new ArgumentException("Expected stream to be readable", nameof(input));
+
             this.limit = limit;
+            m_leaveOpen = leaveOpen;
             this.tmpBuffers = tmpBuffers;
         }
 
         protected override void Dispose(bool disposing)
         {
-            tmpBuffers = null;
+            if (disposing)
+            {
+                tmpBuffers = null;
+            }
 
-            base.Dispose(disposing);
+            if (m_leaveOpen)
+            {
+                base.Detach(disposing);
+            }
+            else
+            {
+                base.Dispose(disposing);
+            }
         }
 
         /**
@@ -146,7 +166,10 @@ namespace Org.BouncyCastle.Asn1
             if (remaining < 1)
                 return new Asn1EncodableVector(0);
 
-            return new Asn1InputStream(defIn, remaining, tmpBuffers).ReadVector();
+            using (var sub = new Asn1InputStream(defIn, remaining, leaveOpen: true, tmpBuffers))
+            {
+                return sub.ReadVector();
+            }
         }
 
         internal virtual Asn1Sequence CreateDLSequence(DefiniteLengthInputStream defIn)
diff --git a/crypto/src/asn1/Asn1OutputStream.cs b/crypto/src/asn1/Asn1OutputStream.cs
index 745674930..588c3733d 100644
--- a/crypto/src/asn1/Asn1OutputStream.cs
+++ b/crypto/src/asn1/Asn1OutputStream.cs
@@ -18,20 +18,56 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1OutputStream Create(Stream output)
         {
-            return new Asn1OutputStream(output);
+            return Create(output, Asn1Encodable.Ber);
         }
 
         public static Asn1OutputStream Create(Stream output, string encoding)
         {
+            return Create(output, encoding, false);
+        }
+
+        public static Asn1OutputStream Create(Stream output, string encoding, bool leaveOpen)
+        {
+            if (Asn1Encodable.Der.Equals(encoding))
+                return new DerOutputStream(output, leaveOpen);
+
+            return new Asn1OutputStream(output, leaveOpen);
+        }
+
+        internal static int GetEncodingType(string encoding)
+        {
             if (Asn1Encodable.Der.Equals(encoding))
-                return new DerOutputStream(output);
+                return EncodingDer;
+
+            return EncodingBer;
+        }
 
-            return new Asn1OutputStream(output);
+        private readonly bool m_leaveOpen;
+
+        internal Asn1OutputStream(Stream output, bool leaveOpen)
+            : base(output)
+        {
+            if (!output.CanWrite)
+                throw new ArgumentException("Expected stream to be writable", nameof(output));
+
+            m_leaveOpen = leaveOpen;
         }
 
-        internal Asn1OutputStream(Stream os)
-            : base(os)
+        protected override void Dispose(bool disposing)
         {
+            if (disposing)
+            {
+                FlushInternal();
+            }
+
+            if (m_leaveOpen)
+            {
+                base.Detach(disposing);
+            }
+            else
+            {
+                base.Dispose(disposing);
+            }
         }
 
         public virtual void WriteObject(Asn1Encodable asn1Encodable)
diff --git a/crypto/src/asn1/BEROctetStringGenerator.cs b/crypto/src/asn1/BEROctetStringGenerator.cs
index 5472802a2..ed2b85010 100644
--- a/crypto/src/asn1/BEROctetStringGenerator.cs
+++ b/crypto/src/asn1/BEROctetStringGenerator.cs
@@ -54,7 +54,7 @@ namespace Org.BouncyCastle.Asn1
 			{
 				_buf = buf;
 				_off = 0;
-				_derOut = Asn1OutputStream.Create(outStream, Asn1Encodable.Der);
+				_derOut = Asn1OutputStream.Create(outStream, Asn1Encodable.Der, leaveOpen: true);
 			}
 
 			public override void Write(byte[] buffer, int offset, int count)
@@ -145,7 +145,7 @@ namespace Org.BouncyCastle.Asn1
                         _off = 0;
                     }
 
-                    _derOut.FlushInternal();
+                    _derOut.Dispose();
                 }
                 base.Dispose(disposing);
             }
diff --git a/crypto/src/asn1/DerOutputStream.cs b/crypto/src/asn1/DerOutputStream.cs
index 6304ca852..509bd436b 100644
--- a/crypto/src/asn1/DerOutputStream.cs
+++ b/crypto/src/asn1/DerOutputStream.cs
@@ -6,8 +6,8 @@ namespace Org.BouncyCastle.Asn1
     internal class DerOutputStream
         : Asn1OutputStream
     {
-        internal DerOutputStream(Stream os)
-            : base(os)
+        internal DerOutputStream(Stream os, bool leaveOpen)
+            : base(os, leaveOpen)
         {
         }
 
diff --git a/crypto/src/asn1/LazyASN1InputStream.cs b/crypto/src/asn1/LazyASN1InputStream.cs
index 8dfbba122..8807416f4 100644
--- a/crypto/src/asn1/LazyASN1InputStream.cs
+++ b/crypto/src/asn1/LazyASN1InputStream.cs
@@ -16,8 +16,18 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
-        internal LazyAsn1InputStream(Stream input, int limit, byte[][] tmpBuffers)
-            : base(input, limit, tmpBuffers)
+        public LazyAsn1InputStream(Stream input, int limit)
+            : base(input, limit)
+        {
+        }
+
+        public LazyAsn1InputStream(Stream input, int limit, bool leaveOpen)
+            : base(input, limit, leaveOpen)
+        {
+        }
+
+        internal LazyAsn1InputStream(Stream input, int limit, bool leaveOpen, byte[][] tmpBuffers)
+            : base(input, limit, leaveOpen, tmpBuffers)
         {
         }
 
@@ -37,7 +47,10 @@ namespace Org.BouncyCastle.Asn1
             if (remaining < 1)
                 return new Asn1EncodableVector(0);
 
-            return new LazyAsn1InputStream(defIn, remaining, tmpBuffers).ReadVector();
+            using (var sub = new LazyAsn1InputStream(defIn, remaining, leaveOpen: true, tmpBuffers))
+            {
+                return sub.ReadVector();
+            }
         }
     }
 }
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index 3a19f1276..67fbba979 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -256,11 +256,13 @@ namespace Org.BouncyCastle.Asn1.Utilities
         /// <summary>Parse ASN.1 objects from input <see cref="Stream"/>, and write them to the output.</summary>
         public static void Dump(Stream input, TextWriter output)
         {
-            Asn1InputStream asn1InputStream = new Asn1InputStream(input);
-            Asn1Object asn1Object;
-            while ((asn1Object = asn1InputStream.ReadObject()) != null)
+            using (var asn1In = new Asn1InputStream(input, int.MaxValue, leaveOpen: true))
             {
-                output.Write(DumpAsString(asn1Object));
+                Asn1Object asn1Object;
+                while ((asn1Object = asn1In.ReadObject()) != null)
+                {
+                    output.Write(DumpAsString(asn1Object));
+                }
             }
         }
 
diff --git a/crypto/src/cms/CMSUtils.cs b/crypto/src/cms/CMSUtils.cs
index 281e1e73a..0041d2fac 100644
--- a/crypto/src/cms/CMSUtils.cs
+++ b/crypto/src/cms/CMSUtils.cs
@@ -33,26 +33,27 @@ namespace Org.BouncyCastle.Cms
 			}
 		}
 
-		internal static ContentInfo ReadContentInfo(
-			byte[] input)
+		internal static ContentInfo ReadContentInfo(byte[] input)
 		{
-			// enforce limit checking as from a byte array
-			return ReadContentInfo(new Asn1InputStream(input));
-		}
+            using (var asn1In = new Asn1InputStream(input))
+			{
+                return ReadContentInfo(asn1In);
+            }
+        }
 
-		internal static ContentInfo ReadContentInfo(
-			Stream input)
+		internal static ContentInfo ReadContentInfo(Stream input)
 		{
-			// enforce some limit checking
-			return ReadContentInfo(new Asn1InputStream(input, MaximumMemory));
+            using (var asn1In = new Asn1InputStream(input, MaximumMemory, leaveOpen: true))
+            {
+                return ReadContentInfo(asn1In);
+            }
 		}
 
-		private static ContentInfo ReadContentInfo(
-			Asn1InputStream aIn)
+		private static ContentInfo ReadContentInfo(Asn1InputStream asn1In)
 		{
 			try
 			{
-				return ContentInfo.GetInstance(aIn.ReadObject());
+				return ContentInfo.GetInstance(asn1In.ReadObject());
 			}
 			catch (IOException e)
 			{
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
index 7a601bebf..d187aeed2 100644
--- a/crypto/src/tls/TlsUtilities.cs
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -966,14 +966,15 @@ namespace Org.BouncyCastle.Tls
 
         public static Asn1Object ReadAsn1Object(byte[] encoding)
         {
-            Asn1InputStream asn1 = new Asn1InputStream(encoding);
-            Asn1Object result = asn1.ReadObject();
-            if (null == result)
-                throw new TlsFatalAlert(AlertDescription.decode_error);
-            if (null != asn1.ReadObject())
-                throw new TlsFatalAlert(AlertDescription.decode_error);
-
-            return result;
+            using (var asn1In = new Asn1InputStream(encoding))
+            {
+                Asn1Object result = asn1In.ReadObject();
+                if (null == result)
+                    throw new TlsFatalAlert(AlertDescription.decode_error);
+                if (asn1In.Length != asn1In.Position)
+                    throw new TlsFatalAlert(AlertDescription.decode_error);
+                return result;
+            }
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/x509/X509CertPairParser.cs b/crypto/src/x509/X509CertPairParser.cs
index 26b417898..95ba522c8 100644
--- a/crypto/src/x509/X509CertPairParser.cs
+++ b/crypto/src/x509/X509CertPairParser.cs
@@ -13,13 +13,12 @@ namespace Org.BouncyCastle.X509
 	{
 		private Stream currentStream;
 
-		private X509CertificatePair ReadDerCrossCertificatePair(
-			Stream inStream)
+		private X509CertificatePair ReadDerCrossCertificatePair(Stream inStream)
 		{
-			Asn1InputStream dIn = new Asn1InputStream(inStream);//, ProviderUtil.getReadLimit(in));
-			Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
-			CertificatePair pair = CertificatePair.GetInstance(seq);
-			return new X509CertificatePair(pair);
+            using (var asn1In = new Asn1InputStream(inStream, int.MaxValue, leaveOpen: true))
+            {
+                return new X509CertificatePair(CertificatePair.GetInstance(asn1In.ReadObject()));
+            }
 		}
 
 		/// <summary>
diff --git a/crypto/src/x509/X509CertificateParser.cs b/crypto/src/x509/X509CertificateParser.cs
index ce50dc8ed..6fc1b3ff4 100644
--- a/crypto/src/x509/X509CertificateParser.cs
+++ b/crypto/src/x509/X509CertificateParser.cs
@@ -138,7 +138,10 @@ namespace Org.BouncyCastle.X509
                 if (tag != 0x30)  // assume ascii PEM encoded.
 					return ReadPemCertificate(inStream);
 
-				return ReadDerCertificate(new Asn1InputStream(inStream));
+				using (var asn1In = new Asn1InputStream(inStream, int.MaxValue, leaveOpen: true))
+				{
+					return ReadDerCertificate(asn1In);
+				}
 			}
 			catch (Exception e)
 			{
diff --git a/crypto/src/x509/X509CrlParser.cs b/crypto/src/x509/X509CrlParser.cs
index ad2b4f704..a60a591c4 100644
--- a/crypto/src/x509/X509CrlParser.cs
+++ b/crypto/src/x509/X509CrlParser.cs
@@ -131,11 +131,14 @@ namespace Org.BouncyCastle.X509
 					return ReadPemCrl(inStream);
 
 				Asn1InputStream asn1 = lazyAsn1
-					?	new LazyAsn1InputStream(inStream)
-					:	new Asn1InputStream(inStream);
+					?	new LazyAsn1InputStream(inStream, int.MaxValue, leaveOpen: true)
+					:	new Asn1InputStream(inStream, int.MaxValue, leaveOpen: true);
 
-				return ReadDerCrl(asn1);
-			}
+                using (asn1)
+                {
+                    return ReadDerCrl(asn1);
+                }
+            }
 			catch (CrlException e)
 			{
 				throw e;
diff --git a/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs b/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
index fb035de18..2ae316f33 100644
--- a/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
+++ b/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
@@ -54,26 +54,28 @@ namespace Org.BouncyCastle.Asn1.Tests
             };
 
 			MemoryStream bOut = new MemoryStream();
-            Asn1OutputStream aOut = Asn1OutputStream.Create(bOut);
-
-            for (int i = 0; i != values.Length; i++)
+            using (var asn1Out = Asn1OutputStream.Create(bOut))
             {
-                aOut.WriteObject(values[i]);
-            }
-
-			Asn1InputStream aIn = new Asn1InputStream(bOut.ToArray());
-
-			for (int i = 0; i != values.Length; i++)
-            {
-                Asn1Object o = aIn.ReadObject();
-                if (!o.Equals(values[i]))
+                for (int i = 0; i != values.Length; i++)
                 {
-                    Fail("Failed equality test for " + o.GetType().Name);
+                    asn1Out.WriteObject(values[i]);
                 }
+            }
 
-                if (o.GetHashCode() != values[i].GetHashCode())
+            byte[] output = bOut.ToArray();
+            using (var asn1In = new Asn1InputStream(output))
+            {
+                for (int i = 0; i != values.Length; i++)
                 {
-                    Fail("Failed hashCode test for " + o.GetType().Name);
+                    Asn1Object o = asn1In.ReadObject();
+                    if (!o.Equals(values[i]))
+                    {
+                        Fail("Failed equality test for " + o.GetType().Name);
+                    }
+                    if (o.GetHashCode() != values[i].GetHashCode())
+                    {
+                        Fail("Failed hashCode test for " + o.GetType().Name);
+                    }
                 }
             }
         }
diff --git a/crypto/test/src/asn1/test/MiscTest.cs b/crypto/test/src/asn1/test/MiscTest.cs
index 96dfa4c3f..0cbf6e8fc 100644
--- a/crypto/test/src/asn1/test/MiscTest.cs
+++ b/crypto/test/src/asn1/test/MiscTest.cs
@@ -105,32 +105,33 @@ namespace Org.BouncyCastle.Asn1.Tests
             byte[] data = Base64.Decode("MA4ECAECAwQFBgcIAgIAgAMCBSAWBWhlbGxvMAoECAECAwQFBgcIFgtodHRwOi8vdGVzdA==");
 
             MemoryStream bOut = new MemoryStream();
-            Asn1OutputStream aOut = Asn1OutputStream.Create(bOut);
-
-            for (int i = 0; i != values.Length; i++)
+            using (var asn1Out = Asn1OutputStream.Create(bOut))
             {
-                aOut.WriteObject(values[i]);
+                for (int i = 0; i != values.Length; i++)
+                {
+                    asn1Out.WriteObject(values[i]);
+                }
             }
 
-            if (!Arrays.AreEqual(bOut.ToArray(), data))
+            byte[] output = bOut.ToArray();
+            if (!Arrays.AreEqual(output, data))
             {
                 Fail("Failed data check");
             }
 
-            Asn1InputStream aIn = new Asn1InputStream(bOut.ToArray());
-
-            for (int i = 0; i != values.Length; i++)
+            using (var asn1In = new Asn1InputStream(output))
             {
-                Asn1Object o = aIn.ReadObject();
-
-                if (!values[i].Equals(o))
-                {
-                    Fail("Failed equality test for " + o);
-                }
-
-                if (o.GetHashCode() != values[i].GetHashCode())
+                for (int i = 0; i != values.Length; i++)
                 {
-                    Fail("Failed hashCode test for " + o);
+                    Asn1Object o = asn1In.ReadObject();
+                    if (!values[i].Equals(o))
+                    {
+                        Fail("Failed equality test for " + o);
+                    }
+                    if (o.GetHashCode() != values[i].GetHashCode())
+                    {
+                        Fail("Failed hashCode test for " + o);
+                    }
                 }
             }
 
diff --git a/crypto/test/src/asn1/test/ParsingTest.cs b/crypto/test/src/asn1/test/ParsingTest.cs
index 43a97555f..c5d2eed9c 100644
--- a/crypto/test/src/asn1/test/ParsingTest.cs
+++ b/crypto/test/src/asn1/test/ParsingTest.cs
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 
 		public override void PerformTest()
 		{
-			inputStreamTest();
+			InputStreamTest();
 			parserTest();
 		}
 
@@ -68,31 +68,32 @@ namespace Org.BouncyCastle.Asn1.Tests
 			}
 		}
 
-		private void inputStreamTest()
+		private void InputStreamTest()
 		{
 			foreach (string stream in streams)
 			{
-				Asn1InputStream aIn = new Asn1InputStream(Base64.Decode(stream));
-
-				try
+				using (var aIn = new Asn1InputStream(Base64.Decode(stream)))
 				{
-					object obj;
-					while ((obj = aIn.ReadObject()) != null)
-					{
-					}
+                    try
+                    {
+                        object obj;
+                        while ((obj = aIn.ReadObject()) != null)
+                        {
+                        }
 
-					Fail("bad stream parsed successfully!");
-				}
-				catch (IOException)
-				{
-					// ignore
-				}
-				// Note: C# may throw these instead, since no InMemoryRepresentable support
-				catch (Asn1ParsingException)
-				{
-					// ignore
-				}
-			}
+                        Fail("bad stream parsed successfully!");
+                    }
+                    catch (IOException)
+                    {
+                        // ignore
+                    }
+                    // Note: C# may throw these instead, since no InMemoryRepresentable support
+                    catch (Asn1ParsingException)
+                    {
+                        // ignore
+                    }
+                }
+            }
 		}
 	}
 }
diff --git a/crypto/test/src/cms/test/SignedDataTest.cs b/crypto/test/src/cms/test/SignedDataTest.cs
index d0702c836..9d1d18ad3 100644
--- a/crypto/test/src/cms/test/SignedDataTest.cs
+++ b/crypto/test/src/cms/test/SignedDataTest.cs
@@ -1057,12 +1057,12 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			Assert.AreEqual(3, s.Version);
 
-			MemoryStream bIn = new MemoryStream(s.GetEncoded(), false);
-			Asn1InputStream aIn = new Asn1InputStream(bIn);
-
-			s = new CmsSignedData(ContentInfo.GetInstance(aIn.ReadObject()));
+			using (var aIn = new Asn1InputStream(s.GetEncoded()))
+			{
+                s = new CmsSignedData(ContentInfo.GetInstance(aIn.ReadObject()));
+            }
 
-			x509Certs = s.GetCertificates();
+            x509Certs = s.GetCertificates();
 			x509Crls = s.GetCrls();
 
 			SignerInformationStore signers = s.GetSignerInfos();
@@ -1101,12 +1101,12 @@ namespace Org.BouncyCastle.Cms.Tests
 
 			s = gen.Generate(msg, true);
 
-			bIn = new MemoryStream(s.GetEncoded(), false);
-			aIn = new Asn1InputStream(bIn);
-
-			s = new CmsSignedData(ContentInfo.GetInstance(aIn.ReadObject()));
+			using (var aIn = new Asn1InputStream(s.GetEncoded()))
+			{
+                s = new CmsSignedData(ContentInfo.GetInstance(aIn.ReadObject()));
+            }
 
-			x509Certs = s.GetCertificates();
+            x509Certs = s.GetCertificates();
 			x509Crls = s.GetCrls();
 
 			signers = s.GetSignerInfos();