sparx
A pure Gleam implementation of the 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
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.
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.
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.
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.
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
- Perrin, L., Dinu, D., Biryukov, A.: SPARX: A side-channel resistant ARX cipher. ASIACRYPT 2016.
Development
gleam test # Run the tests
gleam build # Build the project
License
LGPL-3.0-or-later