/// <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));
            }
Example #3
0
            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);
            }
Example #5
0
        /// <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);
        }
Example #6
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 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));
            }
Example #9
0
            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);
        }
Example #11
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 #12
0
        /// <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);
            }
        }
Example #13
0
 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);
        }
Example #16
0
        /// <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;
        }
Example #17
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 #18
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);
        }
            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);
            }
Example #21
0
 private SignatureDecision RejectSignaturesAtIngestionDecider(PackageSignature signature)
 {
     return((signature.Status == PackageSignatureStatus.Unknown)
                 ? SignatureDecision.Reject
                 : SignatureDecision.Ignore);
 }
Example #22
0
 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);
            }
Example #24
0
 private SignatureDecision RejectSignaturesAtIngestionOtherwiseWarnDecider(PackageSignature signature)
 {
     return((signature.Status == PackageSignatureStatus.Unknown)
             ? SignatureDecision.Reject
             : SignatureDecision.Warn);
 }
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);
            }