File: Cng\CngAuthenticatedEncryptorBaseTests.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 Moq;
 
namespace Microsoft.AspNetCore.DataProtection.Cng.Internal;
 
public unsafe class CngAuthenticatedEncryptorBaseTests
{
    [Fact]
    public void Decrypt_ForwardsArraySegment()
    {
        // Arrange
        var ciphertext = new ArraySegment<byte>(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, 3, 2);
        var aad = new ArraySegment<byte>(new byte[] { 0x10, 0x11, 0x12, 0x13, 0x14 }, 1, 4);
 
        var encryptorMock = new Mock<MockableEncryptor>();
        encryptorMock
            .Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 2, It.IsAny<IntPtr>(), 4))
            .Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
            {
                // ensure that pointers started at the right place
                Assert.Equal((byte)0x03, *(byte*)pbCiphertext);
                Assert.Equal((byte)0x11, *(byte*)pbAdditionalAuthenticatedData);
                return new byte[] { 0x20, 0x21, 0x22 };
            });
 
        // Act
        var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
 
        // Assert
        Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
    }
 
    [Fact]
    public void Decrypt_HandlesEmptyAADPointerFixup()
    {
        // Arrange
        var ciphertext = new ArraySegment<byte>(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, 3, 2);
        var aad = new ArraySegment<byte>(new byte[0]);
 
        var encryptorMock = new Mock<MockableEncryptor>();
        encryptorMock
            .Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 2, It.IsAny<IntPtr>(), 0))
            .Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
            {
                // ensure that pointers started at the right place
                Assert.Equal((byte)0x03, *(byte*)pbCiphertext);
                Assert.NotEqual(IntPtr.Zero, pbAdditionalAuthenticatedData); // CNG will complain if this pointer is zero
                return new byte[] { 0x20, 0x21, 0x22 };
            });
 
        // Act
        var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
 
        // Assert
        Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
    }
 
    [Fact]
    public void Decrypt_HandlesEmptyCiphertextPointerFixup()
    {
        // Arrange
        var ciphertext = new ArraySegment<byte>(new byte[0]);
        var aad = new ArraySegment<byte>(new byte[] { 0x10, 0x11, 0x12, 0x13, 0x14 }, 1, 4);
 
        var encryptorMock = new Mock<MockableEncryptor>();
        encryptorMock
            .Setup(o => o.DecryptHook(It.IsAny<IntPtr>(), 0, It.IsAny<IntPtr>(), 4))
            .Returns((IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) =>
            {
                // ensure that pointers started at the right place
                Assert.NotEqual(IntPtr.Zero, pbCiphertext); // CNG will complain if this pointer is zero
                Assert.Equal((byte)0x11, *(byte*)pbAdditionalAuthenticatedData);
                return new byte[] { 0x20, 0x21, 0x22 };
            });
 
        // Act
        var retVal = encryptorMock.Object.Decrypt(ciphertext, aad);
 
        // Assert
        Assert.Equal(new byte[] { 0x20, 0x21, 0x22 }, retVal);
    }
 
    internal abstract class MockableEncryptor : CngAuthenticatedEncryptorBase
    {
        public override void Dispose()
        {
        }
 
        public abstract byte[] DecryptHook(IntPtr pbCiphertext, uint cbCiphertext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData);
 
        protected sealed override unsafe byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData)
        {
            return DecryptHook((IntPtr)pbCiphertext, cbCiphertext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData);
        }
 
        public abstract byte[] EncryptHook(IntPtr pbPlaintext, uint cbPlaintext, IntPtr pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer);
 
        protected sealed override unsafe byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer)
        {
            return EncryptHook((IntPtr)pbPlaintext, cbPlaintext, (IntPtr)pbAdditionalAuthenticatedData, cbAdditionalAuthenticatedData, cbPreBuffer, cbPostBuffer);
        }
    }
}