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 MessageIsConsumedIfValidationEndsGracefully(CertificateVerificationResult result)
            {
                // Arrange
                _certificateValidationService
                .Setup(s => s.FindCertificateValidationAsync(It.IsAny <CertificateValidationMessage>()))
                .ReturnsAsync(new EndCertificateValidation
                {
                    Status         = null,
                    EndCertificate = new EndCertificate
                    {
                        Status = EndCertificateStatus.Unknown,
                        Use    = EndCertificateUse.CodeSigning,
                        CertificateChainLinks = new CertificateChainLink[0],
                    }
                });

                _certificateStore
                .Setup(s => s.LoadAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(new X509Certificate2());

                _certificateVerifier
                .Setup(v => v.VerifyCodeSigningCertificate(It.IsAny <X509Certificate2>(), It.IsAny <X509Certificate2[]>()))
                .Returns(result);

                _certificateValidationService
                .Setup(s => s.TrySaveResultAsync(It.IsAny <EndCertificateValidation>(), It.IsAny <CertificateVerificationResult>()))
                .ReturnsAsync(true);

                // Act & Assert
                Assert.True(await _target.HandleAsync(_message));

                _certificateValidationService
                .Verify(s => s.TrySaveResultAsync(It.IsAny <EndCertificateValidation>(), It.IsAny <CertificateVerificationResult>()), Times.Once);
            }
            public async Task UnknownResultUpdatesCertificateValidation()
            {
                // Arrange - Create a signature whose certificate and trusted timestamp depends on "_certificateValidation1".
                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Unknown,
                    statusFlags: X509ChainStatusFlags.RevocationStatusUnknown);

                var signature = new PackageSignature
                {
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var timestamp = new TrustedTimestamp {
                    Value = DateTime.UtcNow
                };

                signature.EndCertificate        = _certificate1;
                signature.TrustedTimestamps     = new[] { timestamp };
                _certificate1.PackageSignatures = new[] { signature };
                _certificate1.TrustedTimestamps = new[] { timestamp };

                // Act & Assert - the first Unknown result shouldn't cause any issues.
                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);
                Assert.Null(_certificateValidation1.Status);
                Assert.Equal(EndCertificateStatus.Unknown, _certificateValidation1.EndCertificate.Status);
                Assert.Equal(4, _certificateValidation1.EndCertificate.ValidationFailures);
                Assert.Null(_certificateValidation1.EndCertificate.RevocationTime);

                _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Never);
                _telemetryService.Verify(a => a.TrackPackageSignatureShouldBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Never);
                _context.Verify(c => c.SaveChangesAsync(), Times.Once);
            }
            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 RevokedResultDoesNotInvalidateDependentNonAuthorSignaturesSignatures(PackageSignatureType type)
            {
                // Arrange
                var revocationTime = DateTime.UtcNow;

                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Revoked,
                    statusFlags: X509ChainStatusFlags.Revoked,
                    revocationTime: revocationTime);

                var signingState = new PackageSigningState {
                    SigningStatus = PackageSigningStatus.Valid
                };
                var signature2 = new PackageSignature {
                    Key = 12, Status = PackageSignatureStatus.Valid, Type = type
                };
                var timestamp2 = new TrustedTimestamp {
                    Value = revocationTime.AddDays(1), Status = TrustedTimestampStatus.Valid
                };

                signingState.PackageSignatures = new[] { signature2 };
                signature2.PackageSigningState = signingState;
                signature2.EndCertificate      = _certificate1;
                signature2.TrustedTimestamps   = new[] { timestamp2 };
                timestamp2.PackageSignature    = signature2;
                timestamp2.EndCertificate      = _certificate2;
                _certificate1.Use = EndCertificateUse.CodeSigning;
                _certificate2.Use = EndCertificateUse.Timestamping;
                _certificate1.PackageSignatures = new[] { signature2 };
                _certificate2.TrustedTimestamps = new[] { timestamp2 };

                _context.Mock(
                    packageSigningStates: new[] { signingState },
                    packageSignatures: new[] { signature2 },
                    trustedTimestamps: new[] { timestamp2 },
                    endCertificates: new[] { _certificate1, _certificate2 });

                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);

                Assert.Equal(EndCertificateStatus.Revoked, _certificateValidation1.Status);

                Assert.Equal(EndCertificateStatus.Revoked, _certificate1.Status);
                Assert.Equal(0, _certificate1.ValidationFailures);
                Assert.Equal(revocationTime, _certificate1.RevocationTime);

                Assert.Equal(PackageSignatureStatus.Valid, signature2.Status);

                Assert.Equal(PackageSigningStatus.Valid, signingState.SigningStatus);

                _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Never);
                _telemetryService.Verify(a => a.TrackPackageSignatureShouldBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Never);
                _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Never);
                _context.Verify(c => c.SaveChangesAsync(), Times.Once);
            }
            private async Task <bool> HandleUnknownResultAsync(int validationFailuresStart)
            {
                // Arrange
                // Return an "Unknown" status for the certificate's verification. The validation service should increment the number
                // of failures for the validation's certificate.
                var certificateValidation = new EndCertificateValidation
                {
                    Status         = null,
                    EndCertificate = new EndCertificate
                    {
                        Status                = EndCertificateStatus.Unknown,
                        Use                   = EndCertificateUse.CodeSigning,
                        ValidationFailures    = validationFailuresStart,
                        CertificateChainLinks = new CertificateChainLink[0],
                    }
                };

                var certificateVerificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Unknown,
                    statusFlags: X509ChainStatusFlags.RevocationStatusUnknown);

                _certificateValidationService
                .Setup(s => s.FindCertificateValidationAsync(It.IsAny <CertificateValidationMessage>()))
                .ReturnsAsync(certificateValidation);

                _certificateStore
                .Setup(s => s.LoadAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(new X509Certificate2());

                _certificateVerifier
                .Setup(v => v.VerifyCodeSigningCertificate(It.IsAny <X509Certificate2>(), It.IsAny <X509Certificate2[]>()))
                .Returns(certificateVerificationResult);

                _certificateValidationService
                .Setup(
                    s => s.TrySaveResultAsync(
                        It.IsAny <EndCertificateValidation>(),
                        It.Is <CertificateVerificationResult>(r => r.Status == EndCertificateStatus.Unknown)))
                .Callback <EndCertificateValidation, CertificateVerificationResult>((v, r) => v.EndCertificate.ValidationFailures++)
                .ReturnsAsync(true);

                // Act & Assert
                var result = await _target.HandleAsync(_message);

                Assert.Equal(validationFailuresStart + 1, certificateValidation.EndCertificate.ValidationFailures);

                _certificateValidationService
                .Verify(
                    s => s.TrySaveResultAsync(
                        It.IsAny <EndCertificateValidation>(),
                        It.IsAny <CertificateVerificationResult>()),
                    Times.Once);

                return(result);
            }
            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));
            }
            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));
            }
            public async Task GoodResultUpdatesCertificateValidation()
            {
                // Arrange
                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Good,
                    statusFlags: X509ChainStatusFlags.NoError);

                // Act & Assert
                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);
                Assert.Equal(EndCertificateStatus.Good, _certificateValidation1.Status);
                Assert.Equal(EndCertificateStatus.Good, _certificateValidation1.EndCertificate.Status);
                Assert.Equal(0, _certificateValidation1.EndCertificate.ValidationFailures);
                Assert.Null(_certificateValidation1.EndCertificate.RevocationTime);
            }
Beispiel #11
0
            public async Task DoesNotSendCheckValidatorIfFeatureFlagIsDisabled()
            {
                // Arrange
                var result = new CertificateVerificationResult(
                    status: EndCertificateStatus.Good,
                    statusFlags: X509ChainStatusFlags.NoError);

                _message = new CertificateValidationMessage(
                    CertificateKey,
                    ValidationId,
                    revalidateRevokedCertificate: false,
                    sendCheckValidator: true);
                _certificateValidationService
                .Setup(s => s.FindCertificateValidationAsync(It.IsAny <CertificateValidationMessage>()))
                .ReturnsAsync(new EndCertificateValidation
                {
                    Status         = null,
                    EndCertificate = new EndCertificate
                    {
                        Status = EndCertificateStatus.Unknown,
                        Use    = EndCertificateUse.CodeSigning,
                        CertificateChainLinks = new CertificateChainLink[0],
                    }
                });
                _featureFlagService.Setup(x => x.IsQueueBackEnabled()).Returns(false);

                _certificateStore
                .Setup(s => s.LoadAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(new X509Certificate2());

                _certificateVerifier
                .Setup(v => v.VerifyCodeSigningCertificate(It.IsAny <X509Certificate2>(), It.IsAny <X509Certificate2[]>()))
                .Returns(result);

                _certificateValidationService
                .Setup(s => s.TrySaveResultAsync(It.IsAny <EndCertificateValidation>(), It.IsAny <CertificateVerificationResult>()))
                .ReturnsAsync(true);

                // Act & Assert
                Assert.True(await _target.HandleAsync(_message));

                _certificateValidationService
                .Verify(s => s.TrySaveResultAsync(It.IsAny <EndCertificateValidation>(), It.IsAny <CertificateVerificationResult>()), Times.Once);
                _validationEnqueuer.Verify(
                    x => x.SendMessageAsync(It.IsAny <PackageValidationMessageData>()),
                    Times.Never);
            }
            public async Task RevokedResultInvalidatesDependentTimestamps()
            {
                // Arrange - "signature1" is a signature that whose timestamp depends on the certificate that will be revoked.
                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Revoked,
                    statusFlags: X509ChainStatusFlags.Revoked,
                    revocationTime: DateTime.UtcNow);

                var signingState = new PackageSigningState {
                    SigningStatus = PackageSigningStatus.Valid
                };
                var signature1 = new PackageSignature
                {
                    Key    = 12,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var timestamp1 = new TrustedTimestamp {
                    Value = DateTime.UtcNow, Status = TrustedTimestampStatus.Valid
                };

                signingState.PackageSignatures = new[] { signature1 };
                signature1.PackageSigningState = signingState;
                signature1.EndCertificate      = _certificate1;
                signature1.TrustedTimestamps   = new[] { timestamp1 };
                timestamp1.PackageSignature    = signature1;
                timestamp1.EndCertificate      = _certificate1;
                _certificate1.Use = EndCertificateUse.Timestamping;
                _certificate1.PackageSignatures = new[] { signature1 };

                _context.Mock(
                    packageSigningStates: new[] { signingState },
                    packageSignatures: new[] { signature1 },
                    trustedTimestamps: new[] { timestamp1 },
                    endCertificates: new[] { _certificate1 });

                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);

                Assert.Equal(EndCertificateStatus.Revoked, _certificateValidation1.Status);
                Assert.Equal(TrustedTimestampStatus.Invalid, timestamp1.Status);
                Assert.Equal(EndCertificateStatus.Revoked, _certificate1.Status);
                Assert.Equal(PackageSignatureStatus.Invalid, signature1.Status);
                Assert.Equal(PackageSigningStatus.Invalid, signingState.SigningStatus);
            }
            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));
            }
            public async Task InvalidResultInvalidatesDependentSignatures()
            {
                // Arrange - Invalidate a certificate that is depended on by "signature1"'s certificate.
                // This should result in "signature1" being invalidated.
                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Invalid,
                    statusFlags: X509ChainStatusFlags.ExplicitDistrust);

                var signingState = new PackageSigningState {
                    SigningStatus = PackageSigningStatus.Valid
                };
                var signature1 = new PackageSignature
                {
                    Key    = 123,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var signature2 = new PackageSignature
                {
                    Key    = 456,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var timestamp = new TrustedTimestamp {
                    Value = DateTime.UtcNow
                };

                signingState.PackageSignatures  = new[] { signature1, signature2 };
                signature1.PackageSigningState  = signingState;
                signature2.PackageSigningState  = signingState;
                signature1.EndCertificate       = _certificate1;
                signature2.EndCertificate       = _certificate2;
                signature1.TrustedTimestamps    = new TrustedTimestamp[0];
                signature2.TrustedTimestamps    = new[] { timestamp };
                timestamp.PackageSignature      = signature2;
                timestamp.EndCertificate        = _certificate1;
                _certificate1.PackageSignatures = new[] { signature1 };
                _certificate1.TrustedTimestamps = new[] { timestamp };
                _certificate1.Use = EndCertificateUse.CodeSigning;

                _context.Mock(
                    packageSignatures: new[] { signature1, signature2 },
                    trustedTimestamps: new[] { timestamp });

                // Act
                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);

                Assert.Equal(EndCertificateStatus.Invalid, _certificateValidation1.Status);

                Assert.Equal(EndCertificateStatus.Invalid, _certificate1.Status);
                Assert.Equal(0, _certificate1.ValidationFailures);
                Assert.Null(_certificate1.RevocationTime);

                Assert.Equal(PackageSignatureStatus.Invalid, signature1.Status);
                Assert.Equal(PackageSignatureStatus.Valid, signature2.Status);

                Assert.Equal(PackageSigningStatus.Invalid, signingState.SigningStatus);

                // The package's signing state is "Valid", a MayBeInvalidated (warn) event should be raised.
                _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Never);
                _telemetryService.Verify(a => a.TrackPackageSignatureMayBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Exactly(1));
                _telemetryService.Verify(a => a.TrackPackageSignatureShouldBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Exactly(0));
                _context.Verify(c => c.SaveChangesAsync(), Times.Once);
            }
            public async Task ProcessesSignaturesInBatchesOf500()
            {
                // Arrange - Invalidate a certificate that is depended on by 501 signatures.
                // This should invalidate all signatures in batches of 500 signatures.
                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Invalid,
                    statusFlags: X509ChainStatusFlags.ExplicitDistrust);

                var signatures = new List <PackageSignature>();

                for (var i = 0; i < 501; i++)
                {
                    var state = new PackageSigningState {
                        SigningStatus = PackageSigningStatus.Valid
                    };

                    var signature = new PackageSignature
                    {
                        Key    = i,
                        Status = PackageSignatureStatus.Valid,
                        Type   = PackageSignatureType.Author
                    };

                    state.PackageSignatures       = new[] { signature };
                    signature.PackageSigningState = state;
                    signature.EndCertificate      = _certificate1;
                    signature.TrustedTimestamps   = new TrustedTimestamp[0];

                    signatures.Add(signature);
                }

                _certificate1.PackageSignatures = signatures;
                _certificate1.TrustedTimestamps = new TrustedTimestamp[0];
                _certificate1.Use = EndCertificateUse.CodeSigning;

                _context.Mock(packageSignatures: signatures);

                var hasSaved = false;
                var invalidationsBeforeSave = 0;
                var invalidationsAfterSave  = 0;

                _context.Setup(c => c.SaveChangesAsync())
                .Returns(Task.FromResult(0))
                .Callback(() => hasSaved = true);

                _telemetryService
                .Setup(t => t.TrackPackageSignatureMayBeInvalidatedEvent(It.IsAny <PackageSignature>()))
                .Callback((PackageSignature s) =>
                {
                    if (!hasSaved)
                    {
                        invalidationsBeforeSave++;
                    }
                    else
                    {
                        invalidationsAfterSave++;
                    }
                });

                // Act
                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                // Assert - two batches should be saved. The first batch should invalidate 500 signatures,
                // the second batch should invalidate 1 signature.
                _context.Verify(c => c.SaveChangesAsync(), Times.Exactly(2));

                Assert.Equal(500, invalidationsBeforeSave);
                Assert.Equal(1, invalidationsAfterSave);
            }
            public async Task RevokedResultInvalidatesDependentSignatures()
            {
                // Arrange - "signature1" is a signature that uses the certificate before the revocation date,
                // "signature2" is a signature that uses the certificate after the revocation date, "signature3"
                // is a signature that doesn't depend on the certificate.
                var revocationTime = DateTime.UtcNow;

                var verificationResult = new CertificateVerificationResult(
                    status: EndCertificateStatus.Revoked,
                    statusFlags: X509ChainStatusFlags.Revoked,
                    revocationTime: revocationTime);

                var signingState = new PackageSigningState {
                    SigningStatus = PackageSigningStatus.Valid
                };
                var signature1 = new PackageSignature
                {
                    Key    = 12,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var signature2 = new PackageSignature
                {
                    Key    = 23,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var signature3 = new PackageSignature
                {
                    Key    = 34,
                    Status = PackageSignatureStatus.Valid,
                    Type   = PackageSignatureType.Author,
                };
                var timestamp1 = new TrustedTimestamp {
                    Value = revocationTime.AddDays(-1), Status = TrustedTimestampStatus.Valid
                };
                var timestamp2 = new TrustedTimestamp {
                    Value = revocationTime.AddDays(1), Status = TrustedTimestampStatus.Valid
                };
                var timestamp3 = new TrustedTimestamp {
                    Value = revocationTime.AddDays(-1), Status = TrustedTimestampStatus.Valid
                };

                signingState.PackageSignatures = new[] { signature1, signature2, signature3 };
                signature1.PackageSigningState = signingState;
                signature2.PackageSigningState = signingState;
                signature3.PackageSigningState = signingState;
                signature1.EndCertificate      = _certificate1;
                signature2.EndCertificate      = _certificate1;
                signature3.EndCertificate      = _certificate2;
                signature1.TrustedTimestamps   = new[] { timestamp1 };
                signature2.TrustedTimestamps   = new[] { timestamp2 };
                signature3.TrustedTimestamps   = new[] { timestamp3 };
                timestamp1.PackageSignature    = signature1;
                timestamp2.PackageSignature    = signature2;
                timestamp3.PackageSignature    = signature3;
                timestamp1.EndCertificate      = _certificate2;
                timestamp2.EndCertificate      = _certificate2;
                timestamp3.EndCertificate      = _certificate2;
                _certificate1.Use = EndCertificateUse.CodeSigning;
                _certificate2.Use = EndCertificateUse.Timestamping;
                _certificate1.PackageSignatures = new[] { signature1, signature2 };
                _certificate2.TrustedTimestamps = new[] { timestamp1, timestamp2, timestamp3 };

                _context.Mock(
                    packageSigningStates: new[] { signingState },
                    packageSignatures: new[] { signature1, signature2, signature3 },
                    trustedTimestamps: new[] { timestamp1, timestamp2, timestamp3 },
                    endCertificates: new[] { _certificate1, _certificate2 });

                var result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult);

                Assert.True(result);

                Assert.Equal(EndCertificateStatus.Revoked, _certificateValidation1.Status);

                Assert.Equal(EndCertificateStatus.Revoked, _certificate1.Status);
                Assert.Equal(0, _certificate1.ValidationFailures);
                Assert.Equal(revocationTime, _certificate1.RevocationTime);

                Assert.Equal(PackageSignatureStatus.Valid, signature1.Status);
                Assert.Equal(PackageSignatureStatus.Invalid, signature2.Status);
                Assert.Equal(PackageSignatureStatus.Valid, signature3.Status);

                Assert.Equal(PackageSigningStatus.Invalid, signingState.SigningStatus);

                _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Never);
                _telemetryService.Verify(a => a.TrackPackageSignatureShouldBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Exactly(1));
                _context.Verify(c => c.SaveChangesAsync(), Times.Once);
            }