# Multi-Profile Cryptographic Signing Specification **Version:** 1.0.0 **Status:** Design **Owner:** Security & Cryptography Guild **Last Updated:** 2025-12-23 --- ## 1. Overview This document specifies the pluggable cryptography abstraction layer for StellaOps, enabling jurisdiction-compliant multi-profile signing for audit bundles and attestations. ### 1.1 Design Goals - **Pluggable Architecture**: Add new crypto profiles without modifying core code - **Configuration-Driven**: Select profiles via YAML configuration - **Multi-Signature Support**: Single payload, multiple signatures with different algorithms - **Offline Verification**: All profiles support offline verification - **Deterministic**: Same input + key → same output signature - **KMS Integration**: Support cloud and hardware security modules --- ## 2. Core Abstractions ### 2.1 IContentSigner Interface ```csharp namespace StellaOps.Cryptography; /// /// Core abstraction for cryptographic signing operations. /// All implementations must be deterministic and thread-safe. /// public interface IContentSigner : IDisposable { /// /// Unique identifier for the signing key. /// Format: "{profile}-{key-purpose}-{year}" e.g., "stella-ed25519-2024" /// string KeyId { get; } /// /// Cryptographic profile (algorithm family) used by this signer. /// SignatureProfile Profile { get; } /// /// Algorithm identifier for the signature. /// Examples: "Ed25519", "ES256", "RS256", "GOST3410-2012-256" /// string Algorithm { get; } /// /// Sign a payload and return the signature. /// /// Data to sign (arbitrary bytes) /// Cancellation token /// Signature result with metadata Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default); /// /// Get the public key for verification (optional, for self-contained verification). /// /// Public key bytes, or null if not applicable byte[]? GetPublicKey(); } /// /// Result of a signing operation. /// public sealed record SignatureResult { public required string KeyId { get; init; } public required SignatureProfile Profile { get; init; } public required string Algorithm { get; init; } public required byte[] Signature { get; init; } public DateTimeOffset SignedAt { get; init; } = DateTimeOffset.UtcNow; /// /// Optional metadata (e.g., certificate chain for eIDAS, KMS request ID) /// public IReadOnlyDictionary? Metadata { get; init; } } ``` ### 2.2 IContentVerifier Interface ```csharp /// /// Core abstraction for signature verification. /// public interface IContentVerifier { /// /// Verify a signature against a payload. /// /// Original signed data /// Signature to verify /// Cancellation token /// Verification result with details Task VerifyAsync( ReadOnlyMemory payload, Signature signature, CancellationToken ct = default); /// /// Check if this verifier supports the given profile/algorithm. /// bool Supports(SignatureProfile profile, string algorithm); } /// /// Result of signature verification. /// public sealed record VerificationResult { public required bool IsValid { get; init; } public required SignatureProfile Profile { get; init; } public required string Algorithm { get; init; } public required string KeyId { get; init; } /// /// Human-readable reason if invalid. /// public string? FailureReason { get; init; } /// /// Certificate chain validation result (for eIDAS, etc.) /// public CertificateValidationResult? CertificateValidation { get; init; } /// /// Timestamp validation result (for RFC 3161, etc.) /// public TimestampValidationResult? TimestampValidation { get; init; } } /// /// Signature envelope with metadata. /// public sealed record Signature { public required string KeyId { get; init; } public required SignatureProfile Profile { get; init; } public required string Algorithm { get; init; } public required byte[] SignatureBytes { get; init; } public DateTimeOffset SignedAt { get; init; } /// /// Optional: embedded certificate chain (for eIDAS, PKI-based profiles) /// public byte[]? CertificateChain { get; init; } /// /// Optional: RFC 3161 timestamp token /// public byte[]? TimestampToken { get; init; } /// /// Optional: public key for verification (for raw key-based profiles like EdDSA) /// public byte[]? PublicKey { get; init; } } ``` ### 2.3 SignatureProfile Enum ```csharp /// /// Supported cryptographic profiles. /// Each profile maps to one or more concrete algorithms. /// public enum SignatureProfile { /// /// EdDSA (Ed25519) - Baseline profile for fast, secure signing. /// Algorithms: Ed25519 /// Use case: Default for all deployments /// EdDsa, /// /// ECDSA with NIST P-256 curve - FIPS 186-4 compliant. /// Algorithms: ES256 (ECDSA + SHA-256) /// Use case: US government, FIPS-required environments /// EcdsaP256, /// /// RSA-PSS - FIPS 186-4 compliant. /// Algorithms: PS256 (RSA-PSS + SHA-256), PS384, PS512 /// Use case: Legacy systems, FIPS-required environments /// RsaPss, /// /// GOST R 34.10-2012 - Russian cryptographic standard. /// Algorithms: GOST3410-2012-256, GOST3410-2012-512 /// Use case: Russian Federation deployments /// Gost2012, /// /// SM2 - Chinese national cryptographic standard (GM/T 0003.2-2012). /// Algorithms: SM2DSA (SM2 + SM3) /// Use case: China deployments, GB compliance /// SM2, /// /// eIDAS - EU qualified electronic signatures (ETSI TS 119 312). /// Algorithms: Varies (typically RSA or ECDSA with certificate chains) /// Use case: EU legal compliance, qualified signatures /// Eidas, /// /// Dilithium - NIST post-quantum cryptography (CRYSTALS-Dilithium). /// Algorithms: Dilithium2, Dilithium3, Dilithium5 /// Use case: Future-proofing, quantum-resistant signatures /// Dilithium, /// /// Falcon - NIST post-quantum cryptography (Falcon-512, Falcon-1024). /// Algorithms: Falcon-512, Falcon-1024 /// Use case: Future-proofing, compact quantum-resistant signatures /// Falcon } ``` --- ## 3. Multi-Profile Signer ### 3.1 Implementation ```csharp namespace StellaOps.Cryptography; /// /// Orchestrates signing with multiple profiles simultaneously. /// Used for dual-stack signatures (e.g., EdDSA + GOST for global compatibility). /// public sealed class MultiProfileSigner : IDisposable { private readonly IReadOnlyList _signers; private readonly ILogger _logger; public MultiProfileSigner( IEnumerable signers, ILogger logger) { _signers = signers.ToList(); _logger = logger; if (_signers.Count == 0) { throw new ArgumentException("At least one signer required", nameof(signers)); } } /// /// Sign with all configured profiles concurrently. /// public async Task SignAllAsync( ReadOnlyMemory payload, CancellationToken ct = default) { _logger.LogInformation( "Signing payload ({PayloadSize} bytes) with {SignerCount} profiles", payload.Length, _signers.Count); var tasks = _signers.Select(signer => SignWithProfileAsync(signer, payload, ct)); var results = await Task.WhenAll(tasks); return new MultiSignatureResult { Signatures = results.ToList(), SignedAt = DateTimeOffset.UtcNow }; } private async Task SignWithProfileAsync( IContentSigner signer, ReadOnlyMemory payload, CancellationToken ct) { try { var sw = Stopwatch.StartNew(); var result = await signer.SignAsync(payload, ct); sw.Stop(); _logger.LogDebug( "Signed with {Profile} ({Algorithm}) in {ElapsedMs}ms", signer.Profile, signer.Algorithm, sw.ElapsedMilliseconds); return result; } catch (Exception ex) { _logger.LogError( ex, "Failed to sign with {Profile} ({KeyId})", signer.Profile, signer.KeyId); throw; } } public void Dispose() { foreach (var signer in _signers) { signer.Dispose(); } } } /// /// Result containing multiple signatures from different profiles. /// public sealed record MultiSignatureResult { public required IReadOnlyList Signatures { get; init; } public required DateTimeOffset SignedAt { get; init; } /// /// Get signature by profile. /// public SignatureResult? GetSignature(SignatureProfile profile) { return Signatures.FirstOrDefault(s => s.Profile == profile); } /// /// Check if all signatures succeeded. /// public bool AllSucceeded => Signatures.Count > 0 && Signatures.All(s => s.Signature.Length > 0); } ``` --- ## 4. Profile Implementations ### 4.1 EdDSA Profile (Baseline) ```csharp namespace StellaOps.Cryptography.Profiles.EdDsa; /// /// EdDSA (Ed25519) signer using libsodium. /// Fast, secure, and widely supported. /// public sealed class Ed25519Signer : IContentSigner { private readonly byte[] _privateKey; private readonly byte[] _publicKey; private readonly string _keyId; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.EdDsa; public string Algorithm => "Ed25519"; public Ed25519Signer(string keyId, byte[] privateKey) { if (privateKey.Length != 32) throw new ArgumentException("Ed25519 private key must be 32 bytes", nameof(privateKey)); _keyId = keyId; _privateKey = privateKey; _publicKey = Sodium.PublicKeyAuth.ExtractPublicKey(privateKey); } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // libsodium Ed25519 signing var signature = Sodium.PublicKeyAuth.Sign(payload.Span, _privateKey); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature }); } public byte[]? GetPublicKey() => _publicKey.ToArray(); public void Dispose() { // Zero out private key CryptographicOperations.ZeroMemory(_privateKey); } } ``` ### 4.2 ECDSA Profile (FIPS) ```csharp namespace StellaOps.Cryptography.Profiles.Ecdsa; /// /// ECDSA P-256 signer using .NET crypto (FIPS 186-4 compliant). /// public sealed class EcdsaP256Signer : IContentSigner { private readonly ECDsa _ecdsa; private readonly string _keyId; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.EcdsaP256; public string Algorithm => "ES256"; public EcdsaP256Signer(string keyId, ECDsa ecdsa) { _keyId = keyId; _ecdsa = ecdsa; // Validate it's P-256 if (_ecdsa.KeySize != 256) throw new ArgumentException("ECDSA key must be P-256 (256 bits)", nameof(ecdsa)); } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // Sign with SHA-256 var signature = _ecdsa.SignData(payload.Span, HashAlgorithmName.SHA256); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature }); } public byte[]? GetPublicKey() { // Export public key in SubjectPublicKeyInfo format return _ecdsa.ExportSubjectPublicKeyInfo(); } public void Dispose() { _ecdsa?.Dispose(); } } ``` ### 4.3 GOST Profile (Russia) ```csharp namespace StellaOps.Cryptography.Profiles.Gost; /// /// GOST R 34.10-2012 signer using BouncyCastle or CryptoPro. /// For Russian Federation compliance. /// public sealed class Gost2012Signer : IContentSigner { private readonly AsymmetricKeyParameter _privateKey; private readonly string _keyId; private readonly int _keySize; // 256 or 512 public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.Gost2012; public string Algorithm => _keySize == 256 ? "GOST3410-2012-256" : "GOST3410-2012-512"; public Gost2012Signer(string keyId, AsymmetricKeyParameter privateKey, int keySize = 256) { if (keySize != 256 && keySize != 512) throw new ArgumentException("GOST key size must be 256 or 512", nameof(keySize)); _keyId = keyId; _privateKey = privateKey; _keySize = keySize; } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // BouncyCastle GOST signing var signer = SignerUtilities.GetSigner($"GOST3411-{_keySize}withECGOST3410-{_keySize}"); signer.Init(true, _privateKey); signer.BlockUpdate(payload.Span); var signature = signer.GenerateSignature(); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature }); } public byte[]? GetPublicKey() { // Extract public key from private key parameter // Implementation depends on BouncyCastle key structure return null; // Simplified } public void Dispose() { // BouncyCastle keys don't require disposal } } ``` ### 4.4 SM2 Profile (China) ```csharp namespace StellaOps.Cryptography.Profiles.SM; /// /// SM2 signer using BouncyCastle. /// For Chinese GM/T 0003.2-2012 compliance. /// public sealed class SM2Signer : IContentSigner { private readonly AsymmetricKeyParameter _privateKey; private readonly string _keyId; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.SM2; public string Algorithm => "SM2DSA"; public SM2Signer(string keyId, AsymmetricKeyParameter privateKey) { _keyId = keyId; _privateKey = privateKey; } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // BouncyCastle SM2 signing with SM3 hash var signer = SignerUtilities.GetSigner("SM3withSM2"); signer.Init(true, _privateKey); signer.BlockUpdate(payload.Span); var signature = signer.GenerateSignature(); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature }); } public byte[]? GetPublicKey() => null; // Simplified public void Dispose() { } } ``` ### 4.5 eIDAS Profile (EU) ```csharp namespace StellaOps.Cryptography.Profiles.Eidas; /// /// eIDAS qualified signature using ETSI TS 119 312. /// Requires qualified certificate and QSCD (Qualified Signature Creation Device). /// public sealed class EidasSigner : IContentSigner { private readonly X509Certificate2 _certificate; private readonly RSA _privateKey; private readonly string _keyId; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.Eidas; public string Algorithm => "RS256"; // Typically RSA or ECDSA public EidasSigner(string keyId, X509Certificate2 certificate) { _keyId = keyId; _certificate = certificate; // Extract private key (requires certificate with private key) _privateKey = certificate.GetRSAPrivateKey() ?? throw new ArgumentException("Certificate must have RSA private key", nameof(certificate)); } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // Sign with RSA-PSS var signature = _privateKey.SignData( payload.Span, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); // Include certificate chain for verification var certChain = _certificate.Export(X509ContentType.Cert); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature, Metadata = new Dictionary { ["certificateChain"] = certChain, ["certificateThumbprint"] = _certificate.Thumbprint } }); } public byte[]? GetPublicKey() => _certificate.GetPublicKey(); public void Dispose() { _privateKey?.Dispose(); _certificate?.Dispose(); } } ``` ### 4.6 Post-Quantum Profile (Optional) ```csharp namespace StellaOps.Cryptography.Profiles.Pqc; /// /// Dilithium post-quantum signature using liboqs. /// EXPERIMENTAL - for future-proofing only. /// public sealed class DilithiumSigner : IContentSigner { private readonly byte[] _privateKey; private readonly byte[] _publicKey; private readonly string _keyId; private readonly int _securityLevel; // 2, 3, or 5 public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.Dilithium; public string Algorithm => $"Dilithium{_securityLevel}"; public DilithiumSigner(string keyId, byte[] privateKey, byte[] publicKey, int securityLevel = 3) { _keyId = keyId; _privateKey = privateKey; _publicKey = publicKey; _securityLevel = securityLevel; } public Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { ct.ThrowIfCancellationRequested(); // Call liboqs via P/Invoke or managed wrapper var signature = LibOqs.Sign(_privateKey, payload.Span, $"Dilithium{_securityLevel}"); return Task.FromResult(new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = signature }); } public byte[]? GetPublicKey() => _publicKey.ToArray(); public void Dispose() { CryptographicOperations.ZeroMemory(_privateKey); } } ``` --- ## 5. Configuration Schema ### 5.1 YAML Configuration ```yaml # etc/cryptography.yaml cryptography: # Global settings defaultProfile: EdDsa enableMultiProfile: true # Key storage keyStore: type: filesystem # Options: filesystem, azure-keyvault, aws-kms, hashicorp-vault path: /etc/stellaops/keys # Profile definitions profiles: # Baseline: EdDSA (always enabled) - profile: EdDsa keyId: stella-ed25519-2024 enabled: true keyFile: ed25519-private.key # FIPS: ECDSA P-256 - profile: EcdsaP256 keyId: stella-ecdsa-p256-2024 enabled: true keySource: type: azure-keyvault vaultUrl: https://stellaops-vault.vault.azure.net keyName: stellaops-ecdsa-2024 # FIPS: RSA-PSS - profile: RsaPss keyId: stella-rsa-pss-2024 enabled: false keyFile: rsa-pss-private.key keySize: 3072 # Russia: GOST - profile: Gost2012 keyId: stella-gost-2024 enabled: false # Enable for Russian deployments keyFile: gost-private.key keySize: 256 # China: SM2 - profile: SM2 keyId: stella-sm2-2024 enabled: false # Enable for Chinese deployments keyFile: sm2-private.key # EU: eIDAS - profile: Eidas keyId: stella-eidas-2024 enabled: false # Enable for EU qualified signatures certificateFile: /etc/stellaops/certs/eidas-qscd.pfx certificatePassword: source: env variable: STELLAOPS_EIDAS_CERT_PASSWORD # Post-quantum (optional) - profile: Dilithium keyId: stella-dilithium3-2024 enabled: false keyFile: dilithium3-private.key securityLevel: 3 # Verification settings verification: allowedProfiles: - EdDsa - EcdsaP256 - RsaPss - Gost2012 - SM2 - Eidas - Dilithium trustAnchors: - path: /etc/stellaops/trust/root-ca.pem type: x509-pem - path: /etc/stellaops/trust/eidas-tsl.xml type: eidas-tsl # CRL and OCSP settings revocationChecking: enabled: true crlPaths: - /etc/stellaops/trust/crls/ ocspResponders: - https://ocsp.stellaops.dev cacheDuration: 3600 # seconds # Timestamp verification timestampVerification: enabled: true trustAnchors: - /etc/stellaops/trust/tsa-root.pem ``` ### 5.2 Configuration Loading ```csharp namespace StellaOps.Cryptography.Configuration; public sealed class CryptographyConfiguration { public required string DefaultProfile { get; init; } public required bool EnableMultiProfile { get; init; } public required KeyStoreConfiguration KeyStore { get; init; } public required IReadOnlyList Profiles { get; init; } public required VerificationConfiguration Verification { get; init; } } public sealed class ProfileConfiguration { public required SignatureProfile Profile { get; init; } public required string KeyId { get; init; } public required bool Enabled { get; init; } // Key source options public string? KeyFile { get; init; } public KeySourceConfiguration? KeySource { get; init; } // Profile-specific settings public int? KeySize { get; init; } public string? CertificateFile { get; init; } public SecretConfiguration? CertificatePassword { get; init; } public int? SecurityLevel { get; init; } } /// /// Factory for creating signers from configuration. /// public sealed class SignerFactory { private readonly CryptographyConfiguration _config; private readonly IKeyStore _keyStore; private readonly ILogger _logger; public SignerFactory( IOptions config, IKeyStore keyStore, ILogger logger) { _config = config.Value; _keyStore = keyStore; _logger = logger; } public async Task CreateSignerAsync( SignatureProfile profile, CancellationToken ct = default) { var profileConfig = _config.Profiles.FirstOrDefault(p => p.Profile == profile); if (profileConfig == null || !profileConfig.Enabled) { throw new InvalidOperationException($"Profile {profile} is not configured or not enabled"); } return profile switch { SignatureProfile.EdDsa => await CreateEdDsaSignerAsync(profileConfig, ct), SignatureProfile.EcdsaP256 => await CreateEcdsaSignerAsync(profileConfig, ct), SignatureProfile.RsaPss => await CreateRsaSignerAsync(profileConfig, ct), SignatureProfile.Gost2012 => await CreateGostSignerAsync(profileConfig, ct), SignatureProfile.SM2 => await CreateSM2SignerAsync(profileConfig, ct), SignatureProfile.Eidas => await CreateEidasSignerAsync(profileConfig, ct), SignatureProfile.Dilithium => await CreateDilithiumSignerAsync(profileConfig, ct), _ => throw new NotSupportedException($"Profile {profile} not supported") }; } public async Task CreateMultiProfileSignerAsync(CancellationToken ct = default) { var signers = new List(); foreach (var profileConfig in _config.Profiles.Where(p => p.Enabled)) { try { var signer = await CreateSignerAsync(profileConfig.Profile, ct); signers.Add(signer); } catch (Exception ex) { _logger.LogError( ex, "Failed to create signer for profile {Profile}. Skipping.", profileConfig.Profile); } } if (signers.Count == 0) { throw new InvalidOperationException("No signers could be created"); } return new MultiProfileSigner(signers, _logger); } private async Task CreateEdDsaSignerAsync( ProfileConfiguration config, CancellationToken ct) { var privateKey = await _keyStore.LoadPrivateKeyAsync(config.KeyFile, ct); return new Ed25519Signer(config.KeyId, privateKey); } // Additional factory methods for other profiles... } ``` --- ## 6. KMS Integration ### 6.1 IKeyStore Abstraction ```csharp namespace StellaOps.Cryptography.KeyManagement; /// /// Abstraction for key storage backends. /// public interface IKeyStore { Task LoadPrivateKeyAsync(string keyIdentifier, CancellationToken ct = default); Task LoadPublicKeyAsync(string keyIdentifier, CancellationToken ct = default); Task LoadCertificateAsync(string certIdentifier, CancellationToken ct = default); } /// /// Filesystem-based key store. /// public sealed class FileSystemKeyStore : IKeyStore { private readonly string _basePath; public FileSystemKeyStore(string basePath) { _basePath = basePath ?? throw new ArgumentNullException(nameof(basePath)); } public async Task LoadPrivateKeyAsync(string keyIdentifier, CancellationToken ct = default) { var path = Path.Combine(_basePath, keyIdentifier); return await File.ReadAllBytesAsync(path, ct); } // Additional methods... } /// /// Azure Key Vault-based key store. /// public sealed class AzureKeyVaultKeyStore : IKeyStore { private readonly KeyClient _keyClient; private readonly CryptographyClient _cryptoClient; public AzureKeyVaultKeyStore(Uri vaultUrl, TokenCredential credential) { _keyClient = new KeyClient(vaultUrl, credential); // Crypto operations performed in Key Vault (key never leaves HSM) } public async Task LoadPublicKeyAsync(string keyIdentifier, CancellationToken ct = default) { var key = await _keyClient.GetKeyAsync(keyIdentifier, cancellationToken: ct); return key.Value.Key.ToRSA().ExportSubjectPublicKeyInfo(); } // For Azure KMS, signing is done via CryptographyClient, not by exporting private key public Task LoadPrivateKeyAsync(string keyIdentifier, CancellationToken ct = default) { throw new NotSupportedException("Private keys cannot be exported from Azure Key Vault"); } } /// /// Azure KMS-backed signer (key stays in HSM). /// public sealed class AzureKmsEcdsaSigner : IContentSigner { private readonly CryptographyClient _cryptoClient; private readonly string _keyId; public string KeyId => _keyId; public SignatureProfile Profile => SignatureProfile.EcdsaP256; public string Algorithm => "ES256"; public AzureKmsEcdsaSigner(string keyId, CryptographyClient cryptoClient) { _keyId = keyId; _cryptoClient = cryptoClient; } public async Task SignAsync(ReadOnlyMemory payload, CancellationToken ct = default) { // Hash payload locally var hash = SHA256.HashData(payload.Span); // Sign hash in Azure Key Vault (key never leaves HSM) var result = await _cryptoClient.SignAsync(SignatureAlgorithm.ES256, hash, ct); return new SignatureResult { KeyId = _keyId, Profile = Profile, Algorithm = Algorithm, Signature = result.Signature }; } public byte[]? GetPublicKey() { // Retrieve from Key Vault return null; // Simplified } public void Dispose() { } } ``` --- ## 7. Testing Requirements ### 7.1 Unit Tests Each profile implementation must have: 1. **Signature Determinism Test**: Same input → same signature (for deterministic algorithms) 2. **Roundtrip Test**: Sign → Verify succeeds 3. **Invalid Signature Test**: Modified signature → Verify fails 4. **Cross-Profile Test**: Verify profile isolation 5. **Performance Benchmark**: <100ms signing for typical payloads (p95) ### 7.2 Test Vectors Each profile must validate against official test vectors: - **EdDSA**: RFC 8032 test vectors - **ECDSA**: NIST FIPS 186-4 test vectors - **RSA-PSS**: PKCS#1 v2.2 test vectors - **GOST**: GOST R 34.10-2012 test suite - **SM2**: GM/T 0003.2-2012 test vectors - **eIDAS**: ETSI conformance test suite - **Dilithium/Falcon**: NIST PQC test vectors ### 7.3 Integration Tests - Multi-profile signing with 3+ profiles - Configuration loading and validation - KMS integration (mocked) - Offline verification with embedded keys/certificates --- ## 8. Operational Considerations ### 8.1 Key Rotation - Support multiple active keys per profile (key ID includes year) - Old signatures remain verifiable with archived public keys - Automated rotation procedures documented in runbooks ### 8.2 Performance | Profile | Target Signing Time (p95) | Target Verification Time (p95) | |---------|---------------------------|--------------------------------| | EdDSA | <10ms | <5ms | | ECDSA P-256 | <50ms | <25ms | | RSA-PSS 3072 | <100ms | <10ms | | GOST 2012-256 | <100ms | <50ms | | SM2 | <100ms | <50ms | | eIDAS | <200ms (includes chain validation) | <100ms | | Dilithium3 | <50ms | <25ms | ### 8.3 Compliance Validation - Annual audit of crypto implementations - Automated compliance checking against standards - Certificate expiry monitoring for eIDAS - CRL/OCSP freshness checks --- ## 9. References - **RFC 8032:** Edwards-Curve Digital Signature Algorithm (EdDSA) - **FIPS 186-4:** Digital Signature Standard (DSS) - **GOST R 34.10-2012:** Russian digital signature standard - **GM/T 0003.2-2012:** Chinese SM2 digital signature algorithm - **ETSI TS 119 312:** Electronic Signatures and Infrastructures (ESI); Cryptographic Suites - **NIST PQC:** Post-Quantum Cryptography standardization --- **END OF DOCUMENT**