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