Botan 3.13.0
Crypto and TLS for C&
fpe_fe1.cpp
Go to the documentation of this file.
1/*
2* Format Preserving Encryption (FE1 scheme)
3* (C) 2009,2018 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/fpe_fe1.h>
9
10#include <botan/exceptn.h>
11#include <botan/mac.h>
12#include <botan/numthry.h>
13#include <botan/internal/divide.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/loadstor.h>
16
17namespace Botan {
18
19namespace {
20
21// Normally FPE is for SSNs, CC#s, etc, nothing too big
22const size_t MAX_N_BYTES = 128 / 8;
23
24/*
25* Factor n into a and b which are as close together as possible.
26* Assumes n is composed mostly of small factors which is the case for
27* typical uses of FPE (typically, n is a power of 10)
28*/
29void factor(BigInt n, BigInt& a, BigInt& b) {
30 BOTAN_ARG_CHECK(n >= 2, "Invalid FPE modulus");
31
32 a = BigInt::one();
33 b = BigInt::one();
34
35 /*
36 * This algorithm was poorly designed. It should have fully factored n (to the
37 * extent possible) and then built a/b starting from the largest factor first.
38 *
39 * This can't be fixed now without breaking existing users but if some
40 * incompatible change (or new flag, etc) is added in the future, consider
41 * fixing the factoring for those users.
42 */
43
44 const size_t n_low_zero = low_zero_bits(n);
45
46 a <<= (n_low_zero / 2);
47 b <<= n_low_zero - (n_low_zero / 2);
48 n >>= n_low_zero;
49
50 for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i) {
51 while(n % PRIMES[i] == 0) {
52 a *= PRIMES[i];
53 if(a > b) {
54 std::swap(a, b);
55 }
57 }
58 }
59
60 if(a > b) {
61 std::swap(a, b);
62 }
63 a *= n;
64
65 if(a <= 1 || b <= 1) {
66 throw Invalid_Argument("FPE_FE1 modulus must be composite with at least one prime factor under 65521");
67 }
68}
69
70} // namespace
71
72FPE_FE1::FPE_FE1(const BigInt& n, size_t rounds, bool compat_mode, std::string_view mac_algo) :
73 m_n(n), m_rounds(rounds) {
74 if(m_rounds < 3) {
75 throw Invalid_Argument("FPE_FE1 rounds too small");
76 }
77
79
80 m_n_bytes = m_n.serialize();
81
82 if(m_n_bytes.size() > MAX_N_BYTES) {
83 throw Invalid_Argument("N is too large for FPE encryption");
84 }
85
86 factor(m_n, m_a, m_b);
87
88 if(compat_mode) {
89 if(m_a < m_b) {
90 std::swap(m_a, m_b);
91 }
92 } else {
93 if(m_a > m_b) {
94 std::swap(m_a, m_b);
95 }
96 }
97}
98
99FPE_FE1::FPE_FE1(FPE_FE1&& other) noexcept = default;
100
101FPE_FE1::~FPE_FE1() = default;
102
104 m_mac->clear();
105}
106
107std::string FPE_FE1::name() const {
108 return fmt("FPE_FE1({},{})", m_mac->name(), m_rounds);
109}
110
112 return m_mac->key_spec();
113}
114
116 return m_mac->has_keying_material();
117}
118
119void FPE_FE1::key_schedule(std::span<const uint8_t> key) {
120 m_mac->set_key(key);
121}
122
123BigInt FPE_FE1::F(const BigInt& R,
124 size_t round,
125 const secure_vector<uint8_t>& tweak_mac,
126 secure_vector<uint8_t>& tmp) const {
127 tmp = R.serialize<secure_vector<uint8_t>>();
128
129 m_mac->update(tweak_mac);
130 m_mac->update_be(static_cast<uint32_t>(round));
131
132 m_mac->update_be(static_cast<uint32_t>(tmp.size()));
133 m_mac->update(tmp.data(), tmp.size());
134
135 tmp = m_mac->final();
136 return BigInt::from_bytes(tmp);
137}
138
139secure_vector<uint8_t> FPE_FE1::compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const {
140 m_mac->update_be(static_cast<uint32_t>(m_n_bytes.size()));
141 m_mac->update(m_n_bytes.data(), m_n_bytes.size());
142
143 m_mac->update_be(static_cast<uint32_t>(tweak_len));
144 if(tweak_len > 0) {
145 m_mac->update(tweak, tweak_len);
146 }
147
148 return m_mac->final();
149}
150
151BigInt FPE_FE1::encrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const {
152 BOTAN_ARG_CHECK(input.signum() >= 0 && input < m_n, "Invalid FPE_FE1 input");
153
154 const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
155
156 BigInt X = input;
157
159
160 BigInt L;
161 BigInt R;
162 BigInt Fi;
163 for(size_t i = 0; i != m_rounds; ++i) {
164 ct_divide(X, m_b, L, R);
165 Fi = F(R, i, tweak_mac, tmp);
166 X = m_a * R + ct_modulo(L + Fi, m_a);
167 }
168
169 return X;
170}
171
172BigInt FPE_FE1::decrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const {
173 BOTAN_ARG_CHECK(input.signum() >= 0 && input < m_n, "Invalid FPE_FE1 input");
174
175 const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
176
177 BigInt X = input;
179
180 BigInt W;
181 BigInt R;
182 BigInt Fi;
183 for(size_t i = 0; i != m_rounds; ++i) {
184 ct_divide(X, m_a, R, W);
185
186 Fi = F(R, m_rounds - i - 1, tweak_mac, tmp);
187 X = m_b * ct_modulo(W - Fi, m_a) + R;
188 }
189
190 return X;
191}
192
193BigInt FPE_FE1::encrypt(const BigInt& x, uint64_t tweak) const {
194 uint8_t tweak8[8];
195 store_be(tweak, tweak8);
196 return encrypt(x, tweak8, sizeof(tweak8));
197}
198
199BigInt FPE_FE1::decrypt(const BigInt& x, uint64_t tweak) const {
200 uint8_t tweak8[8];
201 store_be(tweak, tweak8);
202 return decrypt(x, tweak8, sizeof(tweak8));
203}
204
205namespace FPE {
206
207BigInt fe1_encrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector<uint8_t>& tweak) {
208 FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
209 fpe.set_key(key);
210 return fpe.encrypt(X, tweak.data(), tweak.size());
211}
212
213BigInt fe1_decrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector<uint8_t>& tweak) {
214 FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
215 fpe.set_key(key);
216 return fpe.decrypt(X, tweak.data(), tweak.size());
217}
218
219} // namespace FPE
220
221} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
const word * data() const
Definition bigint.h:718
int signum() const
Definition bigint.h:493
static BigInt one()
Definition bigint.h:55
static BigInt from_bytes(std::span< const uint8_t > bytes)
Definition bigint.cpp:83
static BigInt from_word(word n)
Definition bigint.cpp:35
BigInt encrypt(const BigInt &x, const uint8_t tweak[], size_t tweak_len) const
Definition fpe_fe1.cpp:151
void clear() override
Definition fpe_fe1.cpp:103
std::string name() const override
Definition fpe_fe1.cpp:107
BOTAN_FUTURE_EXPLICIT FPE_FE1(const BigInt &n, size_t rounds=5, bool compat_mode=false, std::string_view mac_algo="HMAC(SHA-256)")
Definition fpe_fe1.cpp:72
bool has_keying_material() const override
Definition fpe_fe1.cpp:115
~FPE_FE1() override
Key_Length_Specification key_spec() const override
Definition fpe_fe1.cpp:111
BigInt decrypt(const BigInt &x, const uint8_t tweak[], size_t tweak_len) const
Definition fpe_fe1.cpp:172
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition mac.cpp:149
void set_key(const OctetString &key)
Definition sym_algo.cpp:14
BigInt fe1_decrypt(const BigInt &n, const BigInt &X, const SymmetricKey &key, const std::vector< uint8_t > &tweak)
Definition fpe_fe1.cpp:213
BigInt fe1_encrypt(const BigInt &n, const BigInt &X, const SymmetricKey &key, const std::vector< uint8_t > &tweak)
Definition fpe_fe1.cpp:207
OctetString SymmetricKey
Definition symkey.h:153
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
const uint16_t PRIMES[]
Definition primes.cpp:12
size_t low_zero_bits(const BigInt &n)
Definition numthry.cpp:194
const size_t PRIME_TABLE_SIZE
Definition numthry.h:177
BigInt ct_modulo(const BigInt &x, const BigInt &y)
Definition divide.cpp:198
void ct_divide(const BigInt &x, const BigInt &y, BigInt &q_out, BigInt &r_out)
Definition divide.cpp:55
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745