// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Xml.Linq; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.Internal; using Microsoft.AspNetCore.DataProtection.KeyManagement; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Win32; using Xunit; namespace Microsoft.AspNetCore.DataProtection { public class RegistryPolicyResolverTests { [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_NoEntries_ResultsInNoPolicies() { // Arrange var registryEntries = new Dictionary(); // Act var context = RunTestWithRegValues(registryEntries); // Assert Assert.Null(context.EncryptorConfiguration); Assert.Null(context.DefaultKeyLifetime); Assert.Empty(context.KeyEscrowSinks); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_KeyEscrowSinks() { // Arrange var registryEntries = new Dictionary() { ["KeyEscrowSinks"] = String.Join(" ;; ; ", new Type[] { typeof(MyKeyEscrowSink1), typeof(MyKeyEscrowSink2) }.Select(t => t.AssemblyQualifiedName)) }; // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualKeyEscrowSinks = context.KeyEscrowSinks.ToArray(); Assert.Equal(2, actualKeyEscrowSinks.Length); Assert.IsType(actualKeyEscrowSinks[0]); Assert.IsType(actualKeyEscrowSinks[1]); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_DefaultKeyLifetime() { // Arrange var registryEntries = new Dictionary() { ["DefaultKeyLifetime"] = 1024 // days }; // Act var context = RunTestWithRegValues(registryEntries); // Assert Assert.Equal(1024, context.DefaultKeyLifetime); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_CngCbcEncryption_WithoutExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "cng-cbc" }; var expectedConfiguration = new CngCbcAuthenticatedEncryptorConfiguration(); // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (CngCbcAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithm, actualConfiguration.EncryptionAlgorithm); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.EncryptionAlgorithmProvider, actualConfiguration.EncryptionAlgorithmProvider); Assert.Equal(expectedConfiguration.HashAlgorithm, actualConfiguration.HashAlgorithm); Assert.Equal(expectedConfiguration.HashAlgorithmProvider, actualConfiguration.HashAlgorithmProvider); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_CngCbcEncryption_WithExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "cng-cbc", ["EncryptionAlgorithm"] = "enc-alg", ["EncryptionAlgorithmKeySize"] = 2048, ["EncryptionAlgorithmProvider"] = "my-enc-alg-provider", ["HashAlgorithm"] = "hash-alg", ["HashAlgorithmProvider"] = "my-hash-alg-provider" }; var expectedConfiguration = new CngCbcAuthenticatedEncryptorConfiguration() { EncryptionAlgorithm = "enc-alg", EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmProvider = "my-enc-alg-provider", HashAlgorithm = "hash-alg", HashAlgorithmProvider = "my-hash-alg-provider" }; // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (CngCbcAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithm, actualConfiguration.EncryptionAlgorithm); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.EncryptionAlgorithmProvider, actualConfiguration.EncryptionAlgorithmProvider); Assert.Equal(expectedConfiguration.HashAlgorithm, actualConfiguration.HashAlgorithm); Assert.Equal(expectedConfiguration.HashAlgorithmProvider, actualConfiguration.HashAlgorithmProvider); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_CngGcmEncryption_WithoutExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "cng-gcm" }; var expectedConfiguration = new CngGcmAuthenticatedEncryptorConfiguration(); // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (CngGcmAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithm, actualConfiguration.EncryptionAlgorithm); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.EncryptionAlgorithmProvider, actualConfiguration.EncryptionAlgorithmProvider); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_CngGcmEncryption_WithExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "cng-gcm", ["EncryptionAlgorithm"] = "enc-alg", ["EncryptionAlgorithmKeySize"] = 2048, ["EncryptionAlgorithmProvider"] = "my-enc-alg-provider" }; var expectedConfiguration = new CngGcmAuthenticatedEncryptorConfiguration() { EncryptionAlgorithm = "enc-alg", EncryptionAlgorithmKeySize = 2048, EncryptionAlgorithmProvider = "my-enc-alg-provider" }; // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (CngGcmAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithm, actualConfiguration.EncryptionAlgorithm); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.EncryptionAlgorithmProvider, actualConfiguration.EncryptionAlgorithmProvider); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_ManagedEncryption_WithoutExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "managed" }; var expectedConfiguration = new ManagedAuthenticatedEncryptorConfiguration(); // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (ManagedAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithmType, actualConfiguration.EncryptionAlgorithmType); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.ValidationAlgorithmType, actualConfiguration.ValidationAlgorithmType); } [ConditionalFact] [ConditionalRunTestOnlyIfHkcuRegistryAvailable] public void ResolvePolicy_ManagedEncryption_WithExplicitSettings() { // Arrange var registryEntries = new Dictionary() { ["EncryptionType"] = "managed", ["EncryptionAlgorithmType"] = typeof(TripleDES).AssemblyQualifiedName, ["EncryptionAlgorithmKeySize"] = 2048, ["ValidationAlgorithmType"] = typeof(HMACSHA1).AssemblyQualifiedName }; var expectedConfiguration = new ManagedAuthenticatedEncryptorConfiguration() { EncryptionAlgorithmType = typeof(TripleDES), EncryptionAlgorithmKeySize = 2048, ValidationAlgorithmType = typeof(HMACSHA1) }; // Act var context = RunTestWithRegValues(registryEntries); // Assert var actualConfiguration = (ManagedAuthenticatedEncryptorConfiguration)context.EncryptorConfiguration; Assert.Equal(expectedConfiguration.EncryptionAlgorithmType, actualConfiguration.EncryptionAlgorithmType); Assert.Equal(expectedConfiguration.EncryptionAlgorithmKeySize, actualConfiguration.EncryptionAlgorithmKeySize); Assert.Equal(expectedConfiguration.ValidationAlgorithmType, actualConfiguration.ValidationAlgorithmType); } private static RegistryPolicy RunTestWithRegValues(Dictionary regValues) { return WithUniqueTempRegKey(registryKey => { foreach (var entry in regValues) { registryKey.SetValue(entry.Key, entry.Value); } var policyResolver = new RegistryPolicyResolver( registryKey, activator: SimpleActivator.DefaultWithoutServices); return policyResolver.ResolvePolicy(); }); } /// /// Runs a test and cleans up the registry key afterward. /// private static RegistryPolicy WithUniqueTempRegKey(Func testCode) { string uniqueName = Guid.NewGuid().ToString(); var uniqueSubkey = LazyHkcuTempKey.Value.CreateSubKey(uniqueName); try { return testCode(uniqueSubkey); } finally { // clean up when test is done LazyHkcuTempKey.Value.DeleteSubKeyTree(uniqueName, throwOnMissingSubKey: false); } } private static readonly Lazy LazyHkcuTempKey = new Lazy(() => { try { return Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\ASP.NET\temp"); } catch { // swallow all failures return null; } }); private class ConditionalRunTestOnlyIfHkcuRegistryAvailable : Attribute, ITestCondition { public bool IsMet => (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && LazyHkcuTempKey.Value != null); public string SkipReason { get; } = "HKCU registry couldn't be opened."; } private class MyKeyEscrowSink1 : IKeyEscrowSink { public void Store(Guid keyId, XElement element) { throw new NotImplementedException(); } } private class MyKeyEscrowSink2 : IKeyEscrowSink { public void Store(Guid keyId, XElement element) { throw new NotImplementedException(); } } } }