// SPDX-License-Identifier: AGPL-3.0-or-later // Sprint: SPRINT_4100_0006_0003 - SM Crypto CLI Integration - OSCCA Compliance Tests using System; using System.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using StellaOps.Cryptography; using StellaOps.Cryptography.Plugin.SmSoft; using Xunit; namespace StellaOps.Cryptography.Plugin.SmSoft.Tests; /// /// OSCCA GM/T 0003-2012 compliance tests for SM2 signature algorithm. /// Test vectors from Appendix A of the standard. /// public class Sm2ComplianceTests { private readonly SmSoftCryptoProvider _provider; public Sm2ComplianceTests() { var services = new ServiceCollection(); services.AddLogging(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug)); // Disable environment gate for testing services.Configure(options => { options.RequireEnvironmentGate = false; }); services.AddSingleton(); var serviceProvider = services.BuildServiceProvider(); _provider = serviceProvider.GetRequiredService() as SmSoftCryptoProvider ?? throw new InvalidOperationException("Failed to resolve SmSoftCryptoProvider"); } [Fact] public void Provider_Name_IsCnSmSoft() { Assert.Equal("cn.sm.soft", _provider.Name); } [Theory] [InlineData(CryptoCapability.Signing, "SM2", true)] [InlineData(CryptoCapability.Verification, "SM2", true)] [InlineData(CryptoCapability.ContentHashing, "SM3", true)] [InlineData(CryptoCapability.Signing, "SM4", false)] [InlineData(CryptoCapability.PasswordHashing, "SM2", false)] public void Supports_ReturnsExpectedResults(CryptoCapability capability, string algorithmId, bool expected) { var result = _provider.Supports(capability, algorithmId); Assert.Equal(expected, result); } [Fact] public void GetPasswordHasher_ThrowsNotSupported() { Assert.Throws(() => _provider.GetPasswordHasher("PBKDF2")); } [Fact] public void GetHasher_WithSm3_ReturnsSm3Hasher() { var hasher = _provider.GetHasher("SM3"); Assert.NotNull(hasher); Assert.Equal("SM3", hasher.AlgorithmId); } [Fact] public void GetHasher_WithInvalidAlgorithm_Throws() { Assert.Throws(() => _provider.GetHasher("SHA256")); } [Fact] public void Sm3_ComputeHash_EmptyInput_ReturnsCorrectHash() { // OSCCA GM/T 0004-2012 test vector for empty string // Expected: 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b var hasher = _provider.GetHasher("SM3"); var input = Array.Empty(); var hash = hasher.ComputeHashHex(input); Assert.Equal("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", hash); } [Fact] public void Sm3_ComputeHash_AbcInput_ReturnsCorrectHash() { // OSCCA GM/T 0004-2012 test vector for "abc" // Expected: 66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0 var hasher = _provider.GetHasher("SM3"); var input = Encoding.ASCII.GetBytes("abc"); var hash = hasher.ComputeHashHex(input); Assert.Equal("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", hash); } [Fact] public void Sm3_ComputeHash_LongInput_ReturnsCorrectHash() { // OSCCA GM/T 0004-2012 test vector for 64-byte string // Input: "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd" // Expected: debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732 var hasher = _provider.GetHasher("SM3"); var input = Encoding.ASCII.GetBytes("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"); var hash = hasher.ComputeHashHex(input); Assert.Equal("debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", hash); } [Fact] public async Task Sm2_SignAndVerify_WithTestKey_Succeeds() { // Note: This test uses the existing BouncyCastle SM2 implementation // Full OSCCA test vector validation requires actual test key material // which would be loaded from GM/T 0003-2012 Appendix A // For now, we test that the sign/verify cycle works correctly // with a test key (not from OSCCA vectors) var testData = Encoding.UTF8.GetBytes("Test message for SM2 signature"); // Generate test key (in production, load from OSCCA test vectors) var keyPair = GenerateTestSm2KeyPair(); var keyId = "test-sm2-key"; // Create signing key var signingKey = new CryptoSigningKey( new CryptoKeyReference(keyId), "SM2", SerializeSm2PrivateKey(keyPair), DateTimeOffset.UtcNow ); _provider.UpsertSigningKey(signingKey); // Get signer var signer = _provider.GetSigner("SM2", new CryptoKeyReference(keyId)); // Sign var signature = await signer.SignAsync(testData); Assert.NotNull(signature); Assert.NotEmpty(signature); // Verify var isValid = await signer.VerifyAsync(testData, signature); Assert.True(isValid); // Verify with modified data fails var modifiedData = Encoding.UTF8.GetBytes("Modified message"); var isInvalid = await signer.VerifyAsync(modifiedData, signature); Assert.False(isInvalid); } [Fact] public void Sm2_ExportPublicJsonWebKey_ReturnsValidJwk() { var keyPair = GenerateTestSm2KeyPair(); var keyId = "test-jwk-export"; var signingKey = new CryptoSigningKey( new CryptoKeyReference(keyId), "SM2", SerializeSm2PrivateKey(keyPair), DateTimeOffset.UtcNow ); _provider.UpsertSigningKey(signingKey); var signer = _provider.GetSigner("SM2", new CryptoKeyReference(keyId)); var jwk = signer.ExportPublicJsonWebKey(); Assert.NotNull(jwk); Assert.Equal("EC", jwk.Kty); Assert.Equal("SM2", jwk.Crv); Assert.Equal("SM2", jwk.Alg); Assert.Equal("sig", jwk.Use); Assert.Equal(keyId, jwk.Kid); Assert.NotNull(jwk.X); Assert.NotNull(jwk.Y); } // Helper methods for test key generation private static Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair GenerateTestSm2KeyPair() { var curve = Org.BouncyCastle.Asn1.GM.GMNamedCurves.GetByName("sm2p256v1"); var domainParams = new Org.BouncyCastle.Crypto.Parameters.ECDomainParameters( curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed()); var generator = new Org.BouncyCastle.Crypto.Generators.ECKeyPairGenerator(); generator.Init(new Org.BouncyCastle.Crypto.KeyGenerationParameters( new Org.BouncyCastle.Security.SecureRandom(), 256)); var keyParams = new Org.BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters( domainParams, new Org.BouncyCastle.Security.SecureRandom()); generator.Init(keyParams); return generator.GenerateKeyPair(); } private static byte[] SerializeSm2PrivateKey(Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair) { var privateKey = (Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters)keyPair.Private; // Serialize to PKCS#8 DER format var privateKeyInfo = Org.BouncyCastle.Pkcs.PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey); return privateKeyInfo.GetEncoded(); } } /// /// SM2 algorithm constants. /// public static class SignatureAlgorithms { public const string Sm2 = "SM2"; } /// /// SM3 hash algorithm constants. /// public static class HashAlgorithms { public const string Sm3 = "SM3"; }