Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit ffed70e

Browse files
authored
Add integration tests for CertificateValidationMessageHandler (#341)
1 parent e60d1ae commit ffed70e

8 files changed

Lines changed: 407 additions & 52 deletions

File tree

src/Validation.PackageSigning.ValidateCertificate/Primitives.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ internal struct CERT_TRUST_STATUS
246246
[StructLayout(LayoutKind.Sequential)]
247247
internal unsafe struct CRL_ENTRY
248248
{
249-
public CRYPTOAPI_BLOB SerializeNumber;
249+
public CRYPTOAPI_BLOB SerialNumber;
250250
public FILETIME RevocationDate;
251251
public int cExtension;
252252
public IntPtr rgExtension;

src/Validation.PackageSigning.ValidateCertificate/SignatureDeciderFactory.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ private SignatureDecider MakeDeciderForRevokedCodeSigningCertificate(DateTime? r
107107
throw new InvalidOperationException($"Signature {signature.Key} has multiple trusted timestamps");
108108
}
109109

110+
// Revoked certificates invalidate all dependent signatures at ingestion.
111+
if (signature.Status == PackageSignatureStatus.Unknown)
112+
{
113+
return SignatureDecision.Reject;
114+
}
115+
110116
// The revoked code signing certificate invalidates signatures with no valid timestamps.
111117
if (signature.TrustedTimestamps.All(t => t.Status == TrustedTimestampStatus.Invalid))
112118
{

tests/Validation.PackageSigning.Core.Tests/Support/CertificateIntegrationTestFixture.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ public CertificateIntegrationTestFixture()
5252

5353
public Task<SigningTestServer> GetTestServerAsync() => _testServer.Value;
5454
public Task<Uri> GetTimestampServiceUrlAsync() => _timestampServiceUrl.Value;
55-
public Task<X509Certificate2> GetSigningCertificateAsync() => _signingCertificate.Value;
55+
56+
public async Task<X509Certificate2> GetSigningCertificateAsync()
57+
{
58+
return new X509Certificate2(await _signingCertificate.Value);
59+
}
60+
5661
public Task<string> GetSigningCertificateThumbprintAsync() => _signingCertificateThumbprint.Value;
5762

5863
protected Task<CertificateAuthority> GetRootCertificateAuthority() => _rootCertificateAuthority.Value;
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Security.Cryptography.X509Certificates;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using Microsoft.Extensions.Logging;
10+
using Moq;
11+
using NuGet.Jobs.Validation.PackageSigning.Messages;
12+
using NuGet.Jobs.Validation.PackageSigning.Storage;
13+
using NuGet.Services.Validation;
14+
using Validation.PackageSigning.Helpers;
15+
using Validation.PackageSigning.ValidateCertificate.Tests.Support;
16+
using Xunit;
17+
18+
namespace Validation.PackageSigning.ValidateCertificate.Tests
19+
{
20+
[Collection(CertificateIntegrationTestCollection.Name)]
21+
public class CertificateValidationMessageHandlerIntegrationTests
22+
{
23+
private static readonly DateTime RevocationTime = DateTime.UtcNow.AddDays(-4);
24+
private static readonly DateTime BeforeRevocationTime = RevocationTime.AddDays(-1);
25+
private static readonly DateTime AfterRevocationTime = RevocationTime.AddDays(1);
26+
27+
private readonly CertificateIntegrationTestFixture _fixture;
28+
29+
private readonly Mock<IValidationEntitiesContext> _context;
30+
private readonly Mock<ITelemetryService> _telemetryService;
31+
private readonly Mock<ICertificateStore> _certificateStore;
32+
33+
private readonly CertificateValidationMessageHandler _target;
34+
35+
public CertificateValidationMessageHandlerIntegrationTests(CertificateIntegrationTestFixture fixture)
36+
{
37+
_fixture = fixture;
38+
39+
_context = new Mock<IValidationEntitiesContext>();
40+
_telemetryService = new Mock<ITelemetryService>();
41+
_certificateStore = new Mock<ICertificateStore>();
42+
43+
var certificateValidationService = new CertificateValidationService(
44+
_context.Object,
45+
_telemetryService.Object,
46+
Mock.Of<ILogger<CertificateValidationService>>());
47+
48+
_target = new CertificateValidationMessageHandler(
49+
_certificateStore.Object,
50+
certificateValidationService,
51+
new OnlineCertificateVerifier(),
52+
Mock.Of<ILogger<CertificateValidationMessageHandler>>());
53+
}
54+
55+
[Theory]
56+
[MemberData(nameof(ValidateSigningCertificateData))]
57+
public async Task ValidateSigningCertificate(
58+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> createCertificateFunc,
59+
DateTime signatureTime,
60+
EndCertificateStatus expectedCertificateStatus,
61+
PackageSignatureStatus expectedStatusForSignatureAtIngestion,
62+
PackageSignatureStatus expectedStatusForSignatureInGracePeriod,
63+
PackageSignatureStatus expectedStatusForSignatureAfterGracePeriod)
64+
{
65+
// Arrange
66+
var certificate = await createCertificateFunc(_fixture);
67+
68+
var endCertificateKey = 123;
69+
var validationId = Guid.NewGuid();
70+
71+
var packageSigningState1 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid };
72+
var packageSigningState2 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid };
73+
var packageSigningState3 = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid };
74+
75+
var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown };
76+
var signatureInGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod };
77+
var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid };
78+
79+
var trustedTimestamp1 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime };
80+
var trustedTimestamp2 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime };
81+
var trustedTimestamp3 = new TrustedTimestamp { Status = TrustedTimestampStatus.Valid, Value = signatureTime };
82+
83+
var endCertificate = new EndCertificate
84+
{
85+
Key = endCertificateKey,
86+
Status = EndCertificateStatus.Unknown,
87+
Use = EndCertificateUse.CodeSigning,
88+
CertificateChainLinks = new CertificateChainLink[0],
89+
};
90+
91+
var validation = new EndCertificateValidation
92+
{
93+
EndCertificateKey = endCertificateKey,
94+
ValidationId = validationId,
95+
Status = null,
96+
EndCertificate = endCertificate
97+
};
98+
99+
signatureAtIngestion.PackageSigningState = packageSigningState1;
100+
signatureAtIngestion.EndCertificate = endCertificate;
101+
signatureAtIngestion.TrustedTimestamps = new[] { trustedTimestamp1 };
102+
signatureInGracePeriod.PackageSigningState = packageSigningState2;
103+
signatureInGracePeriod.EndCertificate = endCertificate;
104+
signatureInGracePeriod.TrustedTimestamps = new[] { trustedTimestamp2 };
105+
signatureAfterGracePeriod.PackageSigningState = packageSigningState3;
106+
signatureAfterGracePeriod.EndCertificate = endCertificate;
107+
signatureAfterGracePeriod.TrustedTimestamps = new[] { trustedTimestamp3 };
108+
109+
_context.Mock(
110+
packageSignatures: new[] { signatureAtIngestion, signatureInGracePeriod, signatureAfterGracePeriod },
111+
endCertificates: new[] { endCertificate },
112+
certificateValidations: new EndCertificateValidation[] { validation });
113+
114+
_certificateStore.Setup(s => s.LoadAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
115+
.Returns(Task.FromResult(certificate));
116+
117+
// Act
118+
await _target.HandleAsync(new CertificateValidationMessage(certificateKey: endCertificateKey, validationId: validationId));
119+
120+
// Assert
121+
Assert.Equal(expectedCertificateStatus, validation.Status);
122+
Assert.Equal(expectedCertificateStatus, endCertificate.Status);
123+
Assert.Equal(expectedStatusForSignatureAtIngestion, signatureAtIngestion.Status);
124+
Assert.Equal(expectedStatusForSignatureInGracePeriod, signatureInGracePeriod.Status);
125+
Assert.Equal(expectedStatusForSignatureAfterGracePeriod, signatureAfterGracePeriod.Status);
126+
127+
_context.Verify(c => c.SaveChangesAsync(), Times.Once);
128+
}
129+
130+
public static IEnumerable<object[]> ValidateSigningCertificateData()
131+
{
132+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> getGoodCert =
133+
(CertificateIntegrationTestFixture f) => f.GetSigningCertificateAsync();
134+
135+
yield return ValidateSigningCertificateArguments(
136+
createCertificateFunc: getGoodCert,
137+
signatureTime: DateTime.UtcNow,
138+
expectedCertificateStatus: EndCertificateStatus.Good,
139+
expectedStatusForSignatureAtIngestion: PackageSignatureStatus.Unknown,
140+
expectedStatusForSignatureInGracePeriod: PackageSignatureStatus.InGracePeriod,
141+
expectedStatusForSignatureAfterGracePeriod: PackageSignatureStatus.Valid);
142+
143+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> getRevokedParentCert =
144+
(CertificateIntegrationTestFixture f) => f.GetRevokedParentSigningCertificateAsync();
145+
146+
yield return ValidateSigningCertificateArguments(
147+
createCertificateFunc: getRevokedParentCert,
148+
signatureTime: DateTime.UtcNow,
149+
expectedCertificateStatus: EndCertificateStatus.Invalid,
150+
expectedStatusForSignatureAtIngestion: PackageSignatureStatus.Invalid,
151+
expectedStatusForSignatureInGracePeriod: PackageSignatureStatus.Invalid,
152+
expectedStatusForSignatureAfterGracePeriod: PackageSignatureStatus.Invalid);
153+
154+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> getWeakSignatureParentCert =
155+
(CertificateIntegrationTestFixture f) => f.GetWeakSignatureParentSigningCertificateAsync();
156+
157+
yield return ValidateSigningCertificateArguments(
158+
createCertificateFunc: getWeakSignatureParentCert,
159+
signatureTime: DateTime.UtcNow,
160+
expectedCertificateStatus: EndCertificateStatus.Invalid,
161+
expectedStatusForSignatureAtIngestion: PackageSignatureStatus.Invalid,
162+
expectedStatusForSignatureInGracePeriod: PackageSignatureStatus.InGracePeriod,
163+
expectedStatusForSignatureAfterGracePeriod: PackageSignatureStatus.Valid);
164+
165+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> getRevokedCert =
166+
(CertificateIntegrationTestFixture f) => f.GetRevokedSigningCertificateAsync(RevocationTime);
167+
168+
yield return ValidateSigningCertificateArguments(
169+
createCertificateFunc: getRevokedCert,
170+
signatureTime: AfterRevocationTime,
171+
expectedCertificateStatus: EndCertificateStatus.Revoked,
172+
expectedStatusForSignatureAtIngestion: PackageSignatureStatus.Invalid,
173+
expectedStatusForSignatureInGracePeriod: PackageSignatureStatus.Invalid,
174+
expectedStatusForSignatureAfterGracePeriod: PackageSignatureStatus.Invalid);
175+
176+
yield return ValidateSigningCertificateArguments(
177+
createCertificateFunc: getRevokedCert,
178+
signatureTime: BeforeRevocationTime,
179+
expectedCertificateStatus: EndCertificateStatus.Revoked,
180+
expectedStatusForSignatureAtIngestion: PackageSignatureStatus.Invalid,
181+
expectedStatusForSignatureInGracePeriod: PackageSignatureStatus.InGracePeriod,
182+
expectedStatusForSignatureAfterGracePeriod: PackageSignatureStatus.Valid);
183+
}
184+
185+
private static object[] ValidateSigningCertificateArguments(
186+
Func<CertificateIntegrationTestFixture, Task<X509Certificate2>> createCertificateFunc,
187+
DateTime signatureTime,
188+
EndCertificateStatus expectedCertificateStatus,
189+
PackageSignatureStatus expectedStatusForSignatureAtIngestion,
190+
PackageSignatureStatus expectedStatusForSignatureInGracePeriod,
191+
PackageSignatureStatus expectedStatusForSignatureAfterGracePeriod)
192+
{
193+
return new object[]
194+
{
195+
createCertificateFunc,
196+
signatureTime,
197+
expectedCertificateStatus,
198+
expectedStatusForSignatureAtIngestion,
199+
expectedStatusForSignatureInGracePeriod,
200+
expectedStatusForSignatureAfterGracePeriod,
201+
};
202+
}
203+
204+
[Fact]
205+
public async Task ValidateTimestampingCertificate()
206+
{
207+
// Arrange
208+
var certificate = await _fixture.GetRevokedTimestampingCertificateAsync(RevocationTime);
209+
210+
var endCertificateKey = 123;
211+
var validationId = Guid.NewGuid();
212+
213+
var packageSigningState = new PackageSigningState { SigningStatus = PackageSigningStatus.Valid };
214+
215+
var signatureAtIngestion = new PackageSignature { Status = PackageSignatureStatus.Unknown };
216+
var signatureInGracePeriod = new PackageSignature { Status = PackageSignatureStatus.InGracePeriod };
217+
var signatureAfterGracePeriod = new PackageSignature { Status = PackageSignatureStatus.Valid };
218+
219+
var endCertificate = new EndCertificate
220+
{
221+
Key = endCertificateKey,
222+
Status = EndCertificateStatus.Unknown,
223+
Use = EndCertificateUse.Timestamping,
224+
CertificateChainLinks = new CertificateChainLink[0],
225+
};
226+
227+
var trustedTimestamp = new TrustedTimestamp
228+
{
229+
EndCertificate = endCertificate,
230+
Status = TrustedTimestampStatus.Valid
231+
};
232+
233+
var validation = new EndCertificateValidation
234+
{
235+
EndCertificateKey = endCertificateKey,
236+
ValidationId = validationId,
237+
Status = null,
238+
EndCertificate = endCertificate
239+
};
240+
241+
signatureAtIngestion.PackageSigningState = packageSigningState;
242+
signatureAtIngestion.TrustedTimestamps = new[] { trustedTimestamp };
243+
signatureInGracePeriod.PackageSigningState = packageSigningState;
244+
signatureInGracePeriod.TrustedTimestamps = new[] { trustedTimestamp };
245+
signatureAfterGracePeriod.PackageSigningState = packageSigningState;
246+
signatureAfterGracePeriod.TrustedTimestamps = new[] { trustedTimestamp };
247+
248+
_context.Mock(
249+
packageSignatures: new[] { signatureAtIngestion, signatureInGracePeriod, signatureAfterGracePeriod },
250+
endCertificates: new[] { endCertificate },
251+
certificateValidations: new EndCertificateValidation[] { validation });
252+
253+
_certificateStore.Setup(s => s.LoadAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
254+
.Returns(Task.FromResult(certificate));
255+
256+
// Act
257+
await _target.HandleAsync(new CertificateValidationMessage(certificateKey: endCertificateKey, validationId: validationId));
258+
259+
// Assert
260+
Assert.Equal(EndCertificateStatus.Revoked, validation.Status);
261+
Assert.Equal(EndCertificateStatus.Revoked, endCertificate.Status);
262+
Assert.Equal(PackageSignatureStatus.Invalid, signatureAtIngestion.Status);
263+
Assert.Equal(PackageSignatureStatus.Invalid, signatureInGracePeriod.Status);
264+
Assert.Equal(PackageSignatureStatus.Invalid, signatureAfterGracePeriod.Status);
265+
266+
_context.Verify(c => c.SaveChangesAsync(), Times.Once);
267+
}
268+
}
269+
}

0 commit comments

Comments
 (0)