From 4e5ff50247bae4787cfb8ca4bad849fa3d9534dd Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 15 Sep 2016 15:56:47 -0500 Subject: [PATCH 1/7] Add crypto formatters --- .../packageIndex.json | 3 +- .../DSA/DSASignatureFormatter.cs | 55 +++++++++ .../RSA/RSAKeyExchangeFormatter.cs | 93 +++++++++++++++ .../RSA/RSASignatureFormatter.cs | 81 +++++++++++++ .../AsymmetricKeyExchangeFormatterTests.cs | 36 ++++++ .../AsymmetricSignatureFormatterTests.cs | 63 +++++++++++ ...System.Security.Cryptography.Algorithms.cs | 100 +++++++++++++++++ ...em.Security.Cryptography.Algorithms.csproj | 5 + .../Cryptography/HashAlgorithmNames.cs | 36 ++++-- .../src/Resources/Strings.resx | 9 ++ ...em.Security.Cryptography.Algorithms.builds | 2 +- ...em.Security.Cryptography.Algorithms.csproj | 18 +++ .../AsymmetricKeyExchangeDeformatter.cs | 14 +++ .../AsymmetricKeyExchangeFormatter.cs | 19 ++++ .../AsymmetricSignatureDeformatter.cs | 27 +++++ .../AsymmetricSignatureFormatter.cs | 27 +++++ .../Cryptography/DSASignatureDeformatter.cs | 67 +++++++++++ .../Cryptography/DSASignatureFormatter.cs | 67 +++++++++++ .../RSAOAEPKeyExchangeDeformatter.cs | 47 ++++++++ .../RSAOAEPKeyExchangeFormatter.cs | 76 +++++++++++++ .../RSAPKCS1KeyExchangeDeformatter.cs | 48 ++++++++ .../RSAPKCS1KeyExchangeFormatter.cs | 50 +++++++++ .../RSAPKCS1SignatureDeformatter.cs | 60 ++++++++++ .../RSAPKCS1SignatureFormatter.cs | 59 ++++++++++ .../tests/HashAlgorithmTest.cs | 51 +++++++-- ...urity.Cryptography.Algorithms.Tests.csproj | 26 ++++- .../System.Security.Cryptography.Cng.csproj | 9 ++ .../System.Security.Cryptography.Cng.csproj | 10 ++ ...tem.Security.Cryptography.Cng.Tests.csproj | 25 +++++ .../System.Security.Cryptography.Csp.csproj | 9 ++ .../System.Security.Cryptography.Csp.csproj | 10 ++ ...tem.Security.Cryptography.Csp.Tests.csproj | 25 +++++ ...ystem.Security.Cryptography.OpenSsl.csproj | 9 ++ ...ystem.Security.Cryptography.OpenSsl.csproj | 10 ++ ...Security.Cryptography.OpenSsl.Tests.csproj | 23 ++++ .../dir.props | 2 +- ...m.Security.Cryptography.Primitives.pkgproj | 2 +- ...System.Security.Cryptography.Primitives.cs | 44 +++++--- ...em.Security.Cryptography.Primitives.csproj | 2 +- .../ref/project.json | 10 +- .../src/Resources/Strings.resx | 3 + ...em.Security.Cryptography.Primitives.builds | 2 +- ...em.Security.Cryptography.Primitives.csproj | 11 +- ...yptographicUnexpectedOperationException.cs | 31 +++++ .../Security/Cryptography/HashAlgorithm.cs | 106 ++++++++++++++++-- .../Cryptography/KeyedHashAlgorithm.cs | 12 +- .../src/project.json | 21 ++-- ...urity.Cryptography.Primitives.Tests.builds | 3 +- ...urity.Cryptography.Primitives.Tests.csproj | 3 +- .../tests/project.json | 22 +++- 50 files changed, 1467 insertions(+), 76 deletions(-) create mode 100644 src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs create mode 100644 src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs create mode 100644 src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs create mode 100644 src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs create mode 100644 src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs create mode 100644 src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs create mode 100644 src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs diff --git a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json index a5899bb3f361..fb6df820c985 100644 --- a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json +++ b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json @@ -2001,7 +2001,8 @@ "BaselineVersion": "4.0.0", "AssemblyVersionInPackageVersion": { "4.0.0.0": "4.0.0", - "4.0.1.0": "4.3.0" + "4.0.1.0": "4.0.1", + "4.1.0.0": "4.3.0" } }, "System.Security.Cryptography.ProtectedData": { diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs new file mode 100644 index 000000000000..d331135f9fff --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Dsa.Tests +{ + public class DSAFormatterTests + { + [Fact] + public static void FormatterArguments() + { + AsymmetricSignatureFormatterTests.FormatterArguments(new DSASignatureFormatter()); + } + + [Fact] + public static void DeformatterArguments() + { + AsymmetricSignatureFormatterTests.DeformatterArguments(new DSASignatureDeformatter()); + } + + [Fact] + public static void VerifySignature_SHA1() + { + using (DSA dsa = DSAFactory.Create()) + { + + var formatter = new DSASignatureFormatter(dsa); + var deformatter = new DSASignatureDeformatter(dsa); + AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA1.Create(), HashAlgorithmName.SHA1); + } + } + + [ConditionalFact(nameof(SupportsFips186_3))] + public static void VerifySignature_SHA256() + { + using (DSA dsa = DSAFactory.Create()) + { + var formatter = new DSASignatureFormatter(dsa); + var deformatter = new DSASignatureDeformatter(dsa); + AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA256.Create(), HashAlgorithmName.SHA256); + } + } + + public static bool SupportsFips186_3 + { + get + { + return DSAFactory.SupportsFips186_3; + } + } + } +} \ No newline at end of file diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs new file mode 100644 index 000000000000..ce0c5a465c32 --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Tests; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Rsa.Tests +{ + public class RSAKeyExchangeFormatterTests + { + [Fact] + public static void RSAOAEPFormatterArguments() + { + AsymmetricKeyExchangeFormatterTests.FormatterArguments(new RSAOAEPKeyExchangeFormatter()); + } + + [Fact] + public static void RSAOAEPDeformatterArguments() + { + AsymmetricKeyExchangeFormatterTests.DeformatterArguments(new RSAOAEPKeyExchangeDeformatter()); + } + + [Fact] + public static void RSAPKCS1FormatterArguments() + { + AsymmetricKeyExchangeFormatterTests.FormatterArguments(new RSAPKCS1KeyExchangeFormatter()); + } + + [Fact] + public static void RSAPKCS1DeformatterArguments() + { + AsymmetricKeyExchangeFormatterTests.DeformatterArguments(new RSAPKCS1KeyExchangeDeformatter()); + } + + [Fact] + public static void VerifyDecryptKeyExchangeOaep() + { + using (RSA rsa = RSAFactory.Create(1024)) + { + var formatter = new RSAOAEPKeyExchangeFormatter(rsa); + var deformatter = new RSAOAEPKeyExchangeDeformatter(rsa); + AsymmetricKeyExchangeFormatterTests.VerifyDecryptKeyExchange(formatter, deformatter); + } + } + + [Fact] + public static void VerifyDecryptKeyExchangePkcs1() + { + using (RSA rsa = RSAFactory.Create(1024)) + { + var formatter = new RSAPKCS1KeyExchangeFormatter(rsa); + var deformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); + AsymmetricKeyExchangeFormatterTests.VerifyDecryptKeyExchange(formatter, deformatter); + } + } + + [Fact] + public static void TestKnownValueOaep() + { + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.RSA1024Params); + byte[] encrypted = + ("19134ffba4025a1c651120ca07258a46e005a327c3927f615465060734dc0339114cabfd13803288883abf9329296a3e3a5cb1587927" + + "a6e8a2e736f0a756e342b4adb0f1de5bba9ba5faee30456fb7409678eb71a70185606eda3303d9425fbeb730ab7803bea50e208b563f" + + "e9bfa97a8966deefb211a3bd6abe08cd15e0b927").HexToByteArray(); + RSAOAEPKeyExchangeDeformatter deformatter = new RSAOAEPKeyExchangeDeformatter(rsa); + byte[] plain = deformatter.DecryptKeyExchange(encrypted); + byte[] expectedPlain = { 0x41, 0x42, 0x43 }; + Assert.Equal(expectedPlain, plain); + } + } + + [Fact] + public static void TestKnownValuePkcs1() + { + using (RSA rsa = RSAFactory.Create()) + { + rsa.ImportParameters(TestData.RSA1024Params); + byte[] encrypted = + ("7061adb87a8759f0a0dc6ece42f5b63bf186f845237c6b16bf824b303812486efbb8f5febb681902228a609d4330a6c21abf0fc0d271" + + "ba63d1d0d9e486668270c2dbf73ab33055dfc0b797938557b99c0e9a535605c0a4bceefe5a37594732bb566ab026e4e8d5ce47d0967d" + + "f1c66e7ee4d39d804f6d558670222d708f943eb0").HexToByteArray(); + RSAPKCS1KeyExchangeDeformatter deformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); + byte[] plain = deformatter.DecryptKeyExchange(encrypted); + byte[] expectedPlain = { 0x41, 0x42, 0x43 }; + Assert.Equal(expectedPlain, plain); + } + } + } +} diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs new file mode 100644 index 000000000000..d647c52deb2f --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Tests; +using Test.Cryptography; +using Xunit; + +namespace System.Security.Cryptography.Rsa.Tests +{ + public class RSASignatureFormatterTests + { + [Fact] + public static void FormatterArguments() + { + AsymmetricSignatureFormatterTests.FormatterArguments(new RSAPKCS1SignatureFormatter()); + } + + [Fact] + public static void DeformatterArguments() + { + AsymmetricSignatureFormatterTests.DeformatterArguments(new RSAPKCS1SignatureDeformatter()); + } + + [Fact] + public static void VerifySignature_SHA1() + { + using (RSA rsa = RSAFactory.Create(1024)) + { + var formatter = new RSAPKCS1SignatureFormatter(rsa); + var deformatter = new RSAPKCS1SignatureDeformatter(rsa); + AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA1.Create(), HashAlgorithmName.SHA1); + } + } + + [Fact] + public static void VerifySignature_SHA256() + { + using (RSA rsa = RSAFactory.Create(1024)) + { + var formatter = new RSAPKCS1SignatureFormatter(rsa); + var deformatter = new RSAPKCS1SignatureDeformatter(rsa); + AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA256.Create(), HashAlgorithmName.SHA256); + } + } + + [Fact] + public static void VerifyKnownSignature() + { + byte[] hash = "012d161304fa0c6321221516415813022320620c".HexToByteArray(); + byte[] sig; + + using (RSA key = RSAFactory.Create(1024)) + { + key.ImportParameters(TestData.RSA1024Params); + RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key); + formatter.SetHashAlgorithm("SHA1"); + sig = formatter.CreateSignature(hash); + + byte[] expectedSig = + ("566390012605b1c4c01c3c2f91ce27d19476ab7131d9ee9cd1b811afb2be02ab6b498b862f0b2368ed6b09ccc9e0ec0d4f97a4f318f4f11" + + "ae882a1131012dc35d2e0b810a38e05da71d291e88b306605c9d34815091641370bd7db7a87b115bd427fcb9993bc5ba2bd518745aef80c" + + "a4557cfa1d827ff1610595d8eeb4c15073").HexToByteArray(); + Assert.Equal(expectedSig, sig); + } + + using (RSA key = RSAFactory.Create(1024)) + { + key.ImportParameters(TestData.RSA1024Params); + RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key); + deformatter.SetHashAlgorithm("SHA1"); + bool verified = deformatter.VerifySignature(hash, sig); + Assert.True(verified); + + sig[3] ^= 0xff; + verified = deformatter.VerifySignature(hash, sig); + Assert.False(verified); + } + } + } +} diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs new file mode 100644 index 000000000000..5f96fab6dded --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + public class AsymmetricKeyExchangeFormatterTests + { + public static readonly byte[] HelloBytes = new ASCIIEncoding().GetBytes("Hello"); + + public static void FormatterArguments(AsymmetricKeyExchangeFormatter formatter) + { + Assert.Throws(() => formatter.SetKey(null)); + Assert.Throws(() => formatter.CreateKeyExchange(new byte[] { 0, 1, 2, 3 })); + } + + public static void DeformatterArguments(AsymmetricKeyExchangeDeformatter deformatter) + { + Assert.Throws(() => deformatter.SetKey(null)); + Assert.Throws(() => deformatter.DecryptKeyExchange(new byte[] { 0, 1, 2 })); + } + + public static void VerifyDecryptKeyExchange(AsymmetricKeyExchangeFormatter formatter, AsymmetricKeyExchangeDeformatter deformatter) + { + byte[] encrypted = formatter.CreateKeyExchange(HelloBytes); + byte[] decrypted = deformatter.DecryptKeyExchange(encrypted); + Assert.Equal(HelloBytes, decrypted); + + unchecked { encrypted[encrypted.Length - 1]--; } + Assert.ThrowsAny(() => deformatter.DecryptKeyExchange(encrypted)); + } + } +} diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs new file mode 100644 index 000000000000..a13f8b9a5bae --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + public class AsymmetricSignatureFormatterTests + { + public static readonly byte[] HelloBytes = new ASCIIEncoding().GetBytes("Hello"); + + public static void FormatterArguments(AsymmetricSignatureFormatter formatter) + { + Assert.Throws(() => formatter.SetKey(null)); + Assert.Throws(() => formatter.CreateSignature((byte[])null)); + Assert.Throws(() => formatter.CreateSignature(new byte[] { 0, 1, 2, 3 })); + } + + public static void DeformatterArguments(AsymmetricSignatureDeformatter deformatter) + { + Assert.Throws(() => deformatter.SetKey(null)); + Assert.Throws(() => deformatter.VerifySignature((byte[])null, new byte[] { 0, 1, 2 })); + Assert.Throws(() => deformatter.VerifySignature(new byte[] { 0, 1, 2 }, new byte[] { 0, 1, 2 })); + } + + public static void VerifySignature(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName) + { + formatter.SetHashAlgorithm(hashAlgorithmName.Name); + deformatter.SetHashAlgorithm(hashAlgorithmName.Name); + + byte[] hash = hashAlgorithm.ComputeHash(HelloBytes); + + VerifySignature_ComputeHash(formatter, deformatter, hash); + +#if netstandard17 + // Check that the hash is preserved from the ComputeHash above + VerifySignature_HashAlgorithm(formatter, deformatter, hashAlgorithm); +#endif + } + + private static void VerifySignature_ComputeHash(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, byte[] hash) + { + byte[] signature = formatter.CreateSignature(hash); + Assert.True(deformatter.VerifySignature(hash, signature)); + + unchecked { signature[signature.Length - 1]--; } + Assert.False(deformatter.VerifySignature(hash, signature)); + } + +#if netstandard17 + private static void VerifySignature_HashAlgorithm(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm) + { + byte[] signature = formatter.CreateSignature(hashAlgorithm); + Assert.True(deformatter.VerifySignature(hashAlgorithm, signature)); + + unchecked { signature[signature.Length - 1]--; } + Assert.False(deformatter.VerifySignature(hashAlgorithm, signature)); + } +#endif + } +} diff --git a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index 491b31ab0cd1..41091332d2c6 100644 --- a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -15,6 +15,37 @@ protected Aes() { } public override System.Security.Cryptography.KeySizes[] LegalKeySizes { get { return default(System.Security.Cryptography.KeySizes[]); } } public static System.Security.Cryptography.Aes Create() { return default(System.Security.Cryptography.Aes); } } + public abstract partial class AsymmetricKeyExchangeDeformatter + { + protected AsymmetricKeyExchangeDeformatter() { } + public abstract string Parameters { get; set; } + public abstract byte[] DecryptKeyExchange(byte[] rgb); + public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); + } + public abstract partial class AsymmetricKeyExchangeFormatter + { + protected AsymmetricKeyExchangeFormatter() { } + public abstract string Parameters { get; } + public abstract byte[] CreateKeyExchange(byte[] data); + public abstract byte[] CreateKeyExchange(byte[] data, System.Type symAlgType); + public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); + } + public abstract partial class AsymmetricSignatureDeformatter + { + protected AsymmetricSignatureDeformatter() { } + public abstract void SetHashAlgorithm(string strName); + public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); + public abstract bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); + public virtual bool VerifySignature(System.Security.Cryptography.HashAlgorithm hash, byte[] rgbSignature) { return default(bool); } + } + public abstract partial class AsymmetricSignatureFormatter + { + protected AsymmetricSignatureFormatter() { } + public abstract byte[] CreateSignature(byte[] rgbHash); + public virtual byte[] CreateSignature(System.Security.Cryptography.HashAlgorithm hash) { return default(byte[]); } + public abstract void SetHashAlgorithm(string strName); + public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); + } public abstract partial class DeriveBytes : System.IDisposable { protected DeriveBytes() { } @@ -52,6 +83,22 @@ public partial struct DSAParameters public byte[] X; public byte[] Y; } + public partial class DSASignatureDeformatter : System.Security.Cryptography.AsymmetricSignatureDeformatter + { + public DSASignatureDeformatter() { } + public DSASignatureDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override void SetHashAlgorithm(string strName) { } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) { return default(bool); } + } + public partial class DSASignatureFormatter : System.Security.Cryptography.AsymmetricSignatureFormatter + { + public DSASignatureFormatter() { } + public DSASignatureFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override byte[] CreateSignature(byte[] rgbHash) { return default(byte[]); } + public override void SetHashAlgorithm(string strName) { } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct ECCurve { @@ -273,6 +320,24 @@ public enum RSAEncryptionPaddingMode Oaep = 1, Pkcs1 = 0, } + public partial class RSAOAEPKeyExchangeDeformatter : System.Security.Cryptography.AsymmetricKeyExchangeDeformatter + { + public RSAOAEPKeyExchangeDeformatter() { } + public RSAOAEPKeyExchangeDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override string Parameters { get { return default(string); } set { } } + public override byte[] DecryptKeyExchange(byte[] rgbData) { return default(byte[]); } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } + public partial class RSAOAEPKeyExchangeFormatter : System.Security.Cryptography.AsymmetricKeyExchangeFormatter + { + public RSAOAEPKeyExchangeFormatter() { } + public RSAOAEPKeyExchangeFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public byte[] Parameter { get { return default(byte[]); } set { } } + public override string Parameters { get { return default(string); } } + public override byte[] CreateKeyExchange(byte[] rgbData) { return default(byte[]); } + public override byte[] CreateKeyExchange(byte[] rgbData, System.Type symAlgType) { return default(byte[]); } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct RSAParameters { @@ -285,6 +350,40 @@ public partial struct RSAParameters public byte[] P; public byte[] Q; } + [System.Runtime.InteropServices.ComVisibleAttribute(true)] + public partial class RSAPKCS1KeyExchangeDeformatter : System.Security.Cryptography.AsymmetricKeyExchangeDeformatter + { + public RSAPKCS1KeyExchangeDeformatter() { } + public RSAPKCS1KeyExchangeDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override string Parameters { get { return default(string); } set { } } + public override byte[] DecryptKeyExchange(byte[] rgbIn) { return default(byte[]); } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } + public partial class RSAPKCS1KeyExchangeFormatter : System.Security.Cryptography.AsymmetricKeyExchangeFormatter + { + public RSAPKCS1KeyExchangeFormatter() { } + public RSAPKCS1KeyExchangeFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override string Parameters { get { return default(string); } } + public override byte[] CreateKeyExchange(byte[] rgbData) { return default(byte[]); } + public override byte[] CreateKeyExchange(byte[] rgbData, System.Type symAlgType) { return default(byte[]); } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } + public partial class RSAPKCS1SignatureDeformatter : System.Security.Cryptography.AsymmetricSignatureDeformatter + { + public RSAPKCS1SignatureDeformatter() { } + public RSAPKCS1SignatureDeformatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override void SetHashAlgorithm(string strName) { } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) { return default(bool); } + } + public partial class RSAPKCS1SignatureFormatter : System.Security.Cryptography.AsymmetricSignatureFormatter + { + public RSAPKCS1SignatureFormatter() { } + public RSAPKCS1SignatureFormatter(System.Security.Cryptography.AsymmetricAlgorithm key) { } + public override byte[] CreateSignature(byte[] rgbHash) { return default(byte[]); } + public override void SetHashAlgorithm(string strName) { } + public override void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } + } public sealed partial class RSASignaturePadding : System.IEquatable { internal RSASignaturePadding() { } @@ -333,3 +432,4 @@ protected TripleDES() { } public static bool IsWeakKey(byte[] rgbKey) { return default(bool); } } } + diff --git a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.csproj index 846905e02f3a..fa96adb7965a 100644 --- a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.csproj +++ b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.csproj @@ -7,6 +7,11 @@ + + + + System.Security.Cryptography.Primitives + diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs index 132c498600cb..a3cdda1f5510 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs @@ -2,21 +2,37 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Diagnostics; +using System.Security.Cryptography; namespace Internal.Cryptography { internal static class HashAlgorithmNames { // These are accepted by CNG - public const String MD5 = "MD5"; - public const String SHA1 = "SHA1"; - public const String SHA256 = "SHA256"; - public const String SHA384 = "SHA384"; - public const String SHA512 = "SHA512"; - } -} - + public const string MD5 = "MD5"; + public const string SHA1 = "SHA1"; + public const string SHA256 = "SHA256"; + public const string SHA384 = "SHA384"; + public const string SHA512 = "SHA512"; + /// + /// Map HashAlgorithm type to string; desktop uses CryptoConfig functionality. + /// + public static string ToAlgorithmName(HashAlgorithm hashAlgorithm) + { + if (hashAlgorithm is SHA1) + return HashAlgorithmNames.SHA1; + if (hashAlgorithm is SHA256) + return HashAlgorithmNames.SHA256; + if (hashAlgorithm is SHA384) + return HashAlgorithmNames.SHA384; + if (hashAlgorithm is SHA512) + return HashAlgorithmNames.SHA512; + if (hashAlgorithm is MD5) + return HashAlgorithmNames.MD5; + // Fallback to ToString() which can be extended by derived classes + return hashAlgorithm.ToString(); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx b/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx index 381729c2edb7..17596b11c18b 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx +++ b/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx @@ -195,6 +195,9 @@ Specified initialization vector (IV) does not match the block size for this algorithm. + + This operation is not supported for this class. + Specified padding mode is not valid for this algorithm. @@ -210,6 +213,12 @@ The cipher mode specified requires that an initialization vector (IV) be used. + + No asymmetric key object has been associated with this formatter object. + + + Required object identifier (OID) cannot be found. + TransformBlock may only process bytes in block sized increments. diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.builds b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.builds index 26ced0093aa6..3daa6f65ad9e 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.builds +++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.builds @@ -2,7 +2,7 @@ - + Unix diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 80c3473ec929..4e6abb439b54 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -26,9 +26,15 @@ + + + + + + @@ -51,7 +57,13 @@ + + + + + + @@ -390,5 +402,11 @@ + + + + System.Security.Cryptography.Primitives + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeDeformatter.cs new file mode 100644 index 000000000000..83c3ef505087 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeDeformatter.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public abstract class AsymmetricKeyExchangeDeformatter + { + protected AsymmetricKeyExchangeDeformatter() {} + public abstract string Parameters {get; set;} + public abstract void SetKey(AsymmetricAlgorithm key); + public abstract byte[] DecryptKeyExchange(byte[] rgb); + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs new file mode 100644 index 000000000000..57da523c1152 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public abstract class AsymmetricKeyExchangeFormatter + { + protected AsymmetricKeyExchangeFormatter() {} + + public abstract string Parameters {get;} + + public abstract void SetKey(AsymmetricAlgorithm key); + public abstract byte[] CreateKeyExchange(byte[] data); + + // review note: keeping this even though synAlgType is not used + public abstract byte[] CreateKeyExchange(byte[] data, Type symAlgType); + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs new file mode 100644 index 000000000000..62a5b969473a --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + public abstract class AsymmetricSignatureDeformatter + { + protected AsymmetricSignatureDeformatter() {} + + public abstract void SetKey(AsymmetricAlgorithm key); + public abstract void SetHashAlgorithm(string strName); + + public virtual bool VerifySignature(HashAlgorithm hash, byte[] rgbSignature) + { + if (hash == null) + throw new ArgumentNullException(nameof(hash)); + + SetHashAlgorithm(HashAlgorithmNames.ToAlgorithmName(hash)); + return VerifySignature(hash.Hash, rgbSignature); + } + + public abstract bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs new file mode 100644 index 000000000000..db539fd3f76e --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + public abstract class AsymmetricSignatureFormatter + { + protected AsymmetricSignatureFormatter() {} + + public abstract void SetKey(AsymmetricAlgorithm key); + public abstract void SetHashAlgorithm(string strName); + + public virtual byte[] CreateSignature(HashAlgorithm hash) + { + if (hash == null) + throw new ArgumentNullException(nameof(hash)); + + SetHashAlgorithm(HashAlgorithmNames.ToAlgorithmName(hash)); + return CreateSignature(hash.Hash); + } + + public abstract byte[] CreateSignature(byte[] rgbHash); + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs new file mode 100644 index 000000000000..cba820f99d82 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + public class DSASignatureDeformatter : AsymmetricSignatureDeformatter + { + DSA _dsaKey; + string _algName; + + public DSASignatureDeformatter() + { + // The hash algorithm default is SHA1 + _algName = HashAlgorithmNames.SHA1; + } + + public DSASignatureDeformatter(AsymmetricAlgorithm key) : this() + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _dsaKey = (DSA)key; + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _dsaKey = (DSA)key; + } + + public override void SetHashAlgorithm(string strName) + { + // The implemenation is symmetric with RSAPKCS1SignatureDeformatter even though _algName + // is not used in VerifySignature. + try + { + // Verify the name + Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); + + // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. + _algName = strName; + } + catch (CryptographicException) + { + // For desktop compat there is no exception here + _algName = null; + } + } + + public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) + { + if (rgbHash == null) + throw new ArgumentNullException(nameof(rgbHash)); + if (rgbSignature == null) + throw new ArgumentNullException(nameof(rgbSignature)); + if (_dsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _dsaKey.VerifySignature(rgbHash, rgbSignature); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs new file mode 100644 index 000000000000..67a5541dbdd5 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.Cryptography; + +namespace System.Security.Cryptography +{ + public class DSASignatureFormatter : AsymmetricSignatureFormatter + { + DSA _dsaKey; + string _algName; + + public DSASignatureFormatter() + { + // The hash algorithm default is SHA1 + _algName = HashAlgorithmNames.SHA1; + } + + public DSASignatureFormatter(AsymmetricAlgorithm key) : this() + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _dsaKey = (DSA)key; + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _dsaKey = (DSA)key; + } + + public override void SetHashAlgorithm(string strName) + { + // The implemenation is symmetric with RSAPKCS1SignatureFormatter even though _algName + // is not used in VerifySignature. + try + { + // Verify the name + Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); + + // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. + _algName = strName; + } + catch (CryptographicException) + { + // For desktop compat there is no exception here + _algName = null; + } + } + + public override byte[] CreateSignature(byte[] rgbHash) + { + if (rgbHash == null) + throw new ArgumentNullException(nameof(rgbHash)); + if (_algName == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingOID); + if (_dsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _dsaKey.CreateSignature(rgbHash); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs new file mode 100644 index 000000000000..27ad1b9a8e05 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAOAEPKeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter + { + private RSA _rsaKey; + + public RSAOAEPKeyExchangeDeformatter() { } + public RSAOAEPKeyExchangeDeformatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override string Parameters + { + get + { + return null; + } + set + { + } + } + + public override byte[] DecryptKeyExchange(byte[] rgbData) + { + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.Decrypt(rgbData, RSAEncryptionPadding.OaepSHA1); + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs new file mode 100644 index 000000000000..a39a7380e801 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAOAEPKeyExchangeFormatter : AsymmetricKeyExchangeFormatter + { + private byte[] ParameterValue; + private RSA _rsaKey; + + public RSAOAEPKeyExchangeFormatter() { } + public RSAOAEPKeyExchangeFormatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + /// + public byte[] Parameter + { + get + { + if (ParameterValue != null) + { + return (byte[])ParameterValue.Clone(); + } + + return null; + } + set + { + if (value != null) + { + ParameterValue = (byte[])value.Clone(); + } + else + { + ParameterValue = null; + } + } + } + + /// + public override string Parameters + { + get + { + return null; + } + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override byte[] CreateKeyExchange(byte[] rgbData, Type symAlgType) + { + return CreateKeyExchange(rgbData); + } + + public override byte[] CreateKeyExchange(byte[] rgbData) + { + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.Encrypt(rgbData, RSAEncryptionPadding.OaepSHA1); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs new file mode 100644 index 000000000000..1d6e410f5230 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAPKCS1KeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter + { + RSA _rsaKey; + + public RSAPKCS1KeyExchangeDeformatter() { } + + public RSAPKCS1KeyExchangeDeformatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override string Parameters + { + get + { + return null; + } + set + { + } + } + + public override byte[] DecryptKeyExchange(byte[] rgbIn) + { + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.Decrypt(rgbIn, RSAEncryptionPadding.Pkcs1); + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs new file mode 100644 index 000000000000..9cdf9ce0c05e --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAPKCS1KeyExchangeFormatter : AsymmetricKeyExchangeFormatter + { + RSA _rsaKey; + + public RSAPKCS1KeyExchangeFormatter() { } + + public RSAPKCS1KeyExchangeFormatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override string Parameters + { + get + { + return ""; + } + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override byte[] CreateKeyExchange(byte[] rgbData, Type symAlgType) + { + return CreateKeyExchange(rgbData); + } + + public override byte[] CreateKeyExchange(byte[] rgbData) + { + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.Encrypt(rgbData, RSAEncryptionPadding.Pkcs1); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs new file mode 100644 index 000000000000..1a6decdb723a --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAPKCS1SignatureDeformatter : AsymmetricSignatureDeformatter + { + private RSA _rsaKey; + private string _algName; + + public RSAPKCS1SignatureDeformatter() { } + public RSAPKCS1SignatureDeformatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override void SetHashAlgorithm(string strName) + { + try + { + // Verify the name + Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); + + // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. + _algName = strName; + } + catch (CryptographicException) + { + // For desktop compat there is no exception here + _algName = null; + } + } + + public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) + { + if (rgbHash == null) + throw new ArgumentNullException(nameof(rgbHash)); + if (rgbSignature == null) + throw new ArgumentNullException(nameof(rgbSignature)); + if (_algName == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingOID); + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.VerifyHash(rgbHash, rgbSignature, new HashAlgorithmName(_algName), RSASignaturePadding.Pkcs1); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs new file mode 100644 index 000000000000..f38e82f4446f --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Security.Cryptography +{ + public class RSAPKCS1SignatureFormatter : AsymmetricSignatureFormatter + { + private RSA _rsaKey; + private string _algName; + + public RSAPKCS1SignatureFormatter() { } + + public RSAPKCS1SignatureFormatter(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override void SetKey(AsymmetricAlgorithm key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + _rsaKey = (RSA)key; + } + + public override void SetHashAlgorithm(string strName) + { + try + { + // Verify the name + Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); + + // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. + _algName = strName; + } + catch (CryptographicException) + { + // For desktop compat there is no exception here + _algName = null; + } + } + + public override byte[] CreateSignature(byte[] rgbHash) + { + if (rgbHash == null) + throw new ArgumentNullException(nameof(rgbHash)); + if (_algName == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingOID); + if (_rsaKey == null) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); + + return _rsaKey.SignHash(rgbHash, new HashAlgorithmName(_algName), RSASignaturePadding.Pkcs1); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs index 724af7140640..ec8cae5d2650 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs @@ -17,7 +17,7 @@ protected void Verify(string input, string output) Verify(ByteUtils.AsciiBytes(input), output); } - protected void Verify(Stream input, string output) + private void VerifyComputeHashStream(Stream input, string output) { byte[] expected = ByteUtils.HexToByteArray(output); byte[] actual; @@ -31,6 +31,29 @@ protected void Verify(Stream input, string output) Assert.Equal(expected, actual); } +#if netstandard17 + private void VerifyICryptoTransformStream(Stream input, string output) + { + byte[] expected = ByteUtils.HexToByteArray(output); + byte[] actual; + + using (HashAlgorithm hash = Create()) + using (CryptoStream cryptoStream = new CryptoStream(input, hash, CryptoStreamMode.Read)) + { + byte[] buffer = new byte[1024]; // A different default than HashAlgorithm which uses 4K + int bytesRead; + while ((bytesRead = cryptoStream.Read(buffer, 0, buffer.Length)) > 0) + { + // CryptoStream will build up the hash + } + + actual = hash.Hash; + } + + Assert.Equal(expected, actual); + } +#endif + protected void Verify(byte[] input, string output) { byte[] expected = ByteUtils.HexToByteArray(output); @@ -40,17 +63,29 @@ protected void Verify(byte[] input, string output) { Assert.True(hash.HashSize > 0); actual = hash.ComputeHash(input, 0, input.Length); - } - Assert.Equal(expected, actual); + Assert.Equal(expected, actual); + +#if netstandard17 + actual = hash.Hash; + Assert.Equal(expected, actual); +#endif + } } protected void VerifyRepeating(string input, int repeatCount, string output) { using (Stream stream = new DataRepeatingStream(input, repeatCount)) { - Verify(stream, output); + VerifyComputeHashStream(stream, output); + } + +#if netstandard17 + using (Stream stream = new DataRepeatingStream(input, repeatCount)) + { + VerifyICryptoTransformStream(stream, output); } +#endif } [Fact] @@ -166,14 +201,14 @@ public override int Read(byte[] buffer, int offset, int count) throw new NotSupportedException(); } - if (count < _data.Length) + if (_remaining == 0) { - throw new InvalidOperationException(); + return 0; } - if (_remaining == 0) + if (count < _data.Length) { - return 0; + throw new InvalidOperationException(); } int multiple = count / _data.Length; diff --git a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 7a76e09f8cac..7039fff758a3 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -10,7 +10,8 @@ Library System.Security.Cryptography.Algorithms.Tests System.Security.Cryptography.Algorithms.Tests - .NETStandard,Version=v1.7 + $(DefineConstants);netstandard17 + .NETStandard,Version=v1.6 @@ -131,11 +132,20 @@ - + + + CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs + + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignatureFormatter.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAImportExport.cs @@ -148,10 +158,22 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSATestData.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyExchangeFormatter.cs + + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSASignatureFormatter.cs + + + + + System.Security.Cryptography.Primitives + + diff --git a/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj b/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj index 324376f62dc2..64dcfa8ff325 100644 --- a/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj +++ b/src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.csproj @@ -7,6 +7,15 @@ + + + + System.Security.Cryptography.Algorithms + + + + System.Security.Cryptography.Primitives + diff --git a/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj b/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj index a41b2459de00..9801bb6a5446 100644 --- a/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj +++ b/src/System.Security.Cryptography.Cng/src/System.Security.Cryptography.Cng.csproj @@ -254,6 +254,16 @@ Common\System\Security\Cryptography\RSACng.SignVerify.cs + + + + System.Security.Cryptography.Algorithms + + + + System.Security.Cryptography.Primitives + + diff --git a/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 6d9a0af35cb3..5a2a77bdbfd4 100644 --- a/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -130,6 +130,12 @@ + + CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs + + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs @@ -139,15 +145,34 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyGeneration.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignatureFormatter.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignVerify.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSATestData.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyExchangeFormatter.cs + + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSASignatureFormatter.cs + + + + + System.Security.Cryptography.Primitives + + + + System.Security.Cryptography.Algorithms + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.csproj b/src/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.csproj index 0e5bc01cf7de..f7dc09c46613 100644 --- a/src/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.csproj +++ b/src/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.csproj @@ -7,6 +7,15 @@ + + + + System.Security.Cryptography.Primitives + + + + System.Security.Cryptography.Algorithms + diff --git a/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj b/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj index e7c160d21927..2dd5e06c69c0 100644 --- a/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj +++ b/src/System.Security.Cryptography.Csp/src/System.Security.Cryptography.Csp.csproj @@ -48,5 +48,15 @@ + + + + System.Security.Cryptography.Primitives + + + + System.Security.Cryptography.Algorithms + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index 40e71f49e2bd..dab9da320e34 100644 --- a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -55,6 +55,12 @@ + + CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs + + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs @@ -64,15 +70,34 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyGeneration.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignatureFormatter.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignVerify.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSATestData.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyExchangeFormatter.cs + + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSASignatureFormatter.cs + + + + + System.Security.Cryptography.Primitives + + + + System.Security.Cryptography.Algorithms + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj index 476459f0fd61..f69e524598f3 100644 --- a/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj +++ b/src/System.Security.Cryptography.OpenSsl/ref/System.Security.Cryptography.OpenSsl.csproj @@ -7,6 +7,15 @@ + + + + System.Security.Cryptography.Algorithms + + + + System.Security.Cryptography.Primitives + diff --git a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj index 4dd4e11b39e7..d0df5a375621 100644 --- a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj +++ b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj @@ -113,5 +113,15 @@ + + + + System.Security.Cryptography.Primitives + + + + System.Security.Cryptography.Algorithms + + \ No newline at end of file diff --git a/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index 4f9847f66e2b..424a010144d1 100644 --- a/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -81,6 +81,12 @@ + + CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs + + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs @@ -90,12 +96,21 @@ CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAKeyGeneration.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignatureFormatter.cs + CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSASignVerify.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSATestData.cs + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSAKeyExchangeFormatter.cs + + + CommonTest\System\Security\Cryptography\AlgorithmImplementations\RSA\RSASignatureFormatter.cs + @@ -105,6 +120,14 @@ {78452f3e-ba91-47e7-bb0f-02e8a5c116c4} System.Security.Cryptography.OpenSsl + + + System.Security.Cryptography.Algorithms + + + + System.Security.Cryptography.Primitives + \ No newline at end of file diff --git a/src/System.Security.Cryptography.Primitives/dir.props b/src/System.Security.Cryptography.Primitives/dir.props index 38aaf93ed7aa..0769c12aaa28 100644 --- a/src/System.Security.Cryptography.Primitives/dir.props +++ b/src/System.Security.Cryptography.Primitives/dir.props @@ -1,7 +1,7 @@ - 4.0.1.0 + 4.1.0.0 diff --git a/src/System.Security.Cryptography.Primitives/pkg/System.Security.Cryptography.Primitives.pkgproj b/src/System.Security.Cryptography.Primitives/pkg/System.Security.Cryptography.Primitives.pkgproj index d521c7e4b3d5..251b75c079d0 100644 --- a/src/System.Security.Cryptography.Primitives/pkg/System.Security.Cryptography.Primitives.pkgproj +++ b/src/System.Security.Cryptography.Primitives/pkg/System.Security.Cryptography.Primitives.pkgproj @@ -3,7 +3,7 @@ - net46;netcore50;netcoreapp1.0;$(AllXamarinFrameworks) + net463;netcoreapp1.1;$(AllXamarinFrameworks) diff --git a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 0e706800632e..75e2ed620e03 100644 --- a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -10,13 +10,13 @@ namespace System.Security.Cryptography { public abstract partial class AsymmetricAlgorithm : System.IDisposable { + protected int KeySizeValue; + protected System.Security.Cryptography.KeySizes[] LegalKeySizesValue; protected AsymmetricAlgorithm() { } public virtual int KeySize { get { return default(int); } set { } } public virtual System.Security.Cryptography.KeySizes[] LegalKeySizes { get { return default(System.Security.Cryptography.KeySizes[]); } } public void Dispose() { } protected virtual void Dispose(bool disposing) { } - protected int KeySizeValue; - protected KeySizes[] LegalKeySizesValue; } public enum CipherMode { @@ -32,6 +32,13 @@ public CryptographicException(string message) { } public CryptographicException(string message, System.Exception inner) { } public CryptographicException(string format, string insert) { } } + public partial class CryptographicUnexpectedOperationException : System.Security.Cryptography.CryptographicException + { + public CryptographicUnexpectedOperationException() { } + public CryptographicUnexpectedOperationException(string message) { } + public CryptographicUnexpectedOperationException(string message, System.Exception inner) { } + public CryptographicUnexpectedOperationException(string format, string insert) { } + } public partial class CryptoStream : System.IO.Stream, System.IDisposable { public CryptoStream(System.IO.Stream stream, System.Security.Cryptography.ICryptoTransform transform, System.Security.Cryptography.CryptoStreamMode mode) { } @@ -43,24 +50,32 @@ public CryptoStream(System.IO.Stream stream, System.Security.Cryptography.ICrypt public override long Position { get { return default(long); } set { } } protected override void Dispose(bool disposing) { } public override void Flush() { } - public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { return default(System.Threading.Tasks.Task); } public void FlushFinalBlock() { } - public override int Read(byte[] buffer, int offset, int count) { buffer = default(byte[]); return default(int); } + public override int Read(byte[] buffer, int offset, int count) { return default(int); } public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { return default(System.Threading.Tasks.Task); } + public override int ReadByte() { return default(int); } public override long Seek(long offset, System.IO.SeekOrigin origin) { return default(long); } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { return default(System.Threading.Tasks.Task); } + public override void WriteByte(byte value) { } } public enum CryptoStreamMode { Read = 0, Write = 1, } - public abstract partial class HashAlgorithm : System.IDisposable + public abstract partial class HashAlgorithm : System.IDisposable, System.Security.Cryptography.ICryptoTransform { + protected internal byte[] HashValue; + protected int State; protected HashAlgorithm() { } + public virtual bool CanReuseTransform { get { return default(bool); } } + public virtual bool CanTransformMultipleBlocks { get { return default(bool); } } + public virtual byte[] Hash { get { return default(byte[]); } } public virtual int HashSize { get { return default(int); } } + public virtual int InputBlockSize { get { return default(int); } } + public virtual int OutputBlockSize { get { return default(int); } } public byte[] ComputeHash(byte[] buffer) { return default(byte[]); } public byte[] ComputeHash(byte[] buffer, int offset, int count) { return default(byte[]); } public byte[] ComputeHash(System.IO.Stream inputStream) { return default(byte[]); } @@ -69,6 +84,8 @@ protected virtual void Dispose(bool disposing) { } protected abstract void HashCore(byte[] array, int ibStart, int cbSize); protected abstract byte[] HashFinal(); public abstract void Initialize(); + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { return default(int); } + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { return default(byte[]); } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public partial struct HashAlgorithmName : System.IEquatable @@ -108,6 +125,7 @@ public partial interface ICryptoTransform : System.IDisposable } public abstract partial class KeyedHashAlgorithm : System.Security.Cryptography.HashAlgorithm { + protected byte[] KeyValue; protected KeyedHashAlgorithm() { } public virtual byte[] Key { get { return default(byte[]); } set { } } protected override void Dispose(bool disposing) { } @@ -127,6 +145,14 @@ public enum PaddingMode } public abstract partial class SymmetricAlgorithm : System.IDisposable { + protected int BlockSizeValue; + protected byte[] IVValue; + protected int KeySizeValue; + protected byte[] KeyValue; + protected System.Security.Cryptography.KeySizes[] LegalBlockSizesValue; + protected System.Security.Cryptography.KeySizes[] LegalKeySizesValue; + protected System.Security.Cryptography.CipherMode ModeValue; + protected System.Security.Cryptography.PaddingMode PaddingValue; protected SymmetricAlgorithm() { } public virtual int BlockSize { get { return default(int); } set { } } public virtual byte[] IV { get { return default(byte[]); } set { } } @@ -144,13 +170,5 @@ public void Dispose() { } protected virtual void Dispose(bool disposing) { } public abstract void GenerateIV(); public abstract void GenerateKey(); - protected CipherMode ModeValue; - protected PaddingMode PaddingValue; - protected byte[] KeyValue; - protected byte[] IVValue; - protected int BlockSizeValue; - protected int KeySizeValue; - protected KeySizes[] LegalBlockSizesValue; - protected KeySizes[] LegalKeySizesValue; } } diff --git a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.csproj b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.csproj index 167d83a16ab2..980ed1f40ad2 100644 --- a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.csproj +++ b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.csproj @@ -3,7 +3,7 @@ Library - .NETStandard,Version=v1.3 + .NETStandard,Version=v1.7 diff --git a/src/System.Security.Cryptography.Primitives/ref/project.json b/src/System.Security.Cryptography.Primitives/ref/project.json index 39a6433e4d04..ccc9bc9dacaa 100644 --- a/src/System.Security.Cryptography.Primitives/ref/project.json +++ b/src/System.Security.Cryptography.Primitives/ref/project.json @@ -1,13 +1,13 @@ { "dependencies": { - "System.Runtime": "4.0.0", - "System.IO": "4.0.0", - "System.Threading.Tasks": "4.0.0" + "System.Runtime": "4.3.0-beta-24601-02", + "System.IO": "4.3.0-beta-24601-02", + "System.Threading.Tasks": "4.3.0-beta-24601-02" }, "frameworks": { - "netstandard1.3": { + "netstandard1.7": { "imports": [ - "dotnet5.4" + "dotnet5.8" ] } } diff --git a/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx b/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx index ef85e54b5a46..f30e6a7bdf8d 100644 --- a/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx +++ b/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx @@ -138,6 +138,9 @@ FlushFinalBlock() method was called twice on a CryptoStream. It can only be called once. + + Hash must be finalized before the hash value is retrieved. + Specified block size is not valid for this algorithm. diff --git a/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.builds b/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.builds index f8f1674accbc..f8ea0bace7d8 100644 --- a/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.builds +++ b/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.builds @@ -4,7 +4,7 @@ - net46 + net463 diff --git a/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj b/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj index 60c57d16dd19..b1692e380274 100644 --- a/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj +++ b/src/System.Security.Cryptography.Primitives/src/System.Security.Cryptography.Primitives.csproj @@ -4,18 +4,19 @@ {DF73E985-E143-4BF5-9FA4-E199E7D36235} System.Security.Cryptography.Primitives - true - None - .NETStandard,Version=v1.3 + true + None + .NETStandard,Version=v1.7 - + + @@ -31,7 +32,7 @@ Common\System\Threading\Tasks\ForceAsyncAwaiter.cs - + diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs new file mode 100644 index 000000000000..92073c95e197 --- /dev/null +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; + +namespace System.Security.Cryptography +{ + public class CryptographicUnexpectedOperationException : CryptographicException + { + public CryptographicUnexpectedOperationException() + : base(SR.Arg_CryptographyException) + { + } + + public CryptographicUnexpectedOperationException(string message) + : base(message) + { + } + + public CryptographicUnexpectedOperationException(string message, Exception inner) + : base(message, inner) + { + } + + public CryptographicUnexpectedOperationException(string format, string insert) + : base(string.Format(CultureInfo.CurrentCulture, format, insert)) + { + } + } +} diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index 7a14b5d4a383..e38906b2bafb 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.IO; -using System.Diagnostics; namespace System.Security.Cryptography { - public abstract class HashAlgorithm : IDisposable + public abstract class HashAlgorithm : IDisposable, ICryptoTransform { protected HashAlgorithm() { @@ -22,6 +20,19 @@ public virtual int HashSize } } + public virtual byte[] Hash + { + get + { + if (_disposed) + throw new ObjectDisposedException(null); + if (State != 0) + throw new CryptographicUnexpectedOperationException(SR.Cryptography_HashNotYetFinalized); + + return (byte[])HashValue.Clone(); + } + } + public byte[] ComputeHash(byte[] buffer) { if (_disposed) @@ -68,12 +79,12 @@ public byte[] ComputeHash(Stream inputStream) private byte[] CaptureHashCodeAndReinitialize() { - byte[] hashValue = HashFinal(); + HashValue = HashFinal(); // Clone the hash value prior to invoking Initialize in case the user-defined Initialize // manipulates the array. - hashValue = hashValue.CloneByteArray(); + byte[] tmp = (byte[])HashValue.Clone(); Initialize(); - return hashValue; + return tmp; } public void Dispose() @@ -94,11 +105,92 @@ protected virtual void Dispose(bool disposing) return; } + // ICryptoTransform methods + + // we assume any HashAlgorithm can take input a byte at a time + public virtual int InputBlockSize + { + get { return (1); } + } + + public virtual int OutputBlockSize + { + get { return (1); } + } + + public virtual bool CanTransformMultipleBlocks + { + get { return true; } + } + + public virtual bool CanReuseTransform + { + get { return true; } + } + + public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + // Do some validation, we let BlockCopy do the destination array validation + if (inputBuffer == null) + throw new ArgumentNullException(nameof(inputBuffer)); + if (inputOffset < 0) + throw new ArgumentOutOfRangeException(nameof(inputOffset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (inputCount < 0 || inputCount > inputBuffer.Length) + throw new ArgumentException(SR.Argument_InvalidValue); + if ((inputBuffer.Length - inputCount) < inputOffset) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_disposed) + throw new ObjectDisposedException(null); + + // Change the State value + State = 1; + + HashCore(inputBuffer, inputOffset, inputCount); + if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))) + Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); + return inputCount; + } + + public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + if (inputBuffer == null) + throw new ArgumentNullException(nameof(inputBuffer)); + if (inputOffset < 0) + throw new ArgumentOutOfRangeException(nameof(inputOffset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (inputCount < 0 || inputCount > inputBuffer.Length) + throw new ArgumentException(SR.Argument_InvalidValue); + if ((inputBuffer.Length - inputCount) < inputOffset) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_disposed) + throw new ObjectDisposedException(null); + + HashCore(inputBuffer, inputOffset, inputCount); + HashValue = HashFinal(); + byte[] outputBytes; + if (inputCount != 0) + { + outputBytes = new byte[inputCount]; + Buffer.BlockCopy(inputBuffer, inputOffset, outputBytes, 0, inputCount); + } + else + { + outputBytes = new byte[0]; + } + + // reset the State value + State = 0; + + return outputBytes; + } + protected abstract void HashCore(byte[] array, int ibStart, int cbSize); protected abstract byte[] HashFinal(); public abstract void Initialize(); private bool _disposed; + protected internal byte[] HashValue; + protected int State = 0; } } - diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/KeyedHashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/KeyedHashAlgorithm.cs index 07d976ce428e..293f26d0d888 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/KeyedHashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/KeyedHashAlgorithm.cs @@ -17,12 +17,12 @@ public virtual byte[] Key { get { - return _key.CloneByteArray(); + return KeyValue.CloneByteArray(); } set { - _key = value.CloneByteArray(); + KeyValue = value.CloneByteArray(); } } @@ -31,16 +31,16 @@ protected override void Dispose(bool disposing) // For keyed hash algorithms, we always want to zero out the key value if (disposing) { - if (_key != null) + if (KeyValue != null) { - Array.Clear(_key, 0, _key.Length); + Array.Clear(KeyValue, 0, KeyValue.Length); } - _key = null; + KeyValue = null; } base.Dispose(disposing); } - private byte[] _key; + protected byte[] KeyValue; } } diff --git a/src/System.Security.Cryptography.Primitives/src/project.json b/src/System.Security.Cryptography.Primitives/src/project.json index 56a07084eb12..36057d4272f4 100644 --- a/src/System.Security.Cryptography.Primitives/src/project.json +++ b/src/System.Security.Cryptography.Primitives/src/project.json @@ -1,20 +1,21 @@ { "frameworks": { - "netstandard1.3": { + "netstandard1.7": { "dependencies": { - "System.Diagnostics.Contracts": "4.0.0", - "System.Diagnostics.Debug": "4.0.0", - "System.IO": "4.0.10", - "System.Resources.ResourceManager": "4.0.0", - "System.Runtime": "4.0.20", - "System.Threading": "4.0.0", - "System.Threading.Tasks": "4.0.0" + "System.Diagnostics.Contracts": "4.3.0-beta-24601-02", + "System.Diagnostics.Debug": "4.3.0-beta-24601-02", + "System.Globalization": "4.3.0-beta-24601-02", + "System.IO": "4.3.0-beta-24601-02", + "System.Resources.ResourceManager": "4.3.0-beta-24601-02", + "System.Runtime": "4.3.0-beta-24601-02", + "System.Threading": "4.3.0-beta-24601-02", + "System.Threading.Tasks": "4.3.0-beta-24601-02" }, "imports": [ - "dotnet5.4" + "dotnet5.8" ] }, - "net46": { + "net463": { "dependencies": { "Microsoft.TargetingPack.NETFramework.v4.6": "1.0.1" } diff --git a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.builds b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.builds index f368a64352ba..368e8141fad8 100644 --- a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.builds +++ b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.builds @@ -4,7 +4,8 @@ - netcore50;net46 + netstandard1.7 + netcoreapp1.1 Windows_NT diff --git a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj index f785e6f0ebca..f0bc26ca2bfe 100644 --- a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj +++ b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj @@ -9,7 +9,8 @@ Library System.Security.Cryptography.Primitives.Tests System.Security.Cryptography.Primitives.Tests - .NETStandard,Version=v1.3 + .NETStandard,Version=v1.7 + .NETStandard,Version=v1.3 diff --git a/src/System.Security.Cryptography.Primitives/tests/project.json b/src/System.Security.Cryptography.Primitives/tests/project.json index 91e310fbcb26..8667a1e15f73 100644 --- a/src/System.Security.Cryptography.Primitives/tests/project.json +++ b/src/System.Security.Cryptography.Primitives/tests/project.json @@ -5,6 +5,7 @@ "System.ObjectModel": "4.3.0-beta-24601-02", "System.Runtime": "4.3.0-beta-24601-02", "System.Runtime.Extensions": "4.3.0-beta-24601-02", + "System.Text.Encoding": "4.3.0-beta-24601-02", "System.Text.RegularExpressions": "4.3.0-beta-24601-02", "test-runtime": { "target": "project", @@ -14,7 +15,8 @@ "Microsoft.DotNet.BuildTools.TestSuite": "1.0.0-prerelease-00830-02" }, "frameworks": { - "netstandard1.3": {} + "netstandard1.3": {}, + "netstandard1.7": {} }, "supports": { "coreFx.Test.netcore50": {}, @@ -22,6 +24,22 @@ "coreFx.Test.net46": {}, "coreFx.Test.net461": {}, "coreFx.Test.net462": {}, - "coreFx.Test.net463": {} + "coreFx.Test.net463": {}, + "coreFx.Test.netcoreapp1.1-ns17": { + "netstandard1.7": [ + "win7-x86", + "win7-x64", + "win10-arm64", + "osx.10.10-x64", + "centos.7-x64", + "debian.8-x64", + "rhel.7-x64", + "ubuntu.14.04-x64", + "ubuntu.16.04-x64", + "fedora.23-x64", + "linux-x64", + "opensuse.13.2-x64" + ] + } } } From 54e4e14f74bbb980d7d26ca245a6234b70cc6ac0 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 23 Sep 2016 16:37:25 -0500 Subject: [PATCH 2/7] Code review feedback --- .../DSA/DSASignVerify.cs | 40 ++---------- .../DSA/DSASignatureFormatter.cs | 52 ++++++++++----- .../DSA/DSATestData.cs | 34 ++++++++++ .../RSA/RSAKeyExchangeFormatter.cs | 48 +++++--------- .../RSA/RSASignatureFormatter.cs | 40 +++++++----- .../AsymmetricKeyExchangeFormatterTests.cs | 36 ----------- .../AsymmetricSignatureFormatter.cs | 48 ++++++++++++++ .../AsymmetricSignatureFormatterTests.cs | 63 ------------------- .../Cryptography/HashAlgorithmNames.cs | 17 ++++- .../AsymmetricSignatureDeformatter.cs | 2 +- .../AsymmetricSignatureFormatter.cs | 2 +- .../Cryptography/DSASignatureDeformatter.cs | 22 ++----- .../Cryptography/DSASignatureFormatter.cs | 24 ++----- .../RSAPKCS1KeyExchangeDeformatter.cs | 11 +--- .../RSAPKCS1KeyExchangeFormatter.cs | 2 +- .../RSAPKCS1SignatureDeformatter.cs | 8 ++- .../RSAPKCS1SignatureFormatter.cs | 8 ++- .../AsymmetricSignatureFormatterTests.cs | 28 +++++++++ .../tests/DSASignatureFormatterTests.cs | 24 +++++++ .../tests/RSAKeyExchangeFormatterTests.cs | 47 ++++++++++++++ .../tests/RSASignatureFormatterTests.cs | 24 +++++++ ...urity.Cryptography.Algorithms.Tests.csproj | 11 ++-- ...tem.Security.Cryptography.Cng.Tests.csproj | 7 +-- ...tem.Security.Cryptography.Csp.Tests.csproj | 7 +-- ...Security.Cryptography.OpenSsl.Tests.csproj | 7 +-- .../Security/Cryptography/HashAlgorithm.cs | 10 ++- 26 files changed, 345 insertions(+), 277 deletions(-) delete mode 100644 src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs create mode 100644 src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs delete mode 100644 src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs create mode 100644 src/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs create mode 100644 src/System.Security.Cryptography.Algorithms/tests/DSASignatureFormatterTests.cs create mode 100644 src/System.Security.Cryptography.Algorithms/tests/RSAKeyExchangeFormatterTests.cs create mode 100644 src/System.Security.Cryptography.Algorithms/tests/RSASignatureFormatterTests.cs diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs index 3c422acbbcc0..e4766bdbf7fd 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs @@ -50,49 +50,17 @@ public static void SignAndVerifyDataExplicit2048() [Fact] public static void VerifyKnownSignature() { - // The parameters and signature come from FIPS 186-2 APPENDIX 5. EXAMPLE OF THE DSA using (DSA dsa = DSAFactory.Create()) { - DSAParameters dsaParameters = new DSAParameters - { - P = ( - "8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7" + - "cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac" + - "49693dfbf83724c2ec0736ee31c80291").HexToByteArray(), - - Q = ("c773218c737ec8ee993b4f2ded30f48edace915f").HexToByteArray(), - - G = ( - "626d027839ea0a13413163a55b4cb500299d5522956cefcb" + - "3bff10f399ce2c2e71cb9de5fa24babf58e5b79521925c9c" + - "c42e9f6f464b088cc572af53e6d78802").HexToByteArray(), - - X = ("2070b3223dba372fde1c0ffc7b2e3b498b260614").HexToByteArray(), - - Y = ( - "19131871d75b1612a819f29d78d1b0d7346f7aa77bb62a85" + - "9bfd6c5675da9d212d3a36ef1672ef660b8c7c255cc0ec74" + - "858fba33f44c06699630a76b030ee333").HexToByteArray(), - }; - - byte[] signature = ( - // r - "8bac1ab66410435cb7181f95b16ab97c92b341c0" + - // s - "41e2345f1f56df2458f426d155b4ba2db6dcd8c8" - ).HexToByteArray(); - - byte[] data = Encoding.ASCII.GetBytes("abc"); - + DSAParameters dsaParameters = DSATestData.GetDSA1024_186_2_Params(); dsa.ImportParameters(dsaParameters); + byte[] data = DSATestData.GetDSA1024_186_2_Data(); + byte[] signature = DSATestData.GetDSA1024_186_2_Signature(); Assert.True(dsa.VerifyData(data, signature, HashAlgorithmName.SHA1)); // Negative case - unchecked - { - --signature[signature.Length - 1]; - } + signature[signature.Length - 1] ^= 0xff; Assert.False(dsa.VerifyData(data, signature, HashAlgorithmName.SHA1)); } } diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs index d331135f9fff..85319683690a 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs @@ -7,40 +7,64 @@ namespace System.Security.Cryptography.Dsa.Tests { - public class DSAFormatterTests + public partial class DSASignatureFormatterTests : AsymmetricSignatureFormatterTests { [Fact] - public static void FormatterArguments() + public static void VerifySignature_SHA1() { - AsymmetricSignatureFormatterTests.FormatterArguments(new DSASignatureFormatter()); + using (DSA dsa = DSAFactory.Create()) + { + var formatter = new DSASignatureFormatter(dsa); + var deformatter = new DSASignatureDeformatter(dsa); + VerifySignature(formatter, deformatter, SHA1.Create(), "SHA1"); + VerifySignature(formatter, deformatter, SHA1.Create(), "sha1"); + } } - [Fact] - public static void DeformatterArguments() + [ConditionalFact(nameof(SupportsFips186_3))] + public static void VerifySignature_SHA256() { - AsymmetricSignatureFormatterTests.DeformatterArguments(new DSASignatureDeformatter()); + using (DSA dsa = DSAFactory.Create()) + { + var formatter = new DSASignatureFormatter(dsa); + var deformatter = new DSASignatureDeformatter(dsa); + VerifySignature(formatter, deformatter, SHA256.Create(), "SHA256"); + VerifySignature(formatter, deformatter, SHA256.Create(), "sha256"); + } } [Fact] - public static void VerifySignature_SHA1() + public static void InvalidHashAlgorithm() { using (DSA dsa = DSAFactory.Create()) { - var formatter = new DSASignatureFormatter(dsa); var deformatter = new DSASignatureDeformatter(dsa); - AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA1.Create(), HashAlgorithmName.SHA1); + + // Unlike RSA, DSA will throw during SetHashAlgorithm + Assert.Throws(() => + formatter.SetHashAlgorithm("INVALIDVALUE")); + Assert.Throws(() => + deformatter.SetHashAlgorithm("INVALIDVALUE")); } } - [ConditionalFact(nameof(SupportsFips186_3))] - public static void VerifySignature_SHA256() + [Fact] + public static void VerifyKnownSignature() { using (DSA dsa = DSAFactory.Create()) { - var formatter = new DSASignatureFormatter(dsa); + byte[] hash = SHA1.Create().ComputeHash(DSATestData.GetDSA1024_186_2_Data()); + byte[] signature = DSATestData.GetDSA1024_186_2_Signature(); + + DSAParameters dsaParameters = DSATestData.GetDSA1024_186_2_Params(); + dsa.ImportParameters(dsaParameters); var deformatter = new DSASignatureDeformatter(dsa); - AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA256.Create(), HashAlgorithmName.SHA256); + deformatter.VerifySignature(hash, signature); + + // Negative case + signature[signature.Length - 1] ^= 0xff; + Assert.False(deformatter.VerifySignature(hash, signature)); } } @@ -52,4 +76,4 @@ public static bool SupportsFips186_3 } } } -} \ No newline at end of file +} diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs index 92c4fe35f6f1..bbf47c53bec8 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs @@ -67,5 +67,39 @@ internal static DSAParameters GetDSA2048Params() "081025751572").HexToByteArray(); return p; } + + // The parameters and signature come from FIPS 186-2 APPENDIX 5. EXAMPLE OF THE DSA + internal static DSAParameters GetDSA1024_186_2_Params() + { + return new DSAParameters() + { + P = ( + "8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac" + + "49693dfbf83724c2ec0736ee31c80291").HexToByteArray(), + Q = ("c773218c737ec8ee993b4f2ded30f48edace915f").HexToByteArray(), + G = ( + "626d027839ea0a13413163a55b4cb500299d5522956cefcb3bff10f399ce2c2e71cb9de5fa24babf58e5b79521925c9c" + + "c42e9f6f464b088cc572af53e6d78802").HexToByteArray(), + X = ("2070b3223dba372fde1c0ffc7b2e3b498b260614").HexToByteArray(), + Y = ( + "19131871d75b1612a819f29d78d1b0d7346f7aa77bb62a859bfd6c5675da9d212d3a36ef1672ef660b8c7c255cc0ec74" + + "858fba33f44c06699630a76b030ee333").HexToByteArray() + }; + } + + internal static byte[] GetDSA1024_186_2_Signature() + { + return ( + // r + "8bac1ab66410435cb7181f95b16ab97c92b341c0" + + // s + "41e2345f1f56df2458f426d155b4ba2db6dcd8c8" + ).HexToByteArray(); + } + + internal static byte[] GetDSA1024_186_2_Data() + { + return Encoding.ASCII.GetBytes("abc"); + } } } diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs index ce0c5a465c32..e17d5b9d4bdf 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs @@ -8,51 +8,27 @@ namespace System.Security.Cryptography.Rsa.Tests { - public class RSAKeyExchangeFormatterTests + public partial class RSAKeyExchangeFormatterTests { - [Fact] - public static void RSAOAEPFormatterArguments() - { - AsymmetricKeyExchangeFormatterTests.FormatterArguments(new RSAOAEPKeyExchangeFormatter()); - } - - [Fact] - public static void RSAOAEPDeformatterArguments() - { - AsymmetricKeyExchangeFormatterTests.DeformatterArguments(new RSAOAEPKeyExchangeDeformatter()); - } - - [Fact] - public static void RSAPKCS1FormatterArguments() - { - AsymmetricKeyExchangeFormatterTests.FormatterArguments(new RSAPKCS1KeyExchangeFormatter()); - } - - [Fact] - public static void RSAPKCS1DeformatterArguments() - { - AsymmetricKeyExchangeFormatterTests.DeformatterArguments(new RSAPKCS1KeyExchangeDeformatter()); - } - [Fact] public static void VerifyDecryptKeyExchangeOaep() { - using (RSA rsa = RSAFactory.Create(1024)) + using (RSA rsa = RSAFactory.Create()) { var formatter = new RSAOAEPKeyExchangeFormatter(rsa); var deformatter = new RSAOAEPKeyExchangeDeformatter(rsa); - AsymmetricKeyExchangeFormatterTests.VerifyDecryptKeyExchange(formatter, deformatter); + VerifyDecryptKeyExchange(formatter, deformatter); } } [Fact] public static void VerifyDecryptKeyExchangePkcs1() { - using (RSA rsa = RSAFactory.Create(1024)) + using (RSA rsa = RSAFactory.Create()) { var formatter = new RSAPKCS1KeyExchangeFormatter(rsa); var deformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); - AsymmetricKeyExchangeFormatterTests.VerifyDecryptKeyExchange(formatter, deformatter); + VerifyDecryptKeyExchange(formatter, deformatter); } } @@ -63,7 +39,7 @@ public static void TestKnownValueOaep() { rsa.ImportParameters(TestData.RSA1024Params); byte[] encrypted = - ("19134ffba4025a1c651120ca07258a46e005a327c3927f615465060734dc0339114cabfd13803288883abf9329296a3e3a5cb1587927" + ( "19134ffba4025a1c651120ca07258a46e005a327c3927f615465060734dc0339114cabfd13803288883abf9329296a3e3a5cb1587927" + "a6e8a2e736f0a756e342b4adb0f1de5bba9ba5faee30456fb7409678eb71a70185606eda3303d9425fbeb730ab7803bea50e208b563f" + "e9bfa97a8966deefb211a3bd6abe08cd15e0b927").HexToByteArray(); RSAOAEPKeyExchangeDeformatter deformatter = new RSAOAEPKeyExchangeDeformatter(rsa); @@ -80,7 +56,7 @@ public static void TestKnownValuePkcs1() { rsa.ImportParameters(TestData.RSA1024Params); byte[] encrypted = - ("7061adb87a8759f0a0dc6ece42f5b63bf186f845237c6b16bf824b303812486efbb8f5febb681902228a609d4330a6c21abf0fc0d271" + ( "7061adb87a8759f0a0dc6ece42f5b63bf186f845237c6b16bf824b303812486efbb8f5febb681902228a609d4330a6c21abf0fc0d271" + "ba63d1d0d9e486668270c2dbf73ab33055dfc0b797938557b99c0e9a535605c0a4bceefe5a37594732bb566ab026e4e8d5ce47d0967d" + "f1c66e7ee4d39d804f6d558670222d708f943eb0").HexToByteArray(); RSAPKCS1KeyExchangeDeformatter deformatter = new RSAPKCS1KeyExchangeDeformatter(rsa); @@ -89,5 +65,15 @@ public static void TestKnownValuePkcs1() Assert.Equal(expectedPlain, plain); } } + + private static void VerifyDecryptKeyExchange(AsymmetricKeyExchangeFormatter formatter, AsymmetricKeyExchangeDeformatter deformatter) + { + byte[] encrypted = formatter.CreateKeyExchange(TestData.HelloBytes); + byte[] decrypted = deformatter.DecryptKeyExchange(encrypted); + Assert.Equal(TestData.HelloBytes, decrypted); + + encrypted[encrypted.Length - 1] ^= 0xff; + Assert.ThrowsAny(() => deformatter.DecryptKeyExchange(encrypted)); + } } } diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs index d647c52deb2f..e229850c9b5a 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs @@ -8,39 +8,45 @@ namespace System.Security.Cryptography.Rsa.Tests { - public class RSASignatureFormatterTests + public partial class RSASignatureFormatterTests : AsymmetricSignatureFormatterTests { [Fact] - public static void FormatterArguments() - { - AsymmetricSignatureFormatterTests.FormatterArguments(new RSAPKCS1SignatureFormatter()); - } - - [Fact] - public static void DeformatterArguments() + public static void VerifySignature_SHA1() { - AsymmetricSignatureFormatterTests.DeformatterArguments(new RSAPKCS1SignatureDeformatter()); + using (RSA rsa = RSAFactory.Create()) + { + var formatter = new RSAPKCS1SignatureFormatter(rsa); + var deformatter = new RSAPKCS1SignatureDeformatter(rsa); + VerifySignature(formatter, deformatter, SHA1.Create(), "SHA1"); + VerifySignature(formatter, deformatter, SHA1.Create(), "sha1"); + } } [Fact] - public static void VerifySignature_SHA1() + public static void VerifySignature_SHA256() { - using (RSA rsa = RSAFactory.Create(1024)) + using (RSA rsa = RSAFactory.Create()) { var formatter = new RSAPKCS1SignatureFormatter(rsa); var deformatter = new RSAPKCS1SignatureDeformatter(rsa); - AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA1.Create(), HashAlgorithmName.SHA1); + VerifySignature(formatter, deformatter, SHA256.Create(), "SHA256"); + VerifySignature(formatter, deformatter, SHA256.Create(), "sha256"); } } [Fact] - public static void VerifySignature_SHA256() + public static void InvalidHashAlgorithm() { - using (RSA rsa = RSAFactory.Create(1024)) + using (RSA rsa = RSAFactory.Create()) { var formatter = new RSAPKCS1SignatureFormatter(rsa); var deformatter = new RSAPKCS1SignatureDeformatter(rsa); - AsymmetricSignatureFormatterTests.VerifySignature(formatter, deformatter, SHA256.Create(), HashAlgorithmName.SHA256); + + // Exception is deferred until VerifySignature + formatter.SetHashAlgorithm("INVALIDVALUE"); + deformatter.SetHashAlgorithm("INVALIDVALUE"); + Assert.Throws(() => + VerifySignature(formatter, deformatter, SHA1.Create(), "INVALIDVALUE")); } } @@ -50,7 +56,7 @@ public static void VerifyKnownSignature() byte[] hash = "012d161304fa0c6321221516415813022320620c".HexToByteArray(); byte[] sig; - using (RSA key = RSAFactory.Create(1024)) + using (RSA key = RSAFactory.Create()) { key.ImportParameters(TestData.RSA1024Params); RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key); @@ -64,7 +70,7 @@ public static void VerifyKnownSignature() Assert.Equal(expectedSig, sig); } - using (RSA key = RSAFactory.Create(1024)) + using (RSA key = RSAFactory.Create()) // Test against a different instance { key.ImportParameters(TestData.RSA1024Params); RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key); diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs deleted file mode 100644 index 5f96fab6dded..000000000000 --- a/src/Common/tests/System/Security/Cryptography/AsymmetricKeyExchangeFormatterTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Xunit; - -namespace System.Security.Cryptography.Tests -{ - public class AsymmetricKeyExchangeFormatterTests - { - public static readonly byte[] HelloBytes = new ASCIIEncoding().GetBytes("Hello"); - - public static void FormatterArguments(AsymmetricKeyExchangeFormatter formatter) - { - Assert.Throws(() => formatter.SetKey(null)); - Assert.Throws(() => formatter.CreateKeyExchange(new byte[] { 0, 1, 2, 3 })); - } - - public static void DeformatterArguments(AsymmetricKeyExchangeDeformatter deformatter) - { - Assert.Throws(() => deformatter.SetKey(null)); - Assert.Throws(() => deformatter.DecryptKeyExchange(new byte[] { 0, 1, 2 })); - } - - public static void VerifyDecryptKeyExchange(AsymmetricKeyExchangeFormatter formatter, AsymmetricKeyExchangeDeformatter deformatter) - { - byte[] encrypted = formatter.CreateKeyExchange(HelloBytes); - byte[] decrypted = deformatter.DecryptKeyExchange(encrypted); - Assert.Equal(HelloBytes, decrypted); - - unchecked { encrypted[encrypted.Length - 1]--; } - Assert.ThrowsAny(() => deformatter.DecryptKeyExchange(encrypted)); - } - } -} diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs new file mode 100644 index 000000000000..54be36c4128e --- /dev/null +++ b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + /// + /// Helper methods for DSASignatureFormatterTests and RSASignatureFormatterTests + /// + public partial class AsymmetricSignatureFormatterTests + { + private static readonly byte[] HelloBytes = new ASCIIEncoding().GetBytes("Hello"); + + protected static void VerifySignature(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm, string hashAlgorithmName) + { + formatter.SetHashAlgorithm(hashAlgorithmName); + deformatter.SetHashAlgorithm(hashAlgorithmName); + + byte[] hash = hashAlgorithm.ComputeHash(HelloBytes); + + VerifySignatureWithHashBytes(formatter, deformatter, hash); + + // Check that the hash is preserved from the ComputeHash above + VerifySignatureWithHashAlgorithm(formatter, deformatter, hashAlgorithm); + } + + private static void VerifySignatureWithHashBytes(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, byte[] hash) + { + byte[] signature = formatter.CreateSignature(hash); + Assert.True(deformatter.VerifySignature(hash, signature)); + + signature[signature.Length - 1] ^= 0xff; + Assert.False(deformatter.VerifySignature(hash, signature)); + } + + private static void VerifySignatureWithHashAlgorithm(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm) + { + byte[] signature = formatter.CreateSignature(hashAlgorithm); + Assert.True(deformatter.VerifySignature(hashAlgorithm, signature)); + + signature[signature.Length - 1] ^= 0xff; + Assert.False(deformatter.VerifySignature(hashAlgorithm, signature)); + } + } +} diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs deleted file mode 100644 index a13f8b9a5bae..000000000000 --- a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatterTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text; -using Xunit; - -namespace System.Security.Cryptography.Tests -{ - public class AsymmetricSignatureFormatterTests - { - public static readonly byte[] HelloBytes = new ASCIIEncoding().GetBytes("Hello"); - - public static void FormatterArguments(AsymmetricSignatureFormatter formatter) - { - Assert.Throws(() => formatter.SetKey(null)); - Assert.Throws(() => formatter.CreateSignature((byte[])null)); - Assert.Throws(() => formatter.CreateSignature(new byte[] { 0, 1, 2, 3 })); - } - - public static void DeformatterArguments(AsymmetricSignatureDeformatter deformatter) - { - Assert.Throws(() => deformatter.SetKey(null)); - Assert.Throws(() => deformatter.VerifySignature((byte[])null, new byte[] { 0, 1, 2 })); - Assert.Throws(() => deformatter.VerifySignature(new byte[] { 0, 1, 2 }, new byte[] { 0, 1, 2 })); - } - - public static void VerifySignature(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName) - { - formatter.SetHashAlgorithm(hashAlgorithmName.Name); - deformatter.SetHashAlgorithm(hashAlgorithmName.Name); - - byte[] hash = hashAlgorithm.ComputeHash(HelloBytes); - - VerifySignature_ComputeHash(formatter, deformatter, hash); - -#if netstandard17 - // Check that the hash is preserved from the ComputeHash above - VerifySignature_HashAlgorithm(formatter, deformatter, hashAlgorithm); -#endif - } - - private static void VerifySignature_ComputeHash(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, byte[] hash) - { - byte[] signature = formatter.CreateSignature(hash); - Assert.True(deformatter.VerifySignature(hash, signature)); - - unchecked { signature[signature.Length - 1]--; } - Assert.False(deformatter.VerifySignature(hash, signature)); - } - -#if netstandard17 - private static void VerifySignature_HashAlgorithm(AsymmetricSignatureFormatter formatter, AsymmetricSignatureDeformatter deformatter, HashAlgorithm hashAlgorithm) - { - byte[] signature = formatter.CreateSignature(hashAlgorithm); - Assert.True(deformatter.VerifySignature(hashAlgorithm, signature)); - - unchecked { signature[signature.Length - 1]--; } - Assert.False(deformatter.VerifySignature(hashAlgorithm, signature)); - } -#endif - } -} diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs index a3cdda1f5510..209dea98343b 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Security.Cryptography; namespace Internal.Cryptography @@ -15,10 +16,12 @@ internal static class HashAlgorithmNames public const string SHA384 = "SHA384"; public const string SHA512 = "SHA512"; + private const string ALL_NAMES = "MD5 SHA1 SHA256 SHA384 SHA512"; + /// /// Map HashAlgorithm type to string; desktop uses CryptoConfig functionality. /// - public static string ToAlgorithmName(HashAlgorithm hashAlgorithm) + public static string ToAlgorithmName(this HashAlgorithm hashAlgorithm) { if (hashAlgorithm is SHA1) return HashAlgorithmNames.SHA1; @@ -34,5 +37,17 @@ public static string ToAlgorithmName(HashAlgorithm hashAlgorithm) // Fallback to ToString() which can be extended by derived classes return hashAlgorithm.ToString(); } + + /// + /// Uppercase known hash algorithms. BCrypt is case-sensitive and requires uppercase. + /// + public static string ToUpper(string hashAlgorithName) + { + if (ALL_NAMES.IndexOf(hashAlgorithName, StringComparison.OrdinalIgnoreCase) >= 0) + { + return hashAlgorithName.ToUpperInvariant(); + } + return hashAlgorithName; + } } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs index 62a5b969473a..014ce941e4fc 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureDeformatter.cs @@ -18,7 +18,7 @@ public virtual bool VerifySignature(HashAlgorithm hash, byte[] rgbSignature) if (hash == null) throw new ArgumentNullException(nameof(hash)); - SetHashAlgorithm(HashAlgorithmNames.ToAlgorithmName(hash)); + SetHashAlgorithm(hash.ToAlgorithmName()); return VerifySignature(hash.Hash, rgbSignature); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs index db539fd3f76e..24fdad5767b1 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricSignatureFormatter.cs @@ -18,7 +18,7 @@ public virtual byte[] CreateSignature(HashAlgorithm hash) if (hash == null) throw new ArgumentNullException(nameof(hash)); - SetHashAlgorithm(HashAlgorithmNames.ToAlgorithmName(hash)); + SetHashAlgorithm(hash.ToAlgorithmName()); return CreateSignature(hash.Hash); } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs index cba820f99d82..98febf5b17ae 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs @@ -2,20 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Cryptography; - namespace System.Security.Cryptography { public class DSASignatureDeformatter : AsymmetricSignatureDeformatter { - DSA _dsaKey; - string _algName; + private DSA _dsaKey; - public DSASignatureDeformatter() - { - // The hash algorithm default is SHA1 - _algName = HashAlgorithmNames.SHA1; - } + public DSASignatureDeformatter() { } public DSASignatureDeformatter(AsymmetricAlgorithm key) : this() { @@ -35,20 +28,15 @@ public override void SetKey(AsymmetricAlgorithm key) public override void SetHashAlgorithm(string strName) { - // The implemenation is symmetric with RSAPKCS1SignatureDeformatter even though _algName - // is not used in VerifySignature. try { - // Verify the name + // Verify the name is a valid HashAlgorithm even though it is not currently used Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - - // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. - _algName = strName; } catch (CryptographicException) { - // For desktop compat there is no exception here - _algName = null; + // For desktop compat, throw this exception instead + throw new CryptographicUnexpectedOperationException(SR.Cryptography_InvalidOperation); } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs index 67a5541dbdd5..9d5c7bd8226a 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs @@ -2,20 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Internal.Cryptography; - namespace System.Security.Cryptography { public class DSASignatureFormatter : AsymmetricSignatureFormatter { - DSA _dsaKey; - string _algName; + private DSA _dsaKey; - public DSASignatureFormatter() - { - // The hash algorithm default is SHA1 - _algName = HashAlgorithmNames.SHA1; - } + public DSASignatureFormatter() { } public DSASignatureFormatter(AsymmetricAlgorithm key) : this() { @@ -35,20 +28,15 @@ public override void SetKey(AsymmetricAlgorithm key) public override void SetHashAlgorithm(string strName) { - // The implemenation is symmetric with RSAPKCS1SignatureFormatter even though _algName - // is not used in VerifySignature. try { - // Verify the name + // Verify the name is a valid HashAlgorithm even though it is not currently used Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - - // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. - _algName = strName; } catch (CryptographicException) { - // For desktop compat there is no exception here - _algName = null; + // For desktop compat, throw this exception instead + throw new CryptographicUnexpectedOperationException(SR.Cryptography_InvalidOperation); } } @@ -56,8 +44,6 @@ public override byte[] CreateSignature(byte[] rgbHash) { if (rgbHash == null) throw new ArgumentNullException(nameof(rgbHash)); - if (_algName == null) - throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingOID); if (_dsaKey == null) throw new CryptographicUnexpectedOperationException(SR.Cryptography_MissingKey); diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs index 1d6e410f5230..75b74539432d 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeDeformatter.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography { public class RSAPKCS1KeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter { - RSA _rsaKey; + private RSA _rsaKey; public RSAPKCS1KeyExchangeDeformatter() { } @@ -20,13 +20,8 @@ public RSAPKCS1KeyExchangeDeformatter(AsymmetricAlgorithm key) public override string Parameters { - get - { - return null; - } - set - { - } + get {return null;} + set { } } public override byte[] DecryptKeyExchange(byte[] rgbIn) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs index 9cdf9ce0c05e..59065c9ec04d 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1KeyExchangeFormatter.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography { public class RSAPKCS1KeyExchangeFormatter : AsymmetricKeyExchangeFormatter { - RSA _rsaKey; + private RSA _rsaKey; public RSAPKCS1KeyExchangeFormatter() { } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs index 1a6decdb723a..c8d75906524e 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureDeformatter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Cryptography; + namespace System.Security.Cryptography { public class RSAPKCS1SignatureDeformatter : AsymmetricSignatureDeformatter @@ -33,12 +35,12 @@ public override void SetHashAlgorithm(string strName) // Verify the name Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. - _algName = strName; + // Uppercase known names as required for BCrypt + _algName = HashAlgorithmNames.ToUpper(strName); } catch (CryptographicException) { - // For desktop compat there is no exception here + // For desktop compat, exception is deferred until VerifySignature _algName = null; } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs index f38e82f4446f..cb4fdf80cff0 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAPKCS1SignatureFormatter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Cryptography; + namespace System.Security.Cryptography { public class RSAPKCS1SignatureFormatter : AsymmetricSignatureFormatter @@ -34,12 +36,12 @@ public override void SetHashAlgorithm(string strName) // Verify the name Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - // Keep the raw strName as Oid may change the case ("SHA1" to "sha1") making it incompatible. - _algName = strName; + // Uppercase known names as required for BCrypt + _algName = HashAlgorithmNames.ToUpper(strName); } catch (CryptographicException) { - // For desktop compat there is no exception here + // For desktop compat, exception is deferred until VerifySignature _algName = null; } } diff --git a/src/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs b/src/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs new file mode 100644 index 000000000000..1073b4ab7c95 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/AsymmetricSignatureFormatterTests.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Security.Cryptography.Tests +{ + /// + /// Helper methods for DSASignatureFormatterTests and RSASignatureFormatterTests + /// + public partial class AsymmetricSignatureFormatterTests + { + protected static void InvalidFormatterArguments(AsymmetricSignatureFormatter formatter) + { + Assert.Throws(() => formatter.SetKey(null)); + Assert.Throws(() => formatter.CreateSignature((byte[])null)); + Assert.Throws(() => formatter.CreateSignature(new byte[] { 0, 1, 2, 3 })); + } + + protected static void InvalidDeformatterArguments(AsymmetricSignatureDeformatter deformatter) + { + Assert.Throws(() => deformatter.SetKey(null)); + Assert.Throws(() => deformatter.VerifySignature((byte[])null, new byte[] { 0, 1, 2 })); + Assert.Throws(() => deformatter.VerifySignature(new byte[] { 0, 1, 2 }, new byte[] { 0, 1, 2 })); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/DSASignatureFormatterTests.cs b/src/System.Security.Cryptography.Algorithms/tests/DSASignatureFormatterTests.cs new file mode 100644 index 000000000000..915ee3fa6f84 --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/DSASignatureFormatterTests.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Dsa.Tests +{ + public partial class DSASignatureFormatterTests : AsymmetricSignatureFormatterTests + { + [Fact] + public static void InvalidFormatterArguments() + { + InvalidFormatterArguments(new DSASignatureFormatter()); + } + + [Fact] + public static void InvalidDeformatterArguments() + { + InvalidDeformatterArguments(new DSASignatureDeformatter()); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/RSAKeyExchangeFormatterTests.cs b/src/System.Security.Cryptography.Algorithms/tests/RSAKeyExchangeFormatterTests.cs new file mode 100644 index 000000000000..e03b344c40ed --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/RSAKeyExchangeFormatterTests.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Security.Cryptography.Rsa.Tests +{ + public partial class RSAKeyExchangeFormatterTests + { + [Fact] + public static void RSAOAEPFormatterArguments() + { + InvalidFormatterArguments(new RSAOAEPKeyExchangeFormatter()); + } + + [Fact] + public static void RSAOAEPDeformatterArguments() + { + InvalidDeformatterArguments(new RSAOAEPKeyExchangeDeformatter()); + } + + [Fact] + public static void RSAPKCS1FormatterArguments() + { + InvalidFormatterArguments(new RSAPKCS1KeyExchangeFormatter()); + } + + [Fact] + public static void RSAPKCS1DeformatterArguments() + { + InvalidDeformatterArguments(new RSAPKCS1KeyExchangeDeformatter()); + } + + private static void InvalidFormatterArguments(AsymmetricKeyExchangeFormatter formatter) + { + Assert.Throws(() => formatter.SetKey(null)); + Assert.Throws(() => formatter.CreateKeyExchange(new byte[] { 0, 1, 2, 3 })); + } + + private static void InvalidDeformatterArguments(AsymmetricKeyExchangeDeformatter deformatter) + { + Assert.Throws(() => deformatter.SetKey(null)); + Assert.Throws(() => deformatter.DecryptKeyExchange(new byte[] { 0, 1, 2 })); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/RSASignatureFormatterTests.cs b/src/System.Security.Cryptography.Algorithms/tests/RSASignatureFormatterTests.cs new file mode 100644 index 000000000000..40b86a82fded --- /dev/null +++ b/src/System.Security.Cryptography.Algorithms/tests/RSASignatureFormatterTests.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Cryptography.Tests; +using Xunit; + +namespace System.Security.Cryptography.Rsa.Tests +{ + public partial class RSASignatureFormatterTests : AsymmetricSignatureFormatterTests + { + [Fact] + public static void InvalidFormatterArguments() + { + InvalidFormatterArguments(new RSAPKCS1SignatureFormatter()); + } + + [Fact] + public static void InvalidDeformatterArguments() + { + InvalidDeformatterArguments(new RSAPKCS1SignatureDeformatter()); + } + } +} diff --git a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 7039fff758a3..28782b15dd6a 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -133,12 +133,13 @@ + + + + - - CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs - - - CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatter.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs diff --git a/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj b/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj index 5a2a77bdbfd4..58c1eb8c4efe 100644 --- a/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj +++ b/src/System.Security.Cryptography.Cng/tests/System.Security.Cryptography.Cng.Tests.csproj @@ -130,11 +130,8 @@ - - CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs - - - CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatter.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs diff --git a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj index dab9da320e34..4e914bcae5a7 100644 --- a/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj +++ b/src/System.Security.Cryptography.Csp/tests/System.Security.Cryptography.Csp.Tests.csproj @@ -55,11 +55,8 @@ - - CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs - - - CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatter.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs diff --git a/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj b/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj index 424a010144d1..86a5c63e0703 100644 --- a/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj +++ b/src/System.Security.Cryptography.OpenSsl/tests/System.Security.Cryptography.OpenSsl.Tests.csproj @@ -81,11 +81,8 @@ - - CommonTest\System\Security\Cryptography\AsymmetricKeyExchangeFormatterTests.cs - - - CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatterTests.cs + + CommonTest\System\Security\Cryptography\AsymmetricSignatureFormatter.cs CommonTest\System\Security\Cryptography\AlgorithmImplementations\DSA\DSAFactory.cs diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index e38906b2bafb..08976bfe4746 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -8,9 +8,7 @@ namespace System.Security.Cryptography { public abstract class HashAlgorithm : IDisposable, ICryptoTransform { - protected HashAlgorithm() - { - } + protected HashAlgorithm() { } public virtual int HashSize { @@ -107,7 +105,7 @@ protected virtual void Dispose(bool disposing) // ICryptoTransform methods - // we assume any HashAlgorithm can take input a byte at a time + // We assume any HashAlgorithm can take input a byte at a time public virtual int InputBlockSize { get { return (1); } @@ -176,10 +174,10 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input } else { - outputBytes = new byte[0]; + outputBytes = Array.Empty(); } - // reset the State value + // Reset the State value State = 0; return outputBytes; From 6cdb948f2c1ab904eece412b9cb5ecfab1cb1190 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Sun, 25 Sep 2016 14:50:41 -0500 Subject: [PATCH 3/7] fix comment --- .../Security/Cryptography/AsymmetricKeyExchangeFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs index 57da523c1152..3aff1224f278 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/AsymmetricKeyExchangeFormatter.cs @@ -13,7 +13,7 @@ protected AsymmetricKeyExchangeFormatter() {} public abstract void SetKey(AsymmetricAlgorithm key); public abstract byte[] CreateKeyExchange(byte[] data); - // review note: keeping this even though synAlgType is not used + // For desktop compat, keep this even though symAlgType is not used. public abstract byte[] CreateKeyExchange(byte[] data, Type symAlgType); } } From 139d4cfffeba387ab07c293d76e93dbfdcaa2aff Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 27 Sep 2016 19:07:16 -0500 Subject: [PATCH 4/7] Code review feedback --- .../DSA/DSASignVerify.cs | 9 +- .../DSA/DSASignatureFormatter.cs | 27 ++-- .../DSA/DSATestData.cs | 14 +- .../AsymmetricSignatureFormatter.cs | 2 - ...System.Security.Cryptography.Algorithms.cs | 1 - .../Cryptography/HashAlgorithmNames.cs | 19 ++- .../Cryptography/DSASignatureDeformatter.cs | 11 +- .../Cryptography/DSASignatureFormatter.cs | 11 +- .../RSAOAEPKeyExchangeDeformatter.cs | 9 +- .../RSAOAEPKeyExchangeFormatter.cs | 7 +- .../tests/HashAlgorithmTest.cs | 149 ++++++++++++++++++ .../tests/MD5Tests.cs | 10 ++ .../tests/Sha1Tests.cs | 6 + .../tests/Sha256Tests.cs | 10 ++ .../tests/Sha384Tests.cs | 10 ++ .../tests/Sha512Tests.cs | 10 ++ 16 files changed, 244 insertions(+), 61 deletions(-) diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs index e4766bdbf7fd..6a3f6b7ff696 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs @@ -52,11 +52,12 @@ public static void VerifyKnownSignature() { using (DSA dsa = DSAFactory.Create()) { - DSAParameters dsaParameters = DSATestData.GetDSA1024_186_2_Params(); - dsa.ImportParameters(dsaParameters); + byte[] data; + byte[] signature; + DSAParameters dsaParameters; + DSATestData.GetDSA1024_186_2(out dsaParameters, out signature, out data); - byte[] data = DSATestData.GetDSA1024_186_2_Data(); - byte[] signature = DSATestData.GetDSA1024_186_2_Signature(); + dsa.ImportParameters(dsaParameters); Assert.True(dsa.VerifyData(data, signature, HashAlgorithmName.SHA1)); // Negative case diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs index 85319683690a..7e9854f4b8c8 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs @@ -21,18 +21,6 @@ public static void VerifySignature_SHA1() } } - [ConditionalFact(nameof(SupportsFips186_3))] - public static void VerifySignature_SHA256() - { - using (DSA dsa = DSAFactory.Create()) - { - var formatter = new DSASignatureFormatter(dsa); - var deformatter = new DSASignatureDeformatter(dsa); - VerifySignature(formatter, deformatter, SHA256.Create(), "SHA256"); - VerifySignature(formatter, deformatter, SHA256.Create(), "sha256"); - } - } - [Fact] public static void InvalidHashAlgorithm() { @@ -46,6 +34,12 @@ public static void InvalidHashAlgorithm() formatter.SetHashAlgorithm("INVALIDVALUE")); Assert.Throws(() => deformatter.SetHashAlgorithm("INVALIDVALUE")); + + // Currently anything other than SHA1 fails + Assert.Throws(() => + formatter.SetHashAlgorithm("SHA256")); + Assert.Throws(() => + deformatter.SetHashAlgorithm("SHA256")); } } @@ -54,10 +48,13 @@ public static void VerifyKnownSignature() { using (DSA dsa = DSAFactory.Create()) { - byte[] hash = SHA1.Create().ComputeHash(DSATestData.GetDSA1024_186_2_Data()); - byte[] signature = DSATestData.GetDSA1024_186_2_Signature(); + byte[] data; + byte[] signature; + DSAParameters dsaParameters; + DSATestData.GetDSA1024_186_2(out dsaParameters, out signature, out data); + + byte[] hash = SHA1.Create().ComputeHash(data); - DSAParameters dsaParameters = DSATestData.GetDSA1024_186_2_Params(); dsa.ImportParameters(dsaParameters); var deformatter = new DSASignatureDeformatter(dsa); deformatter.VerifySignature(hash, signature); diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs index bbf47c53bec8..f19707f74680 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSATestData.cs @@ -69,9 +69,9 @@ internal static DSAParameters GetDSA2048Params() } // The parameters and signature come from FIPS 186-2 APPENDIX 5. EXAMPLE OF THE DSA - internal static DSAParameters GetDSA1024_186_2_Params() + internal static void GetDSA1024_186_2(out DSAParameters parameters, out byte[] signature, out byte[] data) { - return new DSAParameters() + parameters = new DSAParameters() { P = ( "8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac" + @@ -85,21 +85,15 @@ internal static DSAParameters GetDSA1024_186_2_Params() "19131871d75b1612a819f29d78d1b0d7346f7aa77bb62a859bfd6c5675da9d212d3a36ef1672ef660b8c7c255cc0ec74" + "858fba33f44c06699630a76b030ee333").HexToByteArray() }; - } - internal static byte[] GetDSA1024_186_2_Signature() - { - return ( + signature = ( // r "8bac1ab66410435cb7181f95b16ab97c92b341c0" + // s "41e2345f1f56df2458f426d155b4ba2db6dcd8c8" ).HexToByteArray(); - } - internal static byte[] GetDSA1024_186_2_Data() - { - return Encoding.ASCII.GetBytes("abc"); + data = Encoding.ASCII.GetBytes("abc"); } } } diff --git a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs index 54be36c4128e..35a9194ad22c 100644 --- a/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AsymmetricSignatureFormatter.cs @@ -22,8 +22,6 @@ protected static void VerifySignature(AsymmetricSignatureFormatter formatter, As byte[] hash = hashAlgorithm.ComputeHash(HelloBytes); VerifySignatureWithHashBytes(formatter, deformatter, hash); - - // Check that the hash is preserved from the ComputeHash above VerifySignatureWithHashAlgorithm(formatter, deformatter, hashAlgorithm); } diff --git a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs index 41091332d2c6..aa6b710b3efe 100644 --- a/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs +++ b/src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs @@ -350,7 +350,6 @@ public partial struct RSAParameters public byte[] P; public byte[] Q; } - [System.Runtime.InteropServices.ComVisibleAttribute(true)] public partial class RSAPKCS1KeyExchangeDeformatter : System.Security.Cryptography.AsymmetricKeyExchangeDeformatter { public RSAPKCS1KeyExchangeDeformatter() { } diff --git a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs index 209dea98343b..4dcf1df08246 100644 --- a/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs +++ b/src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashAlgorithmNames.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Security.Cryptography; namespace Internal.Cryptography @@ -16,7 +17,7 @@ internal static class HashAlgorithmNames public const string SHA384 = "SHA384"; public const string SHA512 = "SHA512"; - private const string ALL_NAMES = "MD5 SHA1 SHA256 SHA384 SHA512"; + private static readonly HashSet s_allNames = CreateAllNames(); /// /// Map HashAlgorithm type to string; desktop uses CryptoConfig functionality. @@ -43,11 +44,25 @@ public static string ToAlgorithmName(this HashAlgorithm hashAlgorithm) /// public static string ToUpper(string hashAlgorithName) { - if (ALL_NAMES.IndexOf(hashAlgorithName, StringComparison.OrdinalIgnoreCase) >= 0) + if (s_allNames.Contains(hashAlgorithName)) { return hashAlgorithName.ToUpperInvariant(); } + return hashAlgorithName; } + + private static HashSet CreateAllNames() + { + var allNames = new HashSet(StringComparer.OrdinalIgnoreCase); + + allNames.Add(HashAlgorithmNames.SHA1); + allNames.Add(HashAlgorithmNames.SHA256); + allNames.Add(HashAlgorithmNames.SHA384); + allNames.Add(HashAlgorithmNames.SHA512); + allNames.Add(HashAlgorithmNames.MD5); + + return allNames; + } } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs index 98febf5b17ae..eec79188b823 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureDeformatter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Cryptography; + namespace System.Security.Cryptography { public class DSASignatureDeformatter : AsymmetricSignatureDeformatter @@ -28,14 +30,9 @@ public override void SetKey(AsymmetricAlgorithm key) public override void SetHashAlgorithm(string strName) { - try - { - // Verify the name is a valid HashAlgorithm even though it is not currently used - Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - } - catch (CryptographicException) + if (strName.ToUpperInvariant() != HashAlgorithmNames.SHA1) { - // For desktop compat, throw this exception instead + // To match desktop, throw here throw new CryptographicUnexpectedOperationException(SR.Cryptography_InvalidOperation); } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs index 9d5c7bd8226a..d14d38d4a2c6 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSASignatureFormatter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Internal.Cryptography; + namespace System.Security.Cryptography { public class DSASignatureFormatter : AsymmetricSignatureFormatter @@ -28,14 +30,9 @@ public override void SetKey(AsymmetricAlgorithm key) public override void SetHashAlgorithm(string strName) { - try - { - // Verify the name is a valid HashAlgorithm even though it is not currently used - Oid.FromFriendlyName(strName, OidGroup.HashAlgorithm); - } - catch (CryptographicException) + if (strName.ToUpperInvariant() != HashAlgorithmNames.SHA1) { - // For desktop compat, throw this exception instead + // To match desktop, throw here throw new CryptographicUnexpectedOperationException(SR.Cryptography_InvalidOperation); } } diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs index 27ad1b9a8e05..9c4df0da9774 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeDeformatter.cs @@ -19,13 +19,8 @@ public RSAOAEPKeyExchangeDeformatter(AsymmetricAlgorithm key) public override string Parameters { - get - { - return null; - } - set - { - } + get {return null;} + set { } } public override byte[] DecryptKeyExchange(byte[] rgbData) diff --git a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs index a39a7380e801..cda0ff0e54a3 100644 --- a/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs +++ b/src/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RSAOAEPKeyExchangeFormatter.cs @@ -18,7 +18,6 @@ public RSAOAEPKeyExchangeFormatter(AsymmetricAlgorithm key) _rsaKey = (RSA)key; } - /// public byte[] Parameter { get @@ -43,13 +42,9 @@ public byte[] Parameter } } - /// public override string Parameters { - get - { - return null; - } + get {return null;} } public override void SetKey(AsymmetricAlgorithm key) diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs index ec8cae5d2650..c0f88694a1a7 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.IO; using Test.Cryptography; using Xunit; @@ -54,6 +55,154 @@ private void VerifyICryptoTransformStream(Stream input, string output) } #endif + protected void VerifyMultiBlock(string block1, string block2, string expectedHash, string emptyHash) + { +#if netstandard17 + byte[] block1_bytes = ByteUtils.AsciiBytes(block1); + byte[] block2_bytes = ByteUtils.AsciiBytes(block2); + byte[] expected_bytes = ByteUtils.HexToByteArray(expectedHash); + byte[] emptyHash_bytes = ByteUtils.HexToByteArray(emptyHash); + + VerifyTransformOutput(block1_bytes, block2_bytes); + VerifyTransformComputeHashInteraction(block1_bytes, block2_bytes, expected_bytes, emptyHash_bytes); +#endif + } + +#if netstandard17 + private void VerifyTransformOutput(byte[] block1, byte[] block2) + { + byte[] output = new byte[block1.Length + block2.Length]; + + // Concat block1 and block2 + byte[] expected = new byte[block1.Length + block2.Length]; + Buffer.BlockCopy(block1, 0, expected, 0, block1.Length); + Buffer.BlockCopy(block2, 0, expected, block1.Length, block2.Length); + + using (HashAlgorithm hash = Create()) + { + int byteCount = hash.TransformBlock(block1, 0, block1.Length, output, 0); + hash.TransformBlock(block2, 0, block2.Length, output, byteCount); + Assert.Equal(expected, output); + } + } + + private void VerifyTransformComputeHashInteraction(byte[] block1, byte[] block2, byte[] expected, byte[] expectedEmpty) + { + using (HashAlgorithm hash = Create()) + { + // TransformBlock + ComputeHash + hash.TransformBlock(block1, 0, block1.Length, null, 0); + byte[] actual = hash.ComputeHash(block2, 0, block2.Length); + Assert.Equal(expected, actual); + + // Verify ComputeHash cleared hash + actual = hash.ComputeHash(Array.Empty(), 0, 0); + Assert.Equal(expectedEmpty, actual); + + // Re-hash the same data + hash.TransformBlock(block1, 0, block1.Length, null, 0); + actual = hash.ComputeHash(block2, 0, block2.Length); + Assert.Equal(expected, actual); + + // Verify TransformFinalBlock + hash.TransformBlock(block1, 0, block1.Length, null, 0); + hash.TransformFinalBlock(block2, 0, block2.Length); + Assert.Equal(expected, hash.Hash); + Assert.Equal(expected, hash.Hash); // .Hash doesn't clear hash + + // Todo: this will also fail due to the ActiveIssue below in VerifyUseAfterTransformFinalBlock + // but commented out so the other tests can run + // actual = hash.ComputeHash(Array.Empty(), 0, 0); + // Assert.Equal(expected, actual); + + // TransformBlock + TransformBlock + ComputeHash + hash.TransformBlock(block1, 0, block1.Length, null, 0); + hash.TransformBlock(block2, 0, block2.Length, null, 0); + actual = hash.ComputeHash(Array.Empty(), 0, 0); + Assert.Equal(expected, actual); + } + } + + [Fact] + public void VerifyObjectDisposedException() + { + HashAlgorithm hash = Create(); + hash.Dispose(); + Assert.Throws(() => hash.Hash); + Assert.Throws(() => hash.ComputeHash(Array.Empty())); + Assert.Throws(() => hash.ComputeHash(Array.Empty(), 0, 0)); + Assert.Throws(() => hash.ComputeHash((Stream)null)); + Assert.Throws(() => hash.TransformBlock(Array.Empty(), 0, 0, null, 0)); + Assert.Throws(() => hash.TransformFinalBlock(Array.Empty(), 0, 0)); + } + + [Fact] + public void VerifyHashNotYetFinalized() + { + using (HashAlgorithm hash = Create()) + { + hash.TransformBlock(Array.Empty(), 0, 0, null, 0); + Assert.Throws(() => hash.Hash); + } + } + + [Fact] + [ActiveIssue(0)] // Todo: netfx treats HashFinal and Initialize separately, corefx doesn't and Initialize is a no-op + public void VerifyUseAfterTransformFinalBlock() + { + byte[] block = new byte[1] {1}; + + using (HashAlgorithm hash = Create()) + { + hash.TransformBlock(block, 0, block.Length, null, 0); + hash.TransformFinalBlock(block, 0, block.Length); + + // Todo: these do not throw on corefx, but do on netfx + Assert.Throws(() => hash.TransformBlock(block, 0, block.Length, null, 0)); + Assert.Throws(() => hash.TransformFinalBlock(block, 0, block.Length)); + + // Transforming 0 bytes should not throw + hash.TransformBlock(block, 0, 0, null, 0); + hash.TransformFinalBlock(block, 0, 0); + } + } + + [Fact] + public void InvalidInput_ComputeHash() + { + using (HashAlgorithm hash = Create()) + { + Assert.Throws("buffer", () => hash.ComputeHash((byte[])null)); + Assert.Throws("buffer", () => hash.ComputeHash(null, 0, 0)); + } + } + + [Fact] + public void InvalidInput_TransformBlock() + { + using (HashAlgorithm hash = Create()) + { + Assert.Throws("inputBuffer", () => hash.TransformBlock(null, 0, 0, null, 0)); + Assert.Throws("inputOffset", () => hash.TransformBlock(Array.Empty(), -1, 0, null, 0)); + Assert.Throws(null, () => hash.TransformBlock(Array.Empty(), 0, 1, null, 0)); + Assert.Throws(null, () => hash.TransformBlock(Array.Empty(), 1, 0, null, 0)); + } + } + + [Fact] + public void InvalidInput_TransformFinalBlock() + { + using (HashAlgorithm hash = Create()) + { + Assert.Throws("inputBuffer", () => hash.TransformFinalBlock(null, 0, 0)); + Assert.Throws("inputOffset", () => hash.TransformFinalBlock(Array.Empty(), -1, 0)); + Assert.Throws(null, () => hash.TransformFinalBlock(Array.Empty(), 1, 0)); + Assert.Throws(null, () => hash.TransformFinalBlock(Array.Empty(), 0, -1)); + Assert.Throws(null, () => hash.TransformFinalBlock(Array.Empty(), 0, 1)); + } + } +#endif + protected void Verify(byte[] input, string output) { byte[] expected = ByteUtils.HexToByteArray(output); diff --git a/src/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs b/src/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs index fa3eb2100cbb..3dbff8fe7822 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/MD5Tests.cs @@ -33,6 +33,16 @@ public void MD5_Rfc1321_3() Verify("abc", "900150983cd24fb0d6963f7d28e17f72"); } + [Fact] + public void MD5_Rfc1321_MultiBlock() + { + VerifyMultiBlock( + "a", + "bc", + "900150983cd24fb0d6963f7d28e17f72", + "d41d8cd98f00b204e9800998ecf8427e"); + } + [Fact] public void MD5_Rfc1321_4() { diff --git a/src/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs b/src/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs index 30dde3a83f95..1480173911e4 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs @@ -27,6 +27,12 @@ public void Sha1_Rfc3174_1() Verify("abc", "A9993E364706816ABA3E25717850C26C9CD0D89D"); } + [Fact] + public void Sha1_Rfc3174_MultiBlock() + { + VerifyMultiBlock("ab", "c", "A9993E364706816ABA3E25717850C26C9CD0D89D", "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"); + } + [Fact] public void Sha1_Rfc3174_2() { diff --git a/src/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs b/src/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs index 85cf406e0efd..336ca89e8a1a 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs @@ -30,6 +30,16 @@ public void Sha256_Fips180_1() "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); } + [Fact] + public void Sha256_Fips180_MultiBlock() + { + VerifyMultiBlock( + "ab", + "c", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + } + [Fact] public void Sha256_Fips180_2() { diff --git a/src/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs b/src/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs index 3525283a5a4f..bb5230c521ed 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs @@ -30,6 +30,16 @@ public void Sha384_NistShaAll_1() "CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7"); } + [Fact] + public void Sha256_Fips180_MultiBlock() + { + VerifyMultiBlock( + "a", + "bc", + "CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7", + "38B060A751AC96384CD9327EB1B1E36A21FDB71114BE07434C0CC7BF63F6E1DA274EDEBFE76F65FBD51AD2F14898B95B"); + } + [Fact] public void Sha384_NistShaAll_2() { diff --git a/src/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs b/src/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs index d3a35cff1a7d..4027a1acf11a 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs @@ -30,6 +30,16 @@ public void Sha512_Fips180_1() "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); } + [Fact] + public void Sha512_Fips180_MultiBlock() + { + VerifyMultiBlock( + "a", + "bc", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + } + [Fact] public void Sha512_Fips180_2() { From a845a14730095e93e05b17e1e6e54f5b566ec156 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 28 Sep 2016 19:01:21 -0500 Subject: [PATCH 5/7] Review feedback plus new crypto exception ctors --- .../DSA/DSASignatureFormatter.cs | 13 ++- .../RSA/RSASignatureFormatter.cs | 24 ++++-- .../tests/HashAlgorithmTest.cs | 18 ++-- ...System.Security.Cryptography.Primitives.cs | 2 + .../src/Resources/Strings.resx | 3 + .../Cryptography/CryptographicException.cs | 6 ++ ...yptographicUnexpectedOperationException.cs | 6 ++ .../Security/Cryptography/HashAlgorithm.cs | 82 ++++++++++++------- 8 files changed, 106 insertions(+), 48 deletions(-) diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs index 7e9854f4b8c8..fb6fbe8d78d3 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs @@ -16,8 +16,11 @@ public static void VerifySignature_SHA1() { var formatter = new DSASignatureFormatter(dsa); var deformatter = new DSASignatureDeformatter(dsa); - VerifySignature(formatter, deformatter, SHA1.Create(), "SHA1"); - VerifySignature(formatter, deformatter, SHA1.Create(), "sha1"); + using (SHA1 alg = SHA1.Create()) + { + VerifySignature(formatter, deformatter, alg, "SHA1"); + VerifySignature(formatter, deformatter, alg, "sha1"); + } } } @@ -53,7 +56,11 @@ public static void VerifyKnownSignature() DSAParameters dsaParameters; DSATestData.GetDSA1024_186_2(out dsaParameters, out signature, out data); - byte[] hash = SHA1.Create().ComputeHash(data); + byte[] hash; + using (SHA1 alg = SHA1.Create()) + { + hash = alg.ComputeHash(data); + } dsa.ImportParameters(dsaParameters); var deformatter = new DSASignatureDeformatter(dsa); diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs index e229850c9b5a..9489c0a2144c 100644 --- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs +++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs @@ -17,8 +17,12 @@ public static void VerifySignature_SHA1() { var formatter = new RSAPKCS1SignatureFormatter(rsa); var deformatter = new RSAPKCS1SignatureDeformatter(rsa); - VerifySignature(formatter, deformatter, SHA1.Create(), "SHA1"); - VerifySignature(formatter, deformatter, SHA1.Create(), "sha1"); + + using (SHA1 alg = SHA1.Create()) + { + VerifySignature(formatter, deformatter, alg, "SHA1"); + VerifySignature(formatter, deformatter, alg, "sha1"); + } } } @@ -29,8 +33,12 @@ public static void VerifySignature_SHA256() { var formatter = new RSAPKCS1SignatureFormatter(rsa); var deformatter = new RSAPKCS1SignatureDeformatter(rsa); - VerifySignature(formatter, deformatter, SHA256.Create(), "SHA256"); - VerifySignature(formatter, deformatter, SHA256.Create(), "sha256"); + + using (SHA256 alg = SHA256.Create()) + { + VerifySignature(formatter, deformatter, alg, "SHA256"); + VerifySignature(formatter, deformatter, alg, "sha256"); + } } } @@ -45,8 +53,12 @@ public static void InvalidHashAlgorithm() // Exception is deferred until VerifySignature formatter.SetHashAlgorithm("INVALIDVALUE"); deformatter.SetHashAlgorithm("INVALIDVALUE"); - Assert.Throws(() => - VerifySignature(formatter, deformatter, SHA1.Create(), "INVALIDVALUE")); + + using (SHA1 alg = SHA1.Create()) + { + Assert.Throws(() => + VerifySignature(formatter, deformatter, alg, "INVALIDVALUE")); + } } } diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs index c0f88694a1a7..a90af17cbbf1 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs @@ -95,27 +95,25 @@ private void VerifyTransformComputeHashInteraction(byte[] block1, byte[] block2, byte[] actual = hash.ComputeHash(block2, 0, block2.Length); Assert.Equal(expected, actual); - // Verify ComputeHash cleared hash + // Verify ComputeHash above cleared hash actual = hash.ComputeHash(Array.Empty(), 0, 0); Assert.Equal(expectedEmpty, actual); - // Re-hash the same data + // We should now be able to re-hash hash.TransformBlock(block1, 0, block1.Length, null, 0); actual = hash.ComputeHash(block2, 0, block2.Length); Assert.Equal(expected, actual); - // Verify TransformFinalBlock + // TransformBlock + TransformFinalBlock hash.TransformBlock(block1, 0, block1.Length, null, 0); hash.TransformFinalBlock(block2, 0, block2.Length); Assert.Equal(expected, hash.Hash); Assert.Equal(expected, hash.Hash); // .Hash doesn't clear hash + // ComputeHash with 0 bytes resets without interfering with hash + actual = hash.ComputeHash(Array.Empty(), 0, 0); + Assert.Equal(expected, actual); - // Todo: this will also fail due to the ActiveIssue below in VerifyUseAfterTransformFinalBlock - // but commented out so the other tests can run - // actual = hash.ComputeHash(Array.Empty(), 0, 0); - // Assert.Equal(expected, actual); - - // TransformBlock + TransformBlock + ComputeHash + // TransformBlock + TransformBlock + ComputeHash(empty) hash.TransformBlock(block1, 0, block1.Length, null, 0); hash.TransformBlock(block2, 0, block2.Length, null, 0); actual = hash.ComputeHash(Array.Empty(), 0, 0); @@ -147,7 +145,6 @@ public void VerifyHashNotYetFinalized() } [Fact] - [ActiveIssue(0)] // Todo: netfx treats HashFinal and Initialize separately, corefx doesn't and Initialize is a no-op public void VerifyUseAfterTransformFinalBlock() { byte[] block = new byte[1] {1}; @@ -157,7 +154,6 @@ public void VerifyUseAfterTransformFinalBlock() hash.TransformBlock(block, 0, block.Length, null, 0); hash.TransformFinalBlock(block, 0, block.Length); - // Todo: these do not throw on corefx, but do on netfx Assert.Throws(() => hash.TransformBlock(block, 0, block.Length, null, 0)); Assert.Throws(() => hash.TransformFinalBlock(block, 0, block.Length)); diff --git a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs index 75e2ed620e03..055c9bf7bc7c 100644 --- a/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs +++ b/src/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs @@ -28,6 +28,7 @@ public partial class CryptographicException : System.Exception { public CryptographicException() { } public CryptographicException(int hr) { } + protected CryptographicException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public CryptographicException(string message) { } public CryptographicException(string message, System.Exception inner) { } public CryptographicException(string format, string insert) { } @@ -35,6 +36,7 @@ public CryptographicException(string format, string insert) { } public partial class CryptographicUnexpectedOperationException : System.Security.Cryptography.CryptographicException { public CryptographicUnexpectedOperationException() { } + protected CryptographicUnexpectedOperationException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public CryptographicUnexpectedOperationException(string message) { } public CryptographicUnexpectedOperationException(string message, System.Exception inner) { } public CryptographicUnexpectedOperationException(string format, string insert) { } diff --git a/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx b/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx index f30e6a7bdf8d..bb9526c9c181 100644 --- a/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx +++ b/src/System.Security.Cryptography.Primitives/src/Resources/Strings.resx @@ -135,6 +135,9 @@ Non-negative number required. + + Hash not valid for use in specified state. + FlushFinalBlock() method was called twice on a CryptoStream. It can only be called once. diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicException.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicException.cs index 19ec15e49390..9de4dff1e9b8 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicException.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicException.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Runtime.Serialization; namespace System.Security.Cryptography { @@ -33,5 +34,10 @@ public CryptographicException(string format, string insert) : base(string.Format(CultureInfo.CurrentCulture, format, insert)) { } + + protected CryptographicException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs index 92073c95e197..d6a20b46f97c 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptographicUnexpectedOperationException.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Runtime.Serialization; namespace System.Security.Cryptography { @@ -27,5 +28,10 @@ public CryptographicUnexpectedOperationException(string format, string insert) : base(string.Format(CultureInfo.CurrentCulture, format, insert)) { } + + protected CryptographicUnexpectedOperationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index 08976bfe4746..4b488d130c0d 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -39,7 +39,7 @@ public byte[] ComputeHash(byte[] buffer) throw new ArgumentNullException(nameof(buffer)); HashCore(buffer, 0, buffer.Length); - return CaptureHashCodeAndReinitialize(); + return CaptureHashCodeAndReinitialize(buffer.Length); } public byte[] ComputeHash(byte[] buffer, int offset, int count) @@ -57,7 +57,7 @@ public byte[] ComputeHash(byte[] buffer, int offset, int count) throw new ObjectDisposedException(null); HashCore(buffer, offset, count); - return CaptureHashCodeAndReinitialize(); + return CaptureHashCodeAndReinitialize(count); } public byte[] ComputeHash(Stream inputStream) @@ -68,20 +68,40 @@ public byte[] ComputeHash(Stream inputStream) // Default the buffer size to 4K. byte[] buffer = new byte[4096]; int bytesRead; + int bytesRead_firstBlock = 0; while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) { + if (bytesRead_firstBlock == 0) + { + bytesRead_firstBlock = bytesRead; + } HashCore(buffer, 0, bytesRead); } - return CaptureHashCodeAndReinitialize(); + return CaptureHashCodeAndReinitialize(bytesRead_firstBlock); } - private byte[] CaptureHashCodeAndReinitialize() + private byte[] CaptureHashCodeAndReinitialize(int inputCount) { - HashValue = HashFinal(); + if (_mustCallInitialize) + { + if (inputCount > 0) + { + // Emulate desktop semantics where ComputeHash (which calls Initialize) + // must be called after TransformFinalBlock. + throw new CryptographicException(SR.Cryptography_BadHashState); + } + // Keep previously created HashValue + } + else + { + HashValue = HashFinal(); + } + // Clone the hash value prior to invoking Initialize in case the user-defined Initialize // manipulates the array. byte[] tmp = (byte[])HashValue.Clone(); Initialize(); + _mustCallInitialize = false; return tmp; } @@ -128,41 +148,23 @@ public virtual bool CanReuseTransform public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { - // Do some validation, we let BlockCopy do the destination array validation - if (inputBuffer == null) - throw new ArgumentNullException(nameof(inputBuffer)); - if (inputOffset < 0) - throw new ArgumentOutOfRangeException(nameof(inputOffset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (inputCount < 0 || inputCount > inputBuffer.Length) - throw new ArgumentException(SR.Argument_InvalidValue); - if ((inputBuffer.Length - inputCount) < inputOffset) - throw new ArgumentException(SR.Argument_InvalidOffLen); - - if (_disposed) - throw new ObjectDisposedException(null); + ValidateTransformBlock(inputBuffer, inputOffset, inputCount); // Change the State value State = 1; HashCore(inputBuffer, inputOffset, inputCount); if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))) + { + // We let BlockCopy do the destination array validation Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); + } return inputCount; } public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { - if (inputBuffer == null) - throw new ArgumentNullException(nameof(inputBuffer)); - if (inputOffset < 0) - throw new ArgumentOutOfRangeException(nameof(inputOffset), SR.ArgumentOutOfRange_NeedNonNegNum); - if (inputCount < 0 || inputCount > inputBuffer.Length) - throw new ArgumentException(SR.Argument_InvalidValue); - if ((inputBuffer.Length - inputCount) < inputOffset) - throw new ArgumentException(SR.Argument_InvalidOffLen); - - if (_disposed) - throw new ObjectDisposedException(null); + ValidateTransformBlock(inputBuffer, inputOffset, inputCount); HashCore(inputBuffer, inputOffset, inputCount); HashValue = HashFinal(); @@ -180,14 +182,38 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input // Reset the State value State = 0; + // Ensure ComputeHash is called before another TranformsBlock\TransformFinalBlock + _mustCallInitialize = true; + return outputBytes; } + private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount) + { + if (inputBuffer == null) + throw new ArgumentNullException(nameof(inputBuffer)); + if (inputOffset < 0) + throw new ArgumentOutOfRangeException(nameof(inputOffset), SR.ArgumentOutOfRange_NeedNonNegNum); + if (inputCount < 0 || inputCount > inputBuffer.Length) + throw new ArgumentException(SR.Argument_InvalidValue); + if ((inputBuffer.Length - inputCount) < inputOffset) + throw new ArgumentException(SR.Argument_InvalidOffLen); + + if (_disposed) + throw new ObjectDisposedException(null); + + // Emulate desktop semantics where ComputeHash (which calls Initialize) + // must be called after TransformFinalBlock. + if (_mustCallInitialize && inputCount > 0) + throw new CryptographicException(SR.Cryptography_BadHashState); + } + protected abstract void HashCore(byte[] array, int ibStart, int cbSize); protected abstract byte[] HashFinal(); public abstract void Initialize(); private bool _disposed; + private bool _mustCallInitialize; protected internal byte[] HashValue; protected int State = 0; } From b4f6fc46e2be077cc7813dc99409f6a5a9d77f71 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 29 Sep 2016 11:04:51 -0500 Subject: [PATCH 6/7] HashAlgorithm netfx compat refactor --- .../Security/Cryptography/HashAlgorithm.cs | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index 4b488d130c0d..6ea7c462aab7 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -38,8 +38,8 @@ public byte[] ComputeHash(byte[] buffer) if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - HashCore(buffer, 0, buffer.Length); - return CaptureHashCodeAndReinitialize(buffer.Length); + HashCoreInternal(buffer, 0, buffer.Length); + return CaptureHashCodeAndReinitialize(); } public byte[] ComputeHash(byte[] buffer, int offset, int count) @@ -56,8 +56,8 @@ public byte[] ComputeHash(byte[] buffer, int offset, int count) if (_disposed) throw new ObjectDisposedException(null); - HashCore(buffer, offset, count); - return CaptureHashCodeAndReinitialize(count); + HashCoreInternal(buffer, offset, count); + return CaptureHashCodeAndReinitialize(); } public byte[] ComputeHash(Stream inputStream) @@ -68,29 +68,29 @@ public byte[] ComputeHash(Stream inputStream) // Default the buffer size to 4K. byte[] buffer = new byte[4096]; int bytesRead; - int bytesRead_firstBlock = 0; while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) { - if (bytesRead_firstBlock == 0) - { - bytesRead_firstBlock = bytesRead; - } - HashCore(buffer, 0, bytesRead); + HashCoreInternal(buffer, 0, bytesRead); } - return CaptureHashCodeAndReinitialize(bytesRead_firstBlock); + return CaptureHashCodeAndReinitialize(); } - private byte[] CaptureHashCodeAndReinitialize(int inputCount) + private void HashCoreInternal(byte[] array, int ibStart, int cbSize) { - if (_mustCallInitialize) + // Emulate desktop semantics where HashCore(nonempty) cannot be + // called after TransformFinalBlock\HashFinal. + if (_hashFinalCalledWithoutInitialize && cbSize > 0) + throw new CryptographicException(SR.Cryptography_BadHashState); + + HashCore(array, ibStart, cbSize); + } + + private byte[] CaptureHashCodeAndReinitialize() + { + if (_hashFinalCalledWithoutInitialize) { - if (inputCount > 0) - { - // Emulate desktop semantics where ComputeHash (which calls Initialize) - // must be called after TransformFinalBlock. - throw new CryptographicException(SR.Cryptography_BadHashState); - } - // Keep previously created HashValue + // Keep the previous hash value for desktop compat; the previous call to + // HashCore was for zero bytes. } else { @@ -101,7 +101,7 @@ private byte[] CaptureHashCodeAndReinitialize(int inputCount) // manipulates the array. byte[] tmp = (byte[])HashValue.Clone(); Initialize(); - _mustCallInitialize = false; + _hashFinalCalledWithoutInitialize = false; return tmp; } @@ -153,7 +153,7 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b // Change the State value State = 1; - HashCore(inputBuffer, inputOffset, inputCount); + HashCoreInternal(inputBuffer, inputOffset, inputCount); if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))) { // We let BlockCopy do the destination array validation @@ -166,7 +166,7 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input { ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - HashCore(inputBuffer, inputOffset, inputCount); + HashCoreInternal(inputBuffer, inputOffset, inputCount); HashValue = HashFinal(); byte[] outputBytes; if (inputCount != 0) @@ -182,8 +182,9 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input // Reset the State value State = 0; - // Ensure ComputeHash is called before another TranformsBlock\TransformFinalBlock - _mustCallInitialize = true; + // For desktop compat, set a flag to enforce that ComputeHash must be called + // with non-empty value before another HashCore. + _hashFinalCalledWithoutInitialize = true; return outputBytes; } @@ -201,11 +202,6 @@ private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inp if (_disposed) throw new ObjectDisposedException(null); - - // Emulate desktop semantics where ComputeHash (which calls Initialize) - // must be called after TransformFinalBlock. - if (_mustCallInitialize && inputCount > 0) - throw new CryptographicException(SR.Cryptography_BadHashState); } protected abstract void HashCore(byte[] array, int ibStart, int cbSize); @@ -213,7 +209,7 @@ private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inp public abstract void Initialize(); private bool _disposed; - private bool _mustCallInitialize; + private bool _hashFinalCalledWithoutInitialize; protected internal byte[] HashValue; protected int State = 0; } From 48f63a44ef2e2afb7193cfe7516dc30ac935f31e Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 4 Oct 2016 13:06:33 -0500 Subject: [PATCH 7/7] TransformFinalBlock assumes Initialize semantics --- .../tests/HashAlgorithmTest.cs | 90 +++++++++---------- ...urity.Cryptography.Algorithms.Tests.csproj | 6 +- .../Security/Cryptography/HashAlgorithm.cs | 38 ++------ 3 files changed, 54 insertions(+), 80 deletions(-) diff --git a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs index a90af17cbbf1..d5117503e938 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs +++ b/src/System.Security.Cryptography.Algorithms/tests/HashAlgorithmTest.cs @@ -63,30 +63,58 @@ protected void VerifyMultiBlock(string block1, string block2, string expectedHas byte[] expected_bytes = ByteUtils.HexToByteArray(expectedHash); byte[] emptyHash_bytes = ByteUtils.HexToByteArray(emptyHash); - VerifyTransformOutput(block1_bytes, block2_bytes); - VerifyTransformComputeHashInteraction(block1_bytes, block2_bytes, expected_bytes, emptyHash_bytes); + VerifyTransformBlockOutput(block1_bytes, block2_bytes); + VerifyTransformBlockHash(block1_bytes, block2_bytes, expected_bytes, emptyHash_bytes); + VerifyTransformBlockComputeHashInteraction(block1_bytes, block2_bytes, expected_bytes, emptyHash_bytes); #endif } #if netstandard17 - private void VerifyTransformOutput(byte[] block1, byte[] block2) + private void VerifyTransformBlockOutput(byte[] block1, byte[] block2) { - byte[] output = new byte[block1.Length + block2.Length]; + using (HashAlgorithm hash = Create()) + { + byte[] actualBlock1 = new byte[block1.Length]; + int byteCount = hash.TransformBlock(block1, 0, block1.Length, actualBlock1, 0); + Assert.Equal(block1.Length, byteCount); + Assert.Equal(block1, actualBlock1); - // Concat block1 and block2 - byte[] expected = new byte[block1.Length + block2.Length]; - Buffer.BlockCopy(block1, 0, expected, 0, block1.Length); - Buffer.BlockCopy(block2, 0, expected, block1.Length, block2.Length); + byte[] actualBlock2 = hash.TransformFinalBlock(block2, 0, block2.Length); + Assert.Equal(block2, actualBlock2); + } + } + private void VerifyTransformBlockHash(byte[] block1, byte[] block2, byte[] expected, byte[] expectedEmpty) + { using (HashAlgorithm hash = Create()) { - int byteCount = hash.TransformBlock(block1, 0, block1.Length, output, 0); - hash.TransformBlock(block2, 0, block2.Length, output, byteCount); - Assert.Equal(expected, output); + // Verify Empty Hash + hash.TransformBlock(Array.Empty(), 0, 0, null, 0); + hash.TransformFinalBlock(Array.Empty(), 0, 0); + Assert.Equal(hash.Hash, expectedEmpty); + hash.TransformFinalBlock(Array.Empty(), 0, 0); + Assert.Equal(hash.Hash, expectedEmpty); + + // Verify Hash + hash.TransformBlock(block1, 0, block1.Length, null, 0); + hash.TransformFinalBlock(block2, 0, block2.Length); + Assert.Equal(expected, hash.Hash); + Assert.Equal(expected, hash.Hash); // .Hash doesn't clear hash + + // Verify bad State + hash.TransformBlock(block1, 0, block1.Length, null, 0); + // Can't access hash until TransformFinalBlock is called + Assert.Throws(() => hash.Hash); + hash.TransformFinalBlock(block2, 0, block2.Length); + Assert.Equal(expected, hash.Hash); + + // Verify clean State + hash.TransformFinalBlock(Array.Empty(), 0, 0); + Assert.Equal(hash.Hash, expectedEmpty); } } - private void VerifyTransformComputeHashInteraction(byte[] block1, byte[] block2, byte[] expected, byte[] expectedEmpty) + private void VerifyTransformBlockComputeHashInteraction(byte[] block1, byte[] block2, byte[] expected, byte[] expectedEmpty) { using (HashAlgorithm hash = Create()) { @@ -95,24 +123,13 @@ private void VerifyTransformComputeHashInteraction(byte[] block1, byte[] block2, byte[] actual = hash.ComputeHash(block2, 0, block2.Length); Assert.Equal(expected, actual); - // Verify ComputeHash above cleared hash + // ComputeHash does not reset State variable + Assert.Throws(() => hash.Hash); + hash.TransformFinalBlock(Array.Empty(), 0, 0); + Assert.Equal(expectedEmpty, hash.Hash); actual = hash.ComputeHash(Array.Empty(), 0, 0); Assert.Equal(expectedEmpty, actual); - // We should now be able to re-hash - hash.TransformBlock(block1, 0, block1.Length, null, 0); - actual = hash.ComputeHash(block2, 0, block2.Length); - Assert.Equal(expected, actual); - - // TransformBlock + TransformFinalBlock - hash.TransformBlock(block1, 0, block1.Length, null, 0); - hash.TransformFinalBlock(block2, 0, block2.Length); - Assert.Equal(expected, hash.Hash); - Assert.Equal(expected, hash.Hash); // .Hash doesn't clear hash - // ComputeHash with 0 bytes resets without interfering with hash - actual = hash.ComputeHash(Array.Empty(), 0, 0); - Assert.Equal(expected, actual); - // TransformBlock + TransformBlock + ComputeHash(empty) hash.TransformBlock(block1, 0, block1.Length, null, 0); hash.TransformBlock(block2, 0, block2.Length, null, 0); @@ -144,25 +161,6 @@ public void VerifyHashNotYetFinalized() } } - [Fact] - public void VerifyUseAfterTransformFinalBlock() - { - byte[] block = new byte[1] {1}; - - using (HashAlgorithm hash = Create()) - { - hash.TransformBlock(block, 0, block.Length, null, 0); - hash.TransformFinalBlock(block, 0, block.Length); - - Assert.Throws(() => hash.TransformBlock(block, 0, block.Length, null, 0)); - Assert.Throws(() => hash.TransformFinalBlock(block, 0, block.Length)); - - // Transforming 0 bytes should not throw - hash.TransformBlock(block, 0, 0, null, 0); - hash.TransformFinalBlock(block, 0, 0); - } - } - [Fact] public void InvalidInput_ComputeHash() { diff --git a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 28782b15dd6a..d0434bda541b 100644 --- a/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -10,8 +10,8 @@ Library System.Security.Cryptography.Algorithms.Tests System.Security.Cryptography.Algorithms.Tests - $(DefineConstants);netstandard17 - .NETStandard,Version=v1.6 + $(DefineConstants);netstandard17 + .NETStandard,Version=v1.7 @@ -132,7 +132,7 @@ - + diff --git a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs index 6ea7c462aab7..6c3792e7ded6 100644 --- a/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs +++ b/src/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/HashAlgorithm.cs @@ -38,7 +38,7 @@ public byte[] ComputeHash(byte[] buffer) if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - HashCoreInternal(buffer, 0, buffer.Length); + HashCore(buffer, 0, buffer.Length); return CaptureHashCodeAndReinitialize(); } @@ -56,7 +56,7 @@ public byte[] ComputeHash(byte[] buffer, int offset, int count) if (_disposed) throw new ObjectDisposedException(null); - HashCoreInternal(buffer, offset, count); + HashCore(buffer, offset, count); return CaptureHashCodeAndReinitialize(); } @@ -70,38 +70,19 @@ public byte[] ComputeHash(Stream inputStream) int bytesRead; while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) { - HashCoreInternal(buffer, 0, bytesRead); + HashCore(buffer, 0, bytesRead); } return CaptureHashCodeAndReinitialize(); } - private void HashCoreInternal(byte[] array, int ibStart, int cbSize) - { - // Emulate desktop semantics where HashCore(nonempty) cannot be - // called after TransformFinalBlock\HashFinal. - if (_hashFinalCalledWithoutInitialize && cbSize > 0) - throw new CryptographicException(SR.Cryptography_BadHashState); - - HashCore(array, ibStart, cbSize); - } - private byte[] CaptureHashCodeAndReinitialize() { - if (_hashFinalCalledWithoutInitialize) - { - // Keep the previous hash value for desktop compat; the previous call to - // HashCore was for zero bytes. - } - else - { - HashValue = HashFinal(); - } + HashValue = HashFinal(); // Clone the hash value prior to invoking Initialize in case the user-defined Initialize // manipulates the array. byte[] tmp = (byte[])HashValue.Clone(); Initialize(); - _hashFinalCalledWithoutInitialize = false; return tmp; } @@ -153,7 +134,7 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b // Change the State value State = 1; - HashCoreInternal(inputBuffer, inputOffset, inputCount); + HashCore(inputBuffer, inputOffset, inputCount); if ((outputBuffer != null) && ((inputBuffer != outputBuffer) || (inputOffset != outputOffset))) { // We let BlockCopy do the destination array validation @@ -166,8 +147,8 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input { ValidateTransformBlock(inputBuffer, inputOffset, inputCount); - HashCoreInternal(inputBuffer, inputOffset, inputCount); - HashValue = HashFinal(); + HashCore(inputBuffer, inputOffset, inputCount); + HashValue = CaptureHashCodeAndReinitialize(); byte[] outputBytes; if (inputCount != 0) { @@ -182,10 +163,6 @@ public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int input // Reset the State value State = 0; - // For desktop compat, set a flag to enforce that ComputeHash must be called - // with non-empty value before another HashCore. - _hashFinalCalledWithoutInitialize = true; - return outputBytes; } @@ -209,7 +186,6 @@ private void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inp public abstract void Initialize(); private bool _disposed; - private bool _hashFinalCalledWithoutInitialize; protected internal byte[] HashValue; protected int State = 0; }