diff --git a/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs b/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs index ce402e6d386bd8..a992ffcf0392fb 100644 --- a/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs +++ b/src/libraries/System.Security.Cryptography.Csp/ref/System.Security.Cryptography.Csp.cs @@ -144,13 +144,17 @@ public partial class PasswordDeriveBytes : System.Security.Cryptography.DeriveBy { public PasswordDeriveBytes(byte[] password, byte[]? salt) { } public PasswordDeriveBytes(byte[] password, byte[]? salt, System.Security.Cryptography.CspParameters? cspParams) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed.")] public PasswordDeriveBytes(byte[] password, byte[]? salt, string hashName, int iterations) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed.")] public PasswordDeriveBytes(byte[] password, byte[]? salt, string hashName, int iterations, System.Security.Cryptography.CspParameters? cspParams) { } public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt) { } public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, System.Security.Cryptography.CspParameters? cspParams) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed.")] public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, string strHashName, int iterations) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed.")] public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, string strHashName, int iterations, System.Security.Cryptography.CspParameters? cspParams) { } - public string HashName { get { throw null; } set { } } + public string HashName { get { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed.")] set { } } public int IterationCount { get { throw null; } set { } } public byte[]? Salt { get { throw null; } set { } } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] diff --git a/src/libraries/System.Security.Cryptography.Csp/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Security.Cryptography.Csp/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 0129976fe94ba4..00000000000000 --- a/src/libraries/System.Security.Cryptography.Csp/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Security.Cryptography.PasswordDeriveBytes.set_HashName(System.String) - - - \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/PasswordDeriveBytes.cs b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/PasswordDeriveBytes.cs index ee79a76f05cdf9..10e52dcf33fbea 100644 --- a/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/PasswordDeriveBytes.cs +++ b/src/libraries/System.Security.Cryptography.Csp/src/System/Security/Cryptography/PasswordDeriveBytes.cs @@ -14,6 +14,8 @@ namespace System.Security.Cryptography [EditorBrowsable(EditorBrowsableState.Never)] public partial class PasswordDeriveBytes : DeriveBytes { + private const string HashAlgorithmUnreferencedCodeMessage = "The hash implementation might be removed. Ensure the referenced hash algorithm is not trimmed."; + private int _extraCount; private int _prefix; private int _iterations; @@ -30,22 +32,30 @@ public partial class PasswordDeriveBytes : DeriveBytes public PasswordDeriveBytes(byte[] password, byte[]? salt) : this(password, salt, new CspParameters()) { } + [RequiresUnreferencedCode(HashAlgorithmUnreferencedCodeMessage)] public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, string strHashName, int iterations) : this(strPassword, rgbSalt, strHashName, iterations, new CspParameters()) { } + [RequiresUnreferencedCode(HashAlgorithmUnreferencedCodeMessage)] public PasswordDeriveBytes(byte[] password, byte[]? salt, string hashName, int iterations) : this(password, salt, hashName, iterations, new CspParameters()) { } #pragma warning restore CA1416 + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The correct hash algorithm is being preserved by the DynamicDependency.")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(SHA1CryptoServiceProvider))] public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, CspParameters? cspParams) : this(strPassword, rgbSalt, "SHA1", 100, cspParams) { } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The correct hash algorithm is being preserved by the DynamicDependency.")] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(SHA1CryptoServiceProvider))] public PasswordDeriveBytes(byte[] password, byte[]? salt, CspParameters? cspParams) : this(password, salt, "SHA1", 100, cspParams) { } + [RequiresUnreferencedCode(HashAlgorithmUnreferencedCodeMessage)] public PasswordDeriveBytes(string strPassword, byte[]? rgbSalt, string strHashName, int iterations, CspParameters? cspParams) : this((new UTF8Encoding(false)).GetBytes(strPassword), rgbSalt, strHashName, iterations, cspParams) { } + [RequiresUnreferencedCode(HashAlgorithmUnreferencedCodeMessage)] public PasswordDeriveBytes(byte[] password, byte[]? salt, string hashName, int iterations, CspParameters? cspParams) { IterationCount = iterations; @@ -58,6 +68,7 @@ public PasswordDeriveBytes(byte[] password, byte[]? salt, string hashName, int i public string HashName { get { return _hashName!; } + [RequiresUnreferencedCode(HashAlgorithmUnreferencedCodeMessage)] set { if (_baseValue != null) diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/PasswordDeriveBytesTest.cs b/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/PasswordDeriveBytesTest.cs new file mode 100644 index 00000000000000..4fb327b499d53d --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/PasswordDeriveBytesTest.cs @@ -0,0 +1,43 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Security.Cryptography; + +/// +/// Tests that using PasswordDeriveBytes without specifying a hash algorithm name +/// works correctly in a trimmed application. +/// +class Program +{ + static int Main() + { + string testPassword = "PasswordGoesHere"; + byte[] testSalt = new byte[] { 9, 5, 5, 5, 1, 2, 1, 2 }; + + byte[] expected = HexToByteArray("12F2497EC3EB78B0EA32AABFD8B9515FBC800BEEB6316A4DDF4EA62518341488A116DA3BBC26C685"); + + using (var deriveBytes = new PasswordDeriveBytes(testPassword, testSalt)) + { + byte[] output = deriveBytes.GetBytes(expected.Length); + if (output.SequenceEqual(expected)) + { + return 100; + } + } + + return -1; + } + + private static byte[] HexToByteArray(string hexString) + { + byte[] bytes = new byte[hexString.Length / 2]; + + for (int i = 0; i < hexString.Length; i += 2) + { + ReadOnlySpan s = hexString.AsSpan(i, 2); + bytes[i / 2] = byte.Parse(s, NumberStyles.HexNumber, null); + } + + return bytes; + } +} diff --git a/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/System.Security.Cryptography.Csp.TrimmingTests.proj b/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/System.Security.Cryptography.Csp.TrimmingTests.proj new file mode 100644 index 00000000000000..64b290af04e8bc --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/System.Security.Cryptography.Csp.TrimmingTests.proj @@ -0,0 +1,9 @@ + + + + + + + + +