• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 21768358452

06 Feb 2026 10:35PM UTC coverage: 90.064% (-0.003%) from 90.067%
21768358452

Pull #5289

github

web-flow
Merge f589db195 into 8ea0ca252
Pull Request #5289: Further misc header reductions, forward declarations, etc

102238 of 113517 relevant lines covered (90.06%)

11357432.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.37
/src/lib/misc/fpe_fe1/fpe_fe1.cpp
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

17
namespace Botan {
18

19
namespace {
20

21
// Normally FPE is for SSNs, CC#s, etc, nothing too big
22
const 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
*/
29
void factor(BigInt n, BigInt& a, BigInt& b) {
14✔
30
   BOTAN_ARG_CHECK(n >= 2, "Invalid FPE modulus");
14✔
31

32
   a = BigInt::one();
14✔
33
   b = BigInt::one();
14✔
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);
14✔
45

46
   a <<= (n_low_zero / 2);
14✔
47
   b <<= n_low_zero - (n_low_zero / 2);
14✔
48
   n >>= n_low_zero;
14✔
49

50
   for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i) {
91,588✔
51
      while(n % PRIMES[i] == 0) {
91,700✔
52
         a *= PRIMES[i];
126✔
53
         if(a > b) {
126✔
54
            std::swap(a, b);
126✔
55
         }
56
         n /= BigInt::from_word(PRIMES[i]);
126✔
57
      }
58
   }
59

60
   if(a > b) {
14✔
61
      std::swap(a, b);
×
62
   }
63
   a *= n;
14✔
64

65
   if(a <= 1 || b <= 1) {
28✔
66
      throw Internal_Error("Could not factor n for use in FPE");
×
67
   }
68
}
14✔
69

70
}  // namespace
71

72
FPE_FE1::FPE_FE1(const BigInt& n, size_t rounds, bool compat_mode, std::string_view mac_algo) : m_rounds(rounds) {
14✔
73
   if(m_rounds < 3) {
14✔
74
      throw Invalid_Argument("FPE_FE1 rounds too small");
×
75
   }
76

77
   m_mac = MessageAuthenticationCode::create_or_throw(mac_algo);
14✔
78

79
   m_n_bytes = n.serialize();
28✔
80

81
   if(m_n_bytes.size() > MAX_N_BYTES) {
14✔
82
      throw Invalid_Argument("N is too large for FPE encryption");
×
83
   }
84

85
   factor(n, m_a, m_b);
14✔
86

87
   if(compat_mode) {
14✔
88
      if(m_a < m_b) {
12✔
89
         std::swap(m_a, m_b);
12✔
90
      }
91
   } else {
92
      if(m_a > m_b) {
2✔
93
         std::swap(m_a, m_b);
×
94
      }
95
   }
96
}
14✔
97

98
FPE_FE1::FPE_FE1(FPE_FE1&& other) noexcept = default;
×
99

100
FPE_FE1::~FPE_FE1() = default;
30✔
101

102
void FPE_FE1::clear() {
×
103
   m_mac->clear();
×
104
}
×
105

106
std::string FPE_FE1::name() const {
×
107
   return fmt("FPE_FE1({},{})", m_mac->name(), m_rounds);
×
108
}
109

110
Key_Length_Specification FPE_FE1::key_spec() const {
14✔
111
   return m_mac->key_spec();
14✔
112
}
113

114
bool FPE_FE1::has_keying_material() const {
×
115
   return m_mac->has_keying_material();
×
116
}
117

118
void FPE_FE1::key_schedule(std::span<const uint8_t> key) {
14✔
119
   m_mac->set_key(key);
14✔
120
}
14✔
121

122
BigInt FPE_FE1::F(const BigInt& R,
66✔
123
                  size_t round,
124
                  const secure_vector<uint8_t>& tweak_mac,
125
                  secure_vector<uint8_t>& tmp) const {
126
   tmp = R.serialize<secure_vector<uint8_t>>();
132✔
127

128
   m_mac->update(tweak_mac);
66✔
129
   m_mac->update_be(static_cast<uint32_t>(round));
66✔
130

131
   m_mac->update_be(static_cast<uint32_t>(tmp.size()));
66✔
132
   m_mac->update(tmp.data(), tmp.size());
66✔
133

134
   tmp = m_mac->final();
132✔
135
   return BigInt::from_bytes(tmp);
66✔
136
}
137

138
secure_vector<uint8_t> FPE_FE1::compute_tweak_mac(const uint8_t tweak[], size_t tweak_len) const {
18✔
139
   m_mac->update_be(static_cast<uint32_t>(m_n_bytes.size()));
18✔
140
   m_mac->update(m_n_bytes.data(), m_n_bytes.size());
18✔
141

142
   m_mac->update_be(static_cast<uint32_t>(tweak_len));
18✔
143
   if(tweak_len > 0) {
18✔
144
      m_mac->update(tweak, tweak_len);
12✔
145
   }
146

147
   return m_mac->final();
18✔
148
}
149

150
BigInt FPE_FE1::encrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const {
9✔
151
   const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
9✔
152

153
   BigInt X = input;
9✔
154

155
   secure_vector<uint8_t> tmp;
9✔
156

157
   BigInt L;
9✔
158
   BigInt R;
9✔
159
   BigInt Fi;
9✔
160
   for(size_t i = 0; i != m_rounds; ++i) {
42✔
161
      ct_divide(X, m_b, L, R);
33✔
162
      Fi = F(R, i, tweak_mac, tmp);
33✔
163
      X = m_a * R + ct_modulo(L + Fi, m_a);
33✔
164
   }
165

166
   return X;
18✔
167
}
27✔
168

169
BigInt FPE_FE1::decrypt(const BigInt& input, const uint8_t tweak[], size_t tweak_len) const {
9✔
170
   const secure_vector<uint8_t> tweak_mac = compute_tweak_mac(tweak, tweak_len);
9✔
171

172
   BigInt X = input;
9✔
173
   secure_vector<uint8_t> tmp;
9✔
174

175
   BigInt W;
9✔
176
   BigInt R;
9✔
177
   BigInt Fi;
9✔
178
   for(size_t i = 0; i != m_rounds; ++i) {
42✔
179
      ct_divide(X, m_a, R, W);
33✔
180

181
      Fi = F(R, m_rounds - i - 1, tweak_mac, tmp);
33✔
182
      X = m_b * ct_modulo(W - Fi, m_a) + R;
33✔
183
   }
184

185
   return X;
18✔
186
}
27✔
187

188
BigInt FPE_FE1::encrypt(const BigInt& x, uint64_t tweak) const {
×
189
   uint8_t tweak8[8];
×
190
   store_be(tweak, tweak8);
×
191
   return encrypt(x, tweak8, sizeof(tweak8));
×
192
}
193

194
BigInt FPE_FE1::decrypt(const BigInt& x, uint64_t tweak) const {
×
195
   uint8_t tweak8[8];
×
196
   store_be(tweak, tweak8);
×
197
   return decrypt(x, tweak8, sizeof(tweak8));
×
198
}
199

200
namespace FPE {
201

202
BigInt fe1_encrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector<uint8_t>& tweak) {
6✔
203
   FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
6✔
204
   fpe.set_key(key);
6✔
205
   return fpe.encrypt(X, tweak.data(), tweak.size());
12✔
206
}
6✔
207

208
BigInt fe1_decrypt(const BigInt& n, const BigInt& X, const SymmetricKey& key, const std::vector<uint8_t>& tweak) {
6✔
209
   FPE_FE1 fpe(n, 3, true, "HMAC(SHA-256)");
6✔
210
   fpe.set_key(key);
6✔
211
   return fpe.decrypt(X, tweak.data(), tweak.size());
12✔
212
}
6✔
213

214
}  // namespace FPE
215

216
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc