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);
}