aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2018-02-28 13:51:54 -0500
committerJack Lloyd <[email protected]>2018-02-28 15:03:50 -0500
commitd7ee63924da94fe7e46af7012cde971ef7588732 (patch)
tree4ac666072b75f9f46474e491142e4128c422a50b /src
parent3870a2a59a9940635a133fbe60ab05c9815a4d1c (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.cpp7
-rw-r--r--src/lib/misc/fpe_fe1/fpe_fe1.cpp196
-rw-r--r--src/lib/misc/fpe_fe1/fpe_fe1.h52
-rw-r--r--src/tests/data/fpe_fe1.vec12
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