Compact RFC 7515 signatures over exact protected and payload segments.
sign/4 takes ordered header pairs, nonempty payload bytes, an algorithm-bound
KeyHandle or Policy.signer, and only timeout: milliseconds (default 5,000;
1–300,000). Protected headers use compact JSON: top-level members keep caller
order, and nested JSON objects are serialized in map order. Callers needing
exact protected bytes use sign_protected/4. No received header is serialized again.
sign_protected/4 accepts caller-serialized protected JSON bytes without
reserialization, with the same bounds and header rejections as Header.json/1.
Payload, signer, timeout, and compact-output bounds match sign/4.
verify/2 takes a map containing exactly :algorithms (nonempty unique list
of the eight Crypto JWS names), :timeout (1–300,000), and :key_resolver.
The arity-one resolver receives %{algorithm: wire_name, header: map} and
returns {:ok, %{algorithm: same_wire_name, key: PublicKey_or_verify_fun}}
or :error. A verification function receives ({:jws, name}, base, signature);
HMAC secrets remain inside that function's custody. All callbacks use the
custody worker and an absolute operation deadline. kid and x5c are only
untrusted header data; they establish no key trust or network permission.
Returns {:ok, compact} / {:ok, Result} or {:error, JOSE.Error}.
Empty payloads reject as :detached_payload. Cryptographic validity supplies
no principal, claims validation, freshness, replay protection, or authorization.
Summary
Functions
Sign caller-serialized protected JSON bytes without reserialization.
Verify a compact JWS over its received protected and payload segments (RFC 7515).
Functions
@spec sign( RequestSeal.JOSE.header(), binary(), RequestSeal.KeyHandle.t() | RequestSeal.Policy.signer(), keyword() ) :: {:ok, binary()} | {:error, RequestSeal.JOSE.Error.t()}
@spec sign_protected( binary(), binary(), RequestSeal.KeyHandle.t() | RequestSeal.Policy.signer(), keyword() ) :: {:ok, binary()} | {:error, RequestSeal.JOSE.Error.t()}
Sign caller-serialized protected JSON bytes without reserialization.
@spec verify(binary(), map()) :: {:ok, RequestSeal.JOSE.JWS.Result.t()} | {:error, RequestSeal.JOSE.Error.t()}
Verify a compact JWS over its received protected and payload segments (RFC 7515).
The policy must be a map containing exactly these atom keys:
:algorithms: a nonempty unique list selected from"RS256","PS256","PS384","PS512","HS256","ES256","ES384", and"EdDSA".:timeout: an integer from 1 through 300,000 milliseconds, shared by key resolution and verification under one absolute deadline.:key_resolver: an arity-one function receiving%{algorithm: wire_name, header: decoded_header_map}. It returns:erroror{:ok, %{algorithm: same_wire_name, key: public_key_or_verify_function}}, with exactly those two entry keys. The verification function has arity three, receives({:jws, wire_name}, exact_base_bytes, signature_bytes), and returns exactly:okon validity. Any other return, including{:ok, value}, is:invalid_signature. Use it for HMAC so secrets remain in caller-owned custody.
Returns {:ok, RequestSeal.JOSE.JWS.Result.t()} only after verification,
otherwise {:error, RequestSeal.JOSE.Error.t()}. Error reasons are
:invalid_policy, :invalid_serialization, :unsupported_serialization,
:limit, :invalid_base64, :invalid_header, :duplicate_member,
:unsupported_critical_header, :compression_unsupported, :unsupported_header,
:algorithm_not_permitted, :detached_payload, :unknown_key,
:algorithm_mismatch, :key_resolver_failed, :invalid_signature, and
:deadline_exceeded. Resolver exceptions or invalid entries return
:key_resolver_failed; resolver :error returns :unknown_key; verification
callback failures return :invalid_signature. Only :deadline_exceeded is
retryable. Protected header "crit" rejects as :unsupported_critical_header,
"zip" as :compression_unsupported, and "jku", "x5u", "jwk", and
"b64" as :unsupported_header, regardless of their values.
Resolver and verification callbacks execute in a sensitive worker with deadline
and caller cancellation. Input is bounded to 1,048,576 bytes. Empty or detached
payloads and noncompact serializations reject. Protected bytes are never
reserialized. Header kid/x5c values confer no trust or network permission.
Valid cryptography supplies no identity, claims validation, replay protection,
freshness, or authorization.
This example constructs and verifies a local RFC 8037 Ed25519 JWS:
iex> {_, seed} = :crypto.generate_key(:eddsa, :ed25519)
iex> {:ok, handle} = RequestSeal.Custody.Local.new({:jws, "EdDSA"}, {:ed25519, seed})
iex> {:ok, key} = RequestSeal.Custody.public_key(handle)
iex> {:ok, token} = RequestSeal.JOSE.JWS.sign([{"alg", "EdDSA"}], "payload", handle)
iex> policy = %{algorithms: ["EdDSA"], timeout: 5_000, key_resolver: fn _ -> {:ok, %{algorithm: "EdDSA", key: key}} end}
iex> {:ok, result} = RequestSeal.JOSE.JWS.verify(token, policy)
iex> result.payload
"payload"
iex> RequestSeal.Custody.Local.release(handle)
:ok
iex> {:error, error} = RequestSeal.JOSE.JWS.verify(token, Map.put(policy, :extra, true))
iex> error.reason
:invalid_policy