From 6b59dbbece3b1509b83b8479259a3c6059e9a60b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 5 Mar 2023 14:45:49 +0700 Subject: Support 'leaveOpen' in Asn1 streams --- crypto/src/asn1/Asn1InputStream.cs | 43 +++++++++++++++----- crypto/src/asn1/Asn1OutputStream.cs | 46 +++++++++++++++++++--- crypto/src/asn1/BEROctetStringGenerator.cs | 4 +- crypto/src/asn1/DerOutputStream.cs | 4 +- crypto/src/asn1/LazyASN1InputStream.cs | 19 +++++++-- crypto/src/asn1/util/Asn1Dump.cs | 10 +++-- crypto/src/cms/CMSUtils.cs | 25 ++++++------ crypto/src/tls/TlsUtilities.cs | 17 ++++---- crypto/src/x509/X509CertPairParser.cs | 11 +++--- crypto/src/x509/X509CertificateParser.cs | 5 ++- crypto/src/x509/X509CrlParser.cs | 11 ++++-- crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs | 32 ++++++++------- crypto/test/src/asn1/test/MiscTest.cs | 35 ++++++++-------- crypto/test/src/asn1/test/ParsingTest.cs | 43 ++++++++++---------- crypto/test/src/cms/test/SignedDataTest.cs | 20 +++++----- 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 /// Parse ASN.1 objects from input , and write them to the output. 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; + } } /// 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())); + } } /// 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(); -- cgit 1.4.1