From: Tobias Brunner Date: Fri, 4 Oct 2024 09:24:49 +0000 (+0200) Subject: wolfssl: Add support for ML-KEM X-Git-Tag: 6.0.0rc1~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1bb6f1dd73438f576d2fcd335dfe2879ed819a47;p=thirdparty%2Fstrongswan.git wolfssl: Add support for ML-KEM --- diff --git a/scripts/test.sh b/scripts/test.sh index 5c05a2bd6e..5fbc856a5e 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -54,8 +54,8 @@ build_wolfssl() --enable-aesccm --enable-aesctr --enable-camellia --enable-curve25519 --enable-curve448 --enable-des3 --enable-ecccustcurves --enable-ed25519 --enable-ed448 - --enable-keygen --with-max-rsa-bits=8192 --enable-md4 - --enable-rsapss --enable-sha3 --enable-shake256" + --enable-keygen --enable-kyber --with-max-rsa-bits=8192 + --enable-md4 --enable-rsapss --enable-sha3 --enable-shake256" git clone https://github.com/wolfSSL/wolfssl.git $WOLFSSL_DIR && cd $WOLFSSL_DIR && @@ -243,8 +243,8 @@ botan) fi ;; wolfssl) - CONFIG="--disable-defaults --enable-pki --enable-wolfssl --enable-pem --enable-pkcs1 --enable-pkcs8 --enable-x509 --enable-constraints" - export TESTS_PLUGINS="test-vectors wolfssl! pem pkcs1 pkcs8 x509 constraints" + CONFIG="--disable-defaults --enable-pki --enable-wolfssl --enable-pem --enable-pkcs1 --enable-pkcs8 --enable-x509 --enable-constraints --enable-drbg" + export TESTS_PLUGINS="test-vectors wolfssl! pem pkcs1 pkcs8 x509 constraints drbg" # build with custom options to enable all the features the plugin supports DEPS="" if test "$1" = "build-deps"; then diff --git a/src/libstrongswan/plugins/wolfssl/Makefile.am b/src/libstrongswan/plugins/wolfssl/Makefile.am index 7409bc9cde..a0fee2b491 100644 --- a/src/libstrongswan/plugins/wolfssl/Makefile.am +++ b/src/libstrongswan/plugins/wolfssl/Makefile.am @@ -25,6 +25,7 @@ libstrongswan_wolfssl_la_SOURCES = \ wolfssl_hasher.h wolfssl_hasher.c \ wolfssl_hmac.h wolfssl_hmac.c \ wolfssl_kdf.h wolfssl_kdf.c \ + wolfssl_kem.h wolfssl_kem.c \ wolfssl_rsa_public_key.h wolfssl_rsa_public_key.c \ wolfssl_rsa_private_key.h wolfssl_rsa_private_key.c \ wolfssl_rng.h wolfssl_rng.c \ diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_kem.c b/src/libstrongswan/plugins/wolfssl/wolfssl_kem.c new file mode 100644 index 0000000000..4dbec640e4 --- /dev/null +++ b/src/libstrongswan/plugins/wolfssl/wolfssl_kem.c @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2024 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include "wolfssl_common.h" + +#ifdef WOLFSSL_HAVE_KYBER + +#include +#ifdef WOLFSSL_WC_KYBER +#include +#endif +#if defined(HAVE_LIBOQS) +#include +#endif + +typedef struct private_key_exchange_t private_key_exchange_t; + +/** + * Private data. + */ +struct private_key_exchange_t { + + /** + * Public interface. + */ + key_exchange_t public; + + /** + * KE method. + */ + key_exchange_method_t method; + + /** + * Internal algorithm type. + */ + int type; + + /** + * Key pair as initiator. + */ + MlKemKey *kem; + + /** + * Ciphertext as responder. + */ + chunk_t ciphertext; + + /** + * Shared secret. + */ + chunk_t shared_secret; + + /** + * DRBG for testing. + */ + drbg_t *drbg; +}; + +/** + * Allocate random bytes from either the DRBG or a new WC_RNG instance to the + * given buffer. Make sure it has enough room. + */ +static bool get_random(private_key_exchange_t *this, size_t len, uint8_t *out) +{ + WC_RNG rng; + + if (this->drbg) + { + return this->drbg->generate(this->drbg, len, out); + } + if (wc_InitRng(&rng) != 0) + { + return FALSE; + } + if (wc_RNG_GenerateBlock(&rng, out, len) != 0) + { + wc_FreeRng(&rng); + return FALSE; + } + wc_FreeRng(&rng); + return TRUE; +} + +/** + * Generate a key pair as initiator and return the public key. + */ +static bool generate_keypair(private_key_exchange_t *this, chunk_t *public) +{ + uint8_t random[WC_ML_KEM_MAKEKEY_RAND_SZ]; + word32 len; + + if (!this->kem) + { + this->kem = malloc(sizeof(MlKemKey)); + if (wc_MlKemKey_Init(this->kem, this->type, NULL, INVALID_DEVID) != 0) + { + free(this->kem); + this->kem = NULL; + return FALSE; + } + if (!get_random(this, sizeof(random), random) || + wc_MlKemKey_MakeKeyWithRandom(this->kem, random, sizeof(random)) != 0) + { + DBG1(DBG_LIB, "%N key pair generation failed", + key_exchange_method_names, this->method); + return FALSE; + } + memwipe(random, sizeof(random)); + } + + if (wc_MlKemKey_PublicKeySize(this->kem, &len) != 0) + { + return FALSE; + } + *public = chunk_alloc(len); + + if (wc_MlKemKey_EncodePublicKey(this->kem, public->ptr, public->len) != 0) + { + DBG1(DBG_LIB, "%N public key encoding failed", + key_exchange_method_names, this->method); + chunk_free(public); + return FALSE; + } + return TRUE; +} + +METHOD(key_exchange_t, get_public_key, bool, + private_key_exchange_t *this, chunk_t *value) +{ + /* as responder, this method is called after set_public_key(), which + * encapsulated the secret to produce this ciphertext */ + if (this->ciphertext.len) + { + *value = chunk_clone(this->ciphertext); + return TRUE; + } + + /* as initiator, we generate a key pair and return the public key */ + return generate_keypair(this, value); +} + +/** + * Decapsulate the shared secret from the given ciphertext using our key pair. + */ +static bool decaps_ciphertext(private_key_exchange_t *this, chunk_t ciphertext) +{ + word32 ss_len; + + if (wc_MlKemKey_SharedSecretSize(this->kem, &ss_len) != 0) + { + return FALSE; + } + this->shared_secret = chunk_alloc(ss_len); + + /* FIXME: can't use the wc_MlKemKey alias here as it's incorrectly mapped + * to wc_KyberKey_Encapsulate */ + if (wc_KyberKey_Decapsulate(this->kem, this->shared_secret.ptr, + ciphertext.ptr, ciphertext.len) != 0) + { + DBG1(DBG_LIB, "%N decapsulation failed", + key_exchange_method_names, this->method); + return FALSE; + } + return TRUE; +} + +/** + * Generate a shared secret an encapsulate it using the given public key. + */ +static bool encaps_shared_secret(private_key_exchange_t *this, chunk_t public) +{ + uint8_t random[WC_ML_KEM_ENC_RAND_SZ]; + MlKemKey kem; + word32 ct_len, ss_len; + + if (wc_MlKemKey_Init(&kem, this->type, NULL, INVALID_DEVID) != 0) + { + return FALSE; + } + if (wc_MlKemKey_DecodePublicKey(&kem, public.ptr, public.len) != 0) + { + DBG1(DBG_LIB, "%N public key invalid", + key_exchange_method_names, this->method); + wc_MlKemKey_Free(&kem); + return FALSE; + } + + if (wc_MlKemKey_CipherTextSize(&kem, &ct_len) != 0 || + wc_MlKemKey_SharedSecretSize(&kem, &ss_len) != 0) + { + wc_MlKemKey_Free(&kem); + return FALSE; + } + this->ciphertext = chunk_alloc(ct_len); + this->shared_secret = chunk_alloc(ss_len); + + if (!get_random(this, sizeof(random), random) || + wc_MlKemKey_EncapsulateWithRandom(&kem, this->ciphertext.ptr, + this->shared_secret.ptr, random, sizeof(random)) != 0) + { + DBG1(DBG_LIB, "%N encapsulation failed", + key_exchange_method_names, this->method); + wc_MlKemKey_Free(&kem); + return FALSE; + } + memwipe(random, sizeof(random)); + wc_MlKemKey_Free(&kem); + return TRUE; +} + +METHOD(key_exchange_t, set_public_key, bool, + private_key_exchange_t *this, chunk_t value) +{ + /* as initiator, we decapsulate the secret from the given ciphertext */ + if (this->kem) + { + return decaps_ciphertext(this, value); + } + + /* as responder, we generate a secret and encapsulate it */ + return encaps_shared_secret(this, value); +} + +METHOD(key_exchange_t, get_shared_secret, bool, + private_key_exchange_t *this, chunk_t *secret) +{ + *secret = chunk_clone(this->shared_secret); + return TRUE; +} + +METHOD(key_exchange_t, get_method, key_exchange_method_t, + private_key_exchange_t *this) +{ + return this->method; +} + +METHOD(key_exchange_t, set_seed, bool, + private_key_exchange_t *this, chunk_t value, drbg_t *drbg) +{ + if (!drbg) + { + return FALSE; + } + DESTROY_IF(this->drbg); + this->drbg = drbg->get_ref(drbg); + return TRUE; +} + +METHOD(key_exchange_t, destroy, void, + private_key_exchange_t *this) +{ + chunk_clear(&this->shared_secret); + chunk_free(&this->ciphertext); + wc_MlKemKey_Free(this->kem); + free(this->kem); + DESTROY_IF(this->drbg); + free(this); +} + +/* + * Described in header + */ +key_exchange_t *wolfssl_kem_create(key_exchange_method_t method) +{ + private_key_exchange_t *this; + int type; + + switch (method) + { +#ifdef WOLFSSL_KYBER512 + case ML_KEM_512: + type = WC_ML_KEM_512; + break; +#endif +#ifdef WOLFSSL_KYBER768 + case ML_KEM_768: + type = WC_ML_KEM_768; + break; +#endif +#ifdef WOLFSSL_KYBER1024 + case ML_KEM_1024: + type = WC_ML_KEM_1024; + break; +#endif + default: + return NULL; + } + + INIT(this, + .public = { + .get_method = _get_method, + .get_public_key = _get_public_key, + .set_public_key = _set_public_key, + .get_shared_secret = _get_shared_secret, + .set_seed = _set_seed, + .destroy = _destroy, + }, + .method = method, + .type = type, + ); + return &this->public; +} + +#endif /* WOLFSSL_HAVE_KYBER */ diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_kem.h b/src/libstrongswan/plugins/wolfssl/wolfssl_kem.h new file mode 100644 index 0000000000..91778afe58 --- /dev/null +++ b/src/libstrongswan/plugins/wolfssl/wolfssl_kem.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Implements key encapsulation methods (KEM) using wolfSSL. + * + * @defgroup wolfssl_kem wolfssl_kem + * @{ @ingroup wolfssl_p + */ + +#ifndef WOLFSSL_KEM_H_ +#define WOLFSSL_KEM_H_ + +#include + +/** + * Creates a new key_exchange_t object. + * + * @param method KEM to instantiate + * @return key_exchange_t object, NULL if not supported + */ +key_exchange_t *wolfssl_kem_create(key_exchange_method_t method); + +#endif /** WOLFSSL_KEM_H_ @}*/ diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c index fa4edeeef9..c31b652d4a 100644 --- a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c +++ b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c @@ -1,6 +1,7 @@ /* - * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc. * Copyright (C) 2021 Andreas Steffen, strongSec GmbH + * Copyright (C) 2019-2024 Tobias Brunner, codelabs GmbH + * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +39,7 @@ #include "wolfssl_hasher.h" #include "wolfssl_hmac.h" #include "wolfssl_kdf.h" +#include "wolfssl_kem.h" #include "wolfssl_rsa_private_key.h" #include "wolfssl_rsa_public_key.h" #include "wolfssl_rng.h" @@ -459,6 +461,12 @@ METHOD(plugin_t, get_features, int, #endif #endif /* HAVE_ECC_VERIFY */ #endif /* HAVE_ECC */ +#ifdef WOLFSSL_HAVE_KYBER + PLUGIN_REGISTER(KE, wolfssl_kem_create), + PLUGIN_PROVIDE(KE, ML_KEM_512), + PLUGIN_PROVIDE(KE, ML_KEM_768), + PLUGIN_PROVIDE(KE, ML_KEM_1024), +#endif #if defined (HAVE_CURVE25519) || defined(HAVE_CURVE448) PLUGIN_REGISTER(KE, wolfssl_x_diffie_hellman_create), #ifdef HAVE_CURVE25519 diff --git a/testing/scripts/recipes/012_wolfssl.mk b/testing/scripts/recipes/012_wolfssl.mk index 2bbe0e87be..70e4033a76 100644 --- a/testing/scripts/recipes/012_wolfssl.mk +++ b/testing/scripts/recipes/012_wolfssl.mk @@ -26,6 +26,7 @@ CONFIG_OPTS = \ --enable-ed25519 \ --enable-ed448 \ --enable-keygen \ + --enable-kyber \ --enable-md4 \ --enable-rsapss \ --enable-sha3 \ diff --git a/testing/tests/wolfssl/rw-cert/hosts/moon/etc/strongswan.conf b/testing/tests/wolfssl/rw-cert/hosts/moon/etc/strongswan.conf index d88319dee6..093f4a201d 100755 --- a/testing/tests/wolfssl/rw-cert/hosts/moon/etc/strongswan.conf +++ b/testing/tests/wolfssl/rw-cert/hosts/moon/etc/strongswan.conf @@ -5,7 +5,7 @@ swanctl { } charon-systemd { - load = nonce test-vectors pem wolfssl x509 revocation constraints pkcs1 pubkey curl kernel-netlink socket-default updown vici + load = nonce test-vectors drbg pem wolfssl x509 revocation constraints pkcs1 pubkey curl kernel-netlink socket-default updown vici rsa_pss = yes integrity_test = yes