From 2e505c2dc7a7e849fe7a327f9cbdfc587477a3d1 Mon Sep 17 00:00:00 2001 From: Dimitri Staessens Date: Sun, 13 Jul 2025 07:42:58 +0200 Subject: irmd: Initial Flow Allocation Protocol Header This adds the initial version for the flow allocation protocol header between IRMd instances. This is a step towards flow authentication. The header supports secure and authenticated flow allocation, supporting certificate-based authentication and ephemeral key exchange for end-to-end encryption. id: 128-bit identifier for the entity. timestamp: 64-bit timestamp (replay protection). certificate: Certificate for authentication. public key: ECDHE public key for key exchange. data: Application data. signature: Signature for integrity/authenticity. Authentication and encryption require OpenSSL to be installed. The IRMd compares the allocation request delay with the MPL of the Layer over which the flow allocation was sent. MPL is now reported by the Layer in ms instead of seconds. Time functions revised for consistency and adds some tests. The TPM can now print thread running times in Debug builds (TPM_DEBUG_REPORT_INTERVAL) and abort processes with hung threads (TPM_DEBUG_ABORT_TIMEOUT). Long running threads waiting for input should call tpm_wait_work() to avoid trigger a process abort. Signed-off-by: Dimitri Staessens Signed-off-by: Sander Vrijders --- src/irmd/tests/CMakeLists.txt | 4 + src/irmd/tests/irm_test.c | 33 +++++ src/irmd/tests/oap_test.c | 273 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 src/irmd/tests/irm_test.c create mode 100644 src/irmd/tests/oap_test.c (limited to 'src/irmd/tests') diff --git a/src/irmd/tests/CMakeLists.txt b/src/irmd/tests/CMakeLists.txt index 99743052..e860acce 100644 --- a/src/irmd/tests/CMakeLists.txt +++ b/src/irmd/tests/CMakeLists.txt @@ -3,6 +3,8 @@ get_filename_component(src_folder "${tmp}" NAME) create_test_sourcelist(${src_folder}_tests test_suite.c # Add new tests here + irm_test.c + oap_test.c ) add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests}) @@ -21,3 +23,5 @@ foreach(test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) add_test(irmd/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name}) endforeach(test) + +set_property(TEST irmd/oap_test PROPERTY SKIP_RETURN_CODE 1) diff --git a/src/irmd/tests/irm_test.c b/src/irmd/tests/irm_test.c new file mode 100644 index 00000000..d440289c --- /dev/null +++ b/src/irmd/tests/irm_test.c @@ -0,0 +1,33 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Unit tests of IRMd functions + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + + +int irm_test(int argc, + char **argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + return ret; +} diff --git a/src/irmd/tests/oap_test.c b/src/irmd/tests/oap_test.c new file mode 100644 index 00000000..e9f67505 --- /dev/null +++ b/src/irmd/tests/oap_test.c @@ -0,0 +1,273 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Unit tests of Ouroboros flow allocation protocol + * Dimitri Staessens + * Sander Vrijders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "config.h" + +#include "oap.c" + +#include +#include + +static const char * pkp_str = \ +"-----BEGIN EC PRIVATE KEY-----\n" +"MHcCAQEEIC13y+5jdKe80HBJD7WITpQamcn3rrkTX1r0v+JwSk4NoAoGCCqGSM49\n" +"AwEHoUQDQgAEcC0yLAfUtufH8cdLybrdWPc6U+xRuhDhqqrEcBO5+eob2xyqEaNk\n" +"nIV/86724zPptGRahWz0rzW2PvNppJdNBg==\n" +"-----END EC PRIVATE KEY-----\n"; + +/* Valid signed server certificate for server-2.unittest.o7s */ +static const char * crt_str = \ +"-----BEGIN CERTIFICATE-----\n" +"MIIDgjCCAyigAwIBAgICEAIwCgYIKoZIzj0EAwIwWzELMAkGA1UEBhMCQkUxDDAK\n" +"BgNVBAgMA09WTDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n" +"GTAXBgNVBAMMEGltMi51bml0dGVzdC5vN3MwHhcNMjUwNzA0MTMxODI5WhcNMzUw\n" +"NzAyMTMxODI5WjBwMQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYDVQQH\n" +"DAVHaGVudDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3MxHjAc\n" +"BgNVBAMMFXNlcnZlci0yLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49\n" +"AwEHA0IABHAtMiwH1Lbnx/HHS8m63Vj3OlPsUboQ4aqqxHATufnqG9scqhGjZJyF\n" +"f/Ou9uMz6bRkWoVs9K81tj7zaaSXTQajggHFMIIBwTAJBgNVHRMEAjAAMBEGCWCG\n" +"SAGG+EIBAQQEAwIGQDA6BglghkgBhvhCAQ0ELRYrR3JpbGxlZCBDaGVlc2UgR2Vu\n" +"ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUTt3xHTwE9amoglxh\n" +"cEMqWv+PpDMwgb8GA1UdIwSBtzCBtIAUFfeZRx8QWWKQr7Aw8zjDu2shvcShgZek\n" +"gZQwgZExCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcMBUdoZW50\n" +"MQwwCgYDVQQKDANvN3MxFTATBgNVBAsMDHVuaXR0ZXN0Lm83czEZMBcGA1UEAwwQ\n" +"Y2EyLnVuaXR0ZXN0Lm83czEkMCIGCSqGSIb3DQEJARYVZHVtbXlAb3Vyb2Jvcm9z\n" +"LnJvY2tzggIQAjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw\n" +"EQYDVR0fBAowCDAGoASgAoYAMCoGCCsGAQUFBwEBBB4wHDAMBggrBgEFBQcwAoYA\n" +"MAwGCCsGAQUFBzABhgAwIAYDVR0RBBkwF4IVc2VydmVyLTEudW5pdHRlc3Qubzdz\n" +"MAoGCCqGSM49BAMCA0gAMEUCIQDHuDb62w/Uah4nKwUFoJVkr4rgdNGh2Rn3SWaK\n" +"0FV/gAIgOLKorTwSgrTFdyOUkuPOhRs8BEMpah+dp8UTO8AnLvY=\n" +"-----END CERTIFICATE-----\n"; + +static int test_oap_hdr_init_fini(void) +{ + struct oap_hdr oap_hdr; + struct timespec now; + uint64_t stamp; + buffer_t ephkey = BUF_INIT; + buffer_t data = BUF_INIT; + uint8_t buf[OAP_ID_SIZE]; + buffer_t id; + void * pkp = NULL; + void * pubcrt = NULL; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + clock_gettime(CLOCK_REALTIME, &now); + stamp = TS_TO_UINT64(now); + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to init OAP request header.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.hdr.len != OAP_HDR_MIN_SIZE) { + printf("OAP request header wrong: %zu < %zu.\n", + oap_hdr.hdr.len, OAP_HDR_MIN_SIZE); + goto fail_req_hdr_chk; + } + + if (oap_hdr.id.len != OAP_ID_SIZE) { + printf("OAP request header ID wrong size: %zu != %zu.\n", + oap_hdr.id.len, (size_t) OAP_ID_SIZE); + goto fail_req_hdr_chk; + } + + if (memcmp(oap_hdr.id.data, id.data, OAP_ID_SIZE) != 0) { + printf("OAP request header ID mismatch.\n"); + goto fail_req_hdr_chk; + } + + if (oap_hdr.timestamp < stamp) { + printf("OAP request header timestamp is too old.\n"); + goto fail_req_hdr_chk; + } + + if (oap_hdr.timestamp > stamp + 1 * BILLION) { + printf("OAP request header timestamp is too new.\n"); + goto fail_req_hdr_chk; + } + + oap_hdr_fini(&oap_hdr); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_req_hdr_chk: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_oap_hdr_init_fini_data(void) + +{ + struct oap_hdr oap_hdr; + buffer_t data; + buffer_t ephkey = BUF_INIT; + uint8_t buf[OAP_ID_SIZE]; + buffer_t id; + void * pkp = NULL; + void * pubcrt = NULL; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + data.len = 100; + data.data = malloc(data.len); + if (data.data == NULL) { + printf("Failed to allocate data buffer.\n"); + goto fail_data; + } + + random_buffer(data.data, data.len); + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to create OAP request header.\n"); + goto fail_req_hdr; + } + + if (oap_hdr.hdr.len != OAP_HDR_MIN_SIZE + data.len) { + printf("OAP request header wrong: %zu < %zu.\n", + oap_hdr.hdr.len, OAP_HDR_MIN_SIZE + data.len); + goto fail_req_hdr_sz; + } + + oap_hdr_fini(&oap_hdr); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_req_hdr_sz: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + freebuf(data); + fail_data: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_oap_hdr_init_fini_signed(void) +{ + struct oap_hdr oap_hdr; + buffer_t ephkey = BUF_INIT; + buffer_t data = BUF_INIT; + buffer_t sign; + buffer_t id; + uint8_t buf[OAP_ID_SIZE]; + void * pkp; + void * pk; + void * pubcrt; + void * pubcrt2; + + TEST_START(); + + random_buffer(buf, OAP_ID_SIZE); + id.data = buf; + id.len = OAP_ID_SIZE; + + if (crypt_load_privkey_str(pkp_str, &pkp) < 0) { + printf("Failed to load private key.\n"); + goto fail_pkp; + } + + if (crypt_load_crt_str(crt_str, &pubcrt) < 0) { + printf("Failed to load public certificate.\n"); + goto fail_pubcrt; + } + + if (oap_hdr_init(id, pkp, pubcrt, ephkey, data, &oap_hdr) < 0) { + printf("Failed to create OAP request header.\n"); + goto fail_req_hdr; + } + + if (crypt_load_crt_der(oap_hdr.crt, &pubcrt2) < 0) { + printf("Failed to load public certificate from DER.\n"); + goto fail_crt_der; + } + + if (crypt_get_pubkey_crt(pubcrt2, &pk) < 0) { + printf("Failed to get public key from certificate.\n"); + goto fail_crt_pk; + } + + sign = oap_hdr.hdr; + sign.len -= (oap_hdr.sig.len + sizeof(uint16_t)); + + if (auth_verify_sig(pk, sign, oap_hdr.sig) < 0) { + printf("Failed to verify OAP request header signature.\n"); + goto fail_check_sig; + } + + oap_hdr_fini(&oap_hdr); + + crypt_free_crt(pubcrt2); + crypt_free_crt(pubcrt); + crypt_free_key(pk); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_check_sig: + crypt_free_key(pk); + fail_crt_pk: + crypt_free_crt(pubcrt2); + fail_crt_der: + oap_hdr_fini(&oap_hdr); + fail_req_hdr: + crypt_free_crt(pubcrt); + fail_pubcrt: + crypt_free_key(pkp); + fail_pkp: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int oap_test(int argc, + char **argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_oap_hdr_init_fini(); + ret |= test_oap_hdr_init_fini_data(); +#ifdef HAVE_OPENSSL + ret |= test_oap_hdr_init_fini_signed(); +#else + (void) test_oap_hdr_init_fini_signed; + + ret = TEST_RC_SKIP; +#endif + return ret; +} -- cgit v1.2.3