Module Signature

RFC 9421 HTTP Message Signatures

This module implements RFC 9421 HTTP Message Signatures, enabling cryptographic signing and verification of HTTP request and response messages.

Overview

HTTP Message Signatures provide a mechanism to create and verify cryptographic signatures on HTTP messages. The signature covers specific message components (headers, derived values) and metadata (timestamps, key identifiers).

Example Usage

  Eio_main.run @@ fun env ->
  let clock = Eio.Stdenv.clock env in

  (* Create a signing configuration *)
  let key = Signature.Key.ed25519
    ~priv:(Base64.decode_exn "private-key-bytes")
    ~pub:(Base64.decode_exn "public-key-bytes") in
  let config = Signature.config
    ~key
    ~keyid:"my-key-id"
    ~components:[Signature.Component.method_; Signature.Component.authority]
    () in

  (* Sign headers *)
  let ctx = Signature.Context.request ~method_:`GET ~uri:(Uri.of_string "https://example.com/") ~headers in
  let signed_headers = Signature.sign ~clock ~config ~context:ctx ~headers |> Result.get_ok in

  (* Verify signatures *)
  let result = Signature.verify ~clock ~key ~context:ctx ~headers:signed_headers () in
  ...

References

Algorithms

module Algorithm : sig ... end

Signature algorithms per RFC 9421 Section 3.3.

Message Components

module Component : sig ... end

Message components that can be included in signatures per RFC 9421 Section 2.

Signature Parameters

module Params : sig ... end

Signature parameters per RFC 9421 Section 2.3.

Key Material

module Key : sig ... end

Cryptographic key material for signing and verification.

Signing Context

type request_ctx = {
  1. method_ : Method.t;
  2. uri : Uri.t;
  3. headers : Headers.t;
}

Request context for signature computation. Contains the HTTP method, request URI, and request headers.

type response_ctx = {
  1. status : int;
  2. headers : Headers.t;
  3. request : request_ctx option;
}

Response context for signature computation. Contains the HTTP status code, response headers, and optionally the original request.

module Context : sig ... end

Context for resolving message components.

Content-Digest

module Content_digest : sig ... end

RFC 9530 Content-Digest support.

Signing Configuration

type config

Configuration for signing requests.

val config : key:Key.t -> ?keyid:string -> ?components:Component.t list -> ?tag:string -> ?include_created:bool -> ?label:string -> unit -> config

config ~key ?keyid ?components ?tag ?include_created ?label () creates a signing configuration.

  • parameter key

    The signing key.

  • parameter keyid

    Key identifier (included in signature parameters).

  • parameter components

    Components to sign. Default: [\@method; \@authority; \@path].

  • parameter tag

    Application-specific tag.

  • parameter include_created

    Include creation timestamp. Default: true.

  • parameter label

    Signature label for dictionary key. Default: "sig1".

val default_components : Component.t list

Default components to sign: [\@method; \@authority; \@path].

Signing

type sign_error = [
  1. | `Key_algorithm_mismatch of string
  2. | `Missing_private_key
  3. | `Component_resolution_error of string
  4. | `Crypto_error of string
]
val sign_error_to_string : sign_error -> string

sign_error_to_string err returns a human-readable error message.

val sign : clock:_ Eio.Time.clock -> config:config -> context:Context.t -> headers:Headers.t -> (Headers.t, sign_error) result

sign ~clock ~config ~context ~headers signs the message and returns headers with Signature-Input and Signature headers added.

  • parameter clock

    Eio clock used for the created timestamp.

val sign_with_digest : clock:_ Eio.Time.clock -> config:config -> context:Context.t -> headers:Headers.t -> body:string -> digest_algorithm:Content_digest.algorithm -> (Headers.t, sign_error) result

sign_with_digest ~clock ~config ~context ~headers ~body ~digest_algorithm computes Content-Digest, adds it to headers, and signs. The content-digest component is automatically added to signed components.

  • parameter clock

    Eio clock used for the created timestamp.

Verification

type verify_error = [
  1. | `Missing_signature_header
  2. | `Missing_signature_input_header
  3. | `Invalid_signature_input of string
  4. | `Signature_label_not_found of string
  5. | `Key_algorithm_mismatch of string
  6. | `Missing_public_key
  7. | `Component_resolution_error of string
  8. | `Signature_mismatch
  9. | `Signature_expired
  10. | `Required_component_missing of string
  11. | `Crypto_error of string
]
val verify_error_to_string : verify_error -> string

verify_error_to_string err returns a human-readable error message.

type verify_result = {
  1. label : string;
  2. keyid : string option;
  3. created : Ptime.t option;
  4. expires : Ptime.t option;
  5. verified_components : Component.t list;
}

Successful verification result.

val verify : clock:_ Eio.Time.clock -> key:Key.t -> ?label:string -> ?max_age:Ptime.Span.t -> ?required_components:Component.t list -> context:Context.t -> headers:Headers.t -> unit -> (verify_result, verify_error) result

verify ~clock ~key ?label ?max_age ?required_components ~context ~headers verifies a signature on the message.

Time validation per RFC 9421:

  • If expires is present and in the past, verification fails
  • If created is present and in the future (with 60s clock skew allowance), verification fails
  • If max_age is specified and created is older than max_age, verification fails
  • parameter clock

    Eio clock used for time validation.

  • parameter key

    The verification key.

  • parameter label

    Signature label to verify. Default: first signature found.

  • parameter max_age

    Maximum age of signature. If created is present and older than max_age, verification fails.

  • parameter required_components

    Components that must be signed. Verification fails if any are missing from the signature.

  • parameter context

    Message context for component resolution.

  • parameter headers

    Headers containing Signature and Signature-Input.

val verify_all : clock:_ Eio.Time.clock -> key_resolver:(string -> Key.t option) -> ?max_age:Ptime.Span.t -> context:Context.t -> headers:Headers.t -> unit -> (verify_result list, verify_error) result

verify_all ~clock ~key_resolver ?max_age ~context ~headers verifies all signatures on a message.

  • parameter clock

    Eio clock used for time validation.

  • parameter key_resolver

    Function to resolve keys by keyid. Called for each signature with its keyid parameter.

  • parameter max_age

    Maximum age for all signatures.