using System.Text; using System.Text.Json; using TheCr3w.Shared.Models; namespace TheCr3w.Shared.Crypto; public static class ManifestSigner { /// /// Zwraca kanoniczne bajty JSON manifestu wdrożeniowego (z wyłączeniem pola sygnatury). /// public static byte[] GetCanonicalBytes(DeploymentManifest manifest) { var canonicalDto = new { config_hash = manifest.ConfigHash.ToLowerInvariant(), created_at = manifest.CreatedAt.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), created_by = manifest.CreatedBy, environment = manifest.Environment.ToLowerInvariant(), healthcheck = new { interval_seconds = manifest.Healthcheck.IntervalSeconds, path = manifest.Healthcheck.Path, port = manifest.Healthcheck.Port, retries = manifest.Healthcheck.Retries, timeout_seconds = manifest.Healthcheck.TimeoutSeconds }, image = manifest.Image, immutable_digest = manifest.ImmutableDigest.ToLowerInvariant(), manifest_id = manifest.ManifestId.ToString().ToLowerInvariant(), project = manifest.Project.ToLowerInvariant(), required_migrations = manifest.RequiredMigrations.OrderBy(m => m, StringComparer.Ordinal).ToArray(), rollback_strategy = new { mode = manifest.RollbackStrategy.Mode.ToLowerInvariant(), target_immutable_digest = manifest.RollbackStrategy.TargetImmutableDigest.ToLowerInvariant() }, secrets_references = manifest.SecretsReferences.OrderBy(s => s, StringComparer.Ordinal).ToArray(), service_name = manifest.ServiceName.ToLowerInvariant() }; var json = JsonSerializer.Serialize(canonicalDto, new JsonSerializerOptions { WriteIndented = false }); return Encoding.UTF8.GetBytes(json); } /// /// Wylicza skrót SHA256 kanonicznej reprezentacji manifestu (używany przy bramce zatwierdzania). /// public static string ComputeManifestSha256(DeploymentManifest manifest) { var canonicalBytes = GetCanonicalBytes(manifest); return Ed25519Service.ComputeSha256(canonicalBytes); } }