From 894388a598834a1fe8a0e483a2bc050c05693313 Mon Sep 17 00:00:00 2001 From: Patricio Beltran Date: Thu, 8 Feb 2018 22:02:18 -0800 Subject: [PATCH] Refactor signature object to add support for repository countersignatures (#2006) * Refactor signature object to add support for repository countersignatures * move verification logic to signature classes --- .../NuGet.Common/Errors/NuGetLogCode.cs | 15 + .../NuGet.Packaging/PackageArchiveReader.cs | 6 +- .../NuGet.Packaging/PackageFolderReader.cs | 4 +- .../NuGet.Packaging/PackageReaderBase.cs | 2 +- .../Signing/Authoring/ISignatureProvider.cs | 2 +- .../Signing/Authoring/ITimestampProvider.cs | 2 +- .../Signing/Authoring/Signer.cs | 2 +- .../Authoring/TestSignatureProvider.cs | 6 +- .../Authoring/X509SignatureProvider.cs | 24 +- .../Signing/Package/ISignedPackageReader.cs | 2 +- .../Signatures/AuthorPrimarySignature.cs | 42 +++ .../Signatures/IRepositorySignature.cs | 17 + .../Signing/Signatures/PrimarySignature.cs | 164 +++++++++ .../Signatures/PrimarySignatureFactory.cs | 29 ++ .../Signatures/RepositoryCounterSignature.cs | 102 ++++++ .../Signatures/RepositoryPrimarySignature.cs | 51 +++ .../Signing/Signatures/Signature.cs | 287 +++++++++------ .../Signatures/UnknownPrimarySignature.cs | 19 + .../Timestamp/Rfc3161TimestampProvider.cs | 6 +- .../Signing/Timestamp/Timestamp.cs | 119 ++++++ .../Signing/Utility/CertificateUtility.cs | 6 +- .../Signing/Utility/SignatureUtility.cs | 16 +- .../Signing/Utility/VerificationUtility.cs | 48 ++- .../AllowListVerificationProvider.cs | 8 +- .../ISignatureVerificationProvider.cs | 2 +- .../IntegrityVerificationProvider.cs | 6 +- .../Verification/PackageSignatureVerifier.cs | 2 +- ...ureTrustAndValidityVerificationProvider.cs | 341 +----------------- .../SignedPackageVerificationResult.cs | 4 +- .../SignedPackageVerifierSettings.cs | 4 +- .../NuGet.Packaging/Strings.Designer.cs | 54 +++ src/NuGet.Core/NuGet.Packaging/Strings.resx | 17 + .../Plugins/PluginPackageReader.cs | 4 +- ...atureTests.cs => PrimarySignatureTests.cs} | 14 +- ...ustAndValidityVerificationProviderTests.cs | 28 +- ...SignedPackageIntegrityVerificationTests.cs | 8 +- .../SigningTests/TimestampProviderTests.cs | 2 +- ...atureTests.cs => PrimarySignatureTests.cs} | 18 +- .../SigningTests/SignatureUtilityTests.cs | 18 +- .../DownloadResourceResultTests.cs | 4 +- .../Signing/SignedArchiveTestUtility.cs | 20 +- 41 files changed, 983 insertions(+), 542 deletions(-) create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/AuthorPrimarySignature.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/IRepositorySignature.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignature.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignatureFactory.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryCounterSignature.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryPrimarySignature.cs create mode 100644 src/NuGet.Core/NuGet.Packaging/Signing/Signatures/UnknownPrimarySignature.cs rename test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/{SignatureTests.cs => PrimarySignatureTests.cs} (93%) rename test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/{SignatureTests.cs => PrimarySignatureTests.cs} (90%) diff --git a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs index 35f29607f6a..2fa00bd557f 100644 --- a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs +++ b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs @@ -369,6 +369,21 @@ public enum NuGetLogCode /// NU3030 = 3030, + /// + /// The repository countersignature is invalid. + /// + NU3031 = 3031, + + /// + /// The package signature contains multiple repository countersignatures. + /// + NU3032 = 3032, + + /// + /// A repository primary signature must not have a repository countersignature. + /// + NU3033 = 3033, + /// /// Undefined Package Error. /// diff --git a/src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs b/src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs index 5ee4e5c2411..95cc5532694 100644 --- a/src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs +++ b/src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs @@ -227,7 +227,7 @@ public IEnumerable EnumeratePackageEntries(IEnumerable pack } } - public override async Task GetSignatureAsync(CancellationToken token) + public override async Task GetPrimarySignatureAsync(CancellationToken token) { token.ThrowIfCancellationRequested(); @@ -236,7 +236,7 @@ public override async Task GetSignatureAsync(CancellationToken token) throw new SignatureException(Strings.SignedPackageUnableToAccessSignature); } - Signature signature = null; + PrimarySignature signature = null; if (await IsSignedAsync(token)) { @@ -246,7 +246,7 @@ public override async Task GetSignatureAsync(CancellationToken token) using (var signatureEntryStream = signatureEntry.Open()) { #if IS_DESKTOP - signature = Signature.Load(signatureEntryStream); + signature = PrimarySignature.Load(signatureEntryStream); #endif } } diff --git a/src/NuGet.Core/NuGet.Packaging/PackageFolderReader.cs b/src/NuGet.Core/NuGet.Packaging/PackageFolderReader.cs index a9c8dcd128a..b66f2e3c67e 100644 --- a/src/NuGet.Core/NuGet.Packaging/PackageFolderReader.cs +++ b/src/NuGet.Core/NuGet.Packaging/PackageFolderReader.cs @@ -220,9 +220,9 @@ protected override void Dispose(bool disposing) // do nothing here } - public override Task GetSignatureAsync(CancellationToken token) + public override Task GetPrimarySignatureAsync(CancellationToken token) { - return Task.FromResult(null); + return Task.FromResult(null); } public override Task IsSignedAsync(CancellationToken token) diff --git a/src/NuGet.Core/NuGet.Packaging/PackageReaderBase.cs b/src/NuGet.Core/NuGet.Packaging/PackageReaderBase.cs index cd1022eaa24..ec89572fe71 100644 --- a/src/NuGet.Core/NuGet.Packaging/PackageReaderBase.cs +++ b/src/NuGet.Core/NuGet.Packaging/PackageReaderBase.cs @@ -546,7 +546,7 @@ public virtual Task CopyNupkgAsync(string nupkgFilePath, CancellationTok throw new NotImplementedException(); } - public abstract Task GetSignatureAsync(CancellationToken token); + public abstract Task GetPrimarySignatureAsync(CancellationToken token); public abstract Task IsSignedAsync(CancellationToken token); diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ISignatureProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ISignatureProvider.cs index 56676c495a1..40e0d207f47 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ISignatureProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ISignatureProvider.cs @@ -21,6 +21,6 @@ public interface ISignatureProvider /// Logger /// Cancellation token. /// A signature for the package. - Task CreateSignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token); + Task CreatePrimarySignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token); } } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ITimestampProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ITimestampProvider.cs index d140447e1e5..a0fb152dd04 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ITimestampProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/ITimestampProvider.cs @@ -10,6 +10,6 @@ namespace NuGet.Packaging.Signing public interface ITimestampProvider { // Add timestamp to signature - Task TimestampSignatureAsync(TimestampRequest request, ILogger logger, CancellationToken token); + Task TimestampPrimarySignatureAsync(TimestampRequest request, ILogger logger, CancellationToken token); } } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/Signer.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/Signer.cs index 0953f59cd3a..00544638dbe 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/Signer.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/Signer.cs @@ -55,7 +55,7 @@ public async Task SignAsync(SignPackageRequest request, ILogger logger, Cancella var zipArchiveHash = await _package.GetArchiveHashAsync(request.SignatureHashAlgorithm, token); var signatureContent = GenerateSignatureContent(request.SignatureHashAlgorithm, zipArchiveHash); - var signature = await _signatureProvider.CreateSignatureAsync(request, signatureContent, logger, token); + var signature = await _signatureProvider.CreatePrimarySignatureAsync(request, signatureContent, logger, token); using (var stream = new MemoryStream(signature.GetBytes())) { diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/TestSignatureProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/TestSignatureProvider.cs index 823c272aa84..ef8324b7224 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/TestSignatureProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/TestSignatureProvider.cs @@ -10,14 +10,14 @@ namespace NuGet.Packaging.Signing { public class TestSignatureProvider : ISignatureProvider { - private readonly Signature _signature; + private readonly PrimarySignature _signature; - public TestSignatureProvider(Signature signature) + public TestSignatureProvider(PrimarySignature signature) { _signature = signature ?? throw new ArgumentNullException(nameof(signature)); } - public Task CreateSignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token) + public Task CreatePrimarySignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token) { return Task.FromResult(_signature); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/X509SignatureProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/X509SignatureProvider.cs index e19f3875a24..2608b356658 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/X509SignatureProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Authoring/X509SignatureProvider.cs @@ -34,7 +34,7 @@ public X509SignatureProvider(ITimestampProvider timestampProvider) /// /// Sign the package stream hash with an X509Certificate2. /// - public Task CreateSignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token) + public Task CreatePrimarySignatureAsync(SignPackageRequest request, SignatureContent signatureContent, ILogger logger, CancellationToken token) { if (request == null) { @@ -51,7 +51,7 @@ public Task CreateSignatureAsync(SignPackageRequest request, Signatur throw new ArgumentNullException(nameof(logger)); } - var signature = CreateSignature(request, signatureContent, logger); + var signature = CreatePrimarySignature(request, signatureContent, logger); if (_timestampProvider == null) { @@ -59,18 +59,18 @@ public Task CreateSignatureAsync(SignPackageRequest request, Signatur } else { - return TimestampSignature(request, logger, signature, token); + return TimestampPrimarySignatureAsync(request, logger, signature, token); } } #if IS_DESKTOP - private Signature CreateSignature(SignPackageRequest request, SignatureContent signatureContent, ILogger logger) + private PrimarySignature CreatePrimarySignature(SignPackageRequest request, SignatureContent signatureContent, ILogger logger) { var cmsSigner = CreateCmsSigner(request, logger); if (request.PrivateKey != null) { - return CreateSignature(cmsSigner, signatureContent, request.PrivateKey); + return CreatePrimarySignature(cmsSigner, signatureContent, request.PrivateKey); } var contentInfo = new ContentInfo(signatureContent.GetBytes()); @@ -89,7 +89,7 @@ private Signature CreateSignature(SignPackageRequest request, SignatureContent s throw new SignatureException(NuGetLogCode.NU3001, exceptionBuilder.ToString()); } - return Signature.Load(cms); + return PrimarySignature.Load(cms); } private static CmsSigner CreateCmsSigner(SignPackageRequest request, ILogger logger) @@ -133,14 +133,14 @@ private static CmsSigner CreateCmsSigner(SignPackageRequest request, ILogger log return signer; } - private Signature CreateSignature(CmsSigner cmsSigner, SignatureContent signatureContent, CngKey privateKey) + private PrimarySignature CreatePrimarySignature(CmsSigner cmsSigner, SignatureContent signatureContent, CngKey privateKey) { var cms = NativeUtilities.NativeSign(cmsSigner, signatureContent.GetBytes(), privateKey); - return Signature.Load(cms); + return PrimarySignature.Load(cms); } - private Task TimestampSignature(SignPackageRequest request, ILogger logger, Signature signature, CancellationToken token) + private Task TimestampPrimarySignatureAsync(SignPackageRequest request, ILogger logger, PrimarySignature signature, CancellationToken token) { var timestampRequest = new TimestampRequest { @@ -149,16 +149,16 @@ private Task TimestampSignature(SignPackageRequest request, ILogger l TimestampHashAlgorithm = request.TimestampHashAlgorithm }; - return _timestampProvider.TimestampSignatureAsync(timestampRequest, logger, token); + return _timestampProvider.TimestampPrimarySignatureAsync(timestampRequest, logger, token); } #else - private Signature CreateSignature(SignPackageRequest request, SignatureContent signatureContent, ILogger logger) + private PrimarySignature CreatePrimarySignature(SignPackageRequest request, SignatureContent signatureContent, ILogger logger) { throw new NotSupportedException(); } - private Task TimestampSignature(SignPackageRequest request, ILogger logger, Signature signature, CancellationToken token) + private Task TimestampPrimarySignatureAsync(SignPackageRequest request, ILogger logger, PrimarySignature signature, CancellationToken token) { throw new NotSupportedException(); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Package/ISignedPackageReader.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Package/ISignedPackageReader.cs index 9157782946b..f72b0f7f309 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Package/ISignedPackageReader.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Package/ISignedPackageReader.cs @@ -17,7 +17,7 @@ public interface ISignedPackageReader : IDisposable /// Get package signature. /// /// Returns a null if the package is unsigned. - Task GetSignatureAsync(CancellationToken token); + Task GetPrimarySignatureAsync(CancellationToken token); /// /// Check if a package contains signing information. diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/AuthorPrimarySignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/AuthorPrimarySignature.cs new file mode 100644 index 00000000000..00e6e8fd43e --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/AuthorPrimarySignature.cs @@ -0,0 +1,42 @@ +// 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.Globalization; +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; +#endif +using NuGet.Common; + +namespace NuGet.Packaging.Signing +{ + public sealed class AuthorPrimarySignature : PrimarySignature + { +#if IS_DESKTOP + + public AuthorPrimarySignature(SignedCms signedCms) + : base(signedCms, SignatureType.Author) + { + } + + internal override SignatureVerificationStatus Verify( + Timestamp timestamp, + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + X509Certificate2Collection certificateExtraStore, + List issues) + { + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); + } + settings = settings ?? SignedPackageVerifierSettings.Default; + + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, Type.ToString()))); + return base.Verify(timestamp, settings, fingerprintAlgorithm, certificateExtraStore, issues); + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/IRepositorySignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/IRepositorySignature.cs new file mode 100644 index 00000000000..ba159e299f7 --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/IRepositorySignature.cs @@ -0,0 +1,17 @@ +// 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; + +namespace NuGet.Packaging.Signing +{ + public interface IRepositorySignature + { +#if IS_DESKTOP + Uri V3ServiceIndexUrl { get; } + + IReadOnlyList PackageOwners { get; } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignature.cs new file mode 100644 index 00000000000..31e98c19599 --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignature.cs @@ -0,0 +1,164 @@ +// 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.IO; +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +#endif +using NuGet.Common; + +namespace NuGet.Packaging.Signing +{ + public abstract class PrimarySignature : Signature + { +#if IS_DESKTOP + /// + /// A SignedCms object holding the signature and SignerInfo. + /// + public SignedCms SignedCms { get; } + + /// + /// Signature content. + /// + public SignatureContent SignatureContent { get; } + + /// + /// Save the signed cms signature to a stream. + /// + /// + public void Save(Stream stream) + { + using (var ms = new MemoryStream(SignedCms.Encode())) + { + ms.CopyTo(stream); + } + } + + /// + /// Retrieve the bytes of the signed cms signature. + /// + public byte[] GetBytes() + { + return SignedCms.Encode(); + } + + /// + /// Create a signature based on a valid signed cms + /// + /// signature data + public static PrimarySignature Load(SignedCms cms) + { + if (cms == null) + { + throw new ArgumentNullException(nameof(cms)); + } + + if (cms.SignerInfos.Count != 1) + { + throw new SignatureException(NuGetLogCode.NU3009, Strings.Error_NotOnePrimarySignature); + } + + var signerInfo = cms.SignerInfos[0]; + VerifySigningCertificate(cms, signerInfo, SigningSpecifications.V1); + + return PrimarySignatureFactory.CreateSignature(cms); + } + + /// + /// Create a signature based on a valid byte array to be decoded as a signed cms + /// + /// signature data + public static PrimarySignature Load(byte[] data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + var cms = new SignedCms(); + cms.Decode(data); + + return Load(cms); + } + + /// + /// Create a signature based on a valid byte stream to be decoded as a signed cms + /// + /// signature data + public static PrimarySignature Load(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + using (stream) + using (var ms = new MemoryStream()) + { + stream.CopyTo(ms); + return Load(ms.ToArray()); + } + } + + public override byte[] GetSignatureHashValue(HashAlgorithmName hashAlgorithm) + { + using (var authorSignatureNativeCms = NativeCms.Decode(SignedCms.Encode(), detached: false)) + { + return NativeCms.GetSignatureValueHash(hashAlgorithm, authorSignatureNativeCms); + } + } + + protected PrimarySignature(SignedCms signedCms, SignatureType signatureType) + : base(GetSignerInfo(signedCms), signatureType) + { + SignedCms = signedCms; + SignatureContent = SignatureContent.Load(SignedCms.ContentInfo.Content, SigningSpecifications.V1); + } + + protected override void ThrowForInvalidSignature() + { + ThrowForInvalidPrimarySignature(); + } + + protected static void ThrowForInvalidPrimarySignature() + { + throw new SignatureException(NuGetLogCode.NU3011, Strings.InvalidPrimarySignature); + } + + private static void VerifySigningCertificate( + SignedCms signedCms, + SignerInfo signerInfo, + SigningSpecifications signingSpecifications) + { + var certificates = SignatureUtility.GetPrimarySignatureCertificates( + signedCms, + signerInfo, + signingSpecifications); + + if (certificates == null || certificates.Count == 0) + { + ThrowForInvalidPrimarySignature(); + } + } + + private static SignerInfo GetSignerInfo(SignedCms signedCms) + { + if (signedCms == null) + { + throw new ArgumentNullException(nameof(signedCms)); + } + return signedCms.SignerInfos[0]; + } + +#else + /// + /// Retrieve the bytes of the signed cms signature. + /// + public byte[] GetBytes() + { + throw new NotSupportedException(); + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignatureFactory.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignatureFactory.cs new file mode 100644 index 00000000000..d5046bdcf15 --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/PrimarySignatureFactory.cs @@ -0,0 +1,29 @@ +// 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. + +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +#endif + +namespace NuGet.Packaging.Signing +{ + public static class PrimarySignatureFactory + { +#if IS_DESKTOP + public static PrimarySignature CreateSignature(SignedCms signedCms) + { + var signatureType = AttributeUtility.GetSignatureType(signedCms.SignerInfos[0].SignedAttributes); + + switch (signatureType) + { + case SignatureType.Author: + return new AuthorPrimarySignature(signedCms); + case SignatureType.Repository: + return new RepositoryPrimarySignature(signedCms); + default: + return new UnknownPrimarySignature(signedCms); + } + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryCounterSignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryCounterSignature.cs new file mode 100644 index 00000000000..0787cfe9a8b --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryCounterSignature.cs @@ -0,0 +1,102 @@ +// 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.Globalization; +using System.Linq; +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; +#endif +using NuGet.Common; + +namespace NuGet.Packaging.Signing +{ + public sealed class RepositoryCountersignature : Signature, IRepositorySignature + { +#if IS_DESKTOP + public Uri V3ServiceIndexUrl { get; } + public IReadOnlyList PackageOwners { get; } + + private RepositoryCountersignature(SignerInfo counterSignerInfo, Uri v3ServiceIndexUrl, IReadOnlyList packageOwners) + : base(counterSignerInfo, SignatureType.Repository) + { + V3ServiceIndexUrl = v3ServiceIndexUrl; + PackageOwners = packageOwners; + } + + public static RepositoryCountersignature GetRepositoryCounterSignature(PrimarySignature primarySignature) + { + if (primarySignature.Type == SignatureType.Repository) + { + throw new SignatureException(NuGetLogCode.NU3033, Strings.Error_RepositorySignatureShouldNotHaveARepositoryCountersignature); + } + + var counterSignatures = primarySignature.SignerInfo.CounterSignerInfos; + RepositoryCountersignature repositoryCountersignature = null; + + // We only care about the repository countersignatures, not any kind of counter signature + foreach (var counterSignature in counterSignatures) + { + var countersignatureType = AttributeUtility.GetSignatureType(counterSignature.SignedAttributes); + if (countersignatureType == SignatureType.Repository) + { + if (repositoryCountersignature != null) + { + throw new SignatureException(NuGetLogCode.NU3032, Strings.Error_NotOneRepositoryCounterSignature); + } + var v3ServiceIndexUrl = AttributeUtility.GetNuGetV3ServiceIndexUrl(counterSignature.SignedAttributes); + var packageOwners = AttributeUtility.GetNuGetPackageOwners(counterSignature.SignedAttributes); + repositoryCountersignature = new RepositoryCountersignature(counterSignature, v3ServiceIndexUrl, packageOwners); + } + } + + return repositoryCountersignature; + } + + public override byte[] GetSignatureHashValue(HashAlgorithmName hashAlgorithm) + { + // TODO: figure out how to get the signature hash value for a countersignature + return new byte[] { }; + } + + protected override void ThrowForInvalidSignature() + { + ThrowForInvalidRepositoryCounterSignature(); + } + + private static void ThrowForInvalidRepositoryCounterSignature() + { + throw new SignatureException(NuGetLogCode.NU3031, Strings.InvalidRepositoryCounterSignature); + } + + internal override SignatureVerificationStatus Verify( + Timestamp timestamp, + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + X509Certificate2Collection certificateExtraStore, + List issues) + { + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); + } + settings = settings ?? SignedPackageVerifierSettings.Default; + + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, Type.ToString()))); + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.NuGetV3ServiceIndexUrl, V3ServiceIndexUrl.ToString()))); + if (PackageOwners != null) + { + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.NuGetPackageOwners, string.Join(", ", PackageOwners)))); + } + return base.Verify(timestamp, settings, fingerprintAlgorithm, certificateExtraStore, issues); + } +#else + public static RepositoryCountersignature GetRepositoryCounterSignature(PrimarySignature primarySignature) + { + return null; + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryPrimarySignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryPrimarySignature.cs new file mode 100644 index 00000000000..3eef1a0fd5b --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/RepositoryPrimarySignature.cs @@ -0,0 +1,51 @@ +// 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.Globalization; +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; +#endif +using NuGet.Common; + +namespace NuGet.Packaging.Signing +{ + public sealed class RepositoryPrimarySignature : PrimarySignature, IRepositorySignature + { +#if IS_DESKTOP + public Uri V3ServiceIndexUrl { get; } + public IReadOnlyList PackageOwners { get; } + + public RepositoryPrimarySignature(SignedCms signedCms) + : base(signedCms, SignatureType.Repository) + { + V3ServiceIndexUrl = AttributeUtility.GetNuGetV3ServiceIndexUrl(SignerInfo.SignedAttributes); + PackageOwners = AttributeUtility.GetNuGetPackageOwners(SignerInfo.SignedAttributes); + } + + internal override SignatureVerificationStatus Verify( + Timestamp timestamp, + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + X509Certificate2Collection certificateExtraStore, + List issues) + { + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); + } + settings = settings ?? SignedPackageVerifierSettings.Default; + + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, Type.ToString()))); + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.NuGetV3ServiceIndexUrl, V3ServiceIndexUrl.ToString()))); + if (PackageOwners != null) + { + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.NuGetPackageOwners, string.Join(", ", PackageOwners)))); + } + return base.Verify(timestamp, settings, fingerprintAlgorithm, certificateExtraStore, issues); + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/Signature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/Signature.cs index 2ee858ceaf4..4277b4fd607 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/Signature.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/Signature.cs @@ -3,9 +3,12 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; +using System.Linq; #if IS_DESKTOP using System.Security.Cryptography.Pkcs; +using System.Security.Cryptography.X509Certificates; #endif using NuGet.Common; @@ -14,26 +17,16 @@ namespace NuGet.Packaging.Signing /// /// Package signature information. /// - public sealed class Signature + public abstract class Signature { #if IS_DESKTOP private readonly Lazy> _timestamps; - /// - /// A SignedCms object holding the signature and SignerInfo. - /// - public SignedCms SignedCms { get; } - /// /// Indicates if this is an author or repository signature. /// public SignatureType Type { get; } - /// - /// Signature content. - /// - public SignatureContent SignatureContent { get; } - /// /// Signature timestamps. /// @@ -42,132 +35,229 @@ public sealed class Signature /// /// SignerInfo for this signature. /// - public SignerInfo SignerInfo => SignedCms.SignerInfos[0]; + public SignerInfo SignerInfo { get; } + + public abstract byte[] GetSignatureHashValue(HashAlgorithmName hashAlgorithm); - private Signature(SignedCms signedCms, SignatureType signatureType) + protected Signature(SignerInfo signerInfo, SignatureType type) { - SignedCms = signedCms ?? throw new ArgumentNullException(nameof(signedCms)); - SignatureContent = SignatureContent.Load(SignedCms.ContentInfo.Content, SigningSpecifications.V1); - Type = signatureType; + SignerInfo = signerInfo; + Type = type; _timestamps = new Lazy>(() => GetTimestamps(SignerInfo)); - } - /// - /// Save the signed cms signature to a stream. - /// - /// - public void Save(Stream stream) - { - using (var ms = new MemoryStream(SignedCms.Encode())) + if (Type != SignatureType.Unknown) { - ms.CopyTo(stream); + VerifySigningTimeAttribute(SignerInfo); } } - /// - /// Retrieve the bytes of the signed cms signature. - /// - public byte[] GetBytes() - { - return SignedCms.Encode(); - } + protected abstract void ThrowForInvalidSignature(); /// - /// Create a signature based on a valid signed cms + /// /// - /// signature data - public static Signature Load(SignedCms cms) + /// + /// + /// + /// + /// + /// + internal Timestamp GetValidTimestamp( + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + List issues) { - if (cms == null) + if (issues == null) { - throw new ArgumentNullException(nameof(cms)); + throw new ArgumentNullException(nameof(issues)); } + var timestamps = Timestamps; + settings = settings ?? SignedPackageVerifierSettings.Default; - if (cms.SignerInfos.Count != 1) + if (timestamps.Count == 0) { - throw new SignatureException(NuGetLogCode.NU3009, Strings.Error_NotOnePrimarySignature); + issues.Add(SignatureLog.Issue(!settings.AllowNoTimestamp, NuGetLogCode.NU3027, Strings.ErrorNoTimestamp)); + if (!settings.AllowNoTimestamp) + { + throw new TimestampException(Strings.TimestampInvalid); + } } - var signingSpecifications = SigningSpecifications.V1; - var signerInfo = cms.SignerInfos[0]; - var signatureType = AttributeUtility.GetSignatureType(signerInfo.SignedAttributes); - - VerifySigningCertificate(cms, signerInfo, signingSpecifications); - - if (signatureType == SignatureType.Author) + if (timestamps.Count > 1 && !settings.AllowMultipleTimestamps) { - VerifySigningTimeAttribute(signerInfo); + issues.Add(SignatureLog.Issue(true, NuGetLogCode.NU3000, Strings.ErrorMultipleTimestamps)); + throw new TimestampException(Strings.TimestampInvalid); } - return new Signature(cms, signatureType); - } - - /// - /// Create a signature based on a valid byte array to be decoded as a signed cms - /// - /// signature data - public static Signature Load(byte[] data) - { - if (data == null) + var timestamp = timestamps.FirstOrDefault(); + if (timestamp != null && !timestamp.Verify(this, settings, fingerprintAlgorithm, issues) && !settings.AllowIgnoreTimestamp) { - throw new ArgumentNullException(nameof(data)); + throw new TimestampException(Strings.TimestampInvalid); } - var cms = new SignedCms(); - cms.Decode(data); - - return Load(cms); + return timestamp; } /// - /// Create a signature based on a valid byte stream to be decoded as a signed cms + /// Verify if the signature object meets the specification trust and validity requirements. /// - /// signature data - public static Signature Load(Stream stream) + /// Timestamp for this signature, if signature is not timestamped it can be null. + /// Setting that tells if a signature that does not meet any soft failure requirements can still be allowed. Used to know if warnings or errors should be logged for an issue. + /// Setting that tells if unkown revocation is valid when building the chain. + /// Setting that tells if an untrusted self-signed certificate should be allowed as the signing certificate. + /// Algorithm used to calculate and display the certificate's fingerprint. + /// Collection of certificates to help the chain building engine as an extra store. + /// List of log messages. + /// Status of trust for signature. + internal virtual SignatureVerificationStatus Verify( + Timestamp timestamp, + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + X509Certificate2Collection certificateExtraStore, + List issues) { - if (stream == null) + if (issues == null) { - throw new ArgumentNullException(nameof(stream)); + throw new ArgumentNullException(nameof(issues)); } + settings = settings ?? SignedPackageVerifierSettings.Default; - using (stream) - using (var ms = new MemoryStream()) + var treatIssueAsError = !settings.AllowUntrusted; + var certificate = SignerInfo.Certificate; + if (certificate == null) { - stream.CopyTo(ms); - return Load(ms.ToArray()); + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3010, Strings.ErrorNoCertificate)); + + return SignatureVerificationStatus.Invalid; } - } - private static void VerifySigningTimeAttribute(SignerInfo signerInfo) - { - var attribute = signerInfo.SignedAttributes.GetAttributeOrDefault(Oids.SigningTime); + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, + Strings.VerificationAuthorCertDisplay, + $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(certificate, fingerprintAlgorithm)}"))); - if (attribute == null) + try { - ThrowForInvalidAuthorSignature(); + SignerInfo.CheckSignature(verifySignatureOnly: true); } - } + catch (Exception e) + { + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, Strings.ErrorSignatureVerificationFailed)); + issues.Add(SignatureLog.DebugLog(e.ToString())); - private static void VerifySigningCertificate( - SignedCms signedCms, - SignerInfo signerInfo, - SigningSpecifications signingSpecifications) - { - var certificates = SignatureUtility.GetPrimarySignatureSigningCertificate( - signedCms, - signerInfo, - signingSpecifications); + return SignatureVerificationStatus.Invalid; + } - if (certificates == null || certificates.Count == 0) + if (VerificationUtility.IsSigningCertificateValid(certificate, treatIssueAsError, issues)) { - ThrowForInvalidAuthorSignature(); + timestamp = timestamp ?? new Timestamp(); + if (Rfc3161TimestampVerificationUtility.ValidateSignerCertificateAgainstTimestamp(certificate, timestamp)) + { + using (var chainHolder = new X509ChainHolder()) + { + var chain = chainHolder.Chain; + + // These flags should only be set for verification scenarios not signing + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; + + CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, timestamp.UpperLimit.LocalDateTime, CertificateType.Signature); + var chainBuildingSucceed = CertificateChainUtility.BuildCertificateChain(chain, certificate, out var chainStatuses); + + issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm))); + + if (chainBuildingSucceed) + { + return SignatureVerificationStatus.Trusted; + } + + var chainBuildingHasIssues = false; + var statusFlags = CertificateChainUtility.DefaultObservedStatusFlags; + var isSelfSignedCertificate = CertificateUtility.IsSelfIssued(certificate); + + if (isSelfSignedCertificate) + { + statusFlags &= ~X509ChainStatusFlags.UntrustedRoot; + } + + IEnumerable messages; + if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, statusFlags, out messages)) + { + foreach (var message in messages) + { + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, message)); + } + + chainBuildingHasIssues = true; + } + + // For all the special cases, chain status list only has unique elements for each chain status flag present + // therefore if we are checking for one specific chain status we can use the first of the returned list + // if we are combining checks for more than one, then we have to use the whole list. + IEnumerable chainStatus = null; + if (CertificateChainUtility.ChainStatusListIncludesStatus(chainStatuses, X509ChainStatusFlags.Revoked, out chainStatus)) + { + var status = chainStatus.First(); + + issues.Add(SignatureLog.Error(NuGetLogCode.NU3012, status.StatusInformation)); + + return SignatureVerificationStatus.Invalid; + } + + if (isSelfSignedCertificate && + CertificateChainUtility.TryGetStatusMessage(chainStatuses, X509ChainStatusFlags.UntrustedRoot, out messages)) + { + issues.Add(SignatureLog.Issue(!settings.AllowUntrustedSelfIssuedCertificate, NuGetLogCode.NU3018, messages.First())); + + if (!chainBuildingHasIssues && settings.AllowUntrustedSelfIssuedCertificate) + { + return SignatureVerificationStatus.Trusted; + } + } + + const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; + if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, RevocationStatusFlags, out messages)) + { + if (treatIssueAsError) + { + foreach (var message in messages) + { + issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3018, message)); + } + } + + if (!chainBuildingHasIssues && settings.AllowUnknownRevocation) + { + return SignatureVerificationStatus.Trusted; + } + + chainBuildingHasIssues = true; + } + + // Debug log any errors + issues.Add(SignatureLog.DebugLog( + string.Format( + CultureInfo.CurrentCulture, + Strings.ErrorInvalidCertificateChain, + string.Join(", ", chainStatuses.Select(x => x.Status.ToString()))))); + } + } + else + { + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3011, Strings.SignatureNotTimeValid)); + } } + + return SignatureVerificationStatus.Untrusted; } - private static void ThrowForInvalidAuthorSignature() + private void VerifySigningTimeAttribute(SignerInfo signerInfo) { - throw new SignatureException(NuGetLogCode.NU3011, Strings.InvalidPrimarySignature); + var attribute = signerInfo.SignedAttributes.GetAttributeOrDefault(Oids.SigningTime); + + if (attribute == null) + { + ThrowForInvalidSignature(); + } } /// @@ -177,18 +267,18 @@ private static void ThrowForInvalidAuthorSignature() /// private static IReadOnlyList GetTimestamps(SignerInfo signer) { - var authorUnsignedAttributes = signer.UnsignedAttributes; + var unsignedAttributes = signer.UnsignedAttributes; var timestampList = new List(); - foreach (var attribute in authorUnsignedAttributes) + foreach (var attribute in unsignedAttributes) { if (string.Equals(attribute.Oid.Value, Oids.SignatureTimeStampTokenAttribute, StringComparison.Ordinal)) { var timestampCms = new SignedCms(); timestampCms.Decode(attribute.Values[0].RawData); - var certificates = SignatureUtility.GetTimestampSignatureSigningCertificate( + var certificates = SignatureUtility.GetTimestampCertificates( timestampCms, SigningSpecifications.V1); @@ -203,15 +293,6 @@ private static IReadOnlyList GetTimestamps(SignerInfo signer) return timestampList; } - -#else - /// - /// Retrieve the bytes of the signed cms signature. - /// - public byte[] GetBytes() - { - throw new NotSupportedException(); - } #endif } } \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/UnknownPrimarySignature.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/UnknownPrimarySignature.cs new file mode 100644 index 00000000000..3b3e943c019 --- /dev/null +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Signatures/UnknownPrimarySignature.cs @@ -0,0 +1,19 @@ +// 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. + +#if IS_DESKTOP +using System.Security.Cryptography.Pkcs; +#endif + +namespace NuGet.Packaging.Signing +{ + public sealed class UnknownPrimarySignature : PrimarySignature + { +#if IS_DESKTOP + public UnknownPrimarySignature(SignedCms signedCms) + : base(signedCms, SignatureType.Unknown) + { + } +#endif + } +} diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Rfc3161TimestampProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Rfc3161TimestampProvider.cs index f691105d881..aad9e8280e0 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Rfc3161TimestampProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Rfc3161TimestampProvider.cs @@ -51,10 +51,10 @@ public Rfc3161TimestampProvider(Uri timeStampServerUrl) /// /// Timestamps data present in the TimestampRequest. /// - public Task TimestampSignatureAsync(TimestampRequest request, ILogger logger, CancellationToken token) + public Task TimestampPrimarySignatureAsync(TimestampRequest request, ILogger logger, CancellationToken token) { var timestampedSignature = TimestampData(request, logger, token); - return Task.FromResult(Signature.Load(timestampedSignature)); + return Task.FromResult(PrimarySignature.Load(timestampedSignature)); } /// @@ -208,7 +208,7 @@ private static byte[] GenerateNonce() /// /// Timestamp a signature. /// - public Task TimestampSignatureAsync(TimestampRequest timestampRequest, ILogger logger, CancellationToken token) + public Task TimestampPrimarySignatureAsync(TimestampRequest timestampRequest, ILogger logger, CancellationToken token) { throw new NotImplementedException(); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Timestamp.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Timestamp.cs index f012314651e..e2dab1b0f56 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Timestamp.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Timestamp.cs @@ -3,6 +3,10 @@ using System; using NuGet.Common; +using System.Collections.Generic; +using System.Globalization; +using System.Security.Cryptography.X509Certificates; +using System.Linq; #if IS_DESKTOP using System.Security.Cryptography.Pkcs; @@ -85,6 +89,121 @@ public Timestamp(SignedCms timestampCms) throw new TimestampException(NuGetLogCode.NU3021, Strings.TimestampSignatureValidationFailed); } } + + /// + /// Verify if the timestamp object meets the specification requirements. + /// + /// Signature which this timestamp is for. + /// Setting that tells if a timestamp can be ignored if it doesn't meet the requirements. Used to know if warnings or errors should be logged for an issue. + /// Setting that tells if unkown revocation is valid when building the chain. + /// List of log messages. + /// true if the timestamp meets the requierements, false otherwise. + internal bool Verify( + Signature signature, + SignedPackageVerifierSettings settings, + HashAlgorithmName fingerprintAlgorithm, + List issues) + { + settings = settings ?? SignedPackageVerifierSettings.Default; + + if (signature == null) + { + throw new ArgumentNullException(nameof(signature)); + } + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); + } + + var treatIssueAsError = !settings.AllowIgnoreTimestamp; + var timestamperCertificate = SignerInfo.Certificate; + if (timestamperCertificate == null) + { + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); + return false; + } + + if (VerificationUtility.IsTimestampValid(this, signature, treatIssueAsError, issues, SigningSpecifications.V1)) + { + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, GeneralizedTime.LocalDateTime.ToString()) + Environment.NewLine)); + + issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, + Strings.VerificationTimestamperCertDisplay, + $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate, fingerprintAlgorithm)}"))); + + var certificateExtraStore = SignedCms.Certificates; + + using (var chainHolder = new X509ChainHolder()) + { + var chain = chainHolder.Chain; + + // This flags should only be set for verification scenarios, not signing + chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; + + CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, DateTime.Now, CertificateType.Timestamp); + + var chainBuildSucceed = CertificateChainUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList); + + issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm))); + + if (chainBuildSucceed) + { + return true; + } + + var chainBuildingHasIssues = false; + IEnumerable messages; + + var timestampInvalidCertificateFlags = CertificateChainUtility.DefaultObservedStatusFlags | + (X509ChainStatusFlags.Revoked) | + (X509ChainStatusFlags.NotTimeValid) | + (X509ChainStatusFlags.CtlNotTimeValid); + + if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, timestampInvalidCertificateFlags, out messages)) + { + foreach (var message in messages) + { + issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3028, message)); + } + + chainBuildingHasIssues = true; + } + + // For all the special cases, chain status list only has unique elements for each chain status flag present + // therefore if we are checking for one specific chain status we can use the first of the returned list + // if we are combining checks for more than one, then we have to use the whole list. + + const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; + if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, RevocationStatusFlags, out messages)) + { + if (treatIssueAsError) + { + foreach (var message in messages) + { + issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3028, message)); + } + } + + if (!chainBuildingHasIssues && (settings.AllowIgnoreTimestamp || settings.AllowUnknownRevocation)) + { + return true; + } + + chainBuildingHasIssues = true; + } + + // Debug log any errors + issues.Add( + SignatureLog.DebugLog( + string.Format( + CultureInfo.CurrentCulture, + Strings.ErrorInvalidCertificateChain, + string.Join(", ", chainStatusList.Select(x => x.Status.ToString()))))); + } + } + + return false; + } #endif } } \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/CertificateUtility.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/CertificateUtility.cs index 538183ff4c5..a672d1083a7 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/CertificateUtility.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/CertificateUtility.cs @@ -28,7 +28,7 @@ public static class CertificateUtility public static string X509Certificate2ToString(X509Certificate2 cert, HashAlgorithmName fingerprintAlgorithm) { var certStringBuilder = new StringBuilder(); - X509Certificate2ToString(cert, certStringBuilder, fingerprintAlgorithm, indentation: ""); + X509Certificate2ToString(cert, certStringBuilder, fingerprintAlgorithm, indentation: " "); return certStringBuilder.ToString(); } @@ -69,7 +69,7 @@ public static string X509Certificate2CollectionToString(X509Certificate2Collecti for (var i = 0; i < Math.Min(_limit, certCollection.Count); i++) { var cert = certCollection[i]; - X509Certificate2ToString(cert, collectionStringBuilder, fingerprintAlgorithm, indentation: ""); + X509Certificate2ToString(cert, collectionStringBuilder, fingerprintAlgorithm, indentation: " "); collectionStringBuilder.AppendLine(); } @@ -84,7 +84,7 @@ public static string X509Certificate2CollectionToString(X509Certificate2Collecti public static string X509ChainToString(X509Chain chain, HashAlgorithmName fingerprintAlgorithm) { var collectionStringBuilder = new StringBuilder(); - var indentationLevel = " "; + var indentationLevel = " "; var indentation = indentationLevel; var chainElementsCount = chain.ChainElements.Count; diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/SignatureUtility.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/SignatureUtility.cs index 83ef821ae5d..0ecc74968ee 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/SignatureUtility.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/SignatureUtility.cs @@ -34,7 +34,7 @@ private enum SigningCertificateRequirement /// /// WARNING: This method does not perform revocation, trust, or certificate validity checking. /// - public static IReadOnlyList GetPrimarySignatureCertificates(Signature signature) + public static IReadOnlyList GetPrimarySignatureCertificates(PrimarySignature signature) { if (signature == null) { @@ -48,7 +48,7 @@ public static IReadOnlyList GetPrimarySignatureCertificates(Si includeChain: true); } - internal static IReadOnlyList GetPrimarySignatureSigningCertificate( + internal static IReadOnlyList GetPrimarySignatureCertificates( SignedCms signedCms, SignerInfo signerInfo, SigningSpecifications signingSpecifications) @@ -115,8 +115,8 @@ private static IReadOnlyList GetPrimarySignatureCertificates( /// /// WARNING: This method does not perform revocation, trust, or certificate validity checking. /// - public static IReadOnlyList GetPrimarySignatureTimestampSignatureCertificates( - Signature signature) + public static IReadOnlyList GetPrimarySignatureTimestampCertificates( + PrimarySignature signature) { if (signature == null) { @@ -130,23 +130,23 @@ public static IReadOnlyList GetPrimarySignatureTimestampSignat throw new SignatureException(NuGetLogCode.NU3029, Strings.PrimarySignatureHasNoTimestamp); } - return GetTimestampSignatureSigningCertificates( + return GetTimestampCertificates( timestamp.SignedCms, SigningSpecifications.V1, includeChain: true); } - internal static IReadOnlyList GetTimestampSignatureSigningCertificate( + internal static IReadOnlyList GetTimestampCertificates( SignedCms signedCms, SigningSpecifications signingSpecifications) { - return GetTimestampSignatureSigningCertificates( + return GetTimestampCertificates( signedCms, signingSpecifications, includeChain: false); } - internal static IReadOnlyList GetTimestampSignatureSigningCertificates( + internal static IReadOnlyList GetTimestampCertificates( SignedCms signedCms, SigningSpecifications signingSpecifications, bool includeChain) diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/VerificationUtility.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/VerificationUtility.cs index fd22a973a03..1690dd1fd11 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Utility/VerificationUtility.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Utility/VerificationUtility.cs @@ -13,6 +13,14 @@ public static class VerificationUtility { internal static bool IsSigningCertificateValid(X509Certificate2 certificate, bool treatIssuesAsErrors, List issues) { + if (certificate == null) + { + throw new ArgumentNullException(nameof(certificate)); + } + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); + } var isValid = true; if (!CertificateUtility.IsSignatureAlgorithmSupported(certificate)) @@ -43,17 +51,27 @@ internal static bool IsSigningCertificateValid(X509Certificate2 certificate, boo } #if IS_DESKTOP - internal static bool IsTimestampValid(Timestamp timestamp, byte[] data, bool treatIssuesAsErrors, List issues, SigningSpecifications spec) + internal static bool IsTimestampValid(Timestamp timestamp, Signature signature, bool treatIssuesAsErrors, List issues, SigningSpecifications spec) { - var isValid = true; - var signerInfo = timestamp.SignerInfo; - - if (!timestamp.TstInfo.HasMessageHash(data)) + if (timestamp == null) { - issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3019, Strings.TimestampIntegrityCheckFailed)); - isValid = false; + throw new ArgumentNullException(nameof(timestamp)); + } + if (signature == null) + { + throw new ArgumentNullException(nameof(signature)); + } + if (issues == null) + { + throw new ArgumentNullException(nameof(issues)); } + // Default to specification v1 + spec = spec ?? SigningSpecifications.V1; + + var isValid = true; + var signerInfo = timestamp.SignerInfo; + if (timestamp.SignerInfo.Certificate != null) { try @@ -85,6 +103,22 @@ internal static bool IsTimestampValid(Timestamp timestamp, byte[] data, bool tre isValid = false; } + try + { + var hashAlgorithm = CryptoHashUtility.OidToHashAlgorithmName(timestamp.TstInfo.HashAlgorithmId.Value); + if (!timestamp.TstInfo.HasMessageHash(signature.GetSignatureHashValue(hashAlgorithm))) + { + issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3019, Strings.TimestampIntegrityCheckFailed)); + isValid = false; + } + } + catch + { + // If the hash algorithm is not supported OidToHashAlgorithmName will throw + issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3030, Strings.TimestampMessageImprintUnsupportedHashAlgorithm)); + isValid = false; + } + if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3025, Strings.TimestampNotYetValid)); diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/AllowListVerificationProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/AllowListVerificationProvider.cs index d89a5a72640..85c32bb0aee 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/AllowListVerificationProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/AllowListVerificationProvider.cs @@ -22,13 +22,13 @@ public AllowListVerificationProvider(IReadOnlyList a _allowList = allowList; } - public Task GetTrustResultAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings, CancellationToken token) + public Task GetTrustResultAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings, CancellationToken token) { return Task.FromResult(VerifyAllowList(package, signature, settings)); } #if IS_DESKTOP - private PackageVerificationResult VerifyAllowList(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings) + private PackageVerificationResult VerifyAllowList(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings) { var status = SignatureVerificationStatus.Trusted; var issues = new List(); @@ -42,7 +42,7 @@ private PackageVerificationResult VerifyAllowList(ISignedPackageReader package, return new SignedPackageVerificationResult(status, signature, issues); } - private bool IsSignatureAllowed(Signature signature) + private bool IsSignatureAllowed(PrimarySignature signature) { // Get information needed for allow list verification var primarySignatureCertificateFingerprint = CertificateUtility.GetHash(signature.SignerInfo.Certificate, _fingerprintAlgorithm); @@ -66,7 +66,7 @@ private bool IsSignatureAllowed(Signature signature) } #else - private PackageVerificationResult VerifyAllowList(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings) + private PackageVerificationResult VerifyAllowList(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings) { throw new NotSupportedException(); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/ISignatureVerificationProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/ISignatureVerificationProvider.cs index f2a3759a366..4ee64bd0ac0 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/ISignatureVerificationProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/ISignatureVerificationProvider.cs @@ -15,6 +15,6 @@ public interface ISignatureVerificationProvider /// /// Check if is trusted by the provider. /// - Task GetTrustResultAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings, CancellationToken token); + Task GetTrustResultAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings, CancellationToken token); } } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/IntegrityVerificationProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/IntegrityVerificationProvider.cs index 649907cc82c..f8d5b2c014f 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/IntegrityVerificationProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/IntegrityVerificationProvider.cs @@ -13,13 +13,13 @@ namespace NuGet.Packaging.Signing { public class IntegrityVerificationProvider : ISignatureVerificationProvider { - public Task GetTrustResultAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings, CancellationToken token) + public Task GetTrustResultAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings, CancellationToken token) { return VerifyPackageIntegrityAsync(package, signature, settings); } #if IS_DESKTOP - private async Task VerifyPackageIntegrityAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings) + private async Task VerifyPackageIntegrityAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings) { var status = SignatureVerificationStatus.Untrusted; var issues = new List(); @@ -51,7 +51,7 @@ private async Task VerifyPackageIntegrityAsync(ISigne return new SignedPackageVerificationResult(status, signature, issues); } #else - private Task VerifyPackageIntegrityAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings) + private Task VerifyPackageIntegrityAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings) { throw new NotSupportedException(); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/PackageSignatureVerifier.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/PackageSignatureVerifier.cs index 0defebd608e..2ba0d03b000 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/PackageSignatureVerifier.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/PackageSignatureVerifier.cs @@ -37,7 +37,7 @@ public PackageSignatureVerifier(IEnumerable veri { try { - var signature = await package.GetSignatureAsync(token); + var signature = await package.GetPrimarySignatureAsync(token); if (signature != null) { diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignatureTrustAndValidityVerificationProvider.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignatureTrustAndValidityVerificationProvider.cs index 2b0e99e494b..84fd4d29186 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignatureTrustAndValidityVerificationProvider.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignatureTrustAndValidityVerificationProvider.cs @@ -23,7 +23,7 @@ public SignatureTrustAndValidityVerificationProvider() _fingerprintAlgorithm = HashAlgorithmName.SHA256; } - public Task GetTrustResultAsync(ISignedPackageReader package, Signature signature, SignedPackageVerifierSettings settings, CancellationToken token) + public Task GetTrustResultAsync(ISignedPackageReader package, PrimarySignature signature, SignedPackageVerifierSettings settings, CancellationToken token) { token.ThrowIfCancellationRequested(); @@ -32,342 +32,39 @@ public Task GetTrustResultAsync(ISignedPackageReader } #if IS_DESKTOP - private PackageVerificationResult VerifyValidityAndTrust(Signature signature, SignedPackageVerifierSettings settings) + private PackageVerificationResult VerifyValidityAndTrust(PrimarySignature signature, SignedPackageVerifierSettings settings) { - var issues = new List - { - SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, signature.Type.ToString())) - }; + var timestampIssues = new List(); Timestamp validTimestamp; try { - validTimestamp = GetValidTimestamp( - signature, - settings.AllowMultipleTimestamps, - settings.AllowIgnoreTimestamp, - settings.AllowNoTimestamp, - settings.AllowUnknownRevocation, - issues); + validTimestamp = signature.GetValidTimestamp( + settings, + _fingerprintAlgorithm, + timestampIssues); } catch (TimestampException) { - return new SignedPackageVerificationResult(SignatureVerificationStatus.Invalid, signature, issues); - } - - var status = VerifySignature( - signature, - validTimestamp, - settings.AllowUntrusted, - settings.AllowUntrustedSelfSignedCertificate, - settings.AllowUnknownRevocation, - issues); - - return new SignedPackageVerificationResult(status, signature, issues); - } - - private Timestamp GetValidTimestamp( - Signature signature, - bool allowMultipleTimestamps, - bool allowIgnoreTimestamp, - bool allowNoTimestamp, - bool allowUnknownRevocation, - List issues) - { - var timestamps = signature.Timestamps; - - if (timestamps.Count == 0) - { - issues.Add(SignatureLog.Issue(!allowNoTimestamp, NuGetLogCode.NU3027, Strings.ErrorNoTimestamp)); - if (!allowNoTimestamp) - { - throw new TimestampException(); - } - } - - if (timestamps.Count > 1 && !allowMultipleTimestamps) - { - issues.Add(SignatureLog.Issue(true, NuGetLogCode.NU3000, Strings.ErrorMultipleTimestamps)); - throw new TimestampException(); - } - - var timestamp = timestamps.FirstOrDefault(); - if (timestamp != null) - { - using (var primarySignatureNativeCms = NativeCms.Decode(signature.SignedCms.Encode(), detached: false)) - { - var timestampHashAlgorithmName = GetTimestampMessageImprintHashAlgorithmName(timestamp); - if (timestampHashAlgorithmName == HashAlgorithmName.Unknown) - { - issues.Add( - SignatureLog.Issue( - !allowNoTimestamp, - NuGetLogCode.NU3030, - Strings.TimestampMessageImprintUnsupportedHashAlgorithm)); - - return null; - } - - var signatureHash = NativeCms.GetSignatureValueHash(timestampHashAlgorithmName, primarySignatureNativeCms); - - if (!IsTimestampValid(timestamp, signatureHash, allowIgnoreTimestamp, allowUnknownRevocation, issues) && !allowIgnoreTimestamp) - { - throw new TimestampException(); - } - } - } - - return timestamp; - } - - private static HashAlgorithmName GetTimestampMessageImprintHashAlgorithmName(Timestamp timestamp) - { - try - { - return CryptoHashUtility.OidToHashAlgorithmName(timestamp.TstInfo.HashAlgorithmId.Value); - } - catch (ArgumentException) - { - } - - return HashAlgorithmName.Unknown; - } - - private SignatureVerificationStatus VerifySignature( - Signature signature, - Timestamp timestamp, - bool allowUntrusted, - bool allowUntrustedSelfSignedCertificate, - bool allowUnknownRevocation, - List issues) - { - var treatIssueAsError = !allowUntrusted; - var certificate = signature.SignerInfo.Certificate; - if (certificate == null) - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3010, Strings.ErrorNoCertificate)); - - return SignatureVerificationStatus.Invalid; - } - - issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, - Strings.VerificationAuthorCertDisplay, - $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(certificate, _fingerprintAlgorithm)}"))); - - try - { - signature.SignerInfo.CheckSignature(verifySignatureOnly: true); - } - catch (Exception e) - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, Strings.ErrorSignatureVerificationFailed)); - issues.Add(SignatureLog.DebugLog(e.ToString())); - - return SignatureVerificationStatus.Invalid; - } - - if (VerificationUtility.IsSigningCertificateValid(certificate, treatIssueAsError, issues)) - { - timestamp = timestamp ?? new Timestamp(); - if (Rfc3161TimestampVerificationUtility.ValidateSignerCertificateAgainstTimestamp(certificate, timestamp)) - { - var certificateExtraStore = signature.SignedCms.Certificates; - - using (var chainHolder = new X509ChainHolder()) - { - var chain = chainHolder.Chain; - - // These flags should only be set for verification scenarios not signing - chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; - - CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, timestamp.UpperLimit.LocalDateTime, CertificateType.Signature); - var chainBuildingSucceed = CertificateChainUtility.BuildCertificateChain(chain, certificate, out var chainStatuses); - - issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, _fingerprintAlgorithm))); - - if (chainBuildingSucceed) - { - return SignatureVerificationStatus.Trusted; - } - - var chainBuildingHasIssues = false; - var statusFlags = CertificateChainUtility.DefaultObservedStatusFlags; - var isSelfSignedCertificate = CertificateUtility.IsSelfIssued(certificate); - - if (isSelfSignedCertificate) - { - statusFlags &= ~X509ChainStatusFlags.UntrustedRoot; - } - - IEnumerable messages; - if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, statusFlags, out messages)) - { - foreach (var message in messages) - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, message)); - } - - chainBuildingHasIssues = true; - } - - // For all the special cases, chain status list only has unique elements for each chain status flag present - // therefore if we are checking for one specific chain status we can use the first of the returned list - // if we are combining checks for more than one, then we have to use the whole list. - IEnumerable chainStatus = null; - if (CertificateChainUtility.ChainStatusListIncludesStatus(chainStatuses, X509ChainStatusFlags.Revoked, out chainStatus)) - { - var status = chainStatus.First(); - - issues.Add(SignatureLog.Error(NuGetLogCode.NU3012, status.StatusInformation)); - - return SignatureVerificationStatus.Invalid; - } - - if (isSelfSignedCertificate && - CertificateChainUtility.TryGetStatusMessage(chainStatuses, X509ChainStatusFlags.UntrustedRoot, out messages)) - { - issues.Add(SignatureLog.Issue(!allowUntrustedSelfSignedCertificate, NuGetLogCode.NU3018, messages.First())); - - if (!chainBuildingHasIssues && allowUntrustedSelfSignedCertificate) - { - return SignatureVerificationStatus.Trusted; - } - } - - const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; - if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, RevocationStatusFlags, out messages)) - { - if (treatIssueAsError) - { - foreach (var message in messages) - { - issues.Add(SignatureLog.Issue(!allowUnknownRevocation, NuGetLogCode.NU3018, message)); - } - } - - if (!chainBuildingHasIssues && allowUnknownRevocation) - { - return SignatureVerificationStatus.Trusted; - } - - chainBuildingHasIssues = true; - } - - // Debug log any errors - issues.Add(SignatureLog.DebugLog( - string.Format( - CultureInfo.CurrentCulture, - Strings.ErrorInvalidCertificateChain, - string.Join(", ", chainStatuses.Select(x => x.Status.ToString()))))); - } - } - else - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3011, Strings.SignatureNotTimeValid)); - } - } - - return SignatureVerificationStatus.Untrusted; - } - - private bool IsTimestampValid( - Timestamp timestamp, - byte[] messageHash, - bool allowIgnoreTimestamp, - bool allowUnknownRevocation, - List issues) - { - var treatIssueAsError = !allowIgnoreTimestamp; - var timestamperCertificate = timestamp.SignerInfo.Certificate; - if (timestamperCertificate == null) - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); - return false; + return new SignedPackageVerificationResult(SignatureVerificationStatus.Invalid, signature, timestampIssues); } - if (VerificationUtility.IsTimestampValid(timestamp, messageHash, treatIssueAsError, issues, _specification)) - { - issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, timestamp.GeneralizedTime.LocalDateTime.ToString()) + Environment.NewLine)); - - issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, - Strings.VerificationTimestamperCertDisplay, - $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate, _fingerprintAlgorithm)}"))); - - var certificateExtraStore = timestamp.SignedCms.Certificates; - - using (var chainHolder = new X509ChainHolder()) - { - var chain = chainHolder.Chain; - - // This flags should only be set for verification scenarios, not signing - chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; - - CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, DateTime.Now, CertificateType.Timestamp); - - var chainBuildSucceed = CertificateChainUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList); + var certificateExtraStore = signature.SignedCms.Certificates; + var signatureIssues = new List(); - issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, _fingerprintAlgorithm))); - - if (chainBuildSucceed) - { - return true; - } - - var chainBuildingHasIssues = false; - IEnumerable messages; - - var timestampInvalidCertificateFlags = CertificateChainUtility.DefaultObservedStatusFlags | - (X509ChainStatusFlags.Revoked) | - (X509ChainStatusFlags.NotTimeValid) | - (X509ChainStatusFlags.CtlNotTimeValid); - - if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, timestampInvalidCertificateFlags, out messages)) - { - foreach (var message in messages) - { - issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3028, message)); - } - - chainBuildingHasIssues = true; - } - - // For all the special cases, chain status list only has unique elements for each chain status flag present - // therefore if we are checking for one specific chain status we can use the first of the returned list - // if we are combining checks for more than one, then we have to use the whole list. - - const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; - if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, RevocationStatusFlags, out messages)) - { - if (treatIssueAsError) - { - foreach (var message in messages) - { - issues.Add(SignatureLog.Issue(!allowUnknownRevocation, NuGetLogCode.NU3028, message)); - } - } - - if (!chainBuildingHasIssues && (allowIgnoreTimestamp || allowUnknownRevocation)) - { - return true; - } - - chainBuildingHasIssues = true; - } + var status = signature.Verify( + validTimestamp, + settings, + _fingerprintAlgorithm, + certificateExtraStore, + signatureIssues); - // Debug log any errors - issues.Add( - SignatureLog.DebugLog( - string.Format( - CultureInfo.CurrentCulture, - Strings.ErrorInvalidCertificateChain, - string.Join(", ", chainStatusList.Select(x => x.Status.ToString()))))); - } - } + signatureIssues.AddRange(timestampIssues); - return false; + return new SignedPackageVerificationResult(status, signature, signatureIssues); } #else - private PackageVerificationResult VerifyValidityAndTrust(Signature signature, SignedPackageVerifierSettings settings) + private PackageVerificationResult VerifyValidityAndTrust(PrimarySignature signature, SignedPackageVerifierSettings settings) { throw new NotSupportedException(); } diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerificationResult.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerificationResult.cs index 00ec16c6451..fc9a185fe84 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerificationResult.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerificationResult.cs @@ -12,9 +12,9 @@ public class SignedPackageVerificationResult : PackageVerificationResult /// /// Signature /// - public Signature Signature { get; } + public PrimarySignature Signature { get; } - public SignedPackageVerificationResult(SignatureVerificationStatus trust, Signature signature, IEnumerable issues): + public SignedPackageVerificationResult(SignatureVerificationStatus trust, PrimarySignature signature, IEnumerable issues): base(trust, issues) { Signature = signature ?? throw new ArgumentNullException(nameof(signature)); diff --git a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerifierSettings.cs b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerifierSettings.cs index ea51a91f41b..9fff3b5618e 100644 --- a/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerifierSettings.cs +++ b/src/NuGet.Core/NuGet.Packaging/Signing/Verification/SignedPackageVerifierSettings.cs @@ -18,7 +18,7 @@ public sealed class SignedPackageVerifierSettings /// public bool AllowUntrusted { get; } - public bool AllowUntrustedSelfSignedCertificate { get; } + public bool AllowUntrustedSelfIssuedCertificate { get; } public bool AllowIgnoreTimestamp { get; } @@ -42,7 +42,7 @@ public SignedPackageVerifierSettings( { AllowUnsigned = allowUnsigned; AllowUntrusted = allowUntrusted; - AllowUntrustedSelfSignedCertificate = allowUntrustedSelfSignedCertificate; + AllowUntrustedSelfIssuedCertificate = allowUntrustedSelfSignedCertificate; AllowIgnoreTimestamp = allowIgnoreTimestamp; AllowMultipleTimestamps = allowMultipleTimestamps; AllowNoTimestamp = allowNoTimestamp; diff --git a/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs b/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs index 39f281ab055..d47232e4107 100644 --- a/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs @@ -196,6 +196,24 @@ internal static string Error_NotOnePrimarySignature { } } + /// + /// Looks up a localized string similar to The package signature contains multiple repository countersignatures.. + /// + internal static string Error_NotOneRepositoryCounterSignature { + get { + return ResourceManager.GetString("Error_NotOneRepositoryCounterSignature", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A repository primary signature should not have a repository countersignatures.. + /// + internal static string Error_RepositorySignatureShouldNotHaveARepositoryCountersignature { + get { + return ResourceManager.GetString("Error_RepositorySignatureShouldNotHaveARepositoryCountersignature", resourceCulture); + } + } + /// /// Looks up a localized string similar to Byte signature not found in package archive: 0x{0}. /// @@ -484,6 +502,15 @@ internal static string InvalidPackageFrameworkFolderName { } } + /// + /// Looks up a localized string similar to Package {0} signature is invalid.. + /// + internal static string InvalidPackageSignature { + get { + return ResourceManager.GetString("InvalidPackageSignature", resourceCulture); + } + } + /// /// Looks up a localized string similar to The package signature file entry is invalid.. /// @@ -520,6 +547,15 @@ internal static string InvalidPrimarySignature { } } + /// + /// Looks up a localized string similar to The repository countersignature is invalid.. + /// + internal static string InvalidRepositoryCounterSignature { + get { + return ResourceManager.GetString("InvalidRepositoryCounterSignature", resourceCulture); + } + } + /// /// Looks up a localized string similar to The package signature content is invalid.. /// @@ -601,6 +637,15 @@ internal static string MustContainAbsolutePath { } } + /// + /// Looks up a localized string similar to nuget-package-owners: {0}. + /// + internal static string NuGetPackageOwners { + get { + return ResourceManager.GetString("NuGetPackageOwners", resourceCulture); + } + } + /// /// Looks up a localized string similar to The nuget-package-owners attribute is invalid.. /// @@ -619,6 +664,15 @@ internal static string NuGetPackageOwnersInvalidValue { } } + /// + /// Looks up a localized string similar to nuget-v3-service-index-url: {0}. + /// + internal static string NuGetV3ServiceIndexUrl { + get { + return ResourceManager.GetString("NuGetV3ServiceIndexUrl", resourceCulture); + } + } + /// /// Looks up a localized string similar to The nuget-v3-service-index-url attribute is invalid.. /// diff --git a/src/NuGet.Core/NuGet.Packaging/Strings.resx b/src/NuGet.Core/NuGet.Packaging/Strings.resx index 9049100a6b9..33c344cfeb9 100644 --- a/src/NuGet.Core/NuGet.Packaging/Strings.resx +++ b/src/NuGet.Core/NuGet.Packaging/Strings.resx @@ -518,4 +518,21 @@ Valid from: The timestamp's message imprint uses an unsupported hash algorithm. + + The package signature contains multiple repository countersignatures. + + + nuget-package-owners: {0} + 0 - The nuget-package-owners value + + + nuget-v3-service-index-url: {0} + 0- The nuget-v3-service-index-url value + + + A repository primary signature should not have a repository countersignatures. + + + The repository countersignature is invalid. + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginPackageReader.cs b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginPackageReader.cs index 2b9274d40c2..7eb70df6958 100644 --- a/src/NuGet.Core/NuGet.Protocol/Plugins/PluginPackageReader.cs +++ b/src/NuGet.Core/NuGet.Protocol/Plugins/PluginPackageReader.cs @@ -1124,9 +1124,9 @@ private static string GetTemporaryDirectoryPath() return tempDirectoryPath; } - public override Task GetSignatureAsync(CancellationToken token) + public override Task GetPrimarySignatureAsync(CancellationToken token) { - return Task.FromResult(null); + return Task.FromResult(null); } public override Task IsSignedAsync(CancellationToken token) diff --git a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTests.cs b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/PrimarySignatureTests.cs similarity index 93% rename from test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTests.cs rename to test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/PrimarySignatureTests.cs index e1b8fbb9e72..088cb07b762 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/PrimarySignatureTests.cs @@ -23,14 +23,14 @@ namespace NuGet.Packaging.FuncTest { [Collection("Signing Functional Test Collection")] - public class SignatureTests + public class PrimarySignatureTests { private SigningTestFixture _testFixture; private TrustedTestCert _trustedTestCert; private IList _trustProviders; private SigningSpecifications _signingSpecifications; - public SignatureTests(SigningTestFixture fixture) + public PrimarySignatureTests(SigningTestFixture fixture) { _testFixture = fixture ?? throw new ArgumentNullException(nameof(fixture)); _trustedTestCert = _testFixture.TrustedTestCertificate; @@ -58,7 +58,7 @@ public async Task Signature_HasTimestamp() using (var stream = File.OpenRead(signedPackagePath)) using (var reader = new PackageArchiveReader(stream)) { - var signature = await reader.GetSignatureAsync(CancellationToken.None); + var signature = await reader.GetPrimarySignatureAsync(CancellationToken.None); signature.Should().NotBeNull(); signature.Timestamps.Should().NotBeEmpty(); @@ -82,7 +82,7 @@ public async Task Signature_HasNoTimestamp() using (var stream = File.OpenRead(signedPackagePath)) using (var reader = new PackageArchiveReader(stream)) { - var signature = await reader.GetSignatureAsync(CancellationToken.None); + var signature = await reader.GetPrimarySignatureAsync(CancellationToken.None); signature.Should().NotBeNull(); signature.Timestamps.Should().BeEmpty(); @@ -131,7 +131,7 @@ public async Task Load_WithPrimarySignatureWithNoCertificates_Throws() Assert.Empty(signedCms.Certificates); var exception = Assert.Throws( - () => Signature.Load(signedCms)); + () => PrimarySignature.Load(signedCms)); Assert.Equal(NuGetLogCode.NU3010, exception.Code); Assert.Equal("The primary signature does not have a signing certificate.", exception.Message); @@ -155,7 +155,7 @@ public async Task Load_WithReissuedSigningCertificate_Throws() using (var packageReader = new PackageArchiveReader(packageFilePath)) { - var signature = (await packageReader.GetSignatureAsync(CancellationToken.None)); + var signature = (await packageReader.GetPrimarySignatureAsync(CancellationToken.None)); var certificateStore = X509StoreFactory.Create( "Certificate/Collection", @@ -180,7 +180,7 @@ public async Task Load_WithReissuedSigningCertificate_Throws() writeStream); var exception = Assert.Throws( - () => Signature.Load(writeStream.ToArray())); + () => PrimarySignature.Load(writeStream.ToArray())); Assert.Equal(NuGetLogCode.NU3011, exception.Code); Assert.Equal("A certificate referenced by the signing-certificate-v2 attribute could not be found.", exception.Message); diff --git a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTrustAndValidityVerificationProviderTests.cs b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTrustAndValidityVerificationProviderTests.cs index 13f06e90e8e..d2edd20f796 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTrustAndValidityVerificationProviderTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignatureTrustAndValidityVerificationProviderTests.cs @@ -336,8 +336,8 @@ public async Task GetTrustResultAsync_WithInvalidSignature_Throws() using (var packageReader = new PackageArchiveReader(packageFilePath)) { - var signature = await packageReader.GetSignatureAsync(CancellationToken.None); - var invalidSignature = SignedArchiveTestUtility.GenerateInvalidSignature(signature); + var signature = await packageReader.GetPrimarySignatureAsync(CancellationToken.None); + var invalidSignature = SignedArchiveTestUtility.GenerateInvalidPrimarySignature(signature); var provider = new SignatureTrustAndValidityVerificationProvider(); var result = await provider.GetTrustResultAsync( @@ -445,7 +445,7 @@ private static async Task> VerifyUnavailableRevocationInfo( using (var package = new PackageArchiveReader(nupkgStream, leaveStreamOpen: false)) { // Read a signature that is valid in every way except that the CRL information is unavailable. - var signature = await package.GetSignatureAsync(CancellationToken.None); + var signature = await package.GetPrimarySignatureAsync(CancellationToken.None); var rootCertificate = SignatureUtility.GetPrimarySignatureCertificates(signature).Last(); // Trust the root CA of the signing certificate. @@ -492,9 +492,9 @@ public async Task GetTrustResultAsync_SettingsRequireExactlyOneTimestamp_Multipl using (var testCertificate = new X509Certificate2(_trustedTestCert.Source.Cert)) using (var signatureRequest = new AuthorSignPackageRequest(testCertificate, HashAlgorithmName.SHA256)) { - var signature = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(signatureProvider, package, signatureRequest, testLogger); - var timestampedSignature = await SignedArchiveTestUtility.TimestampSignature(timestampProvider, signatureRequest, signature, testLogger); - var reTimestampedSignature = await SignedArchiveTestUtility.TimestampSignature(timestampProvider, signatureRequest, timestampedSignature, testLogger); + var signature = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(signatureProvider, package, signatureRequest, testLogger); + var timestampedSignature = await SignedArchiveTestUtility.TimestampPrimarySignature(timestampProvider, signatureRequest, signature, testLogger); + var reTimestampedSignature = await SignedArchiveTestUtility.TimestampPrimarySignature(timestampProvider, signatureRequest, timestampedSignature, testLogger); timestampedSignature.Timestamps.Count.Should().Be(1); reTimestampedSignature.Timestamps.Count.Should().Be(2); @@ -524,7 +524,7 @@ public async Task GetTrustResultAsync_WithUntrustedSelfSignedCertificateAndNotAl using (var test = await GetTrustResultAsyncTest.CreateAsync(settings, _untrustedTestCertificate.Cert)) { - var result = await test.Provider.GetTrustResultAsync(test.Package, test.Signature, settings, CancellationToken.None); + var result = await test.Provider.GetTrustResultAsync(test.Package, test.PrimarySignature, settings, CancellationToken.None); Assert.Equal(SignatureVerificationStatus.Untrusted, result.Trust); Assert.Equal(1, result.Issues.Count(issue => issue.Level == LogLevel.Error)); @@ -549,7 +549,7 @@ public async Task GetTrustResultAsync_WithUntrustedSelfSignedCertificateAndAllow using (var test = await GetTrustResultAsyncTest.CreateAsync(settings, _untrustedTestCertificate.Cert)) { - var result = await test.Provider.GetTrustResultAsync(test.Package, test.Signature, settings, CancellationToken.None); + var result = await test.Provider.GetTrustResultAsync(test.Package, test.PrimarySignature, settings, CancellationToken.None); Assert.Equal(SignatureVerificationStatus.Trusted, result.Trust); Assert.Equal(0, result.Issues.Count(issue => issue.Level == LogLevel.Error)); @@ -574,7 +574,7 @@ public async Task GetTrustResultAsync_WithTrustedSelfSignedCertificateAndNotAllo using (var test = await GetTrustResultAsyncTest.CreateAsync(settings, _trustedTestCert.Source.Cert)) { - var result = await test.Provider.GetTrustResultAsync(test.Package, test.Signature, settings, CancellationToken.None); + var result = await test.Provider.GetTrustResultAsync(test.Package, test.PrimarySignature, settings, CancellationToken.None); Assert.Equal(SignatureVerificationStatus.Trusted, result.Trust); Assert.Equal(0, result.Issues.Count(issue => issue.Level == LogLevel.Error)); @@ -632,17 +632,17 @@ private sealed class GetTrustResultAsyncTest : IDisposable internal SignedPackageArchive Package { get; } internal SignatureTrustAndValidityVerificationProvider Provider { get; } internal SignedPackageVerifierSettings Settings { get; } - internal Signature Signature { get; } + internal PrimarySignature PrimarySignature { get; } private GetTrustResultAsyncTest( TestDirectory directory, SignedPackageArchive package, - Signature signature, + PrimarySignature primarySignature, SignedPackageVerifierSettings settings) { _directory = directory; Package = package; - Signature = signature; + PrimarySignature = primarySignature; Settings = settings; Provider = new SignatureTrustAndValidityVerificationProvider(); } @@ -672,9 +672,9 @@ internal static async Task CreateAsync(SignedPackageVer unsignedPackageFile, certificateClone); var package = new SignedPackageArchive(signedPackageFile.OpenRead(), new MemoryStream()); - var signature = await package.GetSignatureAsync(CancellationToken.None); + var primarySignature = await package.GetPrimarySignatureAsync(CancellationToken.None); - return new GetTrustResultAsyncTest(directory, package, signature, settings); + return new GetTrustResultAsyncTest(directory, package, primarySignature, settings); } } } diff --git a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignedPackageIntegrityVerificationTests.cs b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignedPackageIntegrityVerificationTests.cs index 446f4d10c5d..19fb230c3fb 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignedPackageIntegrityVerificationTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/SignedPackageIntegrityVerificationTests.cs @@ -213,7 +213,7 @@ public async Task VerifyOnInvalidSignatureFileEntry_SignatureIsCreatedUsingZipAr using (var packageStream = nupkg.CreateAsStream()) using (var testCertificate = new X509Certificate2(_trustedTestCert.Source.Cert)) { - var signarture = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(testCertificate, packageStream); + var signarture = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(testCertificate, packageStream); using (var package = new ZipArchive(packageStream, ZipArchiveMode.Update, leaveOpen: true)) { var signatureEntry = package.CreateEntry(_specification.SignaturePath); @@ -251,7 +251,7 @@ public async Task VerifyOnInvalidSignatureFileEntry_SignatureIsCreatedUsingZipAr using (var packageStream = nupkg.CreateAsStream()) using (var testCertificate = new X509Certificate2(_trustedTestCert.Source.Cert)) { - var signarture = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(testCertificate, packageStream); + var signarture = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(testCertificate, packageStream); using (var package = new ZipArchive(packageStream, ZipArchiveMode.Update, leaveOpen: true)) { var signatureEntry = package.CreateEntry(_specification.SignaturePath); @@ -277,7 +277,7 @@ public async Task VerifyOnInvalidSignatureFileEntry_SignatureIsCreatedUsingZipAr using (var packageStream = nupkg.CreateAsStream()) using (var testCertificate = new X509Certificate2(_trustedTestCert.Source.Cert)) { - var signarture = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(testCertificate, packageStream); + var signarture = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(testCertificate, packageStream); using (var package = new ZipArchive(packageStream, ZipArchiveMode.Update, leaveOpen: true)) { var signatureEntry = package.CreateEntry(_specification.SignaturePath, CompressionLevel.Optimal); @@ -315,7 +315,7 @@ public async Task VerifyOnInvalidSignatureFileEntry_SignatureIsCreatedUsingZipAr using (var packageStream = nupkg.CreateAsStream()) using (var testCertificate = new X509Certificate2(_trustedTestCert.Source.Cert)) { - var signarture = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(testCertificate, packageStream); + var signarture = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(testCertificate, packageStream); using (var package = new ZipArchive(packageStream, ZipArchiveMode.Update, leaveOpen: true)) { var signatureEntry = package.CreateEntry(_specification.SignaturePath, CompressionLevel.Optimal); diff --git a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/TimestampProviderTests.cs b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/TimestampProviderTests.cs index 2023fc7e2cf..a474417359c 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/TimestampProviderTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Packaging.FuncTest/SigningTests/TimestampProviderTests.cs @@ -85,7 +85,7 @@ public async Task TimestampData_AssertCompleteChain_Success() using (var packageStream = nupkg.CreateAsStream()) { // Act - var signature = await SignedArchiveTestUtility.CreateSignatureForPackageAsync(authorCert, packageStream, timestampProvider); + var signature = await SignedArchiveTestUtility.CreatePrimarySignatureForPackageAsync(authorCert, packageStream, timestampProvider); var authorSignedCms = signature.SignedCms; var timestamp = signature.Timestamps.First(); var timestampCms = timestamp.SignedCms; diff --git a/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureTests.cs b/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/PrimarySignatureTests.cs similarity index 90% rename from test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureTests.cs rename to test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/PrimarySignatureTests.cs index d682ae56b94..854be5c20fc 100644 --- a/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/PrimarySignatureTests.cs @@ -11,11 +11,11 @@ namespace NuGet.Packaging.Test { - public class SignatureTests : IClassFixture + public class PrimarySignatureTests : IClassFixture { private readonly CertificatesFixture _fixture; - public SignatureTests(CertificatesFixture fixture) + public PrimarySignatureTests(CertificatesFixture fixture) { if (fixture == null) { @@ -29,7 +29,7 @@ public SignatureTests(CertificatesFixture fixture) public void Load_WhenDataNull_Throws() { var exception = Assert.Throws( - () => Signature.Load(data: null)); + () => PrimarySignature.Load(data: null)); Assert.Equal("data", exception.ParamName); } @@ -53,7 +53,7 @@ public void Load_WithMultiplePrimarySignatures_Throws() signedCms.ComputeSignature(cmsSigner); var exception = Assert.Throws( - () => Signature.Load(signedCms.Encode())); + () => PrimarySignature.Load(signedCms.Encode())); Assert.Equal(NuGetLogCode.NU3009, exception.Code); Assert.Equal("The package signature contains multiple primary signatures.", exception.Message); @@ -72,7 +72,7 @@ public void Load_WithAuthorSignatureWithoutSigningCertificateV2Attribute_Throws( test.SignedCms.ComputeSignature(test.CmsSigner); var exception = Assert.Throws( - () => Signature.Load(test.SignedCms.Encode())); + () => PrimarySignature.Load(test.SignedCms.Encode())); Assert.Equal(NuGetLogCode.NU3011, exception.Code); Assert.Equal("Exactly one signing-certificate-v2 attribute is required.", exception.Message); @@ -97,7 +97,7 @@ public void Load_WithAuthorSignatureWithMultipleSigningCertificateV2AttributeVal test.SignedCms.ComputeSignature(test.CmsSigner); var exception = Assert.Throws( - () => Signature.Load(test.SignedCms.Encode())); + () => PrimarySignature.Load(test.SignedCms.Encode())); Assert.Equal(NuGetLogCode.NU3011, exception.Code); Assert.Equal("The signing-certificate-v2 attribute must have exactly one attribute value.", exception.Message); @@ -117,7 +117,7 @@ public void Load_WithAuthorSignatureWithoutPkcs9SigningTimeAttribute_Throws() test.SignedCms.ComputeSignature(test.CmsSigner); var exception = Assert.Throws( - () => Signature.Load(test.SignedCms.Encode())); + () => PrimarySignature.Load(test.SignedCms.Encode())); Assert.Equal(NuGetLogCode.NU3011, exception.Code); Assert.Equal("The primary signature is invalid.", exception.Message); @@ -137,7 +137,7 @@ public void Load_WithAuthorSignature_ReturnsSignature() test.SignedCms.ComputeSignature(test.CmsSigner); - var signature = Signature.Load(test.SignedCms.Encode()); + var signature = PrimarySignature.Load(test.SignedCms.Encode()); Assert.Equal(SignatureType.Author, signature.Type); } @@ -150,7 +150,7 @@ public void Load_WithGenericSignature_ReturnsSignatureWithUnknownType() { test.SignedCms.ComputeSignature(test.CmsSigner); - var signature = Signature.Load(test.SignedCms.Encode()); + var signature = PrimarySignature.Load(test.SignedCms.Encode()); Assert.Equal(SignatureType.Unknown, signature.Type); } diff --git a/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureUtilityTests.cs index 14d05e4d392..0e0233a3db4 100644 --- a/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Packaging.Test/SigningTests/SignatureUtilityTests.cs @@ -41,7 +41,7 @@ public void GetPrimarySignatureCertificates_WhenSignatureNull_Throws() [Fact] public void GetPrimarySignatureCertificates_WithAuthorSignature_ReturnsCertificates() { - var signature = Signature.Load(SignTestUtility.GetResourceBytes("SignatureWithTimestamp.p7s")); + var signature = PrimarySignature.Load(SignTestUtility.GetResourceBytes("SignatureWithTimestamp.p7s")); var certificates = SignatureUtility.GetPrimarySignatureCertificates(signature); @@ -66,7 +66,7 @@ public async Task GetPrimarySignatureCertificates_WithUnknownSignature_ReturnsCe using (var packageReader = new PackageArchiveReader(signedPackageFile.FullName)) { - var signature = await packageReader.GetSignatureAsync(CancellationToken.None); + var signature = await packageReader.GetPrimarySignatureAsync(CancellationToken.None); var certificates = SignatureUtility.GetPrimarySignatureCertificates(signature); @@ -77,20 +77,20 @@ public async Task GetPrimarySignatureCertificates_WithUnknownSignature_ReturnsCe } [Fact] - public void GetPrimarySignatureTimestampSignatureCertificates_WhenSignatureNull_Throws() + public void GetPrimarySignatureTimestampCertificates_WhenSignatureNull_Throws() { var exception = Assert.Throws( - () => SignatureUtility.GetPrimarySignatureTimestampSignatureCertificates(signature: null)); + () => SignatureUtility.GetPrimarySignatureTimestampCertificates(signature: null)); Assert.Equal("signature", exception.ParamName); } [Fact] - public void GetPrimarySignatureTimestampSignatureCertificates_WithValidTimestamp_ReturnsCertificates() + public void GetPrimarySignatureTimestampCertificates_WithValidTimestamp_ReturnsCertificates() { - var signature = Signature.Load(SignTestUtility.GetResourceBytes("SignatureWithTimestamp.p7s")); + var signature = PrimarySignature.Load(SignTestUtility.GetResourceBytes("SignatureWithTimestamp.p7s")); - var certificates = SignatureUtility.GetPrimarySignatureTimestampSignatureCertificates(signature); + var certificates = SignatureUtility.GetPrimarySignatureTimestampCertificates(signature); Assert.Equal(3, certificates.Count); Assert.Equal("ff162bef155cb3d5b5962bbe084b21fc4d740001", certificates[0].Thumbprint, StringComparer.OrdinalIgnoreCase); @@ -98,7 +98,7 @@ public void GetPrimarySignatureTimestampSignatureCertificates_WithValidTimestamp Assert.Equal("3b1efd3a66ea28b16697394703a72ca340a05bd5", certificates[2].Thumbprint, StringComparer.OrdinalIgnoreCase); } - private static Signature GenerateSignatureWithNoCertificates(Signature signature) + private static PrimarySignature GeneratePrimarySignatureWithNoCertificates(PrimarySignature signature) { var certificateStore = X509StoreFactory.Create( "Certificate/Collection", @@ -118,7 +118,7 @@ private static Signature GenerateSignatureWithNoCertificates(Signature signature certificateStore, writeStream); - return Signature.Load(writeStream.ToArray()); + return PrimarySignature.Load(writeStream.ToArray()); } } } diff --git a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/DownloadResourceResultTests.cs b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/DownloadResourceResultTests.cs index d1ccb24688d..3440ea2f455 100644 --- a/test/NuGet.Core.Tests/NuGet.Protocol.Tests/DownloadResourceResultTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Protocol.Tests/DownloadResourceResultTests.cs @@ -243,9 +243,9 @@ public override IEnumerable GetFiles(string folder) throw new NotImplementedException(); } - public override Task GetSignatureAsync(CancellationToken token) + public override Task GetPrimarySignatureAsync(CancellationToken token) { - return Task.FromResult(null); + return Task.FromResult(null); } public override Stream GetStream(string path) diff --git a/test/TestUtilities/Test.Utility/Signing/SignedArchiveTestUtility.cs b/test/TestUtilities/Test.Utility/Signing/SignedArchiveTestUtility.cs index 17c3b77e0d5..41f62722574 100644 --- a/test/TestUtilities/Test.Utility/Signing/SignedArchiveTestUtility.cs +++ b/test/TestUtilities/Test.Utility/Signing/SignedArchiveTestUtility.cs @@ -110,7 +110,7 @@ public static async Task CreateSignedAndTimeStampedPackageAsync( /// Package stream for which the signature has to be generated. /// An optional timestamp provider. /// Signature for the package. - public static async Task CreateSignatureForPackageAsync( + public static async Task CreatePrimarySignatureForPackageAsync( X509Certificate2 testCert, Stream packageStream, ITimestampProvider timestampProvider = null) @@ -126,7 +126,7 @@ public static async Task CreateSignatureForPackageAsync( var signatureContent = new SignatureContent(SigningSpecifications.V1, hashAlgorithm, base64ZipArchiveHash); var testSignatureProvider = new X509SignatureProvider(timestampProvider); - return await testSignatureProvider.CreateSignatureAsync(request, signatureContent, testLogger, CancellationToken.None); + return await testSignatureProvider.CreatePrimarySignatureAsync(request, signatureContent, testLogger, CancellationToken.None); } } @@ -176,13 +176,13 @@ public static async Task VerifySignatureAsync(SignedPack /// SignPackageRequest containing the metadata for the signature request. /// ILogger. /// Signature for the package. - public static async Task CreateSignatureForPackageAsync(ISignatureProvider signatureProvider, PackageArchiveReader package, SignPackageRequest request, TestLogger testLogger) + public static async Task CreatePrimarySignatureForPackageAsync(ISignatureProvider signatureProvider, PackageArchiveReader package, SignPackageRequest request, TestLogger testLogger) { var zipArchiveHash = await package.GetArchiveHashAsync(request.SignatureHashAlgorithm, CancellationToken.None); var base64ZipArchiveHash = Convert.ToBase64String(zipArchiveHash); var signatureContent = new SignatureContent(SigningSpecifications.V1, request.SignatureHashAlgorithm, base64ZipArchiveHash); - return await signatureProvider.CreateSignatureAsync(request, signatureContent, testLogger, CancellationToken.None); + return await signatureProvider.CreatePrimarySignatureAsync(request, signatureContent, testLogger, CancellationToken.None); } #if IS_DESKTOP @@ -246,7 +246,7 @@ public static async Task SignPackageFileWithBasicSignedCmsAsync( /// Signature that needs to be timestamped. /// ILogger. /// Timestamped Signature. - public static Task TimestampSignature(ITimestampProvider timestampProvider, SignPackageRequest signatureRequest, Signature signature, ILogger logger) + public static Task TimestampPrimarySignature(ITimestampProvider timestampProvider, SignPackageRequest signatureRequest, PrimarySignature signature, ILogger logger) { var timestampRequest = new TimestampRequest { @@ -255,7 +255,7 @@ public static Task TimestampSignature(ITimestampProvider timestampPro TimestampHashAlgorithm = signatureRequest.TimestampHashAlgorithm }; - return TimestampSignature(timestampProvider, timestampRequest, signature, logger); + return TimestampPrimarySignature(timestampProvider, timestampRequest, signature, logger); } /// @@ -266,9 +266,9 @@ public static Task TimestampSignature(ITimestampProvider timestampPro /// ILogger. /// timestampRequest containing metadata for timestamp request. /// Timestamped Signature. - public static Task TimestampSignature(ITimestampProvider timestampProvider, TimestampRequest timestampRequest, Signature signature, ILogger logger) + public static Task TimestampPrimarySignature(ITimestampProvider timestampProvider, TimestampRequest timestampRequest, PrimarySignature signature, ILogger logger) { - return timestampProvider.TimestampSignatureAsync(timestampRequest, logger, CancellationToken.None); + return timestampProvider.TimestampPrimarySignatureAsync(timestampRequest, logger, CancellationToken.None); } /// @@ -324,7 +324,7 @@ public static void TamperWithPackage(string signedPackagePath) #if IS_DESKTOP - public static Signature GenerateInvalidSignature(Signature signature) + public static PrimarySignature GenerateInvalidPrimarySignature(PrimarySignature signature) { var hash = Encoding.UTF8.GetBytes(signature.SignatureContent.HashValue); var newHash = Encoding.UTF8.GetBytes(new string('0', hash.Length)); @@ -332,7 +332,7 @@ public static Signature GenerateInvalidSignature(Signature signature) var bytes = signature.SignedCms.Encode(); var newBytes = FindAndReplaceSequence(bytes, hash, newHash); - return Signature.Load(newBytes); + return PrimarySignature.Load(newBytes); } #endif