Skip to content

Commit

Permalink
Merge pull request #3893 from Rohde-Schwarz/feature/mlkem_ipd
Browse files Browse the repository at this point in the history
PQC: ML-KEM
  • Loading branch information
reneme authored Oct 15, 2024
2 parents 4996790 + 33000ab commit 7dd5905
Show file tree
Hide file tree
Showing 53 changed files with 3,095 additions and 395 deletions.
38 changes: 24 additions & 14 deletions doc/api_ref/pubkey.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,28 @@ Dilithium

Post-quantum secure signature scheme based on lattice problems.

Kyber
~~~~~~~~~~~
ML-KEM (FIPS 203)
~~~~~~~~~~~~~~~~~

Post-quantum key encapsulation scheme based on (structured) lattices.
Post-quantum key encapsulation scheme based on (structured) lattices. This
algorithm is standardized in FIPS 203. Decapsulation keys are always stored and
expanded from the 64-byte private random seeds (``d || z``), loading the
expanded key format specified in FIPS 203 is explicitly not supported.

.. note::
Support for ML-KEM is implemented in the module ``ml_kem``.

Additionally, support for the pre-standardized version "Kyber" is retained for
the time being. The implemented specification is commonly referred to as version
3.01 of the CRYSTALS-Kyber submission to NIST's third round of the PQC
competition. This is not compatible to the "Initial Public Draft" version of
FIPS 203 for which Botan does not offer an implementation.

Currently two modes for Kyber are defined: the round3 specification
from the NIST PQC competition, and the "90s mode" (which uses
AES/SHA-2 instead of SHA-3 based primitives). The 90s mode Kyber is
deprecated and will be removed in a future release.
Currently two flavors of Kyber are implemented in separate Botan modules:

The final NIST specification version of Kyber is not yet implemented.
* ``kyber``, that uses Keccak (SHAKE and SHA-3), and that saw some public
usage by early adopters.
* ``kyber_90s``, that uses AES/SHA-2 instead of Keccak-based primitives.
This mode is deprecated and will be removed in a future release.

Ed25519 and Ed448
~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1160,18 +1169,19 @@ encapsulated key and returns the shared secret.
Botan implements the following KEM schemes:

1. RSA
#. Kyber
#. ML-KEM (Kyber)
#. FrodoKEM
#. McEliece

.. _kyber_example:
.. _mlkem_example:

Code Example: Kyber
Code Example: ML-KEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The code below demonstrates key encapsulation using the Kyber post-quantum scheme.
The code below demonstrates key encapsulation using ML-KEM (FIPS 203), formerly
known as Kyber.

.. literalinclude:: /../src/examples/kyber.cpp
.. literalinclude:: /../src/examples/ml_kem.cpp
:language: cpp

.. _mceliece:
Expand Down
9 changes: 9 additions & 0 deletions doc/api_ref/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ Public Key
"sm2p256v1") and the public point as a pair of integers giving
the affine coordinates.
.. py:classmethod:: load_ml_kem(mode, raw_encoding)
Load an ML-KEM public key giving the mode as a string (like
"ML-KEM-512") and the raw encoding of the public key.
.. py:method:: check_key(rng_obj, strong=True):
Test the key for consistency. If ``strong`` is ``True`` then
Expand Down Expand Up @@ -390,6 +395,10 @@ Private Key
Return a private SM2 key
.. py:classmethod:: load_ml_kem(mode, raw_encoding)
Return a private ML-KEM key
.. py:method:: get_public_key()
Return a public_key object
Expand Down
2 changes: 1 addition & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ are listed below:
* :ref:`PBKDFs <pbkdf_example>`
* :ref:`Key Agreement <ecdh_example>`
* :ref:`ECDSA <ecdsa_example>`
* :ref:`Kyber <kyber_example>`
* :ref:`ML-KEM <mlkem_example>`
* :ref:`RSA <rsa_example>`
* :ref:`XMSS <xmss_example>`
* :ref:`Stream Ciphers <stream_ciphers_example>`
Expand Down
2 changes: 1 addition & 1 deletion readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Public Key Cryptography
* DH and ECDH key agreement
* Signature schemes ECDSA, DSA, Ed25519, Ed448, ECGDSA, ECKCDSA, SM2, GOST 34.10
* Post-quantum signature schemes Dilithium, HSS/LMS, SPHINCS+, XMSS
* Post-quantum key agreement schemes McEliece, Kyber, and FrodoKEM
* Post-quantum key agreement schemes McEliece, ML-KEM (Kyber), and FrodoKEM
* ElGamal encryption
* Padding schemes OAEP, PSS, PKCS #1 v1.5, X9.31

Expand Down
6 changes: 6 additions & 0 deletions src/build-data/oids.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
1.3.101.112 = Ed25519
1.3.101.113 = Ed448

# From NIST:
2.16.840.1.101.3.4.4.1 = ML-KEM-512
2.16.840.1.101.3.4.4.2 = ML-KEM-768
2.16.840.1.101.3.4.4.3 = ML-KEM-1024

# FrodoKEM OIDs are currently in Botan's private arc
1.3.6.1.4.1.25258.1.14.1 = FrodoKEM-640-SHAKE
1.3.6.1.4.1.25258.1.14.2 = FrodoKEM-976-SHAKE
Expand All @@ -32,6 +37,7 @@
1.3.6.1.4.1.25258.1.17.3 = eFrodoKEM-1344-AES

# Kyber OIDs are currently in Botan's private arc
# as specified in CRYSTALS-Kyber 3.01 (NIST's PQC competition round 3)
1.3.6.1.4.1.25258.1.7.1 = Kyber-512-r3
1.3.6.1.4.1.25258.1.7.2 = Kyber-768-r3
1.3.6.1.4.1.25258.1.7.3 = Kyber-1024-r3
Expand Down
23 changes: 23 additions & 0 deletions src/cli/perf_pk_kem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ class PerfTest_Kyber final : public PerfTest_PK_KEM {
"Kyber-768-90s-r3",
"Kyber-1024-r3",
"Kyber-1024-90s-r3",
"ML-KEM-512",
"ML-KEM-768",
"ML-KEM-1024",
};
}
};
Expand All @@ -105,6 +108,26 @@ BOTAN_REGISTER_PERF_TEST("Kyber", PerfTest_Kyber);

#endif

#if defined(BOTAN_HAS_ML_KEM)

class PerfTest_ML_KEM final : public PerfTest_PK_KEM {
public:
std::string algo() const override { return "ML-KEM"; }

std::vector<std::string> keygen_params(const PerfConfig& config) const override {
BOTAN_UNUSED(config);
return {
"ML-KEM-512",
"ML-KEM-768",
"ML-KEM-1024",
};
}
};

BOTAN_REGISTER_PERF_TEST("ML-KEM", PerfTest_ML_KEM);

#endif

#if defined(BOTAN_HAS_FRODOKEM)

class PerfTest_FrodoKEM final : public PerfTest_PK_KEM {
Expand Down
1 change: 1 addition & 0 deletions src/cli/speed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ class Speed final : public Command {
"X25519",
"X448",
"Kyber",
"ML-KEM",
"SPHINCS+",
"FrodoKEM",
"HSS-LMS",
Expand Down
4 changes: 2 additions & 2 deletions src/examples/hybrid_key_encapsulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,12 @@ int main() {

// Alice generates two key pairs suitable for:
// 1) key exchange (X25519), and
// 2) key encapsulation (Kyber).
// 2) key encapsulation (ML-KEM).
//
// She then combines them into a custom "hybrid" key pair that acts
// like a key encapsulation mechanism (KEM).
const auto private_key_of_alice = std::make_unique<Hybrid_PrivateKey>(
Botan::create_private_key("X25519", rng), Botan::create_private_key("Kyber", rng, "Kyber-768-r3"));
Botan::create_private_key("X25519", rng), Botan::create_private_key("ML-KEM", rng, "ML-KEM-768"));
const auto public_key_of_alice = private_key_of_alice->public_key();

// Bob uses Alice's public key to encapsulate a shared secret, and
Expand Down
2 changes: 1 addition & 1 deletion src/examples/kyber.cpp → src/examples/ml_kem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ int main() {
std::array<uint8_t, 16> salt;
rng.randomize(salt);

Botan::Kyber_PrivateKey priv_key(rng, Botan::KyberMode::Kyber512_R3);
Botan::Kyber_PrivateKey priv_key(rng, Botan::KyberMode::ML_KEM_768);
auto pub_key = priv_key.public_key();

Botan::PK_KEM_Encryptor enc(*pub_key, kdf);
Expand Down
8 changes: 7 additions & 1 deletion src/lib/asn1/oid_maps.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* OID maps
*
* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2024-07-23
* This file was automatically generated by src/scripts/dev_tools/gen_oids.py on 2024-08-21
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
Expand Down Expand Up @@ -274,6 +274,9 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"2.16.840.1.101.3.4.3.7", "DSA/SHA-3(384)"},
{"2.16.840.1.101.3.4.3.8", "DSA/SHA-3(512)"},
{"2.16.840.1.101.3.4.3.9", "ECDSA/SHA-3(224)"},
{"2.16.840.1.101.3.4.4.1", "ML-KEM-512"},
{"2.16.840.1.101.3.4.4.2", "ML-KEM-768"},
{"2.16.840.1.101.3.4.4.3", "ML-KEM-1024"},
{"2.16.840.1.113730.1.13", "Certificate Comment"},
{"2.5.29.14", "X509v3.SubjectKeyIdentifier"},
{"2.5.29.15", "X509v3.KeyUsage"},
Expand Down Expand Up @@ -427,6 +430,9 @@ std::unordered_map<std::string, OID> OID_Map::load_str2oid_map() {
{"Kyber-768-r3", OID({1, 3, 6, 1, 4, 1, 25258, 1, 7, 2})},
{"MD5", OID({1, 2, 840, 113549, 2, 5})},
{"MGF1", OID({1, 2, 840, 113549, 1, 1, 8})},
{"ML-KEM-1024", OID({2, 16, 840, 1, 101, 3, 4, 4, 3})},
{"ML-KEM-512", OID({2, 16, 840, 1, 101, 3, 4, 4, 1})},
{"ML-KEM-768", OID({2, 16, 840, 1, 101, 3, 4, 4, 2})},
{"McEliece", OID({1, 3, 6, 1, 4, 1, 25258, 1, 3})},
{"Microsoft SmartcardLogon", OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 2})},
{"Microsoft UPN", OID({1, 3, 6, 1, 4, 1, 311, 20, 2, 3})},
Expand Down
12 changes: 12 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1528,9 +1528,11 @@ BOTAN_FFI_EXPORT(3, 1) int botan_privkey_load_kyber(botan_privkey_t* key, const

BOTAN_FFI_EXPORT(3, 1) int botan_pubkey_load_kyber(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len);

BOTAN_FFI_DEPRECATED("Use generic botan_privkey_view_raw")
BOTAN_FFI_EXPORT(3, 1)
int botan_privkey_view_kyber_raw_key(botan_privkey_t key, botan_view_ctx ctx, botan_view_bin_fn view);

BOTAN_FFI_DEPRECATED("Use generic botan_pubkey_view_raw")
BOTAN_FFI_EXPORT(3, 1)
int botan_pubkey_view_kyber_raw_key(botan_pubkey_t key, botan_view_ctx ctx, botan_view_bin_fn view);

Expand All @@ -1544,6 +1546,16 @@ int botan_privkey_load_frodokem(botan_privkey_t* key, const uint8_t privkey[], s
BOTAN_FFI_EXPORT(3, 6)
int botan_pubkey_load_frodokem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* frodo_mode);

/*
* Algorithm specific key operations: ML-KEM
*/

BOTAN_FFI_EXPORT(3, 6)
int botan_privkey_load_ml_kem(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* mlkem_mode);

BOTAN_FFI_EXPORT(3, 6)
int botan_pubkey_load_ml_kem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mlkem_mode);

/*
* Algorithm specific key operations: ECDSA and ECDH
*/
Expand Down
65 changes: 63 additions & 2 deletions src/lib/ffi/ffi_pkey_algs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@
#include <botan/dh.h>
#endif

#if defined(BOTAN_HAS_KYBER)
#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_ML_KEM)
#include <botan/kyber.h>
#include <botan/internal/kyber_constants.h>
#endif

#if defined(BOTAN_HAS_FRODOKEM)
Expand Down Expand Up @@ -961,7 +962,7 @@ int botan_privkey_load_kyber(botan_privkey_t* key, const uint8_t privkey[], size
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}
#else
BOTAN_UNUSED(key, privkey);
BOTAN_UNUSED(key, key_len, privkey);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
Expand Down Expand Up @@ -1031,6 +1032,66 @@ int botan_pubkey_view_kyber_raw_key(botan_pubkey_t key, botan_view_ctx ctx, bota
#endif
}

/*
* Algorithm specific key operations: ML-KEM
*/

int botan_privkey_load_ml_kem(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* mlkem_mode) {
#if defined(BOTAN_HAS_ML_KEM)
if(key == nullptr || privkey == nullptr || mlkem_mode == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::KyberConstants(Botan::KyberMode(mlkem_mode));
if(!mode.mode().is_ml_kem()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

if(key_len != mode.private_key_bytes()) {
return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
}

auto mlkem_key = std::make_unique<Botan::Kyber_PrivateKey>(std::span{privkey, key_len}, mode.mode());
*key = new botan_privkey_struct(std::move(mlkem_key));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(key, key_len, privkey, mlkem_mode);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

int botan_pubkey_load_ml_kem(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* mlkem_mode) {
#if defined(BOTAN_HAS_ML_KEM)
if(key == nullptr || pubkey == nullptr || mlkem_mode == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::KyberConstants(Botan::KyberMode(mlkem_mode));
if(!mode.mode().is_ml_kem()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

if(key_len != mode.public_key_bytes()) {
return BOTAN_FFI_ERROR_INVALID_KEY_LENGTH;
}

auto mlkem_key = std::make_unique<Botan::Kyber_PublicKey>(std::span{pubkey, key_len}, mode.mode());
*key = new botan_pubkey_struct(std::move(mlkem_key));
return BOTAN_FFI_SUCCESS;
});
#else
BOTAN_UNUSED(key, key_len, pubkey, mlkem_mode);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

/*
* Algorithm specific key operations: FrodoKEM
*/
Expand Down
Loading

0 comments on commit 7dd5905

Please sign in to comment.