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

Thin wrapper around an OTP `:public_key`-decoded X.509 certificate.

The library operates on this struct rather than raw DER so cert-resolution
policies and the verify pipeline don't redo ASN.1 work on every call. The
underlying `otp_cert` is kept around for chain validation, name comparisons,
and any downstream X.509 inspection a custom policy needs.

# `otp_cert`

```elixir
@type otp_cert() :: tuple()
```

# `t`

```elixir
@type t() :: %SignCore.X509{
  der: binary(),
  otp_cert: otp_cert(),
  public_key: term(),
  spki_sha256: binary()
}
```

# `check_validity`

```elixir
@spec check_validity(t(), DateTime.t()) :: :ok | {:error, atom()}
```

Checks whether `at` falls within the certificate's validity window
(inclusive). Returns `:ok` or `{:error, :cert_not_yet_valid |
:cert_expired | :cert_validity_unparseable}`.

# `from_der`

```elixir
@spec from_der(binary()) :: {:ok, t()} | {:error, :invalid_cert}
```

Decodes a DER-encoded X.509 certificate into a `SignCore.X509` struct.

Returns `{:error, :invalid_cert}` for malformed DER. Does **not** validate
the certificate (no validity-period check, no chain validation, no signature
check); decoding is a structural operation only — trust decisions live in
`SignCore.Policy`.

The SPKI SHA-256 pin (used by `SignCore.Policy.PinnedRegistry`) is
computed once at construction and cached on the struct — `spki_sha256/1`
is then a constant-time field read instead of two repeated ASN.1 passes
per verify.

# `spki_sha256`

```elixir
@spec spki_sha256(t()) :: binary()
```

Returns the SHA-256 hash of the leaf's `subjectPublicKeyInfo`
(DER-encoded), hex-lowercase — the canonical "SPKI pin" used by
`SignCore.Policy.PinnedRegistry`.

Cached on the struct at `from_der/1` time; this function is now a
field read.

# `validity_window`

```elixir
@spec validity_window(t()) :: {DateTime.t(), DateTime.t()} | :error
```

Returns the certificate's validity window as `{not_before, not_after}`
DateTimes (UTC). The Time CHOICE in X.509 (UTCTime / GeneralizedTime)
is decoded per RFC 5280 §4.1.2.5.

---

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