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

Commit 5a88f20

Browse files
committed
Persist signature and timestamp entities (#313)
Fix test Zip64 package so it's readable by the gallery upload flow Block end certificates that are used for both timestamping and signing Complete NuGet/NuGetGallery#5327 Progress on NuGet/Engineering#785
1 parent 5aa8150 commit 5a88f20

17 files changed

Lines changed: 525 additions & 69 deletions

File tree

src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,25 +131,25 @@
131131
<Version>1.1.2</Version>
132132
</PackageReference>
133133
<PackageReference Include="NuGet.Services.Configuration">
134-
<Version>2.10.0</Version>
134+
<Version>2.12.0</Version>
135135
</PackageReference>
136136
<PackageReference Include="NuGet.Services.Contracts">
137-
<Version>2.10.0</Version>
137+
<Version>2.12.0</Version>
138138
</PackageReference>
139139
<PackageReference Include="NuGet.Services.KeyVault">
140-
<Version>2.10.0</Version>
140+
<Version>2.12.0</Version>
141141
</PackageReference>
142142
<PackageReference Include="NuGet.Services.Logging">
143-
<Version>2.10.0</Version>
143+
<Version>2.12.0</Version>
144144
</PackageReference>
145145
<PackageReference Include="NuGet.Services.ServiceBus">
146-
<Version>2.10.0</Version>
146+
<Version>2.12.0</Version>
147147
</PackageReference>
148148
<PackageReference Include="NuGet.Services.Validation">
149-
<Version>2.10.0</Version>
149+
<Version>2.12.0</Version>
150150
</PackageReference>
151151
<PackageReference Include="NuGet.Services.Validation.Issues">
152-
<Version>2.10.0</Version>
152+
<Version>2.12.0</Version>
153153
</PackageReference>
154154
<PackageReference Include="NuGet.Versioning">
155155
<Version>4.3.0</Version>

src/Validation.Callback.Vcs/Web.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
</dependentAssembly>
7171
<dependentAssembly>
7272
<assemblyIdentity name="NuGet.Services.KeyVault" publicKeyToken="31bf3856ad364e35" culture="neutral" />
73-
<bindingRedirect oldVersion="0.0.0.0-2.10.0.0" newVersion="2.10.0.0" />
73+
<bindingRedirect oldVersion="0.0.0.0-2.12.0.0" newVersion="2.12.0.0" />
7474
</dependentAssembly>
7575
<dependentAssembly>
7676
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />

src/Validation.Common/Validation.Common.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@
105105
<Reference Include="NuGet.ApplicationInsights.Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
106106
<HintPath>..\..\packages\NuGet.ApplicationInsights.Owin.4.1.0\lib\net452\NuGet.ApplicationInsights.Owin.dll</HintPath>
107107
</Reference>
108-
<Reference Include="NuGet.Services.KeyVault, Version=2.10.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
109-
<HintPath>..\..\packages\NuGet.Services.KeyVault.2.10.0\lib\net45\NuGet.Services.KeyVault.dll</HintPath>
108+
<Reference Include="NuGet.Services.KeyVault, Version=2.12.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
109+
<HintPath>..\..\packages\NuGet.Services.KeyVault.2.12.0\lib\net45\NuGet.Services.KeyVault.dll</HintPath>
110110
</Reference>
111-
<Reference Include="NuGet.Services.Logging, Version=2.10.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
112-
<HintPath>..\..\packages\NuGet.Services.Logging.2.10.0\lib\net452\NuGet.Services.Logging.dll</HintPath>
111+
<Reference Include="NuGet.Services.Logging, Version=2.12.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
112+
<HintPath>..\..\packages\NuGet.Services.Logging.2.12.0\lib\net452\NuGet.Services.Logging.dll</HintPath>
113113
</Reference>
114114
<Reference Include="NuGet.Services.VirusScanning.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
115115
<HintPath>..\..\packages\NuGet.Services.VirusScanning.Vcs.3.2.0\lib\net452\NuGet.Services.VirusScanning.Core.dll</HintPath>

src/Validation.Common/app.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
55
<dependentAssembly>
66
<assemblyIdentity name="NuGet.Services.Logging" publicKeyToken="31BF3856AD364E35" culture="neutral" />
7-
<bindingRedirect oldVersion="0.0.0.0-2.10.0.0" newVersion="2.10.0.0" />
7+
<bindingRedirect oldVersion="0.0.0.0-2.12.0.0" newVersion="2.12.0.0" />
88
</dependentAssembly>
99
<dependentAssembly>
1010
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
@@ -40,7 +40,7 @@
4040
</dependentAssembly>
4141
<dependentAssembly>
4242
<assemblyIdentity name="NuGet.Services.KeyVault" publicKeyToken="31bf3856ad364e35" culture="neutral" />
43-
<bindingRedirect oldVersion="0.0.0.0-2.10.0.0" newVersion="2.10.0.0" />
43+
<bindingRedirect oldVersion="0.0.0.0-2.12.0.0" newVersion="2.12.0.0" />
4444
</dependentAssembly>
4545
<dependentAssembly>
4646
<assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />

src/Validation.Common/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.1" targetFramework="net452" />
2323
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
2424
<package id="NuGet.ApplicationInsights.Owin" version="4.1.0" targetFramework="net452" />
25-
<package id="NuGet.Services.KeyVault" version="2.10.0" targetFramework="net452" />
26-
<package id="NuGet.Services.Logging" version="2.10.0" targetFramework="net452" />
25+
<package id="NuGet.Services.KeyVault" version="2.12.0" targetFramework="net452" />
26+
<package id="NuGet.Services.Logging" version="2.12.0" targetFramework="net452" />
2727
<package id="NuGet.Services.VirusScanning.Vcs" version="3.2.0" targetFramework="net452" />
2828
<package id="Owin" version="1.0" targetFramework="net452" />
2929
<package id="Serilog" version="2.0.0" targetFramework="net452" />

src/Validation.PackageSigning.Core/Validation.PackageSigning.Core.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@
3939
</ItemGroup>
4040
<ItemGroup>
4141
<PackageReference Include="NuGet.Services.ServiceBus">
42-
<Version>2.10.0</Version>
42+
<Version>2.12.0</Version>
4343
</PackageReference>
4444
<PackageReference Include="NuGet.Services.Storage">
45-
<Version>2.10.0</Version>
45+
<Version>2.12.0</Version>
4646
</PackageReference>
4747
<PackageReference Include="NuGet.Services.Validation">
48-
<Version>2.10.0</Version>
48+
<Version>2.12.0</Version>
4949
</PackageReference>
5050
<PackageReference Include="WindowsAzure.Storage">
5151
<Version>7.1.2</Version>

src/Validation.PackageSigning.ExtractAndValidateSignature/ISignaturePartsExtractor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ public interface ISignaturePartsExtractor
1717
/// Extracts and persists artifacts from the provided signed package.
1818
/// </summary>
1919
/// <exception cref="ArgumentException">Thrown if the provided package is not signed.</exception>
20+
/// <param name="packageKey">The key of the package in the gallery database.</param>
2021
/// <param name="signedPackageReader">The reader of the signed package.</param>
2122
/// <param name="token">The cancellation token.</param>
22-
Task ExtractAsync(ISignedPackageReader signedPackageReader, CancellationToken token);
23+
Task ExtractAsync(int packageKey, ISignedPackageReader signedPackageReader, CancellationToken token);
2324
}
2425
}

src/Validation.PackageSigning.ExtractAndValidateSignature/SignaturePartsExtractor.cs

Lines changed: 178 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Threading;
99
using System.Threading.Tasks;
10+
using Microsoft.Extensions.Logging;
1011
using NuGet.Jobs.Validation.PackageSigning.Storage;
1112
using NuGet.Packaging.Signing;
1213
using NuGet.Services.Validation;
@@ -17,16 +18,19 @@ public class SignaturePartsExtractor : ISignaturePartsExtractor
1718
{
1819
private readonly ICertificateStore _certificateStore;
1920
private readonly IValidationEntitiesContext _entitiesContext;
21+
private readonly ILogger<SignaturePartsExtractor> _logger;
2022

2123
public SignaturePartsExtractor(
2224
ICertificateStore certificateStore,
23-
IValidationEntitiesContext entitiesContext)
25+
IValidationEntitiesContext entitiesContext,
26+
ILogger<SignaturePartsExtractor> logger)
2427
{
2528
_certificateStore = certificateStore ?? throw new ArgumentNullException(nameof(certificateStore));
2629
_entitiesContext = entitiesContext ?? throw new ArgumentNullException(nameof(entitiesContext));
30+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
2731
}
2832

29-
public async Task ExtractAsync(ISignedPackageReader signedPackageReader, CancellationToken token)
33+
public async Task ExtractAsync(int packageKey, ISignedPackageReader signedPackageReader, CancellationToken token)
3034
{
3135
if (!await signedPackageReader.IsSignedAsync(token))
3236
{
@@ -39,11 +43,14 @@ public async Task ExtractAsync(ISignedPackageReader signedPackageReader, Cancell
3943
// Extract the certificates found in the package signatures.
4044
var extractedCertificates = ExtractCertificates(signature);
4145

46+
// Prepare signature entities for the database (does not commit).
47+
await SaveSignatureToDatabaseAsync(packageKey, signature, extractedCertificates);
48+
4249
// Save the certificates to blob storage.
4350
await SaveCertificatesToStoreAsync(extractedCertificates, token);
4451

45-
// Save the certificates to the database.
46-
await SaveCertificatesToDatabaseAsync(extractedCertificates);
52+
// Commit the database changes.
53+
await _entitiesContext.SaveChangesAsync();
4754
}
4855

4956
private ExtractedCertificates ExtractCertificates(Signature signature)
@@ -90,14 +97,14 @@ private ExtractedCertificates ExtractCertificates(Signature signature)
9097
timestampParentCertificates);
9198
}
9299

93-
private async Task SaveCertificatesToDatabaseAsync(ExtractedCertificates extractedCertificates)
100+
private async Task SaveSignatureToDatabaseAsync(int packageKey, Signature signature, ExtractedCertificates extractedCertificates)
94101
{
95102
// Initialize the end and parent certificates.
96103
var thumbprintToEndCertificate = await InitializeEndCertificatesAsync(
97104
new[]
98105
{
99-
extractedCertificates.SignatureEndCertificate,
100-
extractedCertificates.TimestampEndCertificate
106+
new CertificateAndUse(extractedCertificates.SignatureEndCertificate, EndCertificateUse.CodeSigning),
107+
new CertificateAndUse(extractedCertificates.TimestampEndCertificate, EndCertificateUse.Timestamping),
101108
});
102109

103110
var thumbprintToParentCertificate = await InitializeParentCertificatesAsync(
@@ -118,8 +125,140 @@ private async Task SaveCertificatesToDatabaseAsync(ExtractedCertificates extract
118125
thumbprintToEndCertificate,
119126
thumbprintToParentCertificate);
120127

121-
// Commit
122-
await _entitiesContext.SaveChangesAsync();
128+
// Initialize the package signature record.
129+
var packageSignature = await InitializePackageSignatureAsync(
130+
packageKey,
131+
extractedCertificates.SignatureEndCertificate,
132+
thumbprintToEndCertificate);
133+
134+
// Initialize the trusted timestamp record.
135+
InitializeTrustedTimestamp(
136+
packageSignature,
137+
signature,
138+
extractedCertificates.TimestampEndCertificate,
139+
thumbprintToEndCertificate);
140+
}
141+
142+
public async Task<PackageSignature> InitializePackageSignatureAsync(
143+
int packageKey,
144+
HashedCertificate signatureEndCertificate,
145+
IReadOnlyDictionary<string, EndCertificate> thumbprintToEndCertificate)
146+
{
147+
var packageSignatures = await _entitiesContext
148+
.PackageSignatures
149+
.Include(x => x.TrustedTimestamps)
150+
.Include(x => x.EndCertificate)
151+
.Where(x => x.PackageKey == packageKey)
152+
.ToListAsync();
153+
154+
if (packageSignatures.Count > 1)
155+
{
156+
_logger.LogError(
157+
"There are {Count} package signatures for package key {PackageKey}. There should be either zero or one.",
158+
packageSignatures.Count,
159+
packageKey);
160+
161+
throw new InvalidOperationException("There should never be more than one package signature per package.");
162+
}
163+
164+
PackageSignature packageSignature;
165+
if (packageSignatures.Count == 0)
166+
{
167+
packageSignature = new PackageSignature
168+
{
169+
CreatedAt = DateTime.UtcNow,
170+
EndCertificate = thumbprintToEndCertificate[signatureEndCertificate.Thumbprint],
171+
PackageKey = packageKey,
172+
Status = PackageSignatureStatus.Unknown,
173+
TrustedTimestamps = new List<TrustedTimestamp>(),
174+
};
175+
_entitiesContext.PackageSignatures.Add(packageSignature);
176+
177+
packageSignature.EndCertificateKey = packageSignature.EndCertificate.Key;
178+
}
179+
else
180+
{
181+
packageSignature = packageSignatures.Single();
182+
183+
if (packageSignature.EndCertificate.Thumbprint != signatureEndCertificate.Thumbprint)
184+
{
185+
_logger.LogError(
186+
"The signature end certificate thumbprint cannot change for package {PackageKey}. The " +
187+
"existing signature end certificate is {ExistingThumbprint}. The new thumprint is " +
188+
"{NewThumbprint}.",
189+
packageKey,
190+
packageSignature.EndCertificate.Thumbprint,
191+
signatureEndCertificate.Thumbprint);
192+
193+
throw new InvalidOperationException("The thumbprint of the signature end certificate cannot change.");
194+
}
195+
}
196+
197+
return packageSignature;
198+
}
199+
200+
private void InitializeTrustedTimestamp(
201+
PackageSignature packageSignature,
202+
Signature signature,
203+
HashedCertificate timestampEndCertificate,
204+
IReadOnlyDictionary<string, EndCertificate> thumbprintToEndCertificate)
205+
{
206+
if (packageSignature.TrustedTimestamps.Count > 1)
207+
{
208+
_logger.LogError(
209+
"There are {Count} trusted timestamps for signature on package {PackageKey}. There should be either zero or one.",
210+
packageSignature.TrustedTimestamps.Count,
211+
packageSignature.PackageKey);
212+
213+
throw new InvalidOperationException("There should never be more than one trusted timestamp per package signature.");
214+
}
215+
216+
// Determine the value of the timestamp.
217+
var value = signature.Timestamps.Single().UpperLimit.UtcDateTime;
218+
219+
TrustedTimestamp trustedTimestamp;
220+
if (packageSignature.TrustedTimestamps.Count == 0)
221+
{
222+
trustedTimestamp = new TrustedTimestamp
223+
{
224+
PackageSignature = packageSignature,
225+
PackageSignatureKey = packageSignature.Key,
226+
EndCertificate = thumbprintToEndCertificate[timestampEndCertificate.Thumbprint],
227+
Value = value,
228+
};
229+
trustedTimestamp.EndCertificateKey = trustedTimestamp.EndCertificate.Key;
230+
packageSignature.TrustedTimestamps.Add(trustedTimestamp);
231+
_entitiesContext.TrustedTimestamps.Add(trustedTimestamp);
232+
}
233+
else
234+
{
235+
trustedTimestamp = packageSignature.TrustedTimestamps.Single();
236+
237+
if (trustedTimestamp.EndCertificate.Thumbprint != timestampEndCertificate.Thumbprint)
238+
{
239+
_logger.LogError(
240+
"The timestamp end certificate thumbprint cannot change for package {PackageKey}. The " +
241+
"existing timestamp end certificate is {ExistingThumbprint}. The new thumprint is " +
242+
"{NewThumbprint}.",
243+
packageSignature.PackageKey,
244+
packageSignature.EndCertificate.Thumbprint,
245+
timestampEndCertificate.Thumbprint);
246+
247+
throw new InvalidOperationException("The thumbprint of the timestamp end certificate cannot change.");
248+
}
249+
250+
if (trustedTimestamp.Value != value)
251+
{
252+
_logger.LogError(
253+
"The trusted timestamp value cannot change for package {PackageKey}. The existing timestamp " +
254+
"value is {ExistingValue}. The new value is {NewValue}.",
255+
packageSignature.PackageKey,
256+
trustedTimestamp.Value,
257+
value);
258+
259+
throw new InvalidOperationException("The value of the trusted timestamp cannot change.");
260+
}
261+
}
123262
}
124263

125264
private void ConnectCertificates(
@@ -160,10 +299,10 @@ private void ConnectCertificates(
160299
}
161300

162301
private async Task<IReadOnlyDictionary<string, EndCertificate>> InitializeEndCertificatesAsync(
163-
IEnumerable<HashedCertificate> certificates)
302+
IEnumerable<CertificateAndUse> certificatesAndUses)
164303
{
165-
var thumbprints = certificates
166-
.Select(x => x.Thumbprint)
304+
var thumbprints = certificatesAndUses
305+
.Select(x => x.Certificate.Thumbprint)
167306
.Distinct()
168307
.ToList();
169308

@@ -177,19 +316,30 @@ private async Task<IReadOnlyDictionary<string, EndCertificate>> InitializeEndCer
177316

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

180-
foreach (var certificate in certificates)
319+
foreach (var certificateAndUse in certificatesAndUses)
181320
{
182-
if (!thumbprintToEntity.TryGetValue(certificate.Thumbprint, out var entity))
321+
if (!thumbprintToEntity.TryGetValue(certificateAndUse.Certificate.Thumbprint, out var entity))
183322
{
184323
entity = new EndCertificate
185324
{
186325
Status = EndCertificateStatus.Unknown,
187-
Thumbprint = certificate.Thumbprint,
326+
Use = certificateAndUse.Use,
327+
Thumbprint = certificateAndUse.Certificate.Thumbprint,
188328
CertificateChainLinks = new List<CertificateChainLink>(),
189329
};
190330
_entitiesContext.EndCertificates.Add(entity);
191331

192-
thumbprintToEntity[certificate.Thumbprint] = entity;
332+
thumbprintToEntity[certificateAndUse.Certificate.Thumbprint] = entity;
333+
}
334+
else if (entity.Use != certificateAndUse.Use)
335+
{
336+
_logger.LogError(
337+
"The use of end certificate {Thumbprint} cannot change. The existing use is {ExistingUse}. The new use is {NewUse}.",
338+
certificateAndUse.Certificate.Thumbprint,
339+
entity.Use,
340+
certificateAndUse.Use);
341+
342+
throw new InvalidOperationException("The use of an end certificate cannot change.");
193343
}
194344
}
195345

@@ -256,5 +406,17 @@ private async Task SaveCertificateToStoreAsync(HashedCertificate certificate, Ca
256406

257407
await _certificateStore.SaveAsync(certificate.Certificate, token);
258408
}
409+
410+
private class CertificateAndUse
411+
{
412+
public CertificateAndUse(HashedCertificate hashedCertificate, EndCertificateUse endCertificateUse)
413+
{
414+
Certificate = hashedCertificate;
415+
Use = endCertificateUse;
416+
}
417+
418+
public HashedCertificate Certificate { get; }
419+
public EndCertificateUse Use { get; }
420+
}
259421
}
260422
}

0 commit comments

Comments
 (0)