Botan 3.10.0
Crypto and TLS for C&
kyber.cpp
Go to the documentation of this file.
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/pubkey.h>
19#include <botan/rng.h>
20#include <botan/secmem.h>
21#include <botan/internal/fmt.h>
22#include <botan/internal/kyber_algos.h>
23#include <botan/internal/kyber_constants.h>
24#include <botan/internal/kyber_keys.h>
25#include <botan/internal/kyber_types.h>
26
27#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
28 #include <botan/internal/kyber_round3_impl.h>
29#endif
30
31#if defined(BOTAN_HAS_ML_KEM)
32 #include <botan/internal/ml_kem_impl.h>
33#endif
34
35#include <memory>
36#include <vector>
37
38namespace Botan {
39
40namespace {
41
42KyberMode::Mode kyber_mode_from_string(std::string_view str) {
43 if(str == "Kyber-512-90s-r3") {
45 }
46 if(str == "Kyber-768-90s-r3") {
48 }
49 if(str == "Kyber-1024-90s-r3") {
51 }
52 if(str == "Kyber-512-r3") {
54 }
55 if(str == "Kyber-768-r3") {
57 }
58 if(str == "Kyber-1024-r3") {
60 }
61 if(str == "ML-KEM-512") {
63 }
64 if(str == "ML-KEM-768") {
66 }
67 if(str == "ML-KEM-1024") {
69 }
70
71 throw Invalid_Argument(fmt("'{}' is not a valid Kyber mode name", str));
72}
73
74} // namespace
75
77
78KyberMode::KyberMode(const OID& oid) : m_mode(kyber_mode_from_string(oid.to_formatted_string())) {}
79
80KyberMode::KyberMode(std::string_view str) : m_mode(kyber_mode_from_string(str)) {}
81
85
86std::string KyberMode::to_string() const {
87 switch(m_mode) {
88 case Kyber512_90s:
89 return "Kyber-512-90s-r3";
90 case Kyber768_90s:
91 return "Kyber-768-90s-r3";
92 case Kyber1024_90s:
93 return "Kyber-1024-90s-r3";
94 case Kyber512_R3:
95 return "Kyber-512-r3";
96 case Kyber768_R3:
97 return "Kyber-768-r3";
98 case Kyber1024_R3:
99 return "Kyber-1024-r3";
100 case ML_KEM_512:
101 return "ML-KEM-512";
102 case ML_KEM_768:
103 return "ML-KEM-768";
104 case ML_KEM_1024:
105 return "ML-KEM-1024";
106 }
107
109}
110
111bool KyberMode::is_90s() const {
112 return m_mode == Kyber512_90s || m_mode == Kyber768_90s || m_mode == Kyber1024_90s;
113}
114
116 return !is_90s();
117}
118
120 return m_mode == KyberMode::ML_KEM_512 || m_mode == KyberMode::ML_KEM_768 || m_mode == KyberMode::ML_KEM_1024;
121}
122
124 return m_mode == KyberMode::Kyber512_R3 || m_mode == KyberMode::Kyber768_R3 || m_mode == KyberMode::Kyber1024_R3 ||
126}
127
129#if defined(BOTAN_HAS_KYBER)
130 if(is_kyber_round3() && is_modern()) {
131 return true;
132 }
133#endif
134
135#if defined(BOTAN_HAS_KYBER_90S)
136 if(is_kyber_round3() && is_90s()) {
137 return true;
138 }
139#endif
140
141#if defined(BOTAN_HAS_ML_KEM)
142 if(is_ml_kem()) {
143 return true;
144 }
145#endif
146
147 return false;
148}
149
151 return m_public->mode().mode();
152}
153
154std::string Kyber_PublicKey::algo_name() const {
155 return mode().is_ml_kem() ? "ML-KEM" : "Kyber";
156}
157
159 // draft-ietf-lamps-kyber-certificates-latest (22 July 2024) The
160 // AlgorithmIdentifier for a ML-KEM public key MUST use one of the
161 // id-alg-ml-kem object identifiers [...]. The parameters field of the
162 // AlgorithmIdentifier for the ML-KEM public key MUST be absent.
164}
165
169
171 return m_public->mode().estimated_strength();
172}
173
174Kyber_PublicKey::Kyber_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
175 Kyber_PublicKey(key_bits, KyberMode(alg_id.oid())) {}
176
177Kyber_PublicKey::Kyber_PublicKey(std::span<const uint8_t> pub_key, KyberMode mode) {
178 m_public = std::make_shared<Kyber_PublicKeyInternal>(mode, KyberSerializedPublicKey(pub_key));
179}
180
182 m_public(std::make_shared<Kyber_PublicKeyInternal>(
183 other.m_public->mode(), other.m_public->t().clone(), other.m_public->rho())) {}
184
185std::vector<uint8_t> Kyber_PublicKey::raw_public_key_bits() const {
186 return m_public->public_key_bits_raw().get();
187}
188
189std::vector<uint8_t> Kyber_PublicKey::public_key_bits() const {
190 // Currently, there isn't a finalized definition of an ASN.1 structure for
191 // Kyber aka ML-KEM public keys. Therefore, we return the raw public key bits.
192 return raw_public_key_bits();
193}
194
196 return m_public->mode().canonical_parameter_set_identifier();
197}
198
199bool Kyber_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
200 // The length checks described in FIPS 203, Section 7.2 are already performed
201 // while decoding the public key. See constructor of Kyber_PublicKeyInternal.
202 // The decoding function KyberAlgos::byte_decode() also checks the range of
203 // the decoded values. The check below is added for completeness.
204
205 std::vector<uint8_t> test(m_public->mode().polynomial_vector_bytes());
207
208 const auto& serialized_pubkey = m_public->public_key_bits_raw();
209 return test.size() < serialized_pubkey.size() && std::equal(test.begin(), test.end(), serialized_pubkey.begin());
210}
211
212std::unique_ptr<Private_Key> Kyber_PublicKey::generate_another(RandomNumberGenerator& rng) const {
213 return std::make_unique<Kyber_PrivateKey>(rng, mode());
214}
215
216/**
217 * NIST FIPS 203, Algorithms 19 (ML-KEM.KeyGen)
218 */
225
226Kyber_PrivateKey::Kyber_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
227 Kyber_PrivateKey(key_bits, KyberMode(alg_id.oid())) {}
228
229Kyber_PrivateKey::Kyber_PrivateKey(std::span<const uint8_t> sk, KyberMode m) {
231
232 if(mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) {
233 std::tie(m_public, m_private) = Seed_Expanding_Keypair_Codec().decode_keypair(sk, std::move(mode));
234 } else if(sk.size() == mode.expanded_private_key_bytes()) {
235 std::tie(m_public, m_private) = Expanded_Keypair_Codec().decode_keypair(sk, std::move(mode));
236 } else if(!mode.mode().is_ml_kem() && sk.size() == mode.seed_private_key_bytes()) {
237 throw Invalid_Argument("Kyber round 3 private keys do not support the seed format");
238 } else {
239 throw Invalid_Argument("Private key does not have the correct byte count");
240 }
241}
242
243std::unique_ptr<Public_Key> Kyber_PrivateKey::public_key() const {
244 return std::make_unique<Kyber_PublicKey>(*this);
245}
246
250
254
256 // As we do not support loading a private key in extended format but rather
257 // always extract it from a 64-byte seed, these checks (as described in
258 // FIPS 203, Section 7.1) should never fail. Particularly, the length checks
259 // and the hash consistency check described in Section 7.2 and 7.3 are
260 // trivial when the private key is always extracted from a seed. The encaps/
261 // decaps roundtrip test is added for completeness.
262
263 if(!Kyber_PublicKey::check_key(rng, strong)) {
264 return false;
265 }
266
267 PK_KEM_Encryptor enc(*this, "Raw");
268 PK_KEM_Decryptor dec(*this, rng, "Raw");
269
270 const auto [c, K] = KEM_Encapsulation::destructure(enc.encrypt(rng));
271 const auto K_prime = dec.decrypt(c);
272
273 return K == K_prime;
274}
275
276std::unique_ptr<PK_Ops::KEM_Encryption> Kyber_PublicKey::create_kem_encryption_op(std::string_view params,
277 std::string_view provider) const {
278 if(provider.empty() || provider == "base") {
279#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
280 if(mode().is_kyber_round3()) {
281 return std::make_unique<Kyber_KEM_Encryptor>(m_public, params);
282 }
283#endif
284
285#if defined(BOTAN_HAS_ML_KEM)
286 if(mode().is_ml_kem()) {
287 return std::make_unique<ML_KEM_Encryptor>(m_public, params);
288 }
289#endif
290
292 }
293 throw Provider_Not_Found(algo_name(), provider);
294}
295
296std::unique_ptr<PK_Ops::KEM_Decryption> Kyber_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& rng,
297 std::string_view params,
298 std::string_view provider) const {
299 BOTAN_UNUSED(rng);
300 if(provider.empty() || provider == "base") {
301#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S)
302 if(mode().is_kyber_round3()) {
303 return std::make_unique<Kyber_KEM_Decryptor>(m_private, m_public, params);
304 }
305#endif
306
307#if defined(BOTAN_HAS_ML_KEM)
308 if(mode().is_ml_kem()) {
309 return std::make_unique<ML_KEM_Decryptor>(m_private, m_public, params);
310 }
311#endif
312
314 }
315 throw Provider_Not_Found(algo_name(), provider);
316}
317
319 if(mode().is_ml_kem() && m_private->seed().d.has_value()) {
321 }
323}
324
327 throw Encoding_Error("Expanded private keys do not support the seed format");
328 }
329 const auto codec = [&]() -> std::unique_ptr<Kyber_Keypair_Codec> {
330 switch(format) {
332 return std::make_unique<Seed_Expanding_Keypair_Codec>();
334 return std::make_unique<Expanded_Keypair_Codec>();
335 }
337 }();
338 return codec->encode_keypair({m_public, m_private});
339}
340} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:144
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:163
Codec for expanded private keys (as specified in FIPS 203).
Definition kyber_keys.h:30
KyberInternalKeypair decode_keypair(std::span< const uint8_t > buffer, KyberConstants mode) const override
static std::pair< std::vector< uint8_t >, secure_vector< uint8_t > > destructure(KEM_Encapsulation &&kem)
Definition pubkey.h:581
static constexpr size_t SEED_BYTES
bool is_available() const
Definition kyber.cpp:128
bool is_kyber_round3() const
Definition kyber.cpp:123
std::string to_string() const
Definition kyber.cpp:86
bool is_modern() const
Definition kyber.cpp:115
OID object_identifier() const
Definition kyber.cpp:82
KyberMode(Mode mode)
Definition kyber.cpp:76
bool is_90s() const
Definition kyber.cpp:111
bool is_ml_kem() const
Definition kyber.cpp:119
Mode mode() const
Definition kyber.h:62
MlPrivateKeyFormat private_key_format() const
Definition kyber.cpp:318
secure_vector< uint8_t > private_key_bits_with_format(MlPrivateKeyFormat format) const
Definition kyber.cpp:325
std::unique_ptr< PK_Ops::KEM_Decryption > create_kem_decryption_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition kyber.cpp:296
std::unique_ptr< Public_Key > public_key() const override
Definition kyber.cpp:243
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition kyber.cpp:255
Kyber_PrivateKey(RandomNumberGenerator &rng, KyberMode mode)
Definition kyber.cpp:219
secure_vector< uint8_t > raw_private_key_bits() const override
Definition kyber.cpp:247
secure_vector< uint8_t > private_key_bits() const override
Definition kyber.cpp:251
std::vector< uint8_t > public_key_bits() const override
Definition kyber.cpp:189
std::vector< uint8_t > raw_public_key_bits() const override
Definition kyber.cpp:185
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition kyber.cpp:199
std::string algo_name() const override
Definition kyber.cpp:154
std::shared_ptr< Kyber_PublicKeyInternal > m_public
Definition kyber.h:146
size_t key_length() const override
Definition kyber.cpp:195
AlgorithmIdentifier algorithm_identifier() const override
Definition kyber.cpp:158
std::unique_ptr< PK_Ops::KEM_Encryption > create_kem_encryption_op(std::string_view params, std::string_view provider) const override
Definition kyber.cpp:276
KyberMode mode() const
Definition kyber.cpp:150
OID object_identifier() const override
Definition kyber.cpp:166
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition kyber.cpp:212
Kyber_PublicKey(std::span< const uint8_t > pub_key, KyberMode mode)
Definition kyber.cpp:177
size_t estimated_strength() const override
Definition kyber.cpp:170
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
void decrypt(std::span< uint8_t > out_shared_key, std::span< const uint8_t > encap_key, size_t desired_shared_key_len=32, std::span< const uint8_t > salt={})
Definition pubkey.cpp:206
KEM_Encapsulation encrypt(RandomNumberGenerator &rng, size_t desired_shared_key_len=32, std::span< const uint8_t > salt={})
Definition pubkey.h:665
void random_vec(std::span< uint8_t > v)
Definition rng.h:199
Codec for private keys as 64-byte seeds: d || z.
Definition kyber_keys.h:37
KyberInternalKeypair decode_keypair(std::span< const uint8_t > buffer, KyberConstants mode) const override
void encode_polynomial_vector(std::span< uint8_t > out, const KyberPolyVecNTT &vec)
KyberInternalKeypair expand_keypair(KyberPrivateKeySeed seed, KyberConstants mode)
Strong< secure_vector< uint8_t >, struct KyberImplicitRejectionValue_ > KyberImplicitRejectionValue
Secret random value (called Z in the spec), used for implicit rejection in the decapsulation.
Definition kyber_types.h:42
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
MlPrivateKeyFormat
Byte encoding format of ML-KEM and ML-DSA the private key.
Definition kyber.h:83
@ Expanded
The expanded format, i.e., the format specified in FIPS-203/204.
Definition kyber.h:90
BOTAN_FORCE_INLINE constexpr T rho(T x)
Definition rotate.h:53
Strong< secure_vector< uint8_t >, struct KyberSeedRandomness_ > KyberSeedRandomness
Principal seed used to generate Kyber key pairs.
Definition kyber_types.h:33
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:69
Strong< std::vector< uint8_t >, struct KyberSerializedPublicKey_ > KyberSerializedPublicKey
Public key in serialized form (t || rho).
Definition kyber_types.h:57