/* Noiz-based random number generator Copyright 2002 PhaethonH Permission granted to copy, modify, distribute, or otherwise use this code, provided this copyright notice remains intact. Software is provided as-is without warranties of any kind. */ /* NBRNG 2002.10.27-05 */ /* RNG using a 256-byte entropy pool and MD5 as a stirring function. Based on noiz-0.5. The idea of an entropy collector is to extract sampling values from nondeterministic sources (such as mouse movement, static from a radio, or the Geiger count of a radioactive isotope), then mix (stir) those values up with an "entropy pool", which builds up randomness. When a random number is needed, bits can be pulled out of anywhere in the pool (such as the first 32 bits). In Q3VM, the primary sources of entropy are likely to be data associated with players -- usercmd, positions, timing between GAME_CLIENT_THINK. */ /* to use: #include "qnoiz.h" Declare space for a noiz_t struct somewhere in g_main.c: noiz_t noiz; Make the noiz_t instance globally visible in g_public.h: extern noiz_t noiz; Place a call to noiz_init in G_InitGame(): noiz_init(&noiz); Call noiz_deinit() in G_ShutdownGame(): noiz_destroy(&noiz); Stir up the pool at any time with a call to noiz_stir(): noiz_stir(&noiz, &cmd, sizeof(cmd)); NOIZ_STIR(cmd) To obtain a random number, store the return value of noiz_stir(). Use NULL for source if nothing else is suitable: int rndvalue = noiz_stir(&noiz, (unsigned char*)cmd, sizeof(cmd)); int rndvalue = NOIZ_STIR(cmd); int rndvalue = noiz_stir(&noiz, NULL, 0); int rndvalue = NOIZ_RAND(); */ #include "q_shared.h" #include "g_local.h" #include "qnoiz.h" const char * NOIZ_CVARNAME = "g_noiz"; /* Retrieve a random number. */ //#define noiz_get(self) (*(int*)(self->pool + 42)) #define noiz_get(self,n) ((*(int*)(self->pool + (n*sizeof(int)))) & 0x7fffffff) vmCvar_t g_noiz; /* XXX: Not usable until conversion format changes. */ /* Restore pool state from cvar. Returns 1 if state restored meaningfully. Returns 0 if no stored state found. */ int noiz_load (noiz_t *self) { int i, val, len; char ch; trap_Cvar_VariableStringBuffer(NOIZ_CVARNAME, self->store, sizeof(self->store)); if (self->store[0] == 0) { return 0; } len = strlen(self->store); memset(self->pool, 0, sizeof(self->pool)); /* Expects form of "123456789ABCDEF012345678..." (hex digits) */ //printf("Loading %d hexs from %s...\n", len, NOIZ_CVARNAME); for (i = 0; i < sizeof(self->store); i++) { ch = (i < len) ? self->store[i] : 0; /* Eek, this is nasty... */ val = (('0' <= ch) && (ch <= '9')) ? (ch - '0') : (('A' <= ch) && (ch <= 'F')) ? (ch - 'A' + 10) : (('a' <= ch) && (ch <= 'f')) ? (ch - 'a' + 10) : 0; if (i % 2) self->pool[i/2] |= val; /* odd => low nibble. */ else self->pool[i/2] |= (val << 4); /* even => high nibble. */ } return 1; } /* Store pool state into cvar. Returns 0. */ int noiz_save (noiz_t *self) { int i, val; char ch; //printf("Saving noiz to %s...\n", NOIZ_CVARNAME); for (i = 0; i < sizeof(self->store); i++) { val = self->pool[i/2]; if (i % 2) val &= 0x0F; else val >>= 4; ch = ((0 <= val) && (val <= 9)) ? (val + '0') : (val - 10 + 'A'); self->store[i] = ch; } self->store[i-1] = 0; /* Terminating '\0' */ trap_Cvar_Set(NOIZ_CVARNAME, self->store); return 0; } /* Initialize the entropy pool. Returns 0. */ int noiz_init (noiz_t *self) { int i; self->md5context = &(self->md5context_opaque); if (!noiz_load(self)) { /* Something about seeding from qtime_t on server-side? */ //printf("NOIZ: Seeding pool...\n"); trap_SendConsoleCommand(EXEC_APPEND, va("seta %s \"\"", NOIZ_CVARNAME)); for (i = 0; i < NOIZ_POOLSIZE; i++) { self->pool[i] = i; } } return 0; } /* Save and deinitialize entropy pool. */ int noiz_destroy (noiz_t *self) { int i; noiz_save(self); return 0; } /* Check for autosave. */ void noiz_alarm (noiz_t *self) { if (level.time < self->autosave) return; //printf("Autosaving noiz state.\n"); noiz_save(self); self->autosave = level.time + NOIZ_AUTOSAVE_PERIOD; } /* Stir the entropy pool. Returns a random number. */ int noiz_stir (noiz_t *self, unsigned char *source, int len) { int e, i; int seconds, pid, ppid; int row; static drain = 0; unsigned char noise[NOIZ_POOLSIZE]; if ((!source) || (len == 0)) { //printf("NOIZ: sourceless stir\n"); /* Step through remaining pool bits. */ if (++drain < (NOIZ_POOLSIZE / sizeof(int))) { //printf("NOIZ: draining %d/%d\n", drain, (NOIZ_POOLSIZE / sizeof(int))); return noiz_get(self, drain); } } /* Stir away, even with empty source. */ if (!source) { source = (unsigned char *)&(level.time); len = sizeof(level.time); } //printf("NOIZ: Stirring entropy pool with %d+%d.\n", source, len); memset(noise, 0, sizeof(noise)); memcpy(noise, source, (len < sizeof(noise)) ? len : sizeof(noise)); MD5Init(self->md5context); MD5Update(self->md5context, (unsigned char*)(self->pool), sizeof(self->pool)); /* Populate noise[] from entropy source. */ for(i = 0; (i < len) && (i < NOIZ_POOLSIZE); i++) { noise[i] = (i >= len) ? 0 : source[i]; } MD5Update(self->md5context, (unsigned char*)noise, sizeof(noise)); // time(&seconds); seconds = 0; pid = 0; ppid = 0; // MD5Update(self->md5context, (unsigned char *) &seconds, sizeof seconds); // MD5Update(self->md5context, (unsigned char *) &pid, sizeof pid); // MD5Update(self->md5context, (unsigned char *) &ppid, sizeof pid); MD5Final(self->in_hash, self->md5context); for (row = 0; row < 16; row++) { MD5Init(self->md5context); MD5Update(self->md5context, self->pool + (16 * row), 16); MD5Update(self->md5context, self->in_hash, 16); MD5Update(self->md5context, self->in_hash, row + 1); MD5Final(self->out_hash, self->md5context); for (i = 0; i < 16; i++) { self->pool[ (16*row) + i ] ^= self->out_hash[i]; } } return noiz_get(self,(drain=0)); }