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:
master
2025-11-09 21:59:57 +02:00
parent 75c2bcafce
commit cef4cb2c5a
486 changed files with 32952 additions and 801 deletions

View File

@@ -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();

View File

@@ -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
{