| | | 1 | | using System.Collections.ObjectModel; |
| | | 2 | | |
| | | 3 | | namespace AsiBackbone.Core.Signing; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Maps signature verification categories to host-facing verification policy actions. |
| | | 7 | | /// </summary> |
| | | 8 | | public sealed class VerificationPolicyOptions |
| | | 9 | | { |
| | 2 | 10 | | private static readonly IReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAction> DefaultActionMa |
| | 2 | 11 | | new ReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAction>( |
| | 2 | 12 | | new Dictionary<SignatureVerificationCategory, VerificationPolicyAction> |
| | 2 | 13 | | { |
| | 2 | 14 | | [SignatureVerificationCategory.Valid] = VerificationPolicyAction.Allow, |
| | 2 | 15 | | [SignatureVerificationCategory.InvalidSignature] = VerificationPolicyAction.Deny, |
| | 2 | 16 | | [SignatureVerificationCategory.HashMismatch] = VerificationPolicyAction.Deny, |
| | 2 | 17 | | [SignatureVerificationCategory.MissingSignature] = VerificationPolicyAction.Deny, |
| | 2 | 18 | | [SignatureVerificationCategory.UnknownKeyVersion] = VerificationPolicyAction.Escalate, |
| | 2 | 19 | | [SignatureVerificationCategory.RevokedKey] = VerificationPolicyAction.Deny, |
| | 2 | 20 | | [SignatureVerificationCategory.ProviderUnavailable] = VerificationPolicyAction.Defer, |
| | 2 | 21 | | [SignatureVerificationCategory.CanonicalizationMismatch] = VerificationPolicyAction.Deny, |
| | 2 | 22 | | [SignatureVerificationCategory.UnsupportedAlgorithm] = VerificationPolicyAction.Deny, |
| | 2 | 23 | | [SignatureVerificationCategory.UntrustedKey] = VerificationPolicyAction.Deny, |
| | 2 | 24 | | [SignatureVerificationCategory.UntrustedSigningContext] = VerificationPolicyAction.Deny, |
| | 2 | 25 | | [SignatureVerificationCategory.Failed] = VerificationPolicyAction.Escalate |
| | 2 | 26 | | }); |
| | | 27 | | |
| | 8 | 28 | | private VerificationPolicyOptions(IReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAction> actio |
| | | 29 | | { |
| | 8 | 30 | | Actions = actions; |
| | 8 | 31 | | } |
| | | 32 | | |
| | | 33 | | /// <summary> |
| | | 34 | | /// Gets the default verification policy action map. |
| | | 35 | | /// </summary> |
| | | 36 | | /// <remarks> |
| | | 37 | | /// Only <see cref="SignatureVerificationCategory.Valid" /> allows. Every category that reports an integrity or trus |
| | | 38 | | /// failure denies: an invalid or missing signature, a hash, canonicalization, or algorithm mismatch, a revoked or |
| | | 39 | | /// untrusted key, and an untrusted signing context. Only conditions that a retry or an operator could legitimately |
| | | 40 | | /// resolve use softer actions: <see cref="SignatureVerificationCategory.ProviderUnavailable" /> defers, and |
| | | 41 | | /// <see cref="SignatureVerificationCategory.UnknownKeyVersion" /> and <see cref="SignatureVerificationCategory.Fail |
| | | 42 | | /// escalate. |
| | | 43 | | /// </remarks> |
| | | 44 | | public static VerificationPolicyOptions Default { get; } = new(DefaultActionMap); |
| | | 45 | | |
| | | 46 | | /// <summary> |
| | | 47 | | /// Gets the configured verification category to host action map. |
| | | 48 | | /// </summary> |
| | | 49 | | public IReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAction> Actions { get; } |
| | | 50 | | |
| | | 51 | | /// <summary> |
| | | 52 | | /// Creates verification policy options with optional host overrides. |
| | | 53 | | /// </summary> |
| | | 54 | | /// <param name="actionOverrides">Category to action overrides applied over the defaults.</param> |
| | | 55 | | /// <param name="allowUnsafeAllowOverrides"> |
| | | 56 | | /// When <see langword="true" />, permits mapping a failure category to <see cref="VerificationPolicyAction.Allow" / |
| | | 57 | | /// Such a mapping makes a failed verification indistinguishable from a successful one, so it must be opted into |
| | | 58 | | /// deliberately rather than reached by configuration drift. |
| | | 59 | | /// </param> |
| | | 60 | | /// <exception cref="ArgumentOutOfRangeException">A category or action is undefined, or a failure category was mappe |
| | | 61 | | public static VerificationPolicyOptions Create( |
| | | 62 | | IReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAction>? actionOverrides = null, |
| | | 63 | | bool allowUnsafeAllowOverrides = false) |
| | | 64 | | { |
| | 9 | 65 | | Dictionary<SignatureVerificationCategory, VerificationPolicyAction> actions = new(DefaultActionMap); |
| | | 66 | | |
| | 9 | 67 | | if (actionOverrides is not null) |
| | | 68 | | { |
| | 35 | 69 | | foreach (KeyValuePair<SignatureVerificationCategory, VerificationPolicyAction> item in actionOverrides) |
| | | 70 | | { |
| | 10 | 71 | | if (!Enum.IsDefined(item.Key) || item.Key is SignatureVerificationCategory.Unspecified) |
| | | 72 | | { |
| | 1 | 73 | | throw new ArgumentOutOfRangeException(nameof(actionOverrides), item.Key, "Verification category must |
| | | 74 | | } |
| | | 75 | | |
| | 9 | 76 | | if (!Enum.IsDefined(item.Value) || item.Value is VerificationPolicyAction.Unspecified) |
| | | 77 | | { |
| | 1 | 78 | | throw new ArgumentOutOfRangeException(nameof(actionOverrides), item.Value, "Verification policy acti |
| | | 79 | | } |
| | | 80 | | |
| | 8 | 81 | | if (!allowUnsafeAllowOverrides |
| | 8 | 82 | | && item.Value is VerificationPolicyAction.Allow |
| | 8 | 83 | | && item.Key is not SignatureVerificationCategory.Valid) |
| | | 84 | | { |
| | 1 | 85 | | throw new ArgumentOutOfRangeException( |
| | 1 | 86 | | nameof(actionOverrides), |
| | 1 | 87 | | item.Key, |
| | 1 | 88 | | "Mapping a failure category to Allow requires allowUnsafeAllowOverrides, because it makes a fail |
| | | 89 | | } |
| | | 90 | | |
| | 7 | 91 | | actions[item.Key] = item.Value; |
| | | 92 | | } |
| | | 93 | | } |
| | | 94 | | |
| | 6 | 95 | | return new VerificationPolicyOptions(new ReadOnlyDictionary<SignatureVerificationCategory, VerificationPolicyAct |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | /// <summary> |
| | | 99 | | /// Gets the action configured for the supplied verification category. |
| | | 100 | | /// </summary> |
| | | 101 | | public VerificationPolicyAction GetAction(SignatureVerificationCategory category) |
| | | 102 | | { |
| | 85 | 103 | | return !Enum.IsDefined(category) || category is SignatureVerificationCategory.Unspecified |
| | 85 | 104 | | ? throw new ArgumentOutOfRangeException(nameof(category), category, "Verification category must be a specifi |
| | 85 | 105 | | : Actions.TryGetValue(category, out VerificationPolicyAction action) |
| | 85 | 106 | | && action is not VerificationPolicyAction.Unspecified |
| | 85 | 107 | | ? action |
| | 85 | 108 | | : VerificationPolicyAction.Escalate; |
| | | 109 | | } |
| | | 110 | | } |