using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace StellaOps.Cryptography; /// /// Service for resolving cryptographic algorithms based on the active compliance profile. /// public sealed class CryptoComplianceService : ICryptoComplianceService { private readonly IOptionsMonitor _options; private readonly ILogger _logger; public CryptoComplianceService( IOptionsMonitor options, ILogger? logger = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger ?? NullLogger.Instance; } /// /// Gets the currently active compliance profile. /// public ComplianceProfile ActiveProfile { get { var opts = _options.CurrentValue; opts.ApplyEnvironmentOverrides(); return ComplianceProfiles.GetProfile(opts.ProfileId); } } /// /// Gets the algorithm for a given purpose based on the active profile. /// /// The hash purpose. /// The algorithm identifier. public string GetAlgorithmForPurpose(string purpose) { if (string.IsNullOrWhiteSpace(purpose)) { throw new ArgumentException("Purpose cannot be null or empty.", nameof(purpose)); } var opts = _options.CurrentValue; opts.ApplyEnvironmentOverrides(); // Check for purpose overrides first if (opts.PurposeOverrides?.TryGetValue(purpose, out var overrideAlgorithm) == true && !string.IsNullOrWhiteSpace(overrideAlgorithm)) { _logger.LogDebug( "Using purpose override for {Purpose}: {Algorithm} (profile: {Profile})", purpose, overrideAlgorithm, opts.ProfileId); return overrideAlgorithm; } // Get from active profile var profile = ComplianceProfiles.GetProfile(opts.ProfileId); return profile.GetAlgorithmForPurpose(purpose); } /// /// Gets the hash prefix for a given purpose (e.g., "blake3:", "sha256:"). /// /// The hash purpose. /// The hash prefix string. public string GetHashPrefix(string purpose) { if (string.IsNullOrWhiteSpace(purpose)) { throw new ArgumentException("Purpose cannot be null or empty.", nameof(purpose)); } var profile = ActiveProfile; return profile.GetHashPrefix(purpose); } /// /// Validates an algorithm request against the active compliance profile. /// /// The hash purpose (or null if unknown). /// The requested algorithm. /// /// Thrown when StrictValidation is enabled and the algorithm is not compliant. /// public void ValidateAlgorithm(string? purpose, string requestedAlgorithm) { var opts = _options.CurrentValue; opts.ApplyEnvironmentOverrides(); var profile = ComplianceProfiles.GetProfile(opts.ProfileId); // If purpose is specified, check compliance if (!string.IsNullOrWhiteSpace(purpose)) { if (!profile.IsCompliant(purpose, requestedAlgorithm)) { var expectedAlgorithm = profile.GetAlgorithmForPurpose(purpose); var message = $"Algorithm '{requestedAlgorithm}' is not compliant for purpose '{purpose}' " + $"in profile '{profile.ProfileId}'. Expected: '{expectedAlgorithm}'."; if (opts.StrictValidation) { _logger.LogError( "Compliance violation: {Message}", message); throw new CryptoComplianceException(message, profile.ProfileId, purpose, requestedAlgorithm, expectedAlgorithm); } if (opts.WarnOnNonCompliant) { _logger.LogWarning( "Compliance warning: {Message}", message); } } } } /// /// Checks if the given algorithm is compliant for any purpose in the active profile. /// /// The algorithm to check. /// True if the algorithm is used by any purpose in the profile. public bool IsAlgorithmCompliant(string algorithmId) { var profile = ActiveProfile; return HashPurpose.All.Any(purpose => profile.IsCompliant(purpose, algorithmId)); } } /// /// Interface for compliance-aware cryptographic algorithm resolution. /// public interface ICryptoComplianceService { /// /// Gets the currently active compliance profile. /// ComplianceProfile ActiveProfile { get; } /// /// Gets the algorithm for a given purpose based on the active profile. /// string GetAlgorithmForPurpose(string purpose); /// /// Gets the hash prefix for a given purpose. /// string GetHashPrefix(string purpose); /// /// Validates an algorithm request against the active compliance profile. /// void ValidateAlgorithm(string? purpose, string requestedAlgorithm); /// /// Checks if the given algorithm is compliant for any purpose in the active profile. /// bool IsAlgorithmCompliant(string algorithmId); }