using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Transport;
namespace ZB.MOM.WW.ScadaBridge.Transport.Encryption;
///
/// T-005: computes the AES-GCM Associated Authenticated Data (AAD) for a bundle's
/// encrypted payload. AAD is the SHA-256 of the manifest after normalising the two
/// derivative fields (ContentHash, Encryption) to known sentinel
/// values — those depend on the ciphertext and the IV, so they cannot themselves
/// be authenticated, but every OTHER manifest field (SourceEnvironment,
/// ExportedBy, ScadaBridgeVersion, Summary, Contents,
/// CreatedAtUtc, …) participates in the GCM tag.
///
/// Threading this byte array through AesGcm.Encrypt / AesGcm.Decrypt
/// makes the Step-4 "type the source environment name to confirm" gate
/// tamper-evident: a flipped SourceEnvironment on a stolen bundle yields
/// an AuthenticationTagMismatchException on decrypt instead of producing
/// a valid plaintext with a forged origin label.
///
///
public static class BundleManifestAad
{
///
/// JSON options matching BundleSerializer.JsonOptions so the AAD bytes
/// are stable across the encrypt + decrypt side.
///
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter() },
};
///
/// Computes the AAD bytes for the supplied manifest. The two derivative
/// fields are normalised to fixed sentinels so the AAD is independent of the
/// ciphertext / IV that will eventually populate them in the on-disk
/// manifest.
///
/// The manifest whose non-derivative fields should be authenticated.
/// SHA-256 digest of the canonicalised manifest bytes.
public static byte[] Compute(BundleManifest manifest)
{
ArgumentNullException.ThrowIfNull(manifest);
var canonical = manifest with
{
ContentHash = string.Empty,
Encryption = null,
};
var canonicalBytes = JsonSerializer.SerializeToUtf8Bytes(canonical, JsonOptions);
return SHA256.HashData(canonicalBytes);
}
}