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));
            }
Example #3
0
            public async Task RejectsCertificateWithMultipleUses()
            {
                // Arrange
                var signature = await TestResources.LoadPrimarySignatureAsync(TestResources.SignedPackageLeaf1);

                var existingEndCertificate = new EndCertificate
                {
                    Key                   = 1,
                    Thumbprint            = TestResources.Leaf1Thumbprint,
                    Status                = EndCertificateStatus.Good,
                    Use                   = EndCertificateUse.Timestamping,
                    CertificateChainLinks = new List <CertificateChainLink>(),
                };

                _entitiesContext
                .Setup(x => x.EndCertificates)
                .Returns(DbSetMockFactory.Create(existingEndCertificate));

                // Act & Assert
                var ex = await Assert.ThrowsAsync <InvalidOperationException>(
                    () => _target.ExtractAsync(_packageKey, signature, _token));

                Assert.Equal("The use of an end certificate cannot change.", ex.Message);
                _entitiesContext.Verify(
                    x => x.SaveChangesAsync(),
                    Times.Never);
                Assert.Empty(_savedCertificates);
            }
        public CertificateVerificationEnqueuerFacts()
        {
            _configuration     = new ValidateCertificateConfiguration();
            _brokeredMessage   = new Mock <IBrokeredMessage>();
            _validationRequest = new Mock <IValidationRequest>();
            _endCertificate    = new EndCertificate {
                Key = 23
            };

            _validationRequest.Setup(x => x.ValidationId).Returns(new Guid("68fc78da-af04-4e4e-8128-de68dcfec3ba"));
            _brokeredMessage.SetupProperty(x => x.ScheduledEnqueueTimeUtc);

            _topicClient = new Mock <ITopicClient>();
            _serializer  = new Mock <IBrokeredMessageSerializer <CertificateValidationMessage> >();
            _options     = new Mock <IOptionsSnapshot <ValidateCertificateConfiguration> >();

            _options.Setup(x => x.Value).Returns(() => _configuration);
            _serializer
            .Setup(x => x.Serialize(It.IsAny <CertificateValidationMessage>()))
            .Returns(() => _brokeredMessage.Object);

            _target = new ValidateCertificateEnqueuer(
                _topicClient.Object,
                _serializer.Object,
                _options.Object);
        }
Example #5
0
        /// <summary>
        /// Find all package signatures that depend on the given certificate. This method will return signatures in
        /// batches of size <see cref="MaxSignatureUpdatesPerTransaction"/>.
        /// </summary>
        /// <param name="certificate">The certificate whose signatures should be found.</param>
        /// <param name="page">Which page of signatures should be fetched.</param>
        /// <returns>The signatures that depend on the given certificate.</returns>
        private Task <List <PackageSignature> > FindSignaturesAsync(EndCertificate certificate, int page)
        {
            // A signature may depend on a certificate in one of two ways: the signature itself may have been signed using
            // the certificate, or, one of the signature's trusted timestamps may have been signed using the certificate.
            IQueryable <PackageSignature> packageSignatures;

            switch (certificate.Use)
            {
            case EndCertificateUse.CodeSigning:
                packageSignatures = _context.PackageSignatures
                                    .Where(s => s.Type == PackageSignatureType.Author)
                                    .Where(s => s.EndCertificate.Thumbprint == certificate.Thumbprint);
                break;

            case EndCertificateUse.Timestamping:
                packageSignatures = _context.PackageSignatures
                                    .Where(s => s.Type == PackageSignatureType.Author)
                                    .Where(s => s.TrustedTimestamps.Any(t => t.EndCertificate.Thumbprint == certificate.Thumbprint));

                break;

            default:
                throw new InvalidOperationException($"Unknown {nameof(EndCertificateUse)}: {certificate.Use}");
            }

            return(packageSignatures
                   .Include(s => s.TrustedTimestamps.Select(t => t.EndCertificate))
                   .Include(s => s.PackageSigningState)
                   .OrderBy(s => s.Key)
                   .Skip(page * MaxSignatureUpdatesPerTransaction)
                   .Take(MaxSignatureUpdatesPerTransaction)
                   .ToListAsync());
        }
            public void InvalidCertificateInvalidatesSignatures(
                EndCertificateUse use,
                X509ChainStatusFlags flags,
                SignatureDecision ingestionDecision,
                SignatureDecision gracePeriodDecision,
                SignatureDecision afterGracePeriodDecision)
            {
                // Arrange
                var certificate = new EndCertificate {
                    Use = use
                };

                var result = new CertificateVerificationResult(
                    status: EndCertificateStatus.Invalid,
                    statusFlags: flags);

                var signatureAtIngestion = new PackageSignature {
                    Status = PackageSignatureStatus.Unknown
                };
                var signatureAtGracePeriod = new PackageSignature {
                    Status = PackageSignatureStatus.InGracePeriod
                };
                var signatureAfterGracePeriod = new PackageSignature {
                    Status = PackageSignatureStatus.Valid
                };

                // Act & Assert
                var decider = _target.MakeDeciderForInvalidatedCertificate(certificate, result);

                Assert.Equal(ingestionDecision, decider(signatureAtIngestion));
                Assert.Equal(gracePeriodDecision, decider(signatureAtGracePeriod));
                Assert.Equal(afterGracePeriodDecision, decider(signatureAfterGracePeriod));
            }
        public async Task EnqueueVerificationAsync(IValidationRequest request, EndCertificate certificate)
        {
            var message         = new CertificateValidationMessage(certificate.Key, request.ValidationId);
            var brokeredMessage = _serializer.Serialize(message);

            await _topicClient.SendAsync(brokeredMessage);
        }
Example #8
0
        /// <summary>
        /// The helper that processes how a certificate's status change affects its dependent signatures.
        /// </summary>
        /// <param name="certificate">The certificate whose dependent signatures should be processed.</param>
        /// <param name="certificateVerificationResult">The result of the certificate's verification.</param>
        /// <param name="signatureDecider">The delegate that decides how a dependent signature should be handled.</param>
        /// <param name="onAllSignaturesHandled">The action that will be called once all dependent signatures have been processed.</param>
        /// <returns></returns>
        private async Task ProcessDependentSignaturesAsync(
            EndCertificate certificate,
            CertificateVerificationResult certificateVerificationResult,
            SignatureDecider signatureDecider,
            Action onAllSignaturesHandled)
        {
            // A single certificate may be dependend on by many signatures. To ensure sanity, only up
            // to "MaxSignatureUpdatesPerTransaction" signatures will be invalidated at a time.
            List <PackageSignature> signatures = null;
            int page = 0;

            do
            {
                // If necessary, save the previous iteration's signature invalidations.
                if (page > 0)
                {
                    _logger.LogInformation(
                        "Persisting {Signatures} dependent signature updates for certificate {CertificateThumbprint} (page {Page})",
                        signatures.Count,
                        certificate.Thumbprint,
                        page);

                    await _context.SaveChangesAsync();

                    page++;
                }

                _logger.LogInformation(
                    "Finding more dependent signatures to update for certificate {CertificateThumbprint}... (page {Page})",
                    certificate.Thumbprint,
                    page);

                signatures = await FindSignaturesAsync(certificate, page);

                _logger.LogInformation(
                    "Updating {Signatures} signatures for certificate {CertificateThumbprint}... (page {Page})",
                    signatures.Count,
                    certificate.Thumbprint,
                    page);

                foreach (var signature in signatures)
                {
                    var decision = signatureDecider(signature);

                    HandleSignatureDecision(signature, decision, certificate, certificateVerificationResult);
                }
            }while (signatures.Count == MaxSignatureUpdatesPerTransaction);

            // All signatures have been invalidated. Do any necessary finalizations, and persist the results.
            _logger.LogInformation(
                "Finalizing {Signatures} dependent signature updates for certificate {CertificateThumbprint} (total pages: {Pages})",
                signatures.Count,
                certificate.Thumbprint,
                page + 1);

            onAllSignaturesHandled();

            await _context.SaveChangesAsync();
        }
            public void Dispose()
            {
                EndCertificate.Dispose();

                foreach (var ancestor in AncestorCertificates)
                {
                    ancestor.Dispose();
                }
            }
 public void TrackUnableToValidateCertificateEvent(EndCertificate certificate)
 {
     _telemetryClient.TrackMetric(
         PackageSignatureMayBeInvalidated,
         1,
         new Dictionary <string, string>
     {
         { CertificateId, certificate.Key.ToString() },
         { CertificateThumbprint, certificate.Thumbprint }
     });
 }
        public async Task EnqueueVerificationAsync(IValidationRequest request, EndCertificate certificate)
        {
            var message         = new CertificateValidationMessage(certificate.Key, request.ValidationId);
            var brokeredMessage = _serializer.Serialize(message);

            var visibleAt = DateTimeOffset.UtcNow + (_configuration.Value.MessageDelay ?? TimeSpan.Zero);

            brokeredMessage.ScheduledEnqueueTimeUtc = visibleAt;

            await _topicClient.SendAsync(brokeredMessage);
        }
        public async Task EnqueueValidationAsync(Guid validationId, EndCertificate certificate)
        {
            var message = new CertificateValidationMessage(
                certificate.Key,
                validationId,
                revalidateRevokedCertificate: false,
                sendCheckValidator: false);

            var brokeredMessage = _serializer.Serialize(message);

            await _topicClient.SendAsync(brokeredMessage);
        }
Example #13
0
        /// <summary>
        /// Decide whether or not to revalidate the given certificate.
        /// </summary>
        /// <param name="certificate">The certificate that may be revalidated.</param>
        /// <returns>Whether the certificate should be revalidated.</returns>
        private bool ShouldValidateCertificate(EndCertificate certificate)
        {
            // Validate the certificate only if it has never been validated before, or, if
            // its last validation time is past the maximum revalidation threshold.
            if (certificate.LastVerificationTime.HasValue)
            {
                var timeAgo = DateTime.UtcNow - certificate.LastVerificationTime.Value;

                return(timeAgo >= _certificateRevalidationThresholdTime);
            }

            return(true);
        }
            public void RevokedCodeSigningCertificateRejectsAllSignaturesIfTimestampIsAlreadyInvalid()
            {
                var certificate = new EndCertificate {
                    Use = EndCertificateUse.CodeSigning
                };

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

                var signatureAtIngestion = new PackageSignature
                {
                    Status            = PackageSignatureStatus.Unknown,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp {
                            Status = TrustedTimestampStatus.Invalid
                        }
                    }
                };

                var signatureAtGracePeriod = new PackageSignature
                {
                    Status            = PackageSignatureStatus.InGracePeriod,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp {
                            Status = TrustedTimestampStatus.Invalid
                        }
                    }
                };

                var signatureAfterGracePeriod = new PackageSignature
                {
                    Status            = PackageSignatureStatus.Valid,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp {
                            Status = TrustedTimestampStatus.Invalid
                        }
                    }
                };

                // Act & Assert
                var decider = _target.MakeDeciderForRevokedCertificate(certificate, result);

                Assert.Equal(SignatureDecision.Reject, decider(signatureAtIngestion));
                Assert.Equal(SignatureDecision.Reject, decider(signatureAtGracePeriod));
                Assert.Equal(SignatureDecision.Reject, decider(signatureAfterGracePeriod));
            }
Example #15
0
        /// <summary>
        /// Create a function to decide how signatures should be affected by a revoked certificate.
        /// </summary>
        /// <param name="certificate">The certificate that was revoked.</param>
        /// <param name="result">The verification result that describes when the certificate was revoked.</param>
        /// <returns>The function that describes how dependent signatures should be affected by the certificate status change.</returns>
        public SignatureDecider MakeDeciderForRevokedCertificate(EndCertificate certificate, CertificateVerificationResult result)
        {
            switch (certificate.Use)
            {
            case EndCertificateUse.Timestamping:
                return(RejectSignaturesAtIngestionOtherwiseWarnDecider);

            case EndCertificateUse.CodeSigning:
                return(MakeDeciderForRevokedCodeSigningCertificate(result.RevocationTime));

            default:
                throw new InvalidOperationException($"Revoked certificate has unknown use: {certificate.Use}");
            }
        }
            public void RevokedCodeSigningCertificateThrowsIfThereIsARevocationDateAndMultipleTrustedTimestamps()
            {
                var certificate = new EndCertificate {
                    Use = EndCertificateUse.CodeSigning
                };

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

                var signatureAtIngestion = new PackageSignature
                {
                    Status            = PackageSignatureStatus.Unknown,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp(),
                        new TrustedTimestamp(),
                    }
                };

                var signatureAtGracePeriod = new PackageSignature
                {
                    Status            = PackageSignatureStatus.InGracePeriod,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp(),
                        new TrustedTimestamp(),
                    }
                };

                var signatureAfterGracePeriod = new PackageSignature
                {
                    Status            = PackageSignatureStatus.Valid,
                    TrustedTimestamps = new TrustedTimestamp[]
                    {
                        new TrustedTimestamp(),
                        new TrustedTimestamp(),
                    }
                };

                // Act & Assert
                var decider = _target.MakeDeciderForRevokedCertificate(certificate, result);

                Assert.Throws <InvalidOperationException>(() => decider(signatureAtIngestion));
                Assert.Throws <InvalidOperationException>(() => decider(signatureAtGracePeriod));
                Assert.Throws <InvalidOperationException>(() => decider(signatureAfterGracePeriod));
            }
        private async Task <IReadOnlyDictionary <string, EndCertificate> > InitializeEndCertificatesAsync(
            IReadOnlyCollection <EndCertificateAndUse> certificatesAndUses)
        {
            var thumbprints = certificatesAndUses
                              .Select(x => x.Certificate.Thumbprint)
                              .Distinct()
                              .ToList();

            // Find all of the end certificate entities that intersect with the set of certificates found in the
            // package that is currently being processed.
            var existingEntities = await _entitiesContext
                                   .EndCertificates
                                   .Include(x => x.CertificateChainLinks)
                                   .Where(x => thumbprints.Contains(x.Thumbprint))
                                   .ToListAsync();

            var thumbprintToEntity = existingEntities.ToDictionary(x => x.Thumbprint);

            foreach (var certificateAndUse in certificatesAndUses)
            {
                if (!thumbprintToEntity.TryGetValue(certificateAndUse.Certificate.Thumbprint, out var entity))
                {
                    entity = new EndCertificate
                    {
                        Status                = EndCertificateStatus.Unknown,
                        Use                   = certificateAndUse.Use,
                        Thumbprint            = certificateAndUse.Certificate.Thumbprint,
                        CertificateChainLinks = new List <CertificateChainLink>(),
                    };
                    _entitiesContext.EndCertificates.Add(entity);

                    thumbprintToEntity[certificateAndUse.Certificate.Thumbprint] = entity;
                }
                else if (entity.Use != certificateAndUse.Use)
                {
                    _logger.LogError(
                        "The use of end certificate {Thumbprint} cannot change. The existing use is {ExistingUse}. The new use is {NewUse}.",
                        certificateAndUse.Certificate.Thumbprint,
                        entity.Use,
                        certificateAndUse.Use);

                    throw new InvalidOperationException("The use of an end certificate cannot change.");
                }
            }

            return(thumbprintToEntity);
        }
Example #18
0
        /// <summary>
        /// Handle the decision on how to update the signature.
        /// </summary>
        /// <param name="signature">The signature that should be updated.</param>
        /// <param name="decision">How the signature should be updated.</param>
        /// <param name="certificate">The certificate that signature depends on that changed the signature's state.</param>
        /// <param name="certificateVerificationResult">The certificate verification that changed the signature's state.</param>
        private void HandleSignatureDecision(
            PackageSignature signature,
            SignatureDecision decision,
            EndCertificate certificate,
            CertificateVerificationResult certificateVerificationResult)
        {
            switch (decision)
            {
            case SignatureDecision.Ignore:
                _logger.LogInformation(
                    "Signature {SignatureKey} is not affected by certificate verification result: {CertificateVerificationResult}",
                    signature.Key,
                    certificateVerificationResult);
                break;

            case SignatureDecision.Warn:
                _logger.LogWarning(
                    "Invalidating signature {SignatureKey} due to certificate verification result: {CertificateVerificationResult}",
                    signature.Key,
                    certificateVerificationResult);

                InvalidateSignature(signature, certificate);

                _telemetryService.TrackPackageSignatureMayBeInvalidatedEvent(signature);

                break;

            case SignatureDecision.Reject:
                _logger.LogWarning(
                    "Rejecting signature {SignatureKey} due to certificate verification result: {CertificateVerificationResult}",
                    signature.Key,
                    certificateVerificationResult);

                InvalidateSignature(signature, certificate);

                _telemetryService.TrackPackageSignatureShouldBeInvalidatedEvent(signature);
                break;

            default:
                throw new InvalidOperationException(
                          $"Unknown signature decision '{decision}' for certificate verification result: {certificateVerificationResult}");
            }
        }
            public void RevokedCodeSigningCertificateWithRevocationDateInvalidatesSignatures(
                TimeSpan signatureTimeDeltaToRevocationTime,
                SignatureDecision ingestionDecision,
                SignatureDecision gracePeriodDecision,
                SignatureDecision afterGracePeriodDecision)
            {
                // Arrange - only signatures that were created after the revocation date should
                // be rejected.
                var revocationTime = DateTime.UtcNow;
                var certificate    = new EndCertificate {
                    Use = EndCertificateUse.CodeSigning
                };
                var timestamp = new TrustedTimestamp {
                    Value = revocationTime + signatureTimeDeltaToRevocationTime, Status = TrustedTimestampStatus.Valid
                };

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

                var signatureAtIngestion = new PackageSignature {
                    Status = PackageSignatureStatus.Unknown
                };
                var signatureAtGracePeriod = new PackageSignature {
                    Status = PackageSignatureStatus.InGracePeriod
                };
                var signatureAfterGracePeriod = new PackageSignature {
                    Status = PackageSignatureStatus.Valid
                };

                signatureAtIngestion.TrustedTimestamps      = new[] { timestamp };
                signatureAtGracePeriod.TrustedTimestamps    = new[] { timestamp };
                signatureAfterGracePeriod.TrustedTimestamps = new[] { timestamp };

                // Act & Assert
                var decider = _target.MakeDeciderForRevokedCertificate(certificate, result);

                Assert.Equal(ingestionDecision, decider(signatureAtIngestion));
                Assert.Equal(gracePeriodDecision, decider(signatureAtGracePeriod));
                Assert.Equal(afterGracePeriodDecision, decider(signatureAfterGracePeriod));
            }
Example #20
0
        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;
                }
            }
        }
Example #21
0
        /// <summary>
        /// Create a function to decide how signatures should be affected by an invalidated certificate.
        /// </summary>
        /// <param name="certificate">The certificate that was invalidated.</param>
        /// <param name="result">The verification result that describes why the certificate was invalidated.</param>
        /// <returns>The function that describes how dependent signatures should be affected by the certificate status change.</returns>
        public SignatureDecider MakeDeciderForInvalidatedCertificate(EndCertificate certificate, CertificateVerificationResult result)
        {
            if (result.Status != EndCertificateStatus.Invalid)
            {
                throw new ArgumentException($"Result must have a status of {nameof(EndCertificateStatus.Invalid)}", nameof(result));
            }

            if (result.StatusFlags == X509ChainStatusFlags.NoError)
            {
                throw new ArgumentException($"Invalid flags on invalid verification result: {result.StatusFlags}!", nameof(result));
            }

            // If a certificate used for the primary signature is revoked, all dependent signatures should be invalidated.
            // NOTE: It is assumed that the revoked certificate is an ancestor certificate, but this may not be strictly true.
            if (certificate.Use == EndCertificateUse.CodeSigning && (result.StatusFlags & X509ChainStatusFlags.Revoked) != 0)
            {
                return(RejectAllSignaturesDecider);
            }

            // NotTimeValid and HasWeakSignature fail packages only at ingestion. It is assumed that a chain with HasWeakSignature will
            // ALWAYS have NotSignatureValid.
            else if (result.StatusFlags == X509ChainStatusFlags.NotTimeValid ||
                     result.StatusFlags == (X509ChainStatusFlags.HasWeakSignature | X509ChainStatusFlags.NotSignatureValid) ||
                     result.StatusFlags == (X509ChainStatusFlags.NotTimeValid | X509ChainStatusFlags.HasWeakSignature | X509ChainStatusFlags.NotSignatureValid))
            {
                return(RejectSignaturesAtIngestionDecider);
            }

            // NotTimeNested does not affect signatures and should be ignored if it is the only status.
            else if (result.StatusFlags == X509ChainStatusFlags.NotTimeNested)
            {
                return(NoActionDecider);
            }

            // In all other cases, reject signatures at ingestion and warn on all other signatures.
            else
            {
                return(RejectSignaturesAtIngestionOtherwiseWarnDecider);
            }
        }
            public static IEnumerable <object[]> ValidSignaturesArePromotableData()
            {
                var cert1SecondAgo = new EndCertificate
                {
                    Status           = EndCertificateStatus.Good,
                    StatusUpdateTime = DateTime.UtcNow.AddSeconds(-1),
                };

                var certRevoked1SecondAgo = new EndCertificate
                {
                    Status           = EndCertificateStatus.Revoked,
                    StatusUpdateTime = DateTime.UtcNow.AddSeconds(-1),
                    RevocationTime   = DateTime.UtcNow.AddSeconds(-1),
                };

                var cert1YearAgo = new EndCertificate
                {
                    Status           = EndCertificateStatus.Good,
                    StatusUpdateTime = DateTime.UtcNow.AddYears(-1),
                };

                // A signature whose timestamp is BEFORE the signature's and timestamps' certificates
                // last updates should be promotable.
                yield return(new object[]
                {
                    true,
                    new PackageSignature
                    {
                        EndCertificate = cert1SecondAgo,
                        TrustedTimestamps = new[]
                        {
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddDays(-1),
                                EndCertificate = cert1SecondAgo,
                            }
                        },
                    },
                });

                // A signature whose timestamp is AFTER the signature's certificate last update should not
                // not be promotable
                yield return(new object[]
                {
                    false,
                    new PackageSignature
                    {
                        EndCertificate = cert1YearAgo,
                        TrustedTimestamps = new[]
                        {
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddDays(-1),
                                EndCertificate = cert1SecondAgo,
                            }
                        },
                    },
                });

                // A signature whose timestamp is AFTER the timestamp's certificate last update should not
                // be promotable
                yield return(new object[]
                {
                    false,
                    new PackageSignature
                    {
                        EndCertificate = cert1SecondAgo,
                        TrustedTimestamps = new[]
                        {
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddDays(-1),
                                EndCertificate = cert1YearAgo,
                            }
                        },
                    },
                });

                // The latest timestamp should be used for promotion decisions.
                yield return(new object[]
                {
                    false,
                    new PackageSignature
                    {
                        EndCertificate = cert1YearAgo,
                        TrustedTimestamps = new[]
                        {
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddDays(-1),
                                EndCertificate = cert1YearAgo,
                            },
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddYears(-10),
                                EndCertificate = cert1YearAgo,
                            }
                        },
                    },
                });

                // A signature whose signing certificate is revoked should be promoted to "Valid" as long as the revocation
                // time begins after the package was signed.
                yield return(new object[]
                {
                    true,
                    new PackageSignature
                    {
                        EndCertificate = certRevoked1SecondAgo,
                        TrustedTimestamps = new[]
                        {
                            new TrustedTimestamp
                            {
                                Value = DateTime.UtcNow.AddDays(-1),
                                EndCertificate = cert1SecondAgo,
                            }
                        },
                    },
                });
            }
Example #23
0
        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);
        }
Example #24
0
        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);
        }
Example #25
0
            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);
            }