using System.Net.Http.Json; namespace StellaOps.Cryptography.Plugin.SmRemote; public sealed class SmRemoteHttpClient { private readonly HttpClient client; public SmRemoteHttpClient(HttpClient client) { this.client = client ?? throw new ArgumentNullException(nameof(client)); } public async Task GetStatusAsync(CancellationToken cancellationToken = default) { var response = await client.GetAsync("/status", cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var status = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); return status ?? new SmRemoteStatus { IsAvailable = false, Error = "empty response" }; } public async Task SignAsync(string keyId, string algorithmId, byte[] pae, CancellationToken cancellationToken = default) { var request = new SmRemoteSignRequest { KeyId = keyId, AlgorithmId = algorithmId, PayloadBase64 = Convert.ToBase64String(pae) }; var response = await client.PostAsJsonAsync("/sign", request, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var envelope = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); if (envelope is null || string.IsNullOrWhiteSpace(envelope.Signature)) { throw new InvalidOperationException("SM remote sign response was empty."); } return envelope.Signature; } public async Task VerifyAsync(string keyId, string algorithmId, byte[] pae, string signatureBase64, CancellationToken cancellationToken = default) { var request = new SmRemoteVerifyRequest { KeyId = keyId, AlgorithmId = algorithmId, PayloadBase64 = Convert.ToBase64String(pae), Signature = signatureBase64 }; var response = await client.PostAsJsonAsync("/verify", request, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); return result?.Valid == true; } } internal sealed class SmRemoteSignRequest { public string KeyId { get; set; } = string.Empty; public string AlgorithmId { get; set; } = string.Empty; public string PayloadBase64 { get; set; } = string.Empty; } public sealed class SmRemoteSignResponse { public string Signature { get; set; } = string.Empty; } public sealed class SmRemoteVerifyRequest { public string KeyId { get; set; } = string.Empty; public string AlgorithmId { get; set; } = string.Empty; public string PayloadBase64 { get; set; } = string.Empty; public string Signature { get; set; } = string.Empty; } public sealed class SmRemoteVerifyResponse { public bool Valid { get; set; } } public sealed class SmRemoteStatus { public bool IsAvailable { get; set; } public string? ProviderName { get; set; } public string[] SupportedAlgorithms { get; set; } = Array.Empty(); public string? Error { get; set; } }