< Summary

Information
Class: AsiBackbone.Core.Signing.CanonicalPayloadHasher
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Signing/CanonicalPayloadHash.cs
Line coverage
93%
Covered lines: 15
Uncovered lines: 1
Coverable lines: 16
Total lines: 162
Line coverage: 93.7%
Branch coverage
50%
Covered branches: 2
Total branches: 4
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ComputeHash(...)50%4493.75%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Signing/CanonicalPayloadHash.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2
 3namespace AsiBackbone.Core.Signing;
 4
 5/// <summary>
 6/// Represents provider-neutral hash metadata for a canonical AsiBackbone payload.
 7/// </summary>
 8public sealed class CanonicalPayloadHash
 9{
 10    private CanonicalPayloadHash(
 11        string artifactType,
 12        string artifactId,
 13        string payloadSchemaVersion,
 14        string canonicalizationVersion,
 15        string hashAlgorithm,
 16        string hashValue)
 17    {
 18        ArtifactType = artifactType;
 19        ArtifactId = artifactId;
 20        PayloadSchemaVersion = payloadSchemaVersion;
 21        CanonicalizationVersion = canonicalizationVersion;
 22        HashAlgorithm = hashAlgorithm;
 23        HashValue = hashValue;
 24    }
 25
 26    /// <summary>
 27    /// Gets the artifact type bound into the hashed payload.
 28    /// </summary>
 29    public string ArtifactType { get; }
 30
 31    /// <summary>
 32    /// Gets the artifact identifier bound into the hashed payload.
 33    /// </summary>
 34    public string ArtifactId { get; }
 35
 36    /// <summary>
 37    /// Gets the payload schema version bound into the hashed payload.
 38    /// </summary>
 39    public string PayloadSchemaVersion { get; }
 40
 41    /// <summary>
 42    /// Gets the canonicalization version used before hashing.
 43    /// </summary>
 44    public string CanonicalizationVersion { get; }
 45
 46    /// <summary>
 47    /// Gets the hash algorithm descriptor.
 48    /// </summary>
 49    public string HashAlgorithm { get; }
 50
 51    /// <summary>
 52    /// Gets the lowercase hexadecimal hash value.
 53    /// </summary>
 54    public string HashValue { get; }
 55
 56    /// <summary>
 57    /// Creates provider-neutral hash metadata.
 58    /// </summary>
 59    public static CanonicalPayloadHash Create(
 60        string artifactType,
 61        string artifactId,
 62        string payloadSchemaVersion,
 63        string canonicalizationVersion,
 64        string hashAlgorithm,
 65        string hashValue)
 66    {
 67        ArgumentException.ThrowIfNullOrWhiteSpace(artifactType);
 68        ArgumentException.ThrowIfNullOrWhiteSpace(artifactId);
 69        ArgumentException.ThrowIfNullOrWhiteSpace(payloadSchemaVersion);
 70        ArgumentException.ThrowIfNullOrWhiteSpace(canonicalizationVersion);
 71        ArgumentException.ThrowIfNullOrWhiteSpace(hashAlgorithm);
 72        ArgumentException.ThrowIfNullOrWhiteSpace(hashValue);
 73
 74        return new CanonicalPayloadHash(
 75            artifactType.Trim(),
 76            artifactId.Trim(),
 77            payloadSchemaVersion.Trim(),
 78            canonicalizationVersion.Trim(),
 79            NormalizeHashAlgorithm(hashAlgorithm),
 80            hashValue.Trim().ToLowerInvariant());
 81    }
 82
 83    /// <summary>
 84    /// Creates signing metadata that carries the hash and canonical payload descriptors without implying that the artif
 85    /// </summary>
 86    public SigningMetadata ToSigningMetadata(IReadOnlyDictionary<string, string>? metadata = null)
 87    {
 88        Dictionary<string, string> hashMetadata = new(StringComparer.Ordinal)
 89        {
 90            ["artifact_id"] = ArtifactId,
 91            ["artifact_type"] = ArtifactType,
 92            ["canonicalization_version"] = CanonicalizationVersion,
 93            ["payload_schema_version"] = PayloadSchemaVersion
 94        };
 95
 96        if (metadata is not null)
 97        {
 98            foreach (KeyValuePair<string, string> item in metadata)
 99            {
 100                if (string.IsNullOrWhiteSpace(item.Key))
 101                {
 102                    continue;
 103                }
 104
 105                hashMetadata[item.Key.Trim()] = item.Value?.Trim() ?? string.Empty;
 106            }
 107        }
 108
 109        return SigningMetadata.Create(
 110            signingHash: HashValue,
 111            hashAlgorithm: HashAlgorithm,
 112            metadata: hashMetadata);
 113    }
 114
 115    internal static string NormalizeHashAlgorithm(string hashAlgorithm)
 116    {
 117        string normalized = hashAlgorithm.Trim();
 118
 119        return normalized.Equals("SHA256", StringComparison.OrdinalIgnoreCase)
 120            ? CanonicalPayloadOptions.DefaultHashAlgorithm
 121            : normalized.Equals(CanonicalPayloadOptions.DefaultHashAlgorithm, StringComparison.OrdinalIgnoreCase)
 122                ? CanonicalPayloadOptions.DefaultHashAlgorithm
 123                : normalized;
 124    }
 125}
 126
 127/// <summary>
 128/// Computes provider-neutral hashes for canonical AsiBackbone payloads.
 129/// </summary>
 130public static class CanonicalPayloadHasher
 131{
 132    /// <summary>
 133    /// Computes a hash over the canonical payload bytes.
 134    /// </summary>
 135    public static CanonicalPayloadHash ComputeHash(
 136        CanonicalPayload payload,
 137        string? hashAlgorithm = null)
 138    {
 128139        ArgumentNullException.ThrowIfNull(payload);
 140
 128141        string algorithm = CanonicalPayloadHash.NormalizeHashAlgorithm(
 128142            string.IsNullOrWhiteSpace(hashAlgorithm)
 128143                ? CanonicalPayloadOptions.DefaultHashAlgorithm
 128144                : hashAlgorithm);
 145
 128146        if (!algorithm.Equals(CanonicalPayloadOptions.DefaultHashAlgorithm, StringComparison.Ordinal))
 147        {
 0148            throw new NotSupportedException($"The built-in canonical payload hasher supports {CanonicalPayloadOptions.De
 149        }
 150
 128151        byte[] hashBytes = SHA256.HashData(payload.ToUtf8Bytes());
 128152        string hashValue = Convert.ToHexString(hashBytes).ToLowerInvariant();
 153
 128154        return CanonicalPayloadHash.Create(
 128155            payload.ArtifactType,
 128156            payload.ArtifactId,
 128157            payload.PayloadSchemaVersion,
 128158            payload.CanonicalizationVersion,
 128159            algorithm,
 128160            hashValue);
 161    }
 162}