# `SignCore.CMS.SignedData`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/sign_core/cms/signed_data.ex#L1)

Assemble a CMS `SignedData` `ContentInfo` envelope (RFC 5652 §5).

The hardware signature is supplied by the caller — this module never
signs. The expected flow:

    {:ok, attrs} = SignCore.CMS.SignedAttributes.build(digest: payload_digest)
    {:ok, tbs}   = SignCore.CMS.SignedAttributes.to_be_signed(attrs)
    {:ok, sig}   = Pkcs11ex.sign_bytes(tbs, signer: {:platform, :signing}, alg: :PS256)
    {:ok, der}   = SignCore.CMS.SignedData.build(attrs, sig,
                     certificates: [leaf_x509 | issuer_x509s],
                     digest_algorithm: :sha256,
                     signature_algorithm: :rsa_pss_sha256)

The result is a self-contained DER blob suitable for embedding in
PAdES `/Contents`, an XML `<Object>` element, or any other CMS-shaped
envelope. The PAdES adapter glues this DER (hex-encoded) into the
reserved `/Contents` placeholder.

## What ships in v1

  * One `SignerInfo` per envelope (single-signer documents — the
    Phase 4 contract).
  * `IssuerAndSerialNumber` signer identifier (CMS version 1). The
    `SubjectKeyIdentifier` form (CMS version 3) lands later if the
    maintainer asks; PAdES B-B defaults to issuer+serial.
  * Detached content (`eContent` absent) — the signed payload is the
    bytes covered by `/ByteRange`, not embedded inside the CMS.
  * `unsignedAttrs` omitted (B-B specifically — B-T's timestamp token
    lives there in Phase 5 work).

## What's deferred

  * Multi-signer SignedData (counter-signing).
  * `SubjectKeyIdentifier` signer identifier (CMS v3).
  * `OriginatorInfo`, `crls`, `attribute certificates` (none needed
    for B-B).
  * Embedded eContent (the EncapsulatedContentInfo eContent field is
    always absent in this v1 — detached signatures only).

# `build_opts`

```elixir
@type build_opts() :: keyword()
```

Per-call options for `build/3`.

Required:
  * `:certificates` — `[SignCore.X509.t() | binary()]`. Leaf first,
    then any issuers / intermediates the verifier may need. Each
    entry can be a parsed `SignCore.X509` struct or a raw DER binary
    (parsed inline). The leaf identifies the signer for the
    `IssuerAndSerialNumber` field.

Optional:
  * `:digest_algorithm` — `:sha256` (default) | `:sha384` | `:sha512`.
  * `:signature_algorithm` — `:rsa_sha256` (default — PKCS#1 v1.5) |
    `:rsa_pss_sha256`. Match the algorithm the hardware actually
    produced; the OID lands inside `SignerInfo.signatureAlgorithm`.
  * `:content_oid` — defaults to `id-data`. Must match the
    `:content_oid` passed to `SignedAttributes.build/1`; the codec
    doesn't cross-check.

# `build`

```elixir
@spec build(signed_attrs :: [tuple()], signature :: binary(), opts :: build_opts()) ::
  {:ok, binary()} | {:error, term()}
```

Assemble a SignedData ContentInfo from already-built signedAttrs,
the hardware-produced signature bytes, and the certificate chain.

# `parse`

```elixir
@spec parse(binary()) :: {:ok, SignCore.CMS.Parsed.t()} | {:error, term()}
```

Parse a CMS `ContentInfo` DER and project it into a `SignCore.CMS.Parsed`
struct that carries everything a verify pipeline needs (the
to-be-signed bytes, the signature, the leaf cert, the message digest
and signing time from the signed attributes).

Rejects:
  * non-`id-signedData` ContentInfo (returns `{:error, :not_signed_data}`)
  * envelopes carrying anything other than exactly one `SignerInfo`
    (single-signer is the v1 contract — multi-signature support is
    Phase 4b territory)
  * empty certificate sets (we need the leaf to verify the signature)
  * malformed or absent required signed attributes (`messageDigest`)

---

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