README.md

# AshFeistelCipher

AshFeistelCipher is an `Ash.Resource` extension for transforming integer attribute values using a [Feistel cipher](https://en.wikipedia.org/wiki/Feistel_cipher). 

## Why Use This?

**Problem**: Sequential IDs (1, 2, 3...) expose sensitive business information:
- Competitors can track your growth rate by checking IDs over time
- Users can enumerate all resources (`/posts/1`, `/posts/2`...)
- Total record counts are publicly visible

**Solution**: This library uses a [Feistel cipher](https://en.wikipedia.org/wiki/Feistel_cipher) to transform sequential integers into non-sequential, unpredictable values. You keep a sequential column for ordering, and an encrypted column as the primary key. Only the encrypted ID is exposed in APIs and URLs. The transformation is deterministic, reversible, and automatically handled via database triggers integrated with Ash.

For more details on the algorithm and implementation, see [feistel_cipher](https://github.com/devall-org/feistel_cipher).

## Installation

### Using igniter (Recommended)

```bash
mix igniter.install ash_feistel_cipher
```

You can customize the installation with the following options:

* `--repo` or `-r`: Specify an Ecto repo for FeistelCipher to use.
* `--functions-prefix` or `-p`: Specify the PostgreSQL schema prefix where the FeistelCipher functions will be created, defaults to `public`.
* `--functions-salt` or `-s`: Specify the constant value used in the Feistel cipher algorithm. Changing this value will result in different cipher outputs for the same input, should be less than 2^31, defaults to `1_076_943_109`.

Example with custom options:

```bash
mix igniter.install ash_feistel_cipher --functions-prefix accounts --functions-salt 123456789
```

### Manual Installation

If you need more control over the installation process, you can install manually:

1. Add `ash_feistel_cipher` to your list of dependencies in `mix.exs`:

   ```elixir
   def deps do
     [
       {:ash_feistel_cipher, "~> 0.9.0"}
     ]
   end
   ```

2. Fetch the dependencies:

   ```bash
   mix deps.get
   ```

3. Install FeistelCipher separately with custom options if needed:

   ```bash
   mix igniter.install feistel_cipher --repo MyApp.Repo --functions-prefix accounts
   ```

4. Add `:ash_feistel_cipher` to your formatter configuration in `.formatter.exs`:

   ```elixir
   [
     import_deps: [:ash_feistel_cipher]
   ]
   ```

## Usage

Use `AshFeistelCipher` in your `Ash.Resource` and configure the `feistel_cipher` block as follows:

```elixir
defmodule MyApp.Post do
  use Ash.Resource,
    data_layer: Ash.DataLayer.Postgres,
    extensions: [AshFeistelCipher]

  attributes do
    integer_primary_key :id

    # 'seq' is only a source for generating serial integers, so override with primary_key?: false.
    integer_primary_key :seq, primary_key?: false
  end

  feistel_cipher do
    functions_prefix "accounts" # PostgreSQL schema where feistel functions are installed. Default is "public".
    
    encrypt do
      source :seq # Source attribute for the Feistel cipher.
      target :id # Target attribute for the Feistel cipher.
      bits 40 # Specifies the maximum number of bits for both the source and target integers.
    end

    encrypt do
      source :seq
      target :referral_code
      key 12345 # Custom encryption key (0 to 2^31-1) or derive automatically from attributes.
    end
  end
end
```

Then,

```
mix ash.codegen create_post
```

will generate a migration that sets up a database trigger to encrypt the `seq` attribute into the `id` attribute using a Feistel cipher.

## Related Projects

* [feistel_cipher](https://github.com/devall-org/feistel_cipher): The underlying library that provides Ecto migrations and PostgreSQL functions for Feistel cipher operations. `ash_feistel_cipher` builds on top of this to integrate the capability seamlessly into the Ash framework.

## License

MIT