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

@@ -522,6 +522,71 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task AdvisoryChunksEndpoint_EmitsRequestAndCacheMetrics()
{
await SeedObservationDocumentsAsync(BuildSampleObservationDocuments());
using var client = _factory.CreateClient();
var metrics = await CaptureMetricsAsync(
AdvisoryAiMetrics.MeterName,
new[] { "advisory_ai_chunk_requests_total", "advisory_ai_chunk_cache_hits_total" },
async () =>
{
const string url = "/advisories/CVE-2025-0001/chunks?tenant=tenant-a";
var first = await client.GetAsync(url);
first.EnsureSuccessStatusCode();
var second = await client.GetAsync(url);
second.EnsureSuccessStatusCode();
});
Assert.True(metrics.TryGetValue("advisory_ai_chunk_requests_total", out var requests));
Assert.NotNull(requests);
Assert.Equal(2, requests!.Count);
Assert.Contains(requests!, measurement =>
string.Equals(GetTagValue(measurement, "cache"), "miss", StringComparison.Ordinal));
Assert.Contains(requests!, measurement =>
string.Equals(GetTagValue(measurement, "cache"), "hit", StringComparison.Ordinal));
Assert.True(metrics.TryGetValue("advisory_ai_chunk_cache_hits_total", out var cacheHitMeasurements));
var cacheHit = Assert.Single(cacheHitMeasurements!);
Assert.Equal(1, cacheHit.Value);
Assert.Equal("hit", GetTagValue(cacheHit, "result"));
}
[Fact]
public async Task AdvisoryChunksEndpoint_EmitsGuardrailMetrics()
{
var raw = BsonDocument.Parse("{\"details\":\"tiny\"}");
var document = CreateChunkObservationDocument(
"tenant-a:chunk:1",
"tenant-a",
new DateTime(2025, 2, 1, 0, 0, 0, DateTimeKind.Utc),
"CVE-2025-GUARD",
raw);
await SeedObservationDocumentsAsync(new[] { document });
using var client = _factory.CreateClient();
var guardrailMetrics = await CaptureMetricsAsync(
AdvisoryAiMetrics.MeterName,
"advisory_ai_guardrail_blocks_total",
async () =>
{
var response = await client.GetAsync("/advisories/CVE-2025-GUARD/chunks?tenant=tenant-a");
response.EnsureSuccessStatusCode();
});
var measurement = Assert.Single(guardrailMetrics);
Assert.True(measurement.Value >= 1);
Assert.Equal("below_minimum_length", GetTagValue(measurement, "reason"));
}
[Fact]
public async Task AdvisoryIngestEndpoint_EmitsMetricsWithExpectedTags()
{
@@ -2069,13 +2134,28 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private static async Task<IReadOnlyList<MetricMeasurement>> CaptureMetricsAsync(string meterName, string instrumentName, Func<Task> action)
{
var measurements = new List<MetricMeasurement>();
var map = await CaptureMetricsAsync(meterName, new[] { instrumentName }, action).ConfigureAwait(false);
return map.TryGetValue(instrumentName, out var measurements)
? measurements
: Array.Empty<MetricMeasurement>();
}
private static async Task<Dictionary<string, IReadOnlyList<MetricMeasurement>>> CaptureMetricsAsync(
string meterName,
IReadOnlyCollection<string> instrumentNames,
Func<Task> action)
{
var measurementMap = instrumentNames.ToDictionary(
name => name,
_ => new List<MetricMeasurement>(),
StringComparer.Ordinal);
var instrumentSet = new HashSet<string>(instrumentNames, StringComparer.Ordinal);
var listener = new MeterListener();
listener.InstrumentPublished += (instrument, currentListener) =>
{
if (string.Equals(instrument.Meter.Name, meterName, StringComparison.Ordinal) &&
string.Equals(instrument.Name, instrumentName, StringComparison.Ordinal))
instrumentSet.Contains(instrument.Name))
{
currentListener.EnableMeasurementEvents(instrument);
}
@@ -2083,13 +2163,18 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
if (!measurementMap.TryGetValue(instrument.Name, out var list))
{
return;
}
var tagDictionary = new Dictionary<string, object?>(StringComparer.Ordinal);
foreach (var tag in tags)
{
tagDictionary[tag.Key] = tag.Value;
}
measurements.Add(new MetricMeasurement(instrument.Name, measurement, tagDictionary));
list.Add(new MetricMeasurement(instrument.Name, measurement, tagDictionary));
});
listener.Start();
@@ -2102,7 +2187,9 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
listener.Dispose();
}
return measurements;
return measurementMap.ToDictionary(
kvp => kvp.Key,
kvp => (IReadOnlyList<MetricMeasurement>)kvp.Value);
}
private static string? GetTagValue(MetricMeasurement measurement, string tag)