--- a/sys/net/wg/wg_noise.c 2026-06-29 12:51:19.000000000 +0000 +++ b/sys/net/wg/wg_noise.c 2026-10-26 04:01:54.544244130 +0000 @@ -27,6 +27,7 @@ */ #include +#include #include #include /* ilog2() */ #include @@ -1085,6 +1086,30 @@ return (ok); } +/* + * Constant-time lexicographic (== numeric for big-endian) compare over + * NOISE_TIMESTAMP_LEN bytes; returns <0, 0, >0 like memcmp without + * short-circuiting. Mirrors OpenBSD fixedtime_bcmp hardening. + */ +static int +noise_tai64n_cmp(const uint8_t *a, const uint8_t *b) +{ + const volatile uint8_t *x = a, *y = b; + uint8_t gt = 0, lt = 0, eq = 1; + size_t i; + + for (i = 0; i < NOISE_TIMESTAMP_LEN; i++) { + uint16_t df, db; + + df = (uint16_t)x[i] - y[i]; + db = (uint16_t)y[i] - x[i]; + gt |= (uint8_t)(df >> 8) & eq; + lt |= (uint8_t)(db >> 8) & eq; + eq &= (uint8_t)(~((db | (uint16_t)(0 - db)) >> 8)) & 1; + } + return (int)(gt - lt); +} + struct noise_remote * noise_consume_initiation(struct noise_local *l, uint32_t s_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN], @@ -1139,8 +1164,9 @@ */ lockmgr(&r->r_handshake_lock, LK_EXCLUSIVE); - /* Replay */ - if (memcmp(timestamp, r->r_timestamp, NOISE_TIMESTAMP_LEN) > 0) + /* Replay (constant-time compare: reject-on-less must not leak + * the position of the first differing byte) */ + if (noise_tai64n_cmp(timestamp, r->r_timestamp) > 0) memcpy(r->r_timestamp, timestamp, NOISE_TIMESTAMP_LEN); else goto error_set;