# `MetamorphicCrypto.Seal`
[🔗](https://github.com/moss-piglet/metamorphic_crypto/blob/main/lib/metamorphic_crypto/seal.ex#L1)

Unified seal/unseal with automatic format detection.

This module provides the highest-level encryption API. It automatically
selects the best available algorithm:

- If a post-quantum public key is provided, uses **ML-KEM + X25519 hybrid**
- Otherwise, falls back to **X25519 sealed box** (NaCl-compatible)

On decryption, the format is auto-detected from the ciphertext header byte,
so old (legacy) and new (PQ) ciphertexts can coexist seamlessly.

## Security Levels

When sealing with a PQ key, pass `:level` to choose the NIST category:

- `:cat1` — ML-KEM-512 + X25519 (~AES-128).
- `:cat3` — ML-KEM-768 + X25519 (~AES-192). Default.
- `:cat5` — ML-KEM-1024 + X25519 (~AES-256).

Decryption always auto-detects the level from the ciphertext header.

## Usage

    # Classical (X25519 only)
    {pk, sk} = MetamorphicCrypto.Keys.generate_keypair()
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("data", pk)
    {:ok, "data"} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk)

    # Post-quantum hybrid Cat-3 (default)
    {pq_pk, pq_sk} = MetamorphicCrypto.Hybrid.generate_keypair()
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("data", pk, pq_public_key: pq_pk)
    {:ok, "data"} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk, pq_secret_key: pq_sk)

    # Post-quantum hybrid Cat-1 (lightest standardized tier)
    {pq_pk, pq_sk} = MetamorphicCrypto.Hybrid.generate_keypair(:cat1)
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("data", pk, pq_public_key: pq_pk, level: :cat1)
    {:ok, "data"} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk, pq_secret_key: pq_sk)

    # Post-quantum hybrid Cat-5 (highest security)
    {pq_pk, pq_sk} = MetamorphicCrypto.Hybrid.generate_keypair(:cat5)
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("data", pk, pq_public_key: pq_pk, level: :cat5)
    {:ok, "data"} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk, pq_secret_key: pq_sk)

# `seal_for_user`

```elixir
@spec seal_for_user(
  plaintext :: String.t(),
  public_key_b64 :: String.t(),
  opts :: keyword()
) ::
  {:ok, String.t()} | {:error, String.t()}
```

Seal plaintext (UTF-8 string) to a user's key(s).

## Options

- `:pq_public_key` — if provided, uses hybrid ML-KEM + X25519 encryption.
  Otherwise uses classical X25519 sealed box.
- `:level` — `t:MetamorphicCrypto.Hybrid.security_level/0`, one of `:cat1`,
  `:cat3` (default), or `:cat5`. Only applies when `:pq_public_key` is present.
- `:suite` — `t:MetamorphicCrypto.Hybrid.suite/0`, one of `:hybrid` (default),
  `:hybrid_matched`, or `:pure_cnsa2`. With a non-default suite the result
  carries a new CNSA 2.0 wire tag and **must** be opened with
  `MetamorphicCrypto.Hybrid.open/2,3` (not `unseal_from_user/4`, which only
  auto-detects the legacy `:hybrid` tags). `:pure_cnsa2` requires
  `level: :cat5`.

## Examples

    # Classical
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("secret", public_key)

    # Post-quantum Cat-3 (default)
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("secret", public_key, pq_public_key: pq_pk)

    # Post-quantum Cat-1
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("secret", public_key,
      pq_public_key: pq_pk, level: :cat1)

    # Post-quantum Cat-5
    {:ok, ct} = MetamorphicCrypto.Seal.seal_for_user("secret", public_key,
      pq_public_key: pq_pk, level: :cat5)

# `seal_for_user_raw`

```elixir
@spec seal_for_user_raw(
  plaintext_b64 :: String.t(),
  public_key_b64 :: String.t(),
  opts :: keyword()
) :: {:ok, String.t()} | {:error, String.t()}
```

Seal raw bytes (as base64) to a user's key(s).

Same as `seal_for_user/3` but accepts pre-encoded base64 plaintext.
Supports the same options (`:pq_public_key`, `:level`).

# `unseal_from_user`

```elixir
@spec unseal_from_user(
  ciphertext_b64 :: String.t(),
  public_key_b64 :: String.t(),
  private_key_b64 :: String.t(),
  opts :: keyword()
) :: {:ok, String.t()} | {:error, String.t()}
```

Unseal ciphertext using the user's key(s). Auto-detects format.

## Options

- `:pq_secret_key` — the hybrid ML-KEM secret key. Required for decrypting
  hybrid (v2/v3) ciphertexts. Safe to always pass — legacy ciphertexts are
  detected and decrypted with the classical key regardless.

## Examples

    # Classical
    {:ok, plaintext} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk)

    # With PQ key available (auto-detects format and level)
    {:ok, plaintext} = MetamorphicCrypto.Seal.unseal_from_user(ct, pk, sk, pq_secret_key: pq_sk)

---

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