// 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.Text; using Microsoft.AspNetCore.Cryptography.SafeHandles; using Microsoft.AspNetCore.DataProtection.Test.Shared; using Microsoft.AspNetCore.InternalTesting; using Xunit; namespace Microsoft.AspNetCore.Cryptography.Cng; // This class tests both the properties and the output of hash algorithms. // It only tests the properties of the encryption algorithms. // Output of the encryption and key derivatoin functions are tested by other projects. public unsafe class CachedAlgorithmHandlesTests { private static readonly byte[] _dataToHash = Encoding.UTF8.GetBytes("Sample input data."); private static readonly byte[] _hmacKey = Encoding.UTF8.GetBytes("Secret key material."); [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void AES_CBC_Cached_Handle() { RunAesBlockCipherAlgorithmTest(() => CachedAlgorithmHandles.AES_CBC); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void AES_GCM_Cached_Handle() { RunAesBlockCipherAlgorithmTest(() => CachedAlgorithmHandles.AES_GCM); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA1_Cached_Handle_No_HMAC() { RunHashAlgorithmTest_No_HMAC( getter: () => CachedAlgorithmHandles.SHA1, expectedAlgorithmName: "SHA1", expectedBlockSizeInBytes: 512 / 8, expectedDigestSizeInBytes: 160 / 8, expectedDigest: "MbYo3dZmXtgUZcUoWoxkCDKFvkk="); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA1_Cached_Handle_With_HMAC() { RunHashAlgorithmTest_With_HMAC( getter: () => CachedAlgorithmHandles.HMAC_SHA1, expectedAlgorithmName: "SHA1", expectedBlockSizeInBytes: 512 / 8, expectedDigestSizeInBytes: 160 / 8, expectedDigest: "PjYTgLTWkt6NeH0NudIR7N47Ipg="); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA256_Cached_Handle_No_HMAC() { RunHashAlgorithmTest_No_HMAC( getter: () => CachedAlgorithmHandles.SHA256, expectedAlgorithmName: "SHA256", expectedBlockSizeInBytes: 512 / 8, expectedDigestSizeInBytes: 256 / 8, expectedDigest: "5uRfQadsrnUTa3/TEo5PP6SDZQkb9AcE4wNXDVcM0Fo="); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA256_Cached_Handle_With_HMAC() { RunHashAlgorithmTest_With_HMAC( getter: () => CachedAlgorithmHandles.HMAC_SHA256, expectedAlgorithmName: "SHA256", expectedBlockSizeInBytes: 512 / 8, expectedDigestSizeInBytes: 256 / 8, expectedDigest: "KLzo0lVg5gZkpL5D6Ck7QT8w4iuPCe/pGCrMcOXWbKY="); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA512_Cached_Handle_No_HMAC() { RunHashAlgorithmTest_No_HMAC( getter: () => CachedAlgorithmHandles.SHA512, expectedAlgorithmName: "SHA512", expectedBlockSizeInBytes: 1024 / 8, expectedDigestSizeInBytes: 512 / 8, expectedDigest: "jKI7WrcgPP7n2HAYOb8uFRi7xEsNG/BmdGd18dwwkIpqJ4Vmlk2b+8hssLyMQlprTSKVJNObSiYUqW5THS7okw=="); } [ConditionalFact] [ConditionalRunTestOnlyOnWindows] public void SHA512_Cached_Handle_With_HMAC() { RunHashAlgorithmTest_With_HMAC( getter: () => CachedAlgorithmHandles.HMAC_SHA512, expectedAlgorithmName: "SHA512", expectedBlockSizeInBytes: 1024 / 8, expectedDigestSizeInBytes: 512 / 8, expectedDigest: "pKTX5vtPtbsn7pX9ISDlOYr1NFklTBIPYAFICy0ZQbFc0QVzGaTUvtqTOi91I0sHa1DIod6uIogux5iLdHjfcA=="); } private static void RunAesBlockCipherAlgorithmTest(Func getter) { // Getter must return the same instance of the cached handle var algorithmHandle = getter(); var algorithmHandleSecondAttempt = getter(); Assert.NotNull(algorithmHandle); Assert.Same(algorithmHandle, algorithmHandleSecondAttempt); // Validate that properties are what we expect Assert.Equal("AES", algorithmHandle.GetAlgorithmName()); Assert.Equal((uint)(128 / 8), algorithmHandle.GetCipherBlockLength()); var supportedKeyLengths = algorithmHandle.GetSupportedKeyLengths(); Assert.Equal(128U, supportedKeyLengths.dwMinLength); Assert.Equal(256U, supportedKeyLengths.dwMaxLength); Assert.Equal(64U, supportedKeyLengths.dwIncrement); } private static void RunHashAlgorithmTest_No_HMAC( Func getter, string expectedAlgorithmName, uint expectedBlockSizeInBytes, uint expectedDigestSizeInBytes, string expectedDigest) { // Getter must return the same instance of the cached handle var algorithmHandle = getter(); var algorithmHandleSecondAttempt = getter(); Assert.NotNull(algorithmHandle); Assert.Same(algorithmHandle, algorithmHandleSecondAttempt); // Validate that properties are what we expect Assert.Equal(expectedAlgorithmName, algorithmHandle.GetAlgorithmName()); Assert.Equal(expectedBlockSizeInBytes, algorithmHandle.GetHashBlockLength()); Assert.Equal(expectedDigestSizeInBytes, algorithmHandle.GetHashDigestLength()); // Perform the digest calculation and validate against our expectation var hashHandle = algorithmHandle.CreateHash(); byte[] outputHash = new byte[expectedDigestSizeInBytes]; fixed (byte* pInput = _dataToHash) { fixed (byte* pOutput = outputHash) { hashHandle.HashData(pInput, (uint)_dataToHash.Length, pOutput, (uint)outputHash.Length); } } Assert.Equal(expectedDigest, Convert.ToBase64String(outputHash)); } private static void RunHashAlgorithmTest_With_HMAC( Func getter, string expectedAlgorithmName, uint expectedBlockSizeInBytes, uint expectedDigestSizeInBytes, string expectedDigest) { // Getter must return the same instance of the cached handle var algorithmHandle = getter(); var algorithmHandleSecondAttempt = getter(); Assert.NotNull(algorithmHandle); Assert.Same(algorithmHandle, algorithmHandleSecondAttempt); // Validate that properties are what we expect Assert.Equal(expectedAlgorithmName, algorithmHandle.GetAlgorithmName()); Assert.Equal(expectedBlockSizeInBytes, algorithmHandle.GetHashBlockLength()); Assert.Equal(expectedDigestSizeInBytes, algorithmHandle.GetHashDigestLength()); // Perform the digest calculation and validate against our expectation fixed (byte* pKey = _hmacKey) { var hashHandle = algorithmHandle.CreateHmac(pKey, (uint)_hmacKey.Length); byte[] outputHash = new byte[expectedDigestSizeInBytes]; fixed (byte* pInput = _dataToHash) { fixed (byte* pOutput = outputHash) { hashHandle.HashData(pInput, (uint)_dataToHash.Length, pOutput, (uint)outputHash.Length); } } Assert.Equal(expectedDigest, Convert.ToBase64String(outputHash)); } } }