Botan 3.7.1
Crypto and TLS for C&
cmce_field_ordering.cpp
Go to the documentation of this file.
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
21namespace Botan {
22
23namespace CMCE_CT {
24
25namespace {
26
27template <std::unsigned_integral T1, std::unsigned_integral T2>
28 requires(sizeof(T1) <= 8 && sizeof(T2) <= 8)
29void cond_swap_pair(CT::Mask<uint64_t> cond_mask, std::pair<T1, T2>& a, std::pair<T1, T2>& b) {
30 cond_mask.conditional_swap(a.first, b.first);
31 cond_mask.conditional_swap(a.second, b.second);
32}
33
34template <std::unsigned_integral T1, std::unsigned_integral T2>
35void compare_and_swap_pair(std::span<std::pair<T1, T2>> a, size_t i, size_t k, size_t l) {
36 static_assert(sizeof(T1) <= sizeof(uint64_t) && sizeof(T2) <= sizeof(uint64_t),
37 "Types T1 and T2 must be at most 64 bits wide");
38 if((i & k) == 0) { // i and k do not depend on secret data
39 auto swap_required_mask = CT::Mask<uint64_t>::is_lt(a[l].first, a[i].first);
40 cond_swap_pair(swap_required_mask, a[i], a[l]);
41 } else {
42 auto swap_required_mask = CT::Mask<uint64_t>::is_gt(a[l].first, a[i].first);
43 cond_swap_pair(swap_required_mask, a[i], a[l]);
44 }
45}
46
47// Sorts a vector of pairs after the first element
48template <std::unsigned_integral T1, std::unsigned_integral T2>
49void bitonic_sort_pair(std::span<std::pair<T1, T2>> a) {
50 const size_t n = a.size();
51 BOTAN_ARG_CHECK(is_power_of_2(n), "Input vector size must be a power of 2");
52
53 for(size_t k = 2; k <= n; k *= 2) {
54 for(size_t j = k / 2; j > 0; j /= 2) {
55 for(size_t i = 0; i < n; ++i) {
56 const size_t l = i ^ j;
57 if(l > i) {
58 compare_and_swap_pair(a, i, k, l);
59 }
60 }
61 }
62 }
63}
64
65template <std::unsigned_integral T>
66T min(const T& a, const T& b) {
67 auto mask = CT::Mask<T>::is_lt(a, b);
68 return mask.select(a, b);
69}
70
71} // namespace
72
73} // namespace CMCE_CT
74
75namespace {
76template <std::unsigned_integral T1, std::unsigned_integral T2>
77std::vector<std::pair<T1, T2>> zip(std::span<const T1> vec_1, std::span<const T2> vec_2) {
78 BOTAN_ARG_CHECK(vec_1.size() == vec_2.size(), "Vectors' dimensions do not match");
79 std::vector<std::pair<T1, T2>> vec_zipped;
80 vec_zipped.reserve(vec_1.size());
81 for(size_t i = 0; i < vec_1.size(); ++i) {
82 vec_zipped.push_back(std::make_pair(vec_1[i], vec_2[i]));
83 }
84 return vec_zipped;
85}
86
87template <std::unsigned_integral T1, std::unsigned_integral T2>
88std::pair<secure_vector<T1>, secure_vector<T2>> unzip(const std::span<std::pair<T1, T2>>& vec_zipped) {
89 std::pair<secure_vector<T1>, secure_vector<T2>> res;
90
91 res.first.reserve(vec_zipped.size());
92 res.second.reserve(vec_zipped.size());
93
94 for(const auto& [elem1, elem2] : vec_zipped) {
95 res.first.push_back(elem1);
96 res.second.push_back(elem2);
97 }
98 return res;
99}
100
101/// @returns (vec[0],0), ..., (vec[n-1],n-1)
102std::vector<std::pair<uint32_t, uint16_t>> enumerate(std::span<const uint32_t> vec) {
103 BOTAN_DEBUG_ASSERT(vec.size() < std::numeric_limits<uint16_t>::max());
104
105 std::vector<std::pair<uint32_t, uint16_t>> enumerated;
106
107 std::transform(vec.begin(), vec.end(), std::back_inserter(enumerated), [ctr = uint16_t(0)](uint32_t elem) mutable {
108 return std::make_pair(elem, ctr++);
109 });
110
111 return enumerated;
112}
113
114/**
115 * @brief Create permutation pi as in (Section 8.2, Step 3).
116 *
117 * @param a The vector that is sorted
118 *
119 * @return (pi sorted after a, a sorted after pi)
120 */
121std::pair<secure_vector<uint32_t>, CmcePermutation> create_pi(secure_vector<uint32_t> a) {
122 auto a_pi_zipped = enumerate(a);
123 CMCE_CT::bitonic_sort_pair(std::span(a_pi_zipped));
124
125 CmcePermutation pi_sorted;
126 std::tie(a, pi_sorted.get()) = unzip(std::span(a_pi_zipped));
127
128 return std::make_pair(a, pi_sorted);
129}
130
131/**
132* @brief Create a GF element from pi as in (Section 8.2, Step 4).
133* Corresponds to the reverse bits of pi.
134*/
135Classic_McEliece_GF from_pi(CmcePermutationElement pi_elem, CmceGfMod modulus, size_t m) {
136 auto reversed_bits = ct_reverse_bits(pi_elem.get());
137 reversed_bits >>= (sizeof(uint16_t) * 8 - m);
138 return Classic_McEliece_GF(CmceGfElem(reversed_bits), modulus);
139}
140
141/**
142 * @brief Part of field ordering generation according to ISO 9.2.10
143 */
144secure_vector<uint16_t> composeinv(std::span<const uint16_t> c, std::span<const uint16_t> pi) {
145 auto pi_c_zipped = zip(pi, c);
146 CMCE_CT::bitonic_sort_pair(std::span(pi_c_zipped));
147 // Extract c from the sorted vector
149 std::transform(pi_c_zipped.begin(), pi_c_zipped.end(), std::back_inserter(c_sorted), [](const auto& pair) {
150 return pair.second;
151 });
152
153 return c_sorted;
154}
155
156// p,q = composeinv(p,q),composeinv(q,p)
157void simultaneous_composeinv(secure_vector<uint16_t>& p, secure_vector<uint16_t>& q) {
158 auto p_new = composeinv(p, q);
159 q = composeinv(q, p);
160 p = std::move(p_new);
161}
162
163/**
164 * @brief Generate control bits as in ISO 9.2.10.
165 *
166 * TODO: This function can be optimized (see Classic McEliece reference implementation)
167 */
168secure_vector<uint16_t> generate_control_bits_internal(const secure_vector<uint16_t>& pi) {
169 const auto n = pi.size();
171 const size_t m = ceil_log2(n);
172
173 if(m == 1) {
174 return secure_vector<uint16_t>({pi.at(0)});
175 }
177 for(size_t x = 0; x < n; ++x) {
178 p.at(x) = pi.at(x ^ 1);
179 }
181 for(size_t x = 0; x < n; ++x) {
182 q.at(x) = pi.at(x) ^ 1;
183 }
184
185 secure_vector<uint16_t> range_n(n);
186 std::iota(range_n.begin(), range_n.end(), static_cast<uint16_t>(0));
187 auto piinv = composeinv(range_n, pi);
188
189 simultaneous_composeinv(p, q);
190
192 for(uint16_t x = 0; static_cast<size_t>(x) < n; ++x) {
193 c.at(x) = CMCE_CT::min(x, p.at(x));
194 }
195
196 simultaneous_composeinv(p, q);
197
198 for(size_t i = 1; i < m - 1; ++i) {
199 auto cp = composeinv(c, q);
200 simultaneous_composeinv(p, q);
201 for(size_t x = 0; x < n; ++x) {
202 c.at(x) = CMCE_CT::min(c.at(x), cp.at(x));
203 }
204 }
205
207 for(size_t j = 0; j < n / 2; ++j) {
208 f.at(j) = c.at(2 * j) % 2;
209 }
210
212 for(uint16_t x = 0; size_t(x) < n; ++x) {
213 big_f.at(x) = x ^ f.at(x / 2);
214 }
215
216 auto fpi = composeinv(big_f, piinv);
217
219 for(size_t k = 0; k < n / 2; ++k) {
220 l.at(k) = fpi.at(2 * k) % 2;
221 }
222
224 for(uint16_t y = 0; size_t(y) < n; ++y) {
225 big_l.at(y) = y ^ l.at(y / 2);
226 }
227
228 auto big_m = composeinv(fpi, big_l);
229
230 secure_vector<uint16_t> subm0(n / 2);
231 secure_vector<uint16_t> subm1(n / 2);
232 for(size_t j = 0; j < n / 2; ++j) {
233 subm0.at(j) = big_m.at(2 * j) / 2;
234 subm1.at(j) = big_m.at(2 * j + 1) / 2;
235 }
236
237 auto subz0 = generate_control_bits_internal(subm0);
238 auto subz1 = generate_control_bits_internal(subm1);
239
240 secure_vector<uint16_t> z(subz0.size() + subz1.size());
241 for(size_t j = 0; j < subz0.size(); ++j) {
242 z.at(2 * j) = subz0.at(j);
243 z.at(2 * j + 1) = subz1.at(j);
244 }
245
246 return concat(f, z, l);
247}
248
249CT::Choice ct_has_adjacent_duplicates(std::span<const uint32_t> vec) {
250 CT::Mask<uint32_t> mask = CT::Mask<uint32_t>::cleared();
251 for(size_t i = 0; i < vec.size() - 1; ++i) {
252 mask |= CT::Mask<uint32_t>::is_equal(vec[i], vec[i + 1]);
253 }
254 return mask.as_choice();
255}
256
257} // anonymous namespace
258
259std::optional<Classic_McEliece_Field_Ordering> Classic_McEliece_Field_Ordering::create_field_ordering(
261 BOTAN_ARG_CHECK(random_bits.size() == (params.sigma2() * params.q()) / 8, "Wrong random bits size");
262
263 auto a = load_le<secure_vector<uint32_t>>(random_bits); // contains a_0, a_1, ...
264 auto [sorted_a, pi] = create_pi(std::move(a));
265 if(ct_has_adjacent_duplicates(sorted_a).as_bool()) {
266 return std::nullopt;
267 }
268
269 return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
270}
271
272std::vector<Classic_McEliece_GF> Classic_McEliece_Field_Ordering::alphas(size_t n) const {
273 BOTAN_ASSERT_NOMSG(m_poly_f.get() != 0);
274 BOTAN_ASSERT_NOMSG(m_pi.size() >= n);
275
276 std::vector<Classic_McEliece_GF> n_alphas_vec;
277
278 std::transform(m_pi.begin(), m_pi.begin() + n, std::back_inserter(n_alphas_vec), [this](uint16_t pi_elem) {
279 return from_pi(CmcePermutationElement(pi_elem), m_poly_f, Classic_McEliece_GF::log_q_from_mod(m_poly_f));
280 });
281
282 return n_alphas_vec;
283}
284
286 // Each vector element contains one bit of the control bits
287 const auto control_bits_as_words = generate_control_bits_internal(m_pi.get());
288 auto control_bits = secure_bitvector(control_bits_as_words.size());
289 for(size_t i = 0; i < control_bits.size(); ++i) {
290 control_bits.at(i) = control_bits_as_words.at(i);
291 }
292
293 return control_bits;
294}
295
296// Based on the Python code "permutation(c)" from Bernstein
297// "Verified fast formulas for control bits for permutation networks"
299 const Classic_McEliece_Parameters& params, const secure_bitvector& control_bits) {
300 BOTAN_ASSERT_NOMSG(control_bits.size() == (2 * params.m() - 1) << (params.m() - 1));
301 const uint16_t n = uint16_t(1) << params.m();
302 CmcePermutation pi(n);
303 std::iota(pi.begin(), pi.end(), static_cast<uint16_t>(0));
304 for(size_t i = 0; i < 2 * params.m() - 1; ++i) {
305 const size_t gap = size_t(1) << std::min(i, 2 * params.m() - 2 - i);
306 for(size_t j = 0; j < size_t(n / 2); ++j) {
307 const size_t pos = (j % gap) + 2 * gap * (j / gap);
308 auto mask = CT::Mask<uint16_t>::expand(control_bits[i * n / 2 + j]);
309 mask.conditional_swap(pi[pos], pi[pos + gap]);
310 }
311 }
312
313 return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
314}
315
317 const CmceColumnSelection& pivots) {
318 auto col_offset = params.pk_no_rows() - Classic_McEliece_Parameters::mu();
319
320 for(size_t p_idx = 1; p_idx <= Classic_McEliece_Parameters::mu(); ++p_idx) {
321 size_t p_counter = 0;
322 for(size_t col = 0; col < Classic_McEliece_Parameters::nu(); ++col) {
323 auto mask_is_pivot_set = CT::Mask<size_t>::expand(pivots.at(col));
324 p_counter += CT::Mask<size_t>::expand(pivots.at(col)).if_set_return(1);
325 auto mask_is_current_pivot = CT::Mask<size_t>::is_equal(p_idx, p_counter);
326 (mask_is_pivot_set & mask_is_current_pivot)
327 .conditional_swap(m_pi.get().at(col_offset + col), m_pi.get().at(col_offset + p_idx - 1));
328 }
329 }
330}
331
332} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
void conditional_swap(U &x, U &y) const
Definition ct_utils.h:596
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:408
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:453
static constexpr Mask< T > is_gt(T x, T y)
Definition ct_utils.h:469
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:461
static constexpr Mask< T > cleared()
Definition ct_utils.h:403
Represents a field ordering for the Classic McEliece cryptosystem.
static std::optional< Classic_McEliece_Field_Ordering > create_field_ordering(const Classic_McEliece_Parameters &params, StrongSpan< const CmceOrderingBits > random_bits)
Creates a field ordering from a random bit sequence. Corresponds to the algorithm described in Classi...
static Classic_McEliece_Field_Ordering create_from_control_bits(const Classic_McEliece_Parameters &params, const secure_bitvector &control_bits)
Create the field ordering from the control bits of a benes network.
secure_bitvector alphas_control_bits() const
Generates the control bits of the benes network corresponding to the field ordering.
void permute_with_pivots(const Classic_McEliece_Parameters &params, const CmceColumnSelection &pivots)
Permute the field ordering with the given pivots.
std::vector< Classic_McEliece_GF > alphas(size_t n) const
Returns the field ordering as a vector of all alphas from alpha_0 to alpha_{n-1}.
size_t m() const
The degree of the Classic McEliece instance's underlying Galois Field, i.e. GF(q) = GF(2^m).
size_t q() const
The field size of the Classic McEliece instance's underlying Galois Field, i.e. GF(q) is the underlyi...
CmceGfMod poly_f() const
The monic irreducible polynomial f(z) of degree m over GF(2). Used for modular reduction in GF(2^m).
static constexpr size_t nu()
Constant nu for semi-systematic matrix creation. (see Classic McEliece ISO 7.2.3)
static constexpr size_t mu()
Constant mu for semi-systematic matrix creation. (see Classic McEliece ISO 7.2.3)
size_t pk_no_rows() const
The number of rows in the public key's matrix.
static constexpr size_t sigma2()
Constant for field-ordering generation. (see Classic McEliece ISO 8.2)
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
size_type size() const
Definition bitvector.h:399
constexpr T & get() &
Definition strong_type.h:50
FE_25519 T
Definition ge.cpp:34
Strong< uint16_t, struct CmceGfMod_ > CmceGfMod
Represents a GF(q) modulus.
Definition cmce_types.h:22
bitvector_base< secure_allocator > secure_bitvector
Definition bitvector.h:1297
constexpr bool is_power_of_2(T arg)
Definition bit_ops.h:48
Strong< uint16_t, struct CmcePermutationElement_ > CmcePermutationElement
Represents an element of a permuation (pi in spec). Used in field ordering creation.
Definition cmce_types.h:25
constexpr T ct_reverse_bits(T b)
Definition bit_ops.h:227
Strong< uint16_t, struct CmceGfElem_ > CmceGfElem
Represents a GF(q) element.
Definition cmce_types.h:19
Strong< secure_vector< uint16_t >, struct CmcePermutation_ > CmcePermutation
Represents a permutation (pi in spec). Used in field ordering creation.
Definition cmce_types.h:28
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:521
const SIMD_8x32 & b
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr uint8_t ceil_log2(T x)
Definition bit_ops.h:133