File: Internal\KeyManagementOptionsSetupTest.cs
Web Access
Project: src\src\DataProtection\DataProtection\test\Microsoft.AspNetCore.DataProtection.Tests\Microsoft.AspNetCore.DataProtection.Tests.csproj (Microsoft.AspNetCore.DataProtection.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Win32;
 
namespace Microsoft.AspNetCore.DataProtection.Internal;
 
public class KeyManagementOptionsSetupTest
{
    [Fact]
    public void Configure_SetsExpectedValues()
    {
        // Arrange
        var setup = new KeyManagementOptionsSetup(NullLoggerFactory.Instance);
        var options = new KeyManagementOptions()
        {
            AuthenticatedEncryptorConfiguration = null
        };
 
        // Act
        setup.Configure(options);
 
        // Assert
        Assert.Empty(options.KeyEscrowSinks);
        Assert.NotNull(options.AuthenticatedEncryptorConfiguration);
        Assert.IsType<AuthenticatedEncryptorConfiguration>(options.AuthenticatedEncryptorConfiguration);
        Assert.Collection(
            options.AuthenticatedEncryptorFactories,
            f => Assert.IsType<CngGcmAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<CngCbcAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<ManagedAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<AuthenticatedEncryptorFactory>(f));
    }
 
    [ConditionalFact]
    [ConditionalRunTestOnlyIfHkcuRegistryAvailable]
    public void Configure_WithRegistryPolicyResolver_SetsValuesFromResolver()
    {
        // Arrange
        var registryEntries = new Dictionary<string, object>()
        {
            ["KeyEscrowSinks"] = String.Join(" ;; ; ", new Type[] { typeof(MyKeyEscrowSink1), typeof(MyKeyEscrowSink2) }.Select(t => t.AssemblyQualifiedName)),
            ["EncryptionType"] = "managed",
            ["DefaultKeyLifetime"] = 1024 // days
        };
        var options = new KeyManagementOptions()
        {
            AuthenticatedEncryptorConfiguration = null
        };
 
        // Act
        RunTest(registryEntries, options);
 
        // Assert
        Assert.Collection(
            options.KeyEscrowSinks,
            k => Assert.IsType<MyKeyEscrowSink1>(k),
            k => Assert.IsType<MyKeyEscrowSink2>(k));
        Assert.Equal(TimeSpan.FromDays(1024), options.NewKeyLifetime);
        Assert.NotNull(options.AuthenticatedEncryptorConfiguration);
        Assert.IsType<ManagedAuthenticatedEncryptorConfiguration>(options.AuthenticatedEncryptorConfiguration);
        Assert.Collection(
            options.AuthenticatedEncryptorFactories,
            f => Assert.IsType<CngGcmAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<CngCbcAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<ManagedAuthenticatedEncryptorFactory>(f),
            f => Assert.IsType<AuthenticatedEncryptorFactory>(f));
    }
 
    private static void RunTest(Dictionary<string, object> regValues, KeyManagementOptions options)
    {
        WithUniqueTempRegKey(registryKey =>
        {
            foreach (var entry in regValues)
            {
                registryKey.SetValue(entry.Key, entry.Value);
            }
 
            var policyResolver = new RegistryPolicyResolver(
                registryKey,
                activator: SimpleActivator.DefaultWithoutServices);
 
            var setup = new KeyManagementOptionsSetup(NullLoggerFactory.Instance, policyResolver);
 
            setup.Configure(options);
        });
    }
 
    /// <summary>
    /// Runs a test and cleans up the registry key afterward.
    /// </summary>
    private static void WithUniqueTempRegKey(Action<RegistryKey> testCode)
    {
        string uniqueName = Guid.NewGuid().ToString();
        var uniqueSubkey = LazyHkcuTempKey.Value.CreateSubKey(uniqueName);
        try
        {
            testCode(uniqueSubkey);
        }
        finally
        {
            // clean up when test is done
            LazyHkcuTempKey.Value.DeleteSubKeyTree(uniqueName, throwOnMissingSubKey: false);
        }
    }
 
    private static readonly Lazy<RegistryKey> LazyHkcuTempKey = new Lazy<RegistryKey>(() =>
    {
        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();
        }
    }
}