using StellaOps.Cryptography; using System; using System.Security.Cryptography; using System.Text; namespace StellaOps.Excititor.WebService.Services; /// /// Service interface for hashing operations in Excititor (CRYPTO-90-001). /// Abstracts hashing implementation to support GOST/SM algorithms via ICryptoProviderRegistry. /// public interface IVexHashingService { /// /// Compute hash of a UTF-8 encoded string. /// string ComputeHash(string value, string algorithm = "sha256"); /// /// Compute hash of raw bytes. /// string ComputeHash(ReadOnlySpan data, string algorithm = "sha256"); /// /// Try to compute hash of raw bytes with stack-allocated buffer optimization. /// bool TryComputeHash(ReadOnlySpan data, Span destination, out int bytesWritten, string algorithm = "sha256"); /// /// Format a hash digest with algorithm prefix. /// string FormatDigest(string algorithm, ReadOnlySpan digest); } /// /// Default implementation of that uses ICryptoProviderRegistry /// when available, falling back to System.Security.Cryptography for SHA-256. /// public sealed class VexHashingService : IVexHashingService { private readonly ICryptoProviderRegistry? _registry; public VexHashingService(ICryptoProviderRegistry? registry = null) { _registry = registry; } public string ComputeHash(string value, string algorithm = "sha256") { if (string.IsNullOrEmpty(value)) { return string.Empty; } var bytes = Encoding.UTF8.GetBytes(value); return ComputeHash(bytes, algorithm); } public string ComputeHash(ReadOnlySpan data, string algorithm = "sha256") { Span buffer = stackalloc byte[64]; // Large enough for SHA-512 and GOST if (!TryComputeHash(data, buffer, out var written, algorithm)) { throw new InvalidOperationException($"Failed to compute {algorithm} hash."); } return FormatDigest(algorithm, buffer[..written]); } public bool TryComputeHash(ReadOnlySpan data, Span destination, out int bytesWritten, string algorithm = "sha256") { bytesWritten = 0; // Try to use crypto provider registry first for pluggable algorithms if (_registry is not null) { try { var resolution = _registry.ResolveHasher(algorithm); var hasher = resolution.Hasher; var result = hasher.ComputeHash(data); if (result.Length <= destination.Length) { result.CopyTo(destination); bytesWritten = result.Length; return true; } } catch { // Fall through to built-in implementation } } // Fall back to System.Security.Cryptography for standard algorithms var normalizedAlgorithm = algorithm.ToLowerInvariant().Replace("-", string.Empty); return normalizedAlgorithm switch { "sha256" => SHA256.TryHashData(data, destination, out bytesWritten), "sha384" => SHA384.TryHashData(data, destination, out bytesWritten), "sha512" => SHA512.TryHashData(data, destination, out bytesWritten), _ => throw new NotSupportedException($"Unsupported hash algorithm: {algorithm}") }; } public string FormatDigest(string algorithm, ReadOnlySpan digest) { var normalizedAlgorithm = algorithm.ToLowerInvariant().Replace("-", string.Empty); var hexDigest = Convert.ToHexString(digest).ToLowerInvariant(); return $"{normalizedAlgorithm}:{hexDigest}"; } }