From: Andreas Steffen Date: Tue, 5 Nov 2019 20:52:20 +0000 (+0100) Subject: oqs: Added post-quantum KEM methods based on liboqs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f971be2ee07c6e2af4c84a539056ac95a16484ac;p=thirdparty%2Fstrongswan.git oqs: Added post-quantum KEM methods based on liboqs --- diff --git a/configure.ac b/configure.ac index 541a5877d0..e3b8ba09db 100644 --- a/configure.ac +++ b/configure.ac @@ -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.]) @@ -1211,6 +1212,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!])]) @@ -1577,6 +1586,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]) @@ -1748,6 +1758,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 # ---------------- @@ -2031,6 +2042,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 diff --git a/scripts/test.sh b/scripts/test.sh index d9d03beb29..7142c7600e 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -237,7 +237,7 @@ all|codeql|coverage|sonarcloud) --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" diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index f118c723ae..538b70125b 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -689,6 +689,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 @@ -716,3 +723,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 index 0000000000..0d96552df1 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/Makefile.am @@ -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 index 0000000000..b7d777cf87 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_drbg.c @@ -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 . + * + * 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 + +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 index 0000000000..5237030c75 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_drbg.h @@ -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 . + * + * 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 + +/** + * 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 index 0000000000..60e4e8010a --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_kem.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2018-2019 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 . + * + * 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 + +#include + +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); + OQS_KEM_free(this->kem); + 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); + 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_NEWHOPE_L1: + kem_alg = OQS_KEM_alg_newhope_512cca; + break; + case KE_NEWHOPE_L5: + kem_alg = OQS_KEM_alg_newhope_1024cca; + 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; + 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_BIKE1_L1: + kem_alg = OQS_KEM_alg_bike1_l1; + break; + case KE_BIKE1_L3: + kem_alg = OQS_KEM_alg_bike1_l3; + break; + case KE_BIKE1_L5: + kem_alg = OQS_KEM_alg_bike1_l5; + break; + case KE_BIKE2_L1: + kem_alg = OQS_KEM_alg_bike2_l1; + break; + case KE_BIKE2_L3: + kem_alg = OQS_KEM_alg_bike2_l3; + break; + case KE_BIKE2_L5: + kem_alg = OQS_KEM_alg_bike2_l5; + break; + case KE_BIKE3_L1: + kem_alg = OQS_KEM_alg_bike3_l1; + break; + case KE_BIKE3_L3: + kem_alg = OQS_KEM_alg_bike3_l3; + break; + case KE_BIKE3_L5: + kem_alg = OQS_KEM_alg_bike3_l5; + break; + case KE_SIKE_L1: + kem_alg = OQS_KEM_alg_sike_p434; + break; + case KE_SIKE_L2: + kem_alg = OQS_KEM_alg_sike_p503; + break; + case KE_SIKE_L3: + kem_alg = OQS_KEM_alg_sike_p610; + break; + case KE_SIKE_L5: + kem_alg = OQS_KEM_alg_sike_p751; + 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; + 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 index 0000000000..bdffe4e2e5 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_kem.h @@ -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 . + * + * 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 + +/** + * 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 index 0000000000..f52c1a3637 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_plugin.c @@ -0,0 +1,112 @@ +/* + * 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 . + * + * 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 +#include + +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_BIKE1_L1), + PLUGIN_PROVIDE(KE, KE_BIKE1_L3), + PLUGIN_PROVIDE(KE, KE_BIKE1_L5), + PLUGIN_PROVIDE(KE, KE_BIKE2_L1), + PLUGIN_PROVIDE(KE, KE_BIKE2_L3), + PLUGIN_PROVIDE(KE, KE_BIKE2_L5), + PLUGIN_PROVIDE(KE, KE_BIKE3_L1), + PLUGIN_PROVIDE(KE, KE_BIKE3_L3), + PLUGIN_PROVIDE(KE, KE_BIKE3_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), + PLUGIN_PROVIDE(KE, KE_KYBER_L1), + PLUGIN_PROVIDE(KE, KE_KYBER_L3), + PLUGIN_PROVIDE(KE, KE_KYBER_L5), + PLUGIN_PROVIDE(KE, KE_NEWHOPE_L1), + PLUGIN_PROVIDE(KE, KE_NEWHOPE_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_SIKE_L1), + PLUGIN_PROVIDE(KE, KE_SIKE_L2), + PLUGIN_PROVIDE(KE, KE_SIKE_L3), + PLUGIN_PROVIDE(KE, KE_SIKE_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 index 0000000000..243e5f72e4 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/oqs_plugin.h @@ -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 . + * + * 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 + +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 index 0000000000..2f9dfffc20 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/tests/.gitignore @@ -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 index 0000000000..2f5f0b16b2 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/tests/Makefile.am @@ -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 index 0000000000..1bbdf64f28 --- /dev/null +++ b/src/libstrongswan/plugins/oqs/tests/oqs_tests.c @@ -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 . + * + * 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 + +#include + +/* 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 index 0000000000..2c68a8cb6c --- /dev/null +++ b/src/libstrongswan/plugins/oqs/tests/oqs_tests.h @@ -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 . + * + * 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 index 0000000000..b13dd9a78a --- /dev/null +++ b/src/libstrongswan/plugins/oqs/tests/suites/test_oqs.c @@ -0,0 +1,242 @@ +/* + * 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 . + * + * 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 + +#include + +#include + +const int count = 10; + +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; + + 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; + + /* test non-kem method */ + if (method == KE_BIKE1_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; + + + 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; + + 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_BIKE1_L1, KE_SIKE_L5 + 1); + suite_add_tcase(s, tc); + + tc = tcase_create("wrong"); + tcase_add_loop_test(tc, test_oqs_wrong, KE_BIKE1_L1, KE_SIKE_L5 + 1); + suite_add_tcase(s, tc); + + tc = tcase_create("fail_i"); + tcase_add_loop_test(tc, test_oqs_fail_i, KE_BIKE1_L1, KE_SIKE_L5 + 1); + suite_add_tcase(s, tc); + + tc = tcase_create("fail_r"); + tcase_add_loop_test(tc, test_oqs_fail_r, KE_BIKE1_L1, KE_SIKE_L5 + 1); + suite_add_tcase(s, tc); + + return s; +}