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

randombit / botan / 16395825001

19 Jul 2025 11:30PM UTC coverage: 90.635% (-0.07%) from 90.708%
16395825001

push

github

web-flow
Merge pull request #4998 from randombit/jack/fix-clang-tidy-readability-isolate-declaration

Enable and fix clang-tidy warning readability-isolate-declaration

99940 of 110266 relevant lines covered (90.64%)

12341110.13 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/mac.h>
11
#include <botan/numthry.h>
12
#include <botan/internal/divide.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/loadstor.h>
15

16
namespace Botan {
17

18
namespace {
19

20
// Normally FPE is for SSNs, CC#s, etc, nothing too big
21
const size_t MAX_N_BYTES = 128 / 8;
22

23
/*
24
* Factor n into a and b which are as close together as possible.
25
* Assumes n is composed mostly of small factors which is the case for
26
* typical uses of FPE (typically, n is a power of 10)
27
*/
28
void factor(BigInt n, BigInt& a, BigInt& b) {
15✔
29
   BOTAN_ARG_CHECK(n >= 2, "Invalid FPE modulus");
15✔
30

31
   a = BigInt::one();
15✔
32
   b = BigInt::one();
15✔
33

34
   /*
35
   * This algorithm was poorly designed. It should have fully factored n (to the
36
   * extent possible) and then built a/b starting from the largest factor first.
37
   *
38
   * This can't be fixed now without breaking existing users but if some
39
   * incompatible change (or new flag, etc) is added in the future, consider
40
   * fixing the factoring for those users.
41
   */
42

43
   size_t n_low_zero = low_zero_bits(n);
15✔
44

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

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

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

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

69
}  // namespace
70

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

76
   m_mac = MessageAuthenticationCode::create_or_throw(mac_algo);
15✔
77

78
   m_n_bytes = n.serialize();
30✔
79

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

84
   factor(n, m_a, m_b);
15✔
85

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

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

99
FPE_FE1::~FPE_FE1() = default;
32✔
100

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

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

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

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

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

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

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

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

133
   tmp = m_mac->final();
152✔
134
   return BigInt::from_bytes(tmp);
76✔
135
}
136

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

141
   m_mac->update_be(static_cast<uint32_t>(tweak_len));
20✔
142
   if(tweak_len > 0) {
20✔
143
      m_mac->update(tweak, tweak_len);
14✔
144
   }
145

146
   return m_mac->final();
20✔
147
}
148

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

152
   BigInt X = input;
10✔
153

154
   secure_vector<uint8_t> tmp;
10✔
155

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

165
   return X;
20✔
166
}
30✔
167

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

171
   BigInt X = input;
10✔
172
   secure_vector<uint8_t> tmp;
10✔
173

174
   BigInt W;
10✔
175
   BigInt R;
10✔
176
   BigInt Fi;
10✔
177
   for(size_t i = 0; i != m_rounds; ++i) {
48✔
178
      ct_divide(X, m_a, R, W);
38✔
179

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

184
   return X;
20✔
185
}
30✔
186

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

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

199
namespace FPE {
200

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

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

213
}  // namespace FPE
214

215
}  // 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