1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-07 10:13:12 +02:00

relay: fix timing attack on password authentication (GHSA-vhv8-g2r9-cwcc)

The relay authentication used non-constant-time comparisons (strcasecmp,
strcmp) to verify password hashes and plaintext passwords, allowing an
attacker to derive the expected hash byte-by-byte from response timing
and then authenticate without knowing the password.

- SHA/PBKDF2 hex hash comparisons: normalize the client-supplied hash to
  uppercase and compare in constant time over the fixed expected length.
- Plaintext password comparison: HMAC-SHA256 both passwords with a fresh
  per-call random key and compare the fixed-size MACs in constant time,
  hiding both per-byte timing and the password length.

Add string_memcmp_constant_time helper in core, exposed via the plugin
API. Bump WEECHAT_PLUGIN_API_VERSION accordingly.
This commit is contained in:
Sébastien Helleu
2026-05-30 19:00:59 +02:00
parent 405707d544
commit a17a80f1d0
7 changed files with 158 additions and 11 deletions
+37
View File
@@ -916,6 +916,43 @@ string_strcmp_ignore_chars (const char *string1, const char *string2,
string_charcasecmp (string1, string2);
}
/*
* Compares two memory areas of the same size in constant time.
*
* Use to compare secrets (e.g. password hashes, MACs) without leaking
* information through the comparison's running time. The loop always
* walks the full "size" bytes and uses only bitwise operations on the
* data, so the execution time depends on "size" alone, not on the
* position of the first differing byte.
*
* If either pointer is NULL, the areas are considered different (the
* NULL check itself is not constant time but does not look at any
* secret content).
*
* Returns:
* 0: areas are equal
* 1: areas differ
*/
int
string_memcmp_constant_time (const void *area1, const void *area2, size_t size)
{
const unsigned char *p1, *p2;
unsigned char diff;
size_t i;
if (!area1 || !area2)
return (area1 == area2) ? 0 : 1;
p1 = (const unsigned char *)area1;
p2 = (const unsigned char *)area2;
diff = 0;
for (i = 0; i < size; i++)
diff |= p1[i] ^ p2[i];
return (diff == 0) ? 0 : 1;
}
/*
* Searches for a string in another string (locale and case independent).
*