Botan 3.10.0
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
12#include <botan/internal/cmce_field_ordering.h>
13
14#include <botan/internal/loadstor.h>
15#include <numeric>
16#include <utility>
17#include <vector>
18
19namespace Botan {
20
21namespace CMCE_CT {
22
23namespace {
24
25template <std::unsigned_integral T1, std::unsigned_integral T2>
26 requires(sizeof(T1) <= 8 && sizeof(T2) <= 8)
27void cond_swap_pair(CT::Mask<uint64_t> cond_mask, std::pair<T1, T2>& a, std::pair<T1, T2>& b) {
28 cond_mask.conditional_swap(a.first, b.first);
29 cond_mask.conditional_swap(a.second, b.second);
30}
31
32template <std::unsigned_integral T1, std::unsigned_integral T2>
33void compare_and_swap_pair(std::span<std::pair<T1, T2>> a, size_t i, size_t k, size_t l) {
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
37 auto swap_required_mask = CT::Mask<uint64_t>::is_lt(a[l].first, a[i].first);
38 cond_swap_pair(swap_required_mask, a[i], a[l]);
39 } else {
40 auto swap_required_mask = CT::Mask<uint64_t>::is_gt(a[l].first, a[i].first);
41 cond_swap_pair(swap_required_mask, a[i], a[l]);
42 }
43}
44
45// Sorts a vector of pairs after the first element
46template <std::unsigned_integral T1, std::unsigned_integral T2>
47void bitonic_sort_pair(std::span<std::pair<T1, T2>> a) {
48 const size_t n = a.size();
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) {
52 for(size_t j = k / 2; j > 0; j /= 2) {
53 for(size_t i = 0; i < n; ++i) {
54 const size_t l = i ^ j;
55 if(l > i) {
56 compare_and_swap_pair(a, i, k, l);
57 }
58 }
59 }
60 }
61}
62
63template <std::unsigned_integral T>
64T min(const T& a, const T& b) {
65 auto mask = CT::Mask<T>::is_lt(a, b);
66 return mask.select(a, b);
67}
68
69} // namespace
70
71} // namespace CMCE_CT
72
73namespace {
74template <std::unsigned_integral T1, std::unsigned_integral T2>
75std::vector<std::pair<T1, T2>> zip(std::span<const T1> vec_1, std::span<const T2> vec_2) {
76 BOTAN_ARG_CHECK(vec_1.size() == vec_2.size(), "Vectors' dimensions do not match");
77 std::vector<std::pair<T1, T2>> vec_zipped;
78 vec_zipped.reserve(vec_1.size());
79 for(size_t i = 0; i < vec_1.size(); ++i) {
80 vec_zipped.push_back(std::make_pair(vec_1[i], vec_2[i]));
81 }
82 return vec_zipped;
83}
84
85template <std::unsigned_integral T1, std::unsigned_integral T2>
86std::pair<secure_vector<T1>, secure_vector<T2>> unzip(const std::span<std::pair<T1, T2>>& vec_zipped) {
87 std::pair<secure_vector<T1>, secure_vector<T2>> res;
88
89 res.first.reserve(vec_zipped.size());
90 res.second.reserve(vec_zipped.size());
91
92 for(const auto& [elem1, elem2] : vec_zipped) {
93 res.first.push_back(elem1);
94 res.second.push_back(elem2);
95 }
96 return res;
97}
98
99/// @returns (vec[0],0), ..., (vec[n-1],n-1)
100std::vector<std::pair<uint32_t, uint16_t>> enumerate(std::span<const uint32_t> vec) {
101 BOTAN_DEBUG_ASSERT(vec.size() < std::numeric_limits<uint16_t>::max());
102
103 std::vector<std::pair<uint32_t, uint16_t>> enumerated;
104
105 std::transform(vec.begin(), vec.end(), std::back_inserter(enumerated), [ctr = uint16_t(0)](uint32_t elem) mutable {
106 return std::make_pair(elem, ctr++);
107 });
108
109 return enumerated;
110}
111
112/**
113 * @brief Create permutation pi as in (Section 8.2, Step 3).
114 *
115 * @param a The vector that is sorted
116 *
117 * @return (pi sorted after a, a sorted after pi)
118 */
119std::pair<secure_vector<uint32_t>, CmcePermutation> create_pi(secure_vector<uint32_t> a) {
120 auto a_pi_zipped = enumerate(a);
121 CMCE_CT::bitonic_sort_pair(std::span(a_pi_zipped));
122
123 CmcePermutation pi_sorted;
124 std::tie(a, pi_sorted.get()) = unzip(std::span(a_pi_zipped));
125
126 return std::make_pair(a, pi_sorted);
127}
128
129/**
130* @brief Create a GF element from pi as in (Section 8.2, Step 4).
131* Corresponds to the reverse bits of pi.
132*/
133Classic_McEliece_GF from_pi(CmcePermutationElement pi_elem, CmceGfMod modulus, size_t m) {
134 auto reversed_bits = ct_reverse_bits(pi_elem.get());
135 reversed_bits >>= (sizeof(uint16_t) * 8 - m);
136 return Classic_McEliece_GF(CmceGfElem(reversed_bits), modulus);
137}
138
139/**
140 * @brief Part of field ordering generation according to ISO 9.2.10
141 */
142secure_vector<uint16_t> composeinv(std::span<const uint16_t> c, std::span<const uint16_t> pi) {
143 auto pi_c_zipped = zip(pi, c);
144 CMCE_CT::bitonic_sort_pair(std::span(pi_c_zipped));
145 // Extract c from the sorted vector
147 std::transform(pi_c_zipped.begin(), pi_c_zipped.end(), std::back_inserter(c_sorted), [](const auto& pair) {
148 return pair.second;
149 });
150
151 return c_sorted;
152}
153
154// p,q = composeinv(p,q),composeinv(q,p)
155void simultaneous_composeinv(secure_vector<uint16_t>& p, secure_vector<uint16_t>& q) {
156 auto p_new = composeinv(p, q);
157 q = composeinv(q, p);
158 p = std::move(p_new);
159}
160
161/**
162 * @brief Generate control bits as in ISO 9.2.10.
163 *
164 * TODO: This function can be optimized (see Classic McEliece reference implementation)
165 */
166secure_vector<uint16_t> generate_control_bits_internal(const secure_vector<uint16_t>& pi) {
167 const auto n = pi.size();
169 const size_t m = ceil_log2(n);
170
171 if(m == 1) {
172 return secure_vector<uint16_t>({pi.at(0)});
173 }
175 for(size_t x = 0; x < n; ++x) {
176 p.at(x) = pi.at(x ^ 1);
177 }
179 for(size_t x = 0; x < n; ++x) {
180 q.at(x) = pi.at(x) ^ 1;
181 }
182
183 secure_vector<uint16_t> range_n(n);
184 std::iota(range_n.begin(), range_n.end(), static_cast<uint16_t>(0));
185 auto piinv = composeinv(range_n, pi);
186
187 simultaneous_composeinv(p, q);
188
190 for(uint16_t x = 0; static_cast<size_t>(x) < n; ++x) {
191 c.at(x) = CMCE_CT::min(x, p.at(x));
192 }
193
194 simultaneous_composeinv(p, q);
195
196 for(size_t i = 1; i < m - 1; ++i) {
197 auto cp = composeinv(c, q);
198 simultaneous_composeinv(p, q);
199 for(size_t x = 0; x < n; ++x) {
200 c.at(x) = CMCE_CT::min(c.at(x), cp.at(x));
201 }
202 }
203
205 for(size_t j = 0; j < n / 2; ++j) {
206 f.at(j) = c.at(2 * j) % 2;
207 }
208
210 for(uint16_t x = 0; size_t(x) < n; ++x) {
211 big_f.at(x) = x ^ f.at(x / 2);
212 }
213
214 auto fpi = composeinv(big_f, piinv);
215
217 for(size_t k = 0; k < n / 2; ++k) {
218 l.at(k) = fpi.at(2 * k) % 2;
219 }
220
222 for(uint16_t y = 0; size_t(y) < n; ++y) {
223 big_l.at(y) = y ^ l.at(y / 2);
224 }
225
226 auto big_m = composeinv(fpi, big_l);
227
228 secure_vector<uint16_t> subm0(n / 2);
229 secure_vector<uint16_t> subm1(n / 2);
230 for(size_t j = 0; j < n / 2; ++j) {
231 subm0.at(j) = big_m.at(2 * j) / 2;
232 subm1.at(j) = big_m.at(2 * j + 1) / 2;
233 }
234
235 auto subz0 = generate_control_bits_internal(subm0);
236 auto subz1 = generate_control_bits_internal(subm1);
237
238 secure_vector<uint16_t> z(subz0.size() + subz1.size());
239 for(size_t j = 0; j < subz0.size(); ++j) {
240 z.at(2 * j) = subz0.at(j);
241 z.at(2 * j + 1) = subz1.at(j);
242 }
243
244 return concat(f, z, l);
245}
246
247CT::Choice ct_has_adjacent_duplicates(std::span<const uint32_t> vec) {
249 for(size_t i = 0; i < vec.size() - 1; ++i) {
250 mask |= CT::Mask<uint32_t>::is_equal(vec[i], vec[i + 1]);
251 }
252 return mask.as_choice();
253}
254
255} // anonymous namespace
256
257std::optional<Classic_McEliece_Field_Ordering> Classic_McEliece_Field_Ordering::create_field_ordering(
259 BOTAN_ARG_CHECK(random_bits.size() == (params.sigma2() * params.q()) / 8, "Wrong random bits size");
260
261 auto a = load_le<secure_vector<uint32_t>>(random_bits); // contains a_0, a_1, ...
262 auto [sorted_a, pi] = create_pi(std::move(a));
263 if(ct_has_adjacent_duplicates(sorted_a).as_bool()) {
264 return std::nullopt;
265 }
266
267 return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
268}
269
270std::vector<Classic_McEliece_GF> Classic_McEliece_Field_Ordering::alphas(size_t n) const {
271 BOTAN_ASSERT_NOMSG(m_poly_f.get() != 0);
272 BOTAN_ASSERT_NOMSG(m_pi.size() >= n);
273
274 std::vector<Classic_McEliece_GF> n_alphas_vec;
275
276 std::transform(m_pi.begin(), m_pi.begin() + n, std::back_inserter(n_alphas_vec), [this](uint16_t pi_elem) {
277 return from_pi(CmcePermutationElement(pi_elem), m_poly_f, Classic_McEliece_GF::log_q_from_mod(m_poly_f));
278 });
279
280 return n_alphas_vec;
281}
282
284 // Each vector element contains one bit of the control bits
285 const auto control_bits_as_words = generate_control_bits_internal(m_pi.get());
286 auto control_bits = secure_bitvector(control_bits_as_words.size());
287 for(size_t i = 0; i < control_bits.size(); ++i) {
288 control_bits.at(i) = control_bits_as_words.at(i) != 0;
289 }
290
291 return control_bits;
292}
293
294// Based on the Python code "permutation(c)" from Bernstein
295// "Verified fast formulas for control bits for permutation networks"
297 const Classic_McEliece_Parameters& params, const secure_bitvector& control_bits) {
298 BOTAN_ASSERT_NOMSG(control_bits.size() == (2 * params.m() - 1) << (params.m() - 1));
299 const uint16_t n = uint16_t(1) << params.m();
300 CmcePermutation pi(n);
301 std::iota(pi.begin(), pi.end(), static_cast<uint16_t>(0));
302 for(size_t i = 0; i < 2 * params.m() - 1; ++i) {
303 const size_t gap = size_t(1) << std::min(i, 2 * params.m() - 2 - i);
304 for(size_t j = 0; j < size_t(n) / 2; ++j) {
305 const size_t pos = (j % gap) + 2 * gap * (j / gap);
306 auto mask = CT::Mask<uint16_t>::expand_bool(control_bits[i * n / 2 + j]);
307 mask.conditional_swap(pi[pos], pi[pos + gap]);
308 }
309 }
310
311 return Classic_McEliece_Field_Ordering(std::move(pi), params.poly_f());
312}
313
315 const CmceColumnSelection& pivots) {
316 auto col_offset = params.pk_no_rows() - Classic_McEliece_Parameters::mu();
317
318 for(size_t p_idx = 1; p_idx <= Classic_McEliece_Parameters::mu(); ++p_idx) {
319 size_t p_counter = 0;
320 for(size_t col = 0; col < Classic_McEliece_Parameters::nu(); ++col) {
321 auto mask_is_pivot_set = CT::Mask<size_t>::expand_bool(pivots.at(col));
322 p_counter += mask_is_pivot_set.if_set_return(1);
323 auto mask_is_current_pivot = CT::Mask<size_t>::is_equal(p_idx, p_counter);
324 (mask_is_pivot_set & mask_is_current_pivot)
325 .conditional_swap(m_pi.get().at(col_offset + col), m_pi.get().at(col_offset + p_idx - 1));
326 }
327 }
328}
329
330} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:129
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
void conditional_swap(U &x, U &y) const
Definition ct_utils.h:613
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:470
static constexpr Mask< T > expand_bool(bool v)
Definition ct_utils.h:425
static constexpr Mask< T > is_gt(T x, T y)
Definition ct_utils.h:486
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:478
static constexpr Mask< T > cleared()
Definition ct_utils.h:415
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}.
Represents an element of the finite field GF(q) for q = 2^m.
Definition cmce_gf.h:30
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:403
decltype(auto) begin() noexcept(noexcept(this->get().begin()))
Definition strong_type.h:92
decltype(auto) end() noexcept(noexcept(this->get().end()))
Definition strong_type.h:96
auto at(size_type i) const
Definition bitvector.h:1371
BOTAN_FORCE_INLINE constexpr bool is_power_of_2(T arg)
Definition bit_ops.h:55
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:1303
Strong< uint16_t, struct CmcePermutationElement_ > CmcePermutationElement
Represents an element of a permutation (pi in spec). Used in field ordering creation.
Definition cmce_types.h:25
constexpr T ct_reverse_bits(T b)
Definition bit_ops.h:232
Strong< secure_bitvector, struct CmceColumnSelection_ > CmceColumnSelection
Represents c of private key.
Definition cmce_types.h:46
constexpr uint8_t ceil_log2(T x)
Definition bit_ops.h:133
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:254
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:495
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69