From 50122176440f3b65c825229ef57f086a8526fa33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Mon, 14 Oct 2024 16:38:10 +0200 Subject: [PATCH] Integrate C. McEliece into FFI and Python bindings --- src/lib/ffi/ffi.h | 16 +++++++++++ src/lib/ffi/ffi_pkey_algs.cpp | 54 +++++++++++++++++++++++++++++++++++ src/python/botan3.py | 14 +++++++++ src/scripts/test_python.py | 14 +++++++++ src/tests/test_ffi.cpp | 41 ++++++++++++++++++++++++++ 5 files changed, 139 insertions(+) diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index df469d5c36..b0237fddc2 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -1544,6 +1544,22 @@ 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 operation: Classic McEliece +*/ + +BOTAN_FFI_EXPORT(3, 6) +int botan_privkey_load_classic_mceliece(botan_privkey_t* key, + const uint8_t privkey[], + size_t key_len, + const char* cmce_mode); + +BOTAN_FFI_EXPORT(3, 6) +int botan_pubkey_load_classic_mceliece(botan_pubkey_t* key, + const uint8_t pubkey[], + size_t key_len, + const char* cmce_mode); + /* * Algorithm specific key operations: ECDSA and ECDH */ diff --git a/src/lib/ffi/ffi_pkey_algs.cpp b/src/lib/ffi/ffi_pkey_algs.cpp index 9e1b1bfb2e..0c84438b70 100644 --- a/src/lib/ffi/ffi_pkey_algs.cpp +++ b/src/lib/ffi/ffi_pkey_algs.cpp @@ -79,6 +79,10 @@ #include #endif +#if defined(BOTAN_HAS_CLASSICMCELIECE) + #include +#endif + namespace { #if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO) @@ -1031,6 +1035,56 @@ int botan_pubkey_view_kyber_raw_key(botan_pubkey_t key, botan_view_ctx ctx, bota #endif } +/* +* Algorithm specific key operations: Classic McEliece +*/ + +int botan_privkey_load_classic_mceliece(botan_privkey_t* key, + const uint8_t privkey[], + size_t key_len, + const char* cmce_mode) { +#if defined(BOTAN_HAS_CLASSICMCELIECE) + if(key == nullptr || privkey == nullptr || cmce_mode == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + const auto mode = Botan::Classic_McEliece_Parameter_Set::from_string(cmce_mode); + auto cmce_key = std::make_unique(std::span{privkey, key_len}, mode); + *key = new botan_privkey_struct(std::move(cmce_key)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, privkey, key_len, cmce_mode); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + +int botan_pubkey_load_classic_mceliece(botan_pubkey_t* key, + const uint8_t pubkey[], + size_t key_len, + const char* cmce_mode) { +#if defined(BOTAN_HAS_CLASSICMCELIECE) + if(key == nullptr || pubkey == nullptr || cmce_mode == nullptr) { + return BOTAN_FFI_ERROR_NULL_POINTER; + } + + *key = nullptr; + + return ffi_guard_thunk(__func__, [=]() -> int { + const auto mode = Botan::Classic_McEliece_Parameter_Set::from_string(cmce_mode); + auto cmce_key = std::make_unique(std::span{pubkey, key_len}, mode); + *key = new botan_pubkey_struct(std::move(cmce_key)); + return BOTAN_FFI_SUCCESS; + }); +#else + BOTAN_UNUSED(key, pubkey, key_len, cmce_mode); + return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; +#endif +} + /* * Algorithm specific key operations: FrodoKEM */ diff --git a/src/python/botan3.py b/src/python/botan3.py index d4d9a540ce..95ea8610bd 100755 --- a/src/python/botan3.py +++ b/src/python/botan3.py @@ -365,6 +365,8 @@ def ffi_api(fn, args, allowed_errors=None): ffi_api(dll.botan_pubkey_view_kyber_raw_key, [c_void_p, c_void_p, VIEW_BIN_CALLBACK]) ffi_api(dll.botan_privkey_load_frodokem, [c_void_p, c_void_p, c_int, c_char_p]) ffi_api(dll.botan_pubkey_load_frodokem, [c_void_p, c_void_p, c_int, c_char_p]) + ffi_api(dll.botan_privkey_load_classic_mceliece, [c_void_p, c_void_p, c_int, c_char_p]) + ffi_api(dll.botan_pubkey_load_classic_mceliece, [c_void_p, c_void_p, c_int, c_char_p]) ffi_api(dll.botan_privkey_load_ecdsa, [c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_pubkey_load_ecdsa, [c_void_p, c_void_p, c_void_p, c_char_p]) ffi_api(dll.botan_pubkey_load_ecdh, [c_void_p, c_void_p, c_void_p, c_char_p]) @@ -1240,6 +1242,12 @@ def load_frodokem(cls, frodo_mode, key): _DLL.botan_pubkey_load_frodokem(byref(obj), key, len(key), _ctype_str(frodo_mode)) return PublicKey(obj) + @classmethod + def load_classic_mceliece(cls, cmce_mode, key): + obj = c_void_p(0) + _DLL.botan_pubkey_load_classic_mceliece(byref(obj), key, len(key), _ctype_str(cmce_mode)) + return PublicKey(obj) + def __del__(self): _DLL.botan_pubkey_destroy(self.__obj) @@ -1404,6 +1412,12 @@ def load_frodokem(cls, frodo_mode, key): _DLL.botan_privkey_load_frodokem(byref(obj), key, len(key), _ctype_str(frodo_mode)) return PrivateKey(obj) + @classmethod + def load_classic_mceliece(cls, cmce_mode, key): + obj = c_void_p(0) + _DLL.botan_privkey_load_classic_mceliece(byref(obj), key, len(key), _ctype_str(cmce_mode)) + return PrivateKey(obj) + def __del__(self): _DLL.botan_privkey_destroy(self.__obj) diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py index 6b4762db35..079e9a2b95 100644 --- a/src/scripts/test_python.py +++ b/src/scripts/test_python.py @@ -951,6 +951,20 @@ def test_frodokem_raw_keys(self): self.assertEqual(sk_read.to_raw(), sk_bits) self.assertEqual(pk_read.to_raw(), pk_bits) + def test_classic_mceliece_raw_keys(self): + cmce_mode = "mceliece348864f" + sk = botan.PrivateKey.create("ClassicMcEliece", cmce_mode, botan.RandomNumberGenerator("user")) + pk = sk.get_public_key() + + sk_bits = sk.to_raw() + pk_bits = pk.to_raw() + + sk_read = botan.PrivateKey.load_classic_mceliece(cmce_mode, sk_bits) + pk_read = botan.PublicKey.load_classic_mceliece(cmce_mode, pk_bits) + + self.assertEqual(sk_read.to_raw(), sk_bits) + self.assertEqual(pk_read.to_raw(), pk_bits) + class BotanPythonZfecTests(unittest.TestCase): """ diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp index 80f290d9cf..85bbf37aae 100644 --- a/src/tests/test_ffi.cpp +++ b/src/tests/test_ffi.cpp @@ -17,6 +17,7 @@ #include #include #include + #include #include #endif @@ -3632,6 +3633,45 @@ class FFI_FrodoKEM_Test final : public FFI_KEM_Roundtrip_Test { } }; +class FFI_Classic_McEliece_Test final : public FFI_KEM_Roundtrip_Test { + public: + std::string name() const override { return "FFI Classic McEliece"; } + + protected: + const char* algo() const override { return "ClassicMcEliece"; } + + privkey_loader_fn_t private_key_load_function() const override { return botan_privkey_load_classic_mceliece; } + + pubkey_loader_fn_t public_key_load_function() const override { return botan_pubkey_load_classic_mceliece; } + + std::vector modes() const override { + auto modes = std::vector{ + "mceliece348864f", + "mceliece460896f", + }; + if(Test::run_long_tests()) { + modes = Botan::concat(modes, + std::vector{ + "mceliece348864", + "mceliece460896", + "mceliece6688128", + "mceliece6688128f", + "mceliece6688128pc", + "mceliece6688128pcf", + "mceliece6960119", + "mceliece6960119f", + "mceliece6960119pc", + "mceliece6960119pcf", + "mceliece8192128", + "mceliece8192128f", + "mceliece8192128pc", + "mceliece8192128pcf", + }); + } + return modes; + } +}; + class FFI_ElGamal_Test final : public FFI_Test { public: std::string name() const override { return "FFI ElGamal"; } @@ -3875,6 +3915,7 @@ BOTAN_REGISTER_TEST("ffi", "ffi_kyber512", FFI_Kyber512_Test); BOTAN_REGISTER_TEST("ffi", "ffi_kyber768", FFI_Kyber768_Test); BOTAN_REGISTER_TEST("ffi", "ffi_kyber1024", FFI_Kyber1024_Test); BOTAN_REGISTER_TEST("ffi", "ffi_frodokem", FFI_FrodoKEM_Test); +BOTAN_REGISTER_TEST("ffi", "ffi_cmce", FFI_Classic_McEliece_Test); BOTAN_REGISTER_TEST("ffi", "ffi_elgamal", FFI_ElGamal_Test); BOTAN_REGISTER_TEST("ffi", "ffi_dh", FFI_DH_Test);