]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
wolfssl: Add support for ML-KEM
authorTobias Brunner <tobias@strongswan.org>
Fri, 4 Oct 2024 09:24:49 +0000 (11:24 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 22 Nov 2024 13:03:17 +0000 (14:03 +0100)
scripts/test.sh
src/libstrongswan/plugins/wolfssl/Makefile.am
src/libstrongswan/plugins/wolfssl/wolfssl_kem.c [new file with mode: 0644]
src/libstrongswan/plugins/wolfssl/wolfssl_kem.h [new file with mode: 0644]
src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c
testing/scripts/recipes/012_wolfssl.mk
testing/tests/wolfssl/rw-cert/hosts/moon/etc/strongswan.conf

index 5c05a2bd6e42bf760880971548e8b0570bc91e20..5fbc856a5ef85edba04ad49b074b73ed64aaae85 100755 (executable)
@@ -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
index 7409bc9cde552380cef976e9c94fc7b4ca0f4046..a0fee2b49138c5d5f1e6b91799567a5e2629ac8c 100644 (file)
@@ -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 (file)
index 0000000..4dbec64
--- /dev/null
@@ -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 <wolfssl/wolfcrypt/kyber.h>
+#ifdef WOLFSSL_WC_KYBER
+#include <wolfssl/wolfcrypt/wc_kyber.h>
+#endif
+#if defined(HAVE_LIBOQS)
+#include <wolfssl/wolfcrypt/ext_kyber.h>
+#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 (file)
index 0000000..91778af
--- /dev/null
@@ -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 <crypto/key_exchange.h>
+
+/**
+ * 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_ @}*/
index fa4edeeef9963e3d948d1b25f6f5f8717968f475..c31b652d4ad2216d4f5762a8e6160834ba352950 100644 (file)
@@ -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
index 2bbe0e87bed502720f29879f963934531a4f44cb..70e4033a761d05989f98e63fc65ad41e5cc3c469 100644 (file)
@@ -26,6 +26,7 @@ CONFIG_OPTS = \
        --enable-ed25519 \
        --enable-ed448 \
        --enable-keygen \
+       --enable-kyber \
        --enable-md4 \
        --enable-rsapss \
        --enable-sha3 \
index d88319dee6096e50772cde8d7d66ed847d5bb271..093f4a201d36e47cdc85463caf96b727eb19ffce 100755 (executable)
@@ -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