thecr3w-dev/backend/TheCr3w.Shared/Crypto/ManifestSigner.cs

58 lines
2.3 KiB
C#

using System.Text;
using System.Text.Json;
using TheCr3w.Shared.Models;
namespace TheCr3w.Shared.Crypto;
public static class ManifestSigner
{
/// <summary>
/// Zwraca kanoniczne bajty JSON manifestu wdrożeniowego (z wyłączeniem pola sygnatury).
/// </summary>
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);
}
/// <summary>
/// Wylicza skrót SHA256 kanonicznej reprezentacji manifestu (używany przy bramce zatwierdzania).
/// </summary>
public static string ComputeManifestSha256(DeploymentManifest manifest)
{
var canonicalBytes = GetCanonicalBytes(manifest);
return Ed25519Service.ComputeSha256(canonicalBytes);
}
}