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