public void RevokedCodeSigningCertificateWithoutRevocationDateInvalidatesAllSignatures() { var certificate = new EndCertificate { Use = EndCertificateUse.CodeSigning }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Revoked, statusFlags: X509ChainStatusFlags.Revoked, revocationTime: null); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid }; // Act & Assert var decider = _target.MakeDeciderForRevokedCertificate(certificate, result); Assert.Equal(SignatureDecision.Reject, decider(signatureAtIngestion)); Assert.Equal(SignatureDecision.Reject, decider(signatureAtGracePeriod)); Assert.Equal(SignatureDecision.Reject, decider(signatureAfterGracePeriod)); }
public void RevokedTimestampingCertificateInvalidatesSignatures(bool nullRevocationTime) { // Arrange - only signatures at ingestion should be rejected, all other signatures should // raise a warning. The revocation time should not matter. var revocationTime = nullRevocationTime ? (DateTime?)null : DateTime.Now; var certificate = new EndCertificate { Use = EndCertificateUse.Timestamping }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Revoked, statusFlags: X509ChainStatusFlags.Revoked, revocationTime: revocationTime); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid }; // Act & Assert var decider = _target.MakeDeciderForRevokedCertificate(certificate, result); Assert.Equal(SignatureDecision.Reject, decider(signatureAtIngestion)); Assert.Equal(SignatureDecision.Warn, decider(signatureAtGracePeriod)); Assert.Equal(SignatureDecision.Warn, decider(signatureAfterGracePeriod)); }
public async Task RejectsCertificateWithMultipleUses() { // Arrange var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1); var existingEndCertificate = new EndCertificate { Key = 1, Thumbprint = TestResources.Leaf1Thumbprint, Status = EndCertificateStatus.Good, Use = EndCertificateUse.Timestamping, CertificateChainLinks = new List <CertificateChainLink>(), }; _entitiesContext .Setup(x => x.EndCertificates) .Returns(DbSetMockFactory.Create(existingEndCertificate)); // Act & Assert var ex = await Assert.ThrowsAsync <InvalidOperationException>( () => _target.ExtractAsync(_packageKey, signature, _token)); Assert.Equal("The use of an end certificate cannot change.", ex.Message); _entitiesContext.Verify( x => x.SaveChangesAsync(), Times.Never); Assert.Empty(_savedCertificates); }
public CertificateVerificationEnqueuerFacts() { _configuration = new ValidateCertificateConfiguration(); _brokeredMessage = new Mock <IBrokeredMessage>(); _validationRequest = new Mock <IValidationRequest>(); _endCertificate = new EndCertificate { Key = 23 }; _validationRequest.Setup(x => x.ValidationId).Returns(new Guid("68fc78da-af04-4e4e-8128-de68dcfec3ba")); _brokeredMessage.SetupProperty(x => x.ScheduledEnqueueTimeUtc); _topicClient = new Mock <ITopicClient>(); _serializer = new Mock <IBrokeredMessageSerializer <CertificateValidationMessage> >(); _options = new Mock <IOptionsSnapshot <ValidateCertificateConfiguration> >(); _options.Setup(x => x.Value).Returns(() => _configuration); _serializer .Setup(x => x.Serialize(It.IsAny <CertificateValidationMessage>())) .Returns(() => _brokeredMessage.Object); _target = new ValidateCertificateEnqueuer( _topicClient.Object, _serializer.Object, _options.Object); }
/// <summary> /// Find all package signatures that depend on the given certificate. This method will return signatures in /// batches of size <see cref="MaxSignatureUpdatesPerTransaction"/>. /// </summary> /// <param name="certificate">The certificate whose signatures should be found.</param> /// <param name="page">Which page of signatures should be fetched.</param> /// <returns>The signatures that depend on the given certificate.</returns> private Task <List <PackageSignature> > FindSignaturesAsync(EndCertificate certificate, int page) { // A signature may depend on a certificate in one of two ways: the signature itself may have been signed using // the certificate, or, one of the signature's trusted timestamps may have been signed using the certificate. IQueryable <PackageSignature> packageSignatures; switch (certificate.Use) { case EndCertificateUse.CodeSigning: packageSignatures = _context.PackageSignatures .Where(s => s.Type == PackageSignatureType.Author) .Where(s => s.EndCertificate.Thumbprint == certificate.Thumbprint); break; case EndCertificateUse.Timestamping: packageSignatures = _context.PackageSignatures .Where(s => s.Type == PackageSignatureType.Author) .Where(s => s.TrustedTimestamps.Any(t => t.EndCertificate.Thumbprint == certificate.Thumbprint)); break; default: throw new InvalidOperationException($"Unknown {nameof(EndCertificateUse)}: {certificate.Use}"); } return(packageSignatures .Include(s => s.TrustedTimestamps.Select(t => t.EndCertificate)) .Include(s => s.PackageSigningState) .OrderBy(s => s.Key) .Skip(page * MaxSignatureUpdatesPerTransaction) .Take(MaxSignatureUpdatesPerTransaction) .ToListAsync()); }
public void InvalidCertificateInvalidatesSignatures( EndCertificateUse use, X509ChainStatusFlags flags, SignatureDecision ingestionDecision, SignatureDecision gracePeriodDecision, SignatureDecision afterGracePeriodDecision) { // Arrange var certificate = new EndCertificate { Use = use }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Invalid, statusFlags: flags); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid }; // Act & Assert var decider = _target.MakeDeciderForInvalidatedCertificate(certificate, result); Assert.Equal(ingestionDecision, decider(signatureAtIngestion)); Assert.Equal(gracePeriodDecision, decider(signatureAtGracePeriod)); Assert.Equal(afterGracePeriodDecision, decider(signatureAfterGracePeriod)); }
public async Task EnqueueVerificationAsync(IValidationRequest request, EndCertificate certificate) { var message = new CertificateValidationMessage(certificate.Key, request.ValidationId); var brokeredMessage = _serializer.Serialize(message); await _topicClient.SendAsync(brokeredMessage); }
/// <summary> /// The helper that processes how a certificate's status change affects its dependent signatures. /// </summary> /// <param name="certificate">The certificate whose dependent signatures should be processed.</param> /// <param name="certificateVerificationResult">The result of the certificate's verification.</param> /// <param name="signatureDecider">The delegate that decides how a dependent signature should be handled.</param> /// <param name="onAllSignaturesHandled">The action that will be called once all dependent signatures have been processed.</param> /// <returns></returns> private async Task ProcessDependentSignaturesAsync( EndCertificate certificate, CertificateVerificationResult certificateVerificationResult, SignatureDecider signatureDecider, Action onAllSignaturesHandled) { // A single certificate may be dependend on by many signatures. To ensure sanity, only up // to "MaxSignatureUpdatesPerTransaction" signatures will be invalidated at a time. List <PackageSignature> signatures = null; int page = 0; do { // If necessary, save the previous iteration's signature invalidations. if (page > 0) { _logger.LogInformation( "Persisting {Signatures} dependent signature updates for certificate {CertificateThumbprint} (page {Page})", signatures.Count, certificate.Thumbprint, page); await _context.SaveChangesAsync(); page++; } _logger.LogInformation( "Finding more dependent signatures to update for certificate {CertificateThumbprint}... (page {Page})", certificate.Thumbprint, page); signatures = await FindSignaturesAsync(certificate, page); _logger.LogInformation( "Updating {Signatures} signatures for certificate {CertificateThumbprint}... (page {Page})", signatures.Count, certificate.Thumbprint, page); foreach (var signature in signatures) { var decision = signatureDecider(signature); HandleSignatureDecision(signature, decision, certificate, certificateVerificationResult); } }while (signatures.Count == MaxSignatureUpdatesPerTransaction); // All signatures have been invalidated. Do any necessary finalizations, and persist the results. _logger.LogInformation( "Finalizing {Signatures} dependent signature updates for certificate {CertificateThumbprint} (total pages: {Pages})", signatures.Count, certificate.Thumbprint, page + 1); onAllSignaturesHandled(); await _context.SaveChangesAsync(); }
public void Dispose() { EndCertificate.Dispose(); foreach (var ancestor in AncestorCertificates) { ancestor.Dispose(); } }
public void TrackUnableToValidateCertificateEvent(EndCertificate certificate) { _telemetryClient.TrackMetric( PackageSignatureMayBeInvalidated, 1, new Dictionary <string, string> { { CertificateId, certificate.Key.ToString() }, { CertificateThumbprint, certificate.Thumbprint } }); }
public async Task EnqueueVerificationAsync(IValidationRequest request, EndCertificate certificate) { var message = new CertificateValidationMessage(certificate.Key, request.ValidationId); var brokeredMessage = _serializer.Serialize(message); var visibleAt = DateTimeOffset.UtcNow + (_configuration.Value.MessageDelay ?? TimeSpan.Zero); brokeredMessage.ScheduledEnqueueTimeUtc = visibleAt; await _topicClient.SendAsync(brokeredMessage); }
public async Task EnqueueValidationAsync(Guid validationId, EndCertificate certificate) { var message = new CertificateValidationMessage( certificate.Key, validationId, revalidateRevokedCertificate: false, sendCheckValidator: false); var brokeredMessage = _serializer.Serialize(message); await _topicClient.SendAsync(brokeredMessage); }
/// <summary> /// Decide whether or not to revalidate the given certificate. /// </summary> /// <param name="certificate">The certificate that may be revalidated.</param> /// <returns>Whether the certificate should be revalidated.</returns> private bool ShouldValidateCertificate(EndCertificate certificate) { // Validate the certificate only if it has never been validated before, or, if // its last validation time is past the maximum revalidation threshold. if (certificate.LastVerificationTime.HasValue) { var timeAgo = DateTime.UtcNow - certificate.LastVerificationTime.Value; return(timeAgo >= _certificateRevalidationThresholdTime); } return(true); }
public void RevokedCodeSigningCertificateRejectsAllSignaturesIfTimestampIsAlreadyInvalid() { var certificate = new EndCertificate { Use = EndCertificateUse.CodeSigning }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Revoked, statusFlags: X509ChainStatusFlags.Revoked, revocationTime: DateTime.UtcNow); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp { Status = TrustedTimestampStatus.Invalid } } }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp { Status = TrustedTimestampStatus.Invalid } } }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp { Status = TrustedTimestampStatus.Invalid } } }; // Act & Assert var decider = _target.MakeDeciderForRevokedCertificate(certificate, result); Assert.Equal(SignatureDecision.Reject, decider(signatureAtIngestion)); Assert.Equal(SignatureDecision.Reject, decider(signatureAtGracePeriod)); Assert.Equal(SignatureDecision.Reject, decider(signatureAfterGracePeriod)); }
/// <summary> /// Create a function to decide how signatures should be affected by a revoked certificate. /// </summary> /// <param name="certificate">The certificate that was revoked.</param> /// <param name="result">The verification result that describes when the certificate was revoked.</param> /// <returns>The function that describes how dependent signatures should be affected by the certificate status change.</returns> public SignatureDecider MakeDeciderForRevokedCertificate(EndCertificate certificate, CertificateVerificationResult result) { switch (certificate.Use) { case EndCertificateUse.Timestamping: return(RejectSignaturesAtIngestionOtherwiseWarnDecider); case EndCertificateUse.CodeSigning: return(MakeDeciderForRevokedCodeSigningCertificate(result.RevocationTime)); default: throw new InvalidOperationException($"Revoked certificate has unknown use: {certificate.Use}"); } }
public void RevokedCodeSigningCertificateThrowsIfThereIsARevocationDateAndMultipleTrustedTimestamps() { var certificate = new EndCertificate { Use = EndCertificateUse.CodeSigning }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Revoked, statusFlags: X509ChainStatusFlags.Revoked, revocationTime: DateTime.UtcNow); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp(), new TrustedTimestamp(), } }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp(), new TrustedTimestamp(), } }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid, TrustedTimestamps = new TrustedTimestamp[] { new TrustedTimestamp(), new TrustedTimestamp(), } }; // Act & Assert var decider = _target.MakeDeciderForRevokedCertificate(certificate, result); Assert.Throws <InvalidOperationException>(() => decider(signatureAtIngestion)); Assert.Throws <InvalidOperationException>(() => decider(signatureAtGracePeriod)); Assert.Throws <InvalidOperationException>(() => decider(signatureAfterGracePeriod)); }
private async Task <IReadOnlyDictionary <string, EndCertificate> > InitializeEndCertificatesAsync( IReadOnlyCollection <EndCertificateAndUse> certificatesAndUses) { var thumbprints = certificatesAndUses .Select(x => x.Certificate.Thumbprint) .Distinct() .ToList(); // Find all of the end certificate entities that intersect with the set of certificates found in the // package that is currently being processed. var existingEntities = await _entitiesContext .EndCertificates .Include(x => x.CertificateChainLinks) .Where(x => thumbprints.Contains(x.Thumbprint)) .ToListAsync(); var thumbprintToEntity = existingEntities.ToDictionary(x => x.Thumbprint); foreach (var certificateAndUse in certificatesAndUses) { if (!thumbprintToEntity.TryGetValue(certificateAndUse.Certificate.Thumbprint, out var entity)) { entity = new EndCertificate { Status = EndCertificateStatus.Unknown, Use = certificateAndUse.Use, Thumbprint = certificateAndUse.Certificate.Thumbprint, CertificateChainLinks = new List <CertificateChainLink>(), }; _entitiesContext.EndCertificates.Add(entity); thumbprintToEntity[certificateAndUse.Certificate.Thumbprint] = entity; } else if (entity.Use != certificateAndUse.Use) { _logger.LogError( "The use of end certificate {Thumbprint} cannot change. The existing use is {ExistingUse}. The new use is {NewUse}.", certificateAndUse.Certificate.Thumbprint, entity.Use, certificateAndUse.Use); throw new InvalidOperationException("The use of an end certificate cannot change."); } } return(thumbprintToEntity); }
/// <summary> /// Handle the decision on how to update the signature. /// </summary> /// <param name="signature">The signature that should be updated.</param> /// <param name="decision">How the signature should be updated.</param> /// <param name="certificate">The certificate that signature depends on that changed the signature's state.</param> /// <param name="certificateVerificationResult">The certificate verification that changed the signature's state.</param> private void HandleSignatureDecision( PackageSignature signature, SignatureDecision decision, EndCertificate certificate, CertificateVerificationResult certificateVerificationResult) { switch (decision) { case SignatureDecision.Ignore: _logger.LogInformation( "Signature {SignatureKey} is not affected by certificate verification result: {CertificateVerificationResult}", signature.Key, certificateVerificationResult); break; case SignatureDecision.Warn: _logger.LogWarning( "Invalidating signature {SignatureKey} due to certificate verification result: {CertificateVerificationResult}", signature.Key, certificateVerificationResult); InvalidateSignature(signature, certificate); _telemetryService.TrackPackageSignatureMayBeInvalidatedEvent(signature); break; case SignatureDecision.Reject: _logger.LogWarning( "Rejecting signature {SignatureKey} due to certificate verification result: {CertificateVerificationResult}", signature.Key, certificateVerificationResult); InvalidateSignature(signature, certificate); _telemetryService.TrackPackageSignatureShouldBeInvalidatedEvent(signature); break; default: throw new InvalidOperationException( $"Unknown signature decision '{decision}' for certificate verification result: {certificateVerificationResult}"); } }
public void RevokedCodeSigningCertificateWithRevocationDateInvalidatesSignatures( TimeSpan signatureTimeDeltaToRevocationTime, SignatureDecision ingestionDecision, SignatureDecision gracePeriodDecision, SignatureDecision afterGracePeriodDecision) { // Arrange - only signatures that were created after the revocation date should // be rejected. var revocationTime = DateTime.UtcNow; var certificate = new EndCertificate { Use = EndCertificateUse.CodeSigning }; var timestamp = new TrustedTimestamp { Value = revocationTime + signatureTimeDeltaToRevocationTime, Status = TrustedTimestampStatus.Valid }; var result = new CertificateVerificationResult( status: EndCertificateStatus.Revoked, statusFlags: X509ChainStatusFlags.Revoked, revocationTime: revocationTime); var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown }; var signatureAtGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid }; signatureAtIngestion.TrustedTimestamps = new[] { timestamp }; signatureAtGracePeriod.TrustedTimestamps = new[] { timestamp }; signatureAfterGracePeriod.TrustedTimestamps = new[] { timestamp }; // Act & Assert var decider = _target.MakeDeciderForRevokedCertificate(certificate, result); Assert.Equal(ingestionDecision, decider(signatureAtIngestion)); Assert.Equal(gracePeriodDecision, decider(signatureAtGracePeriod)); Assert.Equal(afterGracePeriodDecision, decider(signatureAfterGracePeriod)); }
private void InvalidateSignature(PackageSignature signature, EndCertificate certificate) { signature.Status = PackageSignatureStatus.Invalid; signature.PackageSigningState.SigningStatus = PackageSigningStatus.Invalid; if (certificate.Use == EndCertificateUse.Timestamping) { var affectedTimestamps = signature.TrustedTimestamps .Where(t => t.EndCertificate.Thumbprint == certificate.Thumbprint); foreach (var timestamp in affectedTimestamps) { _logger.LogWarning( "Invalidating timestamp {TimestampKey} due to invalid certificate {CertificateKey}", signature.Key, certificate.Key); timestamp.Status = TrustedTimestampStatus.Invalid; } } }
/// <summary> /// Create a function to decide how signatures should be affected by an invalidated certificate. /// </summary> /// <param name="certificate">The certificate that was invalidated.</param> /// <param name="result">The verification result that describes why the certificate was invalidated.</param> /// <returns>The function that describes how dependent signatures should be affected by the certificate status change.</returns> public SignatureDecider MakeDeciderForInvalidatedCertificate(EndCertificate certificate, CertificateVerificationResult result) { if (result.Status != EndCertificateStatus.Invalid) { throw new ArgumentException($"Result must have a status of {nameof(EndCertificateStatus.Invalid)}", nameof(result)); } if (result.StatusFlags == X509ChainStatusFlags.NoError) { throw new ArgumentException($"Invalid flags on invalid verification result: {result.StatusFlags}!", nameof(result)); } // If a certificate used for the primary signature is revoked, all dependent signatures should be invalidated. // NOTE: It is assumed that the revoked certificate is an ancestor certificate, but this may not be strictly true. if (certificate.Use == EndCertificateUse.CodeSigning && (result.StatusFlags & X509ChainStatusFlags.Revoked) != 0) { return(RejectAllSignaturesDecider); } // NotTimeValid and HasWeakSignature fail packages only at ingestion. It is assumed that a chain with HasWeakSignature will // ALWAYS have NotSignatureValid. else if (result.StatusFlags == X509ChainStatusFlags.NotTimeValid || result.StatusFlags == (X509ChainStatusFlags.HasWeakSignature | X509ChainStatusFlags.NotSignatureValid) || result.StatusFlags == (X509ChainStatusFlags.NotTimeValid | X509ChainStatusFlags.HasWeakSignature | X509ChainStatusFlags.NotSignatureValid)) { return(RejectSignaturesAtIngestionDecider); } // NotTimeNested does not affect signatures and should be ignored if it is the only status. else if (result.StatusFlags == X509ChainStatusFlags.NotTimeNested) { return(NoActionDecider); } // In all other cases, reject signatures at ingestion and warn on all other signatures. else { return(RejectSignaturesAtIngestionOtherwiseWarnDecider); } }
public static IEnumerable <object[]> ValidSignaturesArePromotableData() { var cert1SecondAgo = new EndCertificate { Status = EndCertificateStatus.Good, StatusUpdateTime = DateTime.UtcNow.AddSeconds(-1), }; var certRevoked1SecondAgo = new EndCertificate { Status = EndCertificateStatus.Revoked, StatusUpdateTime = DateTime.UtcNow.AddSeconds(-1), RevocationTime = DateTime.UtcNow.AddSeconds(-1), }; var cert1YearAgo = new EndCertificate { Status = EndCertificateStatus.Good, StatusUpdateTime = DateTime.UtcNow.AddYears(-1), }; // A signature whose timestamp is BEFORE the signature's and timestamps' certificates // last updates should be promotable. yield return(new object[] { true, new PackageSignature { EndCertificate = cert1SecondAgo, TrustedTimestamps = new[] { new TrustedTimestamp { Value = DateTime.UtcNow.AddDays(-1), EndCertificate = cert1SecondAgo, } }, }, }); // A signature whose timestamp is AFTER the signature's certificate last update should not // not be promotable yield return(new object[] { false, new PackageSignature { EndCertificate = cert1YearAgo, TrustedTimestamps = new[] { new TrustedTimestamp { Value = DateTime.UtcNow.AddDays(-1), EndCertificate = cert1SecondAgo, } }, }, }); // A signature whose timestamp is AFTER the timestamp's certificate last update should not // be promotable yield return(new object[] { false, new PackageSignature { EndCertificate = cert1SecondAgo, TrustedTimestamps = new[] { new TrustedTimestamp { Value = DateTime.UtcNow.AddDays(-1), EndCertificate = cert1YearAgo, } }, }, }); // The latest timestamp should be used for promotion decisions. yield return(new object[] { false, new PackageSignature { EndCertificate = cert1YearAgo, TrustedTimestamps = new[] { new TrustedTimestamp { Value = DateTime.UtcNow.AddDays(-1), EndCertificate = cert1YearAgo, }, new TrustedTimestamp { Value = DateTime.UtcNow.AddYears(-10), EndCertificate = cert1YearAgo, } }, }, }); // A signature whose signing certificate is revoked should be promoted to "Valid" as long as the revocation // time begins after the package was signed. yield return(new object[] { true, new PackageSignature { EndCertificate = certRevoked1SecondAgo, TrustedTimestamps = new[] { new TrustedTimestamp { Value = DateTime.UtcNow.AddDays(-1), EndCertificate = cert1SecondAgo, } }, }, }); }
public async Task ValidateSigningCertificate( Func <CertificateIntegrationTestFixture, Task <X509Certificate2> > createCertificateFunc, DateTime signatureTime, EndCertificateStatus expectedCertificateStatus, PackageSignatureStatus expectedStatusForSignatureAtIngestion, PackageSignatureStatus expectedStatusForSignatureInGracePeriod, PackageSignatureStatus expectedStatusForSignatureAfterGracePeriod) { // Arrange var certificate = await createCertificateFunc(_fixture); var endCertificateKey = 123; var validationId = Guid.NewGuid(); var packageSigningState1 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid }; var packageSigningState2 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid }; var packageSigningState3 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid }; var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown, Type = PackageSignatureType.Author, }; var signatureInGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod, Type = PackageSignatureType.Author, }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid, Type = PackageSignatureType.Author, }; var trustedTimestamp1 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime }; var trustedTimestamp2 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime }; var trustedTimestamp3 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime }; var endCertificate = new EndCertificate { Key = endCertificateKey, Status = EndCertificateStatus.Unknown, Use = EndCertificateUse.CodeSigning, CertificateChainLinks = new CertificateChainLink[0], }; var validation = new EndCertificateValidation { EndCertificateKey = endCertificateKey, ValidationId = validationId, Status = null, EndCertificate = endCertificate }; signatureAtIngestion.PackageSigningState = packageSigningState1; signatureAtIngestion.EndCertificate = endCertificate; signatureAtIngestion.TrustedTimestamps = new[] { trustedTimestamp1 }; signatureInGracePeriod.PackageSigningState = packageSigningState2; signatureInGracePeriod.EndCertificate = endCertificate; signatureInGracePeriod.TrustedTimestamps = new[] { trustedTimestamp2 }; signatureAfterGracePeriod.PackageSigningState = packageSigningState3; signatureAfterGracePeriod.EndCertificate = endCertificate; signatureAfterGracePeriod.TrustedTimestamps = new[] { trustedTimestamp3 }; _context.Mock( packageSignatures: new[] { signatureAtIngestion, signatureInGracePeriod, signatureAfterGracePeriod }, endCertificates: new[] { endCertificate }, certificateValidations: new EndCertificateValidation[] { validation }); _certificateStore.Setup(s => s.LoadAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(certificate)); // Act await _target.HandleAsync(new CertificateValidationMessage(certificateKey : endCertificateKey, validationId : validationId)); // Assert Assert.Equal(expectedCertificateStatus, validation.Status); Assert.Equal(expectedCertificateStatus, endCertificate.Status); Assert.Equal(expectedStatusForSignatureAtIngestion, signatureAtIngestion.Status); Assert.Equal(expectedStatusForSignatureInGracePeriod, signatureInGracePeriod.Status); Assert.Equal(expectedStatusForSignatureAfterGracePeriod, signatureAfterGracePeriod.Status); _context.Verify(c => c.SaveChangesAsync(), Times.Once); }
public async Task ValidateTimestampingCertificate() { // Arrange var certificate = await _fixture.GetRevokedTimestampingCertificateAsync(RevocationTime); var endCertificateKey = 123; var validationId = Guid.NewGuid(); var packageSigningState = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid }; var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown, Type = PackageSignatureType.Author, }; var signatureInGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod, Type = PackageSignatureType.Author, }; var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid, Type = PackageSignatureType.Author, }; var endCertificate = new EndCertificate { Key = endCertificateKey, Status = EndCertificateStatus.Unknown, Use = EndCertificateUse.Timestamping, CertificateChainLinks = new CertificateChainLink[0], }; var trustedTimestamp = new TrustedTimestamp { EndCertificate = endCertificate, Status = TrustedTimestampStatus.Valid }; var validation = new EndCertificateValidation { EndCertificateKey = endCertificateKey, ValidationId = validationId, Status = null, EndCertificate = endCertificate }; signatureAtIngestion.PackageSigningState = packageSigningState; signatureAtIngestion.TrustedTimestamps = new[] { trustedTimestamp }; signatureInGracePeriod.PackageSigningState = packageSigningState; signatureInGracePeriod.TrustedTimestamps = new[] { trustedTimestamp }; signatureAfterGracePeriod.PackageSigningState = packageSigningState; signatureAfterGracePeriod.TrustedTimestamps = new[] { trustedTimestamp }; _context.Mock( packageSignatures: new[] { signatureAtIngestion, signatureInGracePeriod, signatureAfterGracePeriod }, endCertificates: new[] { endCertificate }, certificateValidations: new EndCertificateValidation[] { validation }); _certificateStore.Setup(s => s.LoadAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(certificate)); // Act await _target.HandleAsync(new CertificateValidationMessage(certificateKey : endCertificateKey, validationId : validationId)); // Assert Assert.Equal(EndCertificateStatus.Revoked, validation.Status); Assert.Equal(EndCertificateStatus.Revoked, endCertificate.Status); Assert.Equal(PackageSignatureStatus.Invalid, signatureAtIngestion.Status); Assert.Equal(PackageSignatureStatus.Invalid, signatureInGracePeriod.Status); Assert.Equal(PackageSignatureStatus.Invalid, signatureAfterGracePeriod.Status); _context.Verify(c => c.SaveChangesAsync(), Times.Once); }
public async Task DoesNotDuplicateWhenSomeDataAlreadyExist() { // Arrange var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1); var existingParentCertificate = new ParentCertificate { Key = 1, Thumbprint = TestResources.RootThumbprint, CertificateChainLinks = new List <CertificateChainLink>(), }; var existingEndCertificate = new EndCertificate { Key = 1, Thumbprint = TestResources.Leaf1Thumbprint, Status = EndCertificateStatus.Good, // Different than the default. Use = EndCertificateUse.CodeSigning, CertificateChainLinks = new List <CertificateChainLink>(), }; var existingLink = new CertificateChainLink { ParentCertificate = existingParentCertificate, ParentCertificateKey = existingParentCertificate.Key, EndCertificate = existingEndCertificate, EndCertificateKey = existingEndCertificate.Key, }; existingParentCertificate.CertificateChainLinks.Add(existingLink); existingEndCertificate.CertificateChainLinks.Add(existingLink); var existingPackageSignature = new PackageSignature { Key = 1, EndCertificate = existingEndCertificate, EndCertificateKey = existingEndCertificate.Key, Status = PackageSignatureStatus.Valid, CreatedAt = new DateTime(2017, 1, 1, 8, 30, 0, DateTimeKind.Utc), PackageKey = _packageKey, Type = PackageSignatureType.Author, TrustedTimestamps = new List <TrustedTimestamp>(), }; _entitiesContext .Setup(x => x.ParentCertificates) .Returns(DbSetMockFactory.Create(existingParentCertificate)); _entitiesContext .Setup(x => x.EndCertificates) .Returns(DbSetMockFactory.Create(existingEndCertificate)); _entitiesContext .Setup(x => x.CertificateChainLinks) .Returns(DbSetMockFactory.Create(existingLink)); _entitiesContext .Setup(x => x.PackageSignatures) .Returns(DbSetMockFactory.Create(existingPackageSignature)); // Act await _target.ExtractAsync(_packageKey, signature, _token); // Assert 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()); Assert.Equal(1, _entitiesContext.Object.PackageSignatures.Count()); Assert.Equal(1, _entitiesContext.Object.TrustedTimestamps.Count()); Assert.Equal(EndCertificateStatus.Good, existingEndCertificate.Status); Assert.Equal(PackageSignatureStatus.Valid, existingPackageSignature.Status); }