diff options
author | Jack Lloyd <[email protected]> | 2018-02-28 13:51:54 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-02-28 15:03:50 -0500 |
commit | d7ee63924da94fe7e46af7012cde971ef7588732 (patch) | |
tree | 4ac666072b75f9f46474e491142e4128c422a50b /src | |
parent | 3870a2a59a9940635a133fbe60ab05c9815a4d1c (diff) |
Optimize FE1 format preserving encryption
Expose the state as the FPE_FE1 class which allows most values
to be precomputed. Approx 6-8 times faster.
Diffstat (limited to 'src')
-rw-r--r-- | src/cli/speed.cpp | 7 | ||||
-rw-r--r-- | src/lib/misc/fpe_fe1/fpe_fe1.cpp | 196 | ||||
-rw-r--r-- | src/lib/misc/fpe_fe1/fpe_fe1.h | 52 | ||||
-rw-r--r-- | src/tests/data/fpe_fe1.vec | 12 |
4 files changed, 173 insertions, 94 deletions
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 1e8c3135f..b29c8b9e9 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -1290,17 +1290,20 @@ class Speed final : public Command Botan::BigInt x = 1; + Botan::FPE_FE1 fpe_fe1(n, 3, "HMAC(SHA-256)"); + fpe_fe1.set_key(key); + while(enc_timer.under(runtime)) { enc_timer.start(); - x = Botan::FPE::fe1_encrypt(n, x, key, tweak); + x = fpe_fe1.encrypt(x, tweak.data(), tweak.size()); enc_timer.stop(); } for(size_t i = 0; i != enc_timer.events(); ++i) { dec_timer.start(); - x = Botan::FPE::fe1_decrypt(n, x, key, tweak); + x = fpe_fe1.decrypt(x, tweak.data(), tweak.size()); dec_timer.stop(); } diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.cpp b/src/lib/misc/fpe_fe1/fpe_fe1.cpp index 04503125c..7166e480f 100644 --- a/src/lib/misc/fpe_fe1/fpe_fe1.cpp +++ b/src/lib/misc/fpe_fe1/fpe_fe1.cpp @@ -1,18 +1,17 @@ /* * Format Preserving Encryption (FE1 scheme) -* (C) 2009 Jack Lloyd +* (C) 2009,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include <botan/fpe_fe1.h> #include <botan/numthry.h> +#include <botan/divide.h> #include <botan/mac.h> namespace Botan { -namespace FPE { - namespace { // Normally FPE is for SSNs, CC#s, etc, nothing too big @@ -58,129 +57,154 @@ void factor(BigInt n, BigInt& a, BigInt& b) throw Exception("Could not factor n for use in FPE"); } -/* -* According to a paper by Rogaway, Bellare, etc, the min safe number -* of rounds to use for FPE is 2+log_a(b). If a >= b then log_a(b) <= 1 -* so 3 rounds is safe. The FPE factorization routine should always -* return a >= b, so just confirm that and return 3. -*/ -size_t rounds(const BigInt& a, const BigInt& b) +} + +FPE_FE1::FPE_FE1(const BigInt& n, size_t rounds, const std::string& mac_algo) : + m_rounds(rounds) { - if(a < b) + m_mac = MessageAuthenticationCode::create_or_throw(mac_algo); + + m_n_bytes = BigInt::encode(n); + + if(m_n_bytes.size() > MAX_N_BYTES) + throw Exception("N is too large for FPE encryption"); + + factor(n, m_a, m_b); + + mod_a = Modular_Reducer(m_a); + + /* + * According to a paper by Rogaway, Bellare, etc, the min safe number + * of rounds to use for FPE is 2+log_a(b). If a >= b then log_a(b) <= 1 + * so 3 rounds is safe. The FPE factorization routine should always + * return a >= b, so just confirm that and return 3. + */ + if(m_a < m_b) throw Internal_Error("FPE rounds: a < b"); - return 3; + + if(m_rounds < 3) + throw Invalid_Argument("FPE_FE1 rounds too small"); } -/* -* A simple round function based on HMAC(SHA-256) -*/ -class FPE_Encryptor final +void FPE_FE1::clear() { - public: - FPE_Encryptor(const SymmetricKey& key, - const BigInt& n, - const std::vector<uint8_t>& tweak); - - BigInt operator()(size_t i, const BigInt& R); + m_mac->clear(); + } - private: - std::unique_ptr<MessageAuthenticationCode> m_mac; - std::vector<uint8_t> m_mac_n_t; - }; +std::string FPE_FE1::name() const + { + return "FPE_FE1(" + m_mac->name() + "," + std::to_string(m_rounds) + ")"; + } -FPE_Encryptor::FPE_Encryptor(const SymmetricKey& key, - const BigInt& n, - const std::vector<uint8_t>& tweak) +Key_Length_Specification FPE_FE1::key_spec() const { - m_mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); - m_mac->set_key(key); + return m_mac->key_spec(); + } - std::vector<uint8_t> n_bin = BigInt::encode(n); +void FPE_FE1::key_schedule(const uint8_t key[], size_t length) + { + m_mac->set_key(key, length); + } - if(n_bin.size() > MAX_N_BYTES) - throw Exception("N is too large for FPE encryption"); +BigInt FPE_FE1::F(const BigInt& R, size_t round, + const secure_vector<uint8_t>& tweak_mac, + secure_vector<uint8_t>& tmp) const + { + tmp = BigInt::encode_locked(R); - m_mac->update_be(static_cast<uint32_t>(n_bin.size())); - m_mac->update(n_bin.data(), n_bin.size()); + m_mac->update(tweak_mac); + m_mac->update_be(static_cast<uint32_t>(round)); - m_mac->update_be(static_cast<uint32_t>(tweak.size())); - m_mac->update(tweak.data(), tweak.size()); + m_mac->update_be(static_cast<uint32_t>(tmp.size())); + m_mac->update(tmp.data(), tmp.size()); - m_mac_n_t = unlock(m_mac->final()); + tmp = m_mac->final(); + return BigInt(tmp.data(), tmp.size()); } -BigInt FPE_Encryptor::operator()(size_t round_no, const BigInt& R) +secure_vector<uint8_t> FPE_FE1::compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const { - secure_vector<uint8_t> r_bin = BigInt::encode_locked(R); - - m_mac->update(m_mac_n_t); - m_mac->update_be(static_cast<uint32_t>(round_no)); + m_mac->update_be(static_cast<uint32_t>(m_n_bytes.size())); + m_mac->update(m_n_bytes.data(), m_n_bytes.size()); - m_mac->update_be(static_cast<uint32_t>(r_bin.size())); - m_mac->update(r_bin.data(), r_bin.size()); + m_mac->update_be(static_cast<uint32_t>(tweak_len)); + m_mac->update(tweak, tweak_len); - secure_vector<uint8_t> X = m_mac->final(); - return BigInt(X.data(), X.size()); + return m_mac->final(); } -} - -/* -* Generic Z_n FPE encryption, FE1 scheme -*/ -BigInt fe1_encrypt(const BigInt& n, const BigInt& X0, - const SymmetricKey& key, - const std::vector<uint8_t>& tweak) +BigInt FPE_FE1::encrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const { - FPE_Encryptor F(key, n, tweak); - - BigInt a, b; - factor(n, a, b); + const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len); - const size_t r = rounds(a, b); + BigInt X = input; - BigInt X = X0; + secure_vector<uint8_t> tmp; - for(size_t i = 0; i != r; ++i) + BigInt L, R, Fi; + for(size_t i = 0; i != m_rounds; ++i) { - BigInt L = X / b; - BigInt R = X % b; - - BigInt W = (L + F(i, R)) % a; - X = a * R + W; + divide(X, m_b, L, R); + Fi = F(R, i, tweak_mac, tmp); + X = m_a * R + mod_a.reduce(L + Fi); } return X; } -/* -* Generic Z_n FPE decryption, FD1 scheme -*/ -BigInt fe1_decrypt(const BigInt& n, const BigInt& X0, - const SymmetricKey& key, - const std::vector<uint8_t>& tweak) +BigInt FPE_FE1::decrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const { - FPE_Encryptor F(key, n, tweak); - - BigInt a, b; - factor(n, a, b); + const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len); - const size_t r = rounds(a, b); + BigInt X = input; + secure_vector<uint8_t> tmp; - BigInt X = X0; - - for(size_t i = 0; i != r; ++i) + BigInt W, R, Fi; + for(size_t i = 0; i != m_rounds; ++i) { - BigInt W = X % a; - BigInt R = X / a; + divide(X, m_a, R, W); - BigInt L = (W - F(r-i-1, R)) % a; - X = b * L + R; + Fi = F(R, m_rounds-i-1, tweak_mac, tmp); + X = m_b * mod_a.reduce(W - Fi) + R; } return X; } +BigInt FPE_FE1::encrypt(const BigInt& x, uint64_t tweak) const + { + uint8_t tweak8[8]; + store_be(tweak, tweak8); + return encrypt(x, tweak8, sizeof(tweak8)); + } + +BigInt FPE_FE1::decrypt(const BigInt& x, uint64_t tweak) const + { + uint8_t tweak8[8]; + store_be(tweak, tweak8); + return decrypt(x, tweak8, sizeof(tweak8)); + } + +namespace FPE { + +BigInt fe1_encrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const std::vector<uint8_t>& tweak) + { + FPE_FE1 fpe(n, 3, "HMAC(SHA-256)"); + fpe.set_key(key); + return fpe.encrypt(X, tweak.data(), tweak.size()); + } + +BigInt fe1_decrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const std::vector<uint8_t>& tweak) + { + FPE_FE1 fpe(n, 3, "HMAC(SHA-256)"); + fpe.set_key(key); + return fpe.decrypt(X, tweak.data(), tweak.size()); + } + } } diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.h b/src/lib/misc/fpe_fe1/fpe_fe1.h index 7f92f0601..a38231aa6 100644 --- a/src/lib/misc/fpe_fe1/fpe_fe1.h +++ b/src/lib/misc/fpe_fe1/fpe_fe1.h @@ -1,6 +1,6 @@ /* * Format Preserving Encryption (FE1 scheme) -* (C) 2009 Jack Lloyd +* (C) 2009,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,11 +8,51 @@ #ifndef BOTAN_FPE_FE1_H_ #define BOTAN_FPE_FE1_H_ +#include <botan/sym_algo.h> #include <botan/bigint.h> -#include <botan/symkey.h> +#include <botan/reducer.h> namespace Botan { +class MessageAuthenticationCode; + +class BOTAN_PUBLIC_API(2,5) FPE_FE1 final : public SymmetricAlgorithm + { + public: + FPE_FE1(const BigInt& n, + size_t rounds = 3, + const std::string& mac_algo = "HMAC(SHA-256)"); + + Key_Length_Specification key_spec() const override; + + std::string name() const; + + void clear(); + + BigInt encrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const; + + BigInt decrypt(const BigInt& x, const uint8_t tweak[], size_t tweak_len) const; + + BigInt encrypt(const BigInt& x, uint64_t tweak) const; + + BigInt decrypt(const BigInt& x, uint64_t tweak) const; + private: + void key_schedule(const uint8_t key[], size_t length) override; + + BigInt F(const BigInt& R, size_t round, + const secure_vector<uint8_t>& tweak, + secure_vector<uint8_t>& tmp) const; + + secure_vector<uint8_t> compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const; + + std::unique_ptr<MessageAuthenticationCode> m_mac; + std::vector<uint8_t> m_n_bytes; + BigInt m_a; + BigInt m_b; + Modular_Reducer mod_a; + size_t m_rounds; + }; + namespace FPE { /** @@ -27,8 +67,8 @@ namespace FPE { * @param tweak will modify the ciphertext (think of as an IV) */ BigInt BOTAN_PUBLIC_API(2,0) fe1_encrypt(const BigInt& n, const BigInt& X, - const SymmetricKey& key, - const std::vector<uint8_t>& tweak); + const SymmetricKey& key, + const std::vector<uint8_t>& tweak); /** * Decrypt X from and onto the group Z_n using key and tweak @@ -38,8 +78,8 @@ BigInt BOTAN_PUBLIC_API(2,0) fe1_encrypt(const BigInt& n, const BigInt& X, * @param tweak the same tweak used for encryption */ BigInt BOTAN_PUBLIC_API(2,0) fe1_decrypt(const BigInt& n, const BigInt& X, - const SymmetricKey& key, - const std::vector<uint8_t>& tweak); + const SymmetricKey& key, + const std::vector<uint8_t>& tweak); } diff --git a/src/tests/data/fpe_fe1.vec b/src/tests/data/fpe_fe1.vec index 4108a56a2..ba6720744 100644 --- a/src/tests/data/fpe_fe1.vec +++ b/src/tests/data/fpe_fe1.vec @@ -12,3 +12,15 @@ In = 48166 Out = 69575 Key = AABB Tweak = CCDD + +Mod = 1000000000 +In = 8765309 +Out = 935673214 +Key = 00112233445566778899AABBCCDDEEFF +Tweak = 0123456789 + +Mod = 100000000000 +In = 8765309 +Out = 1082083628 +Key = 00112233445566778899AABBCCDDEEFF +Tweak = 0123456789 |