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

Verifiable Random Function — ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381, suite
`0x03`).

Thin, idiomatic wrappers over the audited `metamorphic-crypto` Rust core — the
same primitive used by the browser WASM build, so a proof produced or verified
here behaves **byte-identically** across native Rust, WASM, and this NIF.

A VRF is a keyed function whose owner can compute, for any input `alpha`, a
pseudorandom output `beta` **and** a proof `pi` that `beta` was computed
correctly under their public key. Anyone with the public key can verify `pi`,
but cannot compute `beta` for a new input themselves, and cannot learn `alpha`
from `(pi, beta)` alone. It backs `MetamorphicLog`'s CONIKS-style
key-transparency layer, mapping a (private) identity index to a pseudorandom
tree position — giving *index privacy* together with a verifiable,
non-equivocable mapping.

This is the Edwards25519 suite; see `MetamorphicCrypto.VrfP256` for the NIST
P-256 sibling (`KT_128_SHA256_P256`).

## Post-quantum posture (honest framing)

> #### This VRF is classical, and protects only index privacy {: .warning}
>
> ECVRF is **classical** (elliptic-curve discrete log) — the one place in the
> transparency stack whose security is not post-quantum. It protects exactly
> one property: *index privacy*. Integrity, authenticity, and the hash-based
> commitments are post-quantum and do not rely on this VRF. Not FIPS-validated.

## Encoding & wire format (base64 in, base64 out)

All arguments and results cross as **base64 strings**, matching the WASM wire
format. Raw byte lengths (before base64):

    secret key  (SK) : 32 bytes   (an Ed25519-style seed)
    public key  (Y)  : 32 bytes   (compressed Edwards point)
    proof       (pi) : 80 bytes   = Gamma(32) || c(16) || s(32)
    output      (beta): 64 bytes  (a SHA-512 digest)

Correctness is pinned byte-for-byte by RFC 9381 Appendix B test vectors, so
independent implementations interoperate exactly.

## Verification result shape

`verify/3` distinguishes a **cryptographic rejection** from a **structural**
(malformed-input) error:

- `{:ok, output_b64}` — the proof is valid; `output_b64` is the VRF output
  `beta` (identical to `proof_to_hash/1` on the same proof).
- `:invalid` — a cryptographic rejection: wrong key, tampered `alpha`, tampered
  proof, or a non-canonical/degenerate key or `Gamma`. The inputs were
  well-formed but the proof does not verify.
- `{:error, reason}` — a structural failure: a wrong-length key/proof or
  invalid base64. Nothing was cryptographically decided.

# `keypair`

```elixir
@type keypair() :: %{secret_key: String.t(), public_key: String.t()}
```

An ECVRF keypair, both fields base64-encoded (32-byte seed / 32-byte point).

# `generate_keypair`

```elixir
@spec generate_keypair() :: keypair()
```

Generate a fresh VRF keypair from the OS CSPRNG.

Returns `%{secret_key: base64, public_key: base64}`. The secret key is a
32-byte seed; handle it as secret material.

## Example

    %{secret_key: sk, public_key: pk} = MetamorphicCrypto.Vrf.generate_keypair()

# `output_len`

```elixir
@spec output_len() :: pos_integer()
```

Output (`beta`) length in bytes (`64`).

# `proof_len`

```elixir
@spec proof_len() :: pos_integer()
```

Proof (`pi`) length in bytes (`80`).

# `proof_to_hash`

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

Recover the VRF output `beta` (base64) directly from a proof `pi`, **without**
verifying it.

This is only the `Gamma -> beta` mapping; it does **not** check that `pi` is a
valid proof for any `(public_key, alpha)`. Use it only on a proof already
verified with `verify/3`, or whose provenance you independently trust. Returns
`{:ok, output_b64}` or `{:error, reason}`.

# `proof_to_hash!`

```elixir
@spec proof_to_hash!(proof_b64 :: String.t()) :: String.t()
```

Same as `proof_to_hash/1` but returns the output directly, raising on invalid
input.

# `prove`

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

Produce a VRF proof `pi` (base64) for base64 `alpha` under a base64
`secret_key`.

The proof both authenticates the output and lets any verifier recompute it.
Returns `{:ok, proof_b64}` or `{:error, reason}`. TAI ECVRF is deterministic:
the same key and `alpha` always yield the same proof.

## Example

    %{secret_key: sk} = MetamorphicCrypto.Vrf.generate_keypair()
    {:ok, pi} = MetamorphicCrypto.Vrf.prove(sk, Base.encode64("identity index"))

# `prove!`

```elixir
@spec prove!(secret_key_b64 :: String.t(), alpha_b64 :: String.t()) :: String.t()
```

Same as `prove/2` but returns the proof directly, raising on invalid input.

# `public_key`

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

Derive the base64 public key for a base64 `secret_key`.

Returns `{:ok, public_key_b64}` or `{:error, reason}` (wrong length / invalid
base64).

## Example

    %{secret_key: sk, public_key: pk} = MetamorphicCrypto.Vrf.generate_keypair()
    {:ok, ^pk} = MetamorphicCrypto.Vrf.public_key(sk)

# `public_key_len`

```elixir
@spec public_key_len() :: pos_integer()
```

Public-key length in bytes (`32`).

# `secret_key_len`

```elixir
@spec secret_key_len() :: pos_integer()
```

Secret-key length in bytes (`32`).

# `suite`

```elixir
@spec suite() :: 3
```

RFC 9381 ciphersuite octet for ECVRF-EDWARDS25519-SHA512-TAI (`0x03`).

# `verify`

```elixir
@spec verify(
  public_key_b64 :: String.t(),
  alpha_b64 :: String.t(),
  proof_b64 :: String.t()
) :: {:ok, String.t()} | :invalid | {:error, String.t()}
```

Verify a VRF proof `pi` for `alpha` under `public_key` (all base64).

Returns:

- `{:ok, output_b64}` — the proof is valid; `output_b64` is the VRF output.
- `:invalid` — a cryptographic rejection (wrong key, tampered input/proof,
  degenerate key or `Gamma`). Well-formed inputs, but the proof does not verify.
- `{:error, reason}` — a structural failure (wrong-length key/proof, invalid
  base64).

## Example

    %{secret_key: sk, public_key: pk} = MetamorphicCrypto.Vrf.generate_keypair()
    alpha = Base.encode64("identity index")
    {:ok, pi} = MetamorphicCrypto.Vrf.prove(sk, alpha)
    {:ok, beta} = MetamorphicCrypto.Vrf.verify(pk, alpha, pi)
    :invalid = MetamorphicCrypto.Vrf.verify(pk, Base.encode64("other"), pi)

---

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