# `ExFPE`
[🔗](https://github.com/g-andrade/ex_fpe/blob/v0.1.0/lib/ex_fpe.ex#L1)

Format-preserving encryption (FPE) for Elixir.

ExFPE encrypts a numerical string into another of the **same length over the
same alphabet**, which is useful to e.g. store an encrypted credit card
number in a field that only accepts credit-card-shaped values, and other
suchlike applications.

By default it uses **FF1** (`ExFPE.FF1`), the only mode approved by NIST in
[SP 800-38Gr1 2pd](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38Gr1.2pd.pdf).
To pick a mode explicitly, pass it as the **second argument** to `new/3`:

* `:ff1` — the FF1 mode (default; variable-length tweak).
* `:ff3_1` — the FF3-1 mode (fixed **7-byte** tweak). ⚠️ NIST removed FF3-1;
  use it only for interop with existing data. See `ExFPE.FF3_1`.

> #### Mode-specific rules {: .info}
>
> The **tweak size** and the **length constraints** on inputs depend on the
> mode.
> * FF1 accepts a variable-length tweak (it may even be empty); see
> `ExFPE.FF1`.
> * FF3-1 uses a fixed 7-byte (56-bit) tweak; see `ExFPE.FF3_1`.

## Guide

See the [usage guide](readme.html) for worked, runnable examples — contexts,
built-in and custom alphabets, tweaks, alphabet-free raw integers, choosing a
mode, and the `use ExFPE` convenience for supervised contexts.

# `alphabet`

```elixir
@type alphabet() :: String.t()
```

The ordered symbols of a custom alphabet, given as a string.

Each symbol is a single Unicode codepoint, and the radix is the amount of
symbols (**not** graphemes).

For any alphabet not covered by `ExFPE.Codec.Builtin`, see
`ExFPE.Codec.Custom` for the exact rules.

# `constraints`

```elixir
@type constraints() :: %{min_length: pos_integer(), max_length: pos_integer()}
```

A mode's inclusive bounds on input length, as returned by `constraints/1`.

# `key`

```elixir
@type key() :: ExFPE.FFX.key()
```

An AES key: 16, 24, or 32 bytes (AES-128/192/256).

# `mode`

```elixir
@type mode() :: :ff1 | :ff3_1
```

A supported FPE mode.

# `radix`

```elixir
@type radix() :: ExFPE.FFX.radix()
```

The base the numerical strings are written in, from 2 up to 65536
(or 65535 in the case of `ExFPE.FF3_1`).

# `t`

```elixir
@opaque t()
```

An encryption/decryption context.

Built by `new/3` (or `new!/3`) and passed to `encrypt!/3` and `decrypt!/3`.
Bundles the chosen mode's `algorithm` (which holds the key) with how inputs map
to integers — an alphabet, or the raw integers of a `{:raw_only, radix}` context.

# `tweak`

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

A tweak: public data that varies the ciphertext for a given key and plaintext.

Its length depends on the mode — variable for FF1, a fixed 7 bytes for FF3-1.

# `child_spec`

```elixir
@callback child_spec() :: Supervisor.child_spec()
```

Returns the `Supervisor` child spec for the module that `use ExFPE`.

Implement it by calling the generated `child_spec/2` or `child_spec/3` with
your key, mode, and radix/alphabet (or `{:raw_only, radix}`) — see the moduledoc.

# `__using__`
*macro* 

Places an `ExFPE` context under your supervision tree so that you can encrypt
and decrypt without threading the context through every call.

A module that declares `use ExFPE` gets:

  * a `child_spec/2` / `child_spec/3` builder and a `start_link/3`, backed by
    a uniquely named process holding the context in a `:persistent_term`;
  * `encrypt/2`, `encrypt!/2`, `decrypt/2`, `decrypt!/2` that retrieve the
    context transparently; plus `constraints/0` and `ex_fpe!/0`.

You implement the `c:child_spec/0` callback declaring your configuration, and
add `MyModule.child_spec()` to your supervision tree.

    defmodule MyApp.CardCipher do
      use ExFPE

      @impl true
      def child_spec do
        child_spec(fetch_key(), :ff3_1, _radix = 10)
      end

      defp fetch_key, do: Application.fetch_env!(:my_app, :fpe_key)
    end

    # in your application's supervision tree:
    children = [
      MyApp.CardCipher.child_spec(),
      # ...
    ]

    # then, anywhere:
    MyApp.CardCipher.encrypt!(tweak, "34436524")

# `constraints`

```elixir
@spec constraints(t()) :: constraints()
```

Returns a `ctx`'s mode-specific length constraints (`min_length`/`max_length`).

# `decrypt`

```elixir
@spec decrypt(t(), tweak(), ciphertext) :: {:ok, plaintext} | {:error, term()}
when ciphertext: String.t(), plaintext: String.t()
```

Decrypts `ciphertext` back into its plaintext numerical string, using `tweak`.

Returns `{:error, reason}` if the tweak or input is invalid.

# `decrypt!`

```elixir
@spec decrypt!(t(), tweak(), ciphertext) :: plaintext
when ciphertext: String.t(), plaintext: String.t()
```

Like `decrypt/3`, but returns the plaintext directly and raises
`ExFPE.InputError` on failure.

# `encrypt`

```elixir
@spec encrypt(t(), tweak(), plaintext) :: {:ok, ciphertext} | {:error, term()}
when plaintext: String.t(), ciphertext: String.t()
```

Encrypts `plaintext` into a numerical string of the same length over the same
alphabet, using `tweak`.

Returns `{:error, reason}` if the tweak or input is invalid.

# `encrypt!`

```elixir
@spec encrypt!(t(), tweak(), plaintext) :: ciphertext
when plaintext: String.t(), ciphertext: String.t()
```

Like `encrypt/3`, but returns the ciphertext directly and raises
`ExFPE.InputError` on failure.

# `new`

```elixir
@spec new(key(), mode(), radix() | alphabet() | {:raw_only, radix()}) ::
  {:ok, ctx :: t()} | {:error, term()}
```

Creates a context for both encryption and decryption from a `key`, an optional
`mode` (`:ff1` by default), and either a `radix`, an `alphabet`, or
`{:raw_only, radix}`.

A `radix` or `alphabet` encrypts strings with `encrypt/3`/`decrypt/3`;
`{:raw_only, radix}` skips symbols entirely and works with integers through
`raw_encrypt/4`/`raw_decrypt/4`.

Returns `{:error, reason}` if any argument is invalid.

# `new!`

```elixir
@spec new!(key(), mode(), radix() | alphabet() | {:raw_only, radix()}) :: ctx :: t()
```

Like `new/3`, but returns the context directly and raises `ExFPE.ArgumentError`
on failure.

# `raw_decrypt`

```elixir
@spec raw_decrypt(t(), tweak(), cipherval, length) ::
  {:ok, plainval} | {:error, term()}
when cipherval: non_neg_integer(),
     length: pos_integer(),
     plainval: non_neg_integer()
```

Decrypts the integer `cipherval` back into its plaintext value, skipping the
symbol alphabet.

The alphabet-free counterpart to `decrypt/3`; see `raw_encrypt/4` for how
`length` and the `0 <= value < radix ** length` bound work.

Returns `{:error, reason}` if the tweak or input is invalid.

# `raw_decrypt!`

```elixir
@spec raw_decrypt!(t(), tweak(), cipherval, length) :: plainval
when cipherval: non_neg_integer(),
     length: pos_integer(),
     plainval: non_neg_integer()
```

Like `raw_decrypt/4`, but returns the plaintext value directly and raises
`ExFPE.InputError` on failure.

# `raw_encrypt`

```elixir
@spec raw_encrypt(t(), tweak(), plainval, length) ::
  {:ok, cipherval} | {:error, term()}
when plainval: non_neg_integer(),
     length: pos_integer(),
     cipherval: non_neg_integer()
```

Encrypts the integer `plainval` directly, skipping the symbol alphabet.

This is the alphabet-free counterpart to `encrypt/3`: you hand it an integer
and it hands an integer back, leaving the mapping between integers and
whatever symbols they stand for up to you. Reach for it when your symbols
aren't a single Unicode scalar each (so a custom alphabet can't accept them),
when the value already lives as an integer in your system, or to avoid string
encoding on a hot path.

`plainval` is interpreted as `length` digits in the context's radix, most
significant first. **`length` is significant** and must be passed explicitly:
FPE treats leading zeroes as real symbols, so `{value: 42, length: 2}` ("42")
and `{value: 42, length: 5}` ("00042") encrypt differently and can't be told
apart from the value alone. The ciphertext preserves `length`.

`plainval` must fit its declared length — that is,
`0 <= plainval < radix ** length` — otherwise this returns
`{:error, {:negative_value, plainval}}` or
`{:error, {:value_is_larger_than_declared_length}}`.

Any context supports this, including one built from an alphabet: it operates on
the context's radix and ignores the alphabet. A context built with
`{:raw_only, radix}` supports *only* these functions, not `encrypt/3`.

Returns `{:error, reason}` if the tweak or input is invalid.

# `raw_encrypt!`

```elixir
@spec raw_encrypt!(t(), tweak(), plainval, length) :: cipherval
when plainval: non_neg_integer(),
     length: pos_integer(),
     cipherval: non_neg_integer()
```

Like `raw_encrypt/4`, but returns the ciphertext value directly and raises
`ExFPE.InputError` on failure.

---

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