audit, advisories and doctors/setup work

This commit is contained in:
master
2026-01-13 18:53:39 +02:00
parent 9ca7cb183e
commit d7be6ba34b
811 changed files with 54242 additions and 4056 deletions

View File

@@ -126,3 +126,5 @@ public record KeysResponse(
[property: JsonPropertyName("public_key_b64")] string PublicKeyBase64,
[property: JsonPropertyName("curve")] string Curve,
[property: JsonPropertyName("simulated_providers")] IEnumerable<string> Providers);
public partial class Program { }

View File

@@ -7,4 +7,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssetTargetFallback></AssetTargetFallback>
</PropertyGroup>
<ItemGroup>
<Compile Remove="__Tests/**" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1 @@
global using Xunit;

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>true</IsTestProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SimCryptoService.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,68 @@
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
namespace SimCryptoService.Tests;
public sealed class SimCryptoServiceTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public SimCryptoServiceTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task SignThenVerify_ReturnsOk()
{
using var client = _factory.CreateClient();
var signResponse = await client.PostAsJsonAsync("/sign", new SignRequest("hello", "SM2"));
signResponse.IsSuccessStatusCode.Should().BeTrue();
var signPayload = await signResponse.Content.ReadFromJsonAsync<SignResponse>();
signPayload.Should().NotBeNull();
signPayload!.SignatureBase64.Should().NotBeNullOrWhiteSpace();
var verifyResponse = await client.PostAsJsonAsync("/verify", new VerifyRequest("hello", signPayload.SignatureBase64, "SM2"));
verifyResponse.IsSuccessStatusCode.Should().BeTrue();
var verifyPayload = await verifyResponse.Content.ReadFromJsonAsync<VerifyResponse>();
verifyPayload.Should().NotBeNull();
verifyPayload!.Ok.Should().BeTrue();
}
[Fact]
public async Task Keys_ReturnsAlgorithmsAndKey()
{
using var client = _factory.CreateClient();
var response = await client.GetFromJsonAsync<KeysResponse>("/keys");
response.Should().NotBeNull();
response!.PublicKeyBase64.Should().NotBeNullOrWhiteSpace();
response.SimulatedProviders.Should().Contain("SM2");
response.SimulatedProviders.Should().Contain("GOST12-256");
}
private sealed record SignRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record SignResponse(
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record VerifyRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record VerifyResponse(
[property: JsonPropertyName("ok")] bool Ok,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record KeysResponse(
[property: JsonPropertyName("public_key_b64")] string PublicKeyBase64,
[property: JsonPropertyName("curve")] string Curve,
[property: JsonPropertyName("simulated_providers")] string[] SimulatedProviders);
}

View File

@@ -1,61 +1,16 @@
using System.Net.Http.Json;
using System.Text.Json.Serialization;
var baseUrl = Environment.GetEnvironmentVariable("STELLAOPS_CRYPTO_SIM_URL") ?? "http://localhost:8080";
var profile = (Environment.GetEnvironmentVariable("SIM_PROFILE") ?? "sm").ToLowerInvariant();
var algList = Environment.GetEnvironmentVariable("SIM_ALGORITHMS")?
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
?? profile switch
{
"ru-free" or "ru-paid" or "gost" or "ru" => new[] { "GOST12-256", "ru.magma.sim", "ru.kuznyechik.sim" },
"sm" or "cn" => new[] { "SM2" },
"eidas" => new[] { "ES256" },
"fips" => new[] { "ES256" },
"kcmvp" => new[] { "ES256" },
"pq" => new[] { "pq.sim", "DILITHIUM3", "FALCON512" },
_ => new[] { "ES256", "SM2", "pq.sim" }
};
var algList = SmokeLogic.ResolveAlgorithms(profile, Environment.GetEnvironmentVariable("SIM_ALGORITHMS"));
var message = Environment.GetEnvironmentVariable("SIM_MESSAGE") ?? "stellaops-sim-smoke";
using var client = new HttpClient { BaseAddress = new Uri(baseUrl) };
static async Task<(bool Ok, string Error)> SignAndVerify(HttpClient client, string algorithm, string message, CancellationToken ct)
{
var signPayload = new SignRequest(message, algorithm);
var signResponse = await client.PostAsJsonAsync("/sign", signPayload, ct).ConfigureAwait(false);
if (!signResponse.IsSuccessStatusCode)
{
return (false, $"sign failed: {(int)signResponse.StatusCode} {signResponse.ReasonPhrase}");
}
var signResult = await signResponse.Content.ReadFromJsonAsync<SignResponse>(cancellationToken: ct).ConfigureAwait(false);
if (signResult is null || string.IsNullOrWhiteSpace(signResult.SignatureBase64))
{
return (false, "sign returned empty payload");
}
var verifyPayload = new VerifyRequest(message, signResult.SignatureBase64, algorithm);
var verifyResponse = await client.PostAsJsonAsync("/verify", verifyPayload, ct).ConfigureAwait(false);
if (!verifyResponse.IsSuccessStatusCode)
{
return (false, $"verify failed: {(int)verifyResponse.StatusCode} {verifyResponse.ReasonPhrase}");
}
var verifyResult = await verifyResponse.Content.ReadFromJsonAsync<VerifyResponse>(cancellationToken: ct).ConfigureAwait(false);
if (verifyResult?.Ok is not true)
{
return (false, "verify returned false");
}
return (true, "");
}
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
var failures = new List<string>();
foreach (var alg in algList)
{
var (ok, error) = await SignAndVerify(client, alg, message, cts.Token);
var (ok, error) = await SmokeLogic.SignAndVerifyAsync(client, alg, message, cts.Token);
if (!ok)
{
failures.Add($"{alg}: {error}");
@@ -77,20 +32,3 @@ if (failures.Count > 0)
}
Console.WriteLine("Simulation smoke passed.");
internal sealed record SignRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("algorithm")] string Algorithm);
internal sealed record SignResponse(
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
internal sealed record VerifyRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
internal sealed record VerifyResponse(
[property: JsonPropertyName("ok")] bool Ok,
[property: JsonPropertyName("algorithm")] string Algorithm);

View File

@@ -8,4 +8,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssetTargetFallback></AssetTargetFallback>
</PropertyGroup>
<ItemGroup>
<Compile Remove="__Tests/**" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,72 @@
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public static class SmokeLogic
{
public static IReadOnlyList<string> ResolveAlgorithms(string profile, string? overrideList)
{
if (!string.IsNullOrWhiteSpace(overrideList))
{
return overrideList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
return profile switch
{
"ru-free" or "ru-paid" or "gost" or "ru" => new[] { "GOST12-256", "ru.magma.sim", "ru.kuznyechik.sim" },
"sm" or "cn" => new[] { "SM2" },
"eidas" => new[] { "ES256" },
"fips" => new[] { "ES256" },
"kcmvp" => new[] { "ES256" },
"pq" => new[] { "pq.sim", "DILITHIUM3", "FALCON512" },
_ => new[] { "ES256", "SM2", "pq.sim" }
};
}
public static async Task<(bool Ok, string Error)> SignAndVerifyAsync(HttpClient client, string algorithm, string message, CancellationToken ct)
{
var signPayload = new SignRequest(message, algorithm);
var signResponse = await client.PostAsJsonAsync("/sign", signPayload, ct).ConfigureAwait(false);
if (!signResponse.IsSuccessStatusCode)
{
return (false, $"sign failed: {(int)signResponse.StatusCode} {signResponse.ReasonPhrase}");
}
var signResult = await signResponse.Content.ReadFromJsonAsync<SignResponse>(cancellationToken: ct).ConfigureAwait(false);
if (signResult is null || string.IsNullOrWhiteSpace(signResult.SignatureBase64))
{
return (false, "sign returned empty payload");
}
var verifyPayload = new VerifyRequest(message, signResult.SignatureBase64, algorithm);
var verifyResponse = await client.PostAsJsonAsync("/verify", verifyPayload, ct).ConfigureAwait(false);
if (!verifyResponse.IsSuccessStatusCode)
{
return (false, $"verify failed: {(int)verifyResponse.StatusCode} {verifyResponse.ReasonPhrase}");
}
var verifyResult = await verifyResponse.Content.ReadFromJsonAsync<VerifyResponse>(cancellationToken: ct).ConfigureAwait(false);
if (verifyResult?.Ok is not true)
{
return (false, "verify returned false");
}
return (true, "");
}
private sealed record SignRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record SignResponse(
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record VerifyRequest(
[property: JsonPropertyName("message")] string Message,
[property: JsonPropertyName("signature_b64")] string SignatureBase64,
[property: JsonPropertyName("algorithm")] string Algorithm);
private sealed record VerifyResponse(
[property: JsonPropertyName("ok")] bool Ok,
[property: JsonPropertyName("algorithm")] string Algorithm);
}

View File

@@ -0,0 +1 @@
global using Xunit;

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>true</IsTestProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\SimCryptoSmoke.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,65 @@
using System.Net;
using System.Text;
using System.Text.Json;
using FluentAssertions;
namespace SimCryptoSmoke.Tests;
public sealed class SimCryptoSmokeTests
{
[Fact]
public void ResolveAlgorithms_UsesProfileDefaults()
{
var algs = SmokeLogic.ResolveAlgorithms("gost", null);
algs.Should().Contain("GOST12-256");
algs.Should().Contain("ru.magma.sim");
}
[Fact]
public void ResolveAlgorithms_UsesOverrideList()
{
var algs = SmokeLogic.ResolveAlgorithms("sm", "ES256,SM2");
algs.Should().ContainInOrder(new[] { "ES256", "SM2" });
}
[Fact]
public async Task SignAndVerifyAsync_ReturnsOk()
{
using var client = new HttpClient(new StubHandler())
{
BaseAddress = new Uri("http://localhost")
};
var result = await SmokeLogic.SignAndVerifyAsync(client, "SM2", "hello", CancellationToken.None);
result.Ok.Should().BeTrue();
result.Error.Should().BeEmpty();
}
private sealed class StubHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var path = request.RequestUri?.AbsolutePath ?? string.Empty;
if (path.Equals("/sign", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(BuildJsonResponse(new { signature_b64 = "c2ln", algorithm = "SM2" }));
}
if (path.Equals("/verify", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(BuildJsonResponse(new { ok = true, algorithm = "SM2" }));
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
private static HttpResponseMessage BuildJsonResponse(object payload)
{
var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions(JsonSerializerDefaults.Web));
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
}
}
}