1 /**
2 * (C) 2007-20 - ntop.org and contributors
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not see see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #ifdef SYS_getrandom
20 #include <errno.h>
21 #endif
22
23 #include "n2n.h"
24
25
26 /* The following code offers an alterate pseudo random number generator
27 namely XORSHIFT128+ to use instead of C's rand(). Its performance is
28 on par with C's rand().
29 */
30
31
32 /* The state must be seeded in a way that it is not all zero, choose some
33 arbitrary defaults (in this case: taken from splitmix64) */
34 static struct rn_generator_state_t rn_current_state = {
35 .a = 0x9E3779B97F4A7C15,
36 .b = 0xBF58476D1CE4E5B9 };
37
38
39 /* used for mixing the initializing seed */
splitmix64(struct splitmix64_state_t * state)40 static uint64_t splitmix64 (struct splitmix64_state_t *state) {
41
42 uint64_t result = state->s;
43
44 state->s = result + 0x9E3779B97F4A7C15;
45
46 result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9;
47 result = (result ^ (result >> 27)) * 0x94D049BB133111EB;
48
49 return result ^ (result >> 31);
50 }
51
52
n2n_srand(uint64_t seed)53 int n2n_srand (uint64_t seed) {
54 uint8_t i;
55 struct splitmix64_state_t smstate = {seed};
56
57 rn_current_state.a = 0;
58 rn_current_state.b = 0;
59
60 rn_current_state.a = splitmix64 (&smstate);
61 rn_current_state.b = splitmix64 (&smstate);
62
63 /* the following lines could be deleted as soon as it is formally prooved that
64 there is no seed leading to (a == b == 0). Until then, just to be safe: */
65 if ( (rn_current_state.a == 0) && (rn_current_state.b == 0) ) {
66 rn_current_state.a = 0x9E3779B97F4A7C15;
67 rn_current_state.b = 0xBF58476D1CE4E5B9;
68 }
69
70 /* stabilize in unlikely case of weak state with only a few bits set */
71 for(i = 0; i < 32; i++)
72 n2n_rand();
73
74 return 0;
75 }
76
77
78 /* The following code of xorshift128p was taken from
79 https://en.wikipedia.org/wiki/Xorshift as of July, 2019
80 and thus is considered public domain. */
n2n_rand()81 uint64_t n2n_rand () {
82
83 uint64_t t = rn_current_state.a;
84 uint64_t const s = rn_current_state.b;
85
86 rn_current_state.a = s;
87 t ^= t << 23;
88 t ^= t >> 17;
89 t ^= s ^ (s >> 26);
90 rn_current_state.b = t;
91
92 return t + s;
93 }
94
95
96 /* The following code tries to gather some entropy from several sources
97 for use as seed. Note, that this code does not set the random generator
98 state yet, a call to n2n_srand ( n2n_seed() ) would do. */
n2n_seed(void)99 uint64_t n2n_seed (void) {
100
101 uint64_t seed = 0;
102 uint64_t ret = 0;
103 size_t i;
104
105 #ifdef SYS_getrandom
106 int rc = -1;
107 for(i = 0; (i < RND_RETRIES) && (rc != sizeof(seed)); i++) {
108 rc = syscall (SYS_getrandom, &seed, sizeof(seed), GRND_NONBLOCK);
109 // if successful, rc should contain the requested number of random bytes
110 if(rc != sizeof(seed)) {
111 if (errno != EAGAIN) {
112 traceEvent(TRACE_ERROR, "n2n_seed faced error errno=%u from getrandom syscall.", errno);
113 break;
114 }
115 }
116 }
117 // if we still see an EAGAIN error here, we must have run out of retries
118 if(errno == EAGAIN) {
119 traceEvent(TRACE_ERROR, "n2n_seed saw getrandom syscall indicate not being able to provide enough entropy yet.");
120 }
121 #endif
122
123 // as we want randomness, it does no harm to add up even uninitialized values or
124 // erroneously arbitrary values returned from the syscall for the first time
125 ret += seed;
126
127 // __RDRND__ is set only if architecturual feature is set, e.g. compile with -march=native
128 #ifdef __RDRND__
129 for(i = 0; i < RND_RETRIES; i++) {
130 if(_rdrand64_step ((unsigned long long*)&seed)) {
131 // success!
132 // from now on, we keep this inside the loop because in case of failure
133 // and with unchanged values, we do not want to double the previous value
134 ret += seed;
135 break;
136 }
137 // continue loop to try again otherwise
138 }
139 if(i == RND_RETRIES){
140 traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generated random number from RDRND.");
141 }
142 #endif
143
144 // __RDSEED__ ist set only if architecturual feature is set, e.g. compile with -march=native
145 #ifdef __RDSEED__
146 for(i = 0; i < RND_RETRIES; i++) {
147 if(_rdseed64_step((unsigned long long*)&seed)) {
148 // success!
149 ret += seed;
150 break;
151 }
152 // continue loop to try again otherwise
153 }
154 if(i == RND_RETRIES){
155 traceEvent(TRACE_ERROR, "n2n_seed was not able to get a hardware generated random number from RDSEED.");
156 }
157 #endif
158
159 /* The WIN32 code is still untested and thus commented
160 #ifdef WIN32
161 HCRYPTPROV crypto_provider;
162 CryptAcquireContext (&crypto_provider, NULL, (LPCWSTR)L"Microsoft Base Cryptographic Provider v1.0",
163 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
164 CryptGenRandom (crypto_provider, 8, &seed);
165 CryptReleaseContext (crypto_provider, 0);
166 ret += seed;
167 #endif */
168
169 seed = time(NULL); /* UTC in seconds */
170 ret += seed;
171
172 seed = clock() * 18444244737; /* clock() = ticks since program start */
173 ret += seed;
174
175 return ret;
176 }
177