# `Cmsex`
[🔗](https://github.com/openspb/cmsex/blob/v0.1.0/lib/cmsex.ex#L1)

Cryptographic Message Syntax (CMS, RFC 5652) in pure Elixir: the signed,
encrypted and other messages of S/MIME, of `.p7s`, `.p7m` and `.p7b`
files, and of the many systems built on them.

## Messages

| Type | Written by | Read by |
|---|---|---|
| SignedData | `sign/3`, `certs_only/2`; changed by `add_signer/3`, `countersign/4` and `add_unsigned_attribute/4` | `verify/2`, `verify_each/2`, `verify_countersignature/3`, `certificates/2` |
| EnvelopedData, AuthEnvelopedData | `encrypt/3` | `decrypt/2` |
| EncryptedData | `encrypt_with_key/3` | `decrypt/2` |
| DigestedData | `digest/2` | `verify_digest/2` |
| AuthenticatedData | `mac/3` | `verify_mac/2` |

`decode/2` reads any of them into its struct, without verifying or
decrypting it.

A signer is a `Cmsex.Signer`, a certificate with its key, and a recipient
a `Cmsex.Recipient`: a certificate, a shared key or a password. The
README has examples of each, with the keys and certificates of RFC 9216.

## Input and output

The functions that read take a message in DER, BER or PEM, and the
functions that write return DER; `Cmsex.PEM.encode/2` turns it into PEM.
Content is a binary, and for signing, verifying, digests and MACs it may
also be `{:stream, enumerable}`, read once, for content that does not
fit in memory.

Reading is bounded by limits on what a message may hold, listed in
`decode/2`, which every function that reads takes.

## Errors

Every function returns `{:ok, result}`, or `{:error, %Cmsex.Error{}}` for
a message it cannot read or that does not check; the functions ending in
`!` return the result alone and raise the error. An argument of the
wrong kind, an unknown option or an option given twice raises
`ArgumentError`: it is a mistake of the calling code, not of the
message.

## Examples

A digest:

    iex> {:ok, cms} = Cmsex.digest("hello")
    iex> {:ok, message} = Cmsex.verify_digest(cms)
    iex> message.content
    "hello"

Encryption with a key the parties share, here with AES-GCM:

    iex> key = :crypto.strong_rand_bytes(32)
    iex> recipient = Cmsex.Recipient.kek(key, "key 1")
    iex> {:ok, cms} = Cmsex.encrypt("secret", recipient, cipher: :aes256_gcm)
    iex> {:ok, decrypted} = Cmsex.decrypt(cms, kek: {"key 1", key})
    iex> decrypted.content
    "secret"
    iex> {:error, error} = Cmsex.decrypt(cms, kek: {"key 1", :crypto.strong_rand_bytes(32)})
    iex> error.reason
    :decryption_failed

A MAC, with the same key:

    iex> key = :crypto.strong_rand_bytes(32)
    iex> {:ok, cms} = Cmsex.mac("content", Cmsex.Recipient.kek(key, "key 1"))
    iex> {:ok, authenticated} = Cmsex.verify_mac(cms, kek: {"key 1", key})
    iex> authenticated.content
    "content"

# `position`

```elixir
@type position() :: non_neg_integer() | [non_neg_integer(), ...]
```

The position of a signer: an integer for a signer of the message, in
the order of `decode/2`, or a list for a countersignature, where `[0, 1]`
is the second countersignature of the first signer, counted across all
its countersignature attributes. `Cmsex.Error` gives positions the same
way.

# `add_signer`

```elixir
@spec add_signer(
  binary(),
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

Adds signers to a SignedData message, over the content the message
signs. The message may be in DER, BER or PEM, and comes back in DER as far as
it was: what does not change keeps its bytes and its place, the other
SignerInfos, the signed part of each, the certificates and attributes
already there, and what is added goes after them. So every signature of
the message still verifies, and the positions of the signers and
countersignatures stay the same from one change to the next. The SETs
are not sorted again; an encapsulated content in BER is written in DER,
which changes no signature.

`signers` is one signer or a list, as in `sign/3`. The new SignerInfos
go after the others; their digest algorithms join digestAlgorithms, and
the version of the message goes up to 3 when RFC 5652, 5.1 asks for it.

A detached message needs its content in `:content`, a binary,
`{:stream, enumerable}` or `{:digest, hash, digest}`. It must be the
content the signers there signed: their message digests are checked
before anything is signed, and a difference is `:digest_mismatch` with
the position of that signer. A message with its content refuses
`:content` (`:unexpected_content`).

Options: `:content`, the limits of `decode/2`, and the options of
`sign/3` that are about the signers: `:certificates` (those of the new
signers are added to the message by default; `:none`; or a list of DER
certificates), `:crls`, `:no_attributes`, `:min_rsa_bits`, `:digest`,
`:rsa_padding`, `:identifier`, `:signing_time` and
`:algorithm_protection`. The content type is that of the message.

# `add_signer!`

```elixir
@spec add_signer!(
  binary(),
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: binary()
```

Like `add_signer/3`, but raises `Cmsex.Error`.

# `add_unsigned_attribute`

```elixir
@spec add_unsigned_attribute(
  binary(),
  position(),
  Cmsex.Attribute.t() | [Cmsex.Attribute.t()],
  keyword()
) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

Adds unsigned attributes, one `Cmsex.Attribute` or a list, to the signer
or countersignature at `position`. The message may be in DER, BER or PEM, and comes back in DER as far as
it was: what does not change keeps its bytes and its place, the other
SignerInfos, the signed part of each, the certificates and attributes
already there, and what is added goes after them. So every signature of
the message still verifies, and the positions of the signers and
countersignatures stay the same from one change to the next. The SETs
are not sorted again; an encapsulated content in BER is written in DER,
which changes no signature.

The attributes go after the unsigned attributes the signer has. This is
how a signature time-stamp enters a message (RFC 3161, appendix A): the
token is over `Cmsex.SignerInfo.signature/1`, and goes in a
`:time_stamp_token` attribute.

    {:ok, %{signer_infos: [signer_info]}} = Cmsex.decode(cms)
    token = time_stamp(Cmsex.SignerInfo.signature(signer_info))
    attribute = Cmsex.Attribute.new(:time_stamp_token, [token])
    {:ok, cms} = Cmsex.add_unsigned_attribute(cms, 0, attribute)

Options: the limits of `decode/2`. A position the message does not have
raises `ArgumentError`.

# `add_unsigned_attribute!`

```elixir
@spec add_unsigned_attribute!(
  binary(),
  position(),
  Cmsex.Attribute.t() | [Cmsex.Attribute.t()],
  keyword()
) :: binary()
```

Like `add_unsigned_attribute/4`, but raises `Cmsex.Error`.

# `certificates`

```elixir
@spec certificates(binary(), keyword()) ::
  {:ok, [binary()]} | {:error, Cmsex.Error.t()}
```

The X.509 certificates of a SignedData message, in DER, in the order of
the message: the certificates of a `.p7b` bundle, or those a signer
included. Takes the options of `decode/2`.

# `certificates!`

```elixir
@spec certificates!(binary(), keyword()) :: [binary()]
```

Like `certificates/2`, but raises `Cmsex.Error`.

# `certs_only`

```elixir
@spec certs_only([binary()], keyword()) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

A SignedData message with certificates and revocation lists and no
signers (RFC 5652, 5.2), as in a `.p7b` file: version 1, no digest
algorithms, id-data without content. Takes the option `:crls`, a list of
DER revocation lists.

Other tools read a `.p7b` in PEM mostly with the `PKCS7` label:
`Cmsex.PEM.encode(p7b, :pkcs7)`. `certificates/2` reads it back.

# `certs_only!`

```elixir
@spec certs_only!([binary()], keyword()) :: binary()
```

Like `certs_only/2`, but raises `Cmsex.Error`.

# `countersign`

```elixir
@spec countersign(
  binary(),
  position(),
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

Countersigns a signer of a message, or a countersignature: a new
SignerInfo over the signature value of the one at `position`, added to
it as a countersignature attribute (RFC 5652, 11.4). The message may be in DER, BER or PEM, and comes back in DER as far as
it was: what does not change keeps its bytes and its place, the other
SignerInfos, the signed part of each, the certificates and attributes
already there, and what is added goes after them. So every signature of
the message still verifies, and the positions of the signers and
countersignatures stay the same from one change to the next. The SETs
are not sorted again; an encapsulated content in BER is written in DER,
which changes no signature.

The countersignature goes at the end of the last countersignature
attribute of that signer, or in a new attribute at the end. Its signed
attributes are the message digest of the signature value, the signing
time and CMSAlgorithmProtection, unless turned off, and those of the
countersigner; never content-type. With `no_attributes: true` it signs
the signature value itself. `verify/2` checks it with
`countersignatures: true`.

`countersigners` is one signer or a list, as in `sign/3`. Options: the
limits of `decode/2`, and `:certificates`, `:crls`, `:no_attributes`,
`:min_rsa_bits`, `:digest`, `:rsa_padding`, `:identifier`,
`:signing_time` and `:algorithm_protection`, as in `add_signer/3`.

A position the message does not have raises `ArgumentError`.

# `countersign!`

```elixir
@spec countersign!(
  binary(),
  position(),
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: binary()
```

Like `countersign/4`, but raises `Cmsex.Error`.

# `decode`

```elixir
@spec decode(binary(), keyword()) ::
  {:ok,
   Cmsex.SignedData.t()
   | Cmsex.EnvelopedData.t()
   | Cmsex.EncryptedData.t()
   | Cmsex.DigestedData.t()
   | Cmsex.AuthenticatedData.t()}
  | {:error, Cmsex.Error.t()}
```

Reads a CMS message without verifying or decrypting it, for inspection
and for the libraries built on this one. The message may be in DER, BER
or PEM. Returns the struct of its type:

  * `Cmsex.SignedData` - signed content, or a certificate bundle;
  * `Cmsex.EnvelopedData` - encrypted content, for EnvelopedData and
    AuthEnvelopedData;
  * `Cmsex.EncryptedData` - content encrypted with a key managed by
    other means;
  * `Cmsex.DigestedData` - content with its digest;
  * `Cmsex.AuthenticatedData` - content with a MAC.

Options, the limits on what the message may hold:

  * `:max_depth` - nesting of the encoding (32).
  * `:max_elements` - encoded elements at all levels (100,000).
  * `:max_signers` - signers, and countersignatures of one signer (64).
  * `:max_certificates` - certificates, and revocation lists (256).
  * `:max_attributes` - attributes in one set (64).
  * `:max_recipients` - recipients, and encrypted keys of one key
    agreement recipient (64).

A ContentInfo of another type, such as a bare `:data`, gives
`:unsupported_content_type`.

# `decode!`

```elixir
@spec decode!(binary(), keyword()) ::
  Cmsex.SignedData.t()
  | Cmsex.EnvelopedData.t()
  | Cmsex.EncryptedData.t()
  | Cmsex.DigestedData.t()
  | Cmsex.AuthenticatedData.t()
```

Like `decode/2`, but raises `Cmsex.Error`.

# `decrypt`

```elixir
@spec decrypt(binary(), keyword()) ::
  {:ok, Cmsex.Decrypted.t()} | {:error, Cmsex.Error.t()}
```

Decrypts an EnvelopedData, AuthEnvelopedData or EncryptedData message,
in DER, BER or PEM.

    {:ok, %Cmsex.Decrypted{content: content}} =
      Cmsex.decrypt(cms, key: key_der, certificate: cert_der)

    {:ok, decrypted} = Cmsex.decrypt(cms, password: "correct horse battery staple")

The credentials, one or more of:

  * `:key` and `:certificate` - a private key and its certificate, in
    DER (PKCS #8, PKCS #1 or SEC 1 for the key, which may also be a key
    record of `:public_key`). The certificate finds the recipient: a
    KeyTransRecipientInfo for RSA, a KeyAgreeRecipientInfo for EC, X25519
    and X448, by issuer and serial number or subject key identifier. The
    key may also be a function of one argument, for a key kept in an
    HSM, which gets a map and returns `{:ok, bytes}` or `{:error, term}`:
      * `%{operation: :decrypt, padding: :oaep, ciphertext:, hash:,
        mgf_hash:, label:}` - RSAES-OAEP decryption;
      * `%{operation: :decrypt, padding: :pkcs1, ciphertext:}` - RSA
        PKCS #1 v1.5 decryption, which should itself follow RFC 3218;
      * `%{operation: :agree, curve:, public_key:}` - the ECDH, X25519 or
        X448 shared secret with the public key of the sender.
  * `:kek` - `{key_identifier, key}`, for a KEKRecipientInfo with that
    identifier.
  * `:password` - for a PasswordRecipientInfo, taken as bytes.
  * `:content_key` - the content-encryption key itself: the key of an
    EncryptedData, or of any message, which skips the recipients.

The recipients that the credentials name are tried in the order of the
message, and the first that gives a key decrypts the content. Errors:

  * `:invalid_key`, `:invalid_certificate` and
    `:key_certificate_mismatch` - for the credentials.
  * `:recipient_not_found` - no recipient for the credentials.
  * `:unsupported_algorithm`, `:not_available`, `:malformed`, or
    `:limit_exceeded` with detail `:max_iterations` - what can be told
    without the secrets.
  * `:decryption_failed` - anything else: a wrong key, a key wrap, a
    padding or a tag that does not check, a message that was changed.
    Nothing tells these apart, on purpose. For RSA PKCS #1 v1.5, which
    the library reads but never writes, a padding that does not check
    gives a random key, so that the failure comes from the content, as
    RFC 3218 asks.
  * `:content_required` and `:unexpected_content` - as in `verify/2`.
  * for AuthEnvelopedData, `:missing_attribute`, `:invalid_attribute`
    and `:content_type_mismatch`, about the content-type attribute,
    once the tag has checked.

EnvelopedData and EncryptedData use CBC, which has no integrity check: a
changed ciphertext, or rarely a wrong key, can decrypt to garbage rather
than fail. AuthEnvelopedData detects both; sign the content, or use an
authenticated cipher, when that matters.

Other options:

  * `:content` - the encrypted content of a detached message, a binary.
  * `:max_iterations` - the most PBKDF2 iterations accepted for a
    password (2,000,000), since the sender chooses them.
  * the limits of `decode/2`.

Without any credentials, `ArgumentError`.

# `decrypt!`

```elixir
@spec decrypt!(binary(), keyword()) :: Cmsex.Decrypted.t()
```

Like `decrypt/2`, but raises `Cmsex.Error`.

# `digest`

```elixir
@spec digest(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  keyword()
) ::
  {:ok, binary()}
```

Returns a DigestedData message in DER (RFC 5652, 7): `content` and its
digest.

    {:ok, cms} = Cmsex.digest(content)
    {:ok, %Cmsex.DigestedData{content: ^content}} = Cmsex.verify_digest(cms)

A digest shows that the content was not changed by accident, and
nothing more: whoever changes the content can write a new digest. To
show who wrote it, sign it, or use `mac/3`.

The content is a binary, `{:stream, enumerable}` read once, or
`{:digest, hash, digest}` already computed; a stream or a digest makes
a detached message.

Options:

  * `:digest` - `:sha256` (the default), `:sha224`, `:sha384` or
    `:sha512`. A ready digest gives its own hash.
  * `:content_type` - the type of the content (`:data`), a name of
    `Cmsex.OID` or an OID tuple.
  * `:detached` - leave the content out of the message (`false` for a
    binary).

The version is 0 for `:data` and 2 for another type (RFC 5652, 7).

# `digest!`

```elixir
@spec digest!(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  keyword()
) :: binary()
```

Like `digest/2`, but returns the message alone.

# `encrypt`

```elixir
@spec encrypt(
  binary(),
  Cmsex.Recipient.t() | binary() | [Cmsex.Recipient.t() | binary()],
  keyword()
) ::
  {:ok, binary() | {binary(), binary()}} | {:error, Cmsex.Error.t()}
```

Encrypts `content`, a binary, for `recipients`, and returns an
EnvelopedData message in DER, or an AuthEnvelopedData (RFC 5083) when the
cipher is an authenticated one, such as AES-GCM.

`recipients` is one recipient or a list of them, each a
`Cmsex.Recipient` or a certificate in DER, which is taken as
`Cmsex.Recipient.certificate/2` with the defaults: RSA-OAEP for an RSA
key, ephemeral-static ECDH for an EC key, X25519 or X448.

    {:ok, cms} = Cmsex.encrypt(content, alice_cert_der)

    {:ok, cms} =
      Cmsex.encrypt(content, [
        alice_cert_der,
        Cmsex.Recipient.kek(shared_key, "backup key 1"),
        Cmsex.Recipient.password("correct horse battery staple")
      ], cipher: :aes256_gcm)

The content-encryption key is random, new for each message, and wrapped
for each recipient. Every recipient is checked before anything is
encrypted; an error that belongs to one carries its position in
`:recipient`. A certificate whose keyUsage does not allow
keyEncipherment (RSA) or keyAgreement (the others) is
`:invalid_recipient` with detail `:key_usage`; a key that cannot encrypt,
such as Ed25519 or RSASSA-PSS, is `:invalid_recipient` with `:key_type`.

Options:

  * `:cipher` - `:aes256_cbc` (the default), `:aes128_cbc` or
    `:aes192_cbc` for EnvelopedData (RFC 3565); `:aes128_gcm`,
    `:aes192_gcm`, `:aes256_gcm`, the same with `_ccm` (RFC 5084), or
    `:chacha20_poly1305` (RFC 8103) for AuthEnvelopedData.
  * `:tag_size` - the size of the tag of an authenticated cipher, in
    bytes (16): 12 to 16 for GCM, an even number from 4 to 16 for CCM,
    16 for ChaCha20-Poly1305.
  * `:content_type` - the type of the content (`:data`), a name of
    `Cmsex.OID` or an OID tuple.
  * `:detached` - leave the encrypted content out of the message
    (`false`); the result is then `{:ok, {cms, encrypted_content}}`, and
    `decrypt/2` takes the encrypted content in `:content`.
  * `:unprotected_attributes` - a list of `Cmsex.Attribute` that nothing
    protects: the unprotectedAttrs of EnvelopedData, the unauthAttrs of
    AuthEnvelopedData (`[]`).
  * `:authenticated_attributes` - for an authenticated cipher, a list of
    `Cmsex.Attribute` that the tag covers (`[]`). The content-type
    attribute is written by the library, with them or for a content type
    other than `:data`; the message-digest attribute is not written
    (RFC 5083, 2.1).
  * `:certificates` and `:crls` - certificates and revocation lists in
    DER for the OriginatorInfo (`[]`).
  * `:min_rsa_bits` - the shortest RSA key accepted (2048).

The versions are those of RFC 5652, 6.1 and of the RecipientInfos.

# `encrypt!`

```elixir
@spec encrypt!(
  binary(),
  Cmsex.Recipient.t() | binary() | [Cmsex.Recipient.t() | binary()],
  keyword()
) ::
  binary() | {binary(), binary()}
```

Like `encrypt/3`, but raises `Cmsex.Error`.

# `encrypt_with_key`

```elixir
@spec encrypt_with_key(binary(), binary(), keyword()) ::
  {:ok, binary() | {binary(), binary()}}
```

Encrypts `content` with `key`, a content-encryption key the parties
manage by other means, and returns an EncryptedData message in DER
(RFC 5652, 8). There are no recipients, and nothing in the message says
which key it is: `decrypt/2` takes it in `:content_key`.

Options: `:cipher`, a CBC one, as in `encrypt/3` (`:aes256_cbc`), since
EncryptedData has no place for a tag; `:content_type`; `:detached`; and
`:unprotected_attributes`. A key that is not of the size of the cipher
raises `ArgumentError`.

To share a key for many messages, `Cmsex.Recipient.kek/3` with
`encrypt/3` is often the better choice: a new key for each message,
with an authenticated cipher if wanted, and the identifier of the key in
the message.

# `encrypt_with_key!`

```elixir
@spec encrypt_with_key!(binary(), binary(), keyword()) ::
  binary() | {binary(), binary()}
```

Like `encrypt_with_key/3`, but returns the message alone.

# `mac`

```elixir
@spec mac(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  Cmsex.Recipient.t() | binary() | [Cmsex.Recipient.t() | binary()],
  keyword()
) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

Returns an AuthenticatedData message in DER (RFC 5652, 9): `content`, in
the clear, with an HMAC over it, and the MAC key, random and new for
each message, wrapped for each of `recipients` as in `encrypt/3`.

    {:ok, cms} = Cmsex.mac(content, Cmsex.Recipient.kek(shared_key, "key 1"))
    {:ok, %Cmsex.Authenticated{content: ^content}} =
      Cmsex.verify_mac(cms, kek: {"key 1", shared_key})

A MAC tells a recipient that the content comes from someone who had the
MAC key, and that nobody changed it since. How much that says depends
on the recipients: with a KEK or a password, the sender is someone who
holds it; with a certificate, RSA or ephemeral ECDH, anyone who has the
certificate can write such a message, and it shows nothing about who
did (RFC 5652, 14). To show who wrote the content, sign it.

The content is a binary, `{:stream, enumerable}` read once, or
`{:digest, hash, digest}` already computed; a stream or a digest makes
a detached message.

Options:

  * `:mac` - `:hmac_sha256` (the default), `:hmac_sha384`,
    `:hmac_sha512` or `:hmac_sha224` (RFC 4231). The key is as long as
    the hash, and 32 bytes for SHA-224.
  * `:content_type` - the type of the content (`:data`), a name of
    `Cmsex.OID` or an OID tuple.
  * `:detached` - leave the content out of the message (`false` for a
    binary).
  * `:authenticated_attributes` - a list of `Cmsex.Attribute` that the
    MAC covers (`[]`). With them, for a content type other than
    `:data`, and for a ready digest, the MAC is over the attributes,
    among which the library writes the content-type and the
    message-digest attributes (RFC 5652, 9.2).
  * `:digest` - the hash of the message-digest attribute: that of the
    HMAC unless given, `:sha224`, `:sha256`, `:sha384` or `:sha512`; for
    a ready digest, its hash.
  * `:unauthenticated_attributes` - a list of `Cmsex.Attribute` that
    nothing covers (`[]`).
  * `:certificates` and `:crls` - certificates and revocation lists in
    DER for the OriginatorInfo (`[]`).
  * `:min_rsa_bits` - the shortest RSA key accepted (2048).

Recipients are checked as in `encrypt/3`, and an error that belongs to
one carries its position in `:recipient`.

# `mac!`

```elixir
@spec mac!(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  Cmsex.Recipient.t() | binary() | [Cmsex.Recipient.t() | binary()],
  keyword()
) :: binary()
```

Like `mac/3`, but raises `Cmsex.Error`.

# `sign`

```elixir
@spec sign(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: {:ok, binary()} | {:error, Cmsex.Error.t()}
```

Signs `content` and returns a SignedData message in DER (use
`Cmsex.PEM.encode/2` for PEM).

`signers` is one signer or a list of them, each a `Cmsex.Signer`, or a
map or keyword list with its fields: at least the certificate and the
private key.

    signer = %{certificate: cert_der, key: key_der, chain: [ca_der]}
    {:ok, cms} = Cmsex.sign(content, signer)
    {:ok, p7s} = Cmsex.sign(content, signer, detached: true)

The content is a binary, `{:stream, enumerable}` read once to compute the
digests, or `{:digest, hash, digest}` already computed, which every
signer must use. A stream or a digest makes a detached message with
signed attributes.

Each signer gets the content-type and message-digest attributes, the
signing time and the CMSAlgorithmProtection attribute (RFC 6211) unless
turned off, and its own attributes; see `Cmsex.Signer`. The algorithms
are checked with the policy of `verify/2`, and SHA-1 is never used.

Options:

  * `:detached` - leave the content out of the message (`false` for a
    binary; a stream or a digest is always detached).
  * `:content_type` - the type of the content (`:data`), a name of
    `Cmsex.OID` or an OID tuple. Another type needs signed attributes.
  * `:no_attributes` - sign without signed attributes (`false`): only
    for id-data given as a binary, and only for a verifier that cannot
    read them, since nothing then binds the algorithms or the time.
  * `:certificates` - `:signers` (the certificate and `:chain` of each
    signer, the default), `:none`, or a list of DER certificates.
  * `:crls` - revocation lists in DER (`[]`).
  * `:min_rsa_bits` - the shortest RSA key accepted (2048).
  * `:digest`, `:rsa_padding`, `:identifier`, `:signing_time` and
    `:algorithm_protection` - defaults for the signers that leave them
    out; see `Cmsex.Signer`.

The versions are those of RFC 5652, 5.1 and 5.3. Errors that belong to
one signer carry its position in `:signer`.

# `sign!`

```elixir
@spec sign!(
  binary() | {:stream, Enumerable.t()} | {:digest, atom(), binary()},
  Cmsex.Signer.t() | map() | keyword() | [Cmsex.Signer.t() | map() | keyword()],
  keyword()
) :: binary()
```

Like `sign/3`, but raises `Cmsex.Error`.

# `verify`

```elixir
@spec verify(binary(), keyword()) ::
  {:ok, Cmsex.Verified.t()} | {:error, Cmsex.Error.t()}
```

Verifies the signers of a SignedData message, in DER, BER or PEM.

A valid signature is not trust. Without `:trust`, `{:ok, verified}` only
says that the key of a certificate signed the content, and that
certificate may have come in the message itself, from anyone. Give the
roots you trust in `:trust` to know who signed.

For each signer, the checks are those of RFC 5652, 5.6: a certificate
that matches its identifier, from `:certificates` or from the message;
algorithms that are allowed and agree with each other and with the key;
the content-type, message-digest and CMSAlgorithmProtection attributes;
the signing-certificate attributes, when present; the signature; and,
with `:trust`, the certificate path. When several certificates match the
identifier, each is tried, and the one that verified is returned.

Options:

  * `:content` - the content of a detached message: a binary,
    `{:stream, enumerable}`, or `{:digest, hash, digest}`, already
    hashed. A stream is read once for all the digests, and once more
    only for an EdDSA signer without signed attributes, which signs the
    whole content and so needs it in memory; a digest is not enough for
    that signer (`:content_required`). Content given for a message that
    carries its own is `:unexpected_content`.
  * `:certificates` - certificates in DER to find the signers in, tried
    before those of the message (`[]`).
  * `:signers` - `:all` (the default) fails at the first signer that does
    not verify; `:any` needs one, and returns only those that verified.
  * `:trust` - root certificates in DER. The path of each signer is built
    from the certificates at hand and validated by
    `:public_key.pkix_path_validation/3`, at the current time; the
    signer's key usage, if any, must allow digitalSignature or
    nonRepudiation. A failure is `:untrusted`, with the reason in
    `detail`.
  * `:path_validation` - options for `:public_key.pkix_path_validation/3`,
    such as a `:verify_fun` that checks revocation (`[]`).
  * `:countersignatures` - also verify the countersignatures of each
    signer (`false`).
  * `:allow_sha1` - accept SHA-1 (`false`). MD5 is never accepted.
  * `:min_rsa_bits` - the shortest RSA key accepted (2048).
  * `:ed25519_digest` - `:lenient` (the default) accepts an Ed25519 signer
    whose digest algorithm is a SHA-2 other than SHA-512, and says so in
    its `:deviations`; `:strict` refuses it.
  * the limits of `decode/2`.

The signing-time attribute is returned as the signer's claim; it is never
the time at which the path is validated.

# `verify!`

```elixir
@spec verify!(binary(), keyword()) :: Cmsex.Verified.t()
```

Like `verify/2`, but raises `Cmsex.Error`.

# `verify_countersignature`

```elixir
@spec verify_countersignature(binary(), [non_neg_integer(), ...], keyword()) ::
  {:ok, Cmsex.Verified.signer()} | {:error, Cmsex.Error.t()}
```

Verifies one countersignature (RFC 5652, 11.4) on its own: the one at
`position`, such as `[0, 0]` for the first countersignature of the first
signer (see `t:position/0`), against the signature value of the
SignerInfo it countersigns, which is not verified here.

This is for a countersignature whose signer cannot be verified, such as
one in an algorithm the library leaves out, or when only the
countersignature matters. `verify/2` with `countersignatures: true`
checks the signers and their countersignatures together.

Returns the countersigner as in `t:Cmsex.Verified.signer/0`. Takes the
options of `verify/2`, except `:content` and `:signers`; with
`countersignatures: true`, the countersignatures below this one are
verified too. A position the message does not have raises
`ArgumentError`.

# `verify_digest`

```elixir
@spec verify_digest(binary(), keyword()) ::
  {:ok, Cmsex.DigestedData.t()} | {:error, Cmsex.Error.t()}
```

Checks the digest of a DigestedData message, in DER, BER or PEM, and
returns the message as `decode/2` reads it, whose `:content` is the
content, or `nil` for a detached message.

Options:

  * `:content` - the content of a detached message: a binary,
    `{:stream, enumerable}` or `{:digest, hash, digest}`.
  * `:allow_sha1` - take a SHA-1 digest (`false`). MD5 is never taken.
  * the limits of `decode/2`.

Errors: `:digest_mismatch`, `:weak_algorithm`, `:unsupported_algorithm`,
`:content_required` and `:unexpected_content`, and those of reading.
A message of another type is `:unsupported_content_type`.

# `verify_digest!`

```elixir
@spec verify_digest!(binary(), keyword()) :: Cmsex.DigestedData.t()
```

Like `verify_digest/2`, but raises `Cmsex.Error`.

# `verify_each`

```elixir
@spec verify_each(binary(), keyword()) ::
  {:ok, Cmsex.SignedData.t(),
   [
     {Cmsex.SignerInfo.t(),
      {:ok, Cmsex.Verified.signer()} | {:error, Cmsex.Error.t()}}
   ]}
  | {:error, Cmsex.Error.t()}
```

The result of each signer, rather than the first failure. Takes the
options of `verify/2`, except `:signers`.

    {:ok, signed_data, [{signer_info, {:ok, signer}}, {signer_info, {:error, error}}]}

An error that is not about one signer, such as a malformed message, a
missing content or a message without signers, is returned as
`{:error, error}`.

# `verify_mac`

```elixir
@spec verify_mac(binary(), keyword()) ::
  {:ok, Cmsex.Authenticated.t()} | {:error, Cmsex.Error.t()}
```

Checks the MAC of an AuthenticatedData message, in DER, BER or PEM.

The credentials are those of `decrypt/2`, with `:mac_key`, the MAC key
itself, in place of `:content_key`. The recipients are tried as in
`decrypt/2`; a key shorter than 16 bytes is not taken, since whoever
sees the MAC could find it.

With authenticated attributes, the MAC is over them, and once it has
checked, so are the content-type attribute, against the content, and
the message-digest attribute, against the digest of the content.

Errors:

  * `:invalid_mac` - the MAC does not check, or the key of the
    recipient does not unwrap; nothing tells these apart.
  * `:digest_mismatch`, `:content_type_mismatch`, `:missing_attribute`
    and `:invalid_attribute` - about the authenticated attributes, once
    the MAC has checked. Content other than `:data` needs them.
  * `:weak_algorithm` - a digest of SHA-1 without `allow_sha1: true`,
    or MD5. HMAC-SHA1, the MAC of RFC 3370, is taken.
  * those of `decrypt/2` for the credentials and the recipients, and
    `:content_required` and `:unexpected_content`.

Other options:

  * `:content` - the content of a detached message: a binary,
    `{:stream, enumerable}`, or `{:digest, hash, digest}` when the
    message has authenticated attributes with that hash.
  * `:allow_sha1` (`false`), `:max_iterations` (2,000,000), and the
    limits of `decode/2`.

# `verify_mac!`

```elixir
@spec verify_mac!(binary(), keyword()) :: Cmsex.Authenticated.t()
```

Like `verify_mac/2`, but raises `Cmsex.Error`.

---

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