summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-07-02 19:05:25 +0200
committerSander Vrijders <sander@ouroboros.rocks>2026-07-08 11:02:24 +0200
commit942e4c64ad03e537a77366c6bf1309241b71f1e8 (patch)
tree18f34aa34befd23673ecc1638919349821fa791f
parentfca717aba18850d2e32a069905f03314cd836c17 (diff)
downloadouroboros-942e4c64ad03e537a77366c6bf1309241b71f1e8.tar.gz
ouroboros-942e4c64ad03e537a77366c6bf1309241b71f1e8.zip
lib: Separate rekey replay from epoch conflict
keyrot_rekey() treated any re-key attempt against a live epoch (current or previous) the same way, whether the offered root key matched the live one (a replay) or was genuinely different (a conflict). Compare the offered key against the live batch's root and return -EREPLAY for a match, keeping -1/-ECRYPT for an actual conflict, so callers can handle replayed re-keys distinctly from real ones. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
-rw-r--r--src/lib/crypt.c8
-rw-r--r--src/lib/crypt/keyrot.c27
-rw-r--r--src/lib/tests/keyrot_test.c23
3 files changed, 42 insertions, 16 deletions
diff --git a/src/lib/crypt.c b/src/lib/crypt.c
index e4cf7885..cbbb9bc2 100644
--- a/src/lib/crypt.c
+++ b/src/lib/crypt.c
@@ -887,11 +887,17 @@ int crypt_get_headsz(struct crypt_ctx * ctx)
int crypt_rekey(struct crypt_ctx * ctx,
struct crypt_sk * sk)
{
+ int ret;
+
assert(ctx != NULL);
assert(sk != NULL);
assert(ctx->kr != NULL);
- return keyrot_rekey(ctx->kr, sk->key, sk->epoch) == 0 ? 0 : -ECRYPT;
+ ret = keyrot_rekey(ctx->kr, sk->key, sk->epoch);
+ if (ret == -EREPLAY)
+ return -EREPLAY;
+
+ return ret == 0 ? 0 : -ECRYPT;
}
int crypt_get_tagsz(struct crypt_ctx * ctx)
diff --git a/src/lib/crypt/keyrot.c b/src/lib/crypt/keyrot.c
index def29297..e98df356 100644
--- a/src/lib/crypt/keyrot.c
+++ b/src/lib/crypt/keyrot.c
@@ -26,6 +26,7 @@
#include <ouroboros/atomics.h>
#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
#include <ouroboros/pthread.h>
#include <ouroboros/rcu.h>
@@ -470,17 +471,17 @@ void keyrot_destroy(struct keyrot * kr)
}
/* A dup live epoch shadows straggler RX; epoch is peer-driven. */
-static bool kr_epoch_live(const struct kr_batch * cur,
- const struct kr_batch * prev,
- uint8_t epoch)
+static struct kr_batch * kr_live_batch(struct kr_batch * cur,
+ struct kr_batch * prev,
+ uint8_t epoch)
{
if (epoch == cur->epoch)
- return true;
+ return cur;
- if (prev == NULL)
- return false;
+ if (prev != NULL && epoch == prev->epoch)
+ return prev;
- return epoch == prev->epoch;
+ return NULL;
}
int keyrot_rekey(struct keyrot * kr,
@@ -491,6 +492,8 @@ int keyrot_rekey(struct keyrot * kr,
struct kr_batch * old_prev;
struct kr_batch * cur;
struct kr_batch * prev;
+ struct kr_batch * live;
+ int ret;
assert(kr != NULL);
assert(root != NULL);
@@ -507,10 +510,16 @@ int keyrot_rekey(struct keyrot * kr,
cur = rcu_deref(kr->cur);
prev = rcu_deref(kr->prev);
- if (kr_epoch_live(cur, prev, epoch)) {
+ live = kr_live_batch(cur, prev, epoch);
+ if (live != NULL) {
+ /* The first node key identifies the root. */
+ if (crypt_ct_cmp(live->nodes, nb->nodes, SYMMKEYSZ) == 0)
+ ret = -EREPLAY;
+ else
+ ret = -1;
rcu_wrunlock(&kr->guard);
kr_batch_free(nb);
- return -1;
+ return ret;
}
old_prev = kr->prev;
diff --git a/src/lib/tests/keyrot_test.c b/src/lib/tests/keyrot_test.c
index 10baee5c..efdc718e 100644
--- a/src/lib/tests/keyrot_test.c
+++ b/src/lib/tests/keyrot_test.c
@@ -28,6 +28,7 @@
#ifdef HAVE_OPENSSL
#include <ouroboros/crypt.h>
+#include <ouroboros/errno.h>
#include <ouroboros/pthread.h>
#include "crypt/keyrot.h"
@@ -112,8 +113,8 @@ static int test_rekey_dup_epoch(void)
if (a == NULL)
goto fail;
- if (keyrot_rekey(a, SEED_B, 0) == 0) {
- printf("Re-key to the current epoch accepted.\n");
+ if (keyrot_rekey(a, SEED_B, 0) != -1) {
+ printf("New key to the current epoch not a conflict.\n");
goto fail_a;
}
@@ -122,13 +123,23 @@ static int test_rekey_dup_epoch(void)
goto fail_a;
}
- if (keyrot_rekey(a, SEED_A, 1) == 0) {
- printf("Re-key to the current epoch accepted.\n");
+ if (keyrot_rekey(a, SEED_B, 1) != -EREPLAY) {
+ printf("Same key to the current epoch not a replay.\n");
goto fail_a;
}
- if (keyrot_rekey(a, SEED_A, 0) == 0) {
- printf("Re-key to the previous epoch accepted.\n");
+ if (keyrot_rekey(a, SEED_A, 1) != -1) {
+ printf("New key to the current epoch not a conflict.\n");
+ goto fail_a;
+ }
+
+ if (keyrot_rekey(a, SEED_A, 0) != -EREPLAY) {
+ printf("Same key to the previous epoch not a replay.\n");
+ goto fail_a;
+ }
+
+ if (keyrot_rekey(a, SEED_B, 0) != -1) {
+ printf("New key to the previous epoch not a conflict.\n");
goto fail_a;
}