Skip to content

Commit

Permalink
Merge pull request #4291 from Rohde-Schwarz/feature/slh-dsa
Browse files Browse the repository at this point in the history
PQC: SLH-DSA
  • Loading branch information
reneme authored Oct 15, 2024
2 parents 41619a2 + bfb9130 commit 7d6c239
Show file tree
Hide file tree
Showing 55 changed files with 1,298 additions and 444 deletions.
31 changes: 23 additions & 8 deletions doc/api_ref/pubkey.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,19 +172,34 @@ signatures, then the whole scheme becomes insecure, and signatures can be
forged.

HSS-LMS
-------
~~~~~~~

A post-quantum secure hash-based signature scheme similar to XMSS. Contains
support for multitrees. It is stateful, meaning the private key changes after
each signature.

SPHINCS+
~~~~~~~~~
SLH-DSA (FIPS 205)
~~~~~~~~~~~~~~~~~~

A post-quantum secure signature scheme whose security is based (only) on the
security of a hash function. Unlike XMSS, it is a stateless signature
scheme, meaning that the private key does not change with each signature. It
has high security but very long signatures and high runtime.
The Stateless Hash-Based Digital Signature Standard (SLH-DSA)
is the FIPS 205 post-quantum secure signature scheme whose security is solely
based on the security of a hash function. Unlike XMSS, it is a stateless
signature scheme, meaning that the private key does not change with each
signature. It has high security but very long signatures and high runtime.

Support for SLH-DSA is implemented in the modules ``slh_dsa_sha2`` and ``slh_dsa_shake``.

Additionally, support for the pre-standardized version "SPHINCS+" is retained
for the time being. The implemented specification is commonly referred to as
version 3.1 of the SPHINCS+ submission to NIST's third round of the
PQC competition. This is not compatible with the "Initial Public Draft" version of
FIPS 205 for which Botan does not offer an implementation. Also, Botan does not
support the Haraka hash function.

Currently, two flavors of SPHINCS+ are implemented in separate Botan modules:

* ``sphincsplus_shake``, that uses Keccak (SHAKE) hash functions
* ``sphincsplus_sha2``, that uses SHA-256

FrodoKEM
~~~~~~~~
Expand Down Expand Up @@ -836,7 +851,7 @@ Botan implements the following signature algorithms:

#. ML-DSA (Dilithium).
Takes the optional parameter ``Deterministic`` (default) or ``Randomized``.
#. SPHINCS+.
#. SLH-DSA.
Takes the optional parameter ``Deterministic`` (default) or ``Randomized``.
#. XMSS. Takes no parameter.
#. HSS-LMS. Takes no parameter.
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 @@ -309,6 +309,11 @@ Public Key
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:classmethod:: load_slh_dsa(mode, raw_encoding)
Load an SLH-DSA public key giving the mode as a string (like
"SLH-DSA-SHAKE-128f") 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 @@ -408,6 +413,10 @@ Private Key
Return a private ML-DSA key
.. py:classmethod:: load_slh_dsa(mode, raw_encoding)
Return a private SLH-DSA key
.. py:method:: get_public_key()
Return a public_key object
Expand Down
4 changes: 2 additions & 2 deletions doc/credits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ snail-mail address (S), and Bitcoin address (B).
N: René Meusel
E: [email protected]
W: https://www.rohde-schwarz.com/cybersecurity
D: CI, TLS 1.3, Kyber, Dilithium, SPHINCS+, FrodoKEM
D: CI, TLS 1.3, Kyber, Dilithium, SLH-DSA, FrodoKEM
S: Berlin, Germany

N: Philippe Lieser
Expand All @@ -175,5 +175,5 @@ snail-mail address (S), and Bitcoin address (B).
N: Fabian Albert
E: [email protected]
W: https://www.rohde-schwarz.com/cybersecurity
D: SPHINCS+, HSS/LMS
D: SLH-DSA, HSS/LMS
S: Bochum, Germany
4 changes: 2 additions & 2 deletions readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ 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 ML-DSA (Dilithium), HSS/LMS, SPHINCS+, XMSS
* Post-quantum key agreement schemes McEliece, ML-KEM (Kyber), and FrodoKEM
* Post-quantum signature schemes ML-DSA (Dilithium), SLH-DSA (SPHINCS+), HSS/LMS, XMSS
* Post-quantum key agreement schemes ML-KEM (Kyber), McEliece, and FrodoKEM
* ElGamal encryption
* Padding schemes OAEP, PSS, PKCS #1 v1.5, X9.31

Expand Down
13 changes: 13 additions & 0 deletions src/build-data/oids.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@
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.20 = SLH-DSA-SHA2-128s
2.16.840.1.101.3.4.3.21 = SLH-DSA-SHA2-128f
2.16.840.1.101.3.4.3.22 = SLH-DSA-SHA2-192s
2.16.840.1.101.3.4.3.23 = SLH-DSA-SHA2-192f
2.16.840.1.101.3.4.3.24 = SLH-DSA-SHA2-256s
2.16.840.1.101.3.4.3.25 = SLH-DSA-SHA2-256f
2.16.840.1.101.3.4.3.26 = SLH-DSA-SHAKE-128s
2.16.840.1.101.3.4.3.27 = SLH-DSA-SHAKE-128f
2.16.840.1.101.3.4.3.28 = SLH-DSA-SHAKE-192s
2.16.840.1.101.3.4.3.29 = SLH-DSA-SHAKE-192f
2.16.840.1.101.3.4.3.30 = SLH-DSA-SHAKE-256s
2.16.840.1.101.3.4.3.31 = SLH-DSA-SHAKE-256f

# 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
35 changes: 35 additions & 0 deletions src/cli/perf_pk_sig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,41 @@ BOTAN_REGISTER_PERF_TEST("SPHINCS+", PerfTest_SPHINCSp);

#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)

class PerfTest_SLH_DSA final : public PerfTest_PKSig {
public:
std::string algo() const override { return "SLH-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 {"SLH-DSA-SHA2-128s",
"SLH-DSA-SHA2-128f",
"SLH-DSA-SHA2-192s",
"SLH-DSA-SHA2-192f",
"SLH-DSA-SHA2-256s",
"SLH-DSA-SHA2-256f",
"SLH-DSA-SHAKE-128s",
"SLH-DSA-SHAKE-128f",
"SLH-DSA-SHAKE-192s",
"SLH-DSA-SHAKE-192f",
"SLH-DSA-SHAKE-256s",
"SLH-DSA-SHAKE-256f"};
}
};

BOTAN_REGISTER_PERF_TEST("SLH-DSA", PerfTest_SLH_DSA);

#endif

#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES)

class PerfTest_Dilithium final : public PerfTest_PKSig {
Expand Down
1 change: 1 addition & 0 deletions src/cli/speed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ class Speed final : public Command {
"X448",
"Kyber",
"ML-KEM",
"SLH-DSA",
"SPHINCS+",
"FrodoKEM",
"HSS-LMS",
Expand Down
26 changes: 25 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-10-15
* 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 @@ -270,7 +270,19 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"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.20", "SLH-DSA-SHA2-128s"},
{"2.16.840.1.101.3.4.3.21", "SLH-DSA-SHA2-128f"},
{"2.16.840.1.101.3.4.3.22", "SLH-DSA-SHA2-192s"},
{"2.16.840.1.101.3.4.3.23", "SLH-DSA-SHA2-192f"},
{"2.16.840.1.101.3.4.3.24", "SLH-DSA-SHA2-256s"},
{"2.16.840.1.101.3.4.3.25", "SLH-DSA-SHA2-256f"},
{"2.16.840.1.101.3.4.3.26", "SLH-DSA-SHAKE-128s"},
{"2.16.840.1.101.3.4.3.27", "SLH-DSA-SHAKE-128f"},
{"2.16.840.1.101.3.4.3.28", "SLH-DSA-SHAKE-192s"},
{"2.16.840.1.101.3.4.3.29", "SLH-DSA-SHAKE-192f"},
{"2.16.840.1.101.3.4.3.3", "DSA/SHA-384"},
{"2.16.840.1.101.3.4.3.30", "SLH-DSA-SHAKE-256s"},
{"2.16.840.1.101.3.4.3.31", "SLH-DSA-SHAKE-256f"},
{"2.16.840.1.101.3.4.3.4", "DSA/SHA-512"},
{"2.16.840.1.101.3.4.3.5", "DSA/SHA-3(224)"},
{"2.16.840.1.101.3.4.3.6", "DSA/SHA-3(256)"},
Expand Down Expand Up @@ -500,6 +512,18 @@ std::unordered_map<std::string, OID> OID_Map::load_str2oid_map() {
{"SHA-512-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 6})},
{"SHAKE-128", OID({2, 16, 840, 1, 101, 3, 4, 2, 11})},
{"SHAKE-256", OID({2, 16, 840, 1, 101, 3, 4, 2, 12})},
{"SLH-DSA-SHA2-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 21})},
{"SLH-DSA-SHA2-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 20})},
{"SLH-DSA-SHA2-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 23})},
{"SLH-DSA-SHA2-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 22})},
{"SLH-DSA-SHA2-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 25})},
{"SLH-DSA-SHA2-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 24})},
{"SLH-DSA-SHAKE-128f", OID({2, 16, 840, 1, 101, 3, 4, 3, 27})},
{"SLH-DSA-SHAKE-128s", OID({2, 16, 840, 1, 101, 3, 4, 3, 26})},
{"SLH-DSA-SHAKE-192f", OID({2, 16, 840, 1, 101, 3, 4, 3, 29})},
{"SLH-DSA-SHAKE-192s", OID({2, 16, 840, 1, 101, 3, 4, 3, 28})},
{"SLH-DSA-SHAKE-256f", OID({2, 16, 840, 1, 101, 3, 4, 3, 31})},
{"SLH-DSA-SHAKE-256s", OID({2, 16, 840, 1, 101, 3, 4, 3, 30})},
{"SM2", OID({1, 2, 156, 10197, 1, 301, 1})},
{"SM2_Enc", OID({1, 2, 156, 10197, 1, 301, 3})},
{"SM2_Kex", OID({1, 2, 156, 10197, 1, 301, 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 @@ -1566,6 +1566,16 @@ int botan_privkey_load_ml_kem(botan_privkey_t* key, const uint8_t privkey[], siz
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: SLH-DSA
*/

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

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

/*
* Algorithm specific key operations: ECDSA and ECDH
*/
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 @@ -84,6 +84,10 @@
#include <botan/dilithium.h>
#endif

#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
#include <botan/sphincsplus.h>
#endif

namespace {

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

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

int botan_privkey_load_slh_dsa(botan_privkey_t* key, const uint8_t privkey[], size_t key_len, const char* slhdsa_mode) {
#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
if(key == nullptr || privkey == nullptr || slhdsa_mode == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::Sphincs_Parameters::create(slhdsa_mode);
if(!mode.is_slh_dsa()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

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

int botan_pubkey_load_slh_dsa(botan_pubkey_t* key, const uint8_t pubkey[], size_t key_len, const char* slhdsa_mode) {
#if defined(BOTAN_HAS_SLH_DSA_WITH_SHA2) || defined(BOTAN_HAS_SLH_DSA_WITH_SHAKE)
if(key == nullptr || pubkey == nullptr || slhdsa_mode == nullptr) {
return BOTAN_FFI_ERROR_NULL_POINTER;
}

*key = nullptr;

return ffi_guard_thunk(__func__, [=]() -> int {
auto mode = Botan::Sphincs_Parameters::create(slhdsa_mode);
if(!mode.is_slh_dsa()) {
return BOTAN_FFI_ERROR_BAD_PARAMETER;
}

auto mldsa_key = std::make_unique<Botan::SphincsPlus_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, slhdsa_mode);
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}

/*
* Algorithm specific key operations: FrodoKEM
*/
Expand Down
16 changes: 9 additions & 7 deletions src/lib/pubkey/pk_algs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
#include <botan/dilithium.h>
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON)
#include <botan/sphincsplus.h>
#endif

Expand Down Expand Up @@ -230,8 +230,9 @@ std::unique_ptr<Public_Key> load_public_key(const AlgorithmIdentifier& alg_id,
}
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) {
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON)
if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-") || alg_name.starts_with("SLH-DSA-") ||
alg_name.starts_with("Hash-SLH-DSA-")) {
return std::make_unique<SphincsPlus_PublicKey>(alg_id, key_bits);
}
#endif
Expand Down Expand Up @@ -365,8 +366,9 @@ std::unique_ptr<Private_Key> load_private_key(const AlgorithmIdentifier& alg_id,
}
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-")) {
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON)
if(alg_name == "SPHINCS+" || alg_name.starts_with("SphincsPlus-") || alg_name.starts_with("SLH-DSA-") ||
alg_name.starts_with("Hash-SLH-DSA-")) {
return std::make_unique<SphincsPlus_PrivateKey>(alg_id, key_bits);
}
#endif
Expand Down Expand Up @@ -533,8 +535,8 @@ std::unique_ptr<Private_Key> create_private_key(std::string_view alg_name,
}
#endif

#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE)
if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus-") {
#if defined(BOTAN_HAS_SPHINCS_PLUS_COMMON)
if(alg_name == "SPHINCS+" || alg_name == "SphincsPlus" || alg_name == "SLH-DSA") {
auto sphincs_params = Sphincs_Parameters::create(params);

return std::make_unique<SphincsPlus_PrivateKey>(rng, sphincs_params);
Expand Down
11 changes: 11 additions & 0 deletions src/lib/pubkey/sphincsplus/slh_dsa_sha2/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<defines>
SLH_DSA_WITH_SHA2 -> 20240806
</defines>

<module_info>
name -> "SLH-DSA (SHA-256)"
</module_info>

<requires>
sphincsplus_sha2_base
</requires>
11 changes: 11 additions & 0 deletions src/lib/pubkey/sphincsplus/slh_dsa_shake/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<defines>
SLH_DSA_WITH_SHAKE -> 20240808
</defines>

<module_info>
name -> "SLH-DSA (SHAKE)"
</module_info>

<requires>
sphincsplus_shake_base
</requires>
4 changes: 2 additions & 2 deletions src/lib/pubkey/sphincsplus/sphincsplus_common/info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ SPHINCS_PLUS_COMMON -> 20230426
</defines>

<module_info>
name -> "SPHINCS+ (common)"
brief -> "Base implementation of SPHINCS+"
name -> "SLH-DSA (common)"
brief -> "Base implementation of Stateless Hash Function DSA"
type -> "Internal"
</module_info>

Expand Down
Loading

0 comments on commit 7d6c239

Please sign in to comment.