license switch agpl -> busl1, sprints work, new product advisories

This commit is contained in:
master
2026-01-20 15:32:20 +02:00
parent 4903395618
commit c32fff8f86
1835 changed files with 38630 additions and 4359 deletions

View File

@@ -19,7 +19,11 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
public TimeTokenFormat Format => TimeTokenFormat.Rfc3161;
public TimeAnchorValidationResult Verify(ReadOnlySpan<byte> tokenBytes, IReadOnlyList<TimeTrustRoot> trustRoots, out TimeAnchor anchor)
public TimeAnchorValidationResult Verify(
ReadOnlySpan<byte> tokenBytes,
IReadOnlyList<TimeTrustRoot> trustRoots,
out TimeAnchor anchor,
TimeTokenVerificationOptions? options = null)
{
anchor = TimeAnchor.Unknown;
@@ -66,13 +70,6 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
return TimeAnchorValidationResult.Failure("rfc3161-no-signer-certificate");
}
// Validate signer certificate against trust roots
var validRoot = ValidateAgainstTrustRoots(signerCert, trustRoots);
if (validRoot is null)
{
return TimeAnchorValidationResult.Failure("rfc3161-certificate-not-trusted");
}
// Extract signing time from the TSTInfo or signed attributes
var signingTime = ExtractSigningTime(signedCms, signerInfo);
if (signingTime is null)
@@ -80,6 +77,27 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
return TimeAnchorValidationResult.Failure("rfc3161-no-signing-time");
}
// Validate signer certificate against trust roots
var extraCertificates = BuildExtraCertificates(signedCms, options);
var verificationTime = options?.VerificationTime ?? signingTime.Value;
var validRoot = ValidateAgainstTrustRoots(
signerCert,
trustRoots,
extraCertificates,
verificationTime);
if (validRoot is null)
{
return TimeAnchorValidationResult.Failure("rfc3161-certificate-not-trusted");
}
if (options?.Offline == true)
{
if (!TryVerifyOfflineRevocation(options, out var revocationReason))
{
return TimeAnchorValidationResult.Failure(revocationReason);
}
}
// Compute certificate fingerprint
var certFingerprint = Convert.ToHexString(SHA256.HashData(signerCert.RawData)).ToLowerInvariant()[..16];
@@ -102,7 +120,11 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
}
}
private static TimeTrustRoot? ValidateAgainstTrustRoots(X509Certificate2 signerCert, IReadOnlyList<TimeTrustRoot> trustRoots)
private static TimeTrustRoot? ValidateAgainstTrustRoots(
X509Certificate2 signerCert,
IReadOnlyList<TimeTrustRoot> trustRoots,
IReadOnlyList<X509Certificate2> extraCertificates,
DateTimeOffset verificationTime)
{
foreach (var root in trustRoots)
{
@@ -122,6 +144,15 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
chain.ChainPolicy.CustomTrustStore.Add(rootCert);
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // Offline mode
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = verificationTime.UtcDateTime;
foreach (var cert in extraCertificates)
{
if (!string.Equals(cert.Thumbprint, rootCert.Thumbprint, StringComparison.OrdinalIgnoreCase))
{
chain.ChainPolicy.ExtraStore.Add(cert);
}
}
if (chain.Build(signerCert))
{
@@ -138,6 +169,86 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
return null;
}
private static IReadOnlyList<X509Certificate2> BuildExtraCertificates(
SignedCms signedCms,
TimeTokenVerificationOptions? options)
{
var extra = new List<X509Certificate2>();
if (options?.CertificateChain is { Count: > 0 })
{
extra.AddRange(options.CertificateChain);
}
foreach (var cert in signedCms.Certificates.Cast<X509Certificate2>())
{
if (!extra.Any(existing =>
existing.Thumbprint.Equals(cert.Thumbprint, StringComparison.OrdinalIgnoreCase)))
{
extra.Add(cert);
}
}
return extra;
}
private static bool TryVerifyOfflineRevocation(
TimeTokenVerificationOptions options,
out string reason)
{
var hasOcsp = options.OcspResponses.Count > 0;
var hasCrl = options.Crls.Count > 0;
if (!hasOcsp && !hasCrl)
{
reason = "rfc3161-revocation-missing";
return false;
}
if (hasOcsp && options.OcspResponses.Any(IsOcspSuccess))
{
reason = "rfc3161-revocation-ocsp";
return true;
}
if (hasCrl && options.Crls.Any(IsCrlParseable))
{
reason = "rfc3161-revocation-crl";
return true;
}
reason = "rfc3161-revocation-invalid";
return false;
}
private static bool IsOcspSuccess(byte[] response)
{
try
{
var reader = new AsnReader(response, AsnEncodingRules.DER);
var sequence = reader.ReadSequence();
var status = sequence.ReadEnumeratedValue<OcspResponseStatus>();
return status == OcspResponseStatus.Successful;
}
catch
{
return false;
}
}
private static bool IsCrlParseable(byte[] crl)
{
try
{
var reader = new AsnReader(crl, AsnEncodingRules.DER);
reader.ReadSequence();
return true;
}
catch
{
return false;
}
}
private static DateTimeOffset? ExtractSigningTime(SignedCms signedCms, SignerInfo signerInfo)
{
// Try to get signing time from signed attributes
@@ -215,4 +326,14 @@ public sealed class Rfc3161Verifier : ITimeTokenVerifier
return null;
}
}
private enum OcspResponseStatus
{
Successful = 0,
MalformedRequest = 1,
InternalError = 2,
TryLater = 3,
SigRequired = 5,
Unauthorized = 6
}
}