Package: cloudproof-fpe
Format Preserving EncryptionThis library providesFormat Preserving Encryption(FPE) techniques for use in a zero-trust environment. These techniques are based on FPE-FF1 which is described inNIST:800-38G.Format Preserving Encryption (FPE)ImplementationUsing FPEEncrypting TextEncrypting and decrypting an alphanumeric textEncrypting and decrypting a credit card numberEncrypting and decrypting a Chinese text with spacesEncrypting IntegersEncrypting FloatsTweaksBenchmarksRun quick startRun detailed report (Linux, MacOS)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).ImplementationThe FPE implementation follows NIST specifications for FF1 (found in theNIST SP 800-38G specification).The code is based on thecosmian_fpedirectory found onGitHub, which is based onstr4d/fpe. The number of Feistel rounds has been increased to 18 following the recommendations of thiscryptanalysis paper.The implementation also enforces the requirement thatradix^min_len > 1_000_000. For theAlphabetandIntegerFPE facilities, this requirement is met with the following parameters:radixexample alphabetmin text len2"01"2010"01234567890"616"01234567890abcdef"5Using FPECosmian FPE proposes 3 structures:fpe::Alphabetto encrypt textfpe::Integerto encrypt integers with various radixesfpe::Floatto encrypt floating numbersEncrypting TextThefpe::Alphabetstructure provides the ability to encrypt a plaintext using analphabet.
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 theAlphabet::instantiate()method:lethexadecimal_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 theextend_withmethod//0-9a-zA-Zletmutalphabet=Alphabet::alphanumeric();// add the space characteralphabet.extend_with(" ");Encrypting and decrypting an alphanumeric textletkey=[0_u8;32];lettweak=b"unique tweak";letalphabet=Alphabet::alpha_numeric();//0-9a-zA-Zletciphertext=alphabet.encrypt(&key,tweak,"alphanumeric").unwrap();assert_eq!("jraqSuFWZmdH",ciphertext);letplaintext=alphabet.decrypt(&key,tweak,&ciphertext).unwrap();assert_eq!("alphanumeric",plaintext);Encrypting and decrypting a credit card numberletkey=[0_u8;32];lettweak=b"unique tweak";letalphabet=Alphabet::numeric();//0-9letciphertext=alphabet.encrypt(&key,tweak,"1234-1234-1234-1234").unwrap();assert_eq!("1415-4650-5562-7272",ciphertext);letplaintext=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 spacesletkey=[0_u8;32];lettweak=b"unique tweak";letmutalphabet=Alphabet::chinese();// add the space character to the alphabetalphabet.extend_with(" ");letciphertext=alphabet.encrypt(&key,tweak,"天地玄黄 宇宙洪荒").unwrap();assert_eq!("儖濣鈍媺惐墷礿截媃",ciphertext);letplaintext=alphabet.decrypt(&key,tweak,&ciphertext).unwrap();assert_eq!("天地玄黄 宇宙洪荒",plaintext);Note: since the space character was added to the alphabet, it is also encrypted.Encrypting IntegersThefpe::Integerstructure 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:letkey=[0_u8;32];lettweak=b"unique tweak";// decimal number with digits 0-9letradix=10_u32;// the number of digits of the biggest number = radix^digits -1// In this case 6 decimal digits -> 999_999letdigits=6;letitg=Integer::instantiate(radix,digits).unwrap();letciphertext=itg.encrypt(&key,tweak,123_456_u64).unwrap();assert_eq!(110_655_u64,ciphertext);letplaintext=itg.decrypt(&key,tweak,ciphertext).unwrap();assert_eq!(123_456_u64,plaintext);There is also support for Big Unsigned Integersletkey=[0_u8;32];lettweak=b"unique tweak";// decimal number with digits 0-9letradix=10_u32;// the number of digits of the greatest number = radix^digits -1 = 10^20-1letdigits=20;// the value to encrypt: 10^17letvalue=BigUint::from_str_radix("100000000000000000",radix).unwrap();letitg=Integer::instantiate(radix,digits).unwrap();letciphertext=itg.encrypt_big(&key,tweak,&value).unwrap();assert_eq!(BigUint::from_str_radix("65348521845006160218",radix).unwrap(),ciphertext);letplaintext=itg.decrypt_big(&key,tweak,&ciphertext).unwrap();assert_eq!(BigUint::from_str_radix("100000000000000000",radix).unwrap(),plaintext);Encrypting FloatsThefpe::Floatstructure provides support for encrypting floats of typef64:letkey=[0_u8;32];lettweak=b"unique tweak";letflt=Float::instantiate().unwrap();letciphertext=flt.encrypt(&key,tweak,123_456.789_f64).unwrap();assert_eq!(1.170438892319619e91_f64,ciphertext);letplaintext=flt.decrypt(&key,tweak,ciphertext).unwrap();assert_eq!(123_456.789_f64,plaintext);TweaksTweaksare public parameters that should vary with each instance of the encryption whenever possible.Tweaksare described inNIST:800-38G: Appendix C. There is no size limit for thetweak.BenchmarksRun quick startRuncargo criterionfrom the current directory (./crates/fpe).Run detailed report (Linux, MacOS)Install criterion and criterion-tablecargoinstallcargo-criterion
cargoinstallcriterion-tableFrom the root of the project, runbash./benches/benches.shThe benchmarks are then available in./benches/BENCHMARKS.md