< Summary

Information
Class: AsiBackbone.Core.Signing.VerificationPolicyContext
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Signing/VerificationPolicyContext.cs
Line coverage
100%
Covered lines: 55
Uncovered lines: 0
Coverable lines: 55
Total lines: 183
Line coverage: 100%
Branch coverage
100%
Covered branches: 16
Total branches: 16
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
get_HasMetadata()100%11100%
WithLegacySignatureInputAllowed()100%11100%
Create(...)100%11100%
NormalizeOptional(...)100%22100%
NormalizeMetadata(...)100%1414100%

File(s)

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

#LineLine coverage
 1using System.Collections.ObjectModel;
 2
 3namespace AsiBackbone.Core.Signing;
 4
 5/// <summary>
 6/// Provides host expectations used while evaluating signature verification policy.
 7/// </summary>
 8/// <remarks>
 9/// The context is provider-neutral. It can carry expected key references, policy identifiers, and request metadata with
 10/// </remarks>
 11public sealed class VerificationPolicyContext
 12{
 213    private static readonly IReadOnlyDictionary<string, string> EmptyMetadata =
 214        new ReadOnlyDictionary<string, string>(
 215            new Dictionary<string, string>(StringComparer.Ordinal));
 16
 3517    private VerificationPolicyContext(
 3518        string? purpose,
 3519        string? expectedKeyId,
 3520        string? expectedKeyVersion,
 3521        string? expectedPolicyVersion,
 3522        string? expectedPolicyHash,
 3523        string? requiredProvider,
 3524        string? requiredHashAlgorithm,
 3525        IReadOnlyDictionary<string, string> metadata)
 26    {
 3527        Purpose = NormalizeOptional(purpose);
 3528        ExpectedKeyId = NormalizeOptional(expectedKeyId);
 3529        ExpectedKeyVersion = NormalizeOptional(expectedKeyVersion);
 3530        ExpectedPolicyVersion = NormalizeOptional(expectedPolicyVersion);
 3531        ExpectedPolicyHash = NormalizeOptional(expectedPolicyHash);
 3532        RequiredProvider = NormalizeOptional(requiredProvider);
 3533        RequiredHashAlgorithm = NormalizeOptional(requiredHashAlgorithm);
 3534        Metadata = metadata;
 3535    }
 36
 37    /// <summary>
 38    /// Gets a context with no additional host expectations.
 39    /// </summary>
 40    public static VerificationPolicyContext Default { get; } = new(null, null, null, null, null, null, null, EmptyMetada
 41
 42    /// <summary>
 43    /// Gets the host-defined verification purpose.
 44    /// </summary>
 45    public string? Purpose { get; }
 46
 47    /// <summary>
 48    /// Gets the expected signing key identifier, when required by host policy.
 49    /// </summary>
 50    public string? ExpectedKeyId { get; }
 51
 52    /// <summary>
 53    /// Gets the expected signing key version, when required by host policy.
 54    /// </summary>
 55    public string? ExpectedKeyVersion { get; }
 56
 57    /// <summary>
 58    /// Gets the expected policy version, when the signed metadata is expected to carry one.
 59    /// </summary>
 60    public string? ExpectedPolicyVersion { get; }
 61
 62    /// <summary>
 63    /// Gets the expected policy hash, when the signed metadata is expected to carry one.
 64    /// </summary>
 65    public string? ExpectedPolicyHash { get; }
 66
 67    /// <summary>
 68    /// Gets the required signing provider descriptor, when required by host policy.
 69    /// </summary>
 70    public string? RequiredProvider { get; }
 71
 72    /// <summary>
 73    /// Gets the required hash algorithm descriptor, when required by host policy.
 74    /// </summary>
 75    public string? RequiredHashAlgorithm { get; }
 76
 77    /// <summary>
 78    /// Gets additional provider-neutral verification request metadata.
 79    /// </summary>
 80    public IReadOnlyDictionary<string, string> Metadata { get; }
 81
 82    /// <summary>
 83    /// Gets a value indicating whether additional metadata is present.
 84    /// </summary>
 385    public bool HasMetadata => Metadata.Count > 0;
 86
 87    /// <summary>
 88    /// Gets a value indicating whether verification may fall back to the pre-6.0 hash-only signature input.
 89    /// </summary>
 90    /// <remarks>
 91    /// Defaults to <see langword="false" />. When enabled, an artifact whose version 1 verification fails as an invalid
 92    /// signature is verified again against <see cref="GovernanceSignatureInput.CreateLegacy" />. A legacy signature
 93    /// authenticates the canonical payload hash only, so it cannot satisfy <see cref="ExpectedPolicyVersion" /> or
 94    /// <see cref="ExpectedPolicyHash" />; such pins deny with <c>signature.policy-context-not-authenticated</c>.
 95    /// </remarks>
 96    public bool AllowLegacySignatureInput { get; private init; }
 97
 98    /// <summary>
 99    /// Creates a copy of this context that accepts artifacts signed with the pre-6.0 hash-only signature input.
 100    /// </summary>
 101    /// <remarks>
 102    /// Use this only for reviewing or migrating artifacts signed before 6.0. Signing metadata labels on such artifacts,
 103    /// including the policy version and policy hash, are not covered by the signature.
 104    /// <para>
 105    /// This opt-in is a supported verification path, not a deprecated one. Governance evidence signed before 6.0 must
 106    /// stay verifiable for its audit-retention period, and removing this method would leave no way to verify it.
 107    /// Producing new hash-only signatures is deprecated separately through
 108    /// <see cref="GovernanceSignatureInput.CreateLegacy" /> (<c>ASIB902</c>).
 109    /// </para>
 110    /// </remarks>
 111    /// <returns>A context identical to this one with <see cref="AllowLegacySignatureInput" /> set.</returns>
 112    public VerificationPolicyContext WithLegacySignatureInputAllowed()
 113    {
 3114        return new VerificationPolicyContext(
 3115            Purpose,
 3116            ExpectedKeyId,
 3117            ExpectedKeyVersion,
 3118            ExpectedPolicyVersion,
 3119            ExpectedPolicyHash,
 3120            RequiredProvider,
 3121            RequiredHashAlgorithm,
 3122            Metadata)
 3123        {
 3124            AllowLegacySignatureInput = true
 3125        };
 126    }
 127
 128    /// <summary>
 129    /// Creates a provider-neutral verification policy context.
 130    /// </summary>
 131    public static VerificationPolicyContext Create(
 132        string? purpose = null,
 133        string? expectedKeyId = null,
 134        string? expectedKeyVersion = null,
 135        string? expectedPolicyVersion = null,
 136        string? expectedPolicyHash = null,
 137        string? requiredProvider = null,
 138        string? requiredHashAlgorithm = null,
 139        IReadOnlyDictionary<string, string>? metadata = null)
 140    {
 30141        return new VerificationPolicyContext(
 30142            purpose,
 30143            expectedKeyId,
 30144            expectedKeyVersion,
 30145            expectedPolicyVersion,
 30146            expectedPolicyHash,
 30147            requiredProvider,
 30148            requiredHashAlgorithm,
 30149            NormalizeMetadata(metadata));
 150    }
 151
 152    private static string? NormalizeOptional(string? value)
 153    {
 245154        return string.IsNullOrWhiteSpace(value)
 245155            ? null
 245156            : value.Trim();
 157    }
 158
 159    private static IReadOnlyDictionary<string, string> NormalizeMetadata(
 160        IReadOnlyDictionary<string, string>? metadata)
 161    {
 30162        if (metadata is null || metadata.Count == 0)
 163        {
 27164            return EmptyMetadata;
 165        }
 166
 3167        Dictionary<string, string> normalizedMetadata = new(StringComparer.Ordinal);
 168
 16169        foreach (KeyValuePair<string, string> item in metadata)
 170        {
 5171            if (string.IsNullOrWhiteSpace(item.Key))
 172            {
 173                continue;
 174            }
 175
 3176            normalizedMetadata[item.Key.Trim()] = item.Value?.Trim() ?? string.Empty;
 177        }
 178
 3179        return normalizedMetadata.Count == 0
 3180            ? EmptyMetadata
 3181            : new ReadOnlyDictionary<string, string>(normalizedMetadata);
 182    }
 183}