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

randombit / botan / 14239872910

03 Apr 2025 09:40AM UTC coverage: 91.372% (+0.01%) from 91.36%
14239872910

Pull #4817

github

web-flow
Merge 2fb60b484 into 88bd49921
Pull Request #4817: ML-KEM: Minimal Support for Expanded Keys

95268 of 104264 relevant lines covered (91.37%)

11587736.02 hits per line

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

94.59
/src/lib/pubkey/kyber/kyber_common/kyber.cpp
1
/*
2
 * Crystals Kyber key encapsulation mechanism
3
 * Based on the public domain reference implementation by the
4
 * designers (https://github.com/pq-crystals/kyber)
5
 *
6
 * Further changes
7
 * (C) 2021-2024 Jack Lloyd
8
 * (C) 2021-2022 Manuel Glaser and Michael Boric, Rohde & Schwarz Cybersecurity
9
 * (C) 2021-2022 René Meusel and Hannes Rantzsch, neXenio GmbH
10
 * (C) 2024      René Meusel, Fabian Albert, Rohde & Schwarz Cybersecurity
11
 *
12
 * Botan is released under the Simplified BSD License (see license.txt)
13
 */
14

15
#include <botan/kyber.h>
16

17
#include <botan/assert.h>
18
#include <botan/mem_ops.h>
19
#include <botan/pubkey.h>
20
#include <botan/rng.h>
21
#include <botan/secmem.h>
22

23
#include <botan/internal/ct_utils.h>
24
#include <botan/internal/fmt.h>
25
#include <botan/internal/kyber_algos.h>
26
#include <botan/internal/kyber_constants.h>
27
#include <botan/internal/kyber_keys.h>
28
#include <botan/internal/kyber_symmetric_primitives.h>
29
#include <botan/internal/kyber_types.h>
30
#include <botan/internal/stl_util.h>
31

32
#if defined(BOTAN_HAS_KYBER)
33
   #include <botan/internal/kyber_modern.h>
34
#endif
35

36
#if defined(BOTAN_HAS_KYBER_90S)
37
   #include <botan/internal/kyber_90s.h>
38
#endif
39

40
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
41
   #include <botan/internal/kyber_round3_impl.h>
42
#endif
43

44
#if defined(BOTAN_HAS_ML_KEM)
45
   #include <botan/internal/ml_kem_impl.h>
46
#endif
47

48
#include <memory>
49
#include <vector>
50

51
namespace Botan {
52

53
namespace {
54

55
KyberMode::Mode kyber_mode_from_string(std::string_view str) {
2,414✔
56
   if(str == "Kyber-512-90s-r3") {
2,414✔
57
      return KyberMode::Kyber512_90s;
218✔
58
   }
59
   if(str == "Kyber-768-90s-r3") {
2,196✔
60
      return KyberMode::Kyber768_90s;
216✔
61
   }
62
   if(str == "Kyber-1024-90s-r3") {
1,980✔
63
      return KyberMode::Kyber1024_90s;
215✔
64
   }
65
   if(str == "Kyber-512-r3") {
1,765✔
66
      return KyberMode::Kyber512_R3;
257✔
67
   }
68
   if(str == "Kyber-768-r3") {
1,508✔
69
      return KyberMode::Kyber768_R3;
216✔
70
   }
71
   if(str == "Kyber-1024-r3") {
1,292✔
72
      return KyberMode::Kyber1024_R3;
216✔
73
   }
74
   if(str == "ML-KEM-512") {
1,076✔
75
      return KyberMode::ML_KEM_512;
349✔
76
   }
77
   if(str == "ML-KEM-768") {
727✔
78
      return KyberMode::ML_KEM_768;
385✔
79
   }
80
   if(str == "ML-KEM-1024") {
342✔
81
      return KyberMode::ML_KEM_1024;
342✔
82
   }
83

84
   throw Invalid_Argument(fmt("'{}' is not a valid Kyber mode name", str));
×
85
}
86

87
}  // namespace
88

89
KyberMode::KyberMode(Mode mode) : m_mode(mode) {}
23✔
90

91
KyberMode::KyberMode(const OID& oid) : m_mode(kyber_mode_from_string(oid.to_formatted_string())) {}
573✔
92

93
KyberMode::KyberMode(std::string_view str) : m_mode(kyber_mode_from_string(str)) {}
1,841✔
94

95
OID KyberMode::object_identifier() const {
645✔
96
   return OID::from_string(to_string());
1,290✔
97
}
98

99
std::string KyberMode::to_string() const {
645✔
100
   switch(m_mode) {
645✔
101
      case Kyber512_90s:
68✔
102
         return "Kyber-512-90s-r3";
68✔
103
      case Kyber768_90s:
68✔
104
         return "Kyber-768-90s-r3";
68✔
105
      case Kyber1024_90s:
68✔
106
         return "Kyber-1024-90s-r3";
68✔
107
      case Kyber512_R3:
101✔
108
         return "Kyber-512-r3";
101✔
109
      case Kyber768_R3:
68✔
110
         return "Kyber-768-r3";
68✔
111
      case Kyber1024_R3:
68✔
112
         return "Kyber-1024-r3";
68✔
113
      case ML_KEM_512:
68✔
114
         return "ML-KEM-512";
68✔
115
      case ML_KEM_768:
68✔
116
         return "ML-KEM-768";
68✔
117
      case ML_KEM_1024:
68✔
118
         return "ML-KEM-1024";
68✔
119
   }
120

121
   BOTAN_ASSERT_UNREACHABLE();
×
122
}
123

124
bool KyberMode::is_90s() const {
3,400✔
125
   return m_mode == Kyber512_90s || m_mode == Kyber768_90s || m_mode == Kyber1024_90s;
3,400✔
126
}
127

128
bool KyberMode::is_modern() const {
1,404✔
129
   return !is_90s();
1,404✔
130
}
131

132
bool KyberMode::is_ml_kem() const {
4,494✔
133
   return m_mode == KyberMode::ML_KEM_512 || m_mode == KyberMode::ML_KEM_768 || m_mode == KyberMode::ML_KEM_1024;
4,494✔
134
}
135

136
bool KyberMode::is_kyber_round3() const {
6,011✔
137
   return m_mode == KyberMode::Kyber512_R3 || m_mode == KyberMode::Kyber768_R3 || m_mode == KyberMode::Kyber1024_R3 ||
6,011✔
138
          m_mode == KyberMode::Kyber512_90s || m_mode == KyberMode::Kyber768_90s || m_mode == KyberMode::Kyber1024_90s;
6,011✔
139
}
140

141
bool KyberMode::is_available() const {
401✔
142
#if defined(BOTAN_HAS_KYBER)
143
   if(is_kyber_round3() && is_modern()) {
401✔
144
      return true;
145
   }
146
#endif
147

148
#if defined(BOTAN_HAS_KYBER_90S)
149
   if(is_kyber_round3() && is_90s()) {
318✔
150
      return true;
151
   }
152
#endif
153

154
#if defined(BOTAN_HAS_ML_KEM)
155
   if(is_ml_kem()) {
237✔
156
      return true;
157
   }
158
#endif
159

160
   return false;
161
}
162

163
KyberMode Kyber_PublicKey::mode() const {
2,908✔
164
   return m_public->mode().mode();
2,908✔
165
}
166

167
std::string Kyber_PublicKey::algo_name() const {
579✔
168
   return mode().is_ml_kem() ? "ML-KEM" : "Kyber";
903✔
169
}
170

171
AlgorithmIdentifier Kyber_PublicKey::algorithm_identifier() const {
645✔
172
   // draft-ietf-lamps-kyber-certificates-latest (22 July 2024) The
173
   //    AlgorithmIdentifier for a ML-KEM public key MUST use one of the
174
   //    id-alg-ml-kem object identifiers [...]. The parameters field of the
175
   //    AlgorithmIdentifier for the ML-KEM public key MUST be absent.
176
   return AlgorithmIdentifier(mode().object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
645✔
177
}
178

179
OID Kyber_PublicKey::object_identifier() const {
×
180
   return mode().object_identifier();
×
181
}
182

183
size_t Kyber_PublicKey::estimated_strength() const {
722✔
184
   return m_public->mode().estimated_strength();
722✔
185
}
186

187
Kyber_PublicKey::Kyber_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
261✔
188
      Kyber_PublicKey(key_bits, KyberMode(alg_id.oid())) {}
261✔
189

190
Kyber_PublicKey::Kyber_PublicKey(std::span<const uint8_t> pub_key, KyberMode mode) {
377✔
191
   m_public = std::make_shared<Kyber_PublicKeyInternal>(mode, KyberSerializedPublicKey(pub_key));
381✔
192
}
377✔
193

194
Kyber_PublicKey::Kyber_PublicKey(const Kyber_PublicKey& other) :
374✔
195
      m_public(std::make_shared<Kyber_PublicKeyInternal>(
374✔
196
         other.m_public->mode(), other.m_public->t().clone(), other.m_public->rho())) {}
748✔
197

198
std::vector<uint8_t> Kyber_PublicKey::raw_public_key_bits() const {
678✔
199
   return m_public->public_key_bits_raw().get();
678✔
200
}
201

202
std::vector<uint8_t> Kyber_PublicKey::public_key_bits() const {
632✔
203
   // Currently, there isn't a finalized definition of an ASN.1 structure for
204
   // Kyber aka ML-KEM public keys. Therefore, we return the raw public key bits.
205
   return raw_public_key_bits();
632✔
206
}
207

208
size_t Kyber_PublicKey::key_length() const {
69✔
209
   return m_public->mode().canonical_parameter_set_identifier();
69✔
210
}
211

212
bool Kyber_PublicKey::check_key(RandomNumberGenerator&, bool) const {
18✔
213
   // The length checks described in FIPS 203, Section 7.2 are already performed
214
   // while decoding the public key. See constructor of Kyber_PublicKeyInternal.
215
   // The decoding function KyberAlgos::byte_decode() also checks the range of
216
   // the decoded values. The check below is added for completeness.
217

218
   std::vector<uint8_t> test(m_public->mode().polynomial_vector_bytes());
18✔
219
   Kyber_Algos::encode_polynomial_vector(test, m_public->t());
18✔
220

221
   const auto& serialized_pubkey = m_public->public_key_bits_raw();
18✔
222
   return test.size() < serialized_pubkey.size() && std::equal(test.begin(), test.end(), serialized_pubkey.begin());
36✔
223
}
18✔
224

225
std::unique_ptr<Private_Key> Kyber_PublicKey::generate_another(RandomNumberGenerator& rng) const {
9✔
226
   return std::make_unique<Kyber_PrivateKey>(rng, mode());
9✔
227
}
228

229
/**
230
 * NIST FIPS 203, Algorithms 19 (ML-KEM.KeyGen)
231
 */
232
Kyber_PrivateKey::Kyber_PrivateKey(RandomNumberGenerator& rng, KyberMode mode) {
384✔
233
   std::tie(m_public, m_private) =
384✔
234
      Kyber_Algos::expand_keypair({rng.random_vec<KyberSeedRandomness>(KyberConstants::SEED_BYTES),
1,152✔
235
                                   rng.random_vec<KyberImplicitRejectionValue>(KyberConstants::SEED_BYTES)},
236
                                  mode);
384✔
237
}
768✔
238

239
Kyber_PrivateKey::Kyber_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
312✔
240
      Kyber_PrivateKey(key_bits, KyberMode(alg_id.oid())) {}
312✔
241

242
Kyber_PrivateKey::Kyber_PrivateKey(std::span<const uint8_t> sk, KyberMode m) {
348✔
243
   KyberConstants mode(m);
348✔
244

245
   if(mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) {
348✔
246
      std::tie(m_public, m_private) = Seed_Expanding_Keypair_Codec().decode_keypair(sk, std::move(mode));
108✔
247
   } else if(sk.size() == mode.expanded_private_key_bytes()) {
243✔
248
      std::tie(m_public, m_private) = Expanded_Keypair_Codec().decode_keypair(sk, std::move(mode));
243✔
249
   } else if(!mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) {
3✔
250
      throw Invalid_Argument("Kyber round 3 private keys do not support the seed format");
1✔
251
   } else {
252
      throw Invalid_Argument("Private key does not have the correct byte count");
2✔
253
   }
254
}
351✔
255

256
std::unique_ptr<Public_Key> Kyber_PrivateKey::public_key() const {
374✔
257
   return std::make_unique<Kyber_PublicKey>(*this);
374✔
258
}
259

260
secure_vector<uint8_t> Kyber_PrivateKey::raw_private_key_bits() const {
316✔
261
   return this->private_key_bits();
316✔
262
}
263

264
secure_vector<uint8_t> Kyber_PrivateKey::private_key_bits() const {
729✔
265
   if(auto seedSk = _seed_private_key_bits()) {
729✔
266
      return std::move(seedSk.value());
284✔
267
   }
284✔
268
   return _expanded_private_key_bits();
445✔
269
}
270

271
bool Kyber_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
9✔
272
   // As we do not support loading a private key in extended format but rather
273
   // always extract it from a 64-byte seed, these checks (as described in
274
   // FIPS 203, Section 7.1) should never fail. Particularly, the length checks
275
   // and the hash consistency check described in Section 7.2 and 7.3 are
276
   // trivial when the private key is always extracted from a seed. The encaps/
277
   // decaps roundtrip test is added for completeness.
278

279
   if(!Kyber_PublicKey::check_key(rng, strong)) {
9✔
280
      return false;
281
   }
282

283
   PK_KEM_Encryptor enc(*this, "Raw");
9✔
284
   PK_KEM_Decryptor dec(*this, rng, "Raw");
9✔
285

286
   const auto [c, K] = KEM_Encapsulation::destructure(enc.encrypt(rng));
9✔
287
   const auto K_prime = dec.decrypt(c);
9✔
288

289
   return K == K_prime;
9✔
290
}
9✔
291

292
std::unique_ptr<PK_Ops::KEM_Encryption> Kyber_PublicKey::create_kem_encryption_op(std::string_view params,
361✔
293
                                                                                  std::string_view provider) const {
294
   if(provider.empty() || provider == "base") {
370✔
295
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
296
      if(mode().is_kyber_round3()) {
361✔
297
         return std::make_unique<Kyber_KEM_Encryptor>(m_public, params);
181✔
298
      }
299
#endif
300

301
#if defined(BOTAN_HAS_ML_KEM)
302
      if(mode().is_ml_kem()) {
180✔
303
         return std::make_unique<ML_KEM_Encryptor>(m_public, params);
180✔
304
      }
305
#endif
306

307
      BOTAN_ASSERT_UNREACHABLE();
×
308
   }
309
   throw Provider_Not_Found(algo_name(), provider);
×
310
}
311

312
std::unique_ptr<PK_Ops::KEM_Decryption> Kyber_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& rng,
282✔
313
                                                                                   std::string_view params,
314
                                                                                   std::string_view provider) const {
315
   BOTAN_UNUSED(rng);
282✔
316
   if(provider.empty() || provider == "base") {
291✔
317
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
318
      if(mode().is_kyber_round3()) {
282✔
319
         return std::make_unique<Kyber_KEM_Decryptor>(m_private, m_public, params);
173✔
320
      }
321
#endif
322

323
#if defined(BOTAN_HAS_ML_KEM)
324
      if(mode().is_ml_kem()) {
109✔
325
         return std::make_unique<ML_KEM_Decryptor>(m_private, m_public, params);
109✔
326
      }
327
#endif
328

329
      BOTAN_ASSERT_UNREACHABLE();
×
330
   }
331
   throw Provider_Not_Found(algo_name(), provider);
×
332
}
333

334
secure_vector<uint8_t> Kyber_PrivateKey::_expanded_private_key_bits() const {
462✔
335
   return Expanded_Keypair_Codec().encode_keypair({m_public, m_private});
924✔
336
}
337

338
std::optional<secure_vector<uint8_t>> Kyber_PrivateKey::_seed_private_key_bits() const {
743✔
339
   if(!mode().is_ml_kem() || !m_private->seed().d.has_value()) {
743✔
340
      return std::nullopt;
456✔
341
   }
342
   return Seed_Expanding_Keypair_Codec().encode_keypair({m_public, m_private});
574✔
343
}
344

345
}  // 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

© 2025 Coveralls, Inc