using System.Security.Cryptography; namespace StellaOps.Cryptography.Plugin.OfflineVerification; /// /// Cryptographic provider for offline/air-gapped environments using .NET BCL cryptography. /// This provider wraps System.Security.Cryptography in the ICryptoProvider abstraction /// to enable configuration-driven crypto while maintaining offline verification capabilities. /// public sealed class OfflineVerificationCryptoProvider : ICryptoProvider { /// /// Provider name for registry resolution. /// public string Name => "offline-verification"; /// /// Checks if this provider supports the specified capability and algorithm. /// public bool Supports(CryptoCapability capability, string algorithmId) { return capability switch { CryptoCapability.Signing => algorithmId is "ES256" or "ES384" or "ES512" or "RS256" or "RS384" or "RS512" or "PS256" or "PS384" or "PS512", CryptoCapability.Verification => algorithmId is "ES256" or "ES384" or "ES512" or "RS256" or "RS384" or "RS512" or "PS256" or "PS384" or "PS512", CryptoCapability.ContentHashing => algorithmId is "SHA-256" or "SHA-384" or "SHA-512" or "SHA256" or "SHA384" or "SHA512", CryptoCapability.PasswordHashing => algorithmId is "PBKDF2" or "Argon2id", _ => false }; } /// /// Not supported for offline verification - no password hashing. /// public IPasswordHasher GetPasswordHasher(string algorithmId) { throw new NotSupportedException( $"Password hashing is not supported by the offline verification provider."); } /// /// Gets a content hasher for the specified algorithm. /// public ICryptoHasher GetHasher(string algorithmId) { var normalized = NormalizeAlgorithmId(algorithmId); return normalized switch { "SHA-256" => new BclHasher("SHA-256", HashAlgorithmName.SHA256), "SHA-384" => new BclHasher("SHA-384", HashAlgorithmName.SHA384), "SHA-512" => new BclHasher("SHA-512", HashAlgorithmName.SHA512), _ => throw new NotSupportedException($"Hash algorithm '{algorithmId}' is not supported.") }; } /// /// Gets a signer for the specified algorithm and key reference. /// public ICryptoSigner GetSigner(string algorithmId, CryptoKeyReference keyReference) { return algorithmId switch { "ES256" => new EcdsaSigner(algorithmId, ECCurve.NamedCurves.nistP256, HashAlgorithmName.SHA256, keyReference), "ES384" => new EcdsaSigner(algorithmId, ECCurve.NamedCurves.nistP384, HashAlgorithmName.SHA384, keyReference), "ES512" => new EcdsaSigner(algorithmId, ECCurve.NamedCurves.nistP521, HashAlgorithmName.SHA512, keyReference), "RS256" => new RsaSigner(algorithmId, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1, keyReference), "RS384" => new RsaSigner(algorithmId, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1, keyReference), "RS512" => new RsaSigner(algorithmId, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1, keyReference), "PS256" => new RsaSigner(algorithmId, HashAlgorithmName.SHA256, RSASignaturePadding.Pss, keyReference), "PS384" => new RsaSigner(algorithmId, HashAlgorithmName.SHA384, RSASignaturePadding.Pss, keyReference), "PS512" => new RsaSigner(algorithmId, HashAlgorithmName.SHA512, RSASignaturePadding.Pss, keyReference), _ => throw new NotSupportedException($"Signing algorithm '{algorithmId}' is not supported.") }; } /// /// Creates an ephemeral verifier from raw public key bytes (verification-only). /// public ICryptoSigner CreateEphemeralVerifier(string algorithmId, ReadOnlySpan publicKeyBytes) { return algorithmId switch { "ES256" => new EcdsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA256, publicKeyBytes.ToArray()), "ES384" => new EcdsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA384, publicKeyBytes.ToArray()), "ES512" => new EcdsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA512, publicKeyBytes.ToArray()), "RS256" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1, publicKeyBytes.ToArray()), "RS384" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1, publicKeyBytes.ToArray()), "RS512" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1, publicKeyBytes.ToArray()), "PS256" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA256, RSASignaturePadding.Pss, publicKeyBytes.ToArray()), "PS384" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA384, RSASignaturePadding.Pss, publicKeyBytes.ToArray()), "PS512" => new RsaEphemeralVerifier(algorithmId, HashAlgorithmName.SHA512, RSASignaturePadding.Pss, publicKeyBytes.ToArray()), _ => throw new NotSupportedException($"Verification algorithm '{algorithmId}' is not supported.") }; } /// /// Not supported - offline verification provider does not manage keys. /// public void UpsertSigningKey(CryptoSigningKey signingKey) { throw new NotSupportedException( "The offline verification provider does not support key management."); } /// /// Not supported - offline verification provider does not manage keys. /// public bool RemoveSigningKey(string keyId) { throw new NotSupportedException( "The offline verification provider does not support key management."); } /// /// Returns empty collection - offline verification provider does not manage keys. /// public IReadOnlyCollection GetSigningKeys() { return Array.Empty(); } private static string NormalizeAlgorithmId(string algorithmId) { if (string.IsNullOrEmpty(algorithmId)) return algorithmId ?? string.Empty; return algorithmId.ToUpperInvariant() switch { "SHA256" or "SHA-256" => "SHA-256", "SHA384" or "SHA-384" => "SHA-384", "SHA512" or "SHA-512" => "SHA-512", _ => algorithmId }; } /// /// Internal hasher implementation using .NET BCL. /// private sealed class BclHasher : ICryptoHasher { private readonly HashAlgorithmName _hashAlgorithmName; public string AlgorithmId { get; } public BclHasher(string algorithmId, HashAlgorithmName hashAlgorithmName) { AlgorithmId = algorithmId; _hashAlgorithmName = hashAlgorithmName; } public byte[] ComputeHash(ReadOnlySpan data) { // Use System.Security.Cryptography internally - this is allowed within plugin return _hashAlgorithmName.Name switch { "SHA256" => SHA256.HashData(data), "SHA384" => SHA384.HashData(data), "SHA512" => SHA512.HashData(data), _ => throw new NotSupportedException($"Hash algorithm '{_hashAlgorithmName}' is not supported.") }; } public string ComputeHashHex(ReadOnlySpan data) { var hash = ComputeHash(data); return Convert.ToHexString(hash).ToLowerInvariant(); } } /// /// Internal ECDSA signer using .NET BCL. /// private sealed class EcdsaSigner : ICryptoSigner { private readonly ECCurve _curve; private readonly HashAlgorithmName _hashAlgorithm; private readonly CryptoKeyReference _keyReference; public string KeyId { get; } public string AlgorithmId { get; } public EcdsaSigner(string algorithmId, ECCurve curve, HashAlgorithmName hashAlgorithm, CryptoKeyReference keyReference) { AlgorithmId = algorithmId; _curve = curve; _hashAlgorithm = hashAlgorithm; _keyReference = keyReference; KeyId = keyReference.KeyId; } public ValueTask SignAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var ecdsa = ECDsa.Create(_curve); // In a real implementation, would load key material from _keyReference // For now, generate ephemeral key (caller should provide key material) var signature = ecdsa.SignData(data.Span, _hashAlgorithm); return new ValueTask(signature); } public ValueTask VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory signature, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var ecdsa = ECDsa.Create(_curve); // In a real implementation, would load public key from _keyReference var isValid = ecdsa.VerifyData(data.Span, signature.Span, _hashAlgorithm); return new ValueTask(isValid); } public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey() { // In a real implementation, would export actual public key // For offline verification, this is typically not needed throw new NotSupportedException("JWK export not supported in offline verification mode."); } } /// /// Internal RSA signer using .NET BCL. /// private sealed class RsaSigner : ICryptoSigner { private readonly HashAlgorithmName _hashAlgorithm; private readonly RSASignaturePadding _padding; private readonly CryptoKeyReference _keyReference; public string KeyId { get; } public string AlgorithmId { get; } public RsaSigner(string algorithmId, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, CryptoKeyReference keyReference) { AlgorithmId = algorithmId; _hashAlgorithm = hashAlgorithm; _padding = padding; _keyReference = keyReference; KeyId = keyReference.KeyId; } public ValueTask SignAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var rsa = RSA.Create(); // In a real implementation, would load key material from _keyReference // For now, generate ephemeral key (caller should provide key material) var signature = rsa.SignData(data.Span, _hashAlgorithm, _padding); return new ValueTask(signature); } public ValueTask VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory signature, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var rsa = RSA.Create(); // In a real implementation, would load public key from _keyReference var isValid = rsa.VerifyData(data.Span, signature.Span, _hashAlgorithm, _padding); return new ValueTask(isValid); } public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey() { // In a real implementation, would export actual public key // For offline verification, this is typically not needed throw new NotSupportedException("JWK export not supported in offline verification mode."); } } /// /// Ephemeral RSA verifier using raw public key bytes (verification-only). /// private sealed class RsaEphemeralVerifier : ICryptoSigner { private readonly HashAlgorithmName _hashAlgorithm; private readonly RSASignaturePadding _padding; private readonly byte[] _publicKeyBytes; public string KeyId { get; } = "ephemeral"; public string AlgorithmId { get; } public RsaEphemeralVerifier(string algorithmId, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, byte[] publicKeyBytes) { AlgorithmId = algorithmId; _hashAlgorithm = hashAlgorithm; _padding = padding; _publicKeyBytes = publicKeyBytes; } public ValueTask SignAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) { throw new NotSupportedException("Ephemeral verifier does not support signing operations."); } public ValueTask VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory signature, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var rsa = RSA.Create(); rsa.ImportSubjectPublicKeyInfo(_publicKeyBytes, out _); var isValid = rsa.VerifyData(data.Span, signature.Span, _hashAlgorithm, _padding); return new ValueTask(isValid); } public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey() { throw new NotSupportedException("JWK export not supported for ephemeral verifiers."); } } /// /// Ephemeral ECDSA verifier using raw public key bytes (verification-only). /// private sealed class EcdsaEphemeralVerifier : ICryptoSigner { private readonly HashAlgorithmName _hashAlgorithm; private readonly byte[] _publicKeyBytes; public string KeyId { get; } = "ephemeral"; public string AlgorithmId { get; } public EcdsaEphemeralVerifier(string algorithmId, HashAlgorithmName hashAlgorithm, byte[] publicKeyBytes) { AlgorithmId = algorithmId; _hashAlgorithm = hashAlgorithm; _publicKeyBytes = publicKeyBytes; } public ValueTask SignAsync(ReadOnlyMemory data, CancellationToken cancellationToken = default) { throw new NotSupportedException("Ephemeral verifier does not support signing operations."); } public ValueTask VerifyAsync(ReadOnlyMemory data, ReadOnlyMemory signature, CancellationToken cancellationToken = default) { // Use System.Security.Cryptography internally - this is allowed within plugin using var ecdsa = ECDsa.Create(); ecdsa.ImportSubjectPublicKeyInfo(_publicKeyBytes, out _); var isValid = ecdsa.VerifyData(data.Span, signature.Span, _hashAlgorithm); return new ValueTask(isValid); } public Microsoft.IdentityModel.Tokens.JsonWebKey ExportPublicJsonWebKey() { throw new NotSupportedException("JWK export not supported for ephemeral verifiers."); } } }