File: AuthenticatedEncryption\ConfigurationModel\ManagedAuthenticatedEncryptorDescriptorDeserializerTests.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.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
 
public class ManagedAuthenticatedEncryptorDescriptorDeserializerTests
{
    [Theory]
    [InlineData(typeof(Aes), typeof(HMACSHA1))]
    [InlineData(typeof(Aes), typeof(HMACSHA256))]
    [InlineData(typeof(Aes), typeof(HMACSHA384))]
    [InlineData(typeof(Aes), typeof(HMACSHA512))]
    public void ImportFromXml_BuiltInTypes_CreatesAppropriateDescriptor(Type encryptionAlgorithmType, Type validationAlgorithmType)
    {
        // Arrange
        var masterKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("[PLACEHOLDER]"));
        var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
            new ManagedAuthenticatedEncryptorConfiguration()
            {
                EncryptionAlgorithmType = encryptionAlgorithmType,
                EncryptionAlgorithmKeySize = 192,
                ValidationAlgorithmType = validationAlgorithmType
            },
            masterKey.ToSecret());
        var control = CreateEncryptorInstanceFromDescriptor(descriptor);
 
        var xml = $@"
                <descriptor>
                  <encryption algorithm='{encryptionAlgorithmType.Name}' keyLength='192' />
                  <validation algorithm='{validationAlgorithmType.Name}' />
                  <masterKey enc:requiresEncryption='true' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
                    <value>{masterKey}</value>
                  </masterKey>
                </descriptor>";
        var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
        var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
 
        // Act & assert
        byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
        byte[] aad = new byte[] { 2, 4, 6, 8, 0 };
        byte[] ciphertext = control.Encrypt(new ArraySegment<byte>(plaintext), new ArraySegment<byte>(aad));
        byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
        Assert.Equal(plaintext, roundTripPlaintext);
    }
 
    [Fact]
    public void ImportFromXml_FullyQualifiedBuiltInTypes_CreatesAppropriateDescriptor()
    {
        // Arrange
        var masterKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("[PLACEHOLDER]"));
        var descriptor = new ManagedAuthenticatedEncryptorDescriptor(
            new ManagedAuthenticatedEncryptorConfiguration()
            {
                EncryptionAlgorithmType = typeof(Aes),
                EncryptionAlgorithmKeySize = 192,
                ValidationAlgorithmType = typeof(HMACSHA384)
            },
            masterKey.ToSecret());
        var control = CreateEncryptorInstanceFromDescriptor(descriptor);
 
        var xml = $@"
                <descriptor>
                  <encryption algorithm='{typeof(Aes).AssemblyQualifiedName}' keyLength='192' />
                  <validation algorithm='{typeof(HMACSHA384).AssemblyQualifiedName}' />
                  <masterKey enc:requiresEncryption='true' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
                    <value>{masterKey}</value>
                  </masterKey>
                </descriptor>";
        var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
        var test = CreateEncryptorInstanceFromDescriptor(deserializedDescriptor as ManagedAuthenticatedEncryptorDescriptor);
 
        // Act & assert
        byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 };
        byte[] aad = new byte[] { 2, 4, 6, 8, 0 };
        byte[] ciphertext = control.Encrypt(new ArraySegment<byte>(plaintext), new ArraySegment<byte>(aad));
        byte[] roundTripPlaintext = test.Decrypt(new ArraySegment<byte>(ciphertext), new ArraySegment<byte>(aad));
        Assert.Equal(plaintext, roundTripPlaintext);
    }
 
    [Fact]
    public void ImportFromXml_CustomType_CreatesAppropriateDescriptor()
    {
        // Arrange
        var masterKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("[PLACEHOLDER]"));
 
        var xml = $@"
                <descriptor>
                  <encryption algorithm='{typeof(CustomAlgorithm).AssemblyQualifiedName}' keyLength='192' />
                  <validation algorithm='{typeof(HMACSHA384).AssemblyQualifiedName}' />
                  <masterKey enc:requiresEncryption='true' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
                    <value>{masterKey}</value>
                  </masterKey>
                </descriptor>";
 
        // Act
        var deserializedDescriptor = new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml));
        var managedDescriptor = (ManagedAuthenticatedEncryptorDescriptor)deserializedDescriptor;
 
        // Assert
        Assert.Equal(typeof(CustomAlgorithm), managedDescriptor.Configuration.EncryptionAlgorithmType);
    }
 
    [Fact]
    public void ImportFromXml_CustomTypeWithoutConstructor_CreatesAppropriateDescriptor()
    {
        // Arrange
        var masterKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("[PLACEHOLDER]"));
 
        var xml = $@"
                <descriptor>
                  <encryption algorithm='{typeof(CustomAlgorithmNoConstructor).AssemblyQualifiedName}' keyLength='192' />
                  <validation algorithm='{typeof(HMACSHA384).AssemblyQualifiedName}' />
                  <masterKey enc:requiresEncryption='true' xmlns:enc='http://schemas.asp.net/2015/03/dataProtection'>
                    <value>{masterKey}</value>
                  </masterKey>
                </descriptor>";
 
        // Act
        var ex = Assert.Throws<InvalidOperationException>(() => new ManagedAuthenticatedEncryptorDescriptorDeserializer().ImportFromXml(XElement.Parse(xml)));
 
        // Assert
        Assert.Equal($"Algorithm type {typeof(CustomAlgorithmNoConstructor).FullName} doesn't have a public parameterless constructor. If the app is published with trimming then the constructor may have been trimmed. Ensure the type's assembly is excluded from trimming.", ex.Message);
    }
 
    public class CustomAlgorithm : SymmetricAlgorithm
    {
        public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => throw new NotImplementedException();
        public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => throw new NotImplementedException();
        public override void GenerateIV() => throw new NotImplementedException();
        public override void GenerateKey() => throw new NotImplementedException();
    }
 
    public class CustomAlgorithmNoConstructor : SymmetricAlgorithm
    {
        private CustomAlgorithmNoConstructor() { }
 
        public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) => throw new NotImplementedException();
        public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) => throw new NotImplementedException();
        public override void GenerateIV() => throw new NotImplementedException();
        public override void GenerateKey() => throw new NotImplementedException();
    }
 
    private static IAuthenticatedEncryptor CreateEncryptorInstanceFromDescriptor(ManagedAuthenticatedEncryptorDescriptor descriptor)
    {
        var encryptorFactory = new ManagedAuthenticatedEncryptorFactory(NullLoggerFactory.Instance);
        var key = new Key(
            Guid.NewGuid(),
            DateTimeOffset.Now,
            DateTimeOffset.Now + TimeSpan.FromHours(1),
            DateTimeOffset.Now + TimeSpan.FromDays(30),
            descriptor,
            new[] { encryptorFactory });
 
        return key.CreateEncryptor();
    }
}