# `X402.HTTPSignature.Key`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/http_signature/key.ex#L1)

Key material for RFC 9421 HTTP Message Signatures.

A key pairs an algorithm from the HTTP Signature Algorithms registry
with its public material and, for signing, its private material:

| `alg`                 | Public material                 | Private material     |
| --------------------- | ------------------------------- | -------------------- |
| `"ed25519"`           | 32-byte public key              | 32-byte seed         |
| `"ecdsa-p256-sha256"` | 65-byte uncompressed point      | 32-byte scalar       |
| `"rsa-pss-sha512"`    | `[e, n]` big-endian binaries    | `[e, n, d]` binaries |

`new/1` accepts the material as raw bytes, PEM (`PUBLIC KEY`,
`PRIVATE KEY`, `EC PRIVATE KEY`, `RSA PRIVATE KEY`), a JWK map, or the
`:public_key` records those PEMs decode to; `from_jwk/1` and `to_jwk/1`
convert to and from the JWK form the
`/.well-known/http-message-signatures-directory` document uses. The key
identifier defaults to the RFC 7638 JWK thumbprint (`thumbprint/1`), as
draft-meunier-http-message-signatures-directory requires.

ECDSA signatures use the raw `r || s` form RFC 9421 §3.3.4 mandates,
and RSA-PSS uses SHA-512 with a 64-byte salt (§3.3.1).

# `alg`

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

An HTTP Signature Algorithms registry name supported here.

# `error`

```elixir
@type error() ::
  :invalid_key | {:unsupported_algorithm, term()} | :missing_private_key
```

# `t`

```elixir
@type t() :: %X402.HTTPSignature.Key{
  alg: alg(),
  kid: String.t() | nil,
  private: term() | nil,
  public: term()
}
```

# `algorithms`
*since 0.9.0* 

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

Returns the supported algorithm names.

## Examples

    iex> X402.HTTPSignature.Key.algorithms()
    ["ed25519", "ecdsa-p256-sha256", "rsa-pss-sha512"]

# `from_jwk`
*since 0.9.0* 

```elixir
@spec from_jwk(map()) :: {:ok, t()} | {:error, error()}
```

Builds a key from a JWK map (RFC 7517), including the private part when
`d` is present.

The `alg` member may name the HTTP signature algorithm; when absent it
is inferred from `kty`/`crv`. Any `kid` is kept.

## Examples

    iex> jwk = %{"kty" => "OKP", "crv" => "Ed25519", "kid" => "test-key-ed25519",
    ...>   "x" => "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs"}
    iex> {:ok, key} = X402.HTTPSignature.Key.from_jwk(jwk)
    iex> {key.alg, key.kid, key.private}
    {"ed25519", "test-key-ed25519", nil}

    iex> X402.HTTPSignature.Key.from_jwk(%{"kty" => "oct", "k" => "secret"})
    {:error, {:unsupported_algorithm, "oct"}}

# `generate`
*since 0.9.0* 

```elixir
@spec generate(alg()) :: {:ok, t()} | {:error, error()}
```

Generates a fresh key pair for an algorithm.

## Examples

    iex> {:ok, key} = X402.HTTPSignature.Key.generate("ecdsa-p256-sha256")
    iex> {byte_size(key.public), byte_size(key.private), String.length(key.kid)}
    {65, 32, 43}

    iex> X402.HTTPSignature.Key.generate("hmac-sha256")
    {:error, {:unsupported_algorithm, "hmac-sha256"}}

# `new`
*since 0.9.0* 

```elixir
@spec new(keyword()) :: {:ok, t()} | {:error, error()}
```

Builds a key from its algorithm and material.

At least one of `:public_key` and `:private_key` is required; the
public material is derived from the private one when omitted.

## Options

* `:alg` - Required. The signature algorithm.

* `:kid` (`t:String.t/0`) - Key identifier; defaults to the JWK thumbprint.

* `:public_key` (`t:term/0`) - Public material (raw, PEM, JWK, or `:public_key` record).

* `:private_key` (`t:term/0`) - Private material (raw, PEM, JWK, or `:public_key` record).

## Examples

    iex> seed = :binary.copy(<<7>>, 32)
    iex> {:ok, key} = X402.HTTPSignature.Key.new(alg: "ed25519", private_key: seed, kid: "k1")
    iex> {key.alg, key.kid, byte_size(key.public), key.private == seed}
    {"ed25519", "k1", 32, true}

    iex> X402.HTTPSignature.Key.new(alg: "ed25519", public_key: "too short")
    {:error, :invalid_key}

    iex> X402.HTTPSignature.Key.new(alg: "hmac-sha256", public_key: "x")
    {:error, {:unsupported_algorithm, "hmac-sha256"}}

# `sign`
*since 0.9.0* 

```elixir
@spec sign(t(), binary()) :: {:ok, binary()} | {:error, :missing_private_key}
```

Signs `data` with the key's private material, returning the raw
signature bytes RFC 9421 §3.3 defines for the algorithm.

## Examples

    iex> {:ok, key} = X402.HTTPSignature.Key.generate("ed25519")
    iex> {:ok, signature} = X402.HTTPSignature.Key.sign(key, "data")
    iex> byte_size(signature)
    64

    iex> {:ok, key} = X402.HTTPSignature.Key.generate("ed25519")
    iex> X402.HTTPSignature.Key.sign(%{key | private: nil}, "data")
    {:error, :missing_private_key}

# `thumbprint`
*since 0.9.0* 

```elixir
@spec thumbprint(t()) :: String.t()
```

Computes the RFC 7638 JWK thumbprint (SHA-256, Base64url) of a key.

## Examples

    iex> jwk = %{"kty" => "OKP", "crv" => "Ed25519", "x" => "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs"}
    iex> {:ok, key} = X402.HTTPSignature.Key.from_jwk(jwk)
    iex> X402.HTTPSignature.Key.thumbprint(key)
    "poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U"

# `to_jwk`
*since 0.9.0* 

```elixir
@spec to_jwk(t()) :: map()
```

Returns the public JWK for a key, with `kid` and the HTTP signature
`alg` name as draft-meunier-http-message-signatures-directory §3 requires.

## Examples

    iex> jwk = %{"kty" => "OKP", "crv" => "Ed25519", "x" => "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs"}
    iex> {:ok, key} = X402.HTTPSignature.Key.from_jwk(jwk)
    iex> X402.HTTPSignature.Key.to_jwk(key)
    %{
      "kty" => "OKP",
      "crv" => "Ed25519",
      "x" => "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs",
      "kid" => "poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U",
      "alg" => "ed25519",
      "use" => "sig"
    }

# `verify`
*since 0.9.0* 

```elixir
@spec verify(t(), binary(), binary()) :: boolean()
```

Verifies a raw signature over `data` with the key's public material.

## Examples

    iex> {:ok, key} = X402.HTTPSignature.Key.generate("ecdsa-p256-sha256")
    iex> {:ok, signature} = X402.HTTPSignature.Key.sign(key, "data")
    iex> X402.HTTPSignature.Key.verify(key, "data", signature)
    true
    iex> X402.HTTPSignature.Key.verify(key, "other", signature)
    false
    iex> X402.HTTPSignature.Key.verify(key, "data", "short")
    false

---

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