From 8cc8b8663d960c8ec52ae1fb3aa74ae4e1c52da8 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Wed, 17 Jun 2026 10:53:02 +0000 Subject: [PATCH] 0701 nwnss: make rand helpers seed deterministic --- nwnss-audit.md | 4 +- src/nwnss/library/misc/rand.c | 54 ++++++++++-------- tests/nwnss/rand/test_nwnss_rand.c | 91 +++++++++++++++++++++++++----- 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/nwnss-audit.md b/nwnss-audit.md index adc8051..5c98a3a 100644 --- a/nwnss-audit.md +++ b/nwnss-audit.md @@ -106,7 +106,7 @@ still stay original and remain listed, because later imports may rely on them. | PARTIAL | `src/nwnss/comn/common/ndp_messagehandler.c` | ORIG wrapper | link smoke | Novell wrapper name/pattern; full audit open. | | TEMP | `src/nwnss/comn/comnModule.c` | TEMP | link smoke | Transition module identity bridge; should disappear later. | | AUDITED | `src/nwnss/library/bit.c` | PORT | nwnss.bit, nwnss.bitmap | No original provider found beyond bit.h/libNSS.imp; native-NINT semantics fixed/tested in 0698. | -| AUDITED | `src/nwnss/library/misc/rand.c` | PORT | nwnss.rand, nwnss.namespace | No original provider found beyond rand.h/libNSS.imp/callers; libsodium entropy port checked in 0700. | +| AUDITED | `src/nwnss/library/misc/rand.c` | PORT | nwnss.rand, nwnss.namespace | No original provider found beyond rand.h/libNSS.imp/callers; seed-deterministic PRNG port checked in 0700/0701; libsodium only initializes default state before explicit seeding. | | TODO | `src/nwnss/library/misc/rbpTree.c` | PORT | nwnss.rbpTree | Algorithmic port; has tests, but original-provider search/checklist still open. | | TODO | `src/nwnss/library/os/currentTime.c` | PORT | nwnss.utc | Userspace time port; full audit open. | | AUDITED | `src/nwnss/library/os/delay.c` | ORIG+FIX/PORT | nwnss.schedule, nwnss.snooze | Scheduler functions kept as functions, not macros; checked in 0688-0690. | @@ -606,7 +606,7 @@ even if it already compiles or has indirect test coverage. | TODO | ORIG+FIX? | not yet classified | `src/nwnss/library/misc/histogram.c` | Must be compared against original source and classified. | | TODO | ORIG+FIX? | not yet classified | `src/nwnss/library/misc/lbVolume.c` | Must be compared against original source and classified. | | TODO | ORIG+FIX? | not yet classified | `src/nwnss/library/misc/nssErrorTable.c` | Must be compared against original source and classified. | -| AUDITED | PORT | nwnss.rand, nwnss.namespace | `src/nwnss/library/misc/rand.c` | No original provider found beyond rand.h/libNSS.imp/callers; libsodium entropy port checked in 0700. | +| AUDITED | PORT | nwnss.rand, nwnss.namespace | `src/nwnss/library/misc/rand.c` | No original provider found beyond rand.h/libNSS.imp/callers; seed-deterministic PRNG port checked in 0700/0701; libsodium only initializes default state before explicit seeding. | | TODO | PORT | nwnss.rbpTree | `src/nwnss/library/misc/rbpTree.c` | Algorithmic port; has tests, but original-provider search/checklist still open. | | TODO | ORIG+FIX? | not yet classified | `src/nwnss/library/misc/register.c` | Must be compared against original source and classified. | | TODO | ORIG+FIX? | not yet classified | `src/nwnss/library/misc/sysimp.c` | Must be compared against original source and classified. | diff --git a/src/nwnss/library/misc/rand.c b/src/nwnss/library/misc/rand.c index 89d3009..6702df0 100644 --- a/src/nwnss/library/misc/rand.c +++ b/src/nwnss/library/misc/rand.c @@ -4,12 +4,18 @@ | | The shipped NSS sources contain shared/sdk/library/rand.h and export | these symbols from the NSS library import lists, but no corresponding - | C implementation was present in the supplied source archives. + | C implementation was present in the supplied source archives or public + | sources checked during the nwnss import audit. | - | Keep the NSS symbol surface and back every generated value with the - | bundled libsodium randombytes backend. The seed entry points are kept - | as API-compatible state hooks only; they do not replace OS entropy with - | a private deterministic generator. + | Novell LibC documents rand()/rand_r() as seeded pseudo-random sequence + | APIs. Keep the same seed-driven model for the wider NSS QUAD helpers: + | srandQuad()/srndQuad()/srndLong() set deterministic state and subsequent + | randQuad()/rndQuad()/rndLong() calls advance that state. If callers use + | the generator before explicitly seeding it, initialize the state once + | from libsodium randombytes_buf(), which is the userspace equivalent of a + | kernel random-byte source. + | + | This is a MARS userspace port, not Novell original source. +-------------------------------------------------------------------------*/ #include @@ -18,12 +24,11 @@ #include #include -static QUAD seedSalt; -static QUAD seedCounter; -static int haveSeedSalt; +static QUAD randState; +static int haveRandState; static int sodiumReady; -static void nwnssRandInit(void) +static void nwnssRandInitSodium(void) { if (!sodiumReady) { int rc = sodium_init(); @@ -32,34 +37,37 @@ static void nwnssRandInit(void) } } -static QUAD nwnssSeedMix(void) +static QUAD nwnssRandNextFromState(QUAD *state) { QUAD z; - if (!haveSeedSalt) - return 0; - - z = seedSalt + (++seedCounter * 0x9e3779b97f4a7c15ULL); + *state += 0x9e3779b97f4a7c15ULL; + z = *state; z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); } +static void nwnssRandEnsureSeeded(void) +{ + if (haveRandState) + return; + + nwnssRandInitSodium(); + randombytes_buf(&randState, sizeof(randState)); + haveRandState = 1; +} + void srandQuad(QUAD seed) { - seedSalt ^= seed + 0x4e53534c49424e57ULL; - seedCounter = 0; - haveSeedSalt = 1; + randState = seed; + haveRandState = 1; } SQUAD randQuad(void) { - QUAD value; - - nwnssRandInit(); - randombytes_buf(&value, sizeof(value)); - value ^= nwnssSeedMix(); - return (SQUAD)value; + nwnssRandEnsureSeeded(); + return (SQUAD)nwnssRandNextFromState(&randState); } void srndQuad(QUAD seed) diff --git a/tests/nwnss/rand/test_nwnss_rand.c b/tests/nwnss/rand/test_nwnss_rand.c index 9ef1619..5f95451 100644 --- a/tests/nwnss/rand/test_nwnss_rand.c +++ b/tests/nwnss/rand/test_nwnss_rand.c @@ -17,27 +17,80 @@ static QUAD deterministic_rand(void) return deterministic_next++; } -int main(void) +static int check_repeatable_quad_seed(void) { - unsigned freq[4]; - SQUAD first; - SQUAD second; - QUAD long_value; - double chi; + SQUAD first_a; + SQUAD second_a; + SQUAD first_b; + SQUAD second_b; + SQUAD first_other; srandQuad(0x123456789abcdef0ULL); - first = randQuad(); - second = randQuad(); - CHECK(first != second); + first_a = randQuad(); + second_a = randQuad(); + CHECK(first_a != second_a); + + srandQuad(0x123456789abcdef0ULL); + first_b = randQuad(); + second_b = randQuad(); + CHECK(first_b == first_a); + CHECK(second_b == second_a); + + srandQuad(0x123456789abcdef1ULL); + first_other = randQuad(); + CHECK(first_other != first_a); + + return 0; +} + +static int check_repeatable_rnd_quad_seed(void) +{ + SQUAD seq_a[3]; + SQUAD seq_b[3]; + int i; srndQuad(0x55aaULL); - first = rndQuad(); - second = rndQuad(); - CHECK(first != second); + for (i = 0; i < 3; ++i) + seq_a[i] = rndQuad(); + + srndQuad(0x55aaULL); + for (i = 0; i < 3; ++i) + seq_b[i] = rndQuad(); + + for (i = 0; i < 3; ++i) + CHECK(seq_a[i] == seq_b[i]); + + CHECK(seq_a[0] != seq_a[1]); + CHECK(seq_a[1] != seq_a[2]); + + return 0; +} + +static int check_repeatable_long_seed(void) +{ + QUAD first_a; + QUAD second_a; + QUAD first_b; + QUAD second_b; srndLong(0x77bbULL); - long_value = rndLong(); - CHECK((SQUAD)long_value == (SQUAD)long_value); + first_a = rndLong(); + second_a = rndLong(); + CHECK(first_a != second_a); + + srndLong(0x77bbULL); + first_b = rndLong(); + second_b = rndLong(); + CHECK(first_b == first_a); + CHECK(second_b == second_a); + + return 0; +} + +static int check_chi_square(void) +{ + unsigned freq[4]; + double chi; deterministic_next = 0; chi = chiSquareQuad(8, 4, freq, deterministic_rand); @@ -62,3 +115,13 @@ int main(void) return 0; } + +int main(void) +{ + CHECK(check_repeatable_quad_seed() == 0); + CHECK(check_repeatable_rnd_quad_seed() == 0); + CHECK(check_repeatable_long_seed() == 0); + CHECK(check_chi_square() == 0); + + return 0; +}