// ----------------------------------------------------------------------------- // CommandHandlers.Crypto.cs // Sprint: SPRINT_4100_0006_0001 - Crypto Plugin CLI Architecture // Description: Command handlers for cryptographic signing and verification. // ----------------------------------------------------------------------------- using System.Text; using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Spectre.Console; using StellaOps.Cryptography; using StellaOps.Cryptography.Kms; namespace StellaOps.Cli.Commands; internal static partial class CommandHandlers { /// /// Handle crypto sign command. /// Signs artifacts using configured crypto provider with regional compliance support. /// internal static async Task HandleCryptoSignAsync( IServiceProvider services, string input, string? output, string? providerName, string? keyId, string format, bool detached, bool verbose, CancellationToken cancellationToken) { var logger = services.GetRequiredService>(); try { AnsiConsole.MarkupLine("[blue]Cryptographic Signing Operation[/]"); AnsiConsole.WriteLine(); // Validate input if (!File.Exists(input)) { AnsiConsole.MarkupLine($"[red]Error: Input file not found: {Markup.Escape(input)}[/]"); return 1; } output ??= $"{input}.sig"; // Display operation details var table = new Table() .Border(TableBorder.Rounded) .AddColumn("Parameter") .AddColumn("Value"); table.AddRow("Input", Markup.Escape(input)); table.AddRow("Output", Markup.Escape(output)); table.AddRow("Format", format); table.AddRow("Detached", detached.ToString()); if (providerName != null) table.AddRow("Provider Override", providerName); if (keyId != null) table.AddRow("Key ID", keyId); AnsiConsole.Write(table); AnsiConsole.WriteLine(); // Get crypto provider from DI var cryptoProviders = services.GetServices().ToList(); if (cryptoProviders.Count == 0) { AnsiConsole.MarkupLine("[red]Error: No crypto providers available. Check your distribution and configuration.[/]"); AnsiConsole.MarkupLine("[yellow]Hint: Use 'stella crypto profiles' to list available providers.[/]"); return 1; } ICryptoProvider? provider = null; if (providerName != null) { provider = cryptoProviders.FirstOrDefault(p => p.Name.Equals(providerName, StringComparison.OrdinalIgnoreCase)); if (provider == null) { AnsiConsole.MarkupLine($"[red]Error: Provider '{Markup.Escape(providerName)}' not found.[/]"); AnsiConsole.MarkupLine("[yellow]Available providers:[/]"); foreach (var p in cryptoProviders) { AnsiConsole.MarkupLine($" - {Markup.Escape(p.Name)}"); } return 1; } } else { provider = cryptoProviders.First(); if (verbose) { AnsiConsole.MarkupLine($"[dim]Using default provider: {Markup.Escape(provider.Name)}[/]"); } } // Read input file var inputData = await File.ReadAllBytesAsync(input, cancellationToken); AnsiConsole.Status() .Start("Signing...", ctx => { ctx.Spinner(Spinner.Known.Dots); ctx.SpinnerStyle(Style.Parse("blue")); // Signing operation would happen here // For now, this is a stub implementation Thread.Sleep(500); }); // Create stub signature var signatureData = CreateStubSignature(inputData, format, provider.Name); await File.WriteAllBytesAsync(output, signatureData, cancellationToken); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine("[green]✓ Signature created successfully[/]"); AnsiConsole.MarkupLine($" Signature: [bold]{Markup.Escape(output)}[/]"); AnsiConsole.MarkupLine($" Provider: {Markup.Escape(provider.Name)}"); AnsiConsole.MarkupLine($" Format: {format}"); if (verbose) { AnsiConsole.WriteLine(); AnsiConsole.MarkupLine($"[dim]Signature size: {signatureData.Length:N0} bytes[/]"); } return 0; } catch (Exception ex) { logger.LogError(ex, "Crypto sign operation failed"); AnsiConsole.MarkupLine($"[red]Error: {Markup.Escape(ex.Message)}[/]"); return 1; } } /// /// Handle crypto verify command. /// Verifies signatures using configured crypto provider. /// internal static async Task HandleCryptoVerifyAsync( IServiceProvider services, string input, string? signature, string? providerName, string? trustPolicy, string? format, bool verbose, CancellationToken cancellationToken) { var logger = services.GetRequiredService>(); try { AnsiConsole.MarkupLine("[blue]Cryptographic Verification Operation[/]"); AnsiConsole.WriteLine(); // Validate input if (!File.Exists(input)) { AnsiConsole.MarkupLine($"[red]Error: Input file not found: {Markup.Escape(input)}[/]"); return 1; } signature ??= $"{input}.sig"; if (!File.Exists(signature)) { AnsiConsole.MarkupLine($"[red]Error: Signature file not found: {Markup.Escape(signature)}[/]"); return 1; } // Display operation details var table = new Table() .Border(TableBorder.Rounded) .AddColumn("Parameter") .AddColumn("Value"); table.AddRow("Input", Markup.Escape(input)); table.AddRow("Signature", Markup.Escape(signature)); if (format != null) table.AddRow("Format", format); if (providerName != null) table.AddRow("Provider Override", providerName); if (trustPolicy != null) table.AddRow("Trust Policy", Markup.Escape(trustPolicy)); AnsiConsole.Write(table); AnsiConsole.WriteLine(); // Get crypto provider from DI var cryptoProviders = services.GetServices().ToList(); if (cryptoProviders.Count == 0) { AnsiConsole.MarkupLine("[red]Error: No crypto providers available. Check your distribution and configuration.[/]"); return 1; } ICryptoProvider? provider = null; if (providerName != null) { provider = cryptoProviders.FirstOrDefault(p => p.Name.Equals(providerName, StringComparison.OrdinalIgnoreCase)); if (provider == null) { AnsiConsole.MarkupLine($"[red]Error: Provider '{Markup.Escape(providerName)}' not found.[/]"); return 1; } } else { provider = cryptoProviders.First(); if (verbose) { AnsiConsole.MarkupLine($"[dim]Using default provider: {Markup.Escape(provider.Name)}[/]"); } } // Read files var inputData = await File.ReadAllBytesAsync(input, cancellationToken); var signatureData = await File.ReadAllBytesAsync(signature, cancellationToken); bool isValid = false; AnsiConsole.Status() .Start("Verifying signature...", ctx => { ctx.Spinner(Spinner.Known.Dots); ctx.SpinnerStyle(Style.Parse("blue")); // Verification would happen here // Stub implementation - always succeeds for now Thread.Sleep(300); isValid = true; }); AnsiConsole.WriteLine(); if (isValid) { AnsiConsole.MarkupLine("[green]✓ Signature verification successful[/]"); AnsiConsole.MarkupLine($" Provider: {Markup.Escape(provider.Name)}"); if (verbose) { AnsiConsole.WriteLine(); AnsiConsole.MarkupLine("[dim]Signature Details:[/]"); AnsiConsole.MarkupLine($"[dim] Algorithm: STUB-ALGORITHM[/]"); AnsiConsole.MarkupLine($"[dim] Key ID: STUB-KEY-ID[/]"); AnsiConsole.MarkupLine($"[dim] Timestamp: {DateTimeOffset.UtcNow:O}[/]"); } return 0; } else { AnsiConsole.MarkupLine("[red]✗ Signature verification failed[/]"); return 1; } } catch (Exception ex) { logger.LogError(ex, "Crypto verify operation failed"); AnsiConsole.MarkupLine($"[red]Error: {Markup.Escape(ex.Message)}[/]"); return 1; } } /// /// Handle crypto profiles command. /// Lists available crypto providers and their capabilities. /// internal static async Task HandleCryptoProfilesAsync( IServiceProvider services, bool showDetails, string? providerFilter, bool test, bool verbose, CancellationToken cancellationToken) { var logger = services.GetRequiredService>(); try { AnsiConsole.MarkupLine("[blue]Available Cryptographic Providers[/]"); AnsiConsole.WriteLine(); // Get crypto providers from DI var cryptoProviders = services.GetServices().ToList(); if (providerFilter != null) { cryptoProviders = cryptoProviders .Where(p => p.Name.Contains(providerFilter, StringComparison.OrdinalIgnoreCase)) .ToList(); } if (cryptoProviders.Count == 0) { if (providerFilter != null) { AnsiConsole.MarkupLine($"[yellow]No providers matching '{Markup.Escape(providerFilter)}' found.[/]"); } else { AnsiConsole.MarkupLine("[yellow]No crypto providers available.[/]"); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine("[dim]This may indicate:[/]"); AnsiConsole.MarkupLine("[dim] • You are using the international distribution (GOST/eIDAS/SM disabled)[/]"); AnsiConsole.MarkupLine("[dim] • Crypto plugins are not properly configured[/]"); AnsiConsole.MarkupLine("[dim] • Build-time distribution flags were not set[/]"); } return 1; } // Display providers foreach (var provider in cryptoProviders) { var panel = new Panel(CreateProviderTable(provider, showDetails, test)) .Header($"[bold]{Markup.Escape(provider.Name)}[/]") .Border(BoxBorder.Rounded) .BorderColor(Color.Blue); AnsiConsole.Write(panel); AnsiConsole.WriteLine(); } // Display distribution info AnsiConsole.MarkupLine("[dim]Distribution Information:[/]"); var distributionTable = new Table() .Border(TableBorder.Rounded) .AddColumn("Feature") .AddColumn("Status"); #if STELLAOPS_ENABLE_GOST distributionTable.AddRow("GOST (Russia)", "[green]Enabled[/]"); #else distributionTable.AddRow("GOST (Russia)", "[dim]Disabled[/]"); #endif #if STELLAOPS_ENABLE_EIDAS distributionTable.AddRow("eIDAS (EU)", "[green]Enabled[/]"); #else distributionTable.AddRow("eIDAS (EU)", "[dim]Disabled[/]"); #endif #if STELLAOPS_ENABLE_SM distributionTable.AddRow("SM (China)", "[green]Enabled[/]"); #else distributionTable.AddRow("SM (China)", "[dim]Disabled[/]"); #endif distributionTable.AddRow("BouncyCastle", "[green]Enabled[/]"); AnsiConsole.Write(distributionTable); return 0; } catch (Exception ex) { logger.LogError(ex, "Crypto profiles operation failed"); AnsiConsole.MarkupLine($"[red]Error: {Markup.Escape(ex.Message)}[/]"); return 1; } } private static Table CreateProviderTable(ICryptoProvider provider, bool showDetails, bool runTests) { var table = new Table() .Border(TableBorder.None) .HideHeaders() .AddColumn("Property") .AddColumn("Value"); table.AddRow("[dim]Provider Name:[/]", Markup.Escape(provider.Name)); table.AddRow("[dim]Status:[/]", "[green]Available[/]"); if (showDetails) { table.AddRow("[dim]Type:[/]", provider.GetType().Name); } if (runTests) { table.AddRow("[dim]Diagnostics:[/]", "[yellow]Test mode not yet implemented[/]"); } return table; } private static byte[] CreateStubSignature(byte[] data, string format, string providerName) { // Stub implementation - creates a JSON signature envelope var signature = new { format = format, provider = providerName, timestamp = DateTimeOffset.UtcNow.ToString("O"), dataHash = CryptoHashFactory.CreateDefault().ComputeHashHex(data, HashAlgorithms.Sha256), signature = "STUB-SIGNATURE-BASE64", keyId = "STUB-KEY-ID" }; return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(signature, new JsonSerializerOptions { WriteIndented = true })); } }