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

Thin wrapper around OTP's `:CryptographicMessageSyntax-2009` ASN.1
codec for CMS structures.

The OTP codec returns `{:error, {:asn1, reason}}` with deeply nested
internal stack frames on failure. This module flattens those into
flat `{:error, {:cms_codec, type, reason}}` tuples that compose with
the rest of `pkcs11ex`'s `with` chains.

## Encoding shape

The codec accepts Elixir tuples shaped after the ASN.1 type
definitions in OTP's
[`CryptographicMessageSyntax-2009.asn1`](https://github.com/erlang/otp/blob/master/lib/public_key/asn1/CryptographicMessageSyntax-2009.asn1).
Some shape rules:

  * Records are represented as tagged tuples with the type name as
    first element. E.g., `ContentInfo` is `{:ContentInfo, oid, content}`.
  * `OPTIONAL` fields use the atom `:asn1_NOVALUE` when absent.
  * For ContentInfo, the `content` field is parameterised by
    `contentType` via the information-object-class table — pass the
    inner term **directly** (not wrapped as `{:asn1_OPENTYPE, der}`).
    The codec recurses.
  * For genuinely opaque fields (`Attribute.values` SET OF ANY), wrap
    bytes as `{:asn1_OPENTYPE, der_bytes}` — the codec emits them
    verbatim.

## Examples

    iex> oid_signed_data = SignCore.CMS.OIDs.id_signed_data()
    iex> oid_data = SignCore.CMS.OIDs.id_data()
    iex> oid_sha256 = SignCore.CMS.OIDs.id_sha256()
    iex> inner = {
    ...>   :SignedData, 1,
    ...>   [{:DigestAlgorithmIdentifier, oid_sha256, :asn1_NOVALUE}],
    ...>   {:EncapsulatedContentInfo, oid_data, :asn1_NOVALUE},
    ...>   :asn1_NOVALUE, :asn1_NOVALUE, []
    ...> }
    iex> ci = {:ContentInfo, oid_signed_data, inner}
    iex> {:ok, der} = SignCore.CMS.Codec.encode(:ContentInfo, ci)
    iex> {:ok, decoded} = SignCore.CMS.Codec.decode(:ContentInfo, der)
    iex> match?({:ContentInfo, ^oid_signed_data, _}, decoded)
    true

# `cms_type`

```elixir
@type cms_type() ::
  :ContentInfo
  | :SignedData
  | :SignerInfo
  | :EncapsulatedContentInfo
  | :SignedAttributes
  | :IssuerAndSerialNumber
  | :Attribute
  | :DigestAlgorithmIdentifier
  | :SignatureAlgorithmIdentifier
```

Top-level CMS types we encode/decode.

# `der`

```elixir
@type der() :: binary()
```

Encoded DER bytes.

# `decode`

```elixir
@spec decode(cms_type(), der()) ::
  {:ok, term()} | {:error, {:cms_codec, cms_type(), term()}}
```

Decode DER to a CMS term. Returns `{:ok, term}` or
`{:error, {:cms_codec, type, reason}}`.

# `encode`

```elixir
@spec encode(cms_type(), term()) ::
  {:ok, der()} | {:error, {:cms_codec, cms_type(), term()}}
```

Encode a CMS term to DER. Returns `{:ok, der}` or
`{:error, {:cms_codec, type, reason}}`.

---

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