summaryrefslogtreecommitdiff
path: root/src/lib/serdes-irm.c
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-06-14 16:16:03 +0200
committerSander Vrijders <sander@ouroboros.rocks>2026-06-29 08:32:58 +0200
commitfdb50b8256f1038d5bc4f906b41605cacc769bf4 (patch)
tree8962c4188a208f81e3cdba39cc54a01da933d787 /src/lib/serdes-irm.c
parentc386d9b7caa56f472fdce20ff5b2841ed41dd539 (diff)
downloadouroboros-fdb50b8256f1038d5bc4f906b41605cacc769bf4.tar.gz
ouroboros-fdb50b8256f1038d5bc4f906b41605cacc769bf4.zip
irmd: Deliver flow re-keying
Re-key each encrypted flow's batch root periodically so a long-lived flow never exhausts or over-uses a single root. The IRMd re-runs the OAP exchange with the peer IRMd over the flow-update relay. The per-flow re-keying state is tracked in the registry (reg_flow). A re-key delivers one root seed from the OAP exchange. keyrot immediately HKDF-expands it into 128 node keys (KR_NODES_SZ = 128 × 32 B) and wipes the root. Then each of the 128 node keys is itself a root → HKDF-expanded into 64 (2^KEY_NODE_BITS) leaf keys, forked per direction; each leaf key is the actual AEAD key, good for 2^20 packets (the low counter bits are its nonce/seq). If the number of keys runs low, a re-key will be triggered (KEY_REKEY_WATERMARK). The rekey is signalled out of band to the application. The rbuff ACL is generalized into a flags word, so an RB_REKEY bit rides alongside the access RB_RD/RB_WR and FLOWDOWN/FLOWPEER bits. The RD and WR bits are revised ditching the fcntl historical weirdness. The seed is pulled via flow_read/flow_write, installed with crypt_rekey(). TX holds the old epoch until the peer is observed on the new one (or a grace deadline elapses), promoted from both the read and write paths so a recv-mostly flow still advances. Also fix the FLOW_ACCEPT and FLOW_ALLOC handlers, which on a key-buffer allocation failure returned from inside the cleanup-push region: that leaked the reply message and skipped both the stack-key scrub and the cleanup pop. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/lib/serdes-irm.c')
-rw-r--r--src/lib/serdes-irm.c132
1 files changed, 106 insertions, 26 deletions
diff --git a/src/lib/serdes-irm.c b/src/lib/serdes-irm.c
index a896576d..24bb349f 100644
--- a/src/lib/serdes-irm.c
+++ b/src/lib/serdes-irm.c
@@ -174,6 +174,48 @@ int flow__irm_result_des(buffer_t * buf,
else
memset(sk->key, 0, SYMMKEYSZ);
+ sk->epoch = msg->has_generation ? (uint8_t) msg->generation : 0;
+
+ irm_msg__free_unpacked(msg, NULL);
+
+ return 0;
+ fail:
+ irm_msg__free_unpacked(msg, NULL);
+ fail_msg:
+ return err;
+}
+
+int flow_rekey__irm_result_des(buffer_t * buf,
+ struct crypt_sk * sk,
+ bool * has_key)
+{
+ irm_msg_t * msg;
+ int err;
+
+ msg = irm_msg__unpack(NULL, buf->len, buf->data);
+ if (msg == NULL) {
+ err = -EIRMD;
+ goto fail_msg;
+ }
+
+ if (!msg->has_result) {
+ err = -EIRMD;
+ goto fail;
+ }
+
+ if (msg->result < 0) {
+ err = msg->result;
+ goto fail;
+ }
+
+ *has_key = msg->has_sym_key && msg->sym_key.len == SYMMKEYSZ;
+ if (*has_key) {
+ memcpy(sk->key, msg->sym_key.data, SYMMKEYSZ);
+ sk->nid = NID_undef;
+ sk->epoch = msg->has_generation ?
+ (uint8_t) msg->generation : 0;
+ }
+
irm_msg__free_unpacked(msg, NULL);
return 0;
@@ -222,6 +264,44 @@ int flow_dealloc__irm_req_ser(buffer_t * buf,
return -ENOMEM;
}
+int flow_update__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ bool rekey)
+{
+ irm_msg_t * msg;
+ size_t len;
+
+ msg = malloc(sizeof(*msg));
+ if (msg == NULL)
+ goto fail_malloc;
+
+ irm_msg__init(msg);
+
+ msg->code = IRM_MSG_CODE__IRM_FLOW_UPDATE;
+ msg->flow_info = flow_info_s_to_msg(flow);
+ if (msg->flow_info == NULL)
+ goto fail_msg;
+
+ msg->has_rekey = true;
+ msg->rekey = rekey;
+
+ len = irm_msg__get_packed_size(msg);
+ if (len == 0 || len > buf->len)
+ goto fail_msg;
+
+ buf->len = len;
+
+ irm_msg__pack(msg, buf->data);
+ irm_msg__free_unpacked(msg, NULL);
+
+ return 0;
+
+ fail_msg:
+ irm_msg__free_unpacked(msg, NULL);
+ fail_malloc:
+ return -ENOMEM;
+}
+
int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow)
{
@@ -398,15 +478,19 @@ int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf,
return 0;
fail_msg:
+ /* hash/pk are borrowed from the caller; detach before free. */
+ msg->hash.len = 0;
+ msg->hash.data = NULL;
+ msg->pk.len = 0;
+ msg->pk.data = NULL;
irm_msg__free_unpacked(msg, NULL);
fail_malloc:
return -ENOMEM;
}
-int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
- const struct flow_info * flow,
- int response,
- const buffer_t * data)
+int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const buffer_t * data)
{
irm_msg_t * msg;
size_t len;
@@ -417,16 +501,14 @@ int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
irm_msg__init(msg);
- msg->code = IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY;
- msg->flow_info = flow_info_s_to_msg(flow);
+ msg->code = IRM_MSG_CODE__IPCP_FLOW_UPDATE_ARR;
+ msg->flow_info = flow_info_s_to_msg(flow);
if (msg->flow_info == NULL)
goto fail_msg;
msg->has_pk = true;
msg->pk.len = data->len;
msg->pk.data = data->data;
- msg->has_response = true;
- msg->response = response;
len = irm_msg__get_packed_size(msg);
if (len == 0 || len > buf->len)
@@ -436,27 +518,25 @@ int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
irm_msg__pack(msg, buf->data);
- /* Don't free * data! */
- msg->pk.len = 0;
+ /* Don't free data! */
+ msg->pk.len = 0;
msg->pk.data = NULL;
-
irm_msg__free_unpacked(msg, NULL);
return 0;
fail_msg:
- /* hash/pk are borrowed from the caller; detach before free. */
- msg->hash.len = 0;
- msg->hash.data = NULL;
- msg->pk.len = 0;
- msg->pk.data = NULL;
+ /* pk.data is borrowed from the caller; detach before free. */
+ msg->pk.len = 0;
+ msg->pk.data = NULL;
irm_msg__free_unpacked(msg, NULL);
fail_malloc:
return -ENOMEM;
}
-int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
- const struct flow_info * flow,
- const buffer_t * data)
+int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ int response,
+ const buffer_t * data)
{
irm_msg_t * msg;
size_t len;
@@ -467,14 +547,16 @@ int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
irm_msg__init(msg);
- msg->code = IRM_MSG_CODE__IPCP_FLOW_UPDATE_ARR;
- msg->flow_info = flow_info_s_to_msg(flow);
+ msg->code = IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY;
+ msg->flow_info = flow_info_s_to_msg(flow);
if (msg->flow_info == NULL)
goto fail_msg;
msg->has_pk = true;
msg->pk.len = data->len;
msg->pk.data = data->data;
+ msg->has_response = true;
+ msg->response = response;
len = irm_msg__get_packed_size(msg);
if (len == 0 || len > buf->len)
@@ -484,16 +566,14 @@ int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
irm_msg__pack(msg, buf->data);
- /* Don't free data! */
- msg->pk.len = 0;
+ /* Don't free * data! */
+ msg->pk.len = 0;
msg->pk.data = NULL;
+
irm_msg__free_unpacked(msg, NULL);
return 0;
fail_msg:
- /* pk.data is borrowed from the caller; detach before free. */
- msg->pk.len = 0;
- msg->pk.data = NULL;
irm_msg__free_unpacked(msg, NULL);
fail_malloc:
return -ENOMEM;