README.md

# sparx

[![Package Version](https://img.shields.io/hexpm/v/sparx)](https://hex.pm/packages/sparx)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/sparx/)

A pure Gleam implementation of the [SPARX](https://www.cryptolux.org/index.php/SPARX) family of lightweight block ciphers, designed by Leo Perrin and Daniel Dinu at CryptoLUX (University of Luxembourg).

All three official variants are implemented and verified against the reference test vectors:

| Variant | Block size | Key size |
|---|---|---|
| `sparx64` | 64-bit | 128-bit |
| `sparx128_128` | 128-bit | 128-bit |
| `sparx128_256` | 128-bit | 256-bit |

## Installation

```sh
gleam add sparx@1
```

## Usage

Keys and plaintexts are represented as tuples of `Uint16` values (16-bit words in little-endian order). Use `sparx/uint16.unsafe_from_int` to construct them from integer literals.

### SPARX-64/128

64-bit block, 128-bit key.

```gleam
import sparx/sparx64
import sparx/uint16

pub fn main() {
  let key =
    sparx64.key_schedule(#(
      uint16.unsafe_from_int(0x0011),
      uint16.unsafe_from_int(0x2233),
      uint16.unsafe_from_int(0x4455),
      uint16.unsafe_from_int(0x6677),
      uint16.unsafe_from_int(0x8899),
      uint16.unsafe_from_int(0xaabb),
      uint16.unsafe_from_int(0xccdd),
      uint16.unsafe_from_int(0xeeff),
    ))

  let plaintext = #(
    uint16.unsafe_from_int(0x0123),
    uint16.unsafe_from_int(0x4567),
    uint16.unsafe_from_int(0x89ab),
    uint16.unsafe_from_int(0xcdef),
  )

  let ciphertext = sparx64.sparx_encrypt(plaintext, key)
  // -> #(0x2bbe, 0xf152, 0x01f5, 0x5f98)

  let recovered = sparx64.sparx_decrypt(ciphertext, key)
  // -> plaintext
}
```

### SPARX-128/128

128-bit block, 128-bit key.

```gleam
import sparx/sparx128_128
import sparx/uint16

pub fn main() {
  let key =
    sparx128_128.key_schedule(#(
      uint16.unsafe_from_int(0x0011),
      uint16.unsafe_from_int(0x2233),
      uint16.unsafe_from_int(0x4455),
      uint16.unsafe_from_int(0x6677),
      uint16.unsafe_from_int(0x8899),
      uint16.unsafe_from_int(0xaabb),
      uint16.unsafe_from_int(0xccdd),
      uint16.unsafe_from_int(0xeeff),
    ))

  let plaintext = #(
    uint16.unsafe_from_int(0x0123),
    uint16.unsafe_from_int(0x4567),
    uint16.unsafe_from_int(0x89ab),
    uint16.unsafe_from_int(0xcdef),
    uint16.unsafe_from_int(0xfedc),
    uint16.unsafe_from_int(0xba98),
    uint16.unsafe_from_int(0x7654),
    uint16.unsafe_from_int(0x3210),
  )

  let ciphertext = sparx128_128.sparx_encrypt(plaintext, key)
  // -> #(0x1cee, 0x7540, 0x7dbf, 0x23d8, 0xe0ee, 0x1597, 0xf428, 0x52d8)

  let recovered = sparx128_128.sparx_decrypt(ciphertext, key)
  // -> plaintext
}
```

### SPARX-128/256

128-bit block, 256-bit key.

```gleam
import sparx/sparx128_256
import sparx/uint16

pub fn main() {
  let key =
    sparx128_256.key_schedule(#(
      uint16.unsafe_from_int(0x0011),
      uint16.unsafe_from_int(0x2233),
      uint16.unsafe_from_int(0x4455),
      uint16.unsafe_from_int(0x6677),
      uint16.unsafe_from_int(0x8899),
      uint16.unsafe_from_int(0xaabb),
      uint16.unsafe_from_int(0xccdd),
      uint16.unsafe_from_int(0xeeff),
      uint16.unsafe_from_int(0xffee),
      uint16.unsafe_from_int(0xddcc),
      uint16.unsafe_from_int(0xbbaa),
      uint16.unsafe_from_int(0x9988),
      uint16.unsafe_from_int(0x7766),
      uint16.unsafe_from_int(0x5544),
      uint16.unsafe_from_int(0x3322),
      uint16.unsafe_from_int(0x1100),
    ))

  let plaintext = #(
    uint16.unsafe_from_int(0x0123),
    uint16.unsafe_from_int(0x4567),
    uint16.unsafe_from_int(0x89ab),
    uint16.unsafe_from_int(0xcdef),
    uint16.unsafe_from_int(0xfedc),
    uint16.unsafe_from_int(0xba98),
    uint16.unsafe_from_int(0x7654),
    uint16.unsafe_from_int(0x3210),
  )

  let ciphertext = sparx128_256.sparx_encrypt(plaintext, key)
  // -> #(0x3328, 0xe637, 0x14c7, 0x6ce6, 0x32d1, 0x5a54, 0xe4b0, 0xc820)

  let recovered = sparx128_256.sparx_decrypt(ciphertext, key)
  // -> plaintext
}
```

## API Reference

Full documentation is available on [hexdocs.pm/sparx](https://hexdocs.pm/sparx).

### `sparx64`

| Function | Signature | Description |
|---|---|---|
| `key_schedule` | `(Masterkey) -> Subkeys` | Derives subkeys from a 128-bit master key (8-tuple of `Uint16`) |
| `sparx_encrypt` | `(Plaintext, Subkeys) -> Plaintext` | Encrypts a 64-bit block (4-tuple of `Uint16`) |
| `sparx_decrypt` | `(Plaintext, Subkeys) -> Plaintext` | Decrypts a 64-bit block |

### `sparx128_128`

| Function | Signature | Description |
|---|---|---|
| `key_schedule` | `(Masterkey) -> Subkeys` | Derives subkeys from a 128-bit master key (8-tuple of `Uint16`) |
| `sparx_encrypt` | `(Plaintext, Subkeys) -> Plaintext` | Encrypts a 128-bit block (8-tuple of `Uint16`) |
| `sparx_decrypt` | `(Plaintext, Subkeys) -> Plaintext` | Decrypts a 128-bit block |

### `sparx128_256`

| Function | Signature | Description |
|---|---|---|
| `key_schedule` | `(Masterkey) -> Subkeys` | Derives subkeys from a 256-bit master key (16-tuple of `Uint16`) |
| `sparx_encrypt` | `(Plaintext, Subkeys) -> Plaintext` | Encrypts a 128-bit block (8-tuple of `Uint16`) |
| `sparx_decrypt` | `(Plaintext, Subkeys) -> Plaintext` | Decrypts a 128-bit block |

## Security Notice

This library is a reference implementation for educational and research purposes. It has not been independently audited. Do not use it to protect sensitive data in production without a thorough security review.

## References

- [SPARX specification](https://www.cryptolux.org/index.php/SPARX)
- Perrin, L., Dinu, D., Biryukov, A.: *SPARX: A side-channel resistant ARX cipher*. ASIACRYPT 2016.

## Development

```sh
gleam test  # Run the tests
gleam build # Build the project
```

## License

LGPL-3.0-or-later