Fast Format Preserving Encryption (FPE) implementation in Rust with Python bindings.
Format-preserving encryption (FPE) is a cryptographic method that encrypts data while preserving its format. For example, encrypting a credit card number yields another valid-looking credit card number, making it useful for data protection while maintaining compatibility with existing systems.
- FF1 and FF3-1 Format Preserving Encryption algorithms
- Fast Rust implementation with Python bindings
- Support for custom alphabets
- Thread-safe
pip install fastfpe>>> from fastfpe import ff1
>>>
>>> # 128/192/256-bit AES keys supported (16/24/32 bytes -> 32/48/64 hex chars)
>>> key = "2b7e151628aed2a6abf7158809cf4f3c" # 128-bit key
>>> tweak = "" # FF1 tweak may be empty or longer (length impacts security domain sizing)
>>> alphabet = "0123456789"
>>> plaintext = "0123456789"
>>>
>>> ciphertext = ff1.encrypt(key, tweak, alphabet, plaintext)
>>> ciphertext
'2433477484'
>>> ff1.decrypt(key, tweak, alphabet, ciphertext)
'0123456789'Notes:
- FF1 tweak can be empty; size limits depend on NIST SP 800-38G constraints (radix and length bounds enforced internally).
- Provide a non-empty tweak for domain separation across contexts.
- Both algorithms return
ValueErrorwith descriptive messages on invalid inputs.
Warning
FF3/FF3-1 are no longer recommended in some security guidance due to design concerns.
FF1 is fully supported by fastfpe and recommended for new deployments.
FF3-1 remains available for compatibility and migration.
See NIST SP 800-38G and other sources for details.
>>> from fastfpe import ff3_1
>>>
>>> key = "3eaa133d22a7ee2432fb8ecfde1e97d9106dcf26b9edaa52b3ed4acd9a9b8445"
>>> tweak = "5be49f26c1dbb7" # 7 bytes (14 hex chars)
>>> alphabet = "abcdef0123456789"
>>> plaintext = "024587931578"
>>>
>>> ciphertext = ff3_1.encrypt(key, tweak, alphabet, plaintext)
>>> ciphertext
'd756b8704a2d'
>>> ff3_1.decrypt(key, tweak, alphabet, ciphertext)
'024587931578'As expected, fastfpe is much faster than the reference python implementation. The gains are more pronounced with larger plaintexts.
The following benchmarks were performed on an Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz.
Running 10,000 iterations, 5 times each, 12-byte plaintext
--------------------------------------------------
Python implementation: 0.110 ms/op (± 0.001 ms)
Rust implementation: 0.008 ms/op (± 0.000 ms)
Rust is 13.2x faster
Running 10,000 iterations, 5 times each, 16-byte plaintext
--------------------------------------------------
Python implementation: 0.133 ms/op (± 0.002 ms)
Rust implementation: 0.009 ms/op (± 0.000 ms)
Rust is 14.5x faster
Running 10,000 iterations, 5 times each, 20-byte plaintext
--------------------------------------------------
Python implementation: 0.161 ms/op (± 0.005 ms)
Rust implementation: 0.010 ms/op (± 0.002 ms)
Rust is 15.9x faster
Running 10,000 iterations, 5 times each, 24-byte plaintext
--------------------------------------------------
Python implementation: 0.189 ms/op (± 0.004 ms)
Rust implementation: 0.011 ms/op (± 0.000 ms)
Rust is 16.8x faster
| Component | Versions / Platforms |
|---|---|
| Python | CPython 3.8 – 3.14 (abi3 single wheel per platform) |
| Operating Systems | Linux (x86_64, aarch64 manylinux), macOS (universal2), Windows (x64, arm64) |
| Algorithms | FF1 (recommended), FF3-1 (compatibility) |
| Key Sizes | 128 / 192 / 256-bit AES |
| Tweak | FF1: variable length (can be empty). FF3-1: exactly 7 bytes |
| Alphabet | Custom per call; size defines radix (validated) |
If you need other targets (e.g., musllinux, ppc64le) open an issue or PR.
Licensed under the MIT License. See LICENSE for more information.
Uses the rust-fpe implementation by johntyner, under the MIT License.
Uses the ff3 python package by mysto as a reference implementation, under the Apache 2.0 License.