mirror of
https://github.com/coturn/coturn.git
synced 2026-10-24 00:08:57 +03:00
## What The legacy (non-`--stateless-nonce`) challenge nonce has been 24 hex chars instead of 16 since #2002, on every 64-bit server — including those that never enabled `--stateless-nonce`. ## Why `generate_random_challenge_nonce` writes each `turn_random_number()` draw with `snprintf` and relies on the size bound to clip it. `"%08lx"` of a 64-bit draw is *up to 16* hex chars, and `"%04x"` of a 32-bit draw is 8 — the bound is what cuts each write back to 8. That bound was `NONCE_MAX_SIZE`, which [grew from 17 to 25](https://github.com/coturn/coturn/blob/master/src/server/ns_turn_session.h) when it was redefined as `TURN_STATELESS_NONCE_SIZE` to hold the 24-char stateless nonce. The extra digits then went through: | Platform | `sizeof(long)` | Branch | Before #2002 | On master | |---|---|---|---|---| | Linux/macOS x86-64, arm64 | 8 | `%08lx` | 16 chars | **24 chars** | | Windows x64, 32-bit | 4 | `%04x` | 16 chars | **20 chars** | Reproduced standalone with the exact expressions from the two loops: ``` sizeof(long)=8 old NONCE_MAX_SIZE=17: len=16 'deadbeefdeadbeef' new NONCE_MAX_SIZE=25: len=24 'deadbeefdeadbeefcafebabf' ``` ## Impact Low, and not a security issue: - **No overflow** — `ss->nonce` is `NONCE_MAX_SIZE` (25) bytes; the maximum write is 24 + NUL. - **No auth breakage** — validation is a `strcmp` against `ss->nonce`, so the longer nonce round-trips. - **Entropy went up, not down** — 96 bits vs. the previous 64 on LP64. - **Still RFC-legal** — RFC 8489 §14.10 caps the nonce below 128 characters. What it does break is a documented invariant: `ns_turn_session.h` and `create_challenge_response` both state that the two nonce formats *differ in length*, and `tests/test_stateless_nonce.c` asserts that "legacy random-style 16-char nonces are not stateless nonces". On 64-bit they no longer differ. Nothing branches on that length today (`turn_check_stateless_nonce` rejects on the MAC, not the length), but it is a trap for the next length-based fast path. Separately, an opt-in feature silently changed wire output for deployments that did not opt in. ## Change - Add `TURN_RANDOM_NONCE_LENGTH` / `TURN_RANDOM_NONCE_SIZE` so each nonce format is sized by its own constant. - Extract the generation into `generate_random_challenge_nonce()`, bounded by `TURN_RANDOM_NONCE_SIZE`, with a comment recording that the bound — not the format specifier — is what fixes the width. - Cover both loops with a unit test in the existing server-core harness. Behavior is restored byte-for-byte to pre-#2002: the truncation semantics are preserved rather than replaced with an explicit bit-selection, which would have picked different bits. ## Validation - `ctest` — 18/18 pass on macOS/arm64, 17/17 in Linux/Docker. - Reverting just the bound fails the new test with `Expected 16 Was 24`. - `run_tests.sh`, `run_tests_conf.sh`, `run_tests_stateless_nonce.sh`, `run_tests_mobile.sh`, `run_tests_multiplex_peer.sh`, `run_tests_dtls_default.sh`, `run_tests_rfc5780.sh` — all clean on Linux; the first four also clean on macOS. - `fuzzing/run-local.sh ASan 0 -runs=1` and `ASan 1 -runs=1` — clean. - `clang-format-15` clean.