From c9cbf98eecf7dd54b7bd9f89609f08aa181ad0bf Mon Sep 17 00:00:00 2001 From: nguyenquocthao Date: Tue, 22 Sep 2026 15:33:41 +0700 Subject: [PATCH] Fix ICE ufrag/pwd collision from per-call time-seeded rand() utils_random_string() called srand(time(NULL)) on every invocation. Since time(NULL) has 1-second resolution and carries no per-process or per-device entropy, any two callers (different processes, different physical devices) that generate ICE credentials within the same UTC second produce byte-identical ufrag/pwd. RFC 8445 15.4 requires these values to be unguessable; a value fully determined by wall-clock second does not meet that, and colliding credentials also defeat ICE's demultiplexing when two agents share a listening port. Confirmed in production across an embedded device fleet using this library (via esp_peer): dozens of independent devices produced identical a=ice-ufrag/a=ice-pwd values, including overlapping sessions. Seed the PRNG once instead of per-call, using /dev/urandom where available (falling back to time+pid mixing otherwise) rather than time(NULL) alone. --- src/utils.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index f9ab38f6..ef728c02 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,21 +4,55 @@ #include #include #include +#if defined(__linux__) || defined(__APPLE__) +#include +#endif #include "mbedtls/md.h" #include "mbedtls/version.h" #if MBEDTLS_VERSION_NUMBER >= 0x04000000 #include "psa/crypto.h" #endif +static unsigned int utils_random_seed(void) { + unsigned int seed; + +#if defined(__linux__) || defined(__APPLE__) + FILE* f = fopen("/dev/urandom", "rb"); + + if (f != NULL) { + size_t n = fread(&seed, sizeof(seed), 1, f); + fclose(f); + + if (n == 1) { + return seed; + } + } + + return (unsigned int)time(NULL) ^ (unsigned int)getpid(); +#else + return (unsigned int)time(NULL); +#endif +} + void utils_random_string(char* s, const int len) { int i; + static int seeded = 0; static const char alphanum[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; - srand(time(NULL)); + // Seeding on every call from time(NULL) (1-second resolution) made this + // fully determined by wall-clock time: any two callers (different + // processes, different devices) invoked within the same second produced + // byte-identical output, which breaks ICE's requirement (RFC 8445 15.4) + // that ufrag/pwd be unguessable. Seed once, from real entropy where + // available. + if (!seeded) { + srand(utils_random_seed()); + seeded = 1; + } for (i = 0; i < len; ++i) { s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];