File: System\IO\Packaging\PackageDigitalSignatureManagerTests.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\tests\UnitTests\WindowsBase.Tests\WindowsBase.Tests.csproj (WindowsBase.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.X509Certificates;
 
namespace System.IO.Packaging.Tests;
 
public class PackageDigitalSignatureManagerTests
{
    // TODO:
    // Signatures - signed.
    // SignatureOrigin - signed.
    // Countersign - signed.
    // GetSignature - signed.
    // RemoveAllSignatures - signed.
    // RemoveSignature - signed.
    // Sign
    // VerifySignatures - signed.
 
    [Fact]
    public void Ctor_Package()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Equal(CertificateEmbeddingOption.InCertificatePart, manager.CertificateOption);
        Assert.Equal("http://www.w3.org/2001/04/xmlenc#sha256", manager.HashAlgorithm);
        Assert.False(manager.IsSigned);
        Assert.Equal(IntPtr.Zero, manager.ParentWindow);
        Assert.Equal(new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative), manager.SignatureOrigin);
        Assert.Empty(manager.Signatures);
        Assert.Same(manager.Signatures, manager.Signatures);
        Assert.Equal("YYYY-MM-DDThh:mm:ss.sTZD", manager.TimeFormat);
        Assert.NotEmpty(manager.TransformMapping);
        Assert.Same(manager.TransformMapping, manager.TransformMapping);
        Assert.Equal(2, manager.TransformMapping.Count);
        Assert.Equal("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", manager.TransformMapping["application/vnd.openxmlformats-package.relationships+xml"]);
        Assert.Equal("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", manager.TransformMapping["application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"]);
    }
 
    [Fact]
    public void Ctor_NullPackage_ThrowsArgumentNullException()
    {
        Assert.Throws<ArgumentNullException>("package", () => new PackageDigitalSignatureManager(null));
    }
 
    [Fact]
    public void DefaultHashAlgorithm_Get_ReturnsExpected()
    {
        Assert.Equal("http://www.w3.org/2001/04/xmlenc#sha256", PackageDigitalSignatureManager.DefaultHashAlgorithm);
    }
 
    [Fact]
    public void SignatureOriginRelationshipType_Get_ReturnsExpected()
    {
        Assert.Equal("http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin", PackageDigitalSignatureManager.SignatureOriginRelationshipType);
    }
 
    [Fact]
    public void VerifyCertificate_NullCertificate_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("certificate", () => PackageDigitalSignatureManager.VerifyCertificate(null));
    }
 
    [Fact]
    public void VerifyCertificate_InvokeDefaultCertificate_ThrowsArgumentException()
    {
#pragma warning disable SYSLIB0026
        var c = new X509Certificate();
#pragma warning restore 0618
        Assert.Throws<ArgumentException>("handle", () => PackageDigitalSignatureManager.VerifyCertificate(c));
    }
 
    [Theory]
    [InlineData(CertificateEmbeddingOption.InCertificatePart)]
    [InlineData(CertificateEmbeddingOption.InSignaturePart)]
    [InlineData(CertificateEmbeddingOption.NotEmbedded)]
    public void CertificateOption_Set_GetReturnsExpected(CertificateEmbeddingOption value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package)
        {
            // Set.
            CertificateOption = value
        };
        Assert.Equal(value, manager.CertificateOption);
 
        // Set same.
        manager.CertificateOption = value;
        Assert.Equal(value, manager.CertificateOption);
    }
 
    [Theory]
    [InlineData(CertificateEmbeddingOption.InCertificatePart - 1)]
    [InlineData(CertificateEmbeddingOption.NotEmbedded + 1)]
    public void CertificateOption_SetInvalid_ThrowsArgumentOutOfRangeException(CertificateEmbeddingOption value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentOutOfRangeException>("value", () => manager.CertificateOption = value);
    }
 
    [Theory]
    [InlineData("http://www.w3.org/2001/04/xmlenc#sha256")]
    [InlineData("value")]
    [InlineData("  ")]
    public void HashAlgorithm_Set_GetReturnsExpected(string value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package)
        {
            // Set.
            HashAlgorithm = value
        };
        Assert.Equal(value, manager.HashAlgorithm);
 
        // Set same.
        manager.HashAlgorithm = value;
        Assert.Equal(value, manager.HashAlgorithm);
    }
 
    [Fact]
    public void HashAlgortithm_SetNull_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("value", () => manager.HashAlgorithm = null);
    }
 
    [Fact]
    public void HashAlgorithm_SetEmpty_ThrowsArgumentException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentException>("value", () => manager.HashAlgorithm = string.Empty);
    }
    
    public static IEnumerable<object[]> ParentWindow_Set_TestData()
    {
        yield return new object[] { (IntPtr)(-1) };
        yield return new object[] { IntPtr.Zero };
        yield return new object[] { (IntPtr)1 };
    }
 
    [Theory]
    [MemberData(nameof(ParentWindow_Set_TestData))]
    public void ParentWindow_Set_GetReturnsExpected(IntPtr value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package)
        {
            // Set.
            ParentWindow = value
        };
        Assert.Equal(value, manager.ParentWindow);
 
        // Set same.
        manager.ParentWindow = value;
        Assert.Equal(value, manager.ParentWindow);
    }
 
    [Theory]
    [InlineData("YYYY-MM-DDThh:mm:ss.sTZD")]
    [InlineData("YYYY-MM-DDThh:mm:ssTZD")]
    [InlineData("YYYY-MM-DDThh:mmTZD")]
    [InlineData("YYYY-MM-DD")]
    [InlineData("YYYY-MM")]                 
    [InlineData("YYYY")]
    public void TimeFormat_Set_GetReturnsExpected(string value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package)
        {
            // Set.
            TimeFormat = value
        };
        Assert.Equal(value, manager.TimeFormat);
 
        // Set same.
        manager.TimeFormat = value;
        Assert.Equal(value, manager.TimeFormat);
    }
 
    [Fact]
    public void TimeFormat_SetNull_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("value", () => manager.TimeFormat = null);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("  ")]
    [InlineData("value")]
    [InlineData("yyyy-MM-ddTHH:mm:ss.fzzz")]
    [InlineData("yyyy-MM-ddTHH:mm:sszzz")] 
    [InlineData("yyyy-MM-ddTHH:mmzzz")]   
    [InlineData("yyyy-MM-dd")]
    [InlineData("yyyy-MM")]
    [InlineData("yyyy")] 
    [InlineData("yyyy-MM-ddTHH:mm:ss.fZ")]
    [InlineData("yyyy-MM-ddTHH:mm:ssZ")]
    [InlineData("yyyy-MM-ddTHH:mmZ")]
    [InlineData(" YYYY ")]
    public void TimeFormat_SetInvalid_ThrowsFormatException(string value)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<FormatException>(() => manager.TimeFormat = value);
    }
 
    [Fact]
    public void Countersign_NotSigned_ThrowsInvalidOperationException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<InvalidOperationException>(() => manager.Countersign());
 
#pragma warning disable SYSLIB0026
        var c = new X509Certificate();
#pragma warning restore 0618
        Assert.Throws<InvalidOperationException>(() => manager.Countersign(c));
        Assert.Throws<InvalidOperationException>(() => manager.Countersign(c, Array.Empty<Uri>()));
    }
 
    [Fact]
    public void Countersign_NullCertificate_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("certificate", () => manager.Countersign(null));
        Assert.Throws<ArgumentNullException>("certificate", () => manager.Countersign(null, Array.Empty<Uri>()));
    }
 
    [Fact]
    public void Countersign_NullSignatures_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        
#pragma warning disable SYSLIB0026
        var c = new X509Certificate();
#pragma warning restore 0618
        Assert.Throws<ArgumentNullException>("signatures", () => manager.Countersign(c, null));
    }
 
    [Fact]
    public void GetSignature_InvokeNotSigned_ReturnsNull()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Null(manager.GetSignature(new Uri("http://microsoft.com")));
    }
 
    [Fact]
    public void GetSignature_NullSignatureUri_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("signatureUri", () => manager.GetSignature(null));
    }
 
    [Fact]
    public void RemoveAllSignatures_InvokeNotSigned_Nop()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        int callCount = 0;
        var parts = new List<Uri>();
        package.DeletePartCoreAction = (uri) =>
        {
            parts.Add(uri);
            callCount++;
        };
 
        // Remove.
        manager.RemoveAllSignatures();
        Assert.Equal(2, callCount);
        Assert.Equal(new Uri[] { new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative), new Uri("/package/services/digital-signature/_rels/origin.psdsor.rels", UriKind.Relative) }, parts);
        Assert.Empty(manager.Signatures);
        Assert.Same(manager.Signatures, manager.Signatures);
 
        // Remove again.
        manager.RemoveAllSignatures();
        Assert.Equal(4, callCount);
        Assert.Equal(new Uri[] { new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative), new Uri("/package/services/digital-signature/_rels/origin.psdsor.rels", UriKind.Relative), new Uri("/package/services/digital-signature/origin.psdsor", UriKind.Relative), new Uri("/package/services/digital-signature/_rels/origin.psdsor.rels", UriKind.Relative) }, parts);
        Assert.Empty(manager.Signatures);
        Assert.Same(manager.Signatures, manager.Signatures);
    }
 
    [Fact]
    public void RemoveSignature_InvokeNotSigned_Nop()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        int callCount = 0;
        package.DeletePartCoreAction = (uri) => callCount++;
 
        // Remove.
        manager.RemoveSignature(new Uri("https://microsoft.com"));
        Assert.Equal(0, callCount);
        Assert.Empty(manager.Signatures);
        Assert.Same(manager.Signatures, manager.Signatures);
 
        // Remove again.
        manager.RemoveSignature(new Uri("https://microsoft.com"));
        Assert.Equal(0, callCount);
        Assert.Empty(manager.Signatures);
        Assert.Same(manager.Signatures, manager.Signatures);
    }
 
    [Fact]
    public void RemoveSignature_NullSignatureUri_ThrowsArgumentNullException()
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Throws<ArgumentNullException>("signatureUri", () => manager.RemoveSignature(null));
    }
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void VerifySignatures_InvokeNotSigned_ReturnsNotSigned(bool exitOnFailure)
    {
        var package = new CustomPackage(FileAccess.ReadWrite);
        var manager = new PackageDigitalSignatureManager(package);
        Assert.Equal(VerifyResult.NotSigned, manager.VerifySignatures(exitOnFailure));
    }
 
    private class CustomPackage : Package
    {
        public CustomPackage(FileAccess openFileAccess) : base(openFileAccess)
        {
        }
 
        protected override PackagePart CreatePartCore(Uri partUri, string contentType, CompressionOption compressionOption)
        {
            throw new NotImplementedException();
        }
 
        public Action<Uri>? DeletePartCoreAction { get; set; }
 
        protected override void DeletePartCore(Uri partUri)
        {
            if (DeletePartCoreAction is null)
            {
                throw new NotImplementedException();
            }
 
            DeletePartCoreAction(partUri);
        }
 
        protected override void FlushCore()
        {
            throw new NotImplementedException();
        }
 
        protected override PackagePart? GetPartCore(Uri partUri)
        {
            throw new NotImplementedException();
        }
 
        protected override PackagePart[] GetPartsCore()
        {
            throw new NotImplementedException();
        }
    }
}