Botan 3.7.1
Crypto and TLS for C&
cmce_decaps.cpp
Go to the documentation of this file.
1/*
2 * Classic McEliece Decapsulation
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_decaps.h>
13
14namespace Botan {
15
16Classic_McEliece_Polynomial Classic_McEliece_Decryptor::compute_goppa_syndrome(
17 const Classic_McEliece_Parameters& params,
18 const Classic_McEliece_Minimal_Polynomial& goppa_poly,
19 const Classic_McEliece_Field_Ordering& ordering,
20 const secure_bitvector& code_word) const {
21 BOTAN_ASSERT(params.n() == code_word.size(), "Correct code word size");
22 std::vector<Classic_McEliece_GF> syndrome(2 * params.t(), params.gf(CmceGfElem(0)));
23
24 auto alphas = ordering.alphas(params.n());
25
26 for(size_t i = 0; i < params.n(); ++i) {
27 auto g_alpha = goppa_poly(alphas[i]);
28 auto r = (g_alpha * g_alpha).inv();
29
30 auto c_mask = GF_Mask::expand(static_cast<bool>(code_word.at(i)));
31
32 for(size_t j = 0; j < 2 * params.t(); ++j) {
33 syndrome[j] += c_mask.if_set_return(r);
34 r = r * alphas[i];
35 }
36 }
37
38 return Classic_McEliece_Polynomial(syndrome);
39}
40
41Classic_McEliece_Polynomial Classic_McEliece_Decryptor::berlekamp_massey(
42 const Classic_McEliece_Parameters& params, const Classic_McEliece_Polynomial& syndrome) const {
43 // Represents coefficients of corresponding polynomials
44 std::vector<Classic_McEliece_GF> big_c(params.t() + 1, params.gf(CmceGfElem(0)));
45 std::vector<Classic_McEliece_GF> big_b(params.t() + 1, params.gf(CmceGfElem(0)));
46
47 auto b = params.gf(CmceGfElem(1));
48
49 // Start with x^m for m=1, see pseudocode of https://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm
50 big_b.at(1) = CmceGfElem(1);
51 big_c.at(0) = CmceGfElem(1);
52
53 for(size_t big_n = 0, big_l = 0; big_n < 2 * params.t(); ++big_n) {
54 auto d = params.gf(CmceGfElem(0));
55 for(size_t i = 0; i <= std::min(big_n, params.t()); ++i) {
56 d += big_c.at(i) * syndrome.coef_at(big_n - i);
57 }
58
59 // Pseudocode branch if (d == 0)
60 auto d_not_zero = GF_Mask::expand(d);
61
62 // Pseudocode branch else if (2* L <= N)
63 auto adjust_big_c = GF_Mask(CT::Mask<uint16_t>::is_lte(uint16_t(2 * big_l), uint16_t(big_n)));
64 adjust_big_c &= d_not_zero;
65
66 auto big_t = big_c; // Copy
67 auto f = d / b;
68
69 for(size_t i = 0; i <= params.t(); ++i) {
70 // Occurs for all other d!=0 branches in the pseudocode
71 big_c.at(i) += d_not_zero.if_set_return((f * big_b.at(i)));
72 }
73
74 big_l = adjust_big_c.select(uint16_t((big_n + 1) - big_l), uint16_t(big_l));
75
76 for(size_t i = 0; i <= params.t(); ++i) {
77 big_b.at(i) = adjust_big_c.select(big_t.at(i), big_b.at(i));
78 }
79
80 b = adjust_big_c.select(d, b);
81
82 // Rotate big_b one to the right (multiplies with x), replaces increments of m in pseudocode
83 std::rotate(big_b.rbegin(), big_b.rbegin() + 1, big_b.rend());
84 }
85
86 std::reverse(big_c.begin(), big_c.end());
87
88 return Classic_McEliece_Polynomial(big_c);
89}
90
91std::pair<CT::Mask<uint8_t>, CmceErrorVector> Classic_McEliece_Decryptor::decode(CmceCodeWord big_c) const {
92 BOTAN_ASSERT(big_c.size() == m_key->params().m() * m_key->params().t(), "Correct ciphertext input size");
93 big_c.resize(m_key->params().n());
94
95 const auto syndrome =
96 compute_goppa_syndrome(m_key->params(), m_key->g(), m_key->field_ordering(), big_c.as<secure_bitvector>());
97 const auto locator = berlekamp_massey(m_key->params(), syndrome);
98
99 std::vector<Classic_McEliece_GF> images;
100 const auto alphas = m_key->field_ordering().alphas(m_key->params().n());
101 std::transform(
102 alphas.begin(), alphas.end(), std::back_inserter(images), [&](const auto& alpha) { return locator(alpha); });
103
104 // Obtain e and check whether wt(e) = t. locator(alpha_i) = 0 <=> error at position i
106 e.get().reserve(m_key->params().n());
107 auto decode_success = CT::Mask<uint8_t>::set(); // Avoid bool to avoid possible compiler optimizations
108 for(const auto& image : images) {
109 e.push_back(GF_Mask::is_zero(image).as_bool());
110 }
111 decode_success &= CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(e.hamming_weight(), m_key->params().t()));
112
113 // Check the error vector by checking H'C = H'e <=> H'(C + e) = 0; see guide for implementors Sec. 6.3
114 const auto syndrome_from_e = compute_goppa_syndrome(m_key->params(), m_key->g(), m_key->field_ordering(), e.get());
115 auto syndromes_are_eq = GF_Mask::set();
116 for(size_t i = 0; i < syndrome.degree() - 1; ++i) {
117 syndromes_are_eq &= GF_Mask::is_equal(syndrome.coef_at(i), syndrome_from_e.coef_at(i));
118 }
119
120 decode_success &= syndromes_are_eq.elem_mask();
121
122 return {decode_success, std::move(e)};
123}
124
125void Classic_McEliece_Decryptor::raw_kem_decrypt(std::span<uint8_t> out_shared_key,
126 std::span<const uint8_t> encapsulated_key) {
127 BOTAN_ARG_CHECK(out_shared_key.size() == m_key->params().hash_out_bytes(), "Invalid shared key output size");
128 BOTAN_ARG_CHECK(encapsulated_key.size() == m_key->params().ciphertext_size(), "Invalid ciphertext size");
129
130 auto scope = CT::scoped_poison(*m_key);
131
132 auto [ct, c1] = [&]() -> std::pair<CmceCodeWord, std::span<const uint8_t>> {
133 if(m_key->params().is_pc()) {
134 BufferSlicer encaps_key_slicer(encapsulated_key);
135 auto c0_ret = encaps_key_slicer.take(m_key->params().encode_out_size());
136 auto c1_ret = encaps_key_slicer.take(m_key->params().hash_out_bytes());
137 BOTAN_ASSERT_NOMSG(encaps_key_slicer.empty());
138 return {CmceCodeWord(secure_bitvector(c0_ret, m_key->params().m() * m_key->params().t())), c1_ret};
139 } else {
140 return {CmceCodeWord(secure_bitvector(encapsulated_key, m_key->params().m() * m_key->params().t())), {}};
141 }
142 }();
143
144 auto [decode_success_mask, maybe_e] = decode(ct);
145
146 secure_vector<uint8_t> e_bytes(m_key->s().size());
147 decode_success_mask.select_n(e_bytes.data(), maybe_e.get().to_bytes().data(), m_key->s().data(), m_key->s().size());
148 uint8_t b = decode_success_mask.select(1, 0);
149
150 auto hash_func = m_key->params().hash_func();
151
152 if(m_key->params().is_pc()) {
153 hash_func->update(0x02);
154 hash_func->update(e_bytes);
155 const auto c1_p = hash_func->final_stdvec();
156 const CT::Mask<uint8_t> eq_mask = CT::is_equal(c1.data(), c1_p.data(), c1.size());
157 eq_mask.select_n(e_bytes.data(), e_bytes.data(), m_key->s().data(), m_key->s().size());
158 b = eq_mask.select(b, 0);
159 }
160
161 hash_func->update(b);
162 hash_func->update(e_bytes);
163 hash_func->update(encapsulated_key);
164 hash_func->final(out_shared_key);
165 CT::unpoison(out_shared_key);
166}
167
168} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
bool empty() const
Definition stl_util.h:129
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:98
static constexpr Mask< T > is_lte(T x, T y)
Definition ct_utils.h:474
static constexpr Mask< T > set()
Definition ct_utils.h:398
constexpr void select_n(T output[], const T x[], const T y[], size_t len) const
Definition ct_utils.h:576
constexpr T select(T x, T y) const
Definition ct_utils.h:559
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:453
void raw_kem_decrypt(std::span< uint8_t > out_shared_key, std::span< const uint8_t > encapsulated_key) override
static GF_Mask expand(T v)
Definition cmce_gf.h:156
static GF_Mask is_equal(Classic_McEliece_GF a, Classic_McEliece_GF b)
Definition cmce_gf.h:168
static GF_Mask set()
Definition cmce_gf.h:172
static GF_Mask is_zero(Classic_McEliece_GF v)
Definition cmce_gf.h:162
constexpr auto scoped_poison(const Ts &... xs)
Definition ct_utils.h:216
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:788
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:64
Strong< secure_bitvector, struct CmceCodeWord_ > CmceCodeWord
Represents C of decapsulation.
Definition cmce_types.h:52
bitvector_base< secure_allocator > secure_bitvector
Definition bitvector.h:1297
Strong< secure_bitvector, struct CmceErrorVector_ > CmceErrorVector
Represents e of encapsulation.
Definition cmce_types.h:49
Strong< uint16_t, struct CmceGfElem_ > CmceGfElem
Represents a GF(q) element.
Definition cmce_types.h:19
const SIMD_8x32 & b
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61