Implement Micrososoft.Bcl.Cryptography without System.Security.Cryptography.Algorithm...
authorKevin Jones <kevin@vcsjones.com>
Wed, 3 May 2023 06:05:42 +0000 (02:05 -0400)
committerGitHub <noreply@github.com>
Wed, 3 May 2023 06:05:42 +0000 (08:05 +0200)
* Implement Micrososoft.Bcl.Cryptography without System.Security.Cryptography.Algorithms

* Assert data written

* Call Initialize on the right thing

src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj
src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdfImplementationCng.cs
src/libraries/Microsoft.Bcl.Cryptography/src/System/Security/Cryptography/SP800108HmacCounterKdfImplementationManaged.cs

index ba7b0ce..0c5d787 100644 (file)
@@ -72,8 +72,4 @@ System.Security.Cryptography.SP800108HmacCounterKdf</PackageDescription>
   <ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
     <PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
   </ItemGroup>
-
-  <ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
-    <PackageReference Include="System.Security.Cryptography.Algorithms" Version="$(SystemSecurityCryptographyAlgorithmsVersion)" />
-  </ItemGroup>
 </Project>
index 8252fc3..4370fd7 100644 (file)
@@ -103,10 +103,28 @@ namespace System.Security.Cryptography
 
         private static byte[] HashOneShot(HashAlgorithmName hashAlgorithm, byte[] data)
         {
-            using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm))
+            using (HashAlgorithm hash = CreateHash(hashAlgorithm))
             {
-                hash.AppendData(data);
-                return hash.GetHashAndReset();
+                return hash.ComputeHash(data);
+            }
+        }
+
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "Weak algorithms are used as instructed by the caller")]
+        private static HashAlgorithm CreateHash(HashAlgorithmName hashAlgorithm)
+        {
+            switch (hashAlgorithm.Name)
+            {
+                case HashAlgorithmNames.SHA1:
+                    return SHA1.Create();
+                case HashAlgorithmNames.SHA256:
+                    return SHA256.Create();
+                case HashAlgorithmNames.SHA384:
+                    return SHA384.Create();
+                case HashAlgorithmNames.SHA512:
+                    return SHA512.Create();
+                default:
+                    Debug.Fail($"Unexpected hash algorithm '{hashAlgorithm.Name}'.");
+                    throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name));
             }
         }
     }
index 60e438f..025e037 100644 (file)
@@ -139,7 +139,7 @@ namespace System.Security.Cryptography
                 // The KDF is defined as K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2)
                 // We know L is already less than 0x1FFFFFFF. h = ceil(L / h) where H is the hash length in bits.
                 // So we don't expect i to overflow.
-                using (IncrementalHash hash = IncrementalHash.CreateHMAC(hashAlgorithm, key))
+                using (HMAC hash = CreateHMAC(hashAlgorithm, key))
                 {
                     // We use this rented buffer for three things. The first two uints for i and L, and last byte
                     // for the zero separator. So this is
@@ -165,19 +165,30 @@ namespace System.Security.Cryptography
                         for (uint i = 1; !destination.IsEmpty; i++)
                         {
                             WriteUInt32BigEndian(i, rentedBuffer.AsSpan(IOffset, ILength));
-                            hash.AppendData(rentedBuffer, IOffset, ILength);
-                            hash.AppendData(label, 0, labelLength);
-                            hash.AppendData(rentedBuffer, ZeroOffset, ZeroLength);
-                            hash.AppendData(context, 0, contextLength);
-                            hash.AppendData(rentedBuffer, LOffset, LLength);
-
-                            byte[] hmac = hash.GetHashAndReset();
+                            int written;
+                            written = hash.TransformBlock(rentedBuffer, IOffset, ILength, null, 0);
+                            Debug.Assert(written == ILength);
+                            written = hash.TransformBlock(label, 0, labelLength, null, 0);
+                            Debug.Assert(written == labelLength);
+                            written = hash.TransformBlock(rentedBuffer, ZeroOffset, ZeroLength, null, 0);
+                            Debug.Assert(written == ZeroLength);
+                            written = hash.TransformBlock(context, 0, contextLength, null, 0);
+                            Debug.Assert(written == contextLength);
+                            written = hash.TransformBlock(rentedBuffer, LOffset, LLength, null, 0);
+                            Debug.Assert(written == LLength);
+
+                            // Use an empty input for the final transform so that the returned value isn't something
+                            // we need to clear since the return value is the same as the input.
+                            hash.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
+
+                            byte[] hmac = hash.Hash;
                             int needed = Math.Min(destination.Length, hmac.Length);
                             hmac.AsSpan(0, needed).CopyTo(destination);
                             destination = destination.Slice(needed);
 
                             // Best effort to zero out the key material.
                             CryptographicOperations.ZeroMemory(hmac);
+                            hash.Initialize();
                         }
                     }
                     finally
@@ -199,5 +210,24 @@ namespace System.Security.Cryptography
             destination[2] = (byte)(value >> 8);
             destination[3] = (byte)(value);
         }
+
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "Weak algorithms are used as instructed by the caller")]
+        private static HMAC CreateHMAC(HashAlgorithmName hashAlgorithm, byte[] key)
+        {
+            switch (hashAlgorithm.Name)
+            {
+                case HashAlgorithmNames.SHA1:
+                    return new HMACSHA1(key);
+                case HashAlgorithmNames.SHA256:
+                    return new HMACSHA256(key);
+                case HashAlgorithmNames.SHA384:
+                    return new HMACSHA384(key);
+                case HashAlgorithmNames.SHA512:
+                    return new HMACSHA512(key);
+                default:
+                    Debug.Fail($"Unexpected HMAC algorithm '{hashAlgorithm.Name}'.");
+                    throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name));
+            }
+        }
     }
 }