Package: ff3
FF3 - Format Preserving Encryption in PythonAn implementation of the NIST approved FF3 and FF3-1 Format Preserving Encryption (FPE) algorithms in Python.This package implements the FF3 algorithm for Format Preserving Encryption as described in the March 2016 NIST publication 800-38GMethods for Format-Preserving Encryption,
and revised on February 28th, 2019 with a draft update for FF3-1.NIST Recommendation SP 800-38G (FF3)NIST Recommendation SP 800-38G Revision 1 (FF3-1)Changes to minimum domain size and revised tweak length have been implemented in this package with
support for both 64-bit and 56-bit tweaks. NIST has only published official test vectors for 64-bit tweaks,
but draft ACVP test vectors have been used for testing FF3-1. It is expected the final
NIST standard will provide updated test vectors with 56-bit tweak lengths.RequiresThis project was built and tested with Python 3.6 and later versions.  The only dependency isPyCryptodome.InstallationFor a normal installation of the latest PyPI release with pip:pip3 install ff3To instead install the development version:gitclonehttps://github.com/mysto/python-fpe.gitcdpython-fpe
pip3install--editable.Before contributing any pull requests, you will need to first fork this repository and change the remote origin to reflect your fork:gitremoteset-urlorigingit@github.com:YOUR-GITHUB-USERNAME/python-fpe.gitTo uninstall:pip3uninstallff3UsageFF3 is a Feistel cipher, and Feistel ciphers are initialized with a radix representing an alphabet. The number of
characters in an alphabet is called theradix.
The following radix values are typical:radix 10: digits 0..9radix 36: alphanumeric 0..9, a-zradix 62: alphanumeric 0..9, a-z, A-ZSpecial characters and international character sets, such as those found in UTF-8, are supported by specifying a custom alphabet.
Also, all elements in a plaintext string share the same radix. Thus, an identification number that consists of an initial letter followed
by 6 digits (e.g. A123456) cannot be correctly encrypted by FPE while preserving this convention.Input plaintext has maximum length restrictions based upon the chosen radix (2 * floor(96/log2(radix))):radix 10: 56radix 36: 36radix 62: 32To work around string length, its possible to encode longer text in chunks.As with any cryptographic package, managing and protecting the key(s) is crucial. The tweak is generally not kept secret.
This package does not store the key in memory after initializing the cipher.Code ExampleThe example code below uses the default domain [0-9] and can help you get started.fromff3importFF3Cipherkey="2DE79D232DF5585D68CE47882AE256D6"tweak="CBD09280979564"c=FF3Cipher(key,tweak)plaintext="3992520240"ciphertext=c.encrypt(plaintext)decrypted=c.decrypt(ciphertext)print(f"{plaintext}->{ciphertext}->{decrypted}")# format encrypted valueccn=f"{ciphertext[:4]}{ciphertext[4:8]}{ciphertext[8:12]}{ciphertext[12:]}"print(f"Encrypted CCN value with formatting:{ccn}")CLI ExampleThis package installs the command line scripts ff3_encrypt and ff3_decrypt which can be run
from the Linux or Windows command line.%ff3_encrypt2DE79D232DF5585D68CE47882AE256D6CBD0928097956439925202408901801106%ff3_decrypt2DE79D232DF5585D68CE47882AE256D6CBD0928097956489018011063992520240Custom alphabetsCustom alphabets up to 256 characters are supported. To use an alphabet consisting of the uppercase letters A-F (radix=6), we can continue
from the above code example with:c6=FF3Cipher.withCustomAlphabet(key,tweak,"ABCDEF")plaintext="BADDCAFE"ciphertext=c6.encrypt(plaintext)decrypted=c6.decrypt(ciphertext)print(f"{plaintext}->{ciphertext}->{decrypted}")TestingOfficialtest vectorsfor FF3 provided by NIST,
are used for testing in this package. Also included are draft ACVP test vectors with 56-bit tweaks.To run unit tests on this implementation, including all test vectors from the NIST specification, run the command:python3-mff3.ff3_testPerformance BenchmarksThe Mysto FF3 was benchmarked on a MacBook Air (1.1 GHz Quad-Core Intel Core i5)
performing 70,000 tokenization per second with random 8 character data input.To run the performance tests:python3-mff3.ff3_perfThe FF3 AlgorithmThe FF3 algorithm is a tweakable block cipher based on an eight round Feistel cipher. A block cipher operates on fixed-length groups of bits, called blocks. A Feistel Cipher is not a specific cipher,
but a design model.  This FF3 Feistel encryption consisting of eight rounds of processing
the plaintext. Each round applies an internal function orround function, followed by transformation steps.The FF3 round function uses AES encryption in ECB mode, which is performed each iteration
on alternating halves of the text being encrypted. Thekeyvalue is used only to initialize the AES cipher. Thereafter
thetweakis used together with the intermediate encrypted text as input to the round function.Other FPE AlgorithmsOnly FF1 and FF3 have been approved by NIST for format preserving encryption. There are patent claims on FF1 which allegedly include open source implementations. Given the issues raised in"The Curse of Small Domains: New Attacks on Format-Preserving Encryption"by Hoang, Tessaro and Trieu in 2018, it is prudent to be very cautious about using any FPE that isn't a standard and hasn't stood up to public scrutiny.Implementation NotesThis implementation was originally based upon theCapital One Go implementation.  It follows the algorithm as outlined in the NIST specification as closely as possible, including naming.FPE can be used for data tokenization of sensitive data which is cryptographically reversible. This implementation does not provide any guarantees regarding PCI DSS or other validation.While all NIST and ACVP test vectors pass, this package has not otherwise been extensively tested.The cryptographic library used isPyCryptodomefor AES encryption. FF3 uses a single-block with an IV of 0, which is effectively ECB mode. AES ECB is the only block cipher function which matches the requirement of the FF3 spec.The domain size was revised in FF3-1 to radixminLen>= 1,000,000 and is represented by the constantDOMAIN_MINinff3.py. FF3-1 is in draft status.The tweak is required in the initialFF3Cipherconstructor, but can optionally be overridden in eachencryptanddecryptcall. This is similar to passing an IV or nonce when creating an encrypter object.AuthorBrad SchoeningLicenseThis project is licensed under the terms of theApache 2.0 license.