/// <summary> /// Promote valid signatures from "Unknown" status to either "Valid" or "InGracePeriod". /// </summary> /// <param name="request">The validation request for the package whose signature should be promoted.</param> /// <param name="signatures">The valid signatures that should be promoted.</param> private void PromoteSignature(INuGetValidationRequest request, PackageSignature signature) { var newSignatureStatus = signature.IsPromotable() ? PackageSignatureStatus.Valid : PackageSignatureStatus.InGracePeriod; _logger.LogInformation( "Promoting package {PackageId} {PackageVersion} signature from status {OldSignatureStatus} to status {NewSignatureStatus}", request.PackageId, request.PackageVersion, signature.Status, newSignatureStatus); signature.Status = newSignatureStatus; }
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 AcceptsRepoSignedPackageWithChangedSigningCertificateThumbprint() { // Arrange var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.RepoSignedPackageLeaf1); var existingTrustedTimestamp = new TrustedTimestamp { EndCertificate = new EndCertificate { Thumbprint = "something else B", }, }; var existingPackageSignature = new PackageSignature { Key = 1, PackageKey = _packageKey, EndCertificate = new EndCertificate { Thumbprint = "something else A", }, Type = PackageSignatureType.Repository, TrustedTimestamps = new List <TrustedTimestamp> { existingTrustedTimestamp }, }; _entitiesContext .Setup(x => x.PackageSignatures) .Returns(DbSetMockFactory.Create(existingPackageSignature)); _entitiesContext .Setup(x => x.TrustedTimestamps) .Returns(DbSetMockFactory.Create(existingTrustedTimestamp)); // Act await _target.ExtractAsync(_packageKey, signature, _token); // Assert var newPackageSignature = Assert.Single(_entitiesContext.Object.PackageSignatures); Assert.NotSame(existingPackageSignature, newPackageSignature); Assert.Equal(TestResources.Leaf1Thumbprint, newPackageSignature.EndCertificate.Thumbprint); var newTrustedTimestamp = Assert.Single(_entitiesContext.Object.TrustedTimestamps); Assert.NotSame(existingTrustedTimestamp, newTrustedTimestamp); Assert.Equal(TestResources.Leaf1TimestampThumbprint, newTrustedTimestamp.EndCertificate.Thumbprint); }
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); }
/// <summary> /// Decide whether the signature should be invalidated. /// </summary> /// <param name="signature">The package's signature.</param> /// <param name="isRevalidationRequest">Whether this package has already been validated. If true, invalid certificates will not invalidate signatures.</param> /// <returns>Whether the signature should be invalidated.</returns> private bool ShouldInvalidateSignature(PackageSignature signature, bool isRevalidationRequest) { // Revalidation requests do NOT revalidate certificates that are known to be revoked. Thus, // certificates that were revoked before the package was signed ALWAYS invalidate the signature. if (signature.EndCertificate.Status == EndCertificateStatus.Revoked) { return(signature.TrustedTimestamps.Any(t => signature.EndCertificate.RevocationTime.Value <= t.Value)); } // Revalidation requests will revalidate invalid certificates. Therefore, invalid certificates // should invalidate the signature only if this is not a revalidation request. if (signature.EndCertificate.Status == EndCertificateStatus.Invalid) { return(!isRevalidationRequest); } return(false); }
/// <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 async Task UnknownResultAlertsIfReachesMaxFailureThreshold() { // 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); // The second result should trigger an alert but should NOT invalidate signatures. result = await _target.TrySaveResultAsync(_certificateValidation1, verificationResult); Assert.True(result); Assert.Equal(EndCertificateStatus.Invalid, _certificateValidation1.Status); Assert.Equal(EndCertificateStatus.Invalid, _certificateValidation1.EndCertificate.Status); Assert.Equal(5, _certificateValidation1.EndCertificate.ValidationFailures); Assert.Null(_certificateValidation1.EndCertificate.RevocationTime); _telemetryService.Verify(a => a.TrackUnableToValidateCertificateEvent(It.IsAny <EndCertificate>()), Times.Once); _telemetryService.Verify(a => a.TrackPackageSignatureShouldBeInvalidatedEvent(It.IsAny <PackageSignature>()), Times.Never); _context.Verify(c => c.SaveChangesAsync(), Times.Exactly(2)); }
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 RejectsPackageWithChangedTimestampValue() { // Arrange var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1); var existingPackageSignature = new PackageSignature { Key = 1, PackageKey = _packageKey, EndCertificate = new EndCertificate { Thumbprint = TestResources.Leaf1Thumbprint, }, TrustedTimestamps = new[] { new TrustedTimestamp { EndCertificate = new EndCertificate { Thumbprint = TestResources.Leaf1TimestampThumbprint, }, Value = new DateTime(2010, 1, 1, 0, 0, 0, DateTimeKind.Utc), }, }, Type = PackageSignatureType.Author, }; _entitiesContext .Setup(x => x.PackageSignatures) .Returns(DbSetMockFactory.Create(existingPackageSignature)); // Act & Assert var ex = await Assert.ThrowsAsync <InvalidOperationException>( () => _target.ExtractAsync(_packageKey, signature, _token)); Assert.Equal("The value of the trusted timestamp cannot change.", ex.Message); _entitiesContext.Verify( x => x.SaveChangesAsync(), Times.Never); Assert.Empty(_savedCertificates); }
private PackageSignature InitializePackageSignature( int packageKey, PackageSignatureType type, HashedCertificate signatureEndCertificate, IReadOnlyDictionary <string, EndCertificate> thumbprintToEndCertificate) { var packageSignature = new PackageSignature { CreatedAt = DateTime.UtcNow, EndCertificate = thumbprintToEndCertificate[signatureEndCertificate.Thumbprint], PackageKey = packageKey, Status = PackageSignatureStatus.Unknown, Type = type, TrustedTimestamps = new List <TrustedTimestamp>(), }; packageSignature.EndCertificateKey = packageSignature.EndCertificate.Key; _entitiesContext.PackageSignatures.Add(packageSignature); return(packageSignature); }
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> /// Decide whether the valid signature should be considered "Valid" or "InGracePeriod". /// </summary> /// <param name="signature">The valid signature whose status should be decided.</param> /// <returns>True if the signature should be "Valid", false if it should be "InGracePeriod".</returns> private bool IsValidSignatureOutOfGracePeriod(PackageSignature signature) { var certificate = signature.EndCertificate; // A signature can be valid even if its certificate is revoked as long as the certificate // revocation date begins after the signature was created. The validation pipeline does // not revalidate revoked certificates, thus, a valid package signature with a revoked // certificate should be "Valid" regardless of the certificate's status update time. if (certificate.Status == EndCertificateStatus.Revoked) { return(true); } else if (certificate.StatusUpdateTime.HasValue) { var signatureTime = signature.TrustedTimestamps.Max(t => t.Value); return(certificate.StatusUpdateTime > signatureTime); } else { return(false); } }
private SignatureDecision RejectAllSignaturesDecider(PackageSignature signature) => SignatureDecision.Reject;
private void InitializeTrustedTimestamp( PackageSignature packageSignature, Signature signature, HashedCertificate timestampEndCertificate, IReadOnlyDictionary <string, EndCertificate> thumbprintToEndCertificate) { if (packageSignature.TrustedTimestamps.Count > 1) { _logger.LogError( "There are {Count} trusted timestamps for the {SignatureType} signature on package {PackageKey}. There should be either zero or one.", packageSignature.TrustedTimestamps.Count, signature.Type, packageSignature.PackageKey); throw new InvalidOperationException("There should never be more than one trusted timestamp per package signature."); } // Determine the value of the timestamp. var value = signature.Timestamps.Single().UpperLimit.UtcDateTime; TrustedTimestamp trustedTimestamp; if (packageSignature.TrustedTimestamps.Count == 0) { trustedTimestamp = new TrustedTimestamp { PackageSignature = packageSignature, PackageSignatureKey = packageSignature.Key, EndCertificate = thumbprintToEndCertificate[timestampEndCertificate.Thumbprint], Value = value, Status = TrustedTimestampStatus.Valid, }; trustedTimestamp.EndCertificateKey = trustedTimestamp.EndCertificate.Key; packageSignature.TrustedTimestamps.Add(trustedTimestamp); _entitiesContext.TrustedTimestamps.Add(trustedTimestamp); } else { trustedTimestamp = packageSignature.TrustedTimestamps.Single(); if (trustedTimestamp.EndCertificate.Thumbprint != timestampEndCertificate.Thumbprint) { _logger.LogError( "The timestamp end certificate thumbprint cannot change for the {SignatureType} signature " + "on package {PackageKey}. The existing timestamp end certificate is {ExistingThumbprint}. " + "The new thumprint is {NewThumbprint}.", signature.Type, packageSignature.PackageKey, packageSignature.EndCertificate.Thumbprint, timestampEndCertificate.Thumbprint); throw new InvalidOperationException("The thumbprint of the timestamp end certificate cannot change."); } if (trustedTimestamp.Value != value) { _logger.LogError( "The trusted timestamp value cannot change for the {SignatureType} signature on package " + "{PackageKey}. The existing timestamp value is {ExistingValue}. The new value is {NewValue}.", signature.Type, packageSignature.PackageKey, trustedTimestamp.Value, value); throw new InvalidOperationException("The value of the trusted timestamp cannot change."); } } }
/// <summary> /// Decide whether the valid signature should be considered "Valid" or "InGracePeriod". /// </summary> /// <param name="request">The validation request for the package whose signature should be inspected.</param> /// <param name="signature">The valid signature whose status should be decided.</param> /// <returns>True if the signature should be "Valid", false if it should be "InGracePeriod".</returns> private bool IsValidSignatureOutOfGracePeriod(INuGetValidationRequest request, PackageSignature signature) { bool IsCertificateStatusPastTime(EndCertificate certificate, DateTime time) { return(certificate.StatusUpdateTime.HasValue && certificate.StatusUpdateTime > time); } var signingTime = signature.TrustedTimestamps.Max(t => t.Value); // Ensure the timestamps' certificate statuses are fresher than the signature. foreach (var timestamp in signature.TrustedTimestamps) { // A valid signature should NEVER have a timestamp whose end certificate is revoked. // Note that it is possible for a valid signature to have an invalid certificate as // certain certificate statuses, like "NotTimeNested", do not affect signatures. if (timestamp.EndCertificate.Status == EndCertificateStatus.Revoked) { _logger.LogError( Error.PackageCertificateValidationInvalidSignatureState, "Valid signature cannot have a timestamp whose end certificate is revoked ({ValidationId}, {PackageId} {PackageVersion})", request.ValidationId, request.PackageId, request.PackageVersion, signature.Status); throw new InvalidOperationException( $"ValidationId {request.ValidationId} has valid signature with a timestamp whose end certificate is revoked"); } if (!IsCertificateStatusPastTime(timestamp.EndCertificate, signingTime)) { return(false); } } // A signature can be valid even if its certificate is revoked as long as the certificate // revocation date begins after the signature was created. The validation pipeline does // not revalidate revoked certificates, thus, a valid package signature with a revoked // certificate is considered out of the grace period regardless of the certificate's // status update time. if (signature.EndCertificate.Status != EndCertificateStatus.Revoked) { // Ensure the signature's certificate status is fresher than the signature. if (!IsCertificateStatusPastTime(signature.EndCertificate, signingTime)) { return(false); } } return(true); }
/// <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 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 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 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 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); }
private SignatureDecision RejectSignaturesAtIngestionDecider(PackageSignature signature) { return((signature.Status == PackageSignatureStatus.Unknown) ? SignatureDecision.Reject : SignatureDecision.Ignore); }
private SignatureDecision NoActionDecider(PackageSignature signature) => SignatureDecision.Ignore;
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); }
private SignatureDecision RejectSignaturesAtIngestionOtherwiseWarnDecider(PackageSignature signature) { return((signature.Status == PackageSignatureStatus.Unknown) ? SignatureDecision.Reject : SignatureDecision.Warn); }
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); }