Skip to content

Commit

Permalink
Merge 3df2804 into aa12651
Browse files Browse the repository at this point in the history
  • Loading branch information
FAlbertDev committed Apr 2, 2024
2 parents aa12651 + 3df2804 commit d1b0483
Show file tree
Hide file tree
Showing 43 changed files with 6,544 additions and 45 deletions.
48 changes: 48 additions & 0 deletions doc/api_ref/pubkey.rst
Expand Up @@ -142,6 +142,11 @@ McEliece
Post-quantum secure key encapsulation scheme based on the hardness of certain
decoding problems.

Classic McEliece
~~~~~~~~~~~~~~~~

Post-quantum secure, code-based key encapsulation scheme.

ElGamal
~~~~~~~~

Expand Down Expand Up @@ -1128,6 +1133,7 @@ Botan implements the following KEM schemes:
#. Kyber
#. FrodoKEM
#. McEliece
#. Classic McEliece

.. _kyber_example:

Expand Down Expand Up @@ -1193,6 +1199,48 @@ parameters n and t, and have the corresponding key sizes listed:
You can check the speed of McEliece with the suggested parameters above
using ``botan speed McEliece``

Classic McEliece KEM
--------------------

`Classic McEliece <https://classic.mceliece.org/>`_ is an IND-CCA2 secure key
encapsulation algorithm based on the McEliece cryptosystem introduced in 1978.
It is a code-based scheme that relies on conservative security assumptions and
is considered secure against quantum computers. It is an alternative to
lattice-based schemes.

Other advantages of Classic McEliece are the small ciphertext size and the fast
encapsulation. Key generation and decapsulation are slower than in lattice-based
schemes. The main disadvantage of Classic McEliece is the large public key size,
ranging from 0.26 MB to 1.36 MB, depending on the instance. Due to its large key
size, Classic McEliece is recommended for applications where the public key is
stored for a long time, and memory is not a critical resource. Usage with
ephemeral keys is not recommended.

Botan's implementation covers the parameter sets of the `NIST round 4
specification <https://classic.mceliece.org/mceliece-spec-20221023.pdf#page=15>`_
and the `Classic McEliece ISO draft specification
<https://classic.mceliece.org/iso-mceliece-20230419.pdf#page=13>`_.
These are the following:

+------------------+-------------------+-------------------+--------------------+-------------------+
| Set without f/pc | Set with f | Set with pc | Set with pcf | Public Key Size |
+==================+===================+===================+====================+===================+
| mceliece348864 | mceliece348864f | | | 0.26 MB |
+------------------+-------------------+-------------------+--------------------+-------------------+
| mceliece460896 | mceliece460896f | | | 0.52 MB |
+------------------+-------------------+-------------------+--------------------+-------------------+
| mceliece6688128 | mceliece6688128f | mceliece6688128pc | mceliece6688128pcf | 1.04 MB |
+------------------+-------------------+-------------------+--------------------+-------------------+
| mceliece6960119 | mceliece6960119f | mceliece6960119pc | mceliece6960119pcf | 1.05 MB |
+------------------+-------------------+-------------------+--------------------+-------------------+
| mceliece8192128 | mceliece8192128f | mceliece8192128pc | mceliece8192128pcf | 1.36 MB |
+------------------+-------------------+-------------------+--------------------+-------------------+

The instances with the suffix 'f' use a faster key generation algorithm that is more consistent in
runtime. The instances with the suffix 'pc' use plaintext confirmation, which is only specified in
the ISO document. The instances mceliece348864(f) and mceliece460896(f) are only defined in the
NIST round 4 submission.


eXtended Merkle Signature Scheme (XMSS)
----------------------------------------
Expand Down
8 changes: 7 additions & 1 deletion doc/credits.rst
Expand Up @@ -175,5 +175,11 @@ snail-mail address (S), and Bitcoin address (B).
N: Fabian Albert
E: fabian.albert@rohde-schwarz.com
W: https://www.rohde-schwarz.com/cybersecurity
D: SPHINCS+
D: SPHINCS+, HSS/LMS, Classic McEliece
S: Bochum, Germany

N: Amos Treiber
E: amos.treiber@rohde-schwarz.com
W: https://www.rohde-schwarz.com/cybersecurity
D: SPHINCS+, FrodoKEM, Classic McEliece
S: Cologne, Germany
9 changes: 9 additions & 0 deletions doc/dev_ref/oids.rst
Expand Up @@ -84,6 +84,15 @@ Values currently assigned are::
SphincsPlus-haraka-256s-r3.1 OBJECT IDENTIFIER ::= { SphincsPlus-haraka 5 }
SphincsPlus-haraka-256f-r3.1 OBJECT IDENTIFIER ::= { SphincsPlus-haraka 6 }

mceliece OBJECT IDENTIFIER ::= { publicKey 18 }

mceliece6688128pc OBJECT IDENTIFIER ::= { mceliece 1 }
mceliece6688128pcf OBJECT IDENTIFIER ::= { mceliece 2 }
mceliece6960119pc OBJECT IDENTIFIER ::= { mceliece 3 }
mceliece6960119pcf OBJECT IDENTIFIER ::= { mceliece 4 }
mceliece8192128pc OBJECT IDENTIFIER ::= { mceliece 5 }
mceliece8192128pcf OBJECT IDENTIFIER ::= { mceliece 6 }

symmetricKey OBJECT IDENTIFIER ::= { randombit 3 }

ocbModes OBJECT IDENTIFIER ::= { symmetricKey 2 }
Expand Down
26 changes: 23 additions & 3 deletions src/build-data/oids.txt
@@ -1,6 +1,6 @@
# Regenerate with ./src/scripts/dev_tools/oids.py oids > src/lib/asn1/oid_maps.cpp
# AND ./src/scripts/dev_tools/oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp
# (if you modified something under [dn]
# Regenerate with ./src/scripts/dev_tools/gen_oids.py oids > src/lib/asn1/oid_maps.cpp
# AND ./src/scripts/dev_tools/gen_oids.py dn_ub > src/lib/x509/x509_dn_ub.cpp
# (if you modified something under [dn])

# Public key types
[pubkey]
Expand Down Expand Up @@ -65,6 +65,26 @@
1.3.6.1.4.1.25258.1.12.3.5 = SphincsPlus-haraka-256s-r3.1
1.3.6.1.4.1.25258.1.12.3.6 = SphincsPlus-haraka-256f-r3.1

# Classic McEliece OID selection from IETF Hackathon/BouncyCastle for non PC instances
1.3.6.1.4.1.22554.5.1.1 = mceliece348864
1.3.6.1.4.1.22554.5.1.2 = mceliece348864f
1.3.6.1.4.1.22554.5.1.3 = mceliece460896
1.3.6.1.4.1.22554.5.1.4 = mceliece460896f
1.3.6.1.4.1.22554.5.1.5 = mceliece6688128
1.3.6.1.4.1.22554.5.1.6 = mceliece6688128f
1.3.6.1.4.1.22554.5.1.7 = mceliece6960119
1.3.6.1.4.1.22554.5.1.8 = mceliece6960119f
1.3.6.1.4.1.22554.5.1.9 = mceliece8192128
1.3.6.1.4.1.22554.5.1.10 = mceliece8192128f

# Classic McEliece PC OIDs are currently in Botan's private arc
1.3.6.1.4.1.25258.1.18.1 = mceliece6688128pc
1.3.6.1.4.1.25258.1.18.2 = mceliece6688128pcf
1.3.6.1.4.1.25258.1.18.3 = mceliece6960119pc
1.3.6.1.4.1.25258.1.18.4 = mceliece6960119pcf
1.3.6.1.4.1.25258.1.18.5 = mceliece8192128pc
1.3.6.1.4.1.25258.1.18.6 = mceliece8192128pcf

# XMSS
1.3.6.1.4.1.25258.1.5 = XMSS-draft6
1.3.6.1.4.1.25258.1.8 = XMSS-draft12
Expand Down
44 changes: 44 additions & 0 deletions src/cli/speed.cpp
Expand Up @@ -132,6 +132,10 @@
#include <botan/frodokem.h>
#endif

#if defined(BOTAN_HAS_CLASSICMCELIECE)
#include <botan/cmce.h>
#endif

#if defined(BOTAN_HAS_ECDSA)
#include <botan/ecdsa.h>
#endif
Expand Down Expand Up @@ -636,6 +640,11 @@ class Speed final : public Command {
bench_frodokem(provider, msec);
}
#endif
#if defined(BOTAN_HAS_CLASSICMCELIECE)
else if(algo == "ClassicMcEliece") {
bench_classic_mceliece(provider, msec);
}
#endif
#if defined(BOTAN_HAS_SCRYPT)
else if(algo == "scrypt") {
bench_scrypt(provider, msec);
Expand Down Expand Up @@ -2106,6 +2115,41 @@ class Speed final : public Command {
}
#endif

#if defined(BOTAN_HAS_CLASSICMCELIECE)
void bench_classic_mceliece(const std::string& provider, std::chrono::milliseconds msec) {
std::vector<Botan::Classic_McEliece_Parameter_Set> cmce_param_sets{
Botan::Classic_McEliece_Parameter_Set::mceliece348864,
Botan::Classic_McEliece_Parameter_Set::mceliece348864f,
Botan::Classic_McEliece_Parameter_Set::mceliece460896,
Botan::Classic_McEliece_Parameter_Set::mceliece460896f,
Botan::Classic_McEliece_Parameter_Set::mceliece6688128,
Botan::Classic_McEliece_Parameter_Set::mceliece6688128f,
Botan::Classic_McEliece_Parameter_Set::mceliece6688128pc,
Botan::Classic_McEliece_Parameter_Set::mceliece6688128pcf,
Botan::Classic_McEliece_Parameter_Set::mceliece6960119,
Botan::Classic_McEliece_Parameter_Set::mceliece6960119f,
Botan::Classic_McEliece_Parameter_Set::mceliece6960119pc,
Botan::Classic_McEliece_Parameter_Set::mceliece6960119pcf,
Botan::Classic_McEliece_Parameter_Set::mceliece8192128,
Botan::Classic_McEliece_Parameter_Set::mceliece8192128f,
Botan::Classic_McEliece_Parameter_Set::mceliece8192128pc,
Botan::Classic_McEliece_Parameter_Set::mceliece8192128pcf,
};

for(auto cmce_set : cmce_param_sets) {
auto cmce_set_str = cmce_set.to_string();

auto keygen_timer = make_timer(cmce_set_str, provider, "keygen");

auto key = keygen_timer->run([&] { return Botan::Classic_McEliece_PrivateKey(rng(), cmce_set); });

record_result(keygen_timer);

bench_pk_kem(key, cmce_set_str, provider, "KDF2(SHA-256)", msec);
}
}
#endif

#if defined(BOTAN_HAS_XMSS_RFC8391)
void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) {
/*
Expand Down
34 changes: 33 additions & 1 deletion src/lib/asn1/oid_maps.cpp
@@ -1,7 +1,7 @@
/*
* OID maps
*
* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2024-02-29
* This file was automatically generated by ./src/scripts/dev_tools/gen_oids.py on 2024-03-26
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
Expand Down Expand Up @@ -140,6 +140,16 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"1.3.36.3.3.2.8.1.1.9", "brainpool320r1"},
{"1.3.6.1.4.1.11591.15.1", "OpenPGP.Ed25519"},
{"1.3.6.1.4.1.11591.4.11", "Scrypt"},
{"1.3.6.1.4.1.22554.5.1.1", "mceliece348864"},
{"1.3.6.1.4.1.22554.5.1.10", "mceliece8192128f"},
{"1.3.6.1.4.1.22554.5.1.2", "mceliece348864f"},
{"1.3.6.1.4.1.22554.5.1.3", "mceliece460896"},
{"1.3.6.1.4.1.22554.5.1.4", "mceliece460896f"},
{"1.3.6.1.4.1.22554.5.1.5", "mceliece6688128"},
{"1.3.6.1.4.1.22554.5.1.6", "mceliece6688128f"},
{"1.3.6.1.4.1.22554.5.1.7", "mceliece6960119"},
{"1.3.6.1.4.1.22554.5.1.8", "mceliece6960119f"},
{"1.3.6.1.4.1.22554.5.1.9", "mceliece8192128"},
{"1.3.6.1.4.1.25258.1.10.1", "Dilithium-4x4-AES-r3"},
{"1.3.6.1.4.1.25258.1.10.2", "Dilithium-6x5-AES-r3"},
{"1.3.6.1.4.1.25258.1.10.3", "Dilithium-8x7-AES-r3"},
Expand Down Expand Up @@ -176,6 +186,12 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"1.3.6.1.4.1.25258.1.17.1", "eFrodoKEM-640-AES"},
{"1.3.6.1.4.1.25258.1.17.2", "eFrodoKEM-976-AES"},
{"1.3.6.1.4.1.25258.1.17.3", "eFrodoKEM-1344-AES"},
{"1.3.6.1.4.1.25258.1.18.1", "mceliece6688128pc"},
{"1.3.6.1.4.1.25258.1.18.2", "mceliece6688128pcf"},
{"1.3.6.1.4.1.25258.1.18.3", "mceliece6960119pc"},
{"1.3.6.1.4.1.25258.1.18.4", "mceliece6960119pcf"},
{"1.3.6.1.4.1.25258.1.18.5", "mceliece8192128pc"},
{"1.3.6.1.4.1.25258.1.18.6", "mceliece8192128pcf"},
{"1.3.6.1.4.1.25258.1.3", "McEliece"},
{"1.3.6.1.4.1.25258.1.5", "XMSS-draft6"},
{"1.3.6.1.4.1.25258.1.6.1", "GOST-34.10-2012-256/SHA-256"},
Expand Down Expand Up @@ -573,6 +589,22 @@ std::unordered_map<std::string, OID> OID_Map::load_str2oid_map() {
{"gost_256B", OID({1, 2, 643, 7, 1, 2, 1, 1, 2})},
{"gost_512A", OID({1, 2, 643, 7, 1, 2, 1, 2, 1})},
{"gost_512B", OID({1, 2, 643, 7, 1, 2, 1, 2, 2})},
{"mceliece348864", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 1})},
{"mceliece348864f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 2})},
{"mceliece460896", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 3})},
{"mceliece460896f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 4})},
{"mceliece6688128", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 5})},
{"mceliece6688128f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 6})},
{"mceliece6688128pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 1})},
{"mceliece6688128pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 2})},
{"mceliece6960119", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 7})},
{"mceliece6960119f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 8})},
{"mceliece6960119pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 3})},
{"mceliece6960119pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 4})},
{"mceliece8192128", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 9})},
{"mceliece8192128f", OID({1, 3, 6, 1, 4, 1, 22554, 5, 1, 10})},
{"mceliece8192128pc", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 5})},
{"mceliece8192128pcf", OID({1, 3, 6, 1, 4, 1, 25258, 1, 18, 6})},
{"secp160k1", OID({1, 3, 132, 0, 9})},
{"secp160r1", OID({1, 3, 132, 0, 8})},
{"secp160r2", OID({1, 3, 132, 0, 30})},
Expand Down
130 changes: 130 additions & 0 deletions src/lib/pubkey/classic_mceliece/cmce.cpp
@@ -0,0 +1,130 @@
/*
* Classic McEliece Key Generation
* (C) 2023 Jack Lloyd
* 2023,2024 Fabian Albert, Amos Treiber - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
**/

#include <botan/cmce.h>
#include <botan/pk_ops.h>
#include <botan/rng.h>
#include <botan/internal/cmce_decaps.h>
#include <botan/internal/cmce_encaps.h>
#include <botan/internal/cmce_field_ordering.h>
#include <botan/internal/cmce_keys_internal.h>
#include <botan/internal/cmce_matrix.h>
#include <botan/internal/pk_ops_impl.h>

#include <algorithm>

namespace Botan {

Classic_McEliece_PublicKey::Classic_McEliece_PublicKey(const AlgorithmIdentifier& alg_id,
std::span<const uint8_t> key_bits) :
Classic_McEliece_PublicKey(key_bits, Classic_McEliece_Parameter_Set::from_oid(alg_id.oid())) {}

Classic_McEliece_PublicKey::Classic_McEliece_PublicKey(std::span<const uint8_t> key_bits,
Classic_McEliece_Parameter_Set param_set) {
auto params = Classic_McEliece_Parameters::create(param_set);
BOTAN_ARG_CHECK(key_bits.size() == params.pk_size_bytes(), "Wrong public key length");
m_public = std::make_shared<Classic_McEliece_PublicKeyInternal>(
params, Classic_McEliece_Matrix(params, {key_bits.begin(), key_bits.end()}));
}

Classic_McEliece_PublicKey::Classic_McEliece_PublicKey(const Classic_McEliece_PublicKey& other) {
m_public = std::make_shared<Classic_McEliece_PublicKeyInternal>(*other.m_public);
}

Classic_McEliece_PublicKey& Classic_McEliece_PublicKey::operator=(const Classic_McEliece_PublicKey& other) {
if(this != &other) {
m_public = std::make_shared<Classic_McEliece_PublicKeyInternal>(*other.m_public);
}
return *this;
}

AlgorithmIdentifier Classic_McEliece_PublicKey::algorithm_identifier() const {
return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

OID Classic_McEliece_PublicKey::object_identifier() const {
return m_public->params().object_identifier();
}

size_t Classic_McEliece_PublicKey::key_length() const {
// The key length is the dimension k of the goppa code (i.e. the code has 2^k codewords)
return m_public->params().pk_no_cols();
}

size_t Classic_McEliece_PublicKey::estimated_strength() const {
return m_public->params().estimated_strength();
}

std::vector<uint8_t> Classic_McEliece_PublicKey::public_key_bits() const {
return m_public->matrix().bytes();
}

bool Classic_McEliece_PublicKey::check_key(RandomNumberGenerator&, bool) const {
return true;
}

std::unique_ptr<Private_Key> Classic_McEliece_PublicKey::generate_another(RandomNumberGenerator& rng) const {
return std::make_unique<Classic_McEliece_PrivateKey>(rng, m_public->params().parameter_set());
}

std::unique_ptr<PK_Ops::KEM_Encryption> Classic_McEliece_PublicKey::create_kem_encryption_op(
std::string_view params, std::string_view provider) const {
if(provider.empty() || provider == "base") {
return std::make_unique<Classic_McEliece_Encryptor>(this->m_public, params);
}
throw Provider_Not_Found(algo_name(), provider);
}

Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(RandomNumberGenerator& rng,
Classic_McEliece_Parameter_Set param_set) {
auto params = Classic_McEliece_Parameters::create(param_set);
auto seed = rng.random_vec<CmceInitialSeed>(params.seed_len());
std::tie(m_private, m_public) = Classic_McEliece_KeyPair_Internal::generate(params, seed).decompose_to_pair();
}

Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(std::span<const uint8_t> sk,
Classic_McEliece_Parameter_Set param_set) {
auto params = Classic_McEliece_Parameters::create(param_set);
auto sk_internal = Classic_McEliece_PrivateKeyInternal::from_bytes(params, sk);
m_private = std::make_shared<Classic_McEliece_PrivateKeyInternal>(std::move(sk_internal));
// This creates and loads the public key, which is very large. Potentially, we could only load
// it on demand (since one may use the private key only for decapsulation without needing the public key).
// TODO: consider building a load-on-demand mechanism for the public key
m_public = Classic_McEliece_PublicKeyInternal::create_from_private_key(*m_private);
}

Classic_McEliece_PrivateKey::Classic_McEliece_PrivateKey(const AlgorithmIdentifier& alg_id,
std::span<const uint8_t> key_bits) :
Classic_McEliece_PrivateKey(key_bits, Classic_McEliece_Parameter_Set::from_oid(alg_id.oid())) {}

std::unique_ptr<Public_Key> Classic_McEliece_PrivateKey::public_key() const {
return std::make_unique<Classic_McEliece_PublicKey>(*this);
}

secure_vector<uint8_t> Classic_McEliece_PrivateKey::private_key_bits() const {
return raw_private_key_bits();
}

secure_vector<uint8_t> Classic_McEliece_PrivateKey::raw_private_key_bits() const {
return m_private->serialize();
}

bool Classic_McEliece_PrivateKey::check_key(RandomNumberGenerator&, bool) const {
return m_private->check_key();
}

std::unique_ptr<PK_Ops::KEM_Decryption> Classic_McEliece_PrivateKey::create_kem_decryption_op(
RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const {
BOTAN_UNUSED(rng);
if(provider.empty() || provider == "base") {
return std::make_unique<Classic_McEliece_Decryptor>(this->m_private, params);
}
throw Provider_Not_Found(algo_name(), provider);
}

} // namespace Botan

0 comments on commit d1b0483

Please sign in to comment.