// SPDX-License-Identifier: AGPL-3.0-or-later
// Sprint: SPRINT_4100_0006_0001 - Crypto Plugin CLI Architecture
// Task: T10 - Crypto profile validation on CLI startup
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Cryptography.DependencyInjection;
namespace StellaOps.Cli.Services;
///
/// Validates crypto provider configuration on CLI startup.
/// Ensures active profile references available providers and configuration is valid.
///
internal sealed class CryptoProfileValidator
{
private readonly ILogger _logger;
public CryptoProfileValidator(ILogger logger)
{
_logger = logger;
}
///
/// Validate crypto configuration on startup.
///
public ValidationResult Validate(
IServiceProvider serviceProvider,
bool enforceAvailability = false,
bool failOnMissing = false)
{
var result = new ValidationResult();
try
{
// Check if crypto registry is available
var registry = serviceProvider.GetService();
if (registry == null)
{
result.Warnings.Add("Crypto provider registry not configured - crypto commands will be unavailable");
_logger.LogWarning("Crypto provider registry not available in this environment");
return result;
}
// Get registry options
var optionsMonitor = serviceProvider.GetService>();
if (optionsMonitor == null)
{
result.Warnings.Add("Crypto provider registry options not configured");
return result;
}
var options = optionsMonitor.CurrentValue;
var activeProfile = options.ActiveProfile ?? "default";
_logger.LogDebug("Validating crypto profile: {Profile}", activeProfile);
// List available providers
var availableProviders = registry.Providers.Select(p => p.Name).ToList();
if (availableProviders.Count == 0)
{
var message = "No crypto providers registered - check distribution build flags";
if (failOnMissing)
{
result.Errors.Add(message);
_logger.LogError(message);
}
else
{
result.Warnings.Add(message);
_logger.LogWarning(message);
}
return result;
}
_logger.LogInformation("Available crypto providers: {Providers}", string.Join(", ", availableProviders));
// Validate distribution-specific providers
ValidateDistributionProviders(result, availableProviders);
// Check provider availability if enforced
if (enforceAvailability)
{
foreach (var provider in registry.Providers)
{
try
{
// Attempt to check provider availability
// This would require ICryptoProviderDiagnostics interface
_logger.LogDebug("Provider {Provider} is available", provider.Name);
}
catch (Exception ex)
{
result.Warnings.Add($"Provider {provider.Name} may not be fully functional: {ex.Message}");
_logger.LogWarning(ex, "Provider {Provider} availability check failed", provider.Name);
}
}
}
result.IsValid = result.Errors.Count == 0;
result.ActiveProfile = activeProfile;
result.AvailableProviders = availableProviders;
}
catch (Exception ex)
{
result.Errors.Add($"Crypto validation failed: {ex.Message}");
_logger.LogError(ex, "Crypto profile validation failed");
}
return result;
}
private void ValidateDistributionProviders(ValidationResult result, List availableProviders)
{
// Check distribution-specific expectations
#if STELLAOPS_ENABLE_GOST
if (!availableProviders.Any(p => p.Contains("gost", StringComparison.OrdinalIgnoreCase)))
{
result.Warnings.Add("GOST distribution enabled but no GOST providers found");
_logger.LogWarning("GOST distribution flag set but no GOST providers registered");
}
else
{
_logger.LogInformation("GOST crypto providers available (Russia distribution)");
}
#endif
#if STELLAOPS_ENABLE_EIDAS
if (!availableProviders.Any(p => p.Contains("eidas", StringComparison.OrdinalIgnoreCase)))
{
result.Warnings.Add("eIDAS distribution enabled but no eIDAS providers found");
_logger.LogWarning("eIDAS distribution flag set but no eIDAS providers registered");
}
else
{
_logger.LogInformation("eIDAS crypto providers available (EU distribution)");
}
#endif
#if STELLAOPS_ENABLE_SM
if (!availableProviders.Any(p => p.Contains("sm", StringComparison.OrdinalIgnoreCase)))
{
result.Warnings.Add("SM distribution enabled but no SM providers found");
_logger.LogWarning("SM distribution flag set but no SM providers registered");
}
else
{
_logger.LogInformation("SM crypto providers available (China distribution)");
}
#endif
// BouncyCastle should always be available in international distribution
if (!availableProviders.Any(p => p.Contains("bouncycastle", StringComparison.OrdinalIgnoreCase)))
{
_logger.LogDebug("BouncyCastle provider not found - may be using distribution-specific crypto only");
}
}
}
///
/// Result of crypto profile validation.
///
internal sealed class ValidationResult
{
public bool IsValid { get; set; }
public string? ActiveProfile { get; set; }
public List AvailableProviders { get; set; } = new();
public List Errors { get; set; } = new();
public List Warnings { get; set; } = new();
public bool HasWarnings => Warnings.Count > 0;
public bool HasErrors => Errors.Count > 0;
}