using System; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Cryptography; using StellaOps.Cryptography.Plugin.SmSoft; using StellaOps.Signer.Core; using StellaOps.Signer.Infrastructure.Signing; using StellaOps.Signer.Tests.Fixtures; using Xunit; namespace StellaOps.Signer.Tests.Signing; public class Sm2SigningTests : IDisposable { private readonly string? _gate; public Sm2SigningTests() { _gate = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED"); Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", "1"); } [Fact] public async Task Sign_Sm2_Succeeds_WhenGateOn() { var registry = TestCryptoFactory.CreateSm2Registry(); var keyResolver = new StubKeyResolver("sm2-key", SignatureAlgorithms.Sm2, "cn.sm.soft"); var options = Options.Create(new DsseSignerOptions { KeylessAlgorithm = SignatureAlgorithms.Sm2, KmsAlgorithm = SignatureAlgorithms.Sm2, PreferredProvider = "cn.sm.soft" }); var signer = new CryptoDsseSigner( registry, keyResolver, options, NullLogger.Instance); var request = BuildRequest(); var entitlement = new ProofOfEntitlementResult("lic", "cust", "plan", 0, 0, 0, DateTimeOffset.UtcNow.AddHours(1)); var caller = BuildCaller(); var bundle = await signer.SignAsync(request, entitlement, caller, default); Assert.Equal(SignatureAlgorithms.Sm2, bundle.Metadata.AlgorithmId); Assert.Equal("cn.sm.soft", bundle.Metadata.ProviderName); } [Fact] public async Task Sign_Sm2_Fails_WhenGateOff() { Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", null); var registry = TestCryptoFactory.CreateSm2Registry(); var keyResolver = new StubKeyResolver("sm2-key", SignatureAlgorithms.Sm2, "cn.sm.soft"); var options = Options.Create(new DsseSignerOptions { KeylessAlgorithm = SignatureAlgorithms.Sm2 }); var signer = new CryptoDsseSigner( registry, keyResolver, options, NullLogger.Instance); var request = BuildRequest(); var entitlement = new ProofOfEntitlementResult("lic", "cust", "plan", 0, 0, 0, DateTimeOffset.UtcNow.AddHours(1)); var caller = BuildCaller(); await Assert.ThrowsAsync(() => signer.SignAsync(request, entitlement, caller, default).AsTask()); } private class StubKeyResolver : ISigningKeyResolver { private readonly string _keyId; private readonly string _alg; private readonly string _provider; public StubKeyResolver(string keyId, string alg, string provider) { _keyId = keyId; _alg = alg; _provider = provider; } public ValueTask ResolveKeyAsync(SigningMode mode, string tenant, CancellationToken cancellationToken) { var resolution = new SigningKeyResolution(_keyId, _provider, "https://sm.test", "sm2-subject"); return ValueTask.FromResult(resolution); } } public void Dispose() { Environment.SetEnvironmentVariable("SM_SOFT_ALLOWED", _gate); } private static SigningRequest BuildRequest() { var subject = new SigningSubject("pkg", new Dictionary { ["sha256"] = "00" }); return new SigningRequest( new[] { subject }, "test-predicate", JsonDocument.Parse("{}"), "sha256:00", new ProofOfEntitlement(SignerPoEFormat.Jwt, "stub"), new SigningOptions(SigningMode.Keyless, null, "dsse")); } private static CallerContext BuildCaller() => new( Subject: "subject-1", Tenant: "tenant-1", Scopes: Array.Empty(), Audiences: Array.Empty(), SenderBinding: string.Empty, ClientCertificateThumbprint: string.Empty); }