7b0b9c7365
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
57 lines
2.4 KiB
C#
57 lines
2.4 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 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 (<c>ContentHash</c>, <c>Encryption</c>) to known sentinel
|
|
/// values — those depend on the ciphertext and the IV, so they cannot themselves
|
|
/// be authenticated, but every OTHER manifest field (<c>SourceEnvironment</c>,
|
|
/// <c>ExportedBy</c>, <c>ScadaBridgeVersion</c>, <c>Summary</c>, <c>Contents</c>,
|
|
/// <c>CreatedAtUtc</c>, …) participates in the GCM tag.
|
|
/// <para>
|
|
/// Threading this byte array through <c>AesGcm.Encrypt</c> / <c>AesGcm.Decrypt</c>
|
|
/// makes the Step-4 "type the source environment name to confirm" gate
|
|
/// tamper-evident: a flipped <c>SourceEnvironment</c> on a stolen bundle yields
|
|
/// an <c>AuthenticationTagMismatchException</c> on decrypt instead of producing
|
|
/// a valid plaintext with a forged origin label.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class BundleManifestAad
|
|
{
|
|
/// <summary>
|
|
/// JSON options matching <c>BundleSerializer.JsonOptions</c> so the AAD bytes
|
|
/// are stable across the encrypt + decrypt side.
|
|
/// </summary>
|
|
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
{
|
|
WriteIndented = false,
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
Converters = { new JsonStringEnumConverter() },
|
|
};
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="manifest">The manifest whose non-derivative fields should be authenticated.</param>
|
|
/// <returns>SHA-256 digest of the canonicalised manifest bytes.</returns>
|
|
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);
|
|
}
|
|
}
|