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); }
/// <summary> /// Mark a package's signing state and its revoked signatures as invalid. This method does NOT /// persist entity changes to the database. /// </summary> /// <param name="request">The request to validate a package.</param> /// <param name="package">The package's overall signing state that should be invalidated.</param> /// <param name="signature">The package's signatures that should be invalidated.</param> /// <returns>A task that completes when the entities have been updated.</returns> private void InvalidatePackageSignature(IValidationRequest request, PackageSigningState package, PackageSignature signature) { _logger.LogWarning( "Invalidating package {PackageId} {PackageVersion} due to revoked signatures.", request.PackageId, request.PackageVersion); package.SigningStatus = PackageSigningStatus.Invalid; signature.Status = PackageSignatureStatus.Invalid; }
public async Task DropsAllPackageSignaturesWhenPackageStateTransitionsToUnsigned() { // Arrange const int packageKey = 1; const string packageId = "packageId"; const string packageVersion = "1.0.0"; const PackageSigningStatus newStatus = PackageSigningStatus.Unsigned; var signature1 = new PackageSignature(); var signature2 = new PackageSignature(); var packageSigningState = new PackageSigningState { PackageId = packageId, PackageKey = packageKey, SigningStatus = PackageSigningStatus.Valid, PackageNormalizedVersion = packageVersion, PackageSignatures = new List <PackageSignature> { signature1, signature2 } }; var logger = _loggerFactory.CreateLogger <PackageSigningStateService>(); var packageSigningStatesDbSetMock = DbSetMockFactory.CreateMock(packageSigningState); var packageSignaturesDbSetMock = DbSetMockFactory.CreateMock(signature1, signature2); var validationContextMock = new Mock <IValidationEntitiesContext>(MockBehavior.Strict); validationContextMock.Setup(m => m.PackageSigningStates).Returns(packageSigningStatesDbSetMock.Object); validationContextMock.Setup(m => m.PackageSignatures).Returns(packageSignaturesDbSetMock.Object); // Act var packageSigningStateService = new PackageSigningStateService(validationContextMock.Object, logger); // Assert await packageSigningStateService.SetPackageSigningState( packageKey, packageId, packageVersion, status : newStatus); // Assert Assert.Equal(newStatus, packageSigningState.SigningStatus); packageSignaturesDbSetMock.Verify(m => m.Remove(signature1), Times.Once); packageSignaturesDbSetMock.Verify(m => m.Remove(signature2), Times.Once); validationContextMock.Verify( m => m.SaveChangesAsync(), Times.Never, "Saving the context here is incorrect as updating the validator's status also saves the context. Doing so would cause both queries not to be executed in the same transaction."); }
/// <summary> /// Mark a package's signing state and its revoked signatures as invalid. This method does NOT /// persist entity changes to the database. /// </summary> /// <param name="request">The request to validate a package.</param> /// <param name="package">The package's overall signing state that should be invalidated.</param> /// <param name="revokedSignatures">The package's signatures that should be invalidated.</param> /// <returns>A task that completes when the entities have been updated.</returns> private void InvalidatePackageSignatures(IValidationRequest request, PackageSigningState package, IEnumerable <PackageSignature> revokedSignatures) { _logger.LogWarning( "Invalidating package {PackageId} {PackageVersion} due to revoked signatures.", request.PackageId, request.PackageVersion); package.SigningStatus = PackageSigningStatus.Invalid; foreach (var signature in revokedSignatures) { signature.Status = PackageSignatureStatus.Invalid; } }
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 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 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); }