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