]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
oqs: Added post-quantum KEM methods based on liboqs
authorAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 5 Nov 2019 20:52:20 +0000 (21:52 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 5 Sep 2024 07:36:12 +0000 (09:36 +0200)
15 files changed:
configure.ac
scripts/test.sh
src/libstrongswan/Makefile.am
src/libstrongswan/plugins/oqs/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_drbg.c [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_drbg.h [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_kem.c [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_kem.h [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_plugin.c [new file with mode: 0644]
src/libstrongswan/plugins/oqs/oqs_plugin.h [new file with mode: 0644]
src/libstrongswan/plugins/oqs/tests/.gitignore [new file with mode: 0644]
src/libstrongswan/plugins/oqs/tests/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/oqs/tests/oqs_tests.c [new file with mode: 0644]
src/libstrongswan/plugins/oqs/tests/oqs_tests.h [new file with mode: 0644]
src/libstrongswan/plugins/oqs/tests/suites/test_oqs.c [new file with mode: 0644]

index 2eaeb0f5a66acbd643fe14863e1060cc668d3c09..45c40179fd4e4ac4d30de1f9864fce9f7a9401c4 100644 (file)
@@ -156,6 +156,7 @@ ARG_ENABL_SET([mgf1],           [enable the MGF1 software implementation plugin.
 ARG_ENABL_SET([newhope],        [enable New Hope crypto plugin.])
 ARG_DISBL_SET([nonce],          [disable nonce generation plugin.])
 ARG_ENABL_SET([ntru],           [enables the NTRU crypto plugin.])
+ARG_ENABL_SET([oqs],            [enable Open Quantum Safe (liboqs) plugin.])
 ARG_ENABL_SET([openssl],        [enables the OpenSSL crypto plugin.])
 ARG_ENABL_SET([wolfssl],        [enables the wolfSSL crypto plugin.])
 ARG_ENABL_SET([padlock],        [enables VIA Padlock crypto plugin.])
@@ -1225,6 +1226,14 @@ if test x$botan = xtrue; then
        LIBS=$saved_LIBS
 fi
 
+if test x$oqs = xtrue; then
+       saved_LIBS=$LIBS
+       LIBS="-lm"
+       AC_CHECK_LIB([oqs],[OQS_init],,[AC_MSG_ERROR([Open Quantum-Safe library oqs not found])],[])
+       LIBS=$saved_LIBS
+       AC_CHECK_HEADER([oqs/oqs.h],,[AC_MSG_ERROR([Open Quantum-Safe header oqs/oqs.h not found!])])
+fi
+
 if test x$uci = xtrue; then
        AC_CHECK_LIB([uci],[uci_alloc_context],[LIBS="$LIBS"],[AC_MSG_ERROR([UCI library libuci not found])],[])
        AC_CHECK_HEADER([uci.h],,[AC_MSG_ERROR([UCI header uci.h not found!])])
@@ -1591,6 +1600,7 @@ ADD_PLUGIN([ctr],                  [s charon scripts nm cmd])
 ADD_PLUGIN([ccm],                  [s charon scripts nm cmd])
 ADD_PLUGIN([gcm],                  [s charon scripts nm cmd])
 ADD_PLUGIN([ntru],                 [s charon scripts nm cmd])
+ADD_PLUGIN([oqs],                  [s charon scripts nm cmd])
 ADD_PLUGIN([drbg],                 [s charon pki scripts nm cmd])
 ADD_PLUGIN([newhope],              [s charon scripts nm cmd])
 ADD_PLUGIN([bliss],                [s charon pki scripts nm cmd])
@@ -1764,6 +1774,7 @@ AM_CONDITIONAL(USE_NTRU, test x$ntru = xtrue)
 AM_CONDITIONAL(USE_NEWHOPE, test x$newhope = xtrue)
 AM_CONDITIONAL(USE_BLISS, test x$bliss = xtrue)
 AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue)
+AM_CONDITIONAL(USE_OQS, test x$oqs = xtrue)
 
 #  charon plugins
 # ----------------
@@ -2051,6 +2062,8 @@ AC_CONFIG_FILES([
        src/libstrongswan/plugins/bliss/tests/Makefile
        src/libstrongswan/plugins/newhope/Makefile
        src/libstrongswan/plugins/newhope/tests/Makefile
+       src/libstrongswan/plugins/oqs/Makefile
+       src/libstrongswan/plugins/oqs/tests/Makefile
        src/libstrongswan/plugins/test_vectors/Makefile
        src/libstrongswan/tests/Makefile
        src/libipsec/Makefile
index 2ba4503e196ac9b6e110a51e312656da0b5fd180..2d85a8e3e5bf9e0d6c88daddfb9c545c5f5e001d 100755 (executable)
@@ -272,7 +272,7 @@ all|alpine|codeql|coverage|sonarcloud|no-dbg)
                        --disable-kernel-pfroute --disable-keychain
                        --disable-lock-profiler --disable-padlock --disable-fuzzing
                        --disable-osx-attr --disable-tkm --disable-uci
-                       --disable-unwind-backtraces
+                       --disable-oqs --disable-unwind-backtraces
                        --disable-svc --disable-dbghelp-backtraces --disable-socket-win
                        --disable-kernel-wfp --disable-kernel-iph --disable-winhttp
                        --disable-python-eggs-install"
index 66ee7b5ed9c8b90315131e392dc2cd73b04850ac..fff57f43f399e43695b1dedcb4ad9bd2a39a3a19 100644 (file)
@@ -711,6 +711,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_OQS
+  SUBDIRS += plugins/oqs
+if MONOLITHIC
+  libstrongswan_la_LIBADD += plugins/oqs/libstrongswan-oqs.la
+endif
+endif
+
 if USE_TEST_VECTORS
   SUBDIRS += plugins/test_vectors
 if MONOLITHIC
@@ -738,3 +745,7 @@ endif
 if USE_NEWHOPE
   SUBDIRS += plugins/newhope/tests
 endif
+
+if USE_OQS
+  SUBDIRS += plugins/oqs/tests
+endif
diff --git a/src/libstrongswan/plugins/oqs/Makefile.am b/src/libstrongswan/plugins/oqs/Makefile.am
new file mode 100644 (file)
index 0000000..0d96552
--- /dev/null
@@ -0,0 +1,27 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = \
+       $(PLUGIN_CFLAGS)
+
+# these files are also used by the tests, we can't directly refer to them
+# because of the subdirectory, which would cause distclean to fail
+noinst_LTLIBRARIES = libqske-oqs.la
+libqske_oqs_la_SOURCES = \
+       oqs_kem.h oqs_kem.c oqs_drbg.h oqs_drbg.c
+
+libqske_oqs_la_LIBADD = \
+       -loqs -lcrypto -lm
+
+if MONOLITHIC
+noinst_LTLIBRARIES += libstrongswan-oqs.la
+else
+plugin_LTLIBRARIES = libstrongswan-oqs.la
+endif
+
+libstrongswan_oqs_la_SOURCES = \
+       oqs_plugin.h oqs_plugin.c
+
+libstrongswan_oqs_la_LDFLAGS = -module -avoid-version
+
+libstrongswan_oqs_la_LIBADD  = libqske-oqs.la
diff --git a/src/libstrongswan/plugins/oqs/oqs_drbg.c b/src/libstrongswan/plugins/oqs/oqs_drbg.c
new file mode 100644 (file)
index 0000000..b7d777c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "oqs_drbg.h"
+
+#include <threading/thread_value.h>
+
+static thread_value_t *local_drbg;
+
+/**
+ * See header.
+ */
+void oqs_drbg_init(void)
+{
+       local_drbg = thread_value_create(NULL);
+}
+
+/**
+ * See header.
+ */
+void oqs_drbg_deinit(void)
+{
+       local_drbg->destroy(local_drbg);
+}
+
+/**
+ * See header.
+ */
+void oqs_drbg_rand(uint8_t *buffer, size_t size)
+{
+       drbg_t *drbg = local_drbg->get(local_drbg);
+
+       if (drbg)
+       {
+               drbg->generate(drbg, size, buffer);
+       }
+}
+
+/**
+ * See header.
+ */
+void oqs_drbg_set(drbg_t *drbg)
+{
+       if (drbg)
+       {
+               local_drbg->set(local_drbg, drbg);
+       }
+}
\ No newline at end of file
diff --git a/src/libstrongswan/plugins/oqs/oqs_drbg.h b/src/libstrongswan/plugins/oqs/oqs_drbg.h
new file mode 100644 (file)
index 0000000..5237030
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup oqs_p oqs
+ * @ingroup plugins
+ *
+ * @defgroup oqs_drbg oqs_drbg
+ * @{ @ingroup oqs_p
+ */
+
+#ifndef OQS_DRBG_H_
+#define OQS_DRBG_H_
+
+#include <library.h>
+
+/**
+ * Initializes the local DRBG
+ */
+void oqs_drbg_init(void);
+
+/**
+ * De-Initializes the local DRBG
+ */
+void oqs_drbg_deinit(void);
+
+/**
+ * Global random function used by liboqs
+ *
+ * @param buffer       buffer where requested random bytes are written to
+ * @param size         number of requested random bytes
+ */
+void oqs_drbg_rand(uint8_t *buffer, size_t size);
+
+/**
+ * Sets the current DRBG used by liboqs
+ *
+ * @param drbg         DRBG to be used
+ */
+void oqs_drbg_set(drbg_t *drbg);
+
+#endif /** OQS_DRBG_H_ @}*/
diff --git a/src/libstrongswan/plugins/oqs/oqs_kem.c b/src/libstrongswan/plugins/oqs/oqs_kem.c
new file mode 100644 (file)
index 0000000..c6e330c
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2018-2020 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * Based on public domain code by Erdem Alkim, Léo Ducas, Thomas Pöppelmann,
+ * and Peter Schwabe.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "oqs_kem.h"
+#include "oqs_drbg.h"
+
+#include <utils/debug.h>
+
+#include <oqs/oqs.h>
+
+typedef struct private_oqs_kem_t private_oqs_kem_t;
+
+/**
+ * Private data of an oqs_kem_t object.
+ */
+struct private_oqs_kem_t {
+
+       /**
+        * Public oqs_kem_t interface.
+        */
+       oqs_kem_t public;
+
+       /**
+        * Key exchange method
+        */
+       key_exchange_method_t method;
+
+       /**
+        * Internal OQS_KEM object
+        */
+       OQS_KEM *kem;
+
+       /**
+        * Public Key
+        */
+       uint8_t *public_key;
+
+       /**
+        * Secret Key
+        */
+       uint8_t *secret_key;
+
+       /**
+        * Ciphertext
+        */
+       uint8_t *ciphertext;
+
+       /**
+        * Shared secret
+        */
+       uint8_t *shared_secret;
+
+       /**
+        * Deterministic Random Bit Generator (DRBG)
+        */
+       drbg_t *drbg;
+
+};
+
+/**
+ * Generate the shared secret and encrypt it with the configured public key
+ */
+static bool encaps_shared_secret(private_oqs_kem_t *this)
+{
+       OQS_STATUS rc;
+
+       if (!this->ciphertext)
+       {
+               this->ciphertext = malloc(this->kem->length_ciphertext);
+       }
+
+       rc = OQS_KEM_encaps(this->kem, this->ciphertext, this->shared_secret,
+                                               this->public_key);
+       if (rc != OQS_SUCCESS)
+       {
+               DBG1(DBG_LIB, "%N encapsulation failed",
+                        key_exchange_method_names, this->method);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Set the ciphertext and decrypt the shared secret using the secret key
+ */
+static bool set_ciphertext(private_oqs_kem_t *this, chunk_t value)
+{
+       OQS_STATUS rc;
+
+       if (value.len != this->kem->length_ciphertext)
+       {
+               DBG1(DBG_LIB, "wrong %N ciphertext size of %u bytes, %u bytes expected",
+                        key_exchange_method_names, this->method, value.len,
+                        this->kem->length_ciphertext);
+               return FALSE;
+       }
+
+       rc = OQS_KEM_decaps(this->kem, this->shared_secret, value.ptr,
+                                               this->secret_key);
+       if (rc != OQS_SUCCESS)
+       {
+               DBG1(DBG_LIB, "%N decapsulation failed",
+                        key_exchange_method_names, this->method);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+METHOD(key_exchange_t, get_public_key, bool,
+       private_oqs_kem_t *this, chunk_t *value)
+{
+       OQS_STATUS rc;
+
+       oqs_drbg_set(this->drbg);
+
+       /* responder action */
+       if (this->ciphertext)
+       {
+               *value = chunk_clone(chunk_create(this->ciphertext,
+                                                                                 this->kem->length_ciphertext));
+               return TRUE;
+       }
+
+       /* initiator action */
+       if (!this->secret_key)
+       {
+               this->secret_key = malloc(this->kem->length_secret_key);
+               rc = OQS_KEM_keypair(this->kem, this->public_key, this->secret_key);
+               if (rc != OQS_SUCCESS)
+               {
+                       DBG1(DBG_LIB, "%N keypair generation failed",
+                                key_exchange_method_names, this->method);
+                       return FALSE;
+               }
+       }
+       *value = chunk_clone(chunk_create(this->public_key,
+                                                                         this->kem->length_public_key));
+       return TRUE;
+}
+
+METHOD(key_exchange_t, set_public_key, bool,
+       private_oqs_kem_t *this, chunk_t value)
+{
+       oqs_drbg_set(this->drbg);
+
+       /* initiator action */
+       if (this->secret_key)
+       {
+               return set_ciphertext(this, value);
+       }
+
+       /* responder action */
+       if (value.len != this->kem->length_public_key)
+       {
+               DBG1(DBG_LIB, "wrong %N public key size of %u bytes, %u bytes expected",
+                        key_exchange_method_names, this->method, value.len,
+                        this->kem->length_public_key);
+               return FALSE;
+       }
+       memcpy(this->public_key, value.ptr, value.len);
+
+       return encaps_shared_secret(this);
+}
+
+METHOD(key_exchange_t, get_shared_secret, bool,
+       private_oqs_kem_t *this, chunk_t *secret)
+{
+       *secret = chunk_clone(chunk_create(this->shared_secret,
+                                                                          this->kem->length_shared_secret));
+       return TRUE;
+}
+
+METHOD(key_exchange_t, get_method, key_exchange_method_t,
+       private_oqs_kem_t *this)
+{
+       return this->method;
+}
+
+METHOD(key_exchange_t, set_seed, bool,
+       private_oqs_kem_t *this, chunk_t value, drbg_t *drbg)
+{
+       if (!drbg)
+       {
+               return FALSE;
+       }
+       DESTROY_IF(this->drbg);
+       this->drbg = drbg->get_ref(drbg);
+       OQS_randombytes_custom_algorithm(oqs_drbg_rand);
+
+       return TRUE;
+}
+
+METHOD(key_exchange_t, destroy, void,
+       private_oqs_kem_t *this)
+{
+       DESTROY_IF(this->drbg);
+       memwipe(this->secret_key, this->kem->length_secret_key);
+       free(this->secret_key);
+       memwipe(this->shared_secret, this->kem->length_shared_secret);
+       free(this->shared_secret);
+       OQS_KEM_free(this->kem);
+       free(this->public_key);
+       free(this->ciphertext);
+       free(this);
+}
+
+/*
+ * Described in header.
+ */
+oqs_kem_t *oqs_kem_create(key_exchange_method_t method)
+{
+       private_oqs_kem_t *this;
+       char *kem_alg = NULL;
+       OQS_KEM *kem;
+
+       switch (method)
+       {
+               case KE_KYBER_L1:
+                       kem_alg = OQS_KEM_alg_kyber_512;
+                       break;
+               case KE_KYBER_L3:
+                       kem_alg = OQS_KEM_alg_kyber_768;
+                       break;
+               case KE_KYBER_L5:
+                       kem_alg = OQS_KEM_alg_kyber_1024;
+                       break;
+               case KE_NTRU_HPS_L1:
+                       kem_alg = OQS_KEM_alg_ntru_hps2048509;
+                       break;
+               case KE_NTRU_HPS_L3:
+                       kem_alg = OQS_KEM_alg_ntru_hps2048677;
+                       break;
+               case KE_NTRU_HPS_L5:
+                       kem_alg = OQS_KEM_alg_ntru_hps4096821;
+                       break;
+               case KE_NTRU_HRSS_L3:
+                       kem_alg = OQS_KEM_alg_ntru_hrss701;
+                       break;
+               case KE_SABER_L1:
+                       kem_alg = OQS_KEM_alg_saber_lightsaber;
+                       break;
+               case KE_SABER_L3:
+                       kem_alg = OQS_KEM_alg_saber_saber;
+                       break;
+               case KE_SABER_L5:
+                       kem_alg = OQS_KEM_alg_saber_firesaber;
+                       break;
+               case KE_FRODO_AES_L1:
+                       kem_alg = OQS_KEM_alg_frodokem_640_aes;
+                       break;
+               case KE_FRODO_AES_L3:
+                       kem_alg = OQS_KEM_alg_frodokem_976_aes;
+                       break;
+               case KE_FRODO_AES_L5:
+                       kem_alg = OQS_KEM_alg_frodokem_1344_aes;
+                       break;
+               case KE_FRODO_SHAKE_L1:
+                       kem_alg = OQS_KEM_alg_frodokem_640_shake;
+                       break;
+               case KE_FRODO_SHAKE_L3:
+                       kem_alg = OQS_KEM_alg_frodokem_976_shake;
+                       break;
+               case KE_FRODO_SHAKE_L5:
+                       kem_alg = OQS_KEM_alg_frodokem_1344_shake;
+                       break;
+               default:
+                       return NULL;
+       }
+
+       if (OQS_randombytes_switch_algorithm(OQS_RAND_alg_openssl) != OQS_SUCCESS)
+       {
+               DBG1(DBG_LIB, "OQS RNG could not be switched to openssl");
+               return NULL;
+       }
+
+       kem = OQS_KEM_new(kem_alg);
+       if (!kem)
+       {
+               DBG1(DBG_LIB, "OQS KEM '%s' not available", kem_alg);
+               return NULL;
+       }
+
+       INIT(this,
+               .public = {
+                       .ke = {
+                               .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,
+               .kem = kem,
+               .public_key = malloc(kem->length_public_key),
+               .shared_secret = malloc(kem->length_shared_secret),
+       );
+       memset(this->shared_secret, 0x00, kem->length_shared_secret);
+
+       return &this->public;
+}
diff --git a/src/libstrongswan/plugins/oqs/oqs_kem.h b/src/libstrongswan/plugins/oqs/oqs_kem.h
new file mode 100644 (file)
index 0000000..bdffe4e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018-2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup oqs_kem oqs_kem
+ * @{ @ingroup oqs_p
+ */
+
+#ifndef OQS_KEM_H_
+#define OQS_KEM_H_
+
+typedef struct oqs_kem_t oqs_kem_t;
+
+#include <crypto/key_exchange.h>
+
+/**
+ * Quantum-safe key encapsulation implementation using the OQS_KEM library
+ */
+struct oqs_kem_t {
+
+       /**
+        * Implements the key_exchange_t interface
+        */
+       key_exchange_t ke;
+};
+
+/**
+ * Creates a new oqs_kem_t object.
+ *
+ * @param method               QSKE mechanism number
+ * @return                             oqs_kem_t object, NULL if not supported
+ */
+oqs_kem_t *oqs_kem_create(key_exchange_method_t method);
+
+#endif /** OQS_KEM_H_ @}*/
+
diff --git a/src/libstrongswan/plugins/oqs/oqs_plugin.c b/src/libstrongswan/plugins/oqs/oqs_plugin.c
new file mode 100644 (file)
index 0000000..c906af1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018-2020 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "oqs_plugin.h"
+#include "oqs_kem.h"
+#include "oqs_drbg.h"
+
+#include <library.h>
+#include <threading/thread_value.h>
+
+typedef struct private_oqs_plugin_t private_oqs_plugin_t;
+
+/**
+ * private data of oqs_plugin
+ */
+struct private_oqs_plugin_t {
+
+       /**
+        * public functions
+        */
+       oqs_plugin_t public;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_oqs_plugin_t *this)
+{
+       return "oqs";
+}
+
+METHOD(plugin_t, get_features, int,
+       private_oqs_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               /* KEM-based key exchange methods */
+               PLUGIN_REGISTER(KE, oqs_kem_create),
+                       PLUGIN_PROVIDE(KE, KE_KYBER_L1),
+                       PLUGIN_PROVIDE(KE, KE_KYBER_L3),
+                       PLUGIN_PROVIDE(KE, KE_KYBER_L5),
+                       PLUGIN_PROVIDE(KE, KE_NTRU_HPS_L1),
+                       PLUGIN_PROVIDE(KE, KE_NTRU_HPS_L3),
+                       PLUGIN_PROVIDE(KE, KE_NTRU_HPS_L5),
+                       PLUGIN_PROVIDE(KE, KE_NTRU_HRSS_L3),
+                       PLUGIN_PROVIDE(KE, KE_SABER_L1),
+                       PLUGIN_PROVIDE(KE, KE_SABER_L3),
+                       PLUGIN_PROVIDE(KE, KE_SABER_L5),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_AES_L1),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_AES_L3),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_AES_L5),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L1),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L3),
+                       PLUGIN_PROVIDE(KE, KE_FRODO_SHAKE_L5),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       private_oqs_plugin_t *this)
+{
+       oqs_drbg_deinit();
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *oqs_plugin_create()
+{
+       private_oqs_plugin_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+       );
+
+       oqs_drbg_init();
+
+       return &this->public.plugin;
+}
diff --git a/src/libstrongswan/plugins/oqs/oqs_plugin.h b/src/libstrongswan/plugins/oqs/oqs_plugin.h
new file mode 100644 (file)
index 0000000..243e5f7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup oqs_p oqs
+ * @ingroup plugins
+ *
+ * @defgroup oqs_plugin oqs_plugin
+ * @{ @ingroup oqs_p
+ */
+
+#ifndef OQS_PLUGIN_H_
+#define OQS_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct oqs_plugin_t oqs_plugin_t;
+
+/**
+ * Plugin implementing quantum-safe crypto algorithms using the OQS library.
+ */
+struct oqs_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+/**
+ * Global random function used by liboqs
+ *
+ * @param buffer       buffer where requested random bytes are written to
+ * @param size         number of requested random bytes
+ */
+void oqs_rand_drbg(uint8_t *buffer, size_t size);
+
+/**
+ * Sets the current DRBG used by liboqs
+ *
+ * @param drbg         DRBG to be used
+ */
+void oqs_set_drbg(drbg_t *drbg);
+
+#endif /** OQS_PLUGIN_H_ @}*/
diff --git a/src/libstrongswan/plugins/oqs/tests/.gitignore b/src/libstrongswan/plugins/oqs/tests/.gitignore
new file mode 100644 (file)
index 0000000..2f9dfff
--- /dev/null
@@ -0,0 +1 @@
+oqs_tests
diff --git a/src/libstrongswan/plugins/oqs/tests/Makefile.am b/src/libstrongswan/plugins/oqs/tests/Makefile.am
new file mode 100644 (file)
index 0000000..2f5f0b1
--- /dev/null
@@ -0,0 +1,21 @@
+TESTS = oqs_tests
+
+check_PROGRAMS = $(TESTS)
+
+oqs_tests_SOURCES = \
+       suites/test_oqs.c \
+       oqs_tests.h oqs_tests.c
+
+oqs_tests_CFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libstrongswan/tests \
+       -I$(top_srcdir)/src/libstrongswan/plugins/oqs \
+       -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \
+       -DPLUGINS=\""${s_plugins}\"" \
+       @COVERAGE_CFLAGS@
+
+oqs_tests_LDFLAGS = @COVERAGE_LDFLAGS@
+oqs_tests_LDADD = \
+       $(top_builddir)/src/libstrongswan/libstrongswan.la \
+       $(top_builddir)/src/libstrongswan/tests/libtest.la \
+       ../libqske-oqs.la
diff --git a/src/libstrongswan/plugins/oqs/tests/oqs_tests.c b/src/libstrongswan/plugins/oqs/tests/oqs_tests.c
new file mode 100644 (file)
index 0000000..1bbdf64
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <test_runner.h>
+
+#include <library.h>
+
+/* declare test suite constructors */
+#define TEST_SUITE(x) test_suite_t* x();
+#include "oqs_tests.h"
+#undef TEST_SUITE
+
+static test_configuration_t tests[] = {
+#define TEST_SUITE(x) \
+       { .suite = x, },
+#include "oqs_tests.h"
+       { .suite = NULL, }
+};
+
+static bool test_runner_init(bool init)
+{
+       if (init)
+       {
+               char *plugins, *plugindir;
+
+               plugins = lib->settings->get_str(lib->settings,
+                                                                               "tests.load", PLUGINS);
+               plugindir = lib->settings->get_str(lib->settings,
+                                                                               "tests.plugindir", PLUGINDIR);
+               plugin_loader_add_plugindirs(plugindir, plugins);
+               if (!lib->plugins->load(lib->plugins, plugins))
+               {
+                       return FALSE;
+               }
+       }
+       else
+       {
+               lib->processor->set_threads(lib->processor, 0);
+               lib->processor->cancel(lib->processor);
+               lib->plugins->unload(lib->plugins);
+       }
+       return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_runner_run("oqs", tests, test_runner_init);
+}
diff --git a/src/libstrongswan/plugins/oqs/tests/oqs_tests.h b/src/libstrongswan/plugins/oqs/tests/oqs_tests.h
new file mode 100644 (file)
index 0000000..2c68a8c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2018-2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+TEST_SUITE(oqs_suite_create)
diff --git a/src/libstrongswan/plugins/oqs/tests/suites/test_oqs.c b/src/libstrongswan/plugins/oqs/tests/suites/test_oqs.c
new file mode 100644 (file)
index 0000000..3586145
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2018-2019 Andreas Steffen
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <oqs_kem.h>
+
+#include <library.h>
+
+#include <time.h>
+
+const int count = 20;
+
+/**
+  * Skip non-supported KE algorithms
+  */
+static bool unsupported(key_exchange_method_t method)
+{
+       switch(method)
+       {
+               case KE_BIKE_L1:
+               case KE_BIKE_L3:
+               case KE_BIKE_L5:
+               case KE_HQC_L1:
+               case KE_HQC_L3:
+               case KE_HQC_L5:
+                       return TRUE;
+               default:
+                       return FALSE;
+       }
+}
+
+START_TEST(test_oqs_good)
+{
+       chunk_t i_msg, r_msg, i_shared_secret, r_shared_secret;
+       key_exchange_method_t method = _i;
+       key_exchange_t *i_ke, *r_ke;
+       struct timespec start, stop;
+       int k;
+
+       if (unsupported(method))
+       {
+               return;
+       }
+
+       clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+       for (k = 0; k < count; k++)
+       {
+               i_ke = (key_exchange_t*)oqs_kem_create(method);
+               ck_assert(i_ke != NULL);
+               ck_assert(i_ke->get_method(i_ke) == method);
+
+               if (k == 0)
+               {
+                       ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+                       chunk_free(&i_msg);
+               }
+               ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+
+               r_ke = (key_exchange_t*)oqs_kem_create(method);
+               ck_assert(r_ke != NULL);
+
+               if (k == 0)
+               {
+                       ck_assert(r_ke->set_public_key(r_ke, i_msg));
+               }
+               ck_assert(r_ke->set_public_key(r_ke, i_msg));
+
+               if (k == 0)
+               {
+                       ck_assert(r_ke->get_public_key(r_ke, &r_msg));
+                       chunk_free(&r_msg);
+               }
+               ck_assert(r_ke->get_public_key(r_ke, &r_msg));
+               ck_assert(r_ke->get_shared_secret(r_ke, &r_shared_secret));
+
+               if (k == 0)
+               {
+                       ck_assert(i_ke->set_public_key(i_ke, r_msg));
+               }
+               ck_assert(i_ke->set_public_key(i_ke, r_msg));
+               ck_assert(i_ke->get_shared_secret(i_ke, &i_shared_secret));
+               ck_assert_chunk_eq(i_shared_secret, r_shared_secret);
+
+               /* cleanup */
+               chunk_clear(&i_shared_secret);
+               chunk_clear(&r_shared_secret);
+               chunk_free(&i_msg);
+               chunk_free(&r_msg);
+               i_ke->destroy(i_ke);
+               r_ke->destroy(r_ke);
+       }
+
+       clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stop);
+
+       DBG0(DBG_LIB, "\n%d %N loops in %d ms", count,
+                                 key_exchange_method_names, method,
+                                 (stop.tv_nsec - start.tv_nsec) / 1000000 +
+                                 (stop.tv_sec - start.tv_sec) * 1000);
+}
+END_TEST
+
+START_TEST(test_oqs_wrong)
+{
+       chunk_t i_msg, r_msg, i_shared_secret = chunk_empty, r_shared_secret;
+       key_exchange_t *i_ke, *r_ke;
+       key_exchange_method_t method = _i;
+
+       if (unsupported(method))
+       {
+               return;
+       }
+
+       /* test non-kem method */
+       if (method == KE_KYBER_L1)
+       {
+               ck_assert(!oqs_kem_create(CURVE_25519));
+       }
+
+       /* create initiator */
+       i_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(i_ke != NULL);
+       ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+
+       /* create responder */
+       r_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(r_ke != NULL);
+
+       ck_assert(r_ke->set_public_key(r_ke, i_msg));
+       ck_assert(r_ke->get_public_key(r_ke, &r_msg));
+       ck_assert(r_ke->get_shared_secret(r_ke, &r_shared_secret));
+
+       DBG0(DBG_LIB, "\n%N shared secret length of %u bytes",
+                                  key_exchange_method_names, method, r_shared_secret.len);
+
+       /* destroy 1st instance of i_ke */
+       i_ke->destroy(i_ke);
+       chunk_free(&i_msg);
+
+       /* create 2nd instance of i_ke */
+       i_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(i_ke != NULL);
+
+       ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+       if (i_ke->set_public_key(i_ke, r_msg))
+       {
+               ck_assert(i_ke->get_shared_secret(i_ke, &i_shared_secret));
+               ck_assert(!chunk_equals(i_shared_secret, r_shared_secret));
+       }
+
+       /* cleanup */
+       chunk_free(&i_msg);
+       chunk_free(&r_msg);
+       chunk_clear(&i_shared_secret);
+       chunk_clear(&r_shared_secret);
+       i_ke->destroy(i_ke);
+       r_ke->destroy(r_ke);
+}
+END_TEST
+
+START_TEST(test_oqs_fail_i)
+{
+       key_exchange_t *i_ke, *r_ke;
+       key_exchange_method_t method = _i;
+       char buf_ff[16384];
+       chunk_t i_msg, r_msg, fail_msg;
+
+       if (unsupported(method))
+       {
+               return;
+       }
+
+       memset(buf_ff, 0xff, sizeof(buf_ff));
+       fail_msg = chunk_create(buf_ff, sizeof(buf_ff));
+
+       i_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(i_ke != NULL);
+       ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+
+       r_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(r_ke != NULL);
+       ck_assert(r_ke->set_public_key(r_ke, i_msg));
+       ck_assert(r_ke->get_public_key(r_ke, &r_msg));
+
+       DBG0(DBG_LIB, "\n%N ciphertext length of %u bytes",
+                key_exchange_method_names, method, r_msg.len);
+       fail_msg.len = 0;
+       ck_assert(!i_ke->set_public_key(i_ke, fail_msg));
+       fail_msg.len = 1;
+       ck_assert(!i_ke->set_public_key(i_ke, fail_msg));
+       fail_msg.len = r_msg.len - 1;
+       ck_assert(!i_ke->set_public_key(i_ke, fail_msg));
+       fail_msg.len = r_msg.len + 1;
+       ck_assert(!i_ke->set_public_key(i_ke, fail_msg));
+
+       chunk_free(&i_msg);
+       chunk_free(&r_msg);
+       i_ke->destroy(i_ke);
+       r_ke->destroy(r_ke);
+}
+END_TEST
+
+START_TEST(test_oqs_fail_r)
+{
+       key_exchange_t *i_ke, *r_ke;
+       key_exchange_method_t method = _i;
+       char buf_ff[18432];
+       chunk_t i_msg, fail_msg;
+
+       if (unsupported(method))
+       {
+               return;
+       }
+
+       memset(buf_ff, 0xff, sizeof(buf_ff));
+       fail_msg = chunk_create(buf_ff, sizeof(buf_ff));
+
+       i_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(i_ke != NULL);
+       ck_assert(i_ke->get_public_key(i_ke, &i_msg));
+
+       r_ke = (key_exchange_t*)oqs_kem_create(method);
+       ck_assert(r_ke != NULL);
+
+       DBG0(DBG_LIB, "\n%N public key length of %u bytes",
+                                  key_exchange_method_names, method, i_msg.len);
+       fail_msg.len = 0;
+       ck_assert(!r_ke->set_public_key(r_ke, fail_msg));
+       fail_msg.len = 1;
+       ck_assert(!r_ke->set_public_key(r_ke, fail_msg));
+       fail_msg.len = i_msg.len - 1;
+       ck_assert(!r_ke->set_public_key(r_ke, fail_msg));
+       fail_msg.len = i_msg.len + 1;
+       ck_assert(!r_ke->set_public_key(r_ke, fail_msg));
+
+       chunk_free(&i_msg);
+       i_ke->destroy(i_ke);
+       r_ke->destroy(r_ke);
+}
+END_TEST
+
+Suite *oqs_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("oqs");
+
+       tc = tcase_create("good");
+       test_case_set_timeout(tc, 30);
+       tcase_add_loop_test(tc, test_oqs_good, KE_KYBER_L1, KE_HQC_L5 + 1);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("wrong");
+       tcase_add_loop_test(tc, test_oqs_wrong, KE_KYBER_L1, KE_HQC_L5 + 1);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("fail_i");
+       tcase_add_loop_test(tc, test_oqs_fail_i, KE_KYBER_L1, KE_HQC_L5 + 1);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("fail_r");
+       tcase_add_loop_test(tc, test_oqs_fail_r, KE_KYBER_L1, KE_HQC_L5 + 1);
+       suite_add_tcase(s, tc);
+
+       return s;
+}