Use managed implementation of pbkdf2 for Android's one-shot implementation. (#49314)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Wed, 10 Mar 2021 00:26:31 +0000 (16:26 -0800)
committerGitHub <noreply@github.com>
Wed, 10 Mar 2021 00:26:31 +0000 (16:26 -0800)
src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs [new file with mode: 0644]
src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs

diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs
new file mode 100644 (file)
index 0000000..c0ab7ee
--- /dev/null
@@ -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.
+
+using System;
+using System.Diagnostics;
+using System.Security.Cryptography;
+
+namespace Internal.Cryptography
+{
+    internal partial class Pbkdf2Implementation
+    {
+        public static unsafe void Fill(
+            ReadOnlySpan<byte> password,
+            ReadOnlySpan<byte> salt,
+            int iterations,
+            HashAlgorithmName hashAlgorithmName,
+            Span<byte> destination)
+        {
+            Debug.Assert(!destination.IsEmpty);
+            Debug.Assert(hashAlgorithmName.Name is not null);
+            // Fall back to managed implementation since Android doesn't support the full Pbkdf2 APIs
+            // until API level 26.
+            using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(
+                password.ToArray(),
+                salt.ToArray(),
+                iterations,
+                hashAlgorithmName,
+                clearPassword: true,
+                requireMinimumSaltLength: false))
+            {
+                byte[] result = deriveBytes.GetBytes(destination.Length);
+                result.AsSpan().CopyTo(destination);
+            }
+        }
+    }
+}
index e7c4bfa..15af720 100644 (file)
     <Compile Include="Internal\Cryptography\DesImplementation.Unix.cs" />
     <Compile Include="Internal\Cryptography\HashProviderDispenser.Unix.cs" />
     <Compile Include="Internal\Cryptography\OpenSslCipher.cs" />
-    <Compile Condition="'$(TargetsBrowser)' != 'true'" Include="Internal\Cryptography\RandomNumberGeneratorImplementation.Unix.cs" />
-    <Compile Condition="'$(TargetsBrowser)' != 'true'" Include="Internal\Cryptography\Pbkdf2Implementation.Unix.cs" />
+    <Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.Unix.cs" />
     <Compile Include="Internal\Cryptography\TripleDesImplementation.Unix.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(UseAppleCrypto)' == 'true'">
              Link="Common\System\Security\Cryptography\RSAOpenSsl.cs" />
     <Compile Include="Internal\Cryptography\RC2Implementation.Unix.cs" />
     <Compile Include="System\Security\Cryptography\ECDiffieHellman.Create.OpenSsl.cs" />
+    <Compile Include="Internal\Cryptography\Pbkdf2Implementation.Unix.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.Dsa.cs"
              Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Dsa.cs" />
     <Compile Include="$(CommonPath)System\Security\Cryptography\DSAOpenSsl.cs"
     <Compile Include="System\Security\Cryptography\ECDiffieHellman.Create.Android.cs" />
     <Compile Include="System\Security\Cryptography\ECDsa.Create.Android.cs" />
     <Compile Include="System\Security\Cryptography\RSA.Create.Android.cs" />
+    <Compile Include="Internal\Cryptography\Pbkdf2Implementation.Android.cs" />
   </ItemGroup>
   <ItemGroup Condition=" '$(TargetsBrowser)' == 'true'">
     <Compile Include="$(CommonPath)Internal\Cryptography\HashProvider.cs"
index 7c15015..2648b26 100644 (file)
@@ -37,7 +37,7 @@ namespace System.Security.Cryptography
         }
 
         public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm)
-            :this(password, salt, iterations, hashAlgorithm, clearPassword: false)
+            :this(password, salt, iterations, hashAlgorithm, clearPassword: false, requireMinimumSaltLength: true)
         {
         }
 
@@ -52,7 +52,7 @@ namespace System.Security.Cryptography
         }
 
         public Rfc2898DeriveBytes(string password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm)
-            : this(Encoding.UTF8.GetBytes(password), salt, iterations, hashAlgorithm, clearPassword: true)
+            : this(Encoding.UTF8.GetBytes(password), salt, iterations, hashAlgorithm, clearPassword: true, requireMinimumSaltLength: true)
         {
         }
 
@@ -89,11 +89,11 @@ namespace System.Security.Cryptography
             Initialize();
         }
 
-        private Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword)
+        internal Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword, bool requireMinimumSaltLength)
         {
             if (salt is null)
                 throw new ArgumentNullException(nameof(salt));
-            if (salt.Length < MinimumSaltSize)
+            if (requireMinimumSaltLength && salt.Length < MinimumSaltSize)
                 throw new ArgumentException(SR.Cryptography_PasswordDerivedBytes_FewBytesSalt, nameof(salt));
             if (iterations <= 0)
                 throw new ArgumentOutOfRangeException(nameof(iterations), SR.ArgumentOutOfRange_NeedPosNum);