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