From fbcc57efcdf184c4cfae053a1dda0496ff31bf12 Mon Sep 17 00:00:00 2001 From: Fabian Albert Date: Tue, 23 Jul 2024 10:57:45 +0200 Subject: [PATCH] Use Valgrind for CMCE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use CT::poison/unpoison for CMCE. Simultaneously, fixes some constant-time issues in the CMCE implementation that may leak some information about the pivots (for the semi-systematic matrix form). Co-Authored-By: René Meusel --- src/lib/pubkey/classic_mceliece/cmce.cpp | 10 +- .../pubkey/classic_mceliece/cmce_decaps.cpp | 3 + .../pubkey/classic_mceliece/cmce_encaps.cpp | 10 +- .../classic_mceliece/cmce_field_ordering.cpp | 38 ++++--- .../classic_mceliece/cmce_field_ordering.h | 4 + src/lib/pubkey/classic_mceliece/cmce_gf.h | 8 +- .../classic_mceliece/cmce_keys_internal.cpp | 10 +- .../classic_mceliece/cmce_keys_internal.h | 6 ++ .../pubkey/classic_mceliece/cmce_matrix.cpp | 99 +++++++++++++------ src/lib/pubkey/classic_mceliece/cmce_matrix.h | 2 + src/lib/pubkey/classic_mceliece/cmce_poly.cpp | 8 +- src/lib/pubkey/classic_mceliece/cmce_poly.h | 4 + src/lib/utils/bitvector.h | 14 ++- 13 files changed, 158 insertions(+), 58 deletions(-) diff --git a/src/lib/pubkey/classic_mceliece/cmce.cpp b/src/lib/pubkey/classic_mceliece/cmce.cpp index 657bb8f3f8c..aaf24fb0c1a 100644 --- a/src/lib/pubkey/classic_mceliece/cmce.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -87,12 +88,18 @@ std::unique_ptr Classic_McEliece_PublicKey::create_kem_e Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(RandomNumberGenerator& rng, Classic_McEliece_Parameter_Set param_set) { auto params = Classic_McEliece_Parameters::create(param_set); - auto seed = rng.random_vec(params.seed_len()); + const auto seed = rng.random_vec(params.seed_len()); + CT::poison(seed); std::tie(m_private, m_public) = Classic_McEliece_KeyPair_Internal::generate(params, seed).decompose_to_pair(); + + BOTAN_ASSERT_NONNULL(m_private); + BOTAN_ASSERT_NONNULL(m_public); + CT::unpoison_all(*m_private, *m_public); } Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(std::span sk, Classic_McEliece_Parameter_Set param_set) { + auto scope = CT::scoped_poison(sk); auto params = Classic_McEliece_Parameters::create(param_set); auto sk_internal = Classic_McEliece_PrivateKeyInternal::from_bytes(params, sk); m_private = std::make_shared(std::move(sk_internal)); @@ -100,6 +107,7 @@ Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(std::span out_shared_k BOTAN_ARG_CHECK(out_shared_key.size() == m_key->params().hash_out_bytes(), "Invalid shared key output size"); BOTAN_ARG_CHECK(encapsulated_key.size() == m_key->params().ciphertext_size(), "Invalid ciphertext size"); + auto scope = CT::scoped_poison(*m_key); + auto [ct, c1] = [&]() -> std::pair> { if(m_key->params().is_pc()) { BufferSlicer encaps_key_slicer(encapsulated_key); @@ -160,6 +162,7 @@ void Classic_McEliece_Decryptor::raw_kem_decrypt(std::span out_shared_k hash_func->update(e_bytes); hash_func->update(encapsulated_key); hash_func->final(out_shared_key); + CT::unpoison(out_shared_key); } } // namespace Botan diff --git a/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp b/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp index e1a5570f206..de88c5430b3 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp @@ -23,6 +23,7 @@ CmceCodeWord Classic_McEliece_Encryptor::encode(const Classic_McEliece_Parameter std::optional Classic_McEliece_Encryptor::fixed_weight_vector_gen( const Classic_McEliece_Parameters& params, RandomNumberGenerator& rng) const { const auto rand = rng.random_vec((params.sigma1() / 8) * params.tau()); + CT::poison(rand); uint16_t mask_m = (uint32_t(1) << params.m()) - 1; // Only take m least significant bits secure_vector a_values; a_values.reserve(params.tau()); @@ -36,7 +37,9 @@ std::optional Classic_McEliece_Encryptor::fixed_weight_vector_g // This side channel only leaks which random elements are selected and which are dropped, // but no information about their content is leaked. d &= mask_m; - if(d < params.n() && a_values.size() < params.t()) { + bool d_in_range = d < params.n(); + CT::unpoison(d_in_range); + if(d_in_range && a_values.size() < params.t()) { a_values.push_back(d); } } @@ -48,7 +51,9 @@ std::optional Classic_McEliece_Encryptor::fixed_weight_vector_g // Step 4: Restart if not all a_i are distinct for(size_t i = 1; i < params.t(); ++i) { for(size_t j = 0; j < i; ++j) { - if(a_values.at(i) == a_values.at(j)) { + bool a_i_j_equal = a_values.at(i) == a_values.at(j); + CT::unpoison(a_i_j_equal); + if(a_i_j_equal) { return std::nullopt; } } @@ -120,6 +125,7 @@ void Classic_McEliece_Encryptor::raw_kem_encrypt(std::span out_encapsul hash_func->update(e_bytes); hash_func->update(out_encapsulated_key); hash_func->final(out_shared_key); + CT::unpoison_all(out_encapsulated_key, out_shared_key); } } // namespace Botan diff --git a/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp b/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp index 7847153c0da..d563edb3716 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce_field_ordering.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -97,6 +96,8 @@ std::pair, secure_vector> unzip(const std::span> enumerate(std::span vec) { + BOTAN_DEBUG_ASSERT(vec.size() < std::numeric_limits::max()); + std::vector> enumerated; std::transform(vec.begin(), vec.end(), std::back_inserter(enumerated), [ctr = uint16_t(0)](uint32_t elem) mutable { @@ -107,16 +108,20 @@ std::vector> enumerate(std::span v } /** -* @brief Create permutation pi as in (Section 8.2, Step 3). -*/ -CmcePermutation create_pi(std::span a) { + * @brief Create permutation pi as in (Section 8.2, Step 3). + * + * @param a The vector that is sorted + * + * @return (pi sorted after a, a sorted after pi) + */ +std::pair, CmcePermutation> create_pi(secure_vector a) { auto a_pi_zipped = enumerate(a); CMCE_CT::bitonic_sort_pair(std::span(a_pi_zipped)); CmcePermutation pi_sorted; std::tie(a, pi_sorted.get()) = unzip(std::span(a_pi_zipped)); - return pi_sorted; + return std::make_pair(a, pi_sorted); } /** @@ -124,16 +129,9 @@ CmcePermutation create_pi(std::span a) { * Corresponds to the reverse bits of pi. */ Classic_McEliece_GF from_pi(CmcePermutationElement pi_elem, CmceGfMod modulus, size_t m) { - std::bitset<16> bits(pi_elem.get()); - std::bitset<16> reversed_bits; - - for(int i = 0; i < 16; ++i) { - reversed_bits[i] = bits[15 - i]; - } - + auto reversed_bits = ct_reverse_bits(pi_elem.get()); reversed_bits >>= (sizeof(uint16_t) * 8 - m); - - return Classic_McEliece_GF(CmceGfElem(static_cast(reversed_bits.to_ulong())), modulus); + return Classic_McEliece_GF(CmceGfElem(reversed_bits), modulus); } /** @@ -244,6 +242,14 @@ secure_vector generate_control_bits_internal(const secure_vector vec) { + CT::Mask mask = CT::Mask::cleared(); + for(size_t i = 0; i < vec.size() - 1; ++i) { + mask |= CT::Mask::is_equal(vec[i], vec[i + 1]); + } + return mask.as_choice(); +} + } // anonymous namespace std::optional Classic_McEliece_Field_Ordering::create_field_ordering( @@ -251,8 +257,8 @@ std::optional Classic_McEliece_Field_Ordering:: BOTAN_ARG_CHECK(random_bits.size() == (params.sigma2() * params.q()) / 8, "Wrong random bits size"); auto a = load_le>(random_bits); // contains a_0, a_1, ... - auto pi = create_pi(a); - if(std::adjacent_find(a.begin(), a.end()) != a.end()) { + auto [sorted_a, pi] = create_pi(std::move(a)); + if(ct_has_adjacent_duplicates(sorted_a).as_bool()) { return std::nullopt; } diff --git a/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h b/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h index c5b366e2628..fed4bea44dd 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h +++ b/src/lib/pubkey/classic_mceliece/cmce_field_ordering.h @@ -99,6 +99,10 @@ class BOTAN_TEST_API Classic_McEliece_Field_Ordering { */ void permute_with_pivots(const Classic_McEliece_Parameters& params, const CmceColumnSelection& pivots); + void _const_time_poison() const { CT::poison(m_pi); } + + void _const_time_unpoison() const { CT::unpoison(m_pi); } + private: Classic_McEliece_Field_Ordering(CmcePermutation pi, CmceGfMod poly_f) : m_pi(std::move(pi)), m_poly_f(poly_f) {} diff --git a/src/lib/pubkey/classic_mceliece/cmce_gf.h b/src/lib/pubkey/classic_mceliece/cmce_gf.h index 722221d8b0e..e49588aceb6 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_gf.h +++ b/src/lib/pubkey/classic_mceliece/cmce_gf.h @@ -86,7 +86,7 @@ class BOTAN_TEST_API Classic_McEliece_GF { * @brief Divide the element by @p other in GF(q). Constant time. */ Classic_McEliece_GF operator/(Classic_McEliece_GF other) const { - BOTAN_ASSERT_NOMSG(m_modulus == other.m_modulus); + BOTAN_DEBUG_ASSERT(m_modulus == other.m_modulus); return *this * other.inv(); } @@ -94,7 +94,7 @@ class BOTAN_TEST_API Classic_McEliece_GF { * @brief Add @p other to the element. Constant time. */ Classic_McEliece_GF operator+(Classic_McEliece_GF other) const { - BOTAN_ASSERT_NOMSG(m_modulus == other.m_modulus); + BOTAN_DEBUG_ASSERT(m_modulus == other.m_modulus); return Classic_McEliece_GF(m_elem ^ other.m_elem, m_modulus); } @@ -102,7 +102,7 @@ class BOTAN_TEST_API Classic_McEliece_GF { * @brief Add @p other to the element. Constant time. */ Classic_McEliece_GF& operator+=(Classic_McEliece_GF other) { - BOTAN_ASSERT_NOMSG(m_modulus == other.m_modulus); + BOTAN_DEBUG_ASSERT(m_modulus == other.m_modulus); m_elem ^= other.m_elem; return *this; } @@ -119,7 +119,7 @@ class BOTAN_TEST_API Classic_McEliece_GF { * @brief Multiply the element by @p other in GF(q). Constant time. */ Classic_McEliece_GF& operator*=(Classic_McEliece_GF other) { - BOTAN_ASSERT_NOMSG(m_modulus == other.m_modulus); + BOTAN_DEBUG_ASSERT(m_modulus == other.m_modulus); *this = *this * other; return *this; } diff --git a/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp b/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp index 0f7a43d3fd0..8d1b3ba6f87 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce_keys_internal.cpp @@ -136,9 +136,13 @@ std::shared_ptr Classic_McEliece_PublicKeyIn throw Decoding_Error("Cannot create public key from private key. Private key is invalid."); } auto& [pk_matrix, pivot] = pk_matrix_and_pivot.value(); - if(!pivot.subvector(0, pivot.size() / 2).all() || !pivot.subvector(pivot.size() / 2).none()) { - // There should not be a pivot other than 0xff ff ff ff 00 00 00 00. Otherwise - // the gauss algorithm failed effectively. + + // There should not be a pivot other than 0xff ff ff ff 00 00 00 00. + // Otherwise the gauss algorithm failed effectively. + const auto pivot_is_valid = (CT::Mask::expand(pivot.subvector(0, pivot.size() / 2).all()) & + CT::Mask::expand(pivot.subvector(pivot.size() / 2).none())) + .as_choice(); + if(!pivot_is_valid.as_bool()) { throw Decoding_Error("Cannot create public key from private key. Private key is invalid."); } diff --git a/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h b/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h index 3b5353ba412..0f41a8a4341 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h +++ b/src/lib/pubkey/classic_mceliece/cmce_keys_internal.h @@ -67,6 +67,8 @@ class BOTAN_TEST_API Classic_McEliece_PublicKeyInternal { */ const Classic_McEliece_Parameters& params() const { return m_params; } + constexpr void _const_time_unpoison() const { CT::unpoison(m_matrix); } + private: Classic_McEliece_Parameters m_params; Classic_McEliece_Matrix m_matrix; @@ -168,6 +170,10 @@ class BOTAN_TEST_API Classic_McEliece_PrivateKeyInternal { */ bool check_key() const; + constexpr void _const_time_poison() const { CT::poison_all(m_delta, m_c, m_g, m_field_ordering, m_s); } + + constexpr void _const_time_unpoison() const { CT::unpoison_all(m_delta, m_c, m_g, m_field_ordering, m_s); } + private: Classic_McEliece_Parameters m_params; CmceKeyGenSeed m_delta; diff --git a/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp b/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp index 6bddce24a9e..021bfb029a4 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce_matrix.cpp @@ -25,13 +25,33 @@ using CmceMatrix = Strong, struct CmceMatrix_>; } // Anonymous namespace namespace { -size_t count_lsb_zeros(const secure_bitvector& n) { +// TODO: This is only a (slow) crook. Impement a bitvector method for this. +void bit_vector_insert_at(secure_bitvector& bv, size_t pos, uint64_t val) { + for(size_t i = 0; i < sizeof(uint64_t) * 8; ++i) { + bv[pos + i] = (val >> i) & 1; + } +} + +CT::Mask bit_at_mask(uint64_t val, size_t pos) { + return CT::Mask::expand((val >> pos) & 1); +} + +/// Swaps bit i with bit j in val +void swap_bits(uint64_t& val, size_t i, size_t j) { + uint64_t bit_i = (val >> i) & 1; + uint64_t bit_j = (val >> j) & 1; + uint64_t xor_sum = bit_i ^ bit_j; + val ^= (xor_sum << i); + val ^= (xor_sum << j); +} + +size_t count_lsb_zeros(uint64_t n) { size_t res = 0; - auto found_only_zeros = Botan::CT::Mask::set(); - for(const auto& bit : n) { - auto bit_set_mask = CT::Mask::expand(bit); + auto found_only_zeros = Botan::CT::Mask::set(); + for(size_t bit_pos = 0; bit_pos < sizeof(uint64_t) * 8; ++bit_pos) { + auto bit_set_mask = bit_at_mask(n, bit_pos); found_only_zeros &= ~bit_set_mask; - res += found_only_zeros.if_set_return(1); + res += static_cast(found_only_zeros.if_set_return(1)); } return res; @@ -67,16 +87,27 @@ CmceMatrix init_matrix_with_alphas(const Classic_McEliece_Parameters& params, std::optional move_columns(CmceMatrix& mat, const Classic_McEliece_Parameters& params) { BOTAN_ASSERT(mat.size() == params.pk_no_rows(), "Matrix has incorrect number of rows"); BOTAN_ASSERT(mat.get().at(0).size() == params.n(), "Matrix has incorrect number of columns"); - static_assert(Classic_McEliece_Parameters::nu() == 64, - "nu needs to be 64"); // Since we use uint64_t to represent rows in the mu x nu sub-matrix - // A 32x64 sub-matrix of mat containing the elements mat[m*t-32][m*t-32] at the top left - std::vector sub_mat(Classic_McEliece_Parameters::mu(), secure_bitvector()); + static_assert(Classic_McEliece_Parameters::nu() == 64, "nu needs to be 64"); const size_t pos_offset = params.pk_no_rows() - Classic_McEliece_Parameters::mu(); + // Get the area of the matrix that needs to be (potentially) swapped. + // Its the sub m*t x nu matrix at column m*t - mu. For const time reasons, + // the sub-matrix is represented as an array of uint64_ts, where the 1st + // bit is the least significant bit + std::vector matrix_swap_area; + matrix_swap_area.reserve(params.pk_no_rows()); + for(size_t i = 0; i < params.pk_no_rows(); ++i) { + matrix_swap_area.push_back(mat[i].subvector(pos_offset)); + } + + // To find which columns need to be swapped to allow for a systematic matrix form, we need to + // investigate how a gauss algorithm affects the last mu rows of the swap area. + std::array sub_mat; + // Extract the bottom mu x nu matrix at offset pos_offset for(size_t i = 0; i < Classic_McEliece_Parameters::mu(); i++) { - sub_mat.at(i) = mat[pos_offset + i].subvector(pos_offset, Classic_McEliece_Parameters::nu()); + sub_mat[i] = matrix_swap_area[pos_offset + i]; } std::array pivot_indices = {0}; // ctz_list @@ -91,7 +122,8 @@ std::optional move_columns(CmceMatrix& mat, const Classic_M row_acc |= sub_mat.at(next_row); } - if(row_acc.none()) { + auto semi_systematic_form_failed = CT::Mask::is_zero(row_acc); + if(semi_systematic_form_failed.as_choice().as_bool()) { // If the current row and all subsequent rows are zero // we cannot create a semi-systematic matrix return std::nullopt; @@ -100,13 +132,15 @@ std::optional move_columns(CmceMatrix& mat, const Classic_M // Using the row accumulator we can predict the index of the pivot // bit for the current row, i.e., the first index where we can set // the bit to one row by adding any subsequent row - pivot_indices.at(row_idx) = count_lsb_zeros(row_acc); + size_t current_pivot_idx = count_lsb_zeros(row_acc); + pivot_indices.at(row_idx) = current_pivot_idx; // Add subsequent rows to the current row, until the pivot // bit is set. for(size_t next_row = row_idx + 1; next_row < Classic_McEliece_Parameters::mu(); ++next_row) { - sub_mat.at(row_idx).ct_conditional_xor(!sub_mat.at(row_idx).at(pivot_indices.at(row_idx)).as_choice(), - sub_mat.at(next_row)); + // Add next row if the pivot bit is still zero + auto add_next_row_mask = ~bit_at_mask(sub_mat.at(row_idx), current_pivot_idx); + sub_mat.at(row_idx) ^= add_next_row_mask.if_set_return(sub_mat.at(next_row)); } // Add the (new) current row to all subsequent rows, where the leading @@ -116,8 +150,9 @@ std::optional move_columns(CmceMatrix& mat, const Classic_M // above the current one. However, here we only need to identify // the columns to swap. Therefore, we can ignore the upper rows. for(size_t next_row = row_idx + 1; next_row < Classic_McEliece_Parameters::mu(); ++next_row) { - sub_mat.at(next_row).ct_conditional_xor(sub_mat.at(next_row).at(pivot_indices.at(row_idx)).as_choice(), - sub_mat.at(row_idx)); + // Add the current row to next_row if the pivot bit of next_row is set + auto add_to_next_row_mask = bit_at_mask(sub_mat.at(next_row), current_pivot_idx); + sub_mat.at(next_row) ^= add_to_next_row_mask.if_set_return(sub_mat.at(row_idx)); } } @@ -130,18 +165,18 @@ std::optional move_columns(CmceMatrix& mat, const Classic_M } } - // Update matrix by swapping columns + // Swap the rows so the matrix can be transformed into systematic form for(size_t mat_row = 0; mat_row < params.pk_no_rows(); ++mat_row) { - for(size_t j = 0; j < Classic_McEliece_Parameters::mu(); ++j) { - // Swap bit j with bit pivot_indices[j] - size_t col_j = pos_offset + j; - size_t col_pivot_j = pos_offset + pivot_indices.at(j); - auto sum = mat[mat_row][col_j] ^ mat[mat_row][col_pivot_j]; - mat[mat_row][col_j] = mat[mat_row][col_j] ^ sum; - mat[mat_row][col_pivot_j] = mat[mat_row][col_pivot_j] ^ sum; + for(size_t col = 0; col < Classic_McEliece_Parameters::mu(); ++col) { + swap_bits(matrix_swap_area.at(mat_row), col, pivot_indices.at(col)); } } + // Reinsert the swapped columns into the matrix + for(size_t row = 0; row < params.pk_no_rows(); ++row) { + bit_vector_insert_at(mat[row].get(), pos_offset, matrix_swap_area[row]); + } + return pivots; } @@ -156,7 +191,9 @@ std::optional apply_gauss(const Classic_McEliece_Parameters for(size_t diag_pos = 0; diag_pos < params.pk_no_rows(); ++diag_pos) { if(params.is_f() && diag_pos == params.pk_no_rows() - params.mu()) { auto ret_pivots = move_columns(mat, params); - if(!ret_pivots) { + bool move_columns_failed = !ret_pivots.has_value(); + CT::unpoison(move_columns_failed); + if(move_columns_failed) { return std::nullopt; } else { pivots = std::move(ret_pivots.value()); @@ -173,7 +210,9 @@ std::optional apply_gauss(const Classic_McEliece_Parameters // If the current bit on the diagonal is not set at this point // the matrix is not systematic. We abort the computation in this case. - if(!mat[diag_pos].at(diag_pos)) { + bool diag_bit_zero = !mat[diag_pos].at(diag_pos); + CT::unpoison(diag_bit_zero); + if(diag_bit_zero) { return std::nullopt; } @@ -214,7 +253,9 @@ std::optional> Classic_M auto mat = init_matrix_with_alphas(params, field_ordering, g); auto pivots = apply_gauss(params, mat); - if(!pivots) { + auto gauss_failed = !pivots.has_value(); + CT::unpoison(gauss_failed); + if(gauss_failed) { return std::nullopt; } @@ -228,7 +269,9 @@ Classic_McEliece_Matrix::create_matrix_and_apply_pivots(const Classic_McEliece_P const Classic_McEliece_Minimal_Polynomial& g) { auto pk_matrix_and_pivots = create_matrix(params, field_ordering, g); - if(!pk_matrix_and_pivots) { + bool matrix_creation_failed = !pk_matrix_and_pivots.has_value(); + CT::unpoison(matrix_creation_failed); + if(matrix_creation_failed) { return std::nullopt; } diff --git a/src/lib/pubkey/classic_mceliece/cmce_matrix.h b/src/lib/pubkey/classic_mceliece/cmce_matrix.h index fde27b36f5c..3d524b56359 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_matrix.h +++ b/src/lib/pubkey/classic_mceliece/cmce_matrix.h @@ -104,6 +104,8 @@ class BOTAN_TEST_API Classic_McEliece_Matrix { */ CmceCodeWord mul(const Classic_McEliece_Parameters& params, const CmceErrorVector& e) const; + constexpr void _const_time_unpoison() const { CT::unpoison(m_mat_bytes); } + private: /// The bytes of the submatrix T const std::vector m_mat_bytes; // can we use bitvector? diff --git a/src/lib/pubkey/classic_mceliece/cmce_poly.cpp b/src/lib/pubkey/classic_mceliece/cmce_poly.cpp index 694ce2197a4..db2ea95af77 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_poly.cpp +++ b/src/lib/pubkey/classic_mceliece/cmce_poly.cpp @@ -16,7 +16,7 @@ namespace Botan { Classic_McEliece_GF Classic_McEliece_Polynomial::operator()(Classic_McEliece_GF a) const { - BOTAN_ASSERT(a.modulus() == coef_at(0).modulus(), "Galois fields match"); + BOTAN_DEBUG_ASSERT(a.modulus() == coef_at(0).modulus()); Classic_McEliece_GF r(CmceGfElem(0), a.modulus()); for(auto it = m_coef.rbegin(); it != m_coef.rend(); ++it) { @@ -93,8 +93,10 @@ std::optional Classic_McEliece_Polynomial_R } } - if(mat.at(j).coef_at(j).is_zero()) { - // Fail if not systematic. + const bool is_zero_at_diagonal = mat.at(j).coef_at(j).is_zero(); + CT::unpoison(is_zero_at_diagonal); + if(is_zero_at_diagonal) { + // Fail if not systematic. New rejection sampling iteration starts. return std::nullopt; } diff --git a/src/lib/pubkey/classic_mceliece/cmce_poly.h b/src/lib/pubkey/classic_mceliece/cmce_poly.h index d16b275774c..38f308b2504 100644 --- a/src/lib/pubkey/classic_mceliece/cmce_poly.h +++ b/src/lib/pubkey/classic_mceliece/cmce_poly.h @@ -65,6 +65,10 @@ class BOTAN_TEST_API Classic_McEliece_Polynomial { */ size_t degree() const { return m_coef.size() + 1; } + void _const_time_poison() const { CT::poison(m_coef); } + + void _const_time_unpoison() const { CT::unpoison(m_coef); } + private: std::vector m_coef; }; diff --git a/src/lib/utils/bitvector.h b/src/lib/utils/bitvector.h index ce6aaacc620..30468e1085b 100644 --- a/src/lib/utils/bitvector.h +++ b/src/lib/utils/bitvector.h @@ -820,6 +820,10 @@ class bitvector_base final { full_range_operation(maybe_xor, *this, unwrap_strong_type(other)); } + constexpr void _const_time_poison() const { CT::poison(m_blocks); } + + constexpr void _const_time_unpoison() const { CT::unpoison(m_blocks); } + /// @} /// @name Iterators @@ -841,7 +845,11 @@ class bitvector_base final { /// @} private: - void check_offset(size_type pos) const { BOTAN_ARG_CHECK(pos < m_bits, "Out of range"); } + void check_offset(size_type pos) const { + // BOTAN_ASSERT_NOMSG(!CT::is_poisoned(&m_bits, sizeof(m_bits))); + // BOTAN_ASSERT_NOMSG(!CT::is_poisoned(&pos, sizeof(pos))); + BOTAN_ARG_CHECK(pos < m_bits, "Out of range"); + } void zero_unused_bits() { const auto first_unused_bit = size(); @@ -1346,6 +1354,10 @@ class Strong_Adapter : public Container_Strong_Adapter_Base { auto capacity() const { return this->get().capacity(); } auto reserve(size_type n) { return this->get().reserve(n); } + + constexpr void _const_time_poison() const { this->get()._const_time_poison(); } + + constexpr void _const_time_unpoison() const { this->get()._const_time_unpoison(); } }; } // namespace detail