@cryptotaxi247 / netdata-1 / commits / 7d800def8

Random numbers improvements (#18939)

* random numbers use RAND_bytes() by default and fallback to others * added rejection sampling to avoid bias; removed bcrypt

Costa Tsaousis committed Nov 4, 2024 at 23:37 UTC 7d800def8877c3639e30fea30b842ae48ddc3c68
5 files changed +129 -163
CMakeLists.txt
+1 -1
@@ -1808,7 +1808,7 @@ target_include_directories(libnetdata BEFORE PUBLIC ${CONFIG_H_DIR} ${CMAKE_SOUR
1808 target_link_libraries(libnetdata PUBLIC
1809 "$<$<NOT:$<BOOL:${HAVE_BUILTIN_ATOMICS}>>:atomic>"
1810 "$<$<OR:$<BOOL:${OS_LINUX}>,$<BOOL:${OS_FREEBSD}>>:pthread;rt>"
1811 - "$<$<BOOL:${OS_WINDOWS}>:kernel32;advapi32;winmm;rpcrt4;bcrypt;wevtapi;ole32;oleaut32;wbemuuid>"
1811 + "$<$<BOOL:${OS_WINDOWS}>:kernel32;advapi32;winmm;rpcrt4;wevtapi;ole32;oleaut32;wbemuuid>"
1812 "$<$<BOOL:${LINK_LIBM}>:m>"
1813 "${SYSTEMD_LDFLAGS}")
1814
src/aclk/mqtt_websockets/ws_client.c
+2 -19
@@ -5,16 +5,6 @@
5 #include "ws_client.h"
6 #include "common_internal.h"
7
8 -static uint32_t generate_random_32bit(void) {
9 - uint32_t random_number = 0;
10 -
11 - if (RAND_bytes((unsigned char *)&random_number, sizeof(random_number)) != 1) {
12 - nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to generate a random uint32 mask");
13 - }
14 -
15 - return random_number;
16 -}
17 -
8 const char *websocket_upgrage_hdr = "GET /mqtt HTTP/1.1\x0D\x0A"
9 "Host: %s\x0D\x0A"
10 "Upgrade: websocket\x0D\x0A"
@@ -129,10 +119,7 @@ int ws_client_start_handshake(ws_client *client)
119 }
120
121 // Generate a random 16-byte nonce
132 - if (!RAND_bytes(nonce, WEBSOCKET_NONCE_SIZE)) {
133 - nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to generate nonce");
134 - return 1;
135 - }
122 + os_random_bytes(nonce, sizeof(nonce));
123
124 // Initialize the digest context
125 #if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110)
@@ -414,11 +401,7 @@ int ws_client_send(const ws_client *client, enum websocket_opcode frame_type, co
401 *ptr++ |= size;
402
403 char *mask = ptr;
417 - uint32_t mask32 = generate_random_32bit();
418 - if (!mask32) {
419 - nd_log(NDLS_DAEMON, NDLP_ERR, "Unable to get mask to XOR websocket payload");
420 - return -2;
421 - }
404 + uint32_t mask32 = os_random32() + 1;
405 memcpy(mask, &mask32, sizeof(mask32));
406
407 rbuf_push(client->buf_write, hdr, hdr_len);
src/libnetdata/common.h
-1
@@ -400,7 +400,6 @@ typedef uint32_t uid_t;
400 #include <wchar.h>
401 #include <tchar.h>
402 #include <guiddef.h>
403 -#include <bcrypt.h> // For BCryptGenRandom
403 #include <io.h>
404 #include <fcntl.h>
405 #include <process.h>
src/libnetdata/os/random.c
+123 -142
@@ -4,7 +4,7 @@
4
5 #if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_RAND_S)
6 static SPINLOCK random_lock = NETDATA_SPINLOCK_INITIALIZER;
7 -static __attribute__((constructor)) void seed_random() {
7 +static __attribute__((constructor)) void random_seed() {
8 // Use current time and process ID to create a high-entropy seed
9 struct timeval tv;
10 gettimeofday(&tv, NULL);
@@ -15,9 +15,37 @@ static __attribute__((constructor)) void seed_random() {
15 srandom(seed);
16 }
17
18 +static void random_get_bytes(void *buf, size_t bytes) {
19 + spinlock_lock(&random_lock);
20 + while (bytes > 0) {
21 + if (bytes >= sizeof(uint32_t)) {
22 + // Generate 4 bytes at a time
23 + uint32_t temp = random();
24 + memcpy(buf, &temp, sizeof(uint32_t));
25 + buf = (uint8_t *)buf + sizeof(uint32_t);
26 + bytes -= sizeof(uint32_t);
27 + } else if (bytes >= sizeof(uint16_t)) {
28 + // Generate 2 bytes at a time
29 + uint16_t temp = random();
30 + memcpy(buf, &temp, sizeof(uint16_t));
31 + buf = (uint8_t *)buf + sizeof(uint16_t);
32 + bytes -= sizeof(uint16_t);
33 + } else {
34 + // Generate remaining bytes
35 + uint32_t temp = random();
36 + for (size_t i = 0; i < bytes; i++) {
37 + ((uint8_t *)buf)[i] = temp & 0xFF;
38 + temp >>= 8;
39 + }
40 + bytes = 0;
41 + }
42 + }
43 + spinlock_unlock(&random_lock);
44 +}
45 +
46 #if defined(HAVE_GETRANDOM)
47 #include <sys/random.h>
20 -void getrandom_helper(void *buf, size_t buflen) {
48 +void inline getrandom_get_bytes(void *buf, size_t buflen) {
49 ssize_t result;
50 while (buflen > 0) {
51 result = getrandom(buf, buflen, 0);
@@ -30,32 +58,8 @@ void getrandom_helper(void *buf, size_t buflen) {
58 tinysleep();
59 continue;
60 } else {
33 - // Fallback to using random() with a spinlock
34 - spinlock_lock(&random_lock);
35 - while (buflen > 0) {
36 - if (buflen >= sizeof(uint32_t)) {
37 - // Generate 4 bytes at a time
38 - uint32_t temp = random();
39 - memcpy(buf, &temp, sizeof(uint32_t));
40 - buf = (uint8_t *)buf + sizeof(uint32_t);
41 - buflen -= sizeof(uint32_t);
42 - } else if (buflen >= sizeof(uint16_t)) {
43 - // Generate 2 bytes at a time
44 - uint16_t temp = random();
45 - memcpy(buf, &temp, sizeof(uint16_t));
46 - buf = (uint8_t *)buf + sizeof(uint16_t);
47 - buflen -= sizeof(uint16_t);
48 - } else {
49 - // Generate remaining bytes
50 - uint32_t temp = random();
51 - for (size_t i = 0; i < buflen; i++) {
52 - ((uint8_t *)buf)[i] = temp & 0xFF;
53 - temp >>= 8;
54 - }
55 - buflen = 0;
56 - }
57 - }
58 - spinlock_unlock(&random_lock);
61 + // fallback to RAND_bytes
62 + random_get_bytes(buf, buflen);
63 return;
64 }
65 }
@@ -66,149 +70,126 @@ void getrandom_helper(void *buf, size_t buflen) {
70 #endif // HAVE_GETRANDOM
71 #endif // !HAVE_ARC4RANDOM_BUF && !HAVE_RAND_S
72
69 -// return a random number 0 to max - 1
70 -uint64_t os_random(uint64_t max) {
71 - if (max <= 1) return 0;
73 +#if defined(HAVE_RAND_S)
74 +static inline void rand_s_get_bytes(void *buf, size_t bytes) {
75 + while (bytes > 0) {
76 + if (bytes >= sizeof(unsigned int)) {
77 + unsigned int temp;
78 + rand_s(&temp);
79 + memcpy(buf, &temp, sizeof(unsigned int));
80 + buf = (uint8_t *)buf + sizeof(unsigned int);
81 + bytes -= sizeof(unsigned int);
82 + } else if (bytes >= sizeof(uint16_t)) {
83 + // Generate 2 bytes at a time
84 + unsigned int t;
85 + rand_s(&t);
86 + uint16_t temp = t;
87 + memcpy(buf, &temp, sizeof(uint16_t));
88 + buf = (uint8_t *)buf + sizeof(uint16_t);
89 + bytes -= sizeof(uint16_t);
90 + } else {
91 + // Generate remaining bytes
92 + unsigned int temp;
93 + rand_s(&temp);
94 + for (size_t i = 0; i < sizeof(temp) && i < bytes; i++) {
95 + ((uint8_t *)buf)[0] = temp & 0xFF;
96 + temp >>= 8;
97 + buf = (uint8_t *)buf + 1;
98 + bytes--;
99 + }
100 + }
101 + }
102 +}
103 +#endif
104
73 - uint64_t value;
105 +inline void os_random_bytes(void *buf, size_t bytes) {
106 + if(RAND_bytes((unsigned char *)buf, bytes) == 1)
107 + return;
108
109 #if defined(HAVE_ARC4RANDOM_BUF)
76 - if (max <= UINT8_MAX) {
77 - uint8_t v;
78 - arc4random_buf(&v, sizeof(v));
79 - value = v;
80 - } else if(max <= UINT16_MAX) {
81 - uint16_t v;
82 - arc4random_buf(&v, sizeof(v));
83 - value = v;
84 - } else if (max <= UINT32_MAX) {
85 - uint32_t v;
86 - arc4random_buf(&v, sizeof(v));
87 - value = v;
88 - } else
89 - arc4random_buf(&value, sizeof(value));
90 -
91 -#elif defined(HAVE_RAND_S)
92 - if (max <= UINT_MAX) {
93 - unsigned int temp;
94 - rand_s(&temp);
95 - value = temp;
96 - } else {
97 - unsigned int temp_lo, temp_hi;
98 - rand_s(&temp_lo);
99 - rand_s(&temp_hi);
100 - value = ((uint64_t)temp_hi << 32) + (uint64_t)temp_lo;
101 - }
102 -
110 + arc4random_buf(buf, bytes);
111 #elif defined(HAVE_GETRANDOM)
104 - if (max <= UINT8_MAX) {
105 - uint8_t v;
106 - getrandom_helper(&v, sizeof(v));
107 - value = v;
108 - } else if(max <= UINT16_MAX) {
109 - uint16_t v;
110 - getrandom_helper(&v, sizeof(v));
111 - value = v;
112 - } else if (max <= UINT32_MAX) {
113 - uint32_t v;
114 - getrandom_helper(&v, sizeof(v));
115 - value = v;
116 - } else
117 - getrandom_helper(&value, sizeof(value));
118 -
112 + getrandom_get_bytes(buf, bytes);
113 +#elif defined(HAVE_RAND_S)
114 + rand_s_get_bytes(buf, bytes);
115 #else
120 - spinlock_lock(&random_lock);
121 - if(max <= INT32_MAX)
122 - value = random();
123 - else
124 - value = ((uint64_t) random() << 33) | ((uint64_t) random() << 2) | (random() & 0x3);
125 - spinlock_unlock(&random_lock);
116 + random_get_bytes(buf, bytes);
117 #endif
127 -
128 - return value % max;
118 }
119
120 // Generate an 8-bit random number
121 uint8_t os_random8(void) {
122 uint8_t value;
134 -
135 -#if defined(HAVE_ARC4RANDOM_BUF)
136 - arc4random_buf(&value, sizeof(value));
137 -#elif defined(HAVE_GETRANDOM)
138 - getrandom_helper(&value, sizeof(value));
139 -#elif defined(HAVE_RAND_S)
140 - unsigned int temp;
141 - rand_s(&temp);
142 - value = (uint8_t)temp;
143 -#else
144 - spinlock_lock(&random_lock);
145 - value = (uint8_t)random();
146 - spinlock_unlock(&random_lock);
147 -#endif
148 -
123 + os_random_bytes(&value, sizeof(value));
124 return value;
125 }
126
127 // Generate a 16-bit random number
128 uint16_t os_random16(void) {
129 uint16_t value;
155 -
156 -#if defined(HAVE_ARC4RANDOM_BUF)
157 - arc4random_buf(&value, sizeof(value));
158 -#elif defined(HAVE_GETRANDOM)
159 - getrandom_helper(&value, sizeof(value));
160 -#elif defined(HAVE_RAND_S)
161 - unsigned int temp;
162 - rand_s(&temp);
163 - value = (uint16_t)temp;
164 -#else
165 - spinlock_lock(&random_lock);
166 - value = (uint16_t)random();
167 - spinlock_unlock(&random_lock);
168 -#endif
169 -
130 + os_random_bytes(&value, sizeof(value));
131 return value;
132 }
133
134 // Generate a 32-bit random number
135 uint32_t os_random32(void) {
136 uint32_t value;
176 -
177 -#if defined(HAVE_ARC4RANDOM_BUF)
178 - arc4random_buf(&value, sizeof(value));
179 -#elif defined(HAVE_GETRANDOM)
180 - getrandom_helper(&value, sizeof(value));
181 -#elif defined(HAVE_RAND_S)
182 - unsigned int temp;
183 - rand_s(&temp);
184 - value = temp;
185 -#else
186 - spinlock_lock(&random_lock);
187 - value = random();
188 - spinlock_unlock(&random_lock);
189 -#endif
190 -
137 + os_random_bytes(&value, sizeof(value));
138 return value;
139 }
140
141 // Generate a 64-bit random number
142 uint64_t os_random64(void) {
143 uint64_t value;
144 + os_random_bytes(&value, sizeof(value));
145 + return value;
146 +}
147
198 -#if defined(HAVE_ARC4RANDOM_BUF)
199 - arc4random_buf(&value, sizeof(value));
200 -#elif defined(HAVE_GETRANDOM)
201 - getrandom_helper(&value, sizeof(value));
202 -#elif defined(HAVE_RAND_S)
203 - unsigned int temp_lo, temp_hi;
204 - rand_s(&temp_lo);
205 - rand_s(&temp_hi);
206 - value = ((uint64_t)temp_hi << 32) | (uint64_t)temp_lo;
207 -#else
208 - spinlock_lock(&random_lock);
209 - value = ((uint64_t)random() << 33) | ((uint64_t)random() << 2) | (random() & 0x3);
210 - spinlock_unlock(&random_lock);
211 -#endif
148 +#define MAX_RETRIES 10 // Limit retries to avoid an infinite loop
149 +uint64_t os_random(uint64_t max) {
150 + if (max <= 1) return 0;
151
213 - return value;
152 + uint64_t value;
153 + uint64_t upper_limit;
154 + int retries = 0;
155 +
156 + /*
157 + * Rejection Sampling
158 + * To reduce bias, we can use rejection sampling without creating an infinite loop.
159 + * This technique works by discarding values that would introduce bias, but limiting
160 + * the number of retries to avoid infinite loops.
161 + */
162 +
163 + // Calculate an upper limit so that the range evenly divides into max.
164 + // Any values greater than this limit would introduce bias, so we discard them.
165 +
166 + if (max <= UINT8_MAX)
167 + upper_limit = UINT8_MAX - (UINT8_MAX % max);
168 + else if (max <= UINT16_MAX)
169 + upper_limit = UINT16_MAX - (UINT16_MAX % max);
170 + else if (max <= UINT32_MAX)
171 + upper_limit = UINT32_MAX - (UINT32_MAX % max);
172 + else
173 + upper_limit = UINT64_MAX - (UINT64_MAX % max);
174 +
175 + do {
176 + // Generate a random number with the appropriate number of bits
177 + if (max <= UINT8_MAX)
178 + value = os_random8();
179 + else if (max <= UINT16_MAX)
180 + value = os_random16();
181 + else if (max <= UINT32_MAX)
182 + value = os_random32();
183 + else
184 + value = os_random64();
185 +
186 + // Retry if the generated value is biased (i.e., exceeds upper_limit)
187 + if (value < upper_limit)
188 + return value % max; // Value is unbiased, return directly
189 +
190 + retries++;
191 + } while (retries < MAX_RETRIES);
192 +
193 + // Fallback to modulo after MAX_RETRIES, accepting minor bias
194 + return value % max;
195 }
src/libnetdata/os/random.h
+3
@@ -5,6 +5,9 @@
5
6 #include "libnetdata/common.h"
7
8 +// fill a buffer with random bytes
9 +void os_random_bytes(void *buf, size_t bytes);
10 +
11 // return a random number 0 to max - 1
12 uint64_t os_random(uint64_t max);
13