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 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(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(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(); foreach (var alg in algList) { var (ok, error) = await SignAndVerify(client, alg, message, cts.Token); if (!ok) { failures.Add($"{alg}: {error}"); continue; } Console.WriteLine($"[ok] {alg} via {baseUrl}"); } if (failures.Count > 0) { Console.Error.WriteLine("Simulation smoke failed:"); foreach (var f in failures) { Console.Error.WriteLine($" - {f}"); } Environment.Exit(1); } 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);