diff options
| author | Dimitri Staessens <dimitri@ouroboros.rocks> | 2026-06-14 16:16:03 +0200 |
|---|---|---|
| committer | Sander Vrijders <sander@ouroboros.rocks> | 2026-06-29 08:32:58 +0200 |
| commit | fdb50b8256f1038d5bc4f906b41605cacc769bf4 (patch) | |
| tree | 8962c4188a208f81e3cdba39cc54a01da933d787 /src/lib/serdes-irm.c | |
| parent | c386d9b7caa56f472fdce20ff5b2841ed41dd539 (diff) | |
| download | ouroboros-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.c | 132 |
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; |
