// verification_policy.mbt — Application verification policy.
//
// RFC 9421 defines how to verify a signature cryptographically; it is up to
// the application to decide which signatures to accept. `VerificationPolicy`
// encodes those application requirements: allowed algorithms, required
// components, time bounds, and the parameters that must be present.

///|
/// Application-level verification requirements.
pub(all) struct VerificationPolicy {
  /// The only algorithms accepted (whitelist). Empty means "reject all".
  allowed_algorithms : Array[String]
  /// Components that every acceptable signature must cover.
  required_components : Array[CoveredComponent]
  /// `created` must be present.
  require_created : Bool
  /// `expires` must be present.
  require_expires : Bool
  /// `keyid` must be present.
  require_keyid : Bool
  /// `nonce` must be present.
  require_nonce : Bool
  /// Reject signatures older than this many seconds (relative to `created`).
  max_signature_age_seconds : Int64?
  /// Permitted clock skew, in seconds, for `created` in the future.
  allowed_clock_skew_seconds : Int64
  /// Reject signatures whose `created` is this far in the future or more.
  max_future_seconds : Int64
  /// The only `tag` value accepted; `None` accepts any (or absent) tag.
  expected_tag : String?
  /// Reject signatures carrying unknown metadata parameters.
  reject_unknown_parameters : Bool
  /// Require a valid Content-Digest for the message body.
  require_content_digest : Bool
  /// Require Content-Digest to be covered by the signature.
  require_content_digest_covered : Bool
}

///|
/// A policy that accepts only `hmac-sha256` with reasonable defaults.
pub fn VerificationPolicy::hmac_only() -> VerificationPolicy {
  {
    allowed_algorithms: ["hmac-sha256"],
    required_components: Array::new(),
    require_created: true,
    require_expires: false,
    require_keyid: true,
    require_nonce: false,
    max_signature_age_seconds: None,
    allowed_clock_skew_seconds: 300L,
    max_future_seconds: 300L,
    expected_tag: None,
    reject_unknown_parameters: true,
    require_content_digest: false,
    require_content_digest_covered: false,
  }
}

///|
/// A permissive policy that imposes no time/keyid/nonce requirements
/// (cryptographic verification still runs and the algorithm whitelist still
/// lists the implemented algorithm). Intended for tests only.
pub fn VerificationPolicy::permissive() -> VerificationPolicy {
  {
    allowed_algorithms: ["hmac-sha256"],
    required_components: Array::new(),
    require_created: false,
    require_expires: false,
    require_keyid: false,
    require_nonce: false,
    max_signature_age_seconds: None,
    allowed_clock_skew_seconds: 0L,
    max_future_seconds: 9223372036854775807L,
    expected_tag: None,
    reject_unknown_parameters: false,
    require_content_digest: false,
    require_content_digest_covered: false,
  }
}

///|
/// Returns `true` if the algorithm is in the whitelist (or the whitelist is
/// empty, which means no algorithm is acceptable).
pub fn VerificationPolicy::allows_algorithm(
  self : VerificationPolicy,
  algorithm : String,
) -> Bool {
  for a in self.allowed_algorithms {
    if a == algorithm {
      return true
    }
  }
  false
}

///|
/// Returns `true` if `component` is covered by the given components.
pub fn components_cover(
  components : Array[CoveredComponent],
  wanted : CoveredComponent,
) -> Bool {
  let wanted_id = wanted.identifier()
  let wanted_str = match wanted.to_component_string() {
    Ok(s) => s
    Err(_) => wanted_id
  }
  for c in components {
    if c.identifier() == wanted_id {
      let cstr = match c.to_component_string() {
        Ok(s) => s
        Err(_) => c.identifier()
      }
      if cstr == wanted_str {
        return true
      }
    }
  }
  false
}