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

Commit 262cd16

Browse files
authored
[Process Signature] Improve validation of package registration requirements (#534)
Improve the Process Signature job for the following scenarios: 1. If a package's primary signature is a repository signature, validate that the package passes its registration's author signing certificate requirements. 2. If a package is already available, skip its registration's author signing certificate requirements. This is needed as the requirements may have changed since the package was uploaded. Addresses https://github.com/NuGet/Engineering/issues/1603 Addresses https://github.com/NuGet/Engineering/issues/1637
1 parent a7ef5ee commit 262cd16

4 files changed

Lines changed: 258 additions & 88 deletions

File tree

src/Validation.PackageSigning.ProcessSignature/SignatureValidator.cs

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,10 @@ public async Task<SignatureValidatorResult> ValidateAsync(
104104

105105
private async Task<SignatureValidatorResult> HandleUnsignedPackageAsync(Context context)
106106
{
107-
var packageRegistration = _corePackageService.FindPackageRegistrationById(context.Message.PackageId);
108-
109-
if (packageRegistration.IsSigningRequired())
107+
var validationResult = await ValidatePackageRegistrationSigningRequirementsAsync(context);
108+
if (validationResult != null)
110109
{
111-
_logger.LogWarning(
112-
"Package {PackageId} {PackageVersion} for validation {ValidationId} must be signed but is unsigned.",
113-
context.Message.PackageId,
114-
context.Message.PackageVersion,
115-
context.Message.ValidationId);
116-
117-
return await RejectAsync(context, ValidationIssue.PackageIsNotSigned);
110+
return validationResult;
118111
}
119112

120113
_logger.LogInformation(
@@ -507,33 +500,21 @@ private async Task<bool> IsValidRepositorySignatureAsync<T>(Context context, T s
507500

508501
private async Task<SignatureValidatorResult> PerformFinalValidationAsync(Context context)
509502
{
510-
var signingCertificate = context.Signature.SignerInfo.Certificate;
511-
var signingFingerprint = signingCertificate.ComputeSHA256Thumbprint();
503+
var packageRegistration = _corePackageService.FindPackageRegistrationById(context.Message.PackageId);
512504

513-
if (context.Signature.Type == SignatureType.Author)
505+
// Ensure the signature matches the package registration's signing requirements.
506+
var validationResult = await ValidatePackageRegistrationSigningRequirementsAsync(context);
507+
if (validationResult != null)
514508
{
515-
// Block packages with any unknown signing certificates.
516-
var packageRegistration = _corePackageService.FindPackageRegistrationById(context.Message.PackageId);
517-
518-
if (!packageRegistration.IsAcceptableSigningCertificate(signingFingerprint))
519-
{
520-
_logger.LogWarning(
521-
"Signed package {PackageId} {PackageVersion} is blocked for validation {ValidationId} since it has an unknown certificate fingerprint: {UnknownFingerprint}",
522-
context.Message.PackageId,
523-
context.Message.PackageVersion,
524-
context.Message.ValidationId,
525-
signingFingerprint);
526-
527-
return await RejectAsync(
528-
context,
529-
new UnauthorizedCertificateFailure(signingCertificate.Thumbprint.ToLowerInvariant()));
530-
}
509+
return validationResult;
531510
}
532-
else if (context.Signature.Type != SignatureType.Repository)
511+
512+
if (context.Signature.Type != SignatureType.Author &&
513+
context.Signature.Type != SignatureType.Repository)
533514
{
534515
_logger.LogInformation(
535516
"Signed package {PackageId} {PackageVersion} is blocked for validation {ValidationId} since it " +
536-
"is not author signed: {SignatureType}",
517+
"is neither author nor repository signed: {SignatureType}",
537518
context.Message.PackageId,
538519
context.Message.PackageVersion,
539520
context.Message.ValidationId,
@@ -560,6 +541,9 @@ private async Task<SignatureValidatorResult> PerformFinalValidationAsync(Context
560541
}
561542

562543
// Do a full verification of all signatures.
544+
var signingCertificate = context.Signature.SignerInfo.Certificate;
545+
var signingFingerprint = signingCertificate.ComputeSHA256Thumbprint();
546+
563547
var verifyResult = await _formatValidator.ValidateAllSignaturesAsync(
564548
context.PackageReader,
565549
context.HasRepositorySignature,
@@ -593,6 +577,65 @@ await _corePackageService.UpdatePackageSigningCertificateAsync(
593577
return null;
594578
}
595579

580+
private async Task<SignatureValidatorResult> ValidatePackageRegistrationSigningRequirementsAsync(Context context)
581+
{
582+
// Skip signing requirement checks if the package is already available. This is needed otherwise revalidating
583+
// a package after its owners have changed their signing requirements may fail.
584+
var package = _corePackageService.FindPackageByIdAndVersionStrict(context.Message.PackageId, context.Message.PackageVersion);
585+
586+
if (package.PackageStatusKey == PackageStatus.Available)
587+
{
588+
_logger.LogInformation(
589+
"Package {PackageId} {PackageVersion} for validation {ValidationId} is already available, " +
590+
"skipping the package registration's certificate signing requirements",
591+
context.Message.PackageId,
592+
context.Message.PackageVersion,
593+
context.Message.ValidationId);
594+
595+
return null;
596+
}
597+
598+
var packageRegistration = _corePackageService.FindPackageRegistrationById(context.Message.PackageId);
599+
600+
if (context.Signature == null || context.Signature.Type == SignatureType.Repository)
601+
{
602+
// Block unsigned packages if the registration requires a signature.
603+
if (packageRegistration.IsSigningRequired())
604+
{
605+
_logger.LogWarning(
606+
"Package {PackageId} {PackageVersion} for validation {ValidationId} must be signed but is unsigned.",
607+
context.Message.PackageId,
608+
context.Message.PackageVersion,
609+
context.Message.ValidationId);
610+
611+
return await RejectAsync(context, ValidationIssue.PackageIsNotSigned);
612+
}
613+
}
614+
615+
if (context.Signature?.Type == SignatureType.Author)
616+
{
617+
var signingCertificate = context.Signature.SignerInfo.Certificate;
618+
var signingFingerprint = signingCertificate.ComputeSHA256Thumbprint();
619+
620+
// Block packages with any unknown signing certificates.
621+
if (!packageRegistration.IsAcceptableSigningCertificate(signingFingerprint))
622+
{
623+
_logger.LogWarning(
624+
"Signed package {PackageId} {PackageVersion} is blocked for validation {ValidationId} since it has an unknown certificate fingerprint: {UnknownFingerprint}",
625+
context.Message.PackageId,
626+
context.Message.PackageVersion,
627+
context.Message.ValidationId,
628+
signingFingerprint);
629+
630+
return await RejectAsync(
631+
context,
632+
new UnauthorizedCertificateFailure(signingCertificate.Thumbprint.ToLowerInvariant()));
633+
}
634+
}
635+
636+
return null;
637+
}
638+
596639
private async Task<SignatureValidatorResult> GetVerifyResult(
597640
Context context,
598641
string verificationName,

0 commit comments

Comments
 (0)