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
Callbacks
@callback sign(signing_input :: binary(), alg()) :: {:ok, signature :: binary()} | {:error, signature_error()}
Sign an encoded JWS signing input with the named JOSE algorithm.
@callback signing_jwk() :: map() | JOSE.JWK.t()
The current signing key's public JWK. Private members are forbidden.