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

randombit / botan / 11972552403

22 Nov 2024 12:26PM UTC coverage: 91.244% (+0.004%) from 91.24%
11972552403

push

github

web-flow
Merge pull request #4436 from randombit/jack/cleanup-curvegfp

Cleanups relating to CurveGFp

93250 of 102198 relevant lines covered (91.24%)

11237819.31 hits per line

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

96.59
/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp
1
/*
2
 * Classic McEliece Field Ordering Generation
3
 * Based on the public domain reference implementation by the designers
4
 * (https://classic.mceliece.org/impl.html - released in Oct 2022 for NISTPQC-R4)
5
 *
6
 * (C) 2023 Jack Lloyd
7
 *     2023,2024 Fabian Albert, Amos Treiber - Rohde & Schwarz Cybersecurity
8
 *
9
 * Botan is released under the Simplified BSD License (see license.txt)
10
 **/
11
#include <botan/internal/cmce_field_ordering.h>
12

13
#include <botan/cmce.h>
14
#include <botan/mem_ops.h>
15
#include <botan/internal/loadstor.h>
16

17
#include <numeric>
18
#include <utility>
19
#include <vector>
20

21
namespace Botan {
22

23
namespace CMCE_CT {
24

25
template <std::unsigned_integral T1, std::unsigned_integral T2>
26
   requires(sizeof(T1) <= 8 && sizeof(T2) <= 8)
27
void cond_swap_pair(CT::Mask<uint64_t> cond_mask, std::pair<T1, T2>& a, std::pair<T1, T2>& b) {
2,147,483,647✔
28
   cond_mask.conditional_swap(a.first, b.first);
2,147,483,647✔
29
   cond_mask.conditional_swap(a.second, b.second);
2,147,483,647✔
30
}
2,147,483,647✔
31

32
template <std::unsigned_integral T1, std::unsigned_integral T2>
33
void compare_and_swap_pair(std::span<std::pair<T1, T2>> a, size_t i, size_t k, size_t l) {
2,147,483,647✔
34
   static_assert(sizeof(T1) <= sizeof(uint64_t) && sizeof(T2) <= sizeof(uint64_t),
35
                 "Types T1 and T2 must be at most 64 bits wide");
36
   if((i & k) == 0) {  // i and k do not depend on secret data
2,147,483,647✔
37
      auto swap_required_mask = CT::Mask<uint64_t>::is_lt(a[l].first, a[i].first);
2,147,483,647✔
38
      cond_swap_pair(swap_required_mask, a[i], a[l]);
2,147,483,647✔
39
   } else {
40
      auto swap_required_mask = CT::Mask<uint64_t>::is_gt(a[l].first, a[i].first);
1,907,268,608✔
41
      cond_swap_pair(swap_required_mask, a[i], a[l]);
1,907,268,608✔
42
   }
43
}
2,147,483,647✔
44

45
// Sorts a vector of pairs after the first element
46
template <std::unsigned_integral T1, std::unsigned_integral T2>
47
void bitonic_sort_pair(std::span<std::pair<T1, T2>> a) {
3,415,897✔
48
   const size_t n = a.size();
3,415,897✔
49
   BOTAN_ARG_CHECK(is_power_of_2(n), "Input vector size must be a power of 2");
×
50

51
   for(size_t k = 2; k <= n; k *= 2) {
15,664,853✔
52
      for(size_t j = k / 2; j > 0; j /= 2) {
45,938,642✔
53
         for(size_t i = 0; i < n; ++i) {
2,147,483,647✔
54
            const size_t l = i ^ j;
2,147,483,647✔
55
            if(l > i) {
2,147,483,647✔
56
               compare_and_swap_pair(a, i, k, l);
2,147,483,647✔
57
            }
58
         }
59
      }
60
   }
61
}
3,415,897✔
62

63
template <std::unsigned_integral T>
64
T min(const T& a, const T& b) {
52,125,696✔
65
   auto mask = CT::Mask<T>::is_lt(a, b);
52,125,696✔
66
   return mask.select(a, b);
52,125,696✔
67
}
68

69
}  // namespace CMCE_CT
70

71
namespace {
72
template <std::unsigned_integral T1, std::unsigned_integral T2>
73
std::vector<std::pair<T1, T2>> zip(std::span<const T1> vec_1, std::span<const T2> vec_2) {
3,415,819✔
74
   BOTAN_ARG_CHECK(vec_1.size() == vec_2.size(), "Vectors' dimensions do not match");
3,415,819✔
75
   std::vector<std::pair<T1, T2>> vec_zipped;
3,415,819✔
76
   vec_zipped.reserve(vec_1.size());
3,415,819✔
77
   for(size_t i = 0; i < vec_1.size(); ++i) {
192,216,843✔
78
      vec_zipped.push_back(std::make_pair(vec_1[i], vec_2[i]));
188,801,024✔
79
   }
80
   return vec_zipped;
3,415,819✔
81
}
×
82

83
template <std::unsigned_integral T1, std::unsigned_integral T2>
84
std::pair<secure_vector<T1>, secure_vector<T2>> unzip(const std::span<std::pair<T1, T2>>& vec_zipped) {
78✔
85
   std::pair<secure_vector<T1>, secure_vector<T2>> res;
78✔
86

87
   res.first.reserve(vec_zipped.size());
78✔
88
   res.second.reserve(vec_zipped.size());
78✔
89

90
   for(const auto& [elem1, elem2] : vec_zipped) {
565,326✔
91
      res.first.push_back(elem1);
565,248✔
92
      res.second.push_back(elem2);
565,248✔
93
   }
94
   return res;
78✔
95
}
×
96

97
/// @returns (vec[0],0), ..., (vec[n-1],n-1)
98
std::vector<std::pair<uint32_t, uint16_t>> enumerate(std::span<const uint32_t> vec) {
78✔
99
   BOTAN_DEBUG_ASSERT(vec.size() < std::numeric_limits<uint16_t>::max());
78✔
100

101
   std::vector<std::pair<uint32_t, uint16_t>> enumerated;
78✔
102

103
   std::transform(vec.begin(), vec.end(), std::back_inserter(enumerated), [ctr = uint16_t(0)](uint32_t elem) mutable {
78✔
104
      return std::make_pair(elem, ctr++);
565,248✔
105
   });
106

107
   return enumerated;
78✔
108
}
×
109

110
/**
111
 * @brief Create permutation pi as in (Section 8.2, Step 3).
112
 *
113
 * @param a The vector that is sorted
114
 *
115
 * @return (pi sorted after a, a sorted after pi)
116
 */
117
std::pair<secure_vector<uint32_t>, CmcePermutation> create_pi(secure_vector<uint32_t> a) {
78✔
118
   auto a_pi_zipped = enumerate(a);
78✔
119
   CMCE_CT::bitonic_sort_pair(std::span(a_pi_zipped));
78✔
120

121
   CmcePermutation pi_sorted;
78✔
122
   std::tie(a, pi_sorted.get()) = unzip(std::span(a_pi_zipped));
78✔
123

124
   return std::make_pair(a, pi_sorted);
156✔
125
}
78✔
126

127
/**
128
* @brief Create a GF element from pi as in (Section 8.2, Step 4).
129
* Corresponds to the reverse bits of pi.
130
*/
131
Classic_McEliece_GF from_pi(CmcePermutationElement pi_elem, CmceGfMod modulus, size_t m) {
1,724,512✔
132
   auto reversed_bits = ct_reverse_bits(pi_elem.get());
1,724,512✔
133
   reversed_bits >>= (sizeof(uint16_t) * 8 - m);
1,724,512✔
134
   return Classic_McEliece_GF(CmceGfElem(reversed_bits), modulus);
1,724,512✔
135
}
136

137
/**
138
 * @brief Part of field ordering generation according to ISO 9.2.10
139
 */
140
secure_vector<uint16_t> composeinv(std::span<const uint16_t> c, std::span<const uint16_t> pi) {
3,415,819✔
141
   auto pi_c_zipped = zip(pi, c);
3,415,819✔
142
   CMCE_CT::bitonic_sort_pair(std::span(pi_c_zipped));
3,415,819✔
143
   // Extract c from the sorted vector
144
   secure_vector<uint16_t> c_sorted;
3,415,819✔
145
   std::transform(pi_c_zipped.begin(), pi_c_zipped.end(), std::back_inserter(c_sorted), [](const auto& pair) {
3,415,819✔
146
      return pair.second;
188,801,024✔
147
   });
148

149
   return c_sorted;
3,415,819✔
150
}
3,415,819✔
151

152
// p,q = composeinv(p,q),composeinv(q,p)
153
void simultaneous_composeinv(secure_vector<uint16_t>& p, secure_vector<uint16_t>& q) {
1,024,633✔
154
   auto p_new = composeinv(p, q);
1,024,633✔
155
   q = composeinv(q, p);
2,049,266✔
156
   p = std::move(p_new);
1,024,633✔
157
}
1,024,633✔
158

159
/**
160
 * @brief Generate control bits as in ISO 9.2.10.
161
 *
162
 * TODO: This function can be optimized (see Classic McEliece reference implementation)
163
 */
164
secure_vector<uint16_t> generate_control_bits_internal(const secure_vector<uint16_t>& pi) {
683,936✔
165
   const auto n = pi.size();
683,936✔
166
   BOTAN_ASSERT_NOMSG(is_power_of_2(n));
683,936✔
167
   const size_t m = ceil_log2(n);
683,936✔
168

169
   if(m == 1) {
683,936✔
170
      return secure_vector<uint16_t>({pi.at(0)});
342,016✔
171
   }
172
   secure_vector<uint16_t> p(n);
341,920✔
173
   for(size_t x = 0; x < n; ++x) {
8,447,904✔
174
      p.at(x) = pi.at(x ^ 1);
8,105,984✔
175
   }
176
   secure_vector<uint16_t> q(n);
341,920✔
177
   for(size_t x = 0; x < n; ++x) {
8,447,904✔
178
      q.at(x) = pi.at(x) ^ 1;
8,105,984✔
179
   }
180

181
   secure_vector<uint16_t> range_n(n);
341,920✔
182
   std::iota(range_n.begin(), range_n.end(), 0);
341,920✔
183
   auto piinv = composeinv(range_n, pi);
341,920✔
184

185
   simultaneous_composeinv(p, q);
341,920✔
186

187
   secure_vector<uint16_t> c(n);
341,920✔
188
   for(uint16_t x = 0; static_cast<size_t>(x) < n; ++x) {
8,447,904✔
189
      c.at(x) = CMCE_CT::min(x, p.at(x));
8,105,984✔
190
   }
191

192
   simultaneous_composeinv(p, q);
341,920✔
193

194
   for(size_t i = 1; i < m - 1; ++i) {
682,713✔
195
      auto cp = composeinv(c, q);
340,793✔
196
      simultaneous_composeinv(p, q);
340,793✔
197
      for(size_t x = 0; x < n; ++x) {
44,360,505✔
198
         c.at(x) = CMCE_CT::min(c.at(x), cp.at(x));
44,019,712✔
199
      }
200
   }
340,793✔
201

202
   secure_vector<uint16_t> f(n / 2);
341,920✔
203
   for(size_t j = 0; j < n / 2; ++j) {
4,394,912✔
204
      f.at(j) = c.at(2 * j) % 2;
4,052,992✔
205
   }
206

207
   secure_vector<uint16_t> big_f(n);
341,920✔
208
   for(uint16_t x = 0; size_t(x) < n; ++x) {
8,447,904✔
209
      big_f.at(x) = x ^ f.at(x / 2);
8,105,984✔
210
   }
211

212
   auto fpi = composeinv(big_f, piinv);
341,920✔
213

214
   secure_vector<uint16_t> l(n / 2);
341,920✔
215
   for(size_t k = 0; k < n / 2; ++k) {
4,394,912✔
216
      l.at(k) = fpi.at(2 * k) % 2;
4,052,992✔
217
   }
218

219
   secure_vector<uint16_t> big_l(n);
341,920✔
220
   for(uint16_t y = 0; size_t(y) < n; ++y) {
8,447,904✔
221
      big_l.at(y) = y ^ l.at(y / 2);
8,105,984✔
222
   }
223

224
   auto big_m = composeinv(fpi, big_l);
341,920✔
225

226
   secure_vector<uint16_t> subm0(n / 2);
341,920✔
227
   secure_vector<uint16_t> subm1(n / 2);
341,920✔
228
   for(size_t j = 0; j < n / 2; ++j) {
4,394,912✔
229
      subm0.at(j) = big_m.at(2 * j) / 2;
4,052,992✔
230
      subm1.at(j) = big_m.at(2 * j + 1) / 2;
4,052,992✔
231
   }
232

233
   auto subz0 = generate_control_bits_internal(subm0);
341,920✔
234
   auto subz1 = generate_control_bits_internal(subm1);
341,920✔
235

236
   secure_vector<uint16_t> z(subz0.size() + subz1.size());
341,920✔
237
   for(size_t j = 0; j < subz0.size(); ++j) {
24,378,272✔
238
      z.at(2 * j) = subz0.at(j);
24,036,352✔
239
      z.at(2 * j + 1) = subz1.at(j);
24,036,352✔
240
   }
241

242
   return concat(f, z, l);
341,920✔
243
}
5,470,720✔
244

245
CT::Choice ct_has_adjacent_duplicates(std::span<const uint32_t> vec) {
78✔
246
   CT::Mask<uint32_t> mask = CT::Mask<uint32_t>::cleared();
78✔
247
   for(size_t i = 0; i < vec.size() - 1; ++i) {
565,248✔
248
      mask |= CT::Mask<uint32_t>::is_equal(vec[i], vec[i + 1]);
565,170✔
249
   }
250
   return mask.as_choice();
78✔
251
}
252

253
}  // anonymous namespace
254

255
std::optional<Classic_McEliece_Field_Ordering> Classic_McEliece_Field_Ordering::create_field_ordering(
78✔
256
   const Classic_McEliece_Parameters& params, StrongSpan<const CmceOrderingBits> random_bits) {
257
   BOTAN_ARG_CHECK(random_bits.size() == (params.sigma2() * params.q()) / 8, "Wrong random bits size");
78✔
258

259
   auto a = load_le<secure_vector<uint32_t>>(random_bits);  // contains a_0, a_1, ...
78✔
260
   auto [sorted_a, pi] = create_pi(std::move(a));
156✔
261
   if(ct_has_adjacent_duplicates(sorted_a).as_bool()) {
78✔
262
      return std::nullopt;
×
263
   }
264

265
   return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
78✔
266
}
78✔
267

268
std::vector<Classic_McEliece_GF> Classic_McEliece_Field_Ordering::alphas(size_t n) const {
274✔
269
   BOTAN_ASSERT_NOMSG(m_poly_f.get() != 0);
274✔
270
   BOTAN_ASSERT_NOMSG(m_pi.size() >= n);
274✔
271

272
   std::vector<Classic_McEliece_GF> n_alphas_vec;
274✔
273

274
   std::transform(m_pi.begin(), m_pi.begin() + n, std::back_inserter(n_alphas_vec), [this](uint16_t pi_elem) {
274✔
275
      return from_pi(CmcePermutationElement(pi_elem), m_poly_f, Classic_McEliece_GF::log_q_from_mod(m_poly_f));
1,724,512✔
276
   });
277

278
   return n_alphas_vec;
274✔
279
}
×
280

281
secure_bitvector Classic_McEliece_Field_Ordering::alphas_control_bits() const {
96✔
282
   // Each vector element contains one bit of the control bits
283
   const auto control_bits_as_words = generate_control_bits_internal(m_pi.get());
96✔
284
   auto control_bits = secure_bitvector(control_bits_as_words.size());
96✔
285
   for(size_t i = 0; i < control_bits.size(); ++i) {
8,448,096✔
286
      control_bits.at(i) = control_bits_as_words.at(i);
8,448,000✔
287
   }
288

289
   return control_bits;
96✔
290
}
96✔
291

292
// Based on the Python code "permutation(c)" from Bernstein
293
// "Verified fast formulas for control bits for permutation networks"
294
Classic_McEliece_Field_Ordering Classic_McEliece_Field_Ordering::create_from_control_bits(
141✔
295
   const Classic_McEliece_Parameters& params, const secure_bitvector& control_bits) {
296
   BOTAN_ASSERT_NOMSG(control_bits.size() == (2 * params.m() - 1) << (params.m() - 1));
141✔
297
   const uint16_t n = uint16_t(1) << params.m();
141✔
298
   CmcePermutation pi(n);
141✔
299
   std::iota(pi.begin(), pi.end(), 0);
141✔
300
   for(size_t i = 0; i < 2 * params.m() - 1; ++i) {
3,594✔
301
      const size_t gap = size_t(1) << std::min(i, 2 * params.m() - 2 - i);
3,453✔
302
      for(size_t j = 0; j < size_t(n / 2); ++j) {
12,451,197✔
303
         const size_t pos = (j % gap) + 2 * gap * (j / gap);
12,447,744✔
304
         auto mask = CT::Mask<uint16_t>::expand(control_bits[i * n / 2 + j]);
12,447,744✔
305
         mask.conditional_swap(pi[pos], pi[pos + gap]);
12,447,744✔
306
      }
307
   }
308

309
   return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
141✔
310
}
141✔
311

312
void Classic_McEliece_Field_Ordering::permute_with_pivots(const Classic_McEliece_Parameters& params,
25✔
313
                                                          const CmceColumnSelection& pivots) {
314
   auto col_offset = params.pk_no_rows() - Classic_McEliece_Parameters::mu();
25✔
315

316
   for(size_t p_idx = 1; p_idx <= Classic_McEliece_Parameters::mu(); ++p_idx) {
825✔
317
      size_t p_counter = 0;
318
      for(size_t col = 0; col < Classic_McEliece_Parameters::nu(); ++col) {
52,000✔
319
         auto mask_is_pivot_set = CT::Mask<size_t>::expand(pivots.at(col));
51,200✔
320
         p_counter += CT::Mask<size_t>::expand(pivots.at(col)).if_set_return(1);
51,200✔
321
         auto mask_is_current_pivot = CT::Mask<size_t>::is_equal(p_idx, p_counter);
51,200✔
322
         (mask_is_pivot_set & mask_is_current_pivot)
51,200✔
323
            .conditional_swap(m_pi.get().at(col_offset + col), m_pi.get().at(col_offset + p_idx - 1));
51,200✔
324
      }
325
   }
326
}
25✔
327

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

© 2025 Coveralls, Inc