Add support for ГОСТ Р 34.10 digital signatures
- Implemented the GostKeyValue class for handling public key parameters in ГОСТ Р 34.10 digital signatures. - Created the GostSignedXml class to manage XML signatures using ГОСТ 34.10, including methods for computing and checking signatures. - Developed the GostSignedXmlImpl class to encapsulate the signature computation logic and public key retrieval. - Added specific key value classes for ГОСТ Р 34.10-2001, ГОСТ Р 34.10-2012/256, and ГОСТ Р 34.10-2012/512 to support different signature algorithms. - Ensured compatibility with existing XML signature standards while integrating ГОСТ cryptography.
This commit is contained in:
@@ -30,14 +30,25 @@ public sealed class RancherHubConnectorTests
|
||||
var sink = new InMemoryRawSink();
|
||||
var context = fixture.CreateContext(sink);
|
||||
|
||||
var documents = await CollectAsync(fixture.Connector.FetchAsync(context, CancellationToken.None));
|
||||
|
||||
documents.Should().HaveCount(1);
|
||||
var document = documents[0];
|
||||
document.Digest.Should().Be(fixture.ExpectedDocumentDigest);
|
||||
document.Metadata.Should().ContainKey("rancher.event.id").WhoseValue.Should().Be("evt-1");
|
||||
document.Metadata.Should().ContainKey("rancher.event.cursor").WhoseValue.Should().Be("cursor-2");
|
||||
sink.Documents.Should().HaveCount(1);
|
||||
var documents = await CollectAsync(fixture.Connector.FetchAsync(context, CancellationToken.None));
|
||||
|
||||
documents.Should().HaveCount(1);
|
||||
var document = documents[0];
|
||||
document.Digest.Should().Be(fixture.ExpectedDocumentDigest);
|
||||
document.Metadata.Should().ContainKey("rancher.event.id").WhoseValue.Should().Be("evt-1");
|
||||
document.Metadata.Should().ContainKey("rancher.event.cursor").WhoseValue.Should().Be("cursor-2");
|
||||
document.Metadata.Should().Contain("vex.provenance.provider", "excititor:suse.rancher");
|
||||
document.Metadata.Should().Contain("vex.provenance.providerName", "SUSE Rancher VEX Hub");
|
||||
document.Metadata.Should().Contain("vex.provenance.providerKind", "hub");
|
||||
document.Metadata.Should().Contain("vex.provenance.trust.weight", "0.42");
|
||||
document.Metadata.Should().Contain("vex.provenance.trust.tier", "hub");
|
||||
document.Metadata.Should().Contain("vex.provenance.trust.note", "tier=hub;weight=0.42");
|
||||
document.Metadata.Should().Contain("vex.provenance.cosign.issuer", "https://issuer.testsuse.example");
|
||||
document.Metadata.Should().Contain("vex.provenance.cosign.identityPattern", "spiffe://rancher-vex/*");
|
||||
document.Metadata.Should().Contain(
|
||||
"vex.provenance.pgp.fingerprints",
|
||||
"11223344556677889900AABBCCDDEEFF00112233,AABBCCDDEEFF00112233445566778899AABBCCDD");
|
||||
sink.Documents.Should().HaveCount(1);
|
||||
|
||||
var state = fixture.StateRepository.State;
|
||||
state.Should().NotBeNull();
|
||||
@@ -60,12 +71,15 @@ public sealed class RancherHubConnectorTests
|
||||
var documents = await CollectAsync(fixture.Connector.FetchAsync(context, CancellationToken.None));
|
||||
|
||||
documents.Should().BeEmpty();
|
||||
sink.Documents.Should().HaveCount(1);
|
||||
var quarantined = sink.Documents[0];
|
||||
quarantined.Metadata.Should().Contain("rancher.event.quarantine", "true");
|
||||
quarantined.Metadata.Should().ContainKey("rancher.event.error").WhoseValue.Should().Contain("document fetch failed");
|
||||
|
||||
var state = fixture.StateRepository.State;
|
||||
sink.Documents.Should().HaveCount(1);
|
||||
var quarantined = sink.Documents[0];
|
||||
quarantined.Metadata.Should().Contain("rancher.event.quarantine", "true");
|
||||
quarantined.Metadata.Should().ContainKey("rancher.event.error").WhoseValue.Should().Contain("document fetch failed");
|
||||
quarantined.Metadata.Should().Contain("vex.provenance.provider", "excititor:suse.rancher");
|
||||
quarantined.Metadata.Should().Contain("vex.provenance.trust.weight", "0.42");
|
||||
quarantined.Metadata.Should().Contain("vex.provenance.trust.tier", "hub");
|
||||
|
||||
var state = fixture.StateRepository.State;
|
||||
state.Should().NotBeNull();
|
||||
state!.DocumentDigests.Should().Contain(d => d.StartsWith("quarantine:", StringComparison.Ordinal));
|
||||
}
|
||||
@@ -265,11 +279,16 @@ public sealed class RancherHubConnectorTests
|
||||
TimeProvider.System,
|
||||
validators);
|
||||
|
||||
var settingsValues = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
settingsValues["DiscoveryUri"] = "https://hub.test/.well-known/rancher-hub.json";
|
||||
settingsValues["OfflineSnapshotPath"] = discoveryPath;
|
||||
settingsValues["PreferOfflineSnapshot"] = "true";
|
||||
var settings = new VexConnectorSettings(settingsValues.ToImmutable());
|
||||
var settingsValues = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
settingsValues["DiscoveryUri"] = "https://hub.test/.well-known/rancher-hub.json";
|
||||
settingsValues["OfflineSnapshotPath"] = discoveryPath;
|
||||
settingsValues["PreferOfflineSnapshot"] = "true";
|
||||
settingsValues["TrustWeight"] = "0.42";
|
||||
settingsValues["CosignIssuer"] = "https://issuer.testsuse.example";
|
||||
settingsValues["CosignIdentityPattern"] = "spiffe://rancher-vex/*";
|
||||
settingsValues["PgpFingerprints:0"] = "AABBCCDDEEFF00112233445566778899AABBCCDD";
|
||||
settingsValues["PgpFingerprints:1"] = "11223344556677889900AABBCCDDEEFF00112233";
|
||||
var settings = new VexConnectorSettings(settingsValues.ToImmutable());
|
||||
await connector.ValidateAsync(settings, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var services = new ServiceCollection().BuildServiceProvider();
|
||||
|
||||
@@ -57,11 +57,21 @@ public sealed class UbuntuCsafConnectorTests
|
||||
NullLogger<UbuntuCsafConnector>.Instance,
|
||||
TimeProvider.System);
|
||||
|
||||
var settings = new VexConnectorSettings(ImmutableDictionary<string, string>.Empty);
|
||||
await connector.ValidateAsync(settings, CancellationToken.None);
|
||||
|
||||
var sink = new InMemoryRawSink();
|
||||
var context = new VexConnectorContext(null, VexConnectorSettings.Empty, sink, new NoopSignatureVerifier(), new NoopNormalizerRouter(), new ServiceCollection().BuildServiceProvider(), ImmutableDictionary<string, string>.Empty);
|
||||
var settings = BuildConnectorSettings(indexUri, trustWeight: 0.63, trustTier: "distro-trusted",
|
||||
fingerprints: new[]
|
||||
{
|
||||
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
});
|
||||
await connector.ValidateAsync(settings, CancellationToken.None);
|
||||
|
||||
var providerStore = new InMemoryProviderStore();
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IVexProviderStore>(providerStore)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var sink = new InMemoryRawSink();
|
||||
var context = new VexConnectorContext(null, VexConnectorSettings.Empty, sink, new NoopSignatureVerifier(), new NoopNormalizerRouter(), services, ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var documents = new List<VexRawDocument>();
|
||||
await foreach (var doc in connector.FetchAsync(context, CancellationToken.None))
|
||||
@@ -71,10 +81,18 @@ public sealed class UbuntuCsafConnectorTests
|
||||
|
||||
documents.Should().HaveCount(1);
|
||||
sink.Documents.Should().HaveCount(1);
|
||||
var stored = sink.Documents.Single();
|
||||
stored.Digest.Should().Be($"sha256:{documentSha}");
|
||||
stored.Metadata.TryGetValue("ubuntu.etag", out var storedEtag).Should().BeTrue();
|
||||
storedEtag.Should().Be("etag-123");
|
||||
var stored = sink.Documents.Single();
|
||||
stored.Digest.Should().Be($"sha256:{documentSha}");
|
||||
stored.Metadata.Should().Contain("ubuntu.etag", "etag-123");
|
||||
stored.Metadata.Should().Contain("vex.provenance.provider", "excititor:ubuntu");
|
||||
stored.Metadata.Should().Contain("vex.provenance.providerName", "Ubuntu CSAF");
|
||||
stored.Metadata.Should().Contain("vex.provenance.providerKind", "distro");
|
||||
stored.Metadata.Should().Contain("vex.provenance.trust.weight", "0.63");
|
||||
stored.Metadata.Should().Contain("vex.provenance.trust.tier", "distro-trusted");
|
||||
stored.Metadata.Should().Contain("vex.provenance.trust.note", "tier=distro-trusted;weight=0.63");
|
||||
stored.Metadata.Should().Contain(
|
||||
"vex.provenance.pgp.fingerprints",
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
|
||||
|
||||
stateRepository.CurrentState.Should().NotBeNull();
|
||||
stateRepository.CurrentState!.DocumentDigests.Should().Contain($"sha256:{documentSha}");
|
||||
@@ -94,8 +112,17 @@ public sealed class UbuntuCsafConnectorTests
|
||||
|
||||
documents.Should().BeEmpty();
|
||||
sink.Documents.Should().BeEmpty();
|
||||
handler.DocumentRequestCount.Should().Be(2);
|
||||
handler.SeenIfNoneMatch.Should().Contain("\"etag-123\"");
|
||||
handler.DocumentRequestCount.Should().Be(2);
|
||||
handler.SeenIfNoneMatch.Should().Contain("\"etag-123\"");
|
||||
|
||||
providerStore.SavedProviders.Should().ContainSingle();
|
||||
var savedProvider = providerStore.SavedProviders.Single();
|
||||
savedProvider.Trust.Weight.Should().Be(0.63);
|
||||
savedProvider.Trust.PgpFingerprints.Should().Contain(new[]
|
||||
{
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -127,10 +154,16 @@ public sealed class UbuntuCsafConnectorTests
|
||||
NullLogger<UbuntuCsafConnector>.Instance,
|
||||
TimeProvider.System);
|
||||
|
||||
await connector.ValidateAsync(new VexConnectorSettings(ImmutableDictionary<string, string>.Empty), CancellationToken.None);
|
||||
|
||||
var sink = new InMemoryRawSink();
|
||||
var context = new VexConnectorContext(null, VexConnectorSettings.Empty, sink, new NoopSignatureVerifier(), new NoopNormalizerRouter(), new ServiceCollection().BuildServiceProvider(), ImmutableDictionary<string, string>.Empty);
|
||||
var settings = BuildConnectorSettings(indexUri);
|
||||
await connector.ValidateAsync(settings, CancellationToken.None);
|
||||
|
||||
var providerStore = new InMemoryProviderStore();
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton<IVexProviderStore>(providerStore)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var sink = new InMemoryRawSink();
|
||||
var context = new VexConnectorContext(null, VexConnectorSettings.Empty, sink, new NoopSignatureVerifier(), new NoopNormalizerRouter(), services, ImmutableDictionary<string, string>.Empty);
|
||||
|
||||
var documents = new List<VexRawDocument>();
|
||||
await foreach (var doc in connector.FetchAsync(context, CancellationToken.None))
|
||||
@@ -142,9 +175,29 @@ public sealed class UbuntuCsafConnectorTests
|
||||
sink.Documents.Should().BeEmpty();
|
||||
stateRepository.CurrentState.Should().NotBeNull();
|
||||
stateRepository.CurrentState!.DocumentDigests.Should().BeEmpty();
|
||||
handler.DocumentRequestCount.Should().Be(1);
|
||||
}
|
||||
|
||||
handler.DocumentRequestCount.Should().Be(1);
|
||||
providerStore.SavedProviders.Should().ContainSingle();
|
||||
}
|
||||
|
||||
private static VexConnectorSettings BuildConnectorSettings(Uri indexUri, double trustWeight = 0.75, string trustTier = "distro", string[]? fingerprints = null)
|
||||
{
|
||||
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
builder["IndexUri"] = indexUri.ToString();
|
||||
builder["Channels:0"] = "stable";
|
||||
builder["TrustWeight"] = trustWeight.ToString(CultureInfo.InvariantCulture);
|
||||
builder["TrustTier"] = trustTier;
|
||||
|
||||
if (fingerprints is not null)
|
||||
{
|
||||
for (var i = 0; i < fingerprints.Length; i++)
|
||||
{
|
||||
builder[$"PgpFingerprints:{i}"] = fingerprints[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new VexConnectorSettings(builder.ToImmutable());
|
||||
}
|
||||
|
||||
private static (string IndexJson, string CatalogJson) CreateTestManifest(Uri advisoryUri, string advisoryId, string timestamp)
|
||||
{
|
||||
var indexJson = """
|
||||
@@ -285,16 +338,42 @@ public sealed class UbuntuCsafConnectorTests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InMemoryRawSink : IVexRawDocumentSink
|
||||
{
|
||||
public List<VexRawDocument> Documents { get; } = new();
|
||||
|
||||
public ValueTask StoreAsync(VexRawDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
Documents.Add(document);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
private sealed class InMemoryRawSink : IVexRawDocumentSink
|
||||
{
|
||||
public List<VexRawDocument> Documents { get; } = new();
|
||||
|
||||
public ValueTask StoreAsync(VexRawDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
Documents.Add(document);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class InMemoryProviderStore : IVexProviderStore
|
||||
{
|
||||
public List<VexProvider> SavedProviders { get; } = new();
|
||||
|
||||
public ValueTask<VexProvider?> FindAsync(string id, CancellationToken cancellationToken, IClientSessionHandle? session = null)
|
||||
=> ValueTask.FromResult(SavedProviders.LastOrDefault(provider => provider.Id == id));
|
||||
|
||||
public ValueTask<IReadOnlyCollection<VexProvider>> ListAsync(CancellationToken cancellationToken, IClientSessionHandle? session = null)
|
||||
=> ValueTask.FromResult<IReadOnlyCollection<VexProvider>>(SavedProviders.ToList());
|
||||
|
||||
public ValueTask SaveAsync(VexProvider provider, CancellationToken cancellationToken, IClientSessionHandle? session = null)
|
||||
{
|
||||
var existingIndex = SavedProviders.FindIndex(p => p.Id == provider.Id);
|
||||
if (existingIndex >= 0)
|
||||
{
|
||||
SavedProviders[existingIndex] = provider;
|
||||
}
|
||||
else
|
||||
{
|
||||
SavedProviders.Add(provider);
|
||||
}
|
||||
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NoopSignatureVerifier : IVexSignatureVerifier
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user