• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 23795492250

31 Mar 2026 11:32AM UTC coverage: 89.527% (-0.006%) from 89.533%
23795492250

push

github

randombit
Update for 3.11.1

105469 of 117807 relevant lines covered (89.53%)

11603183.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.39
/src/lib/pubkey/classic_mceliece/cmce_encaps.cpp
1
/*
2
 * Classic McEliece Encapsulation
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_encaps.h>
13

14
#include <botan/rng.h>
15
#include <botan/internal/buffer_slicer.h>
16
#include <botan/internal/buffer_stuffer.h>
17

18
namespace Botan {
19

20
CmceCodeWord Classic_McEliece_Encryptor::encode(const Classic_McEliece_Parameters& params,
59✔
21
                                                const CmceErrorVector& e,
22
                                                const Classic_McEliece_Matrix& mat) const {
23
   return mat.mul(params, e);
59✔
24
}
25

26
std::optional<CmceErrorVector> Classic_McEliece_Encryptor::fixed_weight_vector_gen(
798✔
27
   const Classic_McEliece_Parameters& params, RandomNumberGenerator& rng) const {
28
   const auto rand = rng.random_vec((params.sigma1() / 8) * params.tau());
798✔
29
   CT::poison(rand);
798✔
30
   const uint16_t mask_m = (uint32_t(1) << params.m()) - 1;  // Only take m least significant bits
798✔
31
   secure_vector<uint16_t> a_values;
798✔
32
   a_values.reserve(params.tau());
798✔
33
   BufferSlicer rand_slicer(rand);
798✔
34

35
   // Steps 2 & 3: Create d_j from uniform random bits. The first t d_j entries
36
   //              in range {0,...,n-1} are defined as a_0,...,a_(t-1). ...
37
   for(size_t j = 0; j < params.tau(); ++j) {
111,232✔
38
      auto d = load_le<uint16_t>(rand_slicer.take(params.sigma1() / 8).data(), 0);
110,434✔
39
      // This is not CT, but neither is the reference implementation here.
40
      // This side channel only leaks which random elements are selected and which are dropped,
41
      // but no information about their content is leaked.
42
      d &= mask_m;
110,434✔
43
      const bool d_in_range = d < params.n();
110,434✔
44
      CT::unpoison(d_in_range);
110,434✔
45
      if(d_in_range && a_values.size() < params.t()) {
110,434✔
46
         a_values.push_back(d);
58,289✔
47
      }
48
   }
49
   if(a_values.size() < params.t()) {
798✔
50
      // Step 3: ... If fewer than t of such elements exist restart
51
      return std::nullopt;
×
52
   }
53

54
   // Step 4: Restart if not all a_i are distinct
55
   for(size_t i = 1; i < params.t(); ++i) {
13,511✔
56
      for(size_t j = 0; j < i; ++j) {
645,146✔
57
         const bool a_i_j_equal = a_values.at(i) == a_values.at(j);
632,433✔
58
         CT::unpoison(a_i_j_equal);
632,433✔
59
         if(a_i_j_equal) {
632,433✔
60
            return std::nullopt;
739✔
61
         }
62
      }
63
   }
64

65
   secure_vector<uint8_t> a_value_byte(params.t());
59✔
66
   secure_vector<uint8_t> e_bytes(ceil_tobytes(params.n()));
59✔
67

68
   // Step 5: Set all bits of e at the positions of a_values
69
   // Prepare the associated byte in e_bytes that is represented by each bit index in a_values
70
   // if we e is represented as a byte vector
71
   for(size_t j = 0; j < a_values.size(); ++j) {
6,223✔
72
      a_value_byte[j] = 1 << (a_values[j] % 8);
6,164✔
73
   }
74

75
   for(size_t i = 0; i < params.n() / 8; ++i) {
43,687✔
76
      for(size_t j = 0; j < a_values.size(); ++j) {
4,949,092✔
77
         // If the current byte is the one that is represented by the current bit index in a_values
78
         // then set the bit in e_bytes (in-byte position prepared above)
79
         auto mask = CT::Mask<uint16_t>::is_equal(static_cast<uint16_t>(i), static_cast<uint16_t>(a_values[j] >> 3));
4,905,464✔
80
         e_bytes[i] |= mask.if_set_return(a_value_byte[j]);
4,905,464✔
81
      }
82
   }
83

84
   return CmceErrorVector(secure_bitvector(e_bytes, params.n()));
59✔
85
}
1,714✔
86

87
void Classic_McEliece_Encryptor::raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
60✔
88
                                                 std::span<uint8_t> out_shared_key,
89
                                                 RandomNumberGenerator& rng) {
90
   BOTAN_ARG_CHECK(out_encapsulated_key.size() == m_key->params().ciphertext_size(),
60✔
91
                   "Incorrect encapsulated key output length");
92
   BOTAN_ARG_CHECK(out_shared_key.size() == m_key->params().hash_out_bytes(), "Incorrect shared key output length");
60✔
93

94
   const auto& params = m_key->params();
60✔
95

96
   // Call fixed_weight until it is successful to
97
   // create a random error vector e of weight tau
98
   const CmceErrorVector e = [&] {
179✔
99
      // Emergency abort in case unexpected logical error to prevent endless loops
100
      //   Success probability: >24% per attempt (25% that elements are distinct * 96% enough elements are in range)
101
      //   => 647 attempts for 2^(-256) fail probability
102
      constexpr size_t MAX_ATTEMPTS = 647;
60✔
103
      for(size_t attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
799✔
104
         if(auto maybe_e = fixed_weight_vector_gen(params, rng)) {
798✔
105
            return maybe_e.value();
118✔
106
         }
798✔
107
      }
108
      throw Internal_Error("Cannot created fixed weight vector. Is your RNG broken?");
1✔
109
   }();
60✔
110

111
   auto hash_func = params.hash_func();
59✔
112

113
   BufferStuffer big_c_stuf(out_encapsulated_key);
59✔
114
   const auto e_bytes = e.get().to_bytes();
59✔
115
   // Compute and store ciphertext C/C_0 from spec
116
   const auto big_c_0 = encode(params, e, m_key->matrix());
59✔
117
   big_c_0.to_bytes(big_c_stuf.next(ceil_tobytes(big_c_0.size())));
59✔
118
   if(params.is_pc()) {
59✔
119
      // Compute and store ciphertext C_1 from spec
120
      hash_func->update(0x02);
18✔
121
      hash_func->update(e_bytes);
18✔
122
      hash_func->final(big_c_stuf.next(hash_func->output_length()));
18✔
123
   }
124
   BOTAN_ASSERT_NOMSG(big_c_stuf.full());
59✔
125

126
   // Compute K = Hash(1,e,C) from spec
127
   hash_func->update(0x01);
59✔
128
   hash_func->update(e_bytes);
59✔
129
   hash_func->update(out_encapsulated_key);
59✔
130
   hash_func->final(out_shared_key);
59✔
131
   CT::unpoison_all(out_encapsulated_key, out_shared_key);
59✔
132
}
236✔
133

134
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc