random_numbers.c (n2n-2.8) | : | random_numbers.c (n2n-3.0) | ||
---|---|---|---|---|
/** | /** | |||
* (C) 2007-20 - ntop.org and contributors | * (C) 2007-21 - ntop.org and contributors | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 3 of the License, or | * the Free Software Foundation; either version 3 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not see see <http://www.gnu.org/licenses/> | * along with this program; if not see see <http://www.gnu.org/licenses/> | |||
* | * | |||
*/ | */ | |||
#ifdef SYS_getrandom | #include "random_numbers.h" | |||
#include <errno.h> | ||||
#endif | ||||
#include "n2n.h" | // the following code offers an alterate pseudo random number generator | |||
// namely XORSHIFT128+ to use instead of C's rand() | ||||
// its performance is on par with C's rand() | ||||
/* The following code offers an alterate pseudo random number generator | // the state must be seeded in a way that it is not all zero, choose some | |||
namely XORSHIFT128+ to use instead of C's rand(). Its performance is | // arbitrary defaults (in this case: taken from splitmix64) | |||
on par with C's rand(). | static rn_generator_state_t rn_current_state = { | |||
*/ | .a = 0x9E3779B97F4A7C15, | |||
.b = 0xBF58476D1CE4E5B9 | ||||
}; | ||||
/* The state must be seeded in a way that it is not all zero, choose some | // used for mixing the initializing seed | |||
arbitrary defaults (in this case: taken from splitmix64) */ | static uint64_t splitmix64 (splitmix64_state_t *state) { | |||
static struct rn_generator_state_t rn_current_state = { | ||||
.a = 0x9E3779B97F4A7C15, | ||||
.b = 0xBF58476D1CE4E5B9 }; | ||||
/* used for mixing the initializing seed */ | uint64_t result = state->s; | |||
static uint64_t splitmix64 (struct splitmix64_state_t *state) { | ||||
uint64_t result = state->s; | state->s = result + 0x9E3779B97F4A7C15; | |||
state->s = result + 0x9E3779B97F4A7C15; | result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; | |||
result = (result ^ (result >> 27)) * 0x94D049BB133111EB; | ||||
result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; | return result ^ (result >> 31); | |||
result = (result ^ (result >> 27)) * 0x94D049BB133111EB; | ||||
return result ^ (result >> 31); | ||||
} | } | |||
int n2n_srand (uint64_t seed) { | int n2n_srand (uint64_t seed) { | |||
uint8_t i; | ||||
struct splitmix64_state_t smstate = {seed}; | ||||
rn_current_state.a = 0; | ||||
rn_current_state.b = 0; | ||||
rn_current_state.a = splitmix64 (&smstate); | uint8_t i; | |||
rn_current_state.b = splitmix64 (&smstate); | splitmix64_state_t smstate = { seed }; | |||
/* the following lines could be deleted as soon as it is formally prooved that | rn_current_state.a = 0; | |||
there is no seed leading to (a == b == 0). Until then, just to be safe: */ | rn_current_state.b = 0; | |||
if ( (rn_current_state.a == 0) && (rn_current_state.b == 0) ) { | ||||
rn_current_state.a = 0x9E3779B97F4A7C15; | ||||
rn_current_state.b = 0xBF58476D1CE4E5B9; | ||||
} | ||||
/* stabilize in unlikely case of weak state with only a few bits set */ | rn_current_state.a = splitmix64 (&smstate); | |||
for(i = 0; i < 32; i++) | rn_current_state.b = splitmix64 (&smstate); | |||
n2n_rand(); | ||||
return 0; | // the following lines could be deleted as soon as it is formally prooved th | |||
} | at | |||
// there is no seed leading to (a == b == 0). until then, just to be safe: | ||||
if((rn_current_state.a == 0) && (rn_current_state.b == 0)) { | ||||
rn_current_state.a = 0x9E3779B97F4A7C15; | ||||
rn_current_state.b = 0xBF58476D1CE4E5B9; | ||||
} | ||||
/* The following code of xorshift128p was taken from | // stabilize in unlikely case of weak state with only a few bits set | |||
https://en.wikipedia.org/wiki/Xorshift as of July, 2019 | for(i = 0; i < 32; i++) | |||
and thus is considered public domain. */ | n2n_rand(); | |||
uint64_t n2n_rand () { | ||||
uint64_t t = rn_current_state.a; | return 0; | |||
uint64_t const s = rn_current_state.b; | } | |||
rn_current_state.a = s; | // the following code of xorshift128p was taken from | |||
t ^= t << 23; | // https://en.wikipedia.org/wiki/Xorshift as of July, 2019 | |||
t ^= t >> 17; | // and thus is considered public domain | |||
t ^= s ^ (s >> 26); | uint64_t n2n_rand (void) { | |||
rn_current_state.b = t; | ||||
uint64_t t = rn_current_state.a; | ||||
uint64_t const s = rn_current_state.b; | ||||
rn_current_state.a = s; | ||||
t ^= t << 23; | ||||
t ^= t >> 17; | ||||
t ^= s ^ (s >> 26); | ||||
rn_current_state.b = t; | ||||
return t + s; | return t + s; | |||
} | } | |||
/* The following code tries to gather some entropy from several sources | // the following code tries to gather some entropy from several sources | |||
for use as seed. Note, that this code does not set the random generator | // for use as seed. Note, that this code does not set the random generator | |||
state yet, a call to n2n_srand ( n2n_seed() ) would do. */ | // state yet, a call to n2n_srand (n2n_seed()) would do | |||
uint64_t n2n_seed (void) { | uint64_t n2n_seed (void) { | |||
uint64_t seed = 0; | uint64_t seed = 0; /* this could even go uninitialized */ | |||
uint64_t ret = 0; | uint64_t ret = 0; /* this could even go uninitialized */ | |||
size_t i; | size_t i = 0; | |||
#ifdef SYS_getrandom | #ifdef SYS_getrandom | |||
int rc = -1; | int rc = -1; | |||
for(i = 0; (i < RND_RETRIES) && (rc != sizeof(seed)); i++) { | for(i = 0; (i < RND_RETRIES) && (rc != sizeof(seed)); i++) { | |||
rc = syscall (SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK); | rc = syscall (SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK); | |||
// if successful, rc should contain the requested number of random bytes | // if successful, rc should contain the requested number of random bytes | |||
if(rc != sizeof(seed)) { | if(rc != sizeof(seed)) { | |||
if (errno != EAGAIN) { | if (errno != EAGAIN) { | |||
traceEvent(TRACE_ERROR, "n2n_seed faced error errno=%u from getrandom sy | traceEvent(TRACE_ERROR, "n2n_seed faced error errno=%u from getr | |||
scall.", errno); | andom syscall.", errno); | |||
break; | break; | |||
} | } | |||
} | } | |||
} | } | |||
// if we still see an EAGAIN error here, we must have run out of retries | ||||
if(errno == EAGAIN) { | // if we still see an EAGAIN error here, we must have run out of retries | |||
traceEvent(TRACE_ERROR, "n2n_seed saw getrandom syscall indicate not being a | if(errno == EAGAIN) { | |||
ble to provide enough entropy yet."); | traceEvent(TRACE_ERROR, "n2n_seed saw getrandom syscall indicate not bei | |||
} | ng able to provide enough entropy yet."); | |||
} | ||||
#endif | #endif | |||
// as we want randomness, it does no harm to add up even uninitialized values | // as we want randomness, it does no harm to add up even uninitialized value | |||
or | s or | |||
// erroneously arbitrary values returned from the syscall for the first time | // erroneously arbitrary values returned from the syscall for the first time | |||
ret += seed; | ret += seed; | |||
// __RDRND__ is set only if architecturual feature is set, e.g. compile with - march=native | // __RDRND__ is set only if architecturual feature is set, e.g. compiled wit h -march=native | |||
#ifdef __RDRND__ | #ifdef __RDRND__ | |||
for(i = 0; i < RND_RETRIES; i++) { | for(i = 0; i < RND_RETRIES; i++) { | |||
if(_rdrand64_step ((unsigned long long*)&seed)) { | if(_rdrand64_step((unsigned long long*)&seed)) { | |||
// success! | // success! | |||
// from now on, we keep this inside the loop because in case of failure | // from now on, we keep this inside the loop because in case of fail | |||
// and with unchanged values, we do not want to double the previous value | ure | |||
ret += seed; | // and with unchanged values, we do not want to double the previous | |||
break; | value | |||
} | ret += seed; | |||
// continue loop to try again otherwise | break; | |||
} | } | |||
if(i == RND_RETRIES){ | // continue loop to try again otherwise | |||
traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generated r | } | |||
andom number from RDRND."); | if(i == RND_RETRIES) { | |||
} | traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generat | |||
ed random number from RDRND."); | ||||
} | ||||
#endif | #endif | |||
// __RDSEED__ ist set only if architecturual feature is set, e.g. compile with -march=native | // __RDSEED__ ist set only if architecturual feature is set, e.g. compile wi th -march=native | |||
#ifdef __RDSEED__ | #ifdef __RDSEED__ | |||
for(i = 0; i < RND_RETRIES; i++) { | #if __GNUC__ > 4 | |||
if(_rdseed64_step((unsigned long long*)&seed)) { | for(i = 0; i < RND_RETRIES; i++) { | |||
// success! | if(_rdseed64_step((unsigned long long*)&seed)) { | |||
ret += seed; | // success! | |||
break; | ret += seed; | |||
} | break; | |||
// continue loop to try again otherwise | } | |||
} | // continue loop to try again otherwise | |||
if(i == RND_RETRIES){ | } | |||
traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generated r | if(i == RND_RETRIES) { | |||
andom number from RDSEED."); | traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generat | |||
} | ed random number from RDSEED."); | |||
} | ||||
#endif | ||||
#endif | #endif | |||
/* The WIN32 code is still untested and thus commented | #ifdef WIN32 | |||
#ifdef WIN32 | HCRYPTPROV crypto_provider; | |||
HCRYPTPROV crypto_provider; | CryptAcquireContext (&crypto_provider, NULL, NULL, | |||
CryptAcquireContext (&crypto_provider, NULL, (LPCWSTR)L"Microsoft Base Cryp | PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); | |||
tographic Provider v1.0", | CryptGenRandom (crypto_provider, 8, &seed); | |||
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); | CryptReleaseContext (crypto_provider, 0); | |||
CryptGenRandom (crypto_provider, 8, &seed); | ret += seed; | |||
CryptReleaseContext (crypto_provider, 0); | #endif | |||
ret += seed; | ||||
#endif */ | seed = time(NULL); /* UTC in seconds */ | |||
ret += seed; | ||||
seed = clock(); /* ticks since program start */ | ||||
seed *= 18444244737; | ||||
ret += seed; | ||||
return ret; | ||||
} | ||||
// an integer squrare root approximation | ||||
// from https://stackoverflow.com/a/1100591 | ||||
static int ftbl[33] = { | ||||
0, 1, 1, 2, 2, 4, 5, 8, 11, 16, 22, 32, 45, 64, 90, | ||||
128, 181 ,256 ,362, 512, 724, 1024, 1448, 2048, 2896, | ||||
4096, 5792, 8192, 11585, 16384, 23170, 32768, 46340 }; | ||||
static int ftbl2[32] = { | ||||
32768, 33276, 33776, 34269, 34755, 35235, 35708, 36174, | ||||
36635, 37090, 37540, 37984, 38423, 38858, 39287, 39712, | ||||
40132, 40548, 40960, 41367, 41771, 42170, 42566, 42959, | ||||
43347, 43733, 44115, 44493, 44869, 45241, 45611, 45977 }; | ||||
static int i_sqrt (int val) { | ||||
int cnt = 0; | ||||
int t = val; | ||||
while(t) { | ||||
cnt++; | ||||
t>>=1; | ||||
} | ||||
if(6 >= cnt) | ||||
t = (val << (6-cnt)); | ||||
else | ||||
t = (val >> (cnt-6)); | ||||
return (ftbl[cnt] * ftbl2[t & 31]) >> 15; | ||||
} | ||||
static int32_t int_sqrt (int val) { | ||||
int ret; | ||||
ret = i_sqrt (val); | ||||
ret += i_sqrt (val - ret * ret) / 16; | ||||
return ret; | ||||
} | ||||
seed = time(NULL); /* UTC in seconds */ | // returns a random number from [0, max_n] with higher probability towards the b | |||
ret += seed; | orders | |||
uint32_t n2n_rand_sqr (uint32_t max_n) { | ||||
seed = clock() * 18444244737; /* clock() = ticks since program start */ | uint32_t raw_max = 0; | |||
ret += seed; | uint32_t raw_rnd = 0; | |||
int32_t ret = 0; | ||||
raw_max = (max_n+2) * (max_n+2); | ||||
raw_rnd = n2n_rand() % (raw_max); | ||||
ret = int_sqrt(raw_rnd) / 2; | ||||
ret = (raw_rnd & 1) ? ret : -ret; | ||||
ret = max_n / 2 + ret; | ||||
if(ret < 0) | ||||
ret = 0; | ||||
if (ret > max_n) | ||||
ret = max_n; | ||||
return ret; | return ret; | |||
} | } | |||
End of changes. 30 change blocks. | ||||
122 lines changed or deleted | 188 lines changed or added |