Skip to main content

Format Preserving Encryption

This library provides Format Preserving Encryption (FPE) techniques for use in a zero-trust environment. These techniques are based on FPE-FF1 which is described in NIST:800-38G.

Format Preserving Encryption (FPE)

FPE aims to encrypt plaintext while retaining its format (alphabet). FPE-FF1 is a normalized algorithm that uses symmetric encryption, but it's not as fast or secure as standardized symmetric (or public key) encryption methods like AES or ChaCha. It should only be used where the format of the ciphertext container is constrained (e.g., a fixed database schema that cannot be changed).

Implementation

The FPE implementation follows NIST specifications for FF1 (found in the NIST SP 800-38G specification).

The code is based on the cosmian_fpe directory found on GitHub, which is based on str4d/fpe. The number of Feistel rounds has been increased to 18 following the recommendations of this cryptanalysis paper.

The implementation also enforces the requirement that radix^min_len > 1_000_000. For the Alphabet and Integer FPE facilities, this requirement is met with the following parameters:

radix example alphabet min text len
2 "01" 20
10 "01234567890" 6
16 "01234567890abcdef" 5

Using FPE

Cosmian FPE proposes 3 structures:

  • fpe::Alphabet to encrypt text
  • fpe::Integer to encrypt integers with various radixes
  • fpe::Float to encrypt floating numbers

Encrypting Text

The fpe::Alphabet structure provides the ability to encrypt a plaintext using an alphabet. Characters of the plaintext that belong to the alphabet are encrypted while the others are left unchanged at their original location in the ciphertext.

An alphabet can be instantiated using the Alphabet::instantiate() method:

let hexadecimal_alphabet = Alphabet::instantiate("01234567890abcdef").unwrap();

There are multiple pre-defined alphabets available:

  • Alphabet::alpha()
  • Alphabet::alpha_lower()
  • Alphabet::alpha_upper()
  • Alphabet::numeric()
  • Alphabet::hexa_decimal()
  • Alphabet::alpha_numeric()
  • Alphabet::chinese()
  • Alphabet::latin1sup()
  • Alphabet::latin1sup_alphanum()

These alphabets can easily be extended using the extend_with method

//0-9a-zA-Z
let mut alphabet = Alphabet::alphanumeric();
// add the space character
alphabet.extend_with(" ");
Encrypting and decrypting an alphanumeric text
let key = [0_u8; 32];
let tweak = b"unique tweak";

let alphabet = Alphabet::alpha_numeric(); //0-9a-zA-Z

let ciphertext = alphabet.encrypt(&key, tweak, "alphanumeric").unwrap();
assert_eq!("jraqSuFWZmdH", ciphertext);

let plaintext = alphabet.decrypt(&key, tweak, &ciphertext).unwrap();
assert_eq!("alphanumeric", plaintext);
Encrypting and decrypting a credit card number
let key = [0_u8; 32];
let tweak = b"unique tweak";

let alphabet = Alphabet::numeric(); //0-9

let ciphertext = alphabet
   .encrypt(&key, tweak, "1234-1234-1234-1234")
   .unwrap();
assert_eq!("1415-4650-5562-7272", ciphertext);

let plaintext = alphabet.decrypt(&key, tweak, &ciphertext).unwrap();
assert_eq!("1234-1234-1234-1234", plaintext);

Note: since the - character is not part of the alphabet it is preserved during encryption and decryption.

Encrypting and decrypting a Chinese text with spaces
let key = [0_u8; 32];
let tweak = b"unique tweak";

let mut alphabet = Alphabet::chinese();
// add the space character to the alphabet
alphabet.extend_with(" ");

let ciphertext = alphabet.encrypt(&key, tweak, "天地玄黄 宇宙洪荒").unwrap();
assert_eq!("儖濣鈍媺惐墷礿截媃", ciphertext);

let plaintext = alphabet.decrypt(&key, tweak, &ciphertext).unwrap();
assert_eq!("天地玄黄 宇宙洪荒", plaintext);

Note: since the space character was added to the alphabet, it is also encrypted.

Encrypting Integers

The fpe::Integer structure offers the ability to encrypt integers with a radix between 2 (binary) and 16 (hexadecimal) and up to a maximum power of this radix.

To encrypt decimal integers up to u64::MAX, use:

let key = [0_u8; 32];
let tweak = b"unique tweak";

// decimal number with digits 0-9
let radix = 10_u32;
// the number of digits of the biggest number = radix^digits -1
// In this case 6 decimal digits -> 999_999
let digits = 6;

let itg = Integer::instantiate(radix, digits).unwrap();

let ciphertext = itg.encrypt(&key, tweak, 123_456_u64).unwrap();
assert_eq!(110_655_u64, ciphertext);

let plaintext = itg.decrypt(&key, tweak, ciphertext).unwrap();
assert_eq!(123_456_u64, plaintext);

There is also support for Big Unsigned Integers

let key = [0_u8; 32];
let tweak = b"unique tweak";

// decimal number with digits 0-9
let radix = 10_u32;
// the number of digits of the greatest number = radix^digits -1 = 10^20-1
let digits = 20;

// the value to encrypt: 10^17
let value = BigUint::from_str_radix("100000000000000000", radix).unwrap();

let itg = Integer::instantiate(radix, digits).unwrap();

let ciphertext = itg.encrypt_big(&key, tweak, &value).unwrap();
assert_eq!(
   BigUint::from_str_radix("65348521845006160218", radix).unwrap(),
   ciphertext
);

let plaintext = itg.decrypt_big(&key, tweak, &ciphertext).unwrap();
assert_eq!(
   BigUint::from_str_radix("100000000000000000", radix).unwrap(),
   plaintext
);

Encrypting Floats

The fpe::Float structure provides support for encrypting floats of type f64:

let key = [0_u8; 32];
let tweak = b"unique tweak";

let flt = Float::instantiate().unwrap();
let ciphertext = flt.encrypt(&key, tweak, 123_456.789_f64).unwrap();
assert_eq!(1.170438892319619e91_f64, ciphertext);

let plaintext = flt.decrypt(&key, tweak, ciphertext).unwrap();
assert_eq!(123_456.789_f64, plaintext);

Tweaks

Tweaks are public parameters that should vary with each instance of the encryption whenever possible. Tweaks are described in NIST:800-38G: Appendix C. There is no size limit for the tweak.

Benchmarks

Run quick start

Run cargo criterion from the current directory (./crates/fpe).

Run detailed report (Linux, MacOS)

  1. Install criterion and criterion-table

    cargo install cargo-criterion
    cargo install criterion-table
    
  2. From the root of the project, run

    bash ./benches/benches.sh
    
  3. The benchmarks are then available in ./benches/BENCHMARKS.md

Metadata

Release files for cloudproof-fpe 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for cloudproof-fpe 0.2.2
File Interpreter ABI Platform
cloudproof_fpe-0.2.2-cp37-abi3-win_amd64.whl CPython 3.7 abi3 Windows x86-64 Details
cloudproof_fpe-0.2.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.7 abi3 Linux glibc 2.17+ x86-64 Details
cloudproof_fpe-0.2.2-cp37-abi3-macosx_10_12_x86_64.whl CPython 3.7 abi3 macOS 10.12+ x86-64 Details

Total release size: 880.7 kB

Release files / cloudproof_fpe-0.2.2-cp37-abi3-win_amd64.whl

Download URL cloudproof_fpe-0.2.2-cp37-abi3-win_amd64.whl
Size 283.8 kB
Tags CPython 3.7 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
65aeb5dda2ecfd34845f0c52f4f845896742f7238c7560b15029f6a8018ba458
BLAKE2b-256 checksum
How to use checksums
cb9c15b9b0cb9c6ae9852b4ec5fa6cf3836197d83b5257bea6c5512ce977bf03
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.7.17

Release files / cloudproof_fpe-0.2.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL cloudproof_fpe-0.2.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 310.2 kB
Tags CPython 3.7 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
2dfae7c2b9529758f86fd2538fc2fe1e545e6fb1afa125d0ff076f2a157f99d1
BLAKE2b-256 checksum
How to use checksums
c01d77ea5348eae387e742cc3205d3c2ea30de73d8283d522ca7885c3bd704cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.7.17

Release files / cloudproof_fpe-0.2.2-cp37-abi3-macosx_10_12_x86_64.whl

Download URL cloudproof_fpe-0.2.2-cp37-abi3-macosx_10_12_x86_64.whl
Size 286.7 kB
Tags CPython 3.7 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
4d148bf452a0c66610c734bfef28dd51a24aec99797692987fd5e6501ee228aa
BLAKE2b-256 checksum
How to use checksums
dc84a175348e0b7ccdb976363f4c2d5a49f2cfc7d186726aae8dc093f37913ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.7.17

Security for cloudproof-fpe 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page