Coverage Report

Created: 2024-11-29 06:10

/src/botan/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp
Line
Count
Source (jump to first uncovered line)
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
0
void cond_swap_pair(CT::Mask<uint64_t> cond_mask, std::pair<T1, T2>& a, std::pair<T1, T2>& b) {
28
0
   cond_mask.conditional_swap(a.first, b.first);
29
0
   cond_mask.conditional_swap(a.second, b.second);
30
0
}
Unexecuted instantiation: _ZN5Botan7CMCE_CT14cond_swap_pairITkNSt3__117unsigned_integralEjTkNS2_17unsigned_integralEtQaalestT_Li8ElestT0_Li8EEEvNS_2CT4MaskImEERNS2_4pairIS3_S4_EESA_
Unexecuted instantiation: _ZN5Botan7CMCE_CT14cond_swap_pairITkNSt3__117unsigned_integralEtTkNS2_17unsigned_integralEtQaalestT_Li8ElestT0_Li8EEEvNS_2CT4MaskImEERNS2_4pairIS3_S4_EESA_
31
32
template <std::unsigned_integral T1, std::unsigned_integral T2>
33
0
void compare_and_swap_pair(std::span<std::pair<T1, T2>> a, size_t i, size_t k, size_t l) {
34
0
   static_assert(sizeof(T1) <= sizeof(uint64_t) && sizeof(T2) <= sizeof(uint64_t),
35
0
                 "Types T1 and T2 must be at most 64 bits wide");
36
0
   if((i & k) == 0) {  // i and k do not depend on secret data
37
0
      auto swap_required_mask = CT::Mask<uint64_t>::is_lt(a[l].first, a[i].first);
38
0
      cond_swap_pair(swap_required_mask, a[i], a[l]);
39
0
   } else {
40
0
      auto swap_required_mask = CT::Mask<uint64_t>::is_gt(a[l].first, a[i].first);
41
0
      cond_swap_pair(swap_required_mask, a[i], a[l]);
42
0
   }
43
0
}
Unexecuted instantiation: _ZN5Botan7CMCE_CT21compare_and_swap_pairITkNSt3__117unsigned_integralEjTkNS2_17unsigned_integralEtEEvNS2_4spanINS2_4pairIT_T0_EELm18446744073709551615EEEmmm
Unexecuted instantiation: _ZN5Botan7CMCE_CT21compare_and_swap_pairITkNSt3__117unsigned_integralEtTkNS2_17unsigned_integralEtEEvNS2_4spanINS2_4pairIT_T0_EELm18446744073709551615EEEmmm
44
45
// Sorts a vector of pairs after the first element
46
template <std::unsigned_integral T1, std::unsigned_integral T2>
47
0
void bitonic_sort_pair(std::span<std::pair<T1, T2>> a) {
48
0
   const size_t n = a.size();
49
0
   BOTAN_ARG_CHECK(is_power_of_2(n), "Input vector size must be a power of 2");
50
51
0
   for(size_t k = 2; k <= n; k *= 2) {
52
0
      for(size_t j = k / 2; j > 0; j /= 2) {
53
0
         for(size_t i = 0; i < n; ++i) {
54
0
            const size_t l = i ^ j;
55
0
            if(l > i) {
56
0
               compare_and_swap_pair(a, i, k, l);
57
0
            }
58
0
         }
59
0
      }
60
0
   }
61
0
}
Unexecuted instantiation: _ZN5Botan7CMCE_CT17bitonic_sort_pairITkNSt3__117unsigned_integralEjTkNS2_17unsigned_integralEtEEvNS2_4spanINS2_4pairIT_T0_EELm18446744073709551615EEE
Unexecuted instantiation: _ZN5Botan7CMCE_CT17bitonic_sort_pairITkNSt3__117unsigned_integralEtTkNS2_17unsigned_integralEtEEvNS2_4spanINS2_4pairIT_T0_EELm18446744073709551615EEE
62
63
template <std::unsigned_integral T>
64
0
T min(const T& a, const T& b) {
65
0
   auto mask = CT::Mask<T>::is_lt(a, b);
66
0
   return mask.select(a, b);
67
0
}
68
69
}  // namespace CMCE_CT
70
71
namespace {
72
template <std::unsigned_integral T1, std::unsigned_integral T2>
73
0
std::vector<std::pair<T1, T2>> zip(std::span<const T1> vec_1, std::span<const T2> vec_2) {
74
0
   BOTAN_ARG_CHECK(vec_1.size() == vec_2.size(), "Vectors' dimensions do not match");
75
0
   std::vector<std::pair<T1, T2>> vec_zipped;
76
0
   vec_zipped.reserve(vec_1.size());
77
0
   for(size_t i = 0; i < vec_1.size(); ++i) {
78
0
      vec_zipped.push_back(std::make_pair(vec_1[i], vec_2[i]));
79
0
   }
80
0
   return vec_zipped;
81
0
}
82
83
template <std::unsigned_integral T1, std::unsigned_integral T2>
84
0
std::pair<secure_vector<T1>, secure_vector<T2>> unzip(const std::span<std::pair<T1, T2>>& vec_zipped) {
85
0
   std::pair<secure_vector<T1>, secure_vector<T2>> res;
86
87
0
   res.first.reserve(vec_zipped.size());
88
0
   res.second.reserve(vec_zipped.size());
89
90
0
   for(const auto& [elem1, elem2] : vec_zipped) {
91
0
      res.first.push_back(elem1);
92
0
      res.second.push_back(elem2);
93
0
   }
94
0
   return res;
95
0
}
96
97
/// @returns (vec[0],0), ..., (vec[n-1],n-1)
98
0
std::vector<std::pair<uint32_t, uint16_t>> enumerate(std::span<const uint32_t> vec) {
99
0
   BOTAN_DEBUG_ASSERT(vec.size() < std::numeric_limits<uint16_t>::max());
100
101
0
   std::vector<std::pair<uint32_t, uint16_t>> enumerated;
102
103
0
   std::transform(vec.begin(), vec.end(), std::back_inserter(enumerated), [ctr = uint16_t(0)](uint32_t elem) mutable {
104
0
      return std::make_pair(elem, ctr++);
105
0
   });
106
107
0
   return enumerated;
108
0
}
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
0
std::pair<secure_vector<uint32_t>, CmcePermutation> create_pi(secure_vector<uint32_t> a) {
118
0
   auto a_pi_zipped = enumerate(a);
119
0
   CMCE_CT::bitonic_sort_pair(std::span(a_pi_zipped));
120
121
0
   CmcePermutation pi_sorted;
122
0
   std::tie(a, pi_sorted.get()) = unzip(std::span(a_pi_zipped));
123
124
0
   return std::make_pair(a, pi_sorted);
125
0
}
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
0
Classic_McEliece_GF from_pi(CmcePermutationElement pi_elem, CmceGfMod modulus, size_t m) {
132
0
   auto reversed_bits = ct_reverse_bits(pi_elem.get());
133
0
   reversed_bits >>= (sizeof(uint16_t) * 8 - m);
134
0
   return Classic_McEliece_GF(CmceGfElem(reversed_bits), modulus);
135
0
}
136
137
/**
138
 * @brief Part of field ordering generation according to ISO 9.2.10
139
 */
140
0
secure_vector<uint16_t> composeinv(std::span<const uint16_t> c, std::span<const uint16_t> pi) {
141
0
   auto pi_c_zipped = zip(pi, c);
142
0
   CMCE_CT::bitonic_sort_pair(std::span(pi_c_zipped));
143
   // Extract c from the sorted vector
144
0
   secure_vector<uint16_t> c_sorted;
145
0
   std::transform(pi_c_zipped.begin(), pi_c_zipped.end(), std::back_inserter(c_sorted), [](const auto& pair) {
146
0
      return pair.second;
147
0
   });
148
149
0
   return c_sorted;
150
0
}
151
152
// p,q = composeinv(p,q),composeinv(q,p)
153
0
void simultaneous_composeinv(secure_vector<uint16_t>& p, secure_vector<uint16_t>& q) {
154
0
   auto p_new = composeinv(p, q);
155
0
   q = composeinv(q, p);
156
0
   p = std::move(p_new);
157
0
}
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
0
secure_vector<uint16_t> generate_control_bits_internal(const secure_vector<uint16_t>& pi) {
165
0
   const auto n = pi.size();
166
0
   BOTAN_ASSERT_NOMSG(is_power_of_2(n));
167
0
   const size_t m = ceil_log2(n);
168
169
0
   if(m == 1) {
170
0
      return secure_vector<uint16_t>({pi.at(0)});
171
0
   }
172
0
   secure_vector<uint16_t> p(n);
173
0
   for(size_t x = 0; x < n; ++x) {
174
0
      p.at(x) = pi.at(x ^ 1);
175
0
   }
176
0
   secure_vector<uint16_t> q(n);
177
0
   for(size_t x = 0; x < n; ++x) {
178
0
      q.at(x) = pi.at(x) ^ 1;
179
0
   }
180
181
0
   secure_vector<uint16_t> range_n(n);
182
0
   std::iota(range_n.begin(), range_n.end(), 0);
183
0
   auto piinv = composeinv(range_n, pi);
184
185
0
   simultaneous_composeinv(p, q);
186
187
0
   secure_vector<uint16_t> c(n);
188
0
   for(uint16_t x = 0; static_cast<size_t>(x) < n; ++x) {
189
0
      c.at(x) = CMCE_CT::min(x, p.at(x));
190
0
   }
191
192
0
   simultaneous_composeinv(p, q);
193
194
0
   for(size_t i = 1; i < m - 1; ++i) {
195
0
      auto cp = composeinv(c, q);
196
0
      simultaneous_composeinv(p, q);
197
0
      for(size_t x = 0; x < n; ++x) {
198
0
         c.at(x) = CMCE_CT::min(c.at(x), cp.at(x));
199
0
      }
200
0
   }
201
202
0
   secure_vector<uint16_t> f(n / 2);
203
0
   for(size_t j = 0; j < n / 2; ++j) {
204
0
      f.at(j) = c.at(2 * j) % 2;
205
0
   }
206
207
0
   secure_vector<uint16_t> big_f(n);
208
0
   for(uint16_t x = 0; size_t(x) < n; ++x) {
209
0
      big_f.at(x) = x ^ f.at(x / 2);
210
0
   }
211
212
0
   auto fpi = composeinv(big_f, piinv);
213
214
0
   secure_vector<uint16_t> l(n / 2);
215
0
   for(size_t k = 0; k < n / 2; ++k) {
216
0
      l.at(k) = fpi.at(2 * k) % 2;
217
0
   }
218
219
0
   secure_vector<uint16_t> big_l(n);
220
0
   for(uint16_t y = 0; size_t(y) < n; ++y) {
221
0
      big_l.at(y) = y ^ l.at(y / 2);
222
0
   }
223
224
0
   auto big_m = composeinv(fpi, big_l);
225
226
0
   secure_vector<uint16_t> subm0(n / 2);
227
0
   secure_vector<uint16_t> subm1(n / 2);
228
0
   for(size_t j = 0; j < n / 2; ++j) {
229
0
      subm0.at(j) = big_m.at(2 * j) / 2;
230
0
      subm1.at(j) = big_m.at(2 * j + 1) / 2;
231
0
   }
232
233
0
   auto subz0 = generate_control_bits_internal(subm0);
234
0
   auto subz1 = generate_control_bits_internal(subm1);
235
236
0
   secure_vector<uint16_t> z(subz0.size() + subz1.size());
237
0
   for(size_t j = 0; j < subz0.size(); ++j) {
238
0
      z.at(2 * j) = subz0.at(j);
239
0
      z.at(2 * j + 1) = subz1.at(j);
240
0
   }
241
242
0
   return concat(f, z, l);
243
0
}
244
245
0
CT::Choice ct_has_adjacent_duplicates(std::span<const uint32_t> vec) {
246
0
   CT::Mask<uint32_t> mask = CT::Mask<uint32_t>::cleared();
247
0
   for(size_t i = 0; i < vec.size() - 1; ++i) {
248
0
      mask |= CT::Mask<uint32_t>::is_equal(vec[i], vec[i + 1]);
249
0
   }
250
0
   return mask.as_choice();
251
0
}
252
253
}  // anonymous namespace
254
255
std::optional<Classic_McEliece_Field_Ordering> Classic_McEliece_Field_Ordering::create_field_ordering(
256
0
   const Classic_McEliece_Parameters& params, StrongSpan<const CmceOrderingBits> random_bits) {
257
0
   BOTAN_ARG_CHECK(random_bits.size() == (params.sigma2() * params.q()) / 8, "Wrong random bits size");
258
259
0
   auto a = load_le<secure_vector<uint32_t>>(random_bits);  // contains a_0, a_1, ...
260
0
   auto [sorted_a, pi] = create_pi(std::move(a));
261
0
   if(ct_has_adjacent_duplicates(sorted_a).as_bool()) {
262
0
      return std::nullopt;
263
0
   }
264
265
0
   return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
266
0
}
267
268
0
std::vector<Classic_McEliece_GF> Classic_McEliece_Field_Ordering::alphas(size_t n) const {
269
0
   BOTAN_ASSERT_NOMSG(m_poly_f.get() != 0);
270
0
   BOTAN_ASSERT_NOMSG(m_pi.size() >= n);
271
272
0
   std::vector<Classic_McEliece_GF> n_alphas_vec;
273
274
0
   std::transform(m_pi.begin(), m_pi.begin() + n, std::back_inserter(n_alphas_vec), [this](uint16_t pi_elem) {
275
0
      return from_pi(CmcePermutationElement(pi_elem), m_poly_f, Classic_McEliece_GF::log_q_from_mod(m_poly_f));
276
0
   });
277
278
0
   return n_alphas_vec;
279
0
}
280
281
0
secure_bitvector Classic_McEliece_Field_Ordering::alphas_control_bits() const {
282
   // Each vector element contains one bit of the control bits
283
0
   const auto control_bits_as_words = generate_control_bits_internal(m_pi.get());
284
0
   auto control_bits = secure_bitvector(control_bits_as_words.size());
285
0
   for(size_t i = 0; i < control_bits.size(); ++i) {
286
0
      control_bits.at(i) = control_bits_as_words.at(i);
287
0
   }
288
289
0
   return control_bits;
290
0
}
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(
295
0
   const Classic_McEliece_Parameters& params, const secure_bitvector& control_bits) {
296
0
   BOTAN_ASSERT_NOMSG(control_bits.size() == (2 * params.m() - 1) << (params.m() - 1));
297
0
   const uint16_t n = uint16_t(1) << params.m();
298
0
   CmcePermutation pi(n);
299
0
   std::iota(pi.begin(), pi.end(), 0);
300
0
   for(size_t i = 0; i < 2 * params.m() - 1; ++i) {
301
0
      const size_t gap = size_t(1) << std::min(i, 2 * params.m() - 2 - i);
302
0
      for(size_t j = 0; j < size_t(n / 2); ++j) {
303
0
         const size_t pos = (j % gap) + 2 * gap * (j / gap);
304
0
         auto mask = CT::Mask<uint16_t>::expand(control_bits[i * n / 2 + j]);
305
0
         mask.conditional_swap(pi[pos], pi[pos + gap]);
306
0
      }
307
0
   }
308
309
0
   return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
310
0
}
311
312
void Classic_McEliece_Field_Ordering::permute_with_pivots(const Classic_McEliece_Parameters& params,
313
0
                                                          const CmceColumnSelection& pivots) {
314
0
   auto col_offset = params.pk_no_rows() - Classic_McEliece_Parameters::mu();
315
316
0
   for(size_t p_idx = 1; p_idx <= Classic_McEliece_Parameters::mu(); ++p_idx) {
317
0
      size_t p_counter = 0;
318
0
      for(size_t col = 0; col < Classic_McEliece_Parameters::nu(); ++col) {
319
0
         auto mask_is_pivot_set = CT::Mask<size_t>::expand(pivots.at(col));
320
0
         p_counter += CT::Mask<size_t>::expand(pivots.at(col)).if_set_return(1);
321
0
         auto mask_is_current_pivot = CT::Mask<size_t>::is_equal(p_idx, p_counter);
322
0
         (mask_is_pivot_set & mask_is_current_pivot)
323
0
            .conditional_swap(m_pi.get().at(col_offset + col), m_pi.get().at(col_offset + p_idx - 1));
324
0
      }
325
0
   }
326
0
}
327
328
}  // namespace Botan