Add post-quantum cryptography support with PqSoftCryptoProvider
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
wine-csp-build / Build Wine CSP Image (push) Has been cancelled

- Implemented PqSoftCryptoProvider for software-only post-quantum algorithms (Dilithium3, Falcon512) using BouncyCastle.
- Added PqSoftProviderOptions and PqSoftKeyOptions for configuration.
- Created unit tests for Dilithium3 and Falcon512 signing and verification.
- Introduced EcdsaPolicyCryptoProvider for compliance profiles (FIPS/eIDAS) with explicit allow-lists.
- Added KcmvpHashOnlyProvider for KCMVP baseline compliance.
- Updated project files and dependencies for new libraries and testing frameworks.
This commit is contained in:
StellaOps Bot
2025-12-07 15:04:19 +02:00
parent 862bb6ed80
commit 98e6b76584
119 changed files with 11436 additions and 1732 deletions

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Attestor.Core.Options;
using StellaOps.Attestor.Core.Signing;
using StellaOps.Attestor.Infrastructure.Signing;
using StellaOps.Cryptography;
using StellaOps.Cryptography.Plugin.SmSoft;
using Xunit;
namespace StellaOps.Attestor.Tests.Signing;
public class Sm2AttestorTests
{
private readonly string? _gate;
public Sm2AttestorTests()
{
_gate = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");
Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", "1");
}
[Fact]
public void Registry_ResolvesSm2_WhenGateEnabled()
{
var keyPath = Sm2TestKeyFactory.WriteTempPem();
var options = Options.Create(new AttestorOptions
{
Signing = new AttestorOptions.SigningOptions
{
PreferredProviders = new[] { "cn.sm.soft" },
Keys = new List<AttestorOptions.SigningKeyOptions>
{
new()
{
KeyId = "sm2-key",
Algorithm = SignatureAlgorithms.Sm2,
KeyPath = keyPath,
MaterialFormat = "pem",
Enabled = true,
Provider = "cn.sm.soft"
}
}
}
});
var registry = new AttestorSigningKeyRegistry(
options,
TimeProvider.System,
NullLogger<AttestorSigningKeyRegistry>.Instance);
var entry = registry.GetRequired("sm2-key");
Assert.Equal(SignatureAlgorithms.Sm2, entry.Algorithm);
Assert.Equal("cn.sm.soft", entry.ProviderName);
var signer = registry.Registry.ResolveSigner(CryptoCapability.Signing, SignatureAlgorithms.Sm2, entry.Key.Reference).Signer;
var payload = System.Text.Encoding.UTF8.GetBytes("sm2-attestor-test");
var sig = signer.SignAsync(payload, CancellationToken.None).Result;
Assert.True(signer.VerifyAsync(payload, sig, CancellationToken.None).Result);
}
[Fact]
public void Registry_Throws_WhenGateDisabled()
{
Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", null);
var keyPath = Sm2TestKeyFactory.WriteTempPem();
var options = Options.Create(new AttestorOptions
{
Signing = new AttestorOptions.SigningOptions
{
PreferredProviders = new[] { "cn.sm.soft" },
Keys = new List<AttestorOptions.SigningKeyOptions>
{
new()
{
KeyId = "sm2-key",
Algorithm = SignatureAlgorithms.Sm2,
KeyPath = keyPath,
MaterialFormat = "pem",
Enabled = true,
Provider = "cn.sm.soft"
}
}
}
});
Assert.Throws<InvalidOperationException>(() =>
new AttestorSigningKeyRegistry(options, TimeProvider.System, NullLogger<AttestorSigningKeyRegistry>.Instance));
}
public void Dispose()
{
Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", _gate);
}
}
internal static class Sm2TestKeyFactory
{
public static string WriteTempPem()
{
var curve = Org.BouncyCastle.Asn1.GM.GMNamedCurves.GetByName("SM2P256V1");
var domain = new Org.BouncyCastle.Crypto.Parameters.ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed());
var generator = new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator("EC");
generator.Init(new Org.BouncyCastle.Crypto.Generators.ECKeyGenerationParameters(domain, new Org.BouncyCastle.Security.SecureRandom()));
var pair = generator.GenerateKeyPair();
var privInfo = Org.BouncyCastle.Asn1.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
var pem = Convert.ToBase64String(privInfo.GetDerEncoded());
var path = System.IO.Path.GetTempFileName();
System.IO.File.WriteAllText(path, "-----BEGIN PRIVATE KEY-----\n" + pem + "\n-----END PRIVATE KEY-----\n");
return path;
}
}

View File

@@ -8,6 +8,7 @@
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
<PackageReference Include="xunit" Version="2.9.2" />