The FFX Mode of Operation for Format-Preserving Encryption
Python
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
ffx
.gitignore
.travis.yml
LICENSE
README.md
aes-ffx-vectors.txt
benchmark.py
example.py
requirements.txt
setup.py

README.md

FFX

Build Status

This is a python implementation of The FFX Mode of Operation for Format-Preserving Encryption [1,2].

This implementation takes into consideration the addendum in [2]. This implementation has been tested to work with message sizes in {2,...,128} and radix values of {2,...,62}. It uses maximally-balanced Feistel with a constant of 10 rounds, independent of messages size, as per [2].

Example Usage

>>> import ffx
>>>
>>> K = ffx.FFXInteger('0'*128, radix=2, blocksize=128)
>>> T = ffx.FFXInteger('0'*8,   radix=2, blocksize=8)
>>> X = ffx.FFXInteger('0'*8,   radix=2, blocksize=8)
>>>
>>> ffxObj = ffx.new(K.to_bytes(16), radix=2)
>>>
>>> Y = ffxObj.encrypt(T, X)
>>> X1 = ffxObj.decrypt(T, Y)
>>>
>>> print X
00000000
>>> print Y
10100010
>>> print X1
00000000

Unit Tests / Test Vectors

We have our own unit tests. In addition, Voltage has provided test vectors, which we've used to validate our implementation: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ffx/aes-ffx-vectors.txt

$ python setup.py test
TEST VECTOR #1: radix=10, input=0123456789, tweak=9876543210, encrypted=6124200773
TEST VECTOR #2: radix=10, input=0123456789, tweak=0, encrypted=2433477484
TEST VECTOR #3: radix=10, input=314159, tweak=2718281828, encrypted=535005
TEST VECTOR #4: radix=10, input=999999999, tweak=7777777, encrypted=658229573
TEST VECTOR #5: radix=36, input=C4XPWULBM3M863JH, tweak=TQF9J5QDAGSCSPB1, encrypted=c8aq3u846zwh6qzp
----------------------------------------------------------------------
Ran 12 tests in 0.006s

OK

Benchmarks

$ python benchmark.py --radix 2 --tweaksize 8 --messagesize 8
RADIX=2, TWEAKSIZE=8, MESSAGESIZE=8, KEY=0x7fab9cfe5f0b2f4b61fc18fc018e1d66L
test #1 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.4ms, tweak=00011000, plaintext=11110110, ciphertext=00000101)
test #2 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.4ms, tweak=10101101, plaintext=01100001, ciphertext=01101001)
test #3 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.4ms, tweak=10111100, plaintext=10011111, ciphertext=11010111)
test #4 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.3ms, tweak=00010000, plaintext=11101011, ciphertext=11010110)
test #5 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.4ms, tweak=01100111, plaintext=11111101, ciphertext=01010100)
test #6 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.3ms, tweak=00010110, plaintext=01111001, ciphertext=01110101)
test #7 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.3ms, tweak=10000010, plaintext=00001110, ciphertext=10011101)
test #8 SUCCESS: (encrypt_cost=0.3ms, decrypt_cost=0.4ms, tweak=01001000, plaintext=11011111, ciphertext=11110110)
test #9 SUCCESS: (encrypt_cost=0.4ms, decrypt_cost=0.3ms, tweak=00110111, plaintext=01001101, ciphertext=10010110)
$ python benchmark.py --radix 16 --tweaksize 32 --messagesize 32
RADIX=16, TWEAKSIZE=32, MESSAGESIZE=32, KEY=0xa8751544df84f7140eaf36ffe3484cc4L
test #1 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=47043403a1e3d0eac42a22cd89a43afd, plaintext=245ad41b48838606173a85083717ef69, ciphertext=79aaa17eaf64fe2d7ecba00dac466898)
test #2 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=1c2d16e0a9e6776117b9cb5d8bcb27e2, plaintext=017c5d0daabe6504e201568bb87a241e, ciphertext=513b96b8ade2d315866a16f3784d141a)
test #3 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=9b3857a1f0cfa8cf2046b53447956af5, plaintext=df18342e3b7331d26b6c978a5dc82e27, ciphertext=a83571857193ec41d13583d935c869e9)
test #4 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=274a3c9963a994e25f7f1c12135f0632, plaintext=46fbade6a0e4e98676d52ce03f25b50b, ciphertext=83cfcda35e960c1af2d29b4d4ebdc915)
test #5 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=84efb2f650923859d42fa00a8c1382c0, plaintext=fc1ee6ec0ad9fc02a04a167904a25412, ciphertext=24430ca362114c7c484985c394afb68d)
test #6 SUCCESS: (encrypt_cost=0.6ms, decrypt_cost=0.5ms, tweak=3287ca9835f976eaaddff4029aeb6eac, plaintext=a12305da848fddd2a6b563a6a6510d6a, ciphertext=281b14ff14f954c8391801db948e4ff1)
test #7 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=4b969bd2495c36179d305b4e6cff8d3b, plaintext=015fb0ded9f8949a287dbb9f2f87f79a, ciphertext=07a45a4a7c7b0a0c78b9b5c936cf29a1)
test #8 SUCCESS: (encrypt_cost=0.5ms, decrypt_cost=0.5ms, tweak=69b55befdfbdacf7bf12e2cb057b723a, plaintext=8874128c934082f202f8963c4c0ee5e4, ciphertext=0e642513e36016bc670615529b06be15)
test #9 SUCCESS: (encrypt_cost=0.6ms, decrypt_cost=0.6ms, tweak=e9e74053084efa895f8a74e90349fc90, plaintext=0bc76a380dd83942db3dccb3ed4918dd, ciphertext=5786d6bd86642052786f89f3521ca68d)

Author

Kevin P. Dyer (kdyer@cs.pdx.edu)