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