in src/NuGet.Core/NuGet.Packaging/Signing/Timestamp/Timestamp.cs [99:280]
internal SignatureVerificationStatusFlags Verify(
Signature signature,
SignedPackageVerifierSettings settings,
HashAlgorithmName fingerprintAlgorithm,
List<SignatureLog> issues)
{
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
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, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampNoCertificate, signature.FriendlyName)));
return SignatureVerificationStatusFlags.NoCertificate;
}
var timestampFlags = VerificationUtility.ValidateTimestamp(this, signature, treatIssueAsError, issues, SigningSpecifications.V1);
if (timestampFlags != SignatureVerificationStatusFlags.NoErrors)
{
return timestampFlags;
}
else
{
issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, GeneralizedTime.LocalDateTime.ToString(CultureInfo.CurrentCulture)) + Environment.NewLine));
issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture,
Strings.VerificationTimestamperCertDisplay,
signature.FriendlyName,
$"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate, fingerprintAlgorithm)}")));
SignatureVerificationStatusFlags flags = SignatureVerificationStatusFlags.NoErrors;
var certificateExtraStore = SignedCms.Certificates;
using (X509ChainHolder chainHolder = X509ChainHolder.CreateForTimestamping())
{
IX509Chain chain = chainHolder.Chain2;
// This flag should only be set for verification scenarios, not signing.
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid;
CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, DateTime.Now, CertificateType.Timestamp);
if (settings.RevocationMode == RevocationMode.Offline)
{
chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;
}
else
{
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
}
var chainBuildSucceed = CertificateChainUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList);
string x509ChainString = CertificateUtility.X509ChainToString(chain.PrivateReference, fingerprintAlgorithm);
if (!string.IsNullOrWhiteSpace(x509ChainString))
{
issues.Add(SignatureLog.DetailedLog(x509ChainString));
}
if (chainBuildSucceed)
{
return SignatureVerificationStatusFlags.NoErrors;
}
else if (chainStatusList.Length == 0)
{
return SignatureVerificationStatusFlags.UnknownBuildStatus;
}
else
{
var chainBuildingHasIssues = false;
IEnumerable<string> messages;
var timestampInvalidCertificateFlags = CertificateChainUtility.DefaultObservedStatusFlags;
if (CertificateChainUtility.TryGetStatusAndMessage(chainStatusList, timestampInvalidCertificateFlags, out messages))
{
foreach (var message in messages)
{
issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, message)));
}
flags |= SignatureVerificationStatusFlags.ChainBuildingFailure;
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.
if (CertificateChainUtility.TryGetStatusAndMessage(chainStatusList, X509ChainStatusFlags.UntrustedRoot, out messages))
{
SignatureUtility.LogAdditionalContext(chain, issues);
issues.Add(SignatureLog.Error(NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyTimestampChainBuildingIssue_UntrustedRoot, signature.FriendlyName)));
flags |= SignatureVerificationStatusFlags.UntrustedRoot;
chainBuildingHasIssues = true;
}
if (CertificateChainUtility.TryGetStatusAndMessage(chainStatusList, X509ChainStatusFlags.Revoked, out messages))
{
issues.Add(SignatureLog.Error(NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, messages.First())));
flags |= SignatureVerificationStatusFlags.CertificateRevoked;
return flags;
}
var offlineRevocationErrors = CertificateChainUtility.TryGetStatusAndMessage(chainStatusList, X509ChainStatusFlags.OfflineRevocation, out var _);
var unknownRevocationErrors = CertificateChainUtility.TryGetStatusAndMessage(chainStatusList, X509ChainStatusFlags.RevocationStatusUnknown, out var unknownRevocationStatusMessages);
if (offlineRevocationErrors || unknownRevocationErrors)
{
if (treatIssueAsError)
{
string unknownRevocationMessage = null;
if (unknownRevocationErrors)
{
unknownRevocationMessage = string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, unknownRevocationStatusMessages.First());
}
if (settings.RevocationMode == RevocationMode.Offline)
{
if (offlineRevocationErrors)
{
issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, Strings.VerifyCertTrustOfflineWhileRevocationModeOffline)));
}
if (unknownRevocationMessage != null)
{
issues.Add(SignatureLog.InformationLog(unknownRevocationMessage));
}
}
else
{
if (offlineRevocationErrors)
{
issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, Strings.VerifyCertTrustOfflineWhileRevocationModeOnline)));
}
if (unknownRevocationMessage != null)
{
issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3028, unknownRevocationMessage));
}
}
}
if (!chainBuildingHasIssues && (settings.AllowIgnoreTimestamp || settings.AllowUnknownRevocation))
{
return SignatureVerificationStatusFlags.NoErrors;
}
flags |= SignatureVerificationStatusFlags.UnknownRevocation;
chainBuildingHasIssues = true;
}
}
// Debug log any errors
issues.Add(
SignatureLog.DebugLog(
string.Format(
CultureInfo.CurrentCulture,
$"{signature.FriendlyName}'s timestamp",
Strings.VerifyError_InvalidCertificateChain,
string.Join(", ", chainStatusList.Select(x => x.Status.ToString())))));
}
return flags;
}
}