From 9eec848fa287102704cc98a70b2d5916e58c2a18 Mon Sep 17 00:00:00 2001 From: fj-blanco Date: Wed, 30 Oct 2024 16:17:45 +0100 Subject: [PATCH] completing plugin template + adding ETSI 004 template to API fixing etsi_api template retrieving lost code fix improving key management --- configure.ac | 4 + src/libstrongswan/Makefile.am | 7 + src/libstrongswan/crypto/key_exchange.c | 1 + .../proposal/proposal_keywords_static.txt | 3 +- src/libstrongswan/plugins/qkd/Makefile.am | 14 +- src/libstrongswan/plugins/qkd/qkd_etsi_api.c | 185 ++++++++++++++++++ src/libstrongswan/plugins/qkd/qkd_etsi_api.h | 44 +++++ src/libstrongswan/plugins/qkd/qkd_kex.c | 148 +++++++++++--- src/libstrongswan/plugins/qkd/qkd_kex.h | 20 +- src/libstrongswan/plugins/qkd/qkd_plugin.c | 45 ++--- 10 files changed, 388 insertions(+), 83 deletions(-) create mode 100644 src/libstrongswan/plugins/qkd/qkd_etsi_api.c create mode 100644 src/libstrongswan/plugins/qkd/qkd_etsi_api.h diff --git a/configure.ac b/configure.ac index 8d74c168522..0b494c301e3 100644 --- a/configure.ac +++ b/configure.ac @@ -155,6 +155,7 @@ ARG_ENABL_SET([mgf1], [enable the MGF1 software implementation plugin. ARG_DISBL_SET([nonce], [disable nonce generation plugin.]) ARG_ENABL_SET([frodo], [enable FrodoKEM Post Quantum Safe plugin.]) ARG_ENABL_SET([oqs], [enable Open Quantum Safe (liboqs) plugin.]) +ARG_ENABL_SET([qkd], [enable QKD plugin.]) ARG_DISBL_SET([openssl], [disable the OpenSSL crypto plugin.]) ARG_ENABL_SET([wolfssl], [enables the wolfSSL crypto plugin.]) ARG_ENABL_SET([padlock], [enables VIA Padlock crypto plugin.]) @@ -1598,6 +1599,7 @@ ADD_PLUGIN([ccm], [s charon scripts nm cmd]) ADD_PLUGIN([gcm], [s charon scripts nm cmd]) ADD_PLUGIN([frodo], [s charon scripts nm cmd]) ADD_PLUGIN([oqs], [s charon scripts nm cmd]) +ADD_PLUGIN([qkd], [s charon scripts nm cmd]) ADD_PLUGIN([drbg], [s charon pki scripts nm cmd]) ADD_PLUGIN([curl], [s charon pki scripts nm cmd]) ADD_PLUGIN([files], [s charon pki scripts nm cmd]) @@ -1767,6 +1769,7 @@ AM_CONDITIONAL(USE_GCM, test x$gcm = xtrue) AM_CONDITIONAL(USE_AF_ALG, test x$af_alg = xtrue) AM_CONDITIONAL(USE_DRBG, test x$drbg = xtrue) AM_CONDITIONAL(USE_OQS, test x$oqs = xtrue) +AM_CONDITIONAL(USE_QKD, test x$qkd = xtrue) AM_CONDITIONAL(USE_FRODO, test x$frodo = xtrue) # charon plugins @@ -2050,6 +2053,7 @@ AC_CONFIG_FILES([ src/libstrongswan/plugins/frodo/Makefile src/libstrongswan/plugins/oqs/Makefile src/libstrongswan/plugins/oqs/tests/Makefile + src/libstrongswan/plugins/qkd/Makefile src/libstrongswan/plugins/test_vectors/Makefile src/libstrongswan/tests/Makefile src/libipsec/Makefile diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index c5ffb97d20e..047ad30fc23 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -679,6 +679,13 @@ if MONOLITHIC endif endif +if USE_QKD + SUBDIRS += plugins/qkd +if MONOLITHIC + libstrongswan_la_LIBADD += plugins/qkd/libstrongswan-qkd.la +endif +endif + if USE_FRODO SUBDIRS += plugins/frodo if MONOLITHIC diff --git a/src/libstrongswan/crypto/key_exchange.c b/src/libstrongswan/crypto/key_exchange.c index cd44ee94342..f78bceafd74 100644 --- a/src/libstrongswan/crypto/key_exchange.c +++ b/src/libstrongswan/crypto/key_exchange.c @@ -732,6 +732,7 @@ bool key_exchange_verify_pubkey(key_exchange_method_t ke, chunk_t value) case KE_HQC_L1: case KE_HQC_L3: case KE_HQC_L5: + case KE_QKD: // TODO_QKD: implement verification in plugin /* verification currently not supported, do in plugin */ valid = FALSE; break; diff --git a/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt b/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt index 613172401dc..58c5377b2c0 100644 --- a/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt +++ b/src/libstrongswan/crypto/proposal/proposal_keywords_static.txt @@ -191,5 +191,6 @@ bike5, KEY_EXCHANGE_METHOD, KE_BIKE_L5, 0 hqc1, KEY_EXCHANGE_METHOD, KE_HQC_L1, 0 hqc3, KEY_EXCHANGE_METHOD, KE_HQC_L3, 0 hqc5, KEY_EXCHANGE_METHOD, KE_HQC_L5, 0 +qkd, KEY_EXCHANGE_METHOD, KE_QKD, 0 noesn, EXTENDED_SEQUENCE_NUMBERS, NO_EXT_SEQ_NUMBERS, 0 -esn, EXTENDED_SEQUENCE_NUMBERS, EXT_SEQ_NUMBERS, 0 +esn, EXTENDED_SEQUENCE_NUMBERS, EXT_SEQ_NUMBERS, 0 \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/Makefile.am b/src/libstrongswan/plugins/qkd/Makefile.am index ae0668b676b..178d11c31e6 100644 --- a/src/libstrongswan/plugins/qkd/Makefile.am +++ b/src/libstrongswan/plugins/qkd/Makefile.am @@ -4,24 +4,20 @@ AM_CPPFLAGS = \ 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-qkd.la +noinst_LTLIBRARIES = libqkd-etsi.la -libqske_qkd_la_SOURCES = \ +libqkd_etsi_la_SOURCES = \ + qkd_etsi_api.h qkd_etsi_api.c \ qkd_kex.h qkd_kex.c -# TODO_QKD: Add any necessary libraries for QKD plugin here: - if MONOLITHIC noinst_LTLIBRARIES += libstrongswan-qkd.la else -plugin_LTLIBRARIES = libstrongswan-qkd.la +plugin_LTLIBRARIES = libstrongswan-qkd.la endif libstrongswan_qkd_la_SOURCES = \ qkd_plugin.h qkd_plugin.c libstrongswan_qkd_la_LDFLAGS = -module -avoid-version - -libstrongswan_qkd_la_LIBADD = libqske-qkd.la \ No newline at end of file +libstrongswan_qkd_la_LIBADD = libqkd-etsi.la \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/qkd_etsi_api.c b/src/libstrongswan/plugins/qkd/qkd_etsi_api.c new file mode 100644 index 00000000000..cab0a2d6ecf --- /dev/null +++ b/src/libstrongswan/plugins/qkd/qkd_etsi_api.c @@ -0,0 +1,185 @@ + /* + * Copyright (C) 2024 Javier Blanco-Romero @fj-blanco (UC3M, QURSA project) + * + * 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. + */ + +/* + * qkd_kex.c + */ +/* + * qkd_etsi_api.c + */ +#include "qkd_etsi_api.h" +#include +#include +#include + +/* Hardcoded test key */ +static u_char test_key[QKD_KEY_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; + +bool qkd_generate_random_key_id(chunk_t *key_id) +{ + int fd; + u_char random_id[QKD_KEY_ID_SIZE]; + ssize_t bytes_read; + + // Open /dev/urandom + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + { + DBG1(DBG_LIB, "QKD_plugin: failed to open /dev/urandom"); + return FALSE; + } + + // Read random bytes + bytes_read = read(fd, random_id, QKD_KEY_ID_SIZE); + close(fd); + + if (bytes_read != QKD_KEY_ID_SIZE) + { + DBG1(DBG_LIB, "QKD_plugin: failed to read random bytes"); + return FALSE; + } + + *key_id = chunk_clone(chunk_create(random_id, QKD_KEY_ID_SIZE)); + qkd_print_key_id("Generated random", *key_id); + return TRUE; +} +void qkd_print_key(const char *prefix, chunk_t key) +{ + char hex[2048] = ""; // Make sure this is large enough + chunk_to_hex(key, hex, FALSE); + DBG1(DBG_LIB, "QKD_plugin: %s key: %s", prefix, hex); +} + +void qkd_print_key_id(const char *prefix, chunk_t key_id) +{ + char hex[256] = ""; + chunk_to_hex(key_id, hex, FALSE); + DBG1(DBG_LIB, "QKD_plugin: %s key ID: %s", prefix, hex); +} + +bool qkd_set_key_id(qkd_handle_t handle, chunk_t key_id) +{ + DBG1(DBG_LIB, "QKD_plugin: qkd_set_key_id()"); + if (!handle || !handle->is_open || key_id.len != QKD_KEY_ID_SIZE) + { + return FALSE; + } + + chunk_clear(&handle->key_id); + handle->key_id = chunk_clone(key_id); + + qkd_print_key_id("Bob received", key_id); + return TRUE; +} + +bool qkd_get_key_id(qkd_handle_t handle, chunk_t *key_id) +{ + DBG1(DBG_LIB, "QKD_plugin: qkd_get_key_id()"); + if (!handle || !handle->is_open || !key_id) + { + return FALSE; + } + + // Generate random key ID for Alice + if (!qkd_generate_random_key_id(key_id)) + { + return FALSE; + } + + // Replace handle's key_id with new one + chunk_free(&handle->key_id); + handle->key_id = chunk_clone(*key_id); + + qkd_print_key_id("QKD_plugin: Alice sending", *key_id); + return TRUE; +} + +bool qkd_get_key(qkd_handle_t handle, chunk_t *key) +{ + DBG1(DBG_LIB, "QKD_plugin: qkd_get_key called"); + + if (!handle || !handle->is_open || !key) + { + DBG1(DBG_LIB, "QKD_plugin: invalid parameters in get_key"); + return FALSE; + } + + if (handle->key_id.len == 0) + { + DBG1(DBG_LIB, "QKD_plugin: no key ID set"); + return FALSE; + } + + // Print the key_id we're actually using from the exchange + qkd_print_key_id("Using", handle->key_id); + + // For demo, return the test key when given a valid key_id + // In a real implementation, we would use the key_id to look up the correct key + *key = chunk_clone(handle->key); + qkd_print_key("Retrieved", *key); + + return TRUE; +} + +bool qkd_open(qkd_handle_t *handle) +{ + DBG1(DBG_LIB, "QKD_plugin: qkd_open called"); + + if (!handle) + { + DBG1(DBG_LIB, "QKD_plugin: invalid handle pointer"); + return FALSE; + } + + *handle = malloc_thing(struct qkd_handle_t); + if (!*handle) + { + DBG1(DBG_LIB, "QKD_plugin: memory allocation failed for handle"); + return FALSE; + } + + (*handle)->id = 1; + (*handle)->is_open = TRUE; + (*handle)->key = chunk_clone(chunk_create(test_key, QKD_KEY_SIZE)); + // Initialize key_id as empty - will be set during exchange + (*handle)->key_id = chunk_empty; + + DBG1(DBG_LIB, "QKD_plugin: opened QKD connection with id %d", (*handle)->id); + return TRUE; +} + +bool qkd_close(qkd_handle_t handle) +{ + DBG1(DBG_LIB, "QKD_plugin: qkd_close called"); + + if (!handle) + { + DBG1(DBG_LIB, "QKD_plugin: invalid handle in close"); + return FALSE; + } + + // Properly free chunks before freeing handle + chunk_clear(&handle->key); + chunk_clear(&handle->key_id); + + handle->is_open = FALSE; + free(handle); + DBG1(DBG_LIB, "QKD_plugin: connection closed successfully"); + return TRUE; +} \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/qkd_etsi_api.h b/src/libstrongswan/plugins/qkd/qkd_etsi_api.h new file mode 100644 index 00000000000..36ef56fddb8 --- /dev/null +++ b/src/libstrongswan/plugins/qkd/qkd_etsi_api.h @@ -0,0 +1,44 @@ + /* + * Copyright (C) 2024 Javier Blanco-Romero @fj-blanco (UC3M, QURSA project) + * + * 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. + */ + +/* + * qkd_etsi_api.h + */ + +#ifndef QKD_ETSI_API_H_ +#define QKD_ETSI_API_H_ + +#include + +#define QKD_KEY_SIZE 32 +#define QKD_KEY_ID_SIZE 8 + +typedef struct qkd_handle_t { + int id; + bool is_open; + chunk_t key; + chunk_t key_id; +} *qkd_handle_t; + +// Declare all functions +bool qkd_open(qkd_handle_t *handle); +bool qkd_close(qkd_handle_t handle); +bool qkd_get_key(qkd_handle_t handle, chunk_t *key); +bool qkd_get_key_id(qkd_handle_t handle, chunk_t *key_id); +bool qkd_set_key_id(qkd_handle_t handle, chunk_t key_id); +bool qkd_generate_random_key_id(chunk_t *key_id); +void qkd_print_key(const char *prefix, chunk_t key); +void qkd_print_key_id(const char *prefix, chunk_t key_id); + +#endif \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/qkd_kex.c b/src/libstrongswan/plugins/qkd/qkd_kex.c index a34c7258c60..2aa3962d7d9 100644 --- a/src/libstrongswan/plugins/qkd/qkd_kex.c +++ b/src/libstrongswan/plugins/qkd/qkd_kex.c @@ -16,63 +16,142 @@ * for more details. */ -#include "qkd_kex.h" -#include "qkd_plugin.h" +/* + * qkd_kex.c + */ +#include "qkd_kex.h" +#include "qkd_etsi_api.h" #include typedef struct private_qkd_kex_t private_qkd_kex_t; -/** - * Private data of a qkd_kex_t object. - */ struct private_qkd_kex_t { - - /** - * Public qkd_kex_t interface. - */ qkd_kex_t public; - - /** - * Key exchange method - */ key_exchange_method_t method; - - /** - * Shared secret - */ - chunk_t shared_secret; + qkd_handle_t handle; }; +METHOD(key_exchange_t, get_public_key, bool, + private_qkd_kex_t *this, chunk_t *value) +{ + if (!this || !value) + { + return FALSE; + } + + // If key_id is NULL, this is Alice generating the initial key_id + if (this->handle->key_id.ptr == NULL) + { + DBG1(DBG_LIB, "QKD_plugin: Alice generating key ID"); + chunk_t key_id; + if (!qkd_get_key_id(this->handle, &key_id)) + { + DBG1(DBG_LIB, "QKD_plugin: failed to get key ID"); + return FALSE; + } + *value = chunk_clone(key_id); + chunk_clear(&key_id); + return TRUE; + } + else + { + // This is Bob, send empty response + DBG1(DBG_LIB, "QKD_plugin: Bob sending empty response"); + *value = chunk_empty; + return TRUE; + } +} + +METHOD(key_exchange_t, set_public_key, bool, + private_qkd_kex_t *this, chunk_t value) +{ + if (!this) + { + return FALSE; + } + + // Bob receives key_id from Alice + if (this->handle->key_id.ptr == NULL) + { + DBG1(DBG_LIB, "QKD_plugin: Bob receiving key ID"); + if (value.len != QKD_KEY_ID_SIZE) + { + DBG1(DBG_LIB, "QKD_plugin: invalid key ID received"); + return FALSE; + } + + if (!qkd_set_key_id(this->handle, value)) + { + DBG1(DBG_LIB, "QKD_plugin: failed to store key ID"); + return FALSE; + } + return TRUE; + } + else + { + // Alice receives empty response from Bob + DBG1(DBG_LIB, "QKD_plugin: Alice receiving empty response"); + return TRUE; + } +} + METHOD(key_exchange_t, get_shared_secret, bool, private_qkd_kex_t *this, chunk_t *secret) { - // TODO_QKD: Implement actual QKD key retrieval here + DBG1(DBG_LIB, "QKD_plugin: retrieving shared secret"); + + if (!this || !secret) + { + DBG1(DBG_LIB, "QKD_plugin: invalid parameters for get_shared_secret"); + return FALSE; + } + + this->public.shared_secret = chunk_empty; + if (!qkd_get_key(this->handle, &this->public.shared_secret)) + { + DBG1(DBG_LIB, "QKD_plugin: failed to get QKD key"); + return FALSE; + } + + *secret = chunk_clone(this->public.shared_secret); + DBG1(DBG_LIB, "QKD_plugin: successfully retrieved key of length %d", + secret->len); + return TRUE; } -METHOD(key_exchange_t, get_method, key_exchange_method_t, +METHOD(key_exchange_t, destroy, void, private_qkd_kex_t *this) { - return this->method; + DBG1(DBG_LIB, "QKD_plugin: destroy()"); + if (this) + { + chunk_clear(&this->public.shared_secret); + if (this->handle) + { + qkd_close(this->handle); + } + free(this); + } } -METHOD(key_exchange_t, destroy, void, +METHOD(key_exchange_t, get_method, key_exchange_method_t, private_qkd_kex_t *this) { - // TODO_QKD: Implement proper cleanup - free(this); + DBG1(DBG_LIB, "QKD_plugin: get_method()"); + return this->method; } -/* - * Described in header - */ qkd_kex_t *qkd_kex_create(key_exchange_method_t method) { private_qkd_kex_t *this; + DBG1(DBG_LIB, "QKD_plugin: qkd_kex_create called with method %d", method); + if (method != KE_QKD) { + DBG1(DBG_LIB, "QKD_plugin: unsupported key exchange method"); return NULL; } @@ -82,14 +161,23 @@ qkd_kex_t *qkd_kex_create(key_exchange_method_t method) .get_shared_secret = _get_shared_secret, .get_method = _get_method, .destroy = _destroy, - // Remove get_public_key and set_public_key + .get_public_key = _get_public_key, + .set_public_key = _set_public_key, + .set_seed = (void*)return_false, }, + .shared_secret = chunk_empty, }, .method = method, - .shared_secret = chunk_alloc(32), // TODO_QKD: Adjust size as needed + .handle = NULL ); - // TODO_QKD: Initialize QKD system or perform any necessary setup here + // Open handle at creation time + if (!qkd_open(&this->handle)) + { + free(this); + return NULL; + } + DBG1(DBG_LIB, "QKD_plugin: key exchange object created"); return &this->public; } \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/qkd_kex.h b/src/libstrongswan/plugins/qkd/qkd_kex.h index 1680e7fbc94..17b2b6f672c 100644 --- a/src/libstrongswan/plugins/qkd/qkd_kex.h +++ b/src/libstrongswan/plugins/qkd/qkd_kex.h @@ -16,11 +16,9 @@ * for more details. */ -/** - * @defgroup qkd_kex qkd_kex - * @{ @ingroup qkd_p +/* + * qkd_kex.h */ - #ifndef QKD_KEX_H_ #define QKD_KEX_H_ @@ -28,23 +26,11 @@ typedef struct qkd_kex_t qkd_kex_t; #include -/** - * QKD-based key exchange implementation - */ struct qkd_kex_t { - - /** - * Implements the key_exchange_t interface - */ key_exchange_t ke; + chunk_t shared_secret; }; -/** - * Creates a new qkd_kex_t object. - * - * @param method Key exchange method - * @return qkd_kex_t object, NULL if not supported - */ qkd_kex_t *qkd_kex_create(key_exchange_method_t method); #endif /** QKD_KEX_H_ @}*/ \ No newline at end of file diff --git a/src/libstrongswan/plugins/qkd/qkd_plugin.c b/src/libstrongswan/plugins/qkd/qkd_plugin.c index 170ec03d3c8..b9597622b01 100644 --- a/src/libstrongswan/plugins/qkd/qkd_plugin.c +++ b/src/libstrongswan/plugins/qkd/qkd_plugin.c @@ -1,33 +1,17 @@ /* - * Copyright (C) 2018-2020 Andreas Steffen - * - * Copyright (C) secunet Security Networks AG - * - * Copyright (C) 2024 Javier Blanco-Romero @fj-blanco (UC3M, QURSA project) - * - * 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. + * qkd_plugin.c */ #include "qkd_plugin.h" #include "qkd_kex.h" - #include typedef struct private_qkd_plugin_t private_qkd_plugin_t; /** - * private data of qkd_plugin + * Private data of qkd_plugin */ struct private_qkd_plugin_t { - /** * public functions */ @@ -47,26 +31,31 @@ METHOD(plugin_t, get_features, int, /* QKD-based key exchange method */ PLUGIN_REGISTER(KE, qkd_kex_create), PLUGIN_PROVIDE(KE, KE_QKD), - // TODO_QKD: add additional features here + /* TODO_QKD: add additional features here */ }; + *features = f; + DBG1(DBG_LIB, "QKD_plugin: QKD plugin providing %d features", countof(f)); return countof(f); } METHOD(plugin_t, destroy, void, private_qkd_plugin_t *this) { - // TODO_QKD: add necessary cleanup + DBG2(DBG_LIB, "QKD_plugin: destroying QKD plugin"); + /* TODO_QKD: add necessary cleanup */ free(this); } -/* - * see header file +/** + * See header file */ plugin_t *qkd_plugin_create() { private_qkd_plugin_t *this; - + + DBG1(DBG_LIB, "QKD_plugin: plugin_create called"); + INIT(this, .public = { .plugin = { @@ -76,8 +65,12 @@ plugin_t *qkd_plugin_create() }, }, ); - - // TODO_QKD: further initialization here - + + if (!this) { + DBG1(DBG_LIB, "QKD_plugin: INIT failed"); + return NULL; + } + + DBG1(DBG_LIB, "QKD_plugin: plugin initialized successfully"); return &this->public.plugin; } \ No newline at end of file