Attesto.Signer behaviour (Attesto v1.15.0)

Copy Markdown View Source

Additive signing contract for non-extractable private keys.

A module implementing this behaviour exposes only the public JWK and signs the already-encoded JWS signing input. The private key may therefore remain inside an HSM, KMS, enclave, or remote custody service; it never needs to be returned to Attesto or loaded into BEAM memory.

The signature returned by sign/2 is the JWS signature octet sequence, not a base64url string. In particular, ECDSA implementations return the fixed width R || S encoding required by RFC 7518, rather than ASN.1 DER. Implementations are responsible for the algorithm-specific requirements, including the required RSA-PSS salt length.

Attesto verifies every returned signature against signing_jwk/0 and the selected alg before returning a compact JWS. A signer using the wrong remote key, message/digest mode, signature encoding, or algorithm therefore fails locally instead of issuing an unusable token.

Existing Attesto.Keystore implementations need no changes. When a module does not implement this behaviour, Attesto continues to read its signing_pem/0 and uses the established in-process JOSE path. A non-extractable implementation supplies signing_jwk/0 and sign/2 while retaining verification_pems/0 (with public PEMs) for verification and JWKS publication.

A signer using an algorithm that cannot be inferred from its public key, such as PS256, also implements the optional Attesto.Keystore.signing_alg/0 callback or publishes an "alg" member in signing_jwk/0.

Summary

Callbacks

Sign an encoded JWS signing input with the named JOSE algorithm.

The current signing key's public JWK. Private members are forbidden.

Types

alg()

@type alg() :: String.t()

signature_error()

@type signature_error() :: term()

Callbacks

sign(signing_input, alg)

@callback sign(signing_input :: binary(), alg()) ::
  {:ok, signature :: binary()} | {:error, signature_error()}

Sign an encoded JWS signing input with the named JOSE algorithm.

signing_jwk()

@callback signing_jwk() :: map() | JOSE.JWK.t()

The current signing key's public JWK. Private members are forbidden.