diff --git a/src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj b/src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj
index f9a9063dc..047d7f454 100644
--- a/src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj
+++ b/src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj
@@ -107,7 +107,7 @@
- 2.22.0
+ 2.23.0
diff --git a/src/Validation.Common.Job/Storage/ValidatorStateService.cs b/src/Validation.Common.Job/Storage/ValidatorStateService.cs
index 73e2b2c74..c24a2368d 100644
--- a/src/Validation.Common.Job/Storage/ValidatorStateService.cs
+++ b/src/Validation.Common.Job/Storage/ValidatorStateService.cs
@@ -9,7 +9,6 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NuGet.Services.Validation;
-using NuGet.Services.Validation.Orchestrator;
namespace NuGet.Jobs.Validation.PackageSigning.Storage
{
@@ -21,20 +20,11 @@ public class ValidatorStateService : IValidatorStateService
public ValidatorStateService(
IValidationEntitiesContext validationContext,
- IValidatorProvider validatorProvider,
string validatorName,
ILogger logger)
{
_validationContext = validationContext ?? throw new ArgumentNullException(nameof(validationContext));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
- if (validatorProvider == null)
- {
- throw new ArgumentNullException(nameof(validatorProvider));
- }
- if (!validatorProvider.IsValidator(validatorName))
- {
- throw new ArgumentException($"\"{validatorName}\" is not a proper validator alias.", nameof(validatorName));
- }
_validatorName = validatorName ?? throw new ArgumentNullException(nameof(validatorName));
}
diff --git a/src/Validation.Common.Job/Validation.Common.Job.csproj b/src/Validation.Common.Job/Validation.Common.Job.csproj
index c4d67bbe7..91a400785 100644
--- a/src/Validation.Common.Job/Validation.Common.Job.csproj
+++ b/src/Validation.Common.Job/Validation.Common.Job.csproj
@@ -88,19 +88,19 @@
1.1.2
- 4.7.0-preview1.5029
+ 4.7.0-preview4.5067
- 2.22.0
+ 2.23.0
- 2.22.0
+ 2.23.0
- 2.22.0
+ 2.23.0
- 2.22.0
+ 2.23.0
4.4.4-dev-26726
diff --git a/src/Validation.Common.Job/Validation.Common.Job.nuspec b/src/Validation.Common.Job/Validation.Common.Job.nuspec
index 90740fb95..0adae1f71 100644
--- a/src/Validation.Common.Job/Validation.Common.Job.nuspec
+++ b/src/Validation.Common.Job/Validation.Common.Job.nuspec
@@ -15,11 +15,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Validation.PackageSigning.ProcessSignature/Job.cs b/src/Validation.PackageSigning.ProcessSignature/Job.cs
index d65fd81ba..fe87a1821 100644
--- a/src/Validation.PackageSigning.ProcessSignature/Job.cs
+++ b/src/Validation.PackageSigning.ProcessSignature/Job.cs
@@ -78,10 +78,6 @@ protected override void ConfigureAutofacServices(ContainerBuilder containerBuild
(pi, ctx) => ValidatorName.PackageSigning)
.As();
- containerBuilder
- .RegisterType()
- .As();
-
containerBuilder
.RegisterType>()
.Keyed>(validateSignatureBindingKey);
diff --git a/src/Validation.PackageSigning.ProcessSignature/MinimalSignatureVerificationProvider.cs b/src/Validation.PackageSigning.ProcessSignature/MinimalSignatureVerificationProvider.cs
index 077215662..69bb527cd 100644
--- a/src/Validation.PackageSigning.ProcessSignature/MinimalSignatureVerificationProvider.cs
+++ b/src/Validation.PackageSigning.ProcessSignature/MinimalSignatureVerificationProvider.cs
@@ -21,7 +21,7 @@ public Task GetTrustResultAsync(
CancellationToken token)
{
var result = new SignedPackageVerificationResult(
- SignatureVerificationStatus.Trusted,
+ SignatureVerificationStatus.Valid,
signature,
Enumerable.Empty());
diff --git a/src/Validation.PackageSigning.ProcessSignature/PackageSignatureVerifierFactory.cs b/src/Validation.PackageSigning.ProcessSignature/PackageSignatureVerifierFactory.cs
index 645285145..48fe52726 100644
--- a/src/Validation.PackageSigning.ProcessSignature/PackageSignatureVerifierFactory.cs
+++ b/src/Validation.PackageSigning.ProcessSignature/PackageSignatureVerifierFactory.cs
@@ -23,6 +23,7 @@ public static IPackageSignatureVerifier CreateMinimal()
var settings = new SignedPackageVerifierSettings(
allowUnsigned: true,
+ allowIllegal: false,
allowUntrusted: false, // Invalid format of the signature uses this flag to determine success.
allowUntrustedSelfIssuedCertificate: true,
allowIgnoreTimestamp: true,
@@ -48,6 +49,7 @@ public static IPackageSignatureVerifier CreateFull()
var settings = new SignedPackageVerifierSettings(
allowUnsigned: false,
+ allowIllegal: false,
allowUntrusted: false,
allowUntrustedSelfIssuedCertificate: false,
allowIgnoreTimestamp: false,
diff --git a/src/Validation.PackageSigning.ProcessSignature/SignaturePartsExtractor.cs b/src/Validation.PackageSigning.ProcessSignature/SignaturePartsExtractor.cs
index 07a79e438..3eccf8bca 100644
--- a/src/Validation.PackageSigning.ProcessSignature/SignaturePartsExtractor.cs
+++ b/src/Validation.PackageSigning.ProcessSignature/SignaturePartsExtractor.cs
@@ -30,35 +30,95 @@ public SignaturePartsExtractor(
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
- public async Task ExtractAsync(int packageKey, PrimarySignature signature, CancellationToken token)
+ public async Task ExtractAsync(int packageKey, PrimarySignature primarySignature, CancellationToken cancellationToken)
{
- // Extract the certificates found in the package signatures.
- var extractedCertificates = ExtractCertificates(signature);
+ using (var context = new Context(packageKey, primarySignature, cancellationToken))
+ {
+ if (primarySignature == null)
+ {
+ throw new ArgumentNullException(nameof(primarySignature));
+ }
- // Prepare signature entities for the database (does not commit).
- await SaveSignatureToDatabaseAsync(packageKey, signature, extractedCertificates);
+ // Extract the certificates found in the package signatures.
+ ExtractSignaturesAndCertificates(context);
- // Save the certificates to blob storage.
- await SaveCertificatesToStoreAsync(extractedCertificates, token);
+ // Prepare signature and certificate entities for the database but don't commit.
+ await PrepareSignaturesAndCertificatesRecordsAsync(context);
- // Commit the database changes.
- await _entitiesContext.SaveChangesAsync();
+ // Save the certificates to blob storage.
+ await SaveCertificatesToStoreAsync(context);
+
+ // Commit the database changes.
+ await _entitiesContext.SaveChangesAsync();
+ }
}
- private ExtractedCertificates ExtractCertificates(PrimarySignature signature)
+ private static void ExtractSignaturesAndCertificates(Context context)
{
- if (signature.Timestamps.Count != 1)
+ if (context.PrimarySignature.Timestamps.Count != 1)
{
- throw new ArgumentException("There should be exactly one timestamp.", nameof(signature));
+ throw new InvalidOperationException("There should be exactly one timestamp on the primary signature.");
}
- var signatureCertificates = SignatureUtility
- .GetPrimarySignatureCertificates(signature);
+ var primarySignatureCertificates = ExtractPrimarySignatureCertificates(context);
+
+ if (context.PrimarySignature.Type == SignatureType.Author)
+ {
+ context.Author = new SignatureAndCertificates(context.PrimarySignature, primarySignatureCertificates);
+
+ var repositoryCountersignature = RepositoryCountersignature.GetRepositoryCountersignature(context.PrimarySignature);
+ if (repositoryCountersignature != null)
+ {
+ if (repositoryCountersignature.Timestamps.Count != 1)
+ {
+ throw new InvalidOperationException("There should be exactly one timestamp on the repository countersignature.");
+ }
+
+ var countersignatureCertificates = ExtractRepositoryCountersignatureCertificates(context, repositoryCountersignature);
+ context.Repository = new SignatureAndCertificates(repositoryCountersignature, countersignatureCertificates);
+ }
+ }
+ else if (context.PrimarySignature.Type == SignatureType.Repository)
+ {
+ context.Repository = new SignatureAndCertificates(context.PrimarySignature, primarySignatureCertificates);
+ }
+ else
+ {
+ throw new InvalidOperationException("The primary signature must be an author or repository signature.");
+ }
+ }
+
+ private static ExtractedCertificates ExtractPrimarySignatureCertificates(Context context)
+ {
+ return ExtractCertificates(context, repositoryCountersignature: null);
+ }
+
+ private static ExtractedCertificates ExtractRepositoryCountersignatureCertificates(
+ Context context,
+ RepositoryCountersignature repositoryCountersignature)
+ {
+ return ExtractCertificates(context, repositoryCountersignature);
+ }
+
+ private static ExtractedCertificates ExtractCertificates(
+ Context context,
+ RepositoryCountersignature repositoryCountersignature)
+ {
+ IX509CertificateChain signatureCertificates;
+ if (repositoryCountersignature == null)
+ {
+ signatureCertificates = SignatureUtility.GetCertificateChain(context.PrimarySignature);
+ }
+ else
+ {
+ signatureCertificates = SignatureUtility.GetCertificateChain(context.PrimarySignature, repositoryCountersignature);
+ }
+
+ context.Disposables.Add(signatureCertificates);
+
if (signatureCertificates == null || !signatureCertificates.Any())
{
- throw new ArgumentException(
- "The provided signature must have at least one primary signing certificate.",
- nameof(signature));
+ throw new InvalidOperationException("The provided signature must have at least one signing certificate.");
}
var hashedSignatureCertificates = signatureCertificates
@@ -67,13 +127,21 @@ private ExtractedCertificates ExtractCertificates(PrimarySignature signature)
var signatureEndCertificate = hashedSignatureCertificates.First();
var signatureParentCertificates = hashedSignatureCertificates.Skip(1).ToList();
- var timestampCertificates = SignatureUtility
- .GetPrimarySignatureTimestampCertificates(signature);
+ IX509CertificateChain timestampCertificates;
+ if (repositoryCountersignature == null)
+ {
+ timestampCertificates = SignatureUtility.GetTimestampCertificateChain(context.PrimarySignature);
+ }
+ else
+ {
+ timestampCertificates = SignatureUtility.GetTimestampCertificateChain(context.PrimarySignature, repositoryCountersignature);
+ }
+
+ context.Disposables.Add(timestampCertificates);
+
if (timestampCertificates == null || !timestampCertificates.Any())
{
- throw new ArgumentException(
- "The provided signature must have at least one timestamp certificate.",
- nameof(signature));
+ throw new InvalidOperationException("The provided signature must have at least one timestamp certificate.");
}
var hashedTimestampCertificates = timestampCertificates
@@ -89,22 +157,51 @@ private ExtractedCertificates ExtractCertificates(PrimarySignature signature)
timestampParentCertificates);
}
- private async Task SaveSignatureToDatabaseAsync(int packageKey, Signature signature, ExtractedCertificates extractedCertificates)
+ private async Task PrepareSignaturesAndCertificatesRecordsAsync(Context context)
{
// Initialize the end and parent certificates.
- var thumbprintToEndCertificate = await InitializeEndCertificatesAsync(
- new[]
- {
- new CertificateAndUse(extractedCertificates.SignatureEndCertificate, EndCertificateUse.CodeSigning),
- new CertificateAndUse(extractedCertificates.TimestampEndCertificate, EndCertificateUse.Timestamping),
- });
+ var endCertificatesAndUses = new List();
+ var parentCertificates = new List();
- var thumbprintToParentCertificate = await InitializeParentCertificatesAsync(
- extractedCertificates
- .SignatureParentCertificates
- .Concat(extractedCertificates.TimestampParentCertificates));
+ CollectCertificates(endCertificatesAndUses, parentCertificates, context.Author?.Certificates);
+ CollectCertificates(endCertificatesAndUses, parentCertificates, context.Repository?.Certificates);
+
+ var thumbprintToEndCertificate = await InitializeEndCertificatesAsync(endCertificatesAndUses);
+ var thumbprintToParentCertificate = await InitializeParentCertificatesAsync(parentCertificates);
// Connect the end and parent certificates.
+ ConnectCertificates(context.Author?.Certificates, thumbprintToEndCertificate, thumbprintToParentCertificate);
+ ConnectCertificates(context.Repository?.Certificates, thumbprintToEndCertificate, thumbprintToParentCertificate);
+
+ // Initialize the package signature for the author signature. If the record is already in the database,
+ // verify that nothing has changed.
+ await InitializePackageSignatureAndTrustedTimestampAsync(
+ context.PackageKey,
+ PackageSignatureType.Author,
+ context.Author,
+ thumbprintToEndCertificate,
+ allowSignatureChanges: false);
+
+ // Initialize the package signature for the repository signature. If the record is already in the database
+ // and different than the current repository signature, replace the old one with the new one.
+ await InitializePackageSignatureAndTrustedTimestampAsync(
+ context.PackageKey,
+ PackageSignatureType.Repository,
+ context.Repository,
+ thumbprintToEndCertificate,
+ allowSignatureChanges: true);
+ }
+
+ private void ConnectCertificates(
+ ExtractedCertificates extractedCertificates,
+ IReadOnlyDictionary thumbprintToEndCertificate,
+ IReadOnlyDictionary thumbprintToParentCertificate)
+ {
+ if (extractedCertificates == null)
+ {
+ return;
+ }
+
ConnectCertificates(
extractedCertificates.SignatureEndCertificate,
extractedCertificates.SignatureParentCertificates,
@@ -116,57 +213,69 @@ private async Task SaveSignatureToDatabaseAsync(int packageKey, Signature signat
extractedCertificates.TimestampParentCertificates,
thumbprintToEndCertificate,
thumbprintToParentCertificate);
+ }
+
+ private async Task InitializePackageSignatureAndTrustedTimestampAsync(
+ int packageKey,
+ PackageSignatureType type,
+ SignatureAndCertificates signatureAndCertificates,
+ IReadOnlyDictionary thumbprintToEndCertificate,
+ bool allowSignatureChanges)
+ {
+ if (signatureAndCertificates == null)
+ {
+ return;
+ }
// Initialize the package signature record.
var packageSignature = await InitializePackageSignatureAsync(
packageKey,
- extractedCertificates.SignatureEndCertificate,
- thumbprintToEndCertificate);
+ type,
+ signatureAndCertificates.Certificates.SignatureEndCertificate,
+ thumbprintToEndCertificate,
+ allowSignatureChanges);
// Initialize the trusted timestamp record.
InitializeTrustedTimestamp(
packageSignature,
- signature,
- extractedCertificates.TimestampEndCertificate,
+ signatureAndCertificates.Signature,
+ signatureAndCertificates.Certificates.TimestampEndCertificate,
thumbprintToEndCertificate);
}
- public async Task InitializePackageSignatureAsync(
+ private async Task InitializePackageSignatureAsync(
int packageKey,
+ PackageSignatureType type,
HashedCertificate signatureEndCertificate,
- IReadOnlyDictionary thumbprintToEndCertificate)
+ IReadOnlyDictionary thumbprintToEndCertificate,
+ bool replacePackageSignature)
{
var packageSignatures = await _entitiesContext
.PackageSignatures
.Include(x => x.TrustedTimestamps)
.Include(x => x.EndCertificate)
- .Where(x => x.PackageKey == packageKey)
+ .Where(x => x.PackageKey == packageKey && x.Type == type)
.ToListAsync();
if (packageSignatures.Count > 1)
{
_logger.LogError(
- "There are {Count} package signatures for package key {PackageKey}. There should be either zero or one.",
+ "There are {Count} package signatures for package key {PackageKey} and type {Type}. There should be either zero or one.",
packageSignatures.Count,
- packageKey);
+ packageKey,
+ type);
- throw new InvalidOperationException("There should never be more than one package signature per package.");
+ throw new InvalidOperationException("There should never be more than one package signature per package and signature type.");
}
PackageSignature packageSignature;
if (packageSignatures.Count == 0)
{
- packageSignature = new PackageSignature
- {
- CreatedAt = DateTime.UtcNow,
- EndCertificate = thumbprintToEndCertificate[signatureEndCertificate.Thumbprint],
- PackageKey = packageKey,
- Status = PackageSignatureStatus.Unknown,
- TrustedTimestamps = new List(),
- };
- _entitiesContext.PackageSignatures.Add(packageSignature);
-
- packageSignature.EndCertificateKey = packageSignature.EndCertificate.Key;
+ packageSignature = InitializePackageSignature(
+ packageKey,
+ type,
+ signatureEndCertificate,
+ thumbprintToEndCertificate);
}
else
{
@@ -174,21 +283,74 @@ public async Task InitializePackageSignatureAsync(
if (packageSignature.EndCertificate.Thumbprint != signatureEndCertificate.Thumbprint)
{
- _logger.LogError(
- "The signature end certificate thumbprint cannot change for package {PackageKey}. The " +
- "existing signature end certificate is {ExistingThumbprint}. The new thumprint is " +
- "{NewThumbprint}.",
- packageKey,
- packageSignature.EndCertificate.Thumbprint,
- signatureEndCertificate.Thumbprint);
-
- throw new InvalidOperationException("The thumbprint of the signature end certificate cannot change.");
+ if (replacePackageSignature)
+ {
+ _logger.LogWarning(
+ "The signature end certificate thumbprint has changed for package {PackageKey} and type " +
+ "{Type}. The previous signature end certificate is {ExistingThumbprint}. The new thumprint " +
+ "is {NewThumbprint}. The previous record with key {PackageSignatureKey} will be removed.",
+ packageKey,
+ type,
+ packageSignature.EndCertificate.Thumbprint,
+ signatureEndCertificate.Thumbprint,
+ packageSignature.Key);
+
+ // Remove the child trusted timestamps. This should be handled by cascading delete but to be
+ // explicit and to facilitate unit testing, we explicitly remove them.
+ foreach (var trustedTimestamp in packageSignature.TrustedTimestamps)
+ {
+ _entitiesContext.TrustedTimestamps.Remove(trustedTimestamp);
+ }
+
+ _entitiesContext.PackageSignatures.Remove(packageSignature);
+
+ packageSignature = InitializePackageSignature(
+ packageKey,
+ type,
+ signatureEndCertificate,
+ thumbprintToEndCertificate);
+ }
+ else
+ {
+ _logger.LogError(
+ "The signature end certificate thumbprint cannot change for package {PackageKey} and type " +
+ "{Type}. The existing signature end certificate is {ExistingThumbprint}. The new thumprint " +
+ "is {NewThumbprint}.",
+ packageKey,
+ type,
+ packageSignature.EndCertificate.Thumbprint,
+ signatureEndCertificate.Thumbprint);
+
+ throw new InvalidOperationException("The thumbprint of the signature end certificate cannot change.");
+ }
}
}
return packageSignature;
}
+ private PackageSignature InitializePackageSignature(
+ int packageKey,
+ PackageSignatureType type,
+ HashedCertificate signatureEndCertificate,
+ IReadOnlyDictionary thumbprintToEndCertificate)
+ {
+ var packageSignature = new PackageSignature
+ {
+ CreatedAt = DateTime.UtcNow,
+ EndCertificate = thumbprintToEndCertificate[signatureEndCertificate.Thumbprint],
+ PackageKey = packageKey,
+ Status = PackageSignatureStatus.Unknown,
+ Type = type,
+ TrustedTimestamps = new List(),
+ };
+
+ packageSignature.EndCertificateKey = packageSignature.EndCertificate.Key;
+ _entitiesContext.PackageSignatures.Add(packageSignature);
+
+ return packageSignature;
+ }
+
private void InitializeTrustedTimestamp(
PackageSignature packageSignature,
Signature signature,
@@ -198,8 +360,9 @@ private void InitializeTrustedTimestamp(
if (packageSignature.TrustedTimestamps.Count > 1)
{
_logger.LogError(
- "There are {Count} trusted timestamps for signature on package {PackageKey}. There should be either zero or one.",
+ "There are {Count} trusted timestamps for the {SignatureType} signature on package {PackageKey}. There should be either zero or one.",
packageSignature.TrustedTimestamps.Count,
+ signature.Type,
packageSignature.PackageKey);
throw new InvalidOperationException("There should never be more than one trusted timestamp per package signature.");
@@ -230,9 +393,10 @@ private void InitializeTrustedTimestamp(
if (trustedTimestamp.EndCertificate.Thumbprint != timestampEndCertificate.Thumbprint)
{
_logger.LogError(
- "The timestamp end certificate thumbprint cannot change for package {PackageKey}. The " +
- "existing timestamp end certificate is {ExistingThumbprint}. The new thumprint is " +
- "{NewThumbprint}.",
+ "The timestamp end certificate thumbprint cannot change for the {SignatureType} signature " +
+ "on package {PackageKey}. The existing timestamp end certificate is {ExistingThumbprint}. " +
+ "The new thumprint is {NewThumbprint}.",
+ signature.Type,
packageSignature.PackageKey,
packageSignature.EndCertificate.Thumbprint,
timestampEndCertificate.Thumbprint);
@@ -243,8 +407,9 @@ private void InitializeTrustedTimestamp(
if (trustedTimestamp.Value != value)
{
_logger.LogError(
- "The trusted timestamp value cannot change for package {PackageKey}. The existing timestamp " +
- "value is {ExistingValue}. The new value is {NewValue}.",
+ "The trusted timestamp value cannot change for the {SignatureType} signature on package " +
+ "{PackageKey}. The existing timestamp value is {ExistingValue}. The new value is {NewValue}.",
+ signature.Type,
packageSignature.PackageKey,
trustedTimestamp.Value,
value);
@@ -256,7 +421,7 @@ private void InitializeTrustedTimestamp(
private void ConnectCertificates(
HashedCertificate endCertificate,
- IReadOnlyList parentCertificates,
+ IReadOnlyCollection parentCertificates,
IReadOnlyDictionary thumbprintToEndCertificate,
IReadOnlyDictionary thumbprintToParentCertificates)
{
@@ -292,7 +457,7 @@ private void ConnectCertificates(
}
private async Task> InitializeEndCertificatesAsync(
- IEnumerable certificatesAndUses)
+ IReadOnlyCollection certificatesAndUses)
{
var thumbprints = certificatesAndUses
.Select(x => x.Certificate.Thumbprint)
@@ -375,41 +540,126 @@ private async Task> InitializePar
return thumbprintToEntity;
}
- private async Task SaveCertificatesToStoreAsync(ExtractedCertificates extractedCertificates, CancellationToken token)
+ private async Task SaveCertificatesToStoreAsync(Context context)
{
- var allCertificates = Enumerable
- .Empty()
- .Concat(new[] { extractedCertificates.SignatureEndCertificate })
- .Concat(extractedCertificates.SignatureParentCertificates)
- .Concat(new[] { extractedCertificates.TimestampEndCertificate })
- .Concat(extractedCertificates.TimestampParentCertificates);
+ var thumbprintToCertificate = new Dictionary();
+
+ CollectCertificates(thumbprintToCertificate, context.Author?.Certificates);
+ CollectCertificates(thumbprintToCertificate, context.Repository?.Certificates);
- foreach (var certificate in allCertificates)
+ foreach (var certificate in thumbprintToCertificate.Values)
{
- await SaveCertificateToStoreAsync(certificate, token);
+ if (await _certificateStore.ExistsAsync(certificate.Thumbprint, context.CancellationToken))
+ {
+ continue;
+ }
+
+ await _certificateStore.SaveAsync(certificate.Certificate, context.CancellationToken);
}
}
- private async Task SaveCertificateToStoreAsync(HashedCertificate certificate, CancellationToken token)
+ private static void CollectCertificates(
+ List endCertificatesAndUses,
+ List parentCertificates,
+ ExtractedCertificates extractedCertificates)
{
- if (await _certificateStore.ExistsAsync(certificate.Thumbprint, token))
+ if (extractedCertificates == null)
{
return;
}
- await _certificateStore.SaveAsync(certificate.Certificate, token);
+ endCertificatesAndUses.Add(new EndCertificateAndUse(extractedCertificates.SignatureEndCertificate, EndCertificateUse.CodeSigning));
+ endCertificatesAndUses.Add(new EndCertificateAndUse(extractedCertificates.TimestampEndCertificate, EndCertificateUse.Timestamping));
+
+ parentCertificates.AddRange(extractedCertificates.SignatureParentCertificates);
+ parentCertificates.AddRange(extractedCertificates.TimestampParentCertificates);
}
- private class CertificateAndUse
+ private static void CollectCertificates(
+ Dictionary thumbprintToCertificate,
+ ExtractedCertificates extractedCertificates)
{
- public CertificateAndUse(HashedCertificate hashedCertificate, EndCertificateUse endCertificateUse)
+ if (extractedCertificates == null)
{
- Certificate = hashedCertificate;
+ return;
+ }
+
+ CollectCertificate(thumbprintToCertificate, extractedCertificates.SignatureEndCertificate);
+ CollectCertificates(thumbprintToCertificate, extractedCertificates.SignatureParentCertificates);
+ CollectCertificate(thumbprintToCertificate, extractedCertificates.TimestampEndCertificate);
+ CollectCertificates(thumbprintToCertificate, extractedCertificates.TimestampParentCertificates);
+ }
+
+ private static void CollectCertificates(
+ Dictionary thumbprintToCertificate,
+ IEnumerable hashedCertificates)
+ {
+ foreach (var hashedCertificate in hashedCertificates)
+ {
+ CollectCertificate(thumbprintToCertificate, hashedCertificate);
+ }
+ }
+
+ private static void CollectCertificate(
+ Dictionary thumbprintToCertificate,
+ HashedCertificate hashedCertificate)
+ {
+ if (!thumbprintToCertificate.ContainsKey(hashedCertificate.Thumbprint))
+ {
+ thumbprintToCertificate.Add(hashedCertificate.Thumbprint, hashedCertificate);
+ }
+ }
+
+ private class EndCertificateAndUse
+ {
+ public EndCertificateAndUse(HashedCertificate hashedCertificate, EndCertificateUse endCertificateUse)
+ {
+ Certificate = hashedCertificate ?? throw new ArgumentNullException(nameof(hashedCertificate));
Use = endCertificateUse;
}
public HashedCertificate Certificate { get; }
public EndCertificateUse Use { get; }
}
+
+ private class Context : IDisposable
+ {
+ public Context(int packageKey, PrimarySignature primarySignature, CancellationToken cancellationToken)
+ {
+ PackageKey = packageKey;
+ PrimarySignature = primarySignature ?? throw new ArgumentNullException(nameof(primarySignature));
+ CancellationToken = cancellationToken;
+ Disposables = new List();
+ }
+
+ public int PackageKey { get; }
+ public PrimarySignature PrimarySignature { get; }
+ public CancellationToken CancellationToken { get; }
+
+ public List Disposables { get; }
+
+ public SignatureAndCertificates Author { get; set; }
+ public SignatureAndCertificates Repository { get; set; }
+
+ public void Dispose()
+ {
+ foreach (var disposable in Disposables)
+ {
+ disposable?.Dispose();
+ }
+ }
+ }
+
+ private class SignatureAndCertificates
+ {
+ public SignatureAndCertificates(Signature signature, ExtractedCertificates certificates)
+ {
+ Signature = signature ?? throw new ArgumentNullException(nameof(signature));
+ Certificates = certificates ?? throw new ArgumentNullException(nameof(certificates));
+ }
+
+ public Signature Signature { get; }
+ public ExtractedCertificates Certificates { get; }
+ }
}
}
diff --git a/tests/NuGet.Services.Validation.Orchestrator.Tests/PackageSigning/ValidateCertificate/PackageCertificatesValidatorFacts.cs b/tests/NuGet.Services.Validation.Orchestrator.Tests/PackageSigning/ValidateCertificate/PackageCertificatesValidatorFacts.cs
index 939c580a3..3dc06b8bf 100644
--- a/tests/NuGet.Services.Validation.Orchestrator.Tests/PackageSigning/ValidateCertificate/PackageCertificatesValidatorFacts.cs
+++ b/tests/NuGet.Services.Validation.Orchestrator.Tests/PackageSigning/ValidateCertificate/PackageCertificatesValidatorFacts.cs
@@ -1173,7 +1173,6 @@ public async Task RevokedSignaturesAreInvalidated()
public abstract class FactsBase
{
protected readonly Mock _validationContext;
- protected readonly Mock _validatorProvider;
protected readonly Mock _certificateVerifier;
protected readonly Mock _telemetryService;
protected readonly Mock> _logger;
@@ -1183,14 +1182,10 @@ public abstract class FactsBase
public FactsBase()
{
_validationContext = new Mock();
- _validatorProvider = new Mock();
_certificateVerifier = new Mock();
_telemetryService = new Mock();
_logger = new Mock>();
- _validatorProvider.Setup(vp => vp.IsValidator(It.IsAny())).Returns(true);
- _validatorProvider.Setup(vp => vp.IsProcessor(It.IsAny())).Returns(true);
-
_validationRequest = new Mock();
_validationRequest.Setup(x => x.NupkgUrl).Returns(NupkgUrl);
_validationRequest.Setup(x => x.PackageId).Returns(PackageId);
@@ -1201,7 +1196,6 @@ public FactsBase()
var validatorStateServiceLogger = new Mock>();
var validatorStateService = new ValidatorStateService(
_validationContext.Object,
- _validatorProvider.Object,
ValidatorName.PackageCertificate,
validatorStateServiceLogger.Object);
diff --git a/tests/NuGet.Services.Validation.Orchestrator.Tests/ValidatorStateServiceFacts.cs b/tests/NuGet.Services.Validation.Orchestrator.Tests/ValidatorStateServiceFacts.cs
index 241bb39fe..93b6d1a65 100644
--- a/tests/NuGet.Services.Validation.Orchestrator.Tests/ValidatorStateServiceFacts.cs
+++ b/tests/NuGet.Services.Validation.Orchestrator.Tests/ValidatorStateServiceFacts.cs
@@ -483,24 +483,9 @@ public async Task PersistsStatus()
}
}
- public class TheConstructor : FactsBase
- {
- [Fact]
- public void ThrowsWhenValidatorNameIsNotProperAlias()
- {
- const string notAValidatorAlias = "pew-pew";
-
- _validatorProvider.Setup(vp => vp.IsValidator(notAValidatorAlias)).Returns(false);
-
- var ex = Assert.Throws(() => CreateValidatorStateService(notAValidatorAlias));
- Assert.Equal("validatorName", ex.ParamName);
- }
- }
-
public abstract class FactsBase
{
protected readonly Mock _validationContext;
- protected readonly Mock _validatorProvider;
protected readonly Mock> _logger;
protected readonly Mock _validationRequest;
protected readonly ValidatorStateService _target;
@@ -508,12 +493,8 @@ public abstract class FactsBase
public FactsBase()
{
_validationContext = new Mock();
- _validatorProvider = new Mock();
_logger = new Mock>();
- _validatorProvider.Setup(vp => vp.IsValidator(It.IsAny())).Returns(true);
- _validatorProvider.Setup(vp => vp.IsProcessor(It.IsAny())).Returns(true);
-
_validationRequest = new Mock();
_validationRequest.Setup(x => x.NupkgUrl).Returns(NupkgUrl);
_validationRequest.Setup(x => x.PackageId).Returns(PackageId);
@@ -527,7 +508,6 @@ public FactsBase()
protected ValidatorStateService CreateValidatorStateService(string validatorName)
=> new ValidatorStateService(
_validationContext.Object,
- _validatorProvider.Object,
validatorName,
_logger.Object);
}
diff --git a/tests/Validation.PackageSigning.Core.Tests/Support/DbSetMockFactory.cs b/tests/Validation.PackageSigning.Core.Tests/Support/DbSetMockFactory.cs
index 20ba8dfdb..c18aa7569 100644
--- a/tests/Validation.PackageSigning.Core.Tests/Support/DbSetMockFactory.cs
+++ b/tests/Validation.PackageSigning.Core.Tests/Support/DbSetMockFactory.cs
@@ -20,6 +20,7 @@ public static IDbSet Create(params T[] sourceList) where T : class
dbSet.As>().Setup(m => m.ElementType).Returns(() => list.AsQueryable().ElementType);
dbSet.As>().Setup(m => m.GetEnumerator()).Returns(() => list.GetEnumerator());
dbSet.Setup(m => m.Add(It.IsAny())).Callback(e => list.Add(e));
+ dbSet.Setup(m => m.Remove(It.IsAny())).Callback(e => list.Remove(e));
return dbSet.Object;
}
diff --git a/tests/Validation.PackageSigning.Core.Tests/Validation.PackageSigning.Core.Tests.csproj b/tests/Validation.PackageSigning.Core.Tests/Validation.PackageSigning.Core.Tests.csproj
index c20480845..dd6ae84aa 100644
--- a/tests/Validation.PackageSigning.Core.Tests/Validation.PackageSigning.Core.Tests.csproj
+++ b/tests/Validation.PackageSigning.Core.Tests/Validation.PackageSigning.Core.Tests.csproj
@@ -73,7 +73,7 @@
4.4.0
- 4.7.0-preview1.5029
+ 4.7.0-preview4.5067
2.3.1
diff --git a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignaturePartsExtractorFacts.cs b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignaturePartsExtractorFacts.cs
index 47aae5bf4..0b4077061 100644
--- a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignaturePartsExtractorFacts.cs
+++ b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignaturePartsExtractorFacts.cs
@@ -30,35 +30,82 @@ public class SignaturePartsExtractorFacts
.Parse("2018-01-26T22:09:01.0000000Z")
.ToUniversalTime();
+ private static readonly DateTime RepoSignedLeaf1TimestampValue = DateTime
+ .Parse("2018-03-21T17:55:33.0000000Z")
+ .ToUniversalTime();
+
+ private static readonly DateTime AuthorAndRepoSignedPrimaryTimestampValue = DateTime
+ .Parse("2018-03-21T17:55:50.0000000Z")
+ .ToUniversalTime();
+
+ private static readonly DateTime AuthorAndRepoSignedCounterTimestampValue = DateTime
+ .Parse("2018-03-21T17:56:00.0000000Z")
+ .ToUniversalTime();
+
///
/// This contains the SHA-256 thumbprints of the test certificate chain as well as the time stamping
/// authoring (TSA) chain. In this case, Symantec's TSA is used.
///
private static readonly ExtractedCertificatesThumbprints Leaf1Certificates = new ExtractedCertificatesThumbprints
{
- SignatureEndCertificate = new SubjectAndThumbprint(
+ PrimarySignature = new SignatureCertificateThumprints
+ {
+ SignatureEndCertificate = new SubjectAndThumbprint(
"CN=NUGET_DO_NOT_TRUST.leaf-1.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
TestResources.Leaf1Thumbprint),
- SignatureParentCertificates = new[]
- {
- new SubjectAndThumbprint(
- "CN=NUGET_DO_NOT_TRUST.intermediate.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
- "7358e4597696b1d02e7aa2b3cf30a7cf154f2c8ff0710fd0dc3ace17e3784054"),
- new SubjectAndThumbprint(
- "CN=NUGET_DO_NOT_TRUST.root.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
- TestResources.RootThumbprint),
+ SignatureParentCertificates = new[]
+ {
+ new SubjectAndThumbprint(
+ "CN=NUGET_DO_NOT_TRUST.intermediate.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
+ "7358e4597696b1d02e7aa2b3cf30a7cf154f2c8ff0710fd0dc3ace17e3784054"),
+ new SubjectAndThumbprint(
+ "CN=NUGET_DO_NOT_TRUST.root.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
+ TestResources.RootThumbprint),
+ },
+ TimestampEndCertificate = new SubjectAndThumbprint(
+ "CN=Symantec SHA256 TimeStamping Signer - G2, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
+ TestResources.Leaf1TimestampThumbprint),
+ TimestampParentCertificates = new[]
+ {
+ new SubjectAndThumbprint(
+ "CN=Symantec SHA256 TimeStamping CA, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
+ "f3516ddcc8afc808788bd8b0e840bda2b5e23c6244252ca3000bb6c87170402a"),
+ new SubjectAndThumbprint(
+ "CN=VeriSign Universal Root Certification Authority, OU=\"(c) 2008 VeriSign, Inc. - For authorized use only\", OU=VeriSign Trust Network, O=\"VeriSign, Inc.\", C=US",
+ "2399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c"),
+ },
},
- TimestampEndCertificate = new SubjectAndThumbprint(
- "CN=Symantec SHA256 TimeStamping Signer - G2, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
- TestResources.Leaf1TimestampThumbprint),
- TimestampParentCertificates = new[]
+ };
+
+ private static readonly ExtractedCertificatesThumbprints AuthorAndRepoSignedCertificates = new ExtractedCertificatesThumbprints
+ {
+ PrimarySignature = Leaf1Certificates.PrimarySignature,
+ Countersignature = new SignatureCertificateThumprints
{
- new SubjectAndThumbprint(
- "CN=Symantec SHA256 TimeStamping CA, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
- "f3516ddcc8afc808788bd8b0e840bda2b5e23c6244252ca3000bb6c87170402a"),
- new SubjectAndThumbprint(
- "CN=VeriSign Universal Root Certification Authority, OU=\"(c) 2008 VeriSign, Inc. - For authorized use only\", OU=VeriSign Trust Network, O=\"VeriSign, Inc.\", C=US",
- "2399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c"),
+ SignatureEndCertificate = new SubjectAndThumbprint(
+ "CN=NUGET_DO_NOT_TRUST.leaf-2.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
+ TestResources.Leaf2Thumbprint),
+ SignatureParentCertificates = new[]
+ {
+ new SubjectAndThumbprint(
+ "CN=NUGET_DO_NOT_TRUST.intermediate.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
+ "7358e4597696b1d02e7aa2b3cf30a7cf154f2c8ff0710fd0dc3ace17e3784054"),
+ new SubjectAndThumbprint(
+ "CN=NUGET_DO_NOT_TRUST.root.test.test, OU=Test Organizational Unit Name, O=Test Organization Name, L=Redmond, S=WA, C=US",
+ TestResources.RootThumbprint),
+ },
+ TimestampEndCertificate = new SubjectAndThumbprint(
+ "CN=Symantec SHA256 TimeStamping Signer - G2, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
+ TestResources.Leaf1TimestampThumbprint),
+ TimestampParentCertificates = new[]
+ {
+ new SubjectAndThumbprint(
+ "CN=Symantec SHA256 TimeStamping CA, OU=Symantec Trust Network, O=Symantec Corporation, C=US",
+ "f3516ddcc8afc808788bd8b0e840bda2b5e23c6244252ca3000bb6c87170402a"),
+ new SubjectAndThumbprint(
+ "CN=VeriSign Universal Root Certification Authority, OU=\"(c) 2008 VeriSign, Inc. - For authorized use only\", OU=VeriSign Trust Network, O=\"VeriSign, Inc.\", C=US",
+ "2399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c"),
+ },
},
};
@@ -86,7 +133,7 @@ public ExtractAsync()
_certificateStore
.Setup(x => x.SaveAsync(It.IsAny(), It.IsAny()))
.Returns(Task.CompletedTask)
- .Callback((cert, _) => _savedCertificates.Add(cert));
+ .Callback((cert, _) => _savedCertificates.Add(new X509Certificate2(cert.RawData)));
_entitiesContext = new Mock();
_entitiesContext
@@ -114,7 +161,7 @@ public ExtractAsync()
}
[Fact]
- public async Task SaveSigningAndTimestampCertificates()
+ public async Task SaveSigningAndTimestampCertificatesForAuthorPrimarySignature()
{
// Arrange
var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1);
@@ -123,7 +170,35 @@ public async Task SaveSigningAndTimestampCertificates()
await _target.ExtractAsync(_packageKey, signature, _token);
// Assert
- VerifySavedCertificates(Leaf1Certificates, Leaf1TimestampValue);
+ VerifyExtractedInformation(Leaf1Certificates, Leaf1TimestampValue, PackageSignatureType.Author);
+ }
+
+ [Fact]
+ public async Task SaveSigningAndTimestampCertificatesForRepositoryPrimarySignature()
+ {
+ // Arrange
+ var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.RepoSignedPackageLeaf1);
+
+ // Act
+ await _target.ExtractAsync(_packageKey, signature, _token);
+
+ // Assert
+ VerifyExtractedInformation(Leaf1Certificates, RepoSignedLeaf1TimestampValue, PackageSignatureType.Repository);
+ }
+
+ [Fact]
+ public async Task SaveSigningAndTimestampCertificatesForAuthorAndReposignedPackage()
+ {
+ // Arrange
+ var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.AuthorAndRepoSignedPackageLeaf1);
+
+ // Act
+ await _target.ExtractAsync(_packageKey, signature, _token);
+
+ // Assert
+ VerifyStoredCertificates(AuthorAndRepoSignedCertificates);
+ VerifyPackageSignatureRecord(AuthorAndRepoSignedCertificates.PrimarySignature, AuthorAndRepoSignedPrimaryTimestampValue, PackageSignatureType.Author);
+ VerifyPackageSignatureRecord(AuthorAndRepoSignedCertificates.Countersignature, AuthorAndRepoSignedCounterTimestampValue, PackageSignatureType.Repository);
}
[Fact]
@@ -191,7 +266,7 @@ public async Task DoesNotDuplicateWhenDataAlreadyExist()
await _target.ExtractAsync(_packageKey, signature, _token);
// Assert
- VerifySavedCertificates(Leaf1Certificates, Leaf1TimestampValue);
+ VerifyExtractedInformation(Leaf1Certificates, Leaf1TimestampValue, PackageSignatureType.Author);
Assert.Equal(2, _entitiesContext.Object.EndCertificates.Count());
Assert.Equal(4, _entitiesContext.Object.ParentCertificates.Count());
Assert.Equal(4, _entitiesContext.Object.CertificateChainLinks.Count());
@@ -236,6 +311,7 @@ public async Task DoesNotDuplicateWhenSomeDataAlreadyExist()
Status = PackageSignatureStatus.Valid,
CreatedAt = new DateTime(2017, 1, 1, 8, 30, 0, DateTimeKind.Utc),
PackageKey = _packageKey,
+ Type = PackageSignatureType.Author,
TrustedTimestamps = new List(),
};
@@ -256,7 +332,7 @@ public async Task DoesNotDuplicateWhenSomeDataAlreadyExist()
await _target.ExtractAsync(_packageKey, signature, _token);
// Assert
- VerifySavedCertificates(Leaf1Certificates, Leaf1TimestampValue);
+ VerifyExtractedInformation(Leaf1Certificates, Leaf1TimestampValue, PackageSignatureType.Author);
Assert.Equal(2, _entitiesContext.Object.EndCertificates.Count());
Assert.Equal(4, _entitiesContext.Object.ParentCertificates.Count());
Assert.Equal(4, _entitiesContext.Object.CertificateChainLinks.Count());
@@ -294,21 +370,25 @@ public async Task RejectsCertificateWithMultipleUses()
Assert.Empty(_savedCertificates);
}
- [Fact]
- public async Task RejectsPackageWithMultipleSignatures()
+ [Theory]
+ [InlineData(PackageSignatureType.Author, TestResources.SignedPackageLeaf1)]
+ [InlineData(PackageSignatureType.Repository, TestResources.RepoSignedPackageLeaf1)]
+ public async Task RejectsPackageWithMultipleSignatures(PackageSignatureType type, string resourceName)
{
// Arrange
- var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1);
+ var signature = await TestResources.LoadPrimarySignatureAsync(resourceName);
var existingPackageSignature1 = new PackageSignature
{
Key = 1,
PackageKey = _packageKey,
+ Type = type,
};
var existingPackageSignature2 = new PackageSignature
{
Key = 2,
PackageKey = _packageKey,
+ Type = type,
};
_entitiesContext
@@ -318,7 +398,7 @@ public async Task RejectsPackageWithMultipleSignatures()
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _target.ExtractAsync(_packageKey, signature, _token));
- Assert.Equal("There should never be more than one package signature per package.", ex.Message);
+ Assert.Equal("There should never be more than one package signature per package and signature type.", ex.Message);
_entitiesContext.Verify(
x => x.SaveChangesAsync(),
Times.Never);
@@ -326,7 +406,7 @@ public async Task RejectsPackageWithMultipleSignatures()
}
[Fact]
- public async Task RejectsPackageWithChangedSigningCertificateThumbprint()
+ public async Task RejectsAuthorSignedPackageWithChangedSigningCertificateThumbprint()
{
// Arrange
var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1);
@@ -337,7 +417,8 @@ public async Task RejectsPackageWithChangedSigningCertificateThumbprint()
EndCertificate = new EndCertificate
{
Thumbprint = "something else",
- }
+ },
+ Type = PackageSignatureType.Author,
};
_entitiesContext
@@ -354,6 +435,53 @@ public async Task RejectsPackageWithChangedSigningCertificateThumbprint()
Assert.Empty(_savedCertificates);
}
+ [Fact]
+ public async Task AcceptsRepoSignedPackageWithChangedSigningCertificateThumbprint()
+ {
+ // Arrange
+ var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.RepoSignedPackageLeaf1);
+ var existingTrustedTimestamp = new TrustedTimestamp
+ {
+ EndCertificate = new EndCertificate
+ {
+ Thumbprint = "something else B",
+ },
+ };
+ var existingPackageSignature = new PackageSignature
+ {
+ Key = 1,
+ PackageKey = _packageKey,
+ EndCertificate = new EndCertificate
+ {
+ Thumbprint = "something else A",
+ },
+ Type = PackageSignatureType.Repository,
+ TrustedTimestamps = new List
+ {
+ existingTrustedTimestamp
+ },
+ };
+
+ _entitiesContext
+ .Setup(x => x.PackageSignatures)
+ .Returns(DbSetMockFactory.Create(existingPackageSignature));
+ _entitiesContext
+ .Setup(x => x.TrustedTimestamps)
+ .Returns(DbSetMockFactory.Create(existingTrustedTimestamp));
+
+ // Act
+ await _target.ExtractAsync(_packageKey, signature, _token);
+
+ // Assert
+ var newPackageSignature = Assert.Single(_entitiesContext.Object.PackageSignatures);
+ Assert.NotSame(existingPackageSignature, newPackageSignature);
+ Assert.Equal(TestResources.Leaf1Thumbprint, newPackageSignature.EndCertificate.Thumbprint);
+
+ var newTrustedTimestamp = Assert.Single(_entitiesContext.Object.TrustedTimestamps);
+ Assert.NotSame(existingTrustedTimestamp, newTrustedTimestamp);
+ Assert.Equal(TestResources.Leaf1TimestampThumbprint, newTrustedTimestamp.EndCertificate.Thumbprint);
+ }
+
[Fact]
public async Task RejectsPackageWithMultipleTimestamps()
{
@@ -371,7 +499,8 @@ public async Task RejectsPackageWithMultipleTimestamps()
{
new TrustedTimestamp(),
new TrustedTimestamp(),
- },
+ },
+ Type = PackageSignatureType.Author,
};
_entitiesContext
@@ -403,14 +532,15 @@ public async Task RejectsPackageWithChangedTimestampCertificateThumbprint()
},
TrustedTimestamps = new[]
{
- new TrustedTimestamp
+ new TrustedTimestamp
+ {
+ EndCertificate = new EndCertificate
{
- EndCertificate = new EndCertificate
- {
- Thumbprint = "something else",
- },
+ Thumbprint = "something else",
},
},
+ },
+ Type = PackageSignatureType.Author,
};
_entitiesContext
@@ -451,6 +581,7 @@ public async Task RejectsPackageWithChangedTimestampValue()
Value = new DateTime(2010, 1, 1, 0, 0, 0, DateTimeKind.Utc),
},
},
+ Type = PackageSignatureType.Author,
};
_entitiesContext
@@ -479,7 +610,7 @@ public async Task IgnoreExtraCertificates()
await _target.ExtractAsync(_packageKey, signature, _token);
// Assert
- VerifySavedCertificates(Leaf1Certificates, Leaf1TimestampValue);
+ VerifyExtractedInformation(Leaf1Certificates, Leaf1TimestampValue, PackageSignatureType.Author);
Assert.Equal(
Leaf1Certificates.Certificates.Count + 1,
signature.SignedCms.Certificates.Count + signature.Timestamps.Sum(x => x.SignedCms.Certificates.Count));
@@ -528,69 +659,96 @@ private void AssignIds()
}
}
- private void VerifySavedCertificates(ExtractedCertificatesThumbprints expected, DateTime timestampValue)
+ private void VerifyExtractedInformation(
+ ExtractedCertificatesThumbprints expected,
+ DateTime timestampValue,
+ PackageSignatureType signatureType)
{
// Assert the certificates saved to the store.
- Assert.Equal(expected.Certificates.Count, _savedCertificates.Count);
- for (var i = 0; i < _savedCertificates.Count; i++)
- {
- var subject = _savedCertificates[i].Subject;
- var thumbprint = _savedCertificates[i].ComputeSHA256Thumbprint();
- Assert.Equal(expected.Certificates[i].Subject, subject);
- Assert.Equal(expected.Certificates[i].Thumbprint, thumbprint);
- }
+ VerifyStoredCertificates(expected);
// Assert the certificates saved to the database.
- var signatureEndCertificate = Assert.Single(_entitiesContext
- .Object
- .EndCertificates
- .Where(x => x.Thumbprint == expected.SignatureEndCertificate.Thumbprint));
-
- Assert.Equal(EndCertificateUse.CodeSigning, signatureEndCertificate.Use);
+ var trustedTimestamp = VerifyPackageSignatureRecord(expected.PrimarySignature, timestampValue, signatureType);
Assert.Equal(
expected
+ .PrimarySignature
.SignatureParentCertificates
.Select(x => x.Thumbprint)
.OrderBy(x => x),
- signatureEndCertificate
+ trustedTimestamp
+ .PackageSignature
+ .EndCertificate
.CertificateChainLinks
.Select(x => x.ParentCertificate.Thumbprint)
.OrderBy(x => x));
- var timestampEndCertificate = Assert.Single(_entitiesContext
- .Object
- .EndCertificates
- .Where(x => x.Thumbprint == expected.TimestampEndCertificate.Thumbprint));
-
- Assert.Equal(EndCertificateUse.Timestamping, timestampEndCertificate.Use);
-
Assert.Equal(
expected
+ .PrimarySignature
.TimestampParentCertificates
.Select(x => x.Thumbprint)
.OrderBy(x => x),
- timestampEndCertificate
+ trustedTimestamp
+ .EndCertificate
.CertificateChainLinks
.Select(x => x.ParentCertificate.Thumbprint)
.OrderBy(x => x));
+ }
- _entitiesContext.Verify(x => x.SaveChangesAsync(), Times.Once);
+ private TrustedTimestamp VerifyPackageSignatureRecord(
+ SignatureCertificateThumprints expected,
+ DateTime timestampValue,
+ PackageSignatureType signatureType)
+ {
+ var signatureEndCertificate = Assert.Single(_entitiesContext
+ .Object
+ .EndCertificates
+ .Where(x => x.Thumbprint == expected.SignatureEndCertificate.Thumbprint));
+ Assert.Equal(EndCertificateUse.CodeSigning, signatureEndCertificate.Use);
+
+ var timestampEndCertificate = Assert.Single(_entitiesContext
+ .Object
+ .EndCertificates
+ .Where(x => x.Thumbprint == expected.TimestampEndCertificate.Thumbprint));
+ Assert.Equal(EndCertificateUse.Timestamping, timestampEndCertificate.Use);
- var packageSignature = Assert.Single(_entitiesContext.Object.PackageSignatures);
+ var packageSignature = Assert.Single(_entitiesContext
+ .Object
+ .PackageSignatures
+ .Where(x => x.Type == signatureType));
Assert.Equal(_packageKey, packageSignature.PackageKey);
Assert.NotEqual(default(DateTime), packageSignature.CreatedAt);
Assert.Equal(expected.SignatureEndCertificate.Thumbprint, packageSignature.EndCertificate.Thumbprint);
Assert.Same(signatureEndCertificate, packageSignature.EndCertificate);
Assert.NotNull(packageSignature.TrustedTimestamps);
- var trustedTimestamp = Assert.Single(_entitiesContext.Object.TrustedTimestamps);
+ var trustedTimestamp = Assert.Single(_entitiesContext
+ .Object
+ .TrustedTimestamps
+ .Where(x => x.PackageSignature == packageSignature));
Assert.Same(trustedTimestamp, packageSignature.TrustedTimestamps.Single());
Assert.Same(packageSignature, trustedTimestamp.PackageSignature);
Assert.Equal(expected.TimestampEndCertificate.Thumbprint, trustedTimestamp.EndCertificate.Thumbprint);
Assert.Same(timestampEndCertificate, trustedTimestamp.EndCertificate);
Assert.Equal(timestampValue, trustedTimestamp.Value);
Assert.Equal(TrustedTimestampStatus.Valid, trustedTimestamp.Status);
+
+ _entitiesContext.Verify(x => x.SaveChangesAsync(), Times.Once);
+
+ return trustedTimestamp;
+ }
+
+ private void VerifyStoredCertificates(ExtractedCertificatesThumbprints expected)
+ {
+ Assert.Equal(expected.Certificates.Count, _savedCertificates.Count);
+ for (var i = 0; i < _savedCertificates.Count; i++)
+ {
+ var subject = _savedCertificates[i].Subject;
+ var thumbprint = _savedCertificates[i].ComputeSHA256Thumbprint();
+ Assert.Equal(expected.Certificates[i].Subject, subject);
+ Assert.Equal(expected.Certificates[i].Thumbprint, thumbprint);
+ }
}
}
@@ -631,18 +789,55 @@ private static PrimarySignature AddCertificates(SignedCms destination, SignedCms
}
private class ExtractedCertificatesThumbprints
+ {
+ public SignatureCertificateThumprints PrimarySignature { get; set; }
+ public SignatureCertificateThumprints Countersignature { get; set; }
+
+ public IReadOnlyList Certificates
+ {
+ get
+ {
+ var all = Enumerable.Empty();
+
+ if (PrimarySignature != null)
+ {
+ all = all
+ .Concat(new[] { PrimarySignature.SignatureEndCertificate })
+ .Concat(PrimarySignature.SignatureParentCertificates)
+ .Concat(new[] { PrimarySignature.TimestampEndCertificate })
+ .Concat(PrimarySignature.TimestampParentCertificates);
+ }
+
+ if (Countersignature != null)
+ {
+ all = all
+ .Concat(new[] { Countersignature.SignatureEndCertificate })
+ .Concat(Countersignature.SignatureParentCertificates)
+ .Concat(new[] { Countersignature.TimestampEndCertificate })
+ .Concat(Countersignature.TimestampParentCertificates);
+ }
+
+ var thumbprints = new HashSet();
+ var output = new List();
+ foreach (var certificate in all)
+ {
+ if (thumbprints.Add(certificate.Thumbprint))
+ {
+ output.Add(certificate);
+ }
+ }
+
+ return output;
+ }
+ }
+ }
+
+ private class SignatureCertificateThumprints
{
public IReadOnlyList SignatureParentCertificates { get; set; }
public SubjectAndThumbprint SignatureEndCertificate { get; set; }
public IReadOnlyList TimestampParentCertificates { get; set; }
public SubjectAndThumbprint TimestampEndCertificate { get; set; }
- public IReadOnlyList Certificates => Enumerable
- .Empty()
- .Concat(new[] { SignatureEndCertificate })
- .Concat(SignatureParentCertificates)
- .Concat(new[] { TimestampEndCertificate })
- .Concat(TimestampParentCertificates)
- .ToList();
}
}
}
diff --git a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorFacts.cs b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorFacts.cs
index a36d22c98..8040f19d0 100644
--- a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorFacts.cs
+++ b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorFacts.cs
@@ -208,7 +208,7 @@ public async Task RejectsPackagesWithMimimalVerificationErrors()
results: new[]
{
new InvalidSignaturePackageVerificationResult(
- SignatureVerificationStatus.Invalid,
+ SignatureVerificationStatus.Illegal,
new[]
{
SignatureLog.Issue(
@@ -264,7 +264,7 @@ public async Task RejectsPackagesWithFullVerificationErrors()
results: new[]
{
new InvalidSignaturePackageVerificationResult(
- SignatureVerificationStatus.Invalid,
+ SignatureVerificationStatus.Illegal,
new[]
{
SignatureLog.Issue(
diff --git a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorIntegrationTests.cs b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorIntegrationTests.cs
index 85898500c..203d6ec6c 100644
--- a/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorIntegrationTests.cs
+++ b/tests/Validation.PackageSigning.ProcessSignature.Tests/SignatureValidatorIntegrationTests.cs
@@ -38,8 +38,10 @@ public class SignatureValidatorIntegrationTests : IDisposable
{
private readonly CertificateIntegrationTestFixture _fixture;
private readonly ITestOutputHelper _output;
- private readonly Mock _packageSigningStateService;
- private readonly Mock _signaturePartsExtractor;
+ private readonly PackageSigningStateService _packageSigningStateService;
+ private readonly Mock _certificateStore;
+ private readonly Mock _validationEntitiesContext;
+ private readonly SignaturePartsExtractor _signaturePartsExtractor;
private readonly Mock _packageFileService;
private readonly Uri _nupkgUri;
private readonly Mock> _certificates;
@@ -61,11 +63,39 @@ public SignatureValidatorIntegrationTests(CertificateIntegrationTestFixture fixt
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
_output = output ?? throw new ArgumentNullException(nameof(output));
- // These dependencies have their own dependencies on the database or blob storage, which don't have good
- // integration test infrastructure in the jobs yet. Therefore, we'll mock them for now.
- _packageSigningStateService = new Mock();
+ _validationEntitiesContext = new Mock();
+ _validationEntitiesContext
+ .Setup(x => x.PackageSigningStates)
+ .Returns(DbSetMockFactory.Create());
+ _validationEntitiesContext
+ .Setup(x => x.ParentCertificates)
+ .Returns(DbSetMockFactory.Create());
+ _validationEntitiesContext
+ .Setup(x => x.EndCertificates)
+ .Returns(DbSetMockFactory.Create());
+ _validationEntitiesContext
+ .Setup(x => x.CertificateChainLinks)
+ .Returns(DbSetMockFactory.Create());
+ _validationEntitiesContext
+ .Setup(x => x.PackageSignatures)
+ .Returns(DbSetMockFactory.Create());
+ _validationEntitiesContext
+ .Setup(x => x.TrustedTimestamps)
+ .Returns(DbSetMockFactory.Create());
- _signaturePartsExtractor = new Mock();
+ var loggerFactory = new LoggerFactory();
+ loggerFactory.AddXunit(output);
+
+ _packageSigningStateService = new PackageSigningStateService(
+ _validationEntitiesContext.Object,
+ loggerFactory.CreateLogger());
+
+ _certificateStore = new Mock();
+
+ _signaturePartsExtractor = new SignaturePartsExtractor(
+ _certificateStore.Object,
+ _validationEntitiesContext.Object,
+ loggerFactory.CreateLogger());
_packageFileService = new Mock();
_nupkgUri = new Uri("https://example-storage/TestProcessor/b777135f-1aac-4ec2-a3eb-1f64fe1880d5/nuget.versioning.4.3.0.nupkg");
@@ -98,8 +128,6 @@ public SignatureValidatorIntegrationTests(CertificateIntegrationTestFixture fixt
_telemetryClient = new Mock();
_telemetryService = new TelemetryService(_telemetryClient.Object);
- var loggerFactory = new LoggerFactory();
- loggerFactory.AddXunit(output);
_logger = new RecordingLogger(loggerFactory.CreateLogger());
// Initialize data.
@@ -113,10 +141,10 @@ public SignatureValidatorIntegrationTests(CertificateIntegrationTestFixture fixt
// Initialize the subject of testing.
_target = new SignatureValidator(
- _packageSigningStateService.Object,
+ _packageSigningStateService,
_minimalPackageSignatureVerifier,
_fullPackageSignatureVerifier,
- _signaturePartsExtractor.Object,
+ _signaturePartsExtractor,
_packageFileService.Object,
_certificates.Object,
_telemetryService,
@@ -834,13 +862,14 @@ private void AllowCertificateThumbprint(string thumbprint)
private void VerifyPackageSigningStatus(SignatureValidatorResult result, ValidationStatus validationStatus, PackageSigningStatus packageSigningStatus)
{
Assert.Equal(validationStatus, result.State);
- _packageSigningStateService.Verify(
- x => x.SetPackageSigningState(
- _packageKey,
- _message.PackageId,
- _message.PackageVersion,
- packageSigningStatus),
- Times.Once);
+ var state = _validationEntitiesContext
+ .Object
+ .PackageSigningStates
+ .Where(x => x.PackageKey == _packageKey)
+ .SingleOrDefault();
+ Assert.Equal(state.PackageId, _message.PackageId);
+ Assert.Equal(state.PackageNormalizedVersion, _message.PackageVersion);
+ Assert.Equal(state.SigningStatus, packageSigningStatus);
}
private static void VerifyNU3008(SignatureValidatorResult result)
diff --git a/tests/Validation.PackageSigning.ProcessSignature.Tests/Support/TestResources.cs b/tests/Validation.PackageSigning.ProcessSignature.Tests/Support/TestResources.cs
index c88ecb350..3cee0307e 100644
--- a/tests/Validation.PackageSigning.ProcessSignature.Tests/Support/TestResources.cs
+++ b/tests/Validation.PackageSigning.ProcessSignature.Tests/Support/TestResources.cs
@@ -10,13 +10,13 @@ namespace Validation.PackageSigning.ProcessSignature.Tests
{
public static class TestResources
{
- private const string ResourceNamespace = "Validation.PackageSigning.ProcessSignature.Tests.TestData";
- public const string SignedPackageLeaf1 = ResourceNamespace + ".TestSigned.leaf-1.1.0.0.nupkg";
- public const string SignedPackageLeaf2 = ResourceNamespace + ".TestSigned.leaf-2.2.0.0.nupkg";
- public const string UnsignedPackage = ResourceNamespace + ".TestUnsigned.1.0.0.nupkg";
- public const string Zip64Package = ResourceNamespace + ".Zip64Package.1.0.0.nupkg";
- public const string RepoSignedPackageLeaf1 = ResourceNamespace + ".TestRepoSigned.leaf-1.1.0.0.nupkg";
- public const string AuthorAndRepoSignedPackageLeaf1 = ResourceNamespace + ".TestAuthorAndRepoSigned.leaf-1.1.0.0.nupkg";
+ private const string ResourcePrefix = "Validation.PackageSigning.ProcessSignature.Tests.TestData.";
+ public const string SignedPackageLeaf1 = "TestSigned.leaf-1.1.0.0.nupkg";
+ public const string SignedPackageLeaf2 = "TestSigned.leaf-2.2.0.0.nupkg";
+ public const string UnsignedPackage = "TestUnsigned.1.0.0.nupkg";
+ public const string Zip64Package = "Zip64Package.1.0.0.nupkg";
+ public const string RepoSignedPackageLeaf1 = "TestRepoSigned.leaf-1.1.0.0.nupkg";
+ public const string AuthorAndRepoSignedPackageLeaf1 = "TestAuthorAndRepoSigned.leaf-1.1.0.0.nupkg";
///
/// This is the SHA-256 thumbprint of the root CA certificate for the signing certificate of .
@@ -45,7 +45,7 @@ public static MemoryStream GetResourceStream(string resourceName)
{
var resourceStream = typeof(TestResources)
.Assembly
- .GetManifestResourceStream(resourceName);
+ .GetManifestResourceStream(ResourcePrefix + resourceName);
if (resourceStream == null)
{
diff --git a/tests/Validation.PackageSigning.RevalidateCertificate.Tests/Validation.PackageSigning.RevalidateCertificate.Tests.csproj b/tests/Validation.PackageSigning.RevalidateCertificate.Tests/Validation.PackageSigning.RevalidateCertificate.Tests.csproj
index ecc6d20fb..5e714bfda 100644
--- a/tests/Validation.PackageSigning.RevalidateCertificate.Tests/Validation.PackageSigning.RevalidateCertificate.Tests.csproj
+++ b/tests/Validation.PackageSigning.RevalidateCertificate.Tests/Validation.PackageSigning.RevalidateCertificate.Tests.csproj
@@ -36,7 +36,6 @@
-