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

randombit / botan / 21943658802

12 Feb 2026 05:52AM UTC coverage: 90.063% (-0.006%) from 90.069%
21943658802

push

github

web-flow
Merge pull request #5292 from Rohde-Schwarz/refactor/tls12_13_server_hello

Refactor: Disentangle Server_Hello implementations

102227 of 113506 relevant lines covered (90.06%)

11565329.36 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,
48✔
21
                                                const CmceErrorVector& e,
22
                                                const Classic_McEliece_Matrix& mat) const {
23
   return mat.mul(params, e);
48✔
24
}
25

26
std::optional<CmceErrorVector> Classic_McEliece_Encryptor::fixed_weight_vector_gen(
766✔
27
   const Classic_McEliece_Parameters& params, RandomNumberGenerator& rng) const {
28
   const auto rand = rng.random_vec((params.sigma1() / 8) * params.tau());
766✔
29
   CT::poison(rand);
766✔
30
   const uint16_t mask_m = (uint32_t(1) << params.m()) - 1;  // Only take m least significant bits
766✔
31
   secure_vector<uint16_t> a_values;
766✔
32
   a_values.reserve(params.tau());
766✔
33
   BufferSlicer rand_slicer(rand);
766✔
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) {
106,832✔
38
      auto d = load_le<uint16_t>(rand_slicer.take(params.sigma1() / 8).data(), 0);
106,066✔
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;
106,066✔
43
      const bool d_in_range = d < params.n();
106,066✔
44
      CT::unpoison(d_in_range);
106,066✔
45
      if(d_in_range && a_values.size() < params.t()) {
106,066✔
46
         a_values.push_back(d);
55,401✔
47
      }
48
   }
49
   if(a_values.size() < params.t()) {
766✔
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) {
11,810✔
56
      for(size_t j = 0; j < i; ++j) {
595,977✔
57
         const bool a_i_j_equal = a_values.at(i) == a_values.at(j);
584,933✔
58
         CT::unpoison(a_i_j_equal);
584,933✔
59
         if(a_i_j_equal) {
584,933✔
60
            return std::nullopt;
718✔
61
         }
62
      }
63
   }
64

65
   secure_vector<uint8_t> a_value_byte(params.t());
48✔
66
   secure_vector<uint8_t> e_bytes(ceil_tobytes(params.n()));
48✔
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) {
5,508✔
72
      a_value_byte[j] = 1 << (a_values[j] % 8);
5,460✔
73
   }
74

75
   for(size_t i = 0; i < params.n() / 8; ++i) {
38,880✔
76
      for(size_t j = 0; j < a_values.size(); ++j) {
4,637,352✔
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,598,520✔
80
         e_bytes[i] |= mask.if_set_return(a_value_byte[j]);
4,598,520✔
81
      }
82
   }
83

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

87
void Classic_McEliece_Encryptor::raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
49✔
88
                                                 std::span<uint8_t> out_shared_key,
89
                                                 RandomNumberGenerator& rng) {
90
   BOTAN_ARG_CHECK(out_encapsulated_key.size() == m_key->params().ciphertext_size(),
49✔
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");
49✔
93

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

96
   // Call fixed_weight until it is successful to
97
   // create a random error vector e of weight tau
98
   const CmceErrorVector e = [&] {
146✔
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;
49✔
103
      for(size_t attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
767✔
104
         if(auto maybe_e = fixed_weight_vector_gen(params, rng)) {
766✔
105
            return maybe_e.value();
96✔
106
         }
766✔
107
      }
108
      throw Internal_Error("Cannot created fixed weight vector. Is your RNG broken?");
1✔
109
   }();
49✔
110

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

113
   BufferStuffer big_c_stuf(out_encapsulated_key);
48✔
114
   const auto e_bytes = e.get().to_bytes();
48✔
115
   // Compute and store ciphertext C/C_0 from spec
116
   const auto big_c_0 = encode(params, e, m_key->matrix());
48✔
117
   big_c_0.to_bytes(big_c_stuf.next(ceil_tobytes(big_c_0.size())));
48✔
118
   if(params.is_pc()) {
48✔
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());
48✔
125

126
   // Compute K = Hash(1,e,C) from spec
127
   hash_func->update(0x01);
48✔
128
   hash_func->update(e_bytes);
48✔
129
   hash_func->update(out_encapsulated_key);
48✔
130
   hash_func->final(out_shared_key);
48✔
131
   CT::unpoison_all(out_encapsulated_key, out_shared_key);
48✔
132
}
192✔
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