Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 7246fc2

Browse files
authored
[Repository Signing] Strip repository signatures that fail verification (#441)
Previously, the Process Signature job only stripped repository signatures if its service index URL or certificate were unexpected. Thus, invalid repository signatures wouldn't be stripped, and the package would fail validation. This change strips repository signatures that fail verification. Addresses https://github.com/NuGet/Engineering/issues/1326 Addresses https://github.com/NuGet/Engineering/issues/1401
1 parent ec58a5b commit 7246fc2

11 files changed

Lines changed: 1310 additions & 306 deletions

File tree

src/Validation.Common.Job/Validation.Common.Job.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
<Version>1.1.2</Version>
8989
</PackageReference>
9090
<PackageReference Include="NuGet.Packaging">
91-
<Version>4.8.0-preview1.5179</Version>
91+
<Version>4.8.0-preview1.5195</Version>
9292
</PackageReference>
9393
<PackageReference Include="NuGet.Services.Configuration">
9494
<Version>2.23.0</Version>

src/Validation.Common.Job/Validation.Common.Job.nuspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<dependency id="Microsoft.ApplicationInsights" version="2.2.0" />
1616
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.1.1" />
1717
<dependency id="Microsoft.Extensions.Options.ConfigurationExtensions" version="1.1.2" />
18-
<dependency id="NuGet.Packaging" version="4.8.0-preview1.5179" />
18+
<dependency id="NuGet.Packaging" version="4.8.0-preview1.5195" />
1919
<dependency id="NuGet.Services.Configuration" version="2.23.0" />
2020
<dependency id="NuGet.Services.Logging" version="2.23.0" />
2121
<dependency id="NuGet.Services.Storage" version="2.23.0" />

src/Validation.PackageSigning.ProcessSignature/ISignatureFormatValidator.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ Task<VerifySignaturesResult> ValidateMinimalAsync(
1919
ISignedPackageReader package,
2020
CancellationToken token);
2121

22+
/// <summary>
23+
/// Run all validations on the package's repository signature. This includes integrity and trust validations.
24+
/// </summary>
25+
/// <param name="package">The package whose repository signature should be validated.</param>
26+
/// <param name="token"></param>
27+
/// <returns>Whether the package's signature is readable.</returns>
28+
Task<SignatureVerificationStatus> VerifyRepositorySignatureAsync(
29+
ISignedPackageReader package,
30+
CancellationToken token);
31+
2232
/// <summary>
2333
/// Run all validations on the package's signature. This includes integrity and trust validations.
2434
/// </summary>

src/Validation.PackageSigning.ProcessSignature/SignatureFormatValidator.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class SignatureFormatValidator : ISignatureFormatValidator
4444
private readonly SignedPackageVerifierSettings _authorSignatureSettings;
4545
private readonly SignedPackageVerifierSettings _authorOrRepositorySignatureSettings;
4646

47+
private readonly RepositorySignatureVerifier _repositorySignatureVerifier;
48+
4749
public SignatureFormatValidator(IOptionsSnapshot<ProcessSignatureConfiguration> config)
4850
{
4951
_config = config ?? throw new ArgumentNullException(nameof(config));
@@ -87,6 +89,8 @@ public SignatureFormatValidator(IOptionsSnapshot<ProcessSignatureConfiguration>
8789
clientAllowListEntries: _authorSignatureSettings.ClientCertificateList,
8890
allowNoRepositoryCertificateList: false,
8991
repoAllowListEntries: repoAllowListEntries);
92+
93+
_repositorySignatureVerifier = new RepositorySignatureVerifier();
9094
}
9195

9296
public async Task<VerifySignaturesResult> ValidateMinimalAsync(
@@ -114,6 +118,13 @@ public async Task<VerifySignaturesResult> ValidateFullAsync(
114118
token);
115119
}
116120

121+
public async Task<SignatureVerificationStatus> VerifyRepositorySignatureAsync(
122+
ISignedPackageReader package,
123+
CancellationToken token)
124+
{
125+
return await _repositorySignatureVerifier.VerifyAsync(package, token);
126+
}
127+
117128
private static async Task<VerifySignaturesResult> VerifyAsync(
118129
ISignedPackageReader package,
119130
IEnumerable<ISignatureVerificationProvider> verificationProviders,

src/Validation.PackageSigning.ProcessSignature/SignatureValidator.cs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Diagnostics;
76
using System.IO;
87
using System.Linq;
@@ -239,7 +238,7 @@ private async Task<SignatureValidatorResult> PerformInitialValidationsAsync(Cont
239238
private async Task<SignatureValidatorResult> StripUnacceptableRepositorySignaturesAsync(Context context)
240239
{
241240
// Check if the repository signing certificates are acceptable.
242-
if (HasAllValidRepositorySignatures(context))
241+
if (await HasAllValidRepositorySignaturesAsync(context))
243242
{
244243
_logger.LogInformation(
245244
"No repository signatures needed removal from package {PackageId} {PackageVersion} for " +
@@ -348,11 +347,11 @@ private async Task<SignatureValidatorResult> StripUnacceptableRepositorySignatur
348347
return null;
349348
}
350349

351-
private bool HasAllValidRepositorySignatures(Context context)
350+
private async Task<bool> HasAllValidRepositorySignaturesAsync(Context context)
352351
{
353352
if (context.Signature.Type == SignatureType.Repository)
354353
{
355-
if (!IsValidRepositorySignature(context, (RepositoryPrimarySignature)context.Signature))
354+
if (!await IsValidRepositorySignatureAsync(context, (RepositoryPrimarySignature)context.Signature))
356355
{
357356
_logger.LogInformation(
358357
"Signed package {PackageId} {PackageVersion} for validation {ValidationId} has repository " +
@@ -370,7 +369,7 @@ private bool HasAllValidRepositorySignatures(Context context)
370369
var repositoryCounterSignature = RepositoryCountersignature.GetRepositoryCountersignature(context.Signature);
371370

372371
if (repositoryCounterSignature != null
373-
&& !IsValidRepositorySignature(context, repositoryCounterSignature))
372+
&& !await IsValidRepositorySignatureAsync(context, repositoryCounterSignature))
374373
{
375374
_logger.LogInformation(
376375
"Signed package {PackageId} {PackageVersion} for validation {ValidationId} has repository " +
@@ -400,7 +399,7 @@ private bool HasAllValidRepositorySignatures(Context context)
400399
return true;
401400
}
402401

403-
private bool IsValidRepositorySignature<T>(Context context, T signature)
402+
private async Task<bool> IsValidRepositorySignatureAsync<T>(Context context, T signature)
404403
where T : Signature, IRepositorySignature
405404
{
406405
if (signature.V3ServiceIndexUrl?.AbsoluteUri != _configuration.Value.V3ServiceIndexUrl)
@@ -431,20 +430,41 @@ private bool IsValidRepositorySignature<T>(Context context, T signature)
431430
return false;
432431
}
433432

434-
if (signature.Timestamps.Count != 1)
433+
var status = await _formatValidator.VerifyRepositorySignatureAsync(context.PackageReader, context.CancellationToken);
434+
435+
if (status == SignatureVerificationStatus.Valid)
436+
{
437+
_logger.LogInformation(
438+
"Signed package {PackageId} {PackageVersion} for validation {ValidationId} has a valid repository signature",
439+
context.Message.PackageId,
440+
context.Message.PackageVersion,
441+
context.Message.ValidationId);
442+
443+
return true;
444+
}
445+
else if (status == SignatureVerificationStatus.Suspect && await _packageSigningStateService.HasValidPackageSigningStateAsync(context.PackageKey))
446+
{
447+
_logger.LogCritical(
448+
"Detected suspect repository signature on revalidation of package {PackageId} {PackageVersion} for " +
449+
"validation {ValidationId}",
450+
context.Message.PackageId,
451+
context.Message.PackageVersion,
452+
context.Message.ValidationId);
453+
454+
throw new InvalidOperationException($"Suspect repository signature for validation id '{context.Message.ValidationId}'");
455+
}
456+
else
435457
{
436458
_logger.LogInformation(
437-
"Signed package {PackageId} {PackageVersion} for validation {ValidationId} has a repository " +
438-
"signature with an improper number of timestamps: {Count}.",
459+
"Package {PackageId} {PackageVersion} for validation {ValidationId} has an unacceptable repository " +
460+
"signature with status {Status} and will be stripped",
439461
context.Message.PackageId,
440462
context.Message.PackageVersion,
441463
context.Message.ValidationId,
442-
signature.Timestamps.Count);
464+
status);
443465

444466
return false;
445467
}
446-
447-
return true;
448468
}
449469

450470
private async Task<SignatureValidatorResult> PerformFinalValidationAsync(Context context)

src/Validation.PackageSigning.ProcessSignature/Storage/IPackageSigningStateService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ Task SetPackageSigningState(
1313
string packageId,
1414
string packageVersion,
1515
PackageSigningStatus status);
16+
17+
Task<bool> HasValidPackageSigningStateAsync(int packageKey);
1618
}
1719
}

src/Validation.PackageSigning.ProcessSignature/Storage/PackageSigningStateService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Data.Entity;
6+
using System.Linq;
67
using System.Threading.Tasks;
78
using Microsoft.Extensions.Logging;
89
using NuGet.Services.Validation;
@@ -59,5 +60,12 @@ public async Task SetPackageSigningState(
5960
});
6061
}
6162
}
63+
64+
public async Task<bool> HasValidPackageSigningStateAsync(int packageKey)
65+
{
66+
return await _validationContext.PackageSigningStates
67+
.Where(s => s.PackageKey == packageKey)
68+
.AnyAsync(s => s.SigningStatus == PackageSigningStatus.Valid);
69+
}
6270
}
6371
}

0 commit comments

Comments
 (0)