# `Pkcs11ex`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/pkcs11ex.ex#L1)

Hardware-backed digital signatures for Elixir, via PKCS#11.

This module hosts the **Layer 2** signing primitives — format-agnostic
`sign_bytes`, `verify_bytes`, `digest`, and `digest_stream`. Format adapters
(`SignCore.JWS`, `SignCore.PDF`, `SignCore.XML`) build on top.

See `docs/specs/specs.md` for architecture and `docs/specs/api.md` for the
full public API specification.

## Surface

Two paths into a sign:

  * **Signer-ref (`:signer`, recommended)** — `signer: {slot_ref, key_ref}`
    resolves through the running `Pkcs11ex.Slot.Server` for that slot,
    which holds the persistent session, applies the configured
    `pin_callback`, and routes through the single-session-pinned model
    from `specs.md` §5.2. Atom shorthand: `signer: :key_ref` resolves
    against `default_slot` from `Pkcs11ex.Application.config/0`.

  * **Explicit (legacy / one-shot)** — pass `module:`, `slot_id:`,
    `key_label:`, and (for token slots) `pin:` directly. Opens a fresh
    session per call. Useful for scripts and tests; production should
    prefer `:signer`.

# `sign_opts`

```elixir
@type sign_opts() :: [
  module: Pkcs11ex.Native.module_resource(),
  slot_id: non_neg_integer(),
  pin: binary() | nil,
  key_label: String.t(),
  alg: atom(),
  encoding_context: :jose | :der
]
```

# `verify_opts`

```elixir
@type verify_opts() :: [
  module: Pkcs11ex.Native.module_resource(),
  slot_id: non_neg_integer(),
  key_label: String.t(),
  alg: atom(),
  encoding_context: :jose | :der
]
```

# `native_version`

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

Returns the version reported by the native bridge.

Smoke test for the Rustler NIF wiring; returns the `Cargo.toml` package
version of `pkcs11ex_nif`.

# `sign_bytes`

```elixir
@spec sign_bytes(iodata(), sign_opts()) :: {:ok, binary()} | {:error, term()}
```

Sign `data` with a hardware-backed key.

## Required options
  * `:module` — a `Pkcs11ex.Native` module resource (from `Native.module_load/1`).
  * `:slot_id` — `CK_SLOT_ID` (non-negative integer).
  * `:key_label` — `CKA_LABEL` of the private key on the slot.
  * `:alg` — algorithm atom (e.g., `:PS256`). Must be in the configured
    `:allowed_algs` allowlist; any value outside the allowlist is rejected
    before the NIF is called.

## Optional options
  * `:pin` — User PIN for token slots; omit for cloud HSMs that don't require login.
  * `:encoding_context` — `:jose` or `:der`; defaults to `:der`. Controls the
    wire-format encoding of the returned signature for algorithms whose
    encoding differs by context (e.g., ES256 — irrelevant for PS256).

# `verify_bytes`

```elixir
@spec verify_bytes(iodata(), binary(), verify_opts()) :: :ok | {:error, term()}
```

Verify a `signature` over `data` using a hardware-backed public key.

Required options: `:module`, `:slot_id`, `:key_label`, `:alg`.
Optional: `:encoding_context` (default `:der`).

---

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