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

NaCl-compatible encryption for Elixir with post-quantum support.

`MetamorphicCrypto` provides NaCl-compatible cryptographic primitives powered
by Rust NIFs with precompiled binaries — no Rust toolchain, no C compiler,
no system packages required.

## Quick Start

    # Generate keys
    key = MetamorphicCrypto.generate_key()
    {public_key, private_key} = MetamorphicCrypto.generate_keypair()

    # Symmetric encryption (XSalsa20-Poly1305)
    {:ok, ciphertext} = MetamorphicCrypto.encrypt("hello", key)
    {:ok, "hello"} = MetamorphicCrypto.decrypt(ciphertext, key)

    # Public-key encryption (X25519 sealed box)
    {:ok, sealed} = MetamorphicCrypto.seal("secret", public_key)
    {:ok, "secret"} = MetamorphicCrypto.unseal(sealed, public_key, private_key)

## Modules

For full control, use the specialized modules directly:

- `MetamorphicCrypto.SecretBox` — symmetric encryption
- `MetamorphicCrypto.BoxSeal` — public-key encryption
- `MetamorphicCrypto.Hybrid` — ML-KEM (512/768/1024) + X25519 post-quantum
- `MetamorphicCrypto.Seal` — unified seal/unseal (auto-detects format)
- `MetamorphicCrypto.KDF` — Argon2id key derivation
- `MetamorphicCrypto.Keys` — key generation and management
- `MetamorphicCrypto.Hash` — SHA3/SHA2 hashing for public data (fingerprints, safety numbers)
- `MetamorphicCrypto.Mac` — HMAC-SHA256 keyed message authentication (RFC 2104)
- `MetamorphicCrypto.Sign` — hybrid ML-DSA + Ed25519 post-quantum signatures
- `MetamorphicCrypto.Vrf` — ECVRF-Edwards25519-SHA512-TAI verifiable random function (RFC 9381)
- `MetamorphicCrypto.VrfP256` — ECVRF-P256-SHA256-TAI verifiable random function (RFC 9381)
- `MetamorphicCrypto.Recovery` — human-readable recovery keys

## Wire Format

All functions accept and return base64-encoded strings. Ciphertext produced
by this library is byte-compatible with libsodium/NaCl and the
`metamorphic-crypto` WASM module used in browser clients.

# `decrypt`

```elixir
@spec decrypt(ciphertext :: String.t(), key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Decrypt a ciphertext back to a UTF-8 string.

## Example

    {:ok, "hello, world!"} = MetamorphicCrypto.decrypt(ciphertext, key)

# `derive_public_key`

```elixir
@spec derive_public_key(secret_key_b64 :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Re-derive the base64 public key from a base64 hybrid signing secret key.

Deterministic — reproduces the keypair's `public_key` exactly, which is what
makes recovery-based key regeneration byte-identical. See
`MetamorphicCrypto.Sign`.

# `encrypt`

```elixir
@spec encrypt(plaintext :: String.t(), key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Encrypt a UTF-8 string with a symmetric key.

Uses XSalsa20-Poly1305 (NaCl secretbox). Returns base64-encoded ciphertext.

## Example

    key = MetamorphicCrypto.generate_key()
    {:ok, ciphertext} = MetamorphicCrypto.encrypt("hello, world!", key)

# `generate_key`

```elixir
@spec generate_key() :: String.t()
```

Generate a random 32-byte symmetric key (base64-encoded).

## Example

    key = MetamorphicCrypto.generate_key()

# `generate_keypair`

```elixir
@spec generate_keypair() :: {String.t(), String.t()}
```

Generate an X25519 keypair.

Returns `{public_key, private_key}` as base64-encoded strings.

## Example

    {public_key, private_key} = MetamorphicCrypto.generate_keypair()

# `generate_signing_keypair`

```elixir
@spec generate_signing_keypair(MetamorphicCrypto.Sign.level()) ::
  MetamorphicCrypto.Sign.keypair()
```

Generate a hybrid ML-DSA + Ed25519 signing keypair (Cat-3 / ML-DSA-65 default).

Returns `%{public_key: base64, secret_key: base64}`. See
`MetamorphicCrypto.Sign` for security levels, the wire format, and the recovery
hook.

## Example

    kp = MetamorphicCrypto.generate_signing_keypair()
    kp = MetamorphicCrypto.generate_signing_keypair(:cat5)

# `seal`

```elixir
@spec seal(plaintext :: String.t(), public_key :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Encrypt a UTF-8 string to a recipient's public key (anonymous sealed box).

The sender remains anonymous — only the recipient can decrypt.

## Example

    {public_key, _private_key} = MetamorphicCrypto.generate_keypair()
    {:ok, sealed} = MetamorphicCrypto.seal("secret message", public_key)

# `sha3_512`

```elixir
@spec sha3_512(data_b64 :: String.t()) :: {:ok, String.t()} | {:error, String.t()}
```

SHA3-512 of base64-encoded data (base64 digest out).

General-purpose digest for **public** data. For key fingerprints, safety
numbers, and key-transparency-log entries, prefer `sha3_512_with_context/2`.
See `MetamorphicCrypto.Hash` for the full menu and the "public data only"
security note.

## Example

    {:ok, digest} = MetamorphicCrypto.sha3_512(Base.encode64("abc"))

# `sha3_512_with_context`

```elixir
@spec sha3_512_with_context(context :: String.t(), data_b64 :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Domain-separated SHA3-512 — recommended for key fingerprints, safety numbers,
and key-transparency-log entries.

`context` is a versioned UTF-8 label (e.g. `"mosslet/key-fingerprint/v1"`);
`data_b64` is the base64-encoded payload. See `MetamorphicCrypto.Hash`.

## Example

    {:ok, fp} =
      MetamorphicCrypto.sha3_512_with_context(
        "mosslet/key-fingerprint/v1",
        Base.encode64("public key bytes")
      )

# `sign`

```elixir
@spec sign(message :: binary(), context :: String.t(), secret_key_b64 :: String.t()) ::
  {:ok, String.t()} | {:error, String.t()}
```

Sign a `message` binary under a UTF-8 `context` with a base64 hybrid
`secret_key`.

Returns `{:ok, signature_b64}`. ML-DSA signing is randomized, so signatures are
non-reproducible (but verify). See `MetamorphicCrypto.Sign`.

## Example

    {:ok, sig} =
      MetamorphicCrypto.sign("log entry", "metamorphic/sign/v1", kp.secret_key)

# `unseal`

```elixir
@spec unseal(
  ciphertext :: String.t(),
  public_key :: String.t(),
  private_key :: String.t()
) ::
  {:ok, String.t()} | {:error, String.t()}
```

Decrypt a sealed box using the recipient's keypair.

## Example

    {public_key, private_key} = MetamorphicCrypto.generate_keypair()
    {:ok, sealed} = MetamorphicCrypto.seal("secret", public_key)
    {:ok, "secret"} = MetamorphicCrypto.unseal(sealed, public_key, private_key)

# `verify`

```elixir
@spec verify(
  message :: binary(),
  context :: String.t(),
  signature_b64 :: String.t(),
  public_key_b64 :: String.t()
) :: boolean()
```

Verify a composite `signature` over `message` / `context` against `public_key`.

Returns `true` only if **both** the Ed25519 and ML-DSA components verify
(strict AND); `false` otherwise (including malformed input). See
`MetamorphicCrypto.Sign`.

## Example

    true = MetamorphicCrypto.verify("log entry", "metamorphic/sign/v1", sig, kp.public_key)

---

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