diff options
| author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2026-06-13 10:18:17 +0200 |
|---|---|---|
| committer | Sander Vrijders <sander@ouroboros.rocks> | 2026-06-29 08:32:58 +0200 |
| commit | 22e2380b09730a2f18deefd688585edb430d3299 (patch) | |
| tree | 1fc03db35d93833220482f9c5f70d4c9d2d618c1 /cmake | |
| parent | df14e6cc81c296d91e9124cd09f25a83defb522f (diff) | |
| download | ouroboros-22e2380b09730a2f18deefd688585edb430d3299.tar.gz ouroboros-22e2380b09730a2f18deefd688585edb430d3299.zip | |
lib: Harden symmetric-key rotation
Flow crypto signalled rotation with a single phase-parity bit, so a
loss burst that hid an even number of rotations went unnoticed and
wedged the flow for good.
Each packet now carries a small cleartext selector naming its key
directly, so a receiver that falls behind recovers on the next packet
instead of getting stuck.
The selector also serves as the AEAD nonce and is authenticated as
associated data (AAD). Key rotation moves into a new backend-agnostic
keyrot module that rotates sub-keys to bound AEAD usage while
preserving forward secrecy.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'cmake')
| -rw-r--r-- | cmake/config/global.cmake | 12 | ||||
| -rw-r--r-- | cmake/config/irmd.cmake | 2 | ||||
| -rw-r--r-- | cmake/config/lib.cmake | 38 | ||||
| -rw-r--r-- | cmake/dependencies.cmake | 1 | ||||
| -rw-r--r-- | cmake/dependencies/system/liburcu.cmake | 45 |
5 files changed, 93 insertions, 5 deletions
diff --git a/cmake/config/global.cmake b/cmake/config/global.cmake index 0ac256bb..1e172724 100644 --- a/cmake/config/global.cmake +++ b/cmake/config/global.cmake @@ -25,8 +25,16 @@ set(SHM_LOCKFILE_NAME "/${SHM_PREFIX}.lockfile" CACHE INTERNAL # Secure memory configuration set(IRMD_SECMEM_MAX 1048576 CACHE STRING "IRMd secure heap size") -set(PROC_SECMEM_MAX 1048576 CACHE STRING "Process secure heap size") -set(SECMEM_GUARD 32 CACHE STRING "Secure heap min size") +# ~8 KiB secure heap per encrypted flow (cur+prev node slabs); the total +# is rounded up to a power of two for the OpenSSL secure-heap allocator. +set(PROC_SECMEM_FLOWS 512 CACHE STRING + "Max concurrent encrypted flows the per-process secure heap is sized for") +math(EXPR PROC_SECMEM_NEED "${PROC_SECMEM_FLOWS} * 8192") +set(PROC_SECMEM_MAX 4096) +while(PROC_SECMEM_MAX LESS PROC_SECMEM_NEED) + math(EXPR PROC_SECMEM_MAX "${PROC_SECMEM_MAX} * 2") +endwhile() +set(SECMEM_MINSIZE 32 CACHE STRING "Secure heap min alloc size") # Container/deployment options set(BUILD_CONTAINER FALSE CACHE BOOL diff --git a/cmake/config/irmd.cmake b/cmake/config/irmd.cmake index 2f5e7f02..b6b2dc40 100644 --- a/cmake/config/irmd.cmake +++ b/cmake/config/irmd.cmake @@ -23,7 +23,7 @@ set(OAP_REPLAY_TIMER 20 CACHE STRING set(OAP_REPLAY_MAX 4096 CACHE STRING "Maximum entries in the OAP replay cache (bounds memory/CPU under flood)") set(OAP_CLIENT_AUTH_DEFAULT TRUE CACHE BOOL - "Client requires the server to authenticate by default (FALSE for testing)") + "Client requires the server to authenticate by default") set(DEBUG_PROTO_OAP FALSE CACHE BOOL "Add Flow allocation protocol message output to IRMd debug logging") diff --git a/cmake/config/lib.cmake b/cmake/config/lib.cmake index 25130519..2c01b311 100644 --- a/cmake/config/lib.cmake +++ b/cmake/config/lib.cmake @@ -87,8 +87,42 @@ set(TPM_DEBUG_ABORT_TIMEOUT 0 CACHE STRING "TPM abort process after a thread reaches this timeout (s), 0 disables") # Encryption -set(KEY_ROTATION_BIT 20 CACHE STRING - "Bit position in packet counter that triggers key rotation (default 20 = every 2^20 packets)") +set(KEY_LEAF_BITS 20 CACHE STRING + "Packets per leaf key as a power of two (2^20 = AEAD-safe default)") +set(KEY_NODE_BITS 6 CACHE STRING + "Leaf keys per node key, power of two (2^6 = 64; leak compartment)") +set(KEY_NODE_COUNT 128 CACHE STRING + "Node keys per batch (N); <= 4096, the 12-bit on-wire node index") +set(KEY_REKEY_WATERMARK 4 CACHE STRING + "Re-key when this many node keys remain; 0 disables the count trigger") +set(KEY_REPLAY_WINDOW 2048 CACHE STRING + "RX replay window in packets; power of two, >= 128") +if(NOT KEY_REPLAY_WINDOW MATCHES "^[0-9]+$") + message(FATAL_ERROR "KEY_REPLAY_WINDOW must be a positive integer") +endif() +math(EXPR _krw_p2 "${KEY_REPLAY_WINDOW} & (${KEY_REPLAY_WINDOW} - 1)") +if(KEY_REPLAY_WINDOW LESS 128 OR NOT _krw_p2 EQUAL 0) + message(FATAL_ERROR "KEY_REPLAY_WINDOW must be a power of two >= 128") +endif() + +# Re-key must finish within its lead window - KEY_REKEY_WATERMARK node keys +# worth of packets - before the batch exhausts and TX fails closed. dev.c only +# evaluates the watermark once per FLOW_WM_CHECK writes, so a lead below ~2x +# that leaves a high-rate flow no room to complete the exchange. Production +# defaults are vast; this guards under-sized (test) geometries. +if(KEY_REKEY_WATERMARK GREATER 0) + set(_rk_wm_check 65536) # FLOW_WM_CHECK in src/lib/dev.c (2^16) + math(EXPR _rk_lead + "${KEY_REKEY_WATERMARK} << (${KEY_LEAF_BITS} + ${KEY_NODE_BITS})") + math(EXPR _rk_min "2 * ${_rk_wm_check}") + if(_rk_lead LESS _rk_min) + message(WARNING + "Re-key lead is ${_rk_lead} packets vs the watermark check interval " + "${_rk_wm_check}; a high-rate flow may exhaust its key batch before the " + "re-key completes (TX fails closed until it does). Raise KEY_LEAF_BITS, " + "KEY_NODE_BITS, or KEY_REKEY_WATERMARK.") + endif() +endif() # Flow statistics (requires FUSE) if(HAVE_FUSE) diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 109fe1d6..ff44ad68 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -7,6 +7,7 @@ include(dependencies/system/libraries) include(dependencies/system/explicit_bzero) include(dependencies/system/robustmutex) include(dependencies/system/fuse) +include(dependencies/system/liburcu) include(dependencies/system/sysrandom) # Cryptography diff --git a/cmake/dependencies/system/liburcu.cmake b/cmake/dependencies/system/liburcu.cmake new file mode 100644 index 00000000..89a7ab12 --- /dev/null +++ b/cmake/dependencies/system/liburcu.cmake @@ -0,0 +1,45 @@ +# Userspace RCU (liburcu) - optional. Enables lock-free data-plane key +# rotation; absent => per-flow rwlock fallback. The "bulletproof" flavour +# (urcu-bp) auto-registers reader threads, so application threads need no +# RCU lifecycle plumbing. +if(PkgConfig_FOUND) + pkg_check_modules(URCU_PKG QUIET IMPORTED_TARGET liburcu-bp) + if(URCU_PKG_FOUND AND NOT TARGET Urcu::Urcu) + add_library(Urcu::Urcu ALIAS PkgConfig::URCU_PKG) + endif() +endif() + +if(NOT URCU_PKG_FOUND) + find_library(URCU_BP_LIBRARY urcu-bp QUIET) + find_library(URCU_COMMON_LIBRARY urcu-common QUIET) + find_path(URCU_INCLUDE_DIR urcu-bp.h QUIET) + if(URCU_BP_LIBRARY AND URCU_COMMON_LIBRARY AND URCU_INCLUDE_DIR) + set(URCU_PKG_FOUND TRUE) + if(NOT TARGET Urcu::Urcu) + add_library(Urcu::Urcu INTERFACE IMPORTED) + set_target_properties(Urcu::Urcu PROPERTIES + INTERFACE_LINK_LIBRARIES "${URCU_BP_LIBRARY};${URCU_COMMON_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${URCU_INCLUDE_DIR}") + endif() + endif() +endif() + +if(URCU_PKG_FOUND) + set(DISABLE_LIBURCU FALSE CACHE BOOL "Disable liburcu (RCU) support") + if(NOT DISABLE_LIBURCU) + if(URCU_PKG_VERSION) + message(STATUS "liburcu (RCU) support enabled (version ${URCU_PKG_VERSION})") + else() + message(STATUS "liburcu (RCU) support enabled") + endif() + set(HAVE_LIBURCU TRUE CACHE INTERNAL "Userspace RCU (liburcu) available") + else() + message(STATUS "liburcu (RCU) support disabled by user") + unset(HAVE_LIBURCU CACHE) + endif() +else() + message(STATUS "Install liburcu (urcu-bp) for lock-free data-plane re-keying") + unset(HAVE_LIBURCU CACHE) +endif() + +mark_as_advanced(URCU_BP_LIBRARY URCU_COMMON_LIBRARY URCU_INCLUDE_DIR) |
