From daaa83360625e4f4b0871225aeaf13ba90532bac Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 4 Sep 2020 14:16:23 -0400 Subject: [PATCH 1/3] Implement {Try}ExportSubjectKeyInfo on ECDHPK. --- .../EC/ECKeyFileTests.cs | 24 +++++++++++++++---- .../ECDiffieHellman/ECDhKeyFileTests.cs | 7 ++++++ ...System.Security.Cryptography.Algorithms.cs | 2 ++ ...DiffieHellmanPublicKey.ExportParameters.cs | 16 +++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs index 1c3a0c2aaccff2..1841ed5ebf76fb 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs @@ -17,6 +17,8 @@ public abstract partial class ECKeyFileTests where T : AsymmetricAlgorithm protected abstract void ImportParameters(T key, ECParameters ecParameters); protected abstract ECParameters ExportParameters(T key, bool includePrivate); protected abstract void Exercise(T key); + protected virtual Func PublicKeyWriteArrayFunc { get; } = null; + protected virtual WriteKeyToSpanFunc PublicKeyWriteSpanFunc { get; } = null; public static bool SupportsBrainpool { get; } = IsCurveSupported(ECCurve.NamedCurves.brainpoolP160r1.Oid); public static bool SupportsSect163k1 { get; } = IsCurveSupported(EccTestData.Sect163k1Key1.Curve.Oid); @@ -1153,7 +1155,9 @@ private void ReadWriteBase64SubjectPublicKeyInfo( key.ImportSubjectPublicKeyInfo(source, out read), key => key.ExportSubjectPublicKeyInfo(), (T key, Span destination, out int written) => - key.TryExportSubjectPublicKeyInfo(destination, out written)); + key.TryExportSubjectPublicKeyInfo(destination, out written), + writePublicArrayFunc: PublicKeyWriteArrayFunc, + writePublicSpanFunc: PublicKeyWriteSpanFunc); } else { @@ -1175,7 +1179,9 @@ private void ReadWriteKey( ReadKeyAction readAction, Func writeArrayFunc, WriteKeyToSpanFunc writeSpanFunc, - bool isEncrypted = false) + bool isEncrypted = false, + Func writePublicArrayFunc = null, + WriteKeyToSpanFunc writePublicSpanFunc = null) { bool isPrivateKey = expected.D != null; @@ -1192,6 +1198,16 @@ private void ReadWriteKey( arrayExport = writeArrayFunc(key); + if (writePublicArrayFunc is not null) + { + byte[] publicArrayExport = writePublicArrayFunc(key); + Assert.Equal(arrayExport, publicArrayExport); + + Assert.True(writePublicSpanFunc(key, publicArrayExport, out int publicExportWritten)); + Assert.Equal(publicExportWritten, publicArrayExport.Length); + Assert.Equal(arrayExport, publicArrayExport); + } + ECParameters ecParameters = ExportParameters(key, isPrivateKey); EccTestBase.AssertEqual(expected, ecParameters); } @@ -1286,7 +1302,7 @@ private void ReadWriteKey( } } - private delegate void ReadKeyAction(T key, ReadOnlySpan source, out int bytesRead); - private delegate bool WriteKeyToSpanFunc(T key, Span destination, out int bytesWritten); + protected delegate void ReadKeyAction(T key, ReadOnlySpan source, out int bytesRead); + protected delegate bool WriteKeyToSpanFunc(T key, Span destination, out int bytesWritten); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs index 6067f33cc55b22..6fb60fb9596122 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDhKeyFileTests.cs @@ -40,5 +40,12 @@ protected override ECParameters ExportParameters(ECDiffieHellman key, bool inclu } protected override void Exercise(ECDiffieHellman key) => key.Exercise(); + + protected override Func PublicKeyWriteArrayFunc { get; } = + key => key.PublicKey.ExportSubjectPublicKeyInfo(); + + protected override WriteKeyToSpanFunc PublicKeyWriteSpanFunc { get; } = + (ECDiffieHellman key, Span destination, out int bytesWritten) => + key.PublicKey.TryExportSubjectPublicKeyInfo(destination, out bytesWritten); } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index caa58cf6bc0b07..3366cb142cc9ed 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -308,8 +308,10 @@ public void Dispose() { } protected virtual void Dispose(bool disposing) { } public virtual System.Security.Cryptography.ECParameters ExportExplicitParameters() { throw null; } public virtual System.Security.Cryptography.ECParameters ExportParameters() { throw null; } + public virtual byte[] ExportSubjectPublicKeyInfo() { throw null; } public virtual byte[] ToByteArray() { throw null; } public virtual string ToXmlString() { throw null; } + public virtual bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; } } public abstract partial class ECDsa : System.Security.Cryptography.AsymmetricAlgorithm { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs index a1193856fb0591..43f34a6519080b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Formats.Asn1; + namespace System.Security.Cryptography { /// @@ -27,5 +29,19 @@ public virtual ECParameters ExportExplicitParameters() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + + public virtual bool TryExportSubjectPublicKeyInfo(Span destination, out int bytesWritten) + { + ECParameters ecParameters = ExportParameters(); + AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters); + return writer.TryEncode(destination, out bytesWritten); + } + + public virtual byte[] ExportSubjectPublicKeyInfo() + { + ECParameters ecParameters = ExportParameters(); + AsnWriter writer = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(ecParameters); + return writer.Encode(); + } } } From 1a034868f2391f715bac451679be7f17b5a5918a Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 4 Sep 2020 14:25:11 -0400 Subject: [PATCH 2/3] Add test for too-small buffer. --- .../ECDiffieHellman/ECDiffieHellmanTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs index d826b7397f18af..920b5b25a3a304 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.cs @@ -111,6 +111,18 @@ public static void PublicKeyIsFactory() } } + [Fact] + public static void PublicKey_TryExportSubjectPublicKeyInfo_TooSmall() + { + using (ECDiffieHellman ecdh = ECDiffieHellmanFactory.Create()) + using (ECDiffieHellmanPublicKey publicKey = ecdh.PublicKey) + { + Span destination = stackalloc byte[1]; + Assert.False(publicKey.TryExportSubjectPublicKeyInfo(destination, out int written)); + Assert.Equal(0, written); + } + } + [Theory] [InlineData(false)] [InlineData(true)] From 496ec20d9e6c02adc9ad3c37a59cf3a0af1508ab Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 4 Sep 2020 14:53:12 -0400 Subject: [PATCH 3/3] XML docs. --- ...DiffieHellmanPublicKey.ExportParameters.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs index 43f34a6519080b..20aef961cfdc80 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/ECDiffieHellmanPublicKey.ExportParameters.cs @@ -30,6 +30,23 @@ public virtual ECParameters ExportExplicitParameters() throw new NotSupportedException(SR.NotSupported_SubclassOverride); } + /// + /// Attempts to export the current key in the X.509 SubjectPublicKeyInfo format. + /// + /// The byte span to receive the X.509 SubjectPublicKeyInfo data. + /// + /// When this method returns, contains a value that indicates the number of bytes written to . + /// This parameter is treated as uninitialized. + /// + /// + /// if is big enough to receive the output; + /// otherwise, . + /// + /// + /// The member has not been overridden in a derived class. + /// + /// The object has already been disposed. + /// The key is invalid and could not be exported. public virtual bool TryExportSubjectPublicKeyInfo(Span destination, out int bytesWritten) { ECParameters ecParameters = ExportParameters(); @@ -37,6 +54,17 @@ public virtual bool TryExportSubjectPublicKeyInfo(Span destination, out in return writer.TryEncode(destination, out bytesWritten); } + /// + /// Exports the current key in the X.509 SubjectPublicKeyInfo format. + /// + /// + /// A byte array containing the X.509 SubjectPublicKeyInfo representation of this key. + /// + /// + /// The member has not been overridden in a derived class. + /// + /// The object has already been disposed. + /// The key is invalid and could not be exported. public virtual byte[] ExportSubjectPublicKeyInfo() { ECParameters ecParameters = ExportParameters();