using System.Security.Cryptography;
using System.Text;
namespace StellaOps.Evidence.Bundle;
/// Content-addressed hash set for all evidence artifacts.
public sealed class EvidenceHashSet
{
public string Algorithm { get; init; } = "SHA-256";
public required IReadOnlyList Hashes { get; init; }
public required string CombinedHash { get; init; }
public IReadOnlyDictionary? LabeledHashes { get; init; }
public static EvidenceHashSet Compute(IDictionary labeledHashes)
{
ArgumentNullException.ThrowIfNull(labeledHashes);
var sorted = labeledHashes.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).ToList();
var combined = string.Join(":", sorted.Select(kvp => $"{kvp.Key}={kvp.Value}"));
var hash = ComputeSha256(combined);
return new EvidenceHashSet
{
Hashes = sorted.Select(kvp => kvp.Value).ToList(),
CombinedHash = hash,
LabeledHashes = sorted.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
};
}
public static EvidenceHashSet Empty() => new()
{
Hashes = Array.Empty(),
CombinedHash = ComputeSha256(string.Empty),
LabeledHashes = new Dictionary()
};
private static string ComputeSha256(string input) =>
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input))).ToLowerInvariant();
}