Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PQC: ML-DSA #4270

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions doc/api_ref/pubkey.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ removed in a future major release.
.. cpp:function:: std::string algo_name()

Return a short string identifying the algorithm of this key,
eg "RSA" or "Dilithium".
eg "RSA" or "ML-DSA".

.. cpp:function:: size_t estimated_strength() const

Expand Down Expand Up @@ -110,10 +110,28 @@ ECDH, DH, X25519 and X448
Key agreement schemes. DH uses arithmetic over finite fields and is slower and
with larger keys. ECDH, X25519 and X448 use elliptic curves instead.

Dilithium
~~~~~~~~~~
ML-DSA (FIPS 204)
~~~~~~~~~~~~~~~~~

Post-quantum secure signature scheme based on (structured) lattices.
This algorithm is standardized in FIPS 204. Signing keys are always stored and
expanded from the 32-byte private random seed (`xi`), loading the expanded key
format specified in FIPS 204 is explicitly not supported.

Support for ML-DSA is implemented in the module ``ml_dsa``

Post-quantum secure signature scheme based on lattice problems.
Additionally, support for the pre-standardized version "Dilithium" is retained
for the time being. The implemented specification is commonly referred to as
version 3.1 of the CRYSTALS-Dilithium submission to NIST's third round of the
PQC competition. This is not compatible to the "Initial Public Draft" version of
FIPS 204 for which Botan does not offer an implementation.

Currently two flavors of Dilithium are implemented in separate Botan modules:

* ``dilithium``, that uses Keccak (SHAKE), and that saw some public usage
by early adopters.
* ``dilithium_aes``, that uses AES instead of Keccak-based primitives.
This mode is deprecated and will be removed in a future release.

ML-KEM (FIPS 203)
~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -816,7 +834,7 @@ Botan implements the following signature algorithms:
- ``<user ID>`` (uses ``SM3``)
- ``<user ID>,<HashFunction>``

#. Dilithium.
#. ML-DSA (Dilithium).
Takes the optional parameter ``Deterministic`` (default) or ``Randomized``.
#. SPHINCS+.
Takes the optional parameter ``Deterministic`` (default) or ``Randomized``.
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 @@ -304,6 +304,11 @@ Public Key
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:classmethod:: load_ml_dsa(mode, raw_encoding)

Load an ML-DSA public key giving the mode as a string (like
"ML-DSA-4x4") 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 @@ -399,6 +404,10 @@ Private Key

Return a private ML-KEM key

.. py:classmethod:: load_ml_dsa(mode, raw_encoding)

Return a private ML-DSA key

.. py:method:: get_public_key()

Return a public_key object
Expand Down
2 changes: 1 addition & 1 deletion readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ Public Key Cryptography
* RSA signatures and encryption
* 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 signature schemes ML-DSA (Dilithium), HSS/LMS, SPHINCS+, XMSS
* 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
4 changes: 4 additions & 0 deletions src/build-data/oids.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
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.101.3.4.3.17 = ML-DSA-4x4
2.16.840.1.101.3.4.3.18 = ML-DSA-6x5
2.16.840.1.101.3.4.3.19 = ML-DSA-8x7

# 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 Down
28 changes: 28 additions & 0 deletions src/cli/perf_pk_sig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,32 @@ BOTAN_REGISTER_PERF_TEST("Dilithium", PerfTest_Dilithium);

#endif

#if defined(BOTAN_HAS_ML_DSA)

class PerfTest_ML_DSA final : public PerfTest_PKSig {
public:
std::string algo() const override { return "ML-DSA"; }

std::string hash() const override { return ""; }

std::string format_name(const std::string& alg, const std::string& param) const override {
BOTAN_UNUSED(alg);
return param; // Param already has algo in the string
}

std::vector<std::string> keygen_params(const PerfConfig& config) const override {
BOTAN_UNUSED(config);

return {
"ML-DSA-4x4",
"ML-DSA-6x5",
"ML-DSA-8x7",
};
}
};

BOTAN_REGISTER_PERF_TEST("ML-DSA", PerfTest_ML_DSA);

#endif

} // namespace Botan_CLI
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-08-21
* This file was automatically generated by src/scripts/dev_tools/gen_oids.py on 2024-10-15
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
Expand Down Expand Up @@ -266,6 +266,9 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"2.16.840.1.101.3.4.3.14", "RSA/EMSA3(SHA-3(256))"},
{"2.16.840.1.101.3.4.3.15", "RSA/EMSA3(SHA-3(384))"},
{"2.16.840.1.101.3.4.3.16", "RSA/EMSA3(SHA-3(512))"},
{"2.16.840.1.101.3.4.3.17", "ML-DSA-4x4"},
{"2.16.840.1.101.3.4.3.18", "ML-DSA-6x5"},
{"2.16.840.1.101.3.4.3.19", "ML-DSA-8x7"},
{"2.16.840.1.101.3.4.3.2", "DSA/SHA-256"},
{"2.16.840.1.101.3.4.3.3", "DSA/SHA-384"},
{"2.16.840.1.101.3.4.3.4", "DSA/SHA-512"},
Expand Down Expand Up @@ -430,6 +433,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-DSA-4x4", OID({2, 16, 840, 1, 101, 3, 4, 3, 17})},
{"ML-DSA-6x5", OID({2, 16, 840, 1, 101, 3, 4, 3, 18})},
{"ML-DSA-8x7", OID({2, 16, 840, 1, 101, 3, 4, 3, 19})},
{"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})},
Expand Down
10 changes: 10 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,16 @@ BOTAN_FFI_EXPORT(3, 4) int botan_privkey_x448_get_privkey(botan_privkey_t key, u

BOTAN_FFI_EXPORT(3, 4) int botan_pubkey_x448_get_pubkey(botan_pubkey_t key, uint8_t pubkey[56]);

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

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

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

/*
* Algorithm specific key operations: Kyber
*/
Expand Down
56 changes: 56 additions & 0 deletions src/lib/ffi/ffi_pkey_algs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
#include <botan/frodokem.h>
#endif

#if defined(BOTAN_HAS_ML_DSA)
#include <botan/dilithium.h>
#endif

namespace {

#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
Expand Down Expand Up @@ -1092,6 +1096,58 @@ int botan_pubkey_load_ml_kem(botan_pubkey_t* key, const uint8_t pubkey[], size_t
#endif
}

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

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

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::DilithiumMode(mldsa_mode);
if(!mode.is_ml_dsa()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

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

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

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::DilithiumMode(mldsa_mode);
if(!mode.is_ml_dsa()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

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

/*
* Algorithm specific key operations: FrodoKEM
*/
Expand Down
53 changes: 0 additions & 53 deletions src/lib/pubkey/dilithium/dilithium/dilithium_modern.h

This file was deleted.

Loading
Loading