aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2024-11-05 10:22:10 +0100
committerToni Uhlig <matzeton@googlemail.com>2025-01-26 20:40:33 +0100
commit2f81928de970878a3a24bffc32a2ec3ffaeac7ba (patch)
tree4a6ef77511578693b7c09d437ecff70346625853
parent899e5a80d67610af1ee9c2d0231a9a23ba3248e1 (diff)
initial nDPId UDP crypto [WiP!]
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r--CMakeLists.txt22
-rw-r--r--nDPId-test.c212
-rw-r--r--nDPId.c134
-rw-r--r--ncrypt.c499
-rw-r--r--ncrypt.h91
-rw-r--r--nio.c2
-rw-r--r--nio.h2
-rwxr-xr-xscripts/gen_keypair.sh21
8 files changed, 971 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1bcae37de..e6a51f26d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,6 +93,7 @@ option(ENABLE_SANITIZER_THREAD "Enable TSAN (does not work together with ASAN)."
option(ENABLE_MEMORY_PROFILING "Enable dynamic memory tracking." OFF)
option(ENABLE_ZLIB "Enable zlib support for nDPId (experimental)." OFF)
option(ENABLE_SYSTEMD "Install systemd components." OFF)
+option(ENABLE_CRYPTO "Enable OpenSSL cryptographic support in nDPId/nDPIsrvd." OFF)
option(BUILD_EXAMPLES "Build C examples." ON)
if(BUILD_EXAMPLES)
option(ENABLE_DBUS "Build DBus notification example." OFF)
@@ -196,7 +197,10 @@ endif()
if(ENABLE_PFRING)
set(NDPID_PFRING_SRCS npfring.c)
endif()
-add_executable(nDPId nDPId.c ${NDPID_PFRING_SRCS} nio.c utils.c)
+if(ENABLE_CRYPTO)
+ set(CRYPTO_SRCS ncrypt.c)
+endif()
+add_executable(nDPId nDPId.c ${NDPID_PFRING_SRCS} ${CRYPTO_SRCS} nio.c utils.c)
add_executable(nDPIsrvd nDPIsrvd.c nio.c utils.c)
add_executable(nDPId-test nDPId-test.c ${NDPID_PFRING_SRCS})
@@ -384,6 +388,12 @@ if(BUILD_NDPI)
add_dependencies(nDPId-test libnDPI)
endif()
+if(ENABLE_CRYPTO)
+ find_package(OpenSSL REQUIRED)
+ set(OSSL_DEFS "-DENABLE_CRYPTO=1")
+ set(OSSL_LIBRARY "${OPENSSL_CRYPTO_LIBRARY}")
+endif()
+
if(STATIC_LIBNDPI_INSTALLDIR OR BUILD_NDPI OR NDPI_NO_PKGCONFIG)
if(NDPI_WITH_GCRYPT)
find_package(GCRYPT "1.4.2" REQUIRED)
@@ -447,25 +457,26 @@ if(NOT pkgcfg_lib_PCAP_pcap)
endif()
target_compile_options(nDPId PRIVATE "-pthread")
-target_compile_definitions(nDPId PRIVATE -D_GNU_SOURCE=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\" ${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS})
+target_compile_definitions(nDPId PRIVATE -D_GNU_SOURCE=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\"
+ ${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS} ${OSSL_DEFS})
target_include_directories(nDPId PRIVATE "${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" ${NDPID_DEPS_INC} ${PFRING_KERNEL_INC} ${PFRING_INC})
target_link_libraries(nDPId "${STATIC_LIBNDPI_LIB}" "${STATIC_PFRING_LIB}" "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}"
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "${PF_RING_LIB}"
- "-pthread")
+ "${OSSL_LIBRARY}" "-pthread")
target_compile_definitions(nDPIsrvd PRIVATE -D_GNU_SOURCE=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\" ${NDPID_DEFS} ${EPOLL_DEFS})
target_include_directories(nDPIsrvd PRIVATE ${NDPID_DEPS_INC})
target_compile_options(nDPId-test PRIVATE "-Wno-unused-function" "-pthread")
target_compile_definitions(nDPId-test PRIVATE -D_GNU_SOURCE=1 -DNO_MAIN=1 -DPKG_VERSION=\"${PKG_VERSION}\" -DGIT_VERSION=\"${GIT_VERSION}\"
- ${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS} ${NDPID_TEST_MPROF_DEFS})
+ ${NDPID_DEFS} ${EPOLL_DEFS} ${ZLIB_DEFS} ${PFRING_DEFS} ${OSSL_DEFS} ${NDPID_TEST_MPROF_DEFS})
target_include_directories(nDPId-test PRIVATE
"${STATIC_LIBNDPI_INC}" "${DEFAULT_NDPI_INCLUDE}" ${NDPID_DEPS_INC} ${PFRING_KERNEL_INC} ${PFRING_INC})
target_link_libraries(nDPId-test "${STATIC_LIBNDPI_LIB}" "${STATIC_PFRING_LIB}" "${pkgcfg_lib_PCAP_pcap}" "${pkgcfg_lib_NDPI_ndpi}"
"${pkgcfg_lib_PCRE_pcre2-8}" "${pkgcfg_lib_MAXMINDDB_maxminddb}" "${pkgcfg_lib_ZLIB_z}"
"${GCRYPT_LIBRARY}" "${GCRYPT_ERROR_LIBRARY}" "${PCAP_LIBRARY}" "${LIBM_LIB}" "${PF_RING_LIB}"
- "-pthread")
+ "${OSSL_LIBRARY}" "-pthread")
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_executable(fuzz_ndpi_process_packet test/fuzz_ndpi_process_packet.c)
@@ -614,6 +625,7 @@ message(STATUS "ENABLE_PFRING............: ${ENABLE_PFRING}")
if(ENABLE_PFRING)
message(STATUS "PFRING_LINK_STATIC.......: ${PFRING_LINK_STATIC}")
endif()
+message(STATUS "ENABLE_CRYPTO............: ${ENABLE_CRYPTO}")
message(STATUS "ENABLE_COVERAGE..........: ${ENABLE_COVERAGE}")
message(STATUS "ENABLE_SANITIZER.........: ${ENABLE_SANITIZER}")
message(STATUS "ENABLE_SANITIZER_THREAD..: ${ENABLE_SANITIZER_THREAD}")
diff --git a/nDPId-test.c b/nDPId-test.c
index e1d2bf9f5..5210513f7 100644
--- a/nDPId-test.c
+++ b/nDPId-test.c
@@ -10,6 +10,9 @@ extern void nDPIsrvd_memprof_log_free(size_t free_size);
// #define VERBOSE_MEMORY_PROFILING 1
#define NO_MAIN 1
#include "utils.c"
+#ifdef ENABLE_CRYPTO
+#include "ncrypt.c"
+#endif
#include "nio.c"
#include "nDPIsrvd.c"
#include "nDPId.c"
@@ -1638,6 +1641,210 @@ error:
return 1;
}
+#ifdef ENABLE_CRYPTO
+static int ncrypt_selftest()
+{
+ int ret = 0;
+ struct ncrypt nc_peer1 = {};
+ struct ncrypt nc_peer2 = {};
+ unsigned char peer1_priv_key[NCRYPT_X25519_KEYLEN];
+ unsigned char peer2_priv_key[NCRYPT_X25519_KEYLEN];
+ unsigned char peer1_pub_key[NCRYPT_X25519_KEYLEN];
+ unsigned char peer2_pub_key[NCRYPT_X25519_KEYLEN];
+ unsigned char iv[NCRYPT_AES_IVLEN];
+
+ if (ncrypt_keygen(peer1_priv_key, peer1_pub_key) != 0)
+ {
+ ret++;
+ }
+ if (ncrypt_keygen(peer2_priv_key, peer2_pub_key) != 0)
+ {
+ ret++;
+ }
+ if (ncrypt_init(&nc_peer1, peer1_priv_key, peer2_pub_key) != 0)
+ {
+ ret++;
+ }
+ if (ncrypt_init(&nc_peer2, peer2_priv_key, peer1_pub_key) != 0)
+ {
+ ret++;
+ }
+ if (ncrypt_init_encrypt(&nc_peer1) != 0)
+ {
+ ret++;
+ }
+ if (ncrypt_init_decrypt(&nc_peer2, nc_peer1.iv) != 0)
+ {
+ ret++;
+ }
+ if (memcmp(nc_peer1.shared_secret, nc_peer2.shared_secret, NCRYPT_X25519_KEYLEN) != 0)
+ {
+ ret++;
+ }
+ if (memcmp(nc_peer1.iv, nc_peer2.iv, NCRYPT_AES_IVLEN) != 0)
+ {
+ ret++;
+ }
+
+ memcpy(iv, nc_peer1.iv, NCRYPT_AES_IVLEN);
+ unsigned char plaintext[] = "Secret Message";
+ unsigned char encrypted1[NCRYPT_BUFFER_SIZE];
+ unsigned char tag1[NCRYPT_TAG_SIZE];
+ unsigned char encrypted2[NCRYPT_BUFFER_SIZE];
+ unsigned char tag2[NCRYPT_TAG_SIZE];
+ unsigned char tmp1[NETWORK_BUFFER_MAX_SIZE];
+ unsigned char tmp2[NETWORK_BUFFER_MAX_SIZE];
+
+ memset(encrypted1, 0xFF, sizeof(encrypted1));
+ memset(encrypted2, 0xFF, sizeof(encrypted2));
+ memset(tag1, 0xFF, sizeof(tag1));
+ memset(tag2, 0xFF, sizeof(tag2));
+
+ int enc_bytes;
+ int dec_bytes;
+
+ enc_bytes = ncrypt_encrypt(&nc_peer1, plaintext, sizeof(plaintext), encrypted1, tag1);
+ dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted1, enc_bytes, tag1, tmp1);
+
+ if (enc_bytes != dec_bytes)
+ {
+ ret++;
+ }
+ if (memcmp(plaintext, tmp1, dec_bytes) != 0)
+ {
+ ret++;
+ }
+ memset(tmp1, 0xFF, sizeof(tmp1));
+
+ enc_bytes = ncrypt_encrypt(&nc_peer1, plaintext, sizeof(plaintext), encrypted2, tag2);
+ dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted2, enc_bytes, tag2, tmp1);
+
+ if (enc_bytes != dec_bytes)
+ {
+ ret++;
+ }
+ if (memcmp(plaintext, tmp1, dec_bytes) != 0)
+ {
+ ret++;
+ }
+
+ if (memcmp(tag1, tag2, NCRYPT_TAG_SIZE) == 0)
+ {
+ ret++;
+ }
+ if (memcmp(encrypted1, encrypted2, enc_bytes) == 0)
+ {
+ ret++;
+ }
+
+ if (memcmp(iv, nc_peer1.iv, NCRYPT_AES_IVLEN) == 0)
+ {
+ ret++;
+ }
+
+ memset(encrypted1, 0xFF, sizeof(encrypted1));
+ memset(tag1, 0xFF, sizeof(tag1));
+ memset(tmp1, 0x41, sizeof(tmp1));
+ enc_bytes = ncrypt_encrypt(&nc_peer1, tmp1, sizeof(tmp1), encrypted1, tag1);
+ dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted1, enc_bytes, tag1, tmp2);
+ if (enc_bytes != sizeof(tmp1) || dec_bytes != sizeof(tmp2))
+ {
+ ret++;
+ }
+ if (memcmp(tmp1, tmp2, sizeof(tmp1)) != 0)
+ {
+ ret++;
+ }
+
+ enc_bytes = ncrypt_encrypt(&nc_peer1, tmp1, sizeof(tmp1), encrypted2, tag2);
+ dec_bytes = ncrypt_decrypt(&nc_peer2, encrypted2, enc_bytes, tag2, tmp2);
+ if (enc_bytes != sizeof(tmp1) || dec_bytes != sizeof(tmp2))
+ {
+ ret++;
+ }
+ if (memcmp(tmp1, tmp2, sizeof(tmp1)) != 0)
+ {
+ ret++;
+ }
+
+ if (enc_bytes != dec_bytes)
+ {
+ ret++;
+ }
+ if (memcmp(tmp2, encrypted1, dec_bytes) == 0)
+ {
+ ret++;
+ }
+ if (memcmp(tag1, tag2, NCRYPT_TAG_SIZE) == 0)
+ {
+ ret++;
+ }
+ if (memcmp(encrypted1, encrypted2, enc_bytes) == 0)
+ {
+ ret++;
+ }
+ if (memcmp(iv, nc_peer1.iv, NCRYPT_AES_IVLEN) == 0)
+ {
+ ret++;
+ }
+ if (memcmp(nc_peer1.iv, nc_peer2.iv, NCRYPT_AES_IVLEN) != 0)
+ {
+ ret++;
+ }
+
+ int pipefds[2] = {-1, -1};
+ if (pipe2(pipefds, O_DIRECT) != 0)
+ {
+ ret++;
+ }
+
+ struct ncrypt_buffer encrypted_buf = {};
+ struct ncrypt_buffer decrypted_buf = {};
+ memcpy(encrypted_buf.plaintext.data, plaintext, sizeof(plaintext));
+ encrypted_buf.data_used = sizeof(plaintext);
+
+ int sent;
+ sent = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
+ if (sent < 0)
+ {
+ ret++;
+ }
+
+ int received = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
+ if (received < 0)
+ {
+ ret++;
+ }
+
+ if (received != sent)
+ {
+ ret++;
+ }
+
+ sent = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
+ int sent2 = ncrypt_encrypt_send(&nc_peer1, pipefds[1], &encrypted_buf);
+ if (sent < 0 || sent != sent2)
+ {
+ ret++;
+ }
+
+ received = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
+ int received2 = ncrypt_decrypt_recv(&nc_peer2, pipefds[0], &decrypted_buf);
+ if (received < 0 || received != received2)
+ {
+ ret++;
+ }
+
+ close(pipefds[0]);
+ close(pipefds[1]);
+
+ ncrypt_free(&nc_peer2);
+ ncrypt_free(&nc_peer1);
+
+ return ret;
+}
+#endif
+
#define THREADS_RETURNED_ERROR() \
(nDPId_return.thread_return_value.val != 0 || nDPIsrvd_return.val != 0 || \
distributor_return.thread_return_value.val != 0)
@@ -1671,8 +1878,11 @@ int main(int argc, char ** argv)
retval += base64_selftest();
retval += nio_selftest();
+#ifdef ENABLE_CRYPTO
+ retval += ncrypt_selftest();
+#endif
- logger(1, "Selftest returned: %d", retval);
+ logger(1, "Selftest returned: %d%s", retval, (retval == 0 ? " (OK)" : ""));
return retval;
}
diff --git a/nDPId.c b/nDPId.c
index 8bbf99fbd..082686dda 100644
--- a/nDPId.c
+++ b/nDPId.c
@@ -34,6 +34,9 @@
#include "config.h"
#include "nDPIsrvd.h"
+#ifdef ENABLE_CRYPTO
+#include "ncrypt.h"
+#endif
#include "nio.h"
#ifdef ENABLE_PFRING
#include "npfring.h"
@@ -307,6 +310,9 @@ struct nDPId_workflow
uint64_t total_compression_diff;
uint64_t current_compression_diff;
#endif
+#ifdef ENABLE_CRYPTO
+ struct ncrypt crypto;
+#endif
uint64_t last_scan_time;
uint64_t last_status_time;
@@ -502,6 +508,10 @@ static struct
#ifdef ENABLE_PFRING
struct cmdarg use_pfring;
#endif
+#ifdef ENABLE_CRYPTO
+ struct cmdarg local_private_key_file;
+ struct cmdarg remote_public_key_file;
+#endif
/* subopts */
struct cmdarg max_flows_per_thread;
struct cmdarg max_idle_flows_per_thread;
@@ -551,6 +561,10 @@ static struct
#ifdef ENABLE_PFRING
.use_pfring = CMDARG_BOOL(0),
#endif
+#ifdef ENABLE_CRYPTO
+ .local_private_key_file = CMDARG_STR(NULL),
+ .remote_public_key_file = CMDARG_STR(NULL),
+#endif
.max_flows_per_thread = CMDARG_ULL(nDPId_MAX_FLOWS_PER_THREAD / 2),
.max_idle_flows_per_thread = CMDARG_ULL(nDPId_MAX_IDLE_FLOWS_PER_THREAD / 2),
#ifdef CROSS_COMPILATION
@@ -1561,6 +1575,61 @@ static struct nDPId_workflow * init_workflow(char const * const file_or_device)
return NULL;
}
+#ifdef ENABLE_CRYPTO
+ if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
+ IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
+ {
+ unsigned char local_priv_key[NCRYPT_X25519_KEYLEN];
+ unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN];
+ int rv;
+
+ rv = chmod_chown(GET_CMDARG_STR(nDPId_options.local_private_key_file), S_IRUSR | S_IWUSR, "root", "root");
+ if (rv != 0)
+ {
+ logger_early(1,
+ "Could not chmod/chown private key file `%s' to 0600/root: %s",
+ GET_CMDARG_STR(nDPId_options.local_private_key_file),
+ strerror(rv));
+ free_workflow(&workflow);
+ return NULL;
+ }
+ rv = ncrypt_load_privkey(GET_CMDARG_STR(nDPId_options.local_private_key_file), local_priv_key);
+ if (rv != 0)
+ {
+ logger_early(1,
+ "Could not load (local) private key file `%s': %d",
+ GET_CMDARG_STR(nDPId_options.local_private_key_file),
+ rv);
+ free_workflow(&workflow);
+ return NULL;
+ }
+ rv = ncrypt_load_pubkey(GET_CMDARG_STR(nDPId_options.remote_public_key_file), remote_pub_key);
+ if (rv != 0)
+ {
+ logger_early(1,
+ "Could not load (remote) public key file `%s': %d",
+ GET_CMDARG_STR(nDPId_options.remote_public_key_file),
+ rv);
+ free_workflow(&workflow);
+ return NULL;
+ }
+ rv = ncrypt_init(&workflow->crypto, local_priv_key, remote_pub_key);
+ if (rv != 0)
+ {
+ logger_early(1, "Could not init crypto system: %d", rv);
+ free_workflow(&workflow);
+ return NULL;
+ }
+ rv = ncrypt_init_encrypt(&workflow->crypto);
+ if (rv != 0)
+ {
+ logger_early(1, "Could not init encryption mode: %d", rv);
+ free_workflow(&workflow);
+ return NULL;
+ }
+ }
+#endif
+
return workflow;
}
@@ -1689,6 +1758,13 @@ static void free_workflow(struct nDPId_workflow ** const workflow)
npfring_close(&w->npf);
}
#endif
+#ifdef ENABLE_CRYPTO
+ if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
+ IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
+ {
+ ncrypt_free(&w->crypto);
+ }
+#endif
if (w->pcap_handle != NULL)
{
@@ -2289,7 +2365,9 @@ static void jsonize_daemon(struct nDPId_reader_thread * const reader_thread, enu
#endif
ndpi_serialize_string_string(&workflow->ndpi_serializer, "ndpi_version", ndpi_revision());
ndpi_serialize_string_uint32(&workflow->ndpi_serializer, "ndpi_api_version", ndpi_get_api_version());
- ndpi_serialize_string_uint64(&workflow->ndpi_serializer, "size_per_flow", (uint64_t)(sizeof(struct nDPId_flow) + sizeof(struct nDPId_detection_data)));
+ ndpi_serialize_string_uint64(&workflow->ndpi_serializer,
+ "size_per_flow",
+ (uint64_t)(sizeof(struct nDPId_flow) + sizeof(struct nDPId_detection_data)));
switch (event)
{
@@ -2604,6 +2682,28 @@ static void send_to_collector(struct nDPId_reader_thread * const reader_thread,
}
}
+#ifdef ENABLE_CRYPTO
+ if (IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
+ IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0)
+ {
+ int rv;
+ struct ncrypt_buffer buf = {.data_used = s_ret};
+
+ memcpy(buf.plaintext.data, newline_json_msg, s_ret);
+ rv = ncrypt_encrypt_send(&workflow->crypto, reader_thread->collector_sockfd, &buf);
+ if (rv - (NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE) != s_ret)
+ {
+ logger(1,
+ "[%8llu, %zu] Crypto: encrypt and send returned %d, but expected %d",
+ workflow->packets_captured,
+ reader_thread->array_index,
+ rv,
+ s_ret + (NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE));
+ }
+ return;
+ }
+#endif
+
errno = 0;
ssize_t written;
if (reader_thread->collector_sock_last_errno == 0 &&
@@ -3448,8 +3548,8 @@ static uint32_t calculate_ndpi_flow_struct_hash(struct ndpi_flow_struct const *
/* mask for FCF */
#define WIFI_DATA 0x2
#define FCF_TYPE(fc) (((fc) >> 2) & 0x3) /* 0000 0011 = 0x3 */
-#define FCF_TO_DS(fc) ((fc)&0x0100)
-#define FCF_FROM_DS(fc) ((fc)&0x0200)
+#define FCF_TO_DS(fc) ((fc) & 0x0100)
+#define FCF_FROM_DS(fc) ((fc) & 0x0200)
/* mask for Bad FCF presence */
#define BAD_FCS 0x50 /* 0101 0000 */
static int process_datalink_layer(struct nDPId_reader_thread * const reader_thread,
@@ -5420,7 +5520,7 @@ static int nDPId_parse_options(int argc, char ** argv)
{
int opt;
- while ((opt = getopt(argc, argv, "f:i:rIEB:lL:c:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1)
+ while ((opt = getopt(argc, argv, "f:i:rIEB:lL:c:k:K:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1)
{
switch (opt)
{
@@ -5459,6 +5559,22 @@ static int nDPId_parse_options(int argc, char ** argv)
case 'c':
set_cmdarg_string(&nDPId_options.collector_address, optarg);
break;
+ case 'k':
+#ifdef ENABLE_CRYPTO
+ set_cmdarg_string(&nDPId_options.local_private_key_file, optarg);
+ break;
+#else
+ logger(1, "%s", "nDPId was built w/o OpenSSL/Crypto support");
+ return 1;
+#endif
+ case 'K':
+#ifdef ENABLE_CRYPTO
+ set_cmdarg_string(&nDPId_options.remote_public_key_file, optarg);
+ break;
+#else
+ logger(1, "%s", "nDPId was built w/o OpenSSL/Crypto support");
+ return 1;
+#endif
case 'e':
#ifdef ENABLE_EPOLL
set_cmdarg_boolean(&nDPId_options.use_poll, 1);
@@ -5817,6 +5933,16 @@ static int validate_options(void)
{
logger_early(1, "%s", "Higher values of max-packets-per-flow-to-send may cause superfluous network usage.");
}
+#ifdef ENABLE_CRYPTO
+ if ((IS_CMDARG_SET(nDPId_options.local_private_key_file) != 0 &&
+ IS_CMDARG_SET(nDPId_options.remote_public_key_file) == 0) ||
+ (IS_CMDARG_SET(nDPId_options.local_private_key_file) == 0 &&
+ IS_CMDARG_SET(nDPId_options.remote_public_key_file) != 0))
+ {
+ logger_early(1, "%s", "Encryption requires a local private key file and a remote public key file to be set.");
+ retval = 1;
+ }
+#endif
return retval;
}
diff --git a/ncrypt.c b/ncrypt.c
new file mode 100644
index 000000000..36277b8f6
--- /dev/null
+++ b/ncrypt.c
@@ -0,0 +1,499 @@
+#include "ncrypt.h"
+
+#include <endian.h>
+#include <openssl/conf.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OPENSSL_DUMP(ptr, siz) \
+ do \
+ { \
+ BIO_dump_indent_fp(stderr, ptr, siz, 2); \
+ fputc('\n', stderr); \
+ } while (0);
+#define OPENSSL_ERROR(retval) \
+ do \
+ { \
+ fprintf(stderr, "OpenSSL Error: %s\n", ERR_error_string(ERR_get_error(), NULL)); \
+ } while (0);
+
+union iv
+{
+ struct
+ {
+ uint32_t upper;
+ uint64_t lower;
+ } __attribute__((__packed__)) numeric;
+ unsigned char buffer[NCRYPT_AES_IVLEN];
+};
+
+int ncrypt_keygen(unsigned char priv_key[NCRYPT_X25519_KEYLEN], unsigned char pub_key[NCRYPT_X25519_KEYLEN])
+{
+ EVP_PKEY * const pkey = EVP_PKEY_Q_keygen(NULL, NULL, "X25519");
+ size_t klen = NCRYPT_X25519_KEYLEN;
+
+ if (EVP_PKEY_get_raw_private_key(pkey, priv_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
+ {
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+ if (EVP_PKEY_get_raw_public_key(pkey, pub_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
+ {
+ return -2;
+ EVP_PKEY_free(pkey);
+ }
+
+ EVP_PKEY_free(pkey);
+ return 0;
+}
+
+int ncrypt_load_privkey(char const * const private_key_file, unsigned char priv_key[NCRYPT_X25519_KEYLEN])
+{
+ FILE * const pkfp = fopen(private_key_file, "r+b");
+ EVP_PKEY * pkey = NULL;
+ size_t klen = NCRYPT_X25519_KEYLEN;
+
+ if (pkfp == NULL)
+ {
+ return -1;
+ }
+
+ pkey = PEM_read_PrivateKey(pkfp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ {
+ fclose(pkfp);
+ return -2;
+ }
+ fclose(pkfp);
+
+ if (EVP_PKEY_get_raw_private_key(pkey, priv_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
+ {
+ EVP_PKEY_free(pkey);
+ return -3;
+ }
+
+ EVP_PKEY_free(pkey);
+ return 0;
+}
+
+int ncrypt_load_pubkey(char const * const public_key_file, unsigned char pub_key[NCRYPT_X25519_KEYLEN])
+{
+ FILE * const pkfp = fopen(public_key_file, "r+b");
+ EVP_PKEY * pkey = NULL;
+ size_t klen = NCRYPT_X25519_KEYLEN;
+
+ if (pkfp == NULL)
+ {
+ return -1;
+ }
+
+ pkey = PEM_read_PUBKEY(pkfp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ {
+ fclose(pkfp);
+ return -2;
+ }
+ fclose(pkfp);
+
+ if (EVP_PKEY_get_raw_public_key(pkey, pub_key, &klen) == 0 || klen != NCRYPT_X25519_KEYLEN)
+ {
+ EVP_PKEY_free(pkey);
+ return -3;
+ }
+
+ EVP_PKEY_free(pkey);
+ return 0;
+}
+
+static int init_iv(struct ncrypt * const nc)
+{
+ FILE * rnd_fp;
+
+ rnd_fp = fopen("/dev/random", "r+b");
+
+ if (rnd_fp == NULL)
+ {
+ return -1;
+ }
+
+ if (fread(&nc->iv[0], sizeof(nc->iv[0]), sizeof(nc->iv) / sizeof(nc->iv[0]), rnd_fp) != NCRYPT_AES_IVLEN)
+ {
+ fclose(rnd_fp);
+ return -2;
+ }
+
+ fclose(rnd_fp);
+
+ return 0;
+}
+
+static void next_iv(struct ncrypt * const nc)
+{
+ union iv * const iv = (union iv *)&nc->iv[0];
+
+ uint64_t lower = be64toh(iv->numeric.lower);
+ lower++;
+ iv->numeric.lower = htobe64(lower);
+
+ if (iv->numeric.lower == 0)
+ {
+ uint32_t upper = be32toh(iv->numeric.upper);
+ upper++;
+ iv->numeric.upper = htobe32(upper);
+ }
+}
+
+int ncrypt_init(struct ncrypt * const nc,
+ unsigned char local_priv_key[NCRYPT_X25519_KEYLEN],
+ unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN])
+{
+ EVP_PKEY_CTX * key_ctx;
+ size_t pub_key_datalen = 0;
+ size_t secret_len = 0;
+
+ if (nc->libctx != NULL)
+ {
+ return -1;
+ }
+ nc->libctx = OSSL_LIB_CTX_new();
+ if (nc->libctx == NULL)
+ {
+ return -2;
+ }
+
+ nc->local.priv_key =
+ EVP_PKEY_new_raw_private_key_ex(nc->libctx, "X25519", nc->propq, local_priv_key, NCRYPT_X25519_KEYLEN);
+ if (nc->local.priv_key == NULL)
+ {
+ return -3;
+ }
+
+ if (EVP_PKEY_get_octet_string_param(nc->local.priv_key,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ nc->local.pub_key,
+ sizeof(nc->local.pub_key),
+ &pub_key_datalen) == 0)
+ {
+ return -4;
+ }
+ if (pub_key_datalen != NCRYPT_X25519_KEYLEN)
+ {
+ return -5;
+ }
+
+ nc->remote.pub_key =
+ EVP_PKEY_new_raw_public_key_ex(nc->libctx, "X25519", nc->propq, remote_pub_key, NCRYPT_X25519_KEYLEN);
+ if (nc->remote.pub_key == NULL)
+ {
+ return -6;
+ }
+
+ key_ctx = EVP_PKEY_CTX_new_from_pkey(nc->libctx, nc->local.priv_key, nc->propq);
+ if (key_ctx == NULL)
+ {
+ return -7;
+ }
+
+ if (EVP_PKEY_derive_init(key_ctx) == 0)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ return -8;
+ }
+
+ if (EVP_PKEY_derive_set_peer(key_ctx, nc->remote.pub_key) == 0)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ return -9;
+ }
+
+ if (EVP_PKEY_derive(key_ctx, NULL, &secret_len) == 0)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ return -10;
+ }
+ if (secret_len != NCRYPT_X25519_KEYLEN)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ return -11;
+ }
+
+ nc->shared_secret = OPENSSL_malloc(secret_len);
+ if (nc->shared_secret == NULL)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ return -12;
+ }
+ if (EVP_PKEY_derive(key_ctx, nc->shared_secret, &secret_len) == 0)
+ {
+ EVP_PKEY_CTX_free(key_ctx);
+ OPENSSL_clear_free(nc->shared_secret, secret_len);
+ nc->shared_secret = NULL;
+ return -13;
+ }
+
+ nc->iv_mismatches = 0;
+
+ OPENSSL_cleanse(local_priv_key, NCRYPT_X25519_KEYLEN);
+ OPENSSL_cleanse(remote_pub_key, NCRYPT_X25519_KEYLEN);
+
+ EVP_PKEY_CTX_free(key_ctx);
+ return 0;
+}
+
+int ncrypt_init_encrypt(struct ncrypt * const nc)
+{
+ if (nc->aesctx == NULL)
+ {
+ nc->aesctx = EVP_CIPHER_CTX_new();
+ if (nc->aesctx == NULL)
+ {
+ return -1;
+ }
+
+ if (EVP_EncryptInit_ex(nc->aesctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
+ {
+ return -2;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
+ {
+ return -3;
+ }
+ }
+
+ if (init_iv(nc) != 0)
+ {
+ return -4;
+ }
+
+ if (EVP_EncryptInit_ex(nc->aesctx, NULL, NULL, nc->shared_secret, nc->iv) == 0)
+ {
+ return -5;
+ }
+
+ return 0;
+}
+
+int ncrypt_init_decrypt(struct ncrypt * const nc, unsigned char iv[NCRYPT_AES_IVLEN])
+{
+ if (nc->aesctx == NULL)
+ {
+ nc->aesctx = EVP_CIPHER_CTX_new();
+ if (nc->aesctx == NULL)
+ {
+ return -1;
+ }
+
+ if (EVP_DecryptInit_ex(nc->aesctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 0)
+ {
+ return -2;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_IVLEN, NCRYPT_AES_IVLEN, NULL) == 0)
+ {
+ return -3;
+ }
+ }
+
+ memcpy(nc->iv, iv, NCRYPT_AES_IVLEN);
+
+ if (EVP_DecryptInit_ex(nc->aesctx, NULL, NULL, nc->shared_secret, nc->iv) == 0)
+ {
+ return -4;
+ }
+
+ return 0;
+}
+
+void ncrypt_free(struct ncrypt * const nc)
+{
+ if (nc->aesctx != NULL)
+ {
+ EVP_CIPHER_CTX_free(nc->aesctx);
+ nc->aesctx = NULL;
+ }
+
+ if (nc->shared_secret != NULL)
+ {
+ OPENSSL_clear_free(nc->shared_secret, NCRYPT_X25519_KEYLEN);
+ nc->shared_secret = NULL;
+ }
+
+ if (nc->local.priv_key != NULL)
+ {
+ EVP_PKEY_free(nc->local.priv_key);
+ nc->local.priv_key = NULL;
+ }
+
+ if (nc->remote.pub_key != NULL)
+ {
+ EVP_PKEY_free(nc->remote.pub_key);
+ nc->remote.pub_key = NULL;
+ }
+
+ if (nc->libctx != NULL)
+ {
+ OSSL_LIB_CTX_free(nc->libctx);
+ nc->libctx = NULL;
+ }
+}
+
+static int encrypt(struct ncrypt * const nc,
+ unsigned char const * const plaintext,
+ size_t used,
+ unsigned char encrypted[NCRYPT_BUFFER_SIZE],
+ unsigned char tag[NCRYPT_TAG_SIZE])
+{
+ int encrypted_used;
+ int remaining;
+
+ if (EVP_EncryptInit_ex(nc->aesctx, NULL, NULL, NULL, nc->iv) == 0)
+ {
+ return -2;
+ }
+
+ if (EVP_EncryptUpdate(nc->aesctx, encrypted, &encrypted_used, plaintext, used) == 0)
+ {
+ return -3;
+ }
+
+ if (EVP_EncryptFinal_ex(nc->aesctx, encrypted + encrypted_used, &remaining) == 0)
+ {
+ return -4;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_GET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
+ {
+ return -5;
+ }
+
+ return encrypted_used + remaining;
+}
+
+int ncrypt_encrypt(struct ncrypt * const nc,
+ unsigned char const * const plaintext,
+ size_t used,
+ unsigned char encrypted[NCRYPT_BUFFER_SIZE],
+ unsigned char tag[NCRYPT_TAG_SIZE])
+{
+ if (used > NCRYPT_BUFFER_SIZE)
+ {
+ return -1;
+ }
+
+ next_iv(nc);
+
+ return encrypt(nc, plaintext, used, encrypted, tag);
+}
+
+int decrypt(struct ncrypt * const nc,
+ unsigned char const * const encrypted,
+ size_t used,
+ unsigned char tag[NCRYPT_TAG_SIZE],
+ unsigned char plaintext[NCRYPT_BUFFER_SIZE])
+{
+ int decrypted_used;
+ int remaining;
+
+ if (EVP_DecryptInit_ex(nc->aesctx, NULL, NULL, NULL, nc->iv) == 0)
+ {
+ return -2;
+ }
+
+ if (EVP_DecryptUpdate(nc->aesctx, plaintext, &decrypted_used, encrypted, used) == 0)
+ {
+ return -3;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(nc->aesctx, EVP_CTRL_GCM_SET_TAG, NCRYPT_TAG_SIZE, tag) == 0)
+ {
+ return -4;
+ }
+
+ if (EVP_DecryptFinal_ex(nc->aesctx, plaintext + decrypted_used, &remaining) == 0)
+ {
+ return -5;
+ }
+
+ return decrypted_used + remaining;
+}
+
+int ncrypt_decrypt(struct ncrypt * const nc,
+ unsigned char const * const encrypted,
+ size_t used,
+ unsigned char tag[NCRYPT_TAG_SIZE],
+ unsigned char plaintext[NCRYPT_BUFFER_SIZE])
+{
+ if (used > NCRYPT_BUFFER_SIZE)
+ {
+ return -1;
+ }
+
+ next_iv(nc);
+
+ return decrypt(nc, encrypted, used, tag, plaintext);
+}
+
+int ncrypt_encrypt_send(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf)
+{
+ int encrypted_used = encrypt(nc, buf->plaintext.data, buf->data_used, buf->encrypted.data, buf->encrypted.tag);
+ if (encrypted_used < 0)
+ {
+ return -1;
+ }
+
+ memcpy(buf->encrypted.iv, nc->iv, NCRYPT_AES_IVLEN);
+ ssize_t bytes_written = write(fd, buf->encrypted.raw, NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + encrypted_used);
+ next_iv(nc);
+
+ if (bytes_written < 0)
+ {
+ return -2;
+ }
+ if (bytes_written != NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + encrypted_used)
+ {
+ nc->partial_writes++;
+ buf->write_offset += bytes_written;
+ }
+
+ return (int)bytes_written;
+}
+
+int ncrypt_decrypt_recv(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf)
+{
+ ssize_t bytes_read = read(fd, buf->encrypted.raw, sizeof(buf->encrypted.raw));
+
+ if (bytes_read < 0)
+ {
+ return -1;
+ }
+ if (bytes_read < NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + 1)
+ {
+ return -2;
+ }
+
+ if (memcmp(nc->iv, buf->encrypted.iv, NCRYPT_AES_IVLEN) != 0)
+ {
+ nc->iv_mismatches++;
+ }
+ memcpy(nc->iv, buf->encrypted.iv, NCRYPT_AES_IVLEN);
+ int decrypted_used = decrypt(nc,
+ buf->encrypted.data,
+ bytes_read - NCRYPT_AES_IVLEN - NCRYPT_TAG_SIZE,
+ buf->encrypted.tag,
+ buf->plaintext.data);
+ next_iv(nc);
+
+ if (decrypted_used < 0)
+ {
+ return -3;
+ }
+
+ buf->data_used = decrypted_used;
+
+ return (int)bytes_read;
+}
diff --git a/ncrypt.h b/ncrypt.h
new file mode 100644
index 000000000..f31de8aee
--- /dev/null
+++ b/ncrypt.h
@@ -0,0 +1,91 @@
+#ifndef NCRYPT_H
+#define NCRYPT_H 1
+
+#include <stdlib.h>
+
+#include "config.h"
+
+#define NCRYPT_X25519_KEYLEN 32
+#define NCRYPT_AES_IVLEN 12
+#define NCRYPT_TAG_SIZE 16
+#define NCRYPT_BUFFER_SIZE NETWORK_BUFFER_MAX_SIZE
+#define NCRYPT_PACKET_BUFFER_SIZE NCRYPT_AES_IVLEN + NCRYPT_TAG_SIZE + NCRYPT_BUFFER_SIZE
+
+struct ncrypt
+{
+ void * libctx;
+ void * aesctx;
+ unsigned char * shared_secret;
+ const char * propq;
+ struct
+ {
+ void * priv_key;
+ unsigned char pub_key[NCRYPT_X25519_KEYLEN];
+ } local;
+ struct
+ {
+ void * pub_key;
+ } remote;
+ unsigned char iv[NCRYPT_AES_IVLEN];
+ size_t iv_mismatches;
+ size_t partial_writes;
+};
+
+struct ncrypt_buffer
+{
+ struct
+ {
+ unsigned char data[NCRYPT_BUFFER_SIZE];
+ } plaintext;
+
+ struct
+ {
+ union
+ {
+ unsigned char raw[NCRYPT_PACKET_BUFFER_SIZE];
+ struct
+ {
+ unsigned char iv[NCRYPT_AES_IVLEN];
+ unsigned char tag[NCRYPT_TAG_SIZE];
+ unsigned char data[NCRYPT_BUFFER_SIZE];
+ } __attribute__((__packed__));
+ };
+ } encrypted;
+
+ size_t data_used; // size of plaintext and encrypted is equal for AES-GCM
+ size_t write_offset; // partial write; offset to next bytes of data
+};
+
+int ncrypt_keygen(unsigned char priv_key[NCRYPT_X25519_KEYLEN], unsigned char pub_key[NCRYPT_X25519_KEYLEN]);
+
+int ncrypt_load_privkey(char const * const private_key_file, unsigned char priv_key[NCRYPT_X25519_KEYLEN]);
+
+int ncrypt_load_pubkey(char const * const public_key_file, unsigned char pub_key[NCRYPT_X25519_KEYLEN]);
+
+int ncrypt_init(struct ncrypt * const nc,
+ unsigned char local_priv_key[NCRYPT_X25519_KEYLEN],
+ unsigned char remote_pub_key[NCRYPT_X25519_KEYLEN]);
+
+int ncrypt_init_encrypt(struct ncrypt * const nc);
+
+int ncrypt_init_decrypt(struct ncrypt * const nc, unsigned char iv[NCRYPT_AES_IVLEN]);
+
+void ncrypt_free(struct ncrypt * const nc);
+
+int ncrypt_encrypt(struct ncrypt * const nc,
+ unsigned char const * const plaintext,
+ size_t used,
+ unsigned char encrypted[NCRYPT_BUFFER_SIZE],
+ unsigned char tag[NCRYPT_TAG_SIZE]);
+
+int ncrypt_decrypt(struct ncrypt * const nc,
+ unsigned char const * const encrypted,
+ size_t used,
+ unsigned char tag[NCRYPT_TAG_SIZE],
+ unsigned char plaintext[NCRYPT_BUFFER_SIZE]);
+
+int ncrypt_encrypt_send(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf);
+
+int ncrypt_decrypt_recv(struct ncrypt * const nc, int fd, struct ncrypt_buffer * const buf);
+
+#endif
diff --git a/nio.c b/nio.c
index 90785eeaa..0c6953fc4 100644
--- a/nio.c
+++ b/nio.c
@@ -329,7 +329,7 @@ int nio_check(struct nio * io, int index, int event_flags)
return NIO_ERROR_INTERNAL;
}
-int nio_is_valid(struct nio const * const io, int index)
+int nio_is_valid(struct nio const * io, int index)
{
if (index < 0 || index >= io->nready)
return NIO_ERROR_INTERNAL;
diff --git a/nio.h b/nio.h
index 60e1b0926..995766dc9 100644
--- a/nio.h
+++ b/nio.h
@@ -64,7 +64,7 @@ WARN_UNUSED
int nio_check(struct nio * io, int index, int events);
WARN_UNUSED
-int nio_is_valid(struct nio const * const io, int index);
+int nio_is_valid(struct nio const * io, int index);
WARN_UNUSED
int nio_get_fd(struct nio * io, int index);
diff --git a/scripts/gen_keypair.sh b/scripts/gen_keypair.sh
new file mode 100755
index 000000000..b63c62f94
--- /dev/null
+++ b/scripts/gen_keypair.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+printf 'usage: %s [out-pem-private-key-file] [out-pem-public-key-file]\n' "${0}"
+
+if [ -z "${1}" ]; then
+ PRIV_KEY="./nDPId-x25519-priv.pem"
+else
+ PRIV_KEY="${1}"
+fi
+
+if [ -z "${2}" ]; then
+ PUB_KEY="./nDPId-x25519-pub.pem"
+else
+ PUB_KEY="${2}"
+fi
+
+printf 'Private Key: %s\n' "${PRIV_KEY}"
+printf 'Public Key.: %s\n' "${PUB_KEY}"
+
+openssl genpkey -algorithm x25519 -out "${PRIV_KEY}"
+openssl pkey -in "${PRIV_KEY}" -outform PEM -pubout -out "${PUB_KEY}"