# `Attesto.SigningAlg`
[🔗](https://github.com/XukuLLC/attesto/blob/v2.0.1/lib/attesto/signing_alg.ex#L1)

Key-derived JOSE signing algorithm helpers.

Attesto treats the algorithm as metadata of the trusted key selected by
`kid`, never as policy learned from the presented token. RSA keys infer
RS256 (RSASSA-PKCS1-v1_5) as the JWA default for the `RSA` key type, while
EC/OKP keys infer their JOSE algorithm from the public JWK curve. For wire
compatibility, Ed25519 and Ed448 inference retains the legacy `EdDSA`
identifier; trusted keystore metadata can opt into RFC 9864's explicit
`Ed25519` or `Ed448` identifiers. RSA deployments that intentionally use
PS256 can likewise label the key through keystore metadata. Ed448 requires
a JOSE backend with Curve448 and SHAKE256 support.

# `alg`

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

# `oidc_hash_profile`

```elixir
@type oidc_hash_profile() ::
  {:fixed, :sha256 | :sha384 | :sha512, pos_integer()}
  | {:xof, :shake256, pos_integer(), pos_integer()}
```

# `allowed`

```elixir
@spec allowed() :: [alg()]
```

Algorithms Attesto can sign/verify when backed by a matching key.

# `default_client_algs`

```elixir
@spec default_client_algs() :: [alg()]
```

Default set of algorithms accepted for signatures a *client* presents
(client assertions and request objects).

Equal to `fapi_algs/0`: PS256, ES256, legacy EdDSA over Ed25519, and explicit
Ed25519. A host with a non-FAPI profile can pass an explicit `:accepted_algs`
option to the relevant verifier. A composed FAPI profile that narrows this
list also passes `enforce_fapi_alg_policy: true` to retain the FAPI 2
key-strength and curve gates.

# `fapi_algs`

```elixir
@spec fapi_algs() :: [alg()]
```

Signing algorithms permitted for FAPI 2 client authentication and, when a
signed Request Object is processed, its signature: PS256, ES256, legacy
EdDSA over Ed25519, and RFC 9864 Ed25519.

The algorithm list alone cannot express legacy EdDSA's curve. Verifiers
using this policy also call `fapi_compatible?/2`, which requires an RSA
modulus of at least 2048 bits and rejects an Ed448 key even when its trusted
metadata uses the legacy `EdDSA` identifier.

RS256 (RSASSA-PKCS1-v1_5) is deliberately excluded - FAPI 2 mandates PS256
for RSA keys. This is the policy gate for verifying a signature a *client*
presents; it is narrower than `allowed/0`, which still admits RS256 for the
provider's own token signing.

# `fapi_compatible?`

```elixir
@spec fapi_compatible?(term(), JOSE.JWK.t()) :: boolean()
```

Whether `alg` and its trusted key satisfy Attesto's default FAPI policy.

RSA keys require a modulus of at least 2048 bits. The legacy `EdDSA`
identifier is accepted only over an Ed25519 key. Ed448 and weaker RSA keys
remain available to callers that explicitly select a non-FAPI algorithm
policy.

# `for_jwk`

```elixir
@spec for_jwk(module(), JOSE.JWK.t(), keyword()) :: alg()
```

Resolve the algorithm for an already parsed key in the keystore.

This is the key-preserving counterpart to for_key/3; callers that already
loaded a PEM can derive its algorithm without parsing the PEM a second time.

# `for_key`

```elixir
@spec for_key(module(), String.t(), keyword()) :: alg()
```

Resolve the algorithm for a key in `keystore`.

Resolution order:

  * per-key metadata from `key_algs/0`, keyed by RFC 7638 `kid`
  * the trusted JWK's `alg` member, when present
  * `signing_alg/0` for the current signing key only
  * inference from the JWK type/curve

# `hash_alg`

> This function is deprecated. EdDSA is curve-dependent; use oidc_hash_profile/2 or oidc_hash/3.

```elixir
@spec hash_alg(alg()) :: :sha256 | :sha384 | :sha512
```

Return the fixed digest associated with an ID Token signing algorithm.

Deprecated because EdDSA's digest is curve-dependent. Its keyless EdDSA
result corresponds to Ed25519; use `oidc_hash_profile/2` or `oidc_hash/3`
for key-aware calculation.

# `hash_half_bytes`

> This function is deprecated. EdDSA is curve-dependent; use oidc_hash_profile/2 or oidc_hash/3.

```elixir
@spec hash_half_bytes(alg()) :: pos_integer()
```

Return half the fixed digest length for an ID Token signing algorithm.

Deprecated because EdDSA's digest length is curve-dependent. Its keyless
EdDSA result corresponds to Ed25519; use `oidc_hash_profile/2` or
`oidc_hash/3` for key-aware calculation.

# `infer`

```elixir
@spec infer(JOSE.JWK.t()) :: alg()
```

Infer the default algorithm from a parsed JWK's public members.

# `keystore_algs`

```elixir
@spec keystore_algs(module()) :: [alg()]
```

The unique signing algorithms across a keystore's verification keys.

Used to advertise the algorithms the server itself signs with (the
`id_token_signing_alg_values_supported` and the JARM
`authorization_signing_alg_values_supported`, which share the same keys).
Returns `[]` when the keystore exposes no verification keys (or resolution
fails), leaving the caller to apply any default.

# `oidc_hash`

```elixir
@spec oidc_hash(binary(), alg(), JOSE.JWK.t()) :: String.t()
```

Calculate an OIDC `at_hash` / `c_hash` value for a key-bound algorithm.

Applying OIDC's generic "hash associated with the signing algorithm" rule
to RFC 8032, legacy EdDSA selects from the trusted key curve: SHA-512 (left
32 bytes) for Ed25519, or SHAKE256 with 114 bytes of output (left 57 bytes)
for Ed448. SHAKE256 is invoked through JOSE's configured SHA3 module,
preserving the application's chosen pure-Erlang, NIF, or driver backend.

# `oidc_hash_profile`

```elixir
@spec oidc_hash_profile(alg(), JOSE.JWK.t()) :: oidc_hash_profile()
```

Return the OIDC hash profile bound to `alg` and a trusted key.

Fixed-output hashes return `{:fixed, digest, half_bytes}`. Ed448 returns
`{:xof, :shake256, output_bytes, half_bytes}`, making both SHAKE256 lengths
explicit and avoiding the ambiguity of the keyless legacy helpers.

# `rsa_modulus_at_least?`

```elixir
@spec rsa_modulus_at_least?(JOSE.JWK.t(), pos_integer()) :: boolean()
```

Whether an RSA JWK's unsigned modulus is at least `minimum_bits` long.

Returns `false` for a non-RSA key or malformed modulus. This keeps protocol
policy checks independent from JOSE's backend-specific key representation.

# `rsa_params_ok?`

```elixir
@spec rsa_params_ok?(map()) :: boolean()
```

Returns `true` iff an RSA verification key's parameters are within safe
bounds: modulus at most 8192 bits and public exponent an odd integer in
`3..65537`. Non-RSA keys pass (not RSA's concern).

This is a fail-closed DoS guard evaluated on the RAW base64url `n`/`e` - the
encoded byte length is bounded BEFORE any bignum decode - so an attacker-
supplied key with a multi-hundred-KB exponent or an oversized modulus is
rejected in microseconds instead of pinning a scheduler inside
OpenSSL/JOSE `modexp`. Call it at every gate that admits an untrusted key
before handing it to a verifier.

# `validate!`

```elixir
@spec validate!(term()) :: alg()
```

Validate that `alg` is one of Attesto's supported asymmetric JOSE algorithms.

# `validate_for_key!`

```elixir
@spec validate_for_key!(term(), JOSE.JWK.t()) :: alg()
```

Validate an algorithm and bind it to a compatible trusted key.

RFC 9864's explicit `Ed25519` and `Ed448` identifiers require the matching
OKP curve. Legacy `EdDSA` remains compatible with either curve. RSA and EC
algorithms are likewise checked against their key type and curve so trusted
metadata cannot relabel a key with an incompatible algorithm.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
