# `Pkcs11ex.Audit.Anchor.RFC3161`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/pkcs11ex/audit/anchor/rfc3161.ex#L1)

RFC 3161 Time-Stamp Protocol — anchor an audit chain head against an
external Time-Stamping Authority (TSA).

The library doesn't trust the operator's clock for "this entry was
inserted at time T" — `Pkcs11ex.Audit.Entry.inserted_at` is whatever
the operator says. RFC 3161 fixes that: send the chain head's
`content_hash` to a third-party TSA, get back a TimeStampToken (TST,
a CMS SignedData) that binds the hash to a TSA-attested time. Store
the TST as an audit entry. Auditors verify the TST against the TSA's
certificate chain to bound when the chain reached that state.

## Request structure (RFC 3161 §2.4.1)

    TimeStampReq ::= SEQUENCE {
      version          INTEGER  { v1(1) },
      messageImprint   MessageImprint,
      reqPolicy        TSAPolicyId      OPTIONAL,
      nonce            INTEGER          OPTIONAL,
      certReq          BOOLEAN          DEFAULT FALSE,
      extensions       [0] IMPLICIT Extensions OPTIONAL
    }

    MessageImprint ::= SEQUENCE {
      hashAlgorithm    AlgorithmIdentifier,
      hashedMessage    OCTET STRING
    }

This module emits SHA-256 only and includes a 64-bit random nonce.

## What this module does NOT do

  * Parse the response. The TST is stored opaquely as the entry's
    payload. Verification (TST signature against TSA cert chain) is
    the auditor's job and an entirely separate workflow.
  * Verify the TSA's certificate chain.
  * Pick a TSA. Apps configure their TSA URL.

## Network

Uses OTP `:httpc` (requires `:inets` started; added to extra_applications
in mix.exs).

# `request`

```elixir
@type request() :: %{der: binary(), nonce: non_neg_integer(), hash: binary()}
```

Output of build_request/2 — the encoded request and the random nonce we used.

# `build_request`

```elixir
@spec build_request(
  binary(),
  keyword()
) :: {:ok, request()} | {:error, term()}
```

Build an RFC 3161 TimeStampReq DER over `hash_bytes` (which must be
exactly 32 bytes — SHA-256 output).

Returns `{:ok, request}` where `request` is a map with `:der` (the
bytes to POST), `:nonce` (random integer included in the request), and
`:hash` (echoed back for the audit entry).

# `extract_token`

```elixir
@spec extract_token(binary()) ::
  {:ok, binary()}
  | {:error,
     {:tsa_status, non_neg_integer()}
     | :missing_time_stamp_token
     | {:malformed_tsa_response, term()}}
```

Extracts the `TimeStampToken` (a `ContentInfo` per RFC 3161 §2.4.2)
from a `TimeStampResp` body returned by `fetch_token/3`.

RFC 3161 §2.4.2 grammar:

    TimeStampResp ::= SEQUENCE {
      status           PKIStatusInfo,
      timeStampToken   TimeStampToken     OPTIONAL
    }

The TST is OPTIONAL — present only when `PKIStatus` is `granted (0)`
or `grantedWithMods (1)`. This function refuses to extract on any
other status. Returns the TST DER bytes verbatim — they are a CMS
ContentInfo (id-signedData) ready to embed as the value of a
`signature-time-stamp-token` unsigned attribute (PAdES B-T) or a
`<xades:EncapsulatedTimeStamp>` (XAdES B-T).

# `fetch_token`

```elixir
@spec fetch_token(String.t(), binary(), keyword()) ::
  {:ok, binary()} | {:error, term()}
```

POST a TimeStampReq DER to a TSA over HTTP and return the response bytes.

Content type is `application/timestamp-query`; response Content-Type
is expected to be `application/timestamp-reply`. The library doesn't
parse the response — apps and auditors verify the TST against the
TSA's certificate chain themselves.

---

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