Skip to content

Commit bc44ede

Browse files
authored
Merge pull request #102 from sysprog21/rewrite-randombyte
Rewrite randombyte
2 parents 3ebf302 + 078ecc9 commit bc44ede

File tree

5 files changed

+286
-23
lines changed

5 files changed

+286
-23
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,4 @@ External source code:
193193
* [git-good-commit](https://github.com/tommarshall/git-good-commit): MIT License
194194
* [linenoise](https://github.com/antirez/linenoise): BSD 2-Clause "Simplified" License
195195
* [tiny-web-server](https://github.com/7890/tiny-web-server): MIT License
196+
* [randombytes](https://github.com/dsprenkels/randombytes): MIT License

random.c

Lines changed: 281 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,299 @@
1+
/*
2+
* Copyright (C) 2023 National Cheng Kung University, Taiwan.
3+
* Copyright (c) 2017 Daan Sprenkels <[email protected]>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
/* In the case that are compiling on linux, we need to define _GNU_SOURCE
25+
* *before* random.h is included. Otherwise SYS_getrandom will not be declared.
26+
*/
27+
#if defined(__linux__) || defined(__GNU__)
28+
#define _GNU_SOURCE
29+
#endif
30+
31+
#include "random.h"
32+
33+
#if defined(__linux__) || defined(__GNU__)
34+
/* We would need to include <linux/random.h>, but not every target has access
35+
* to the linux headers. We only need RNDGETENTCNT, so we instead inline it.
36+
* RNDGETENTCNT is originally defined in `include/uapi/linux/random.h` in the
37+
* Linux kernel source.
38+
*/
39+
#define RNDGETENTCNT 0x80045200
40+
141
#include <assert.h>
42+
#include <errno.h>
243
#include <fcntl.h>
44+
#include <poll.h>
345
#include <stdint.h>
46+
#include <stdio.h>
47+
#include <sys/ioctl.h>
48+
#if (defined(__linux__) || defined(__GNU__)) && defined(__GLIBC__) && \
49+
((__GLIBC__ > 2) || (__GLIBC_MINOR__ > 24))
50+
#define USE_GLIBC
51+
#include <sys/random.h>
52+
#endif
53+
#include <sys/stat.h>
54+
#include <sys/syscall.h>
55+
#include <sys/types.h>
456
#include <unistd.h>
557

6-
#include "random.h"
58+
/* We need SSIZE_MAX as the maximum read len from /dev/urandom */
59+
#if !defined(SSIZE_MAX)
60+
#include <bits/wordsize.h>
61+
#if __WORDSIZE == 64
62+
#define SSIZE_MAX LONG_MAX
63+
#else
64+
#define SSIZE_MAX INT_MAX
65+
#endif
66+
#endif
67+
68+
#endif /* defined(__linux__) || defined(__GNU__) */
69+
70+
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
71+
/* Dragonfly, FreeBSD, NetBSD, OpenBSD (has arc4random) */
72+
#include <sys/param.h>
73+
#if defined(BSD)
74+
#include <stdlib.h>
75+
#endif
76+
/* GNU/Hurd defines BSD in sys/param.h which causes problems later */
77+
#if defined(__GNU__)
78+
#undef BSD
79+
#endif
80+
#endif
81+
82+
#if (defined(__linux__) || defined(__GNU__)) && \
83+
(defined(USE_GLIBC) || defined(SYS_getrandom))
84+
#if defined(USE_GLIBC)
85+
/* getrandom is declared in glibc. */
86+
#elif defined(SYS_getrandom)
87+
static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags)
88+
{
89+
return syscall(SYS_getrandom, buf, buflen, flags);
90+
}
91+
#endif
92+
93+
static int randombytes_linux_randombytes_getrandom(void *buf, size_t n)
94+
{
95+
size_t offset = 0;
96+
int ret;
97+
while (n > 0) {
98+
/* getrandom does not allow chunks larger than 33554431 */
99+
size_t chunk = n <= 33554431 ? n : 33554431;
100+
do {
101+
ret = getrandom((char *) buf + offset, chunk, 0);
102+
} while (ret == -1 && errno == EINTR);
103+
if (ret < 0)
104+
return ret;
105+
offset += ret;
106+
n -= ret;
107+
}
108+
assert(n == 0);
109+
return 0;
110+
}
111+
#endif /* (defined(__linux__) || defined(__GNU__)) && (defined(USE_GLIBC) || \
112+
defined(SYS_getrandom)) */
7113

8-
/* shameless stolen from ebacs */
9-
void randombytes(uint8_t *x, size_t how_much)
114+
#if defined(__linux__) && !defined(SYS_getrandom)
115+
116+
#if defined(__linux__)
117+
static inline int randombytes_linux_read_entropy_ioctl(int device, int *entropy)
10118
{
11-
ssize_t i;
12-
static int fd = -1;
119+
return ioctl(device, RNDGETENTCNT, entropy);
120+
}
121+
122+
static int randombytes_linux_read_entropy_proc(FILE *stream, int *entropy)
123+
{
124+
int retcode;
125+
do {
126+
rewind(stream);
127+
retcode = fscanf(stream, "%d", entropy);
128+
} while (retcode != 1 && errno == EINTR);
129+
if (retcode != 1)
130+
return -1;
131+
return 0;
132+
}
13133

14-
ssize_t xlen = (ssize_t) how_much;
15-
assert(xlen >= 0);
134+
static int randombytes_linux_wait_for_entropy(int device)
135+
{
136+
/* We will block on /dev/random, because any increase in the OS' entropy
137+
* level will unblock the request. I use poll here (as does libsodium),
138+
* because we don't *actually* want to read from the device. */
139+
enum { IOCTL, PROC } strategy = IOCTL;
140+
const int bits = 128;
141+
struct pollfd pfd;
142+
int fd;
143+
FILE *proc_file;
144+
int retcode, retcode_error = 0; /* Used as return codes */
145+
int entropy = 0;
146+
147+
/* If the device has enough entropy already, we will want to return early */
148+
retcode = randombytes_linux_read_entropy_ioctl(device, &entropy);
149+
if (retcode != 0 && (errno == ENOTTY || errno == ENOSYS)) {
150+
/* The ioctl call on /dev/urandom has failed due to a
151+
* - ENOTTY (unsupported action), or
152+
* - ENOSYS (invalid ioctl; this happens on MIPS, see #22).
153+
*
154+
* We will fall back to reading from
155+
* `/proc/sys/kernel/random/entropy_avail`. This less ideal,
156+
* because it allocates a file descriptor, and it may not work
157+
* in a chroot. But at this point it seems we have no better
158+
* options left.
159+
*/
160+
strategy = PROC;
161+
/* Open the entropy count file */
162+
proc_file = fopen("/proc/sys/kernel/random/entropy_avail", "r");
163+
if (!proc_file)
164+
return -1;
165+
} else if (retcode != 0) {
166+
/* Unrecoverable ioctl error */
167+
return -1;
168+
}
169+
if (entropy >= bits)
170+
return 0;
171+
172+
do {
173+
fd = open("/dev/random", O_RDONLY);
174+
} while (fd == -1 && errno == EINTR); /* EAGAIN will not occur */
16175
if (fd == -1) {
17-
for (;;) {
18-
fd = open("/dev/urandom", O_RDONLY);
19-
if (fd != -1)
176+
/* Unrecoverable IO error */
177+
return -1;
178+
}
179+
180+
pfd.fd = fd;
181+
pfd.events = POLLIN;
182+
for (;;) {
183+
retcode = poll(&pfd, 1, -1);
184+
if (retcode == -1 && (errno == EINTR || errno == EAGAIN)) {
185+
continue;
186+
} else if (retcode == 1) {
187+
if (strategy == IOCTL) {
188+
retcode =
189+
randombytes_linux_read_entropy_ioctl(device, &entropy);
190+
} else if (strategy == PROC) {
191+
retcode =
192+
randombytes_linux_read_entropy_proc(proc_file, &entropy);
193+
} else {
194+
return -1; /* Unreachable */
195+
}
196+
197+
if (retcode != 0) {
198+
/* Unrecoverable I/O error */
199+
retcode_error = retcode;
20200
break;
21-
sleep(1);
201+
}
202+
if (entropy >= bits)
203+
break;
204+
} else {
205+
/* Unreachable: poll() should only return -1 or 1 */
206+
retcode_error = -1;
207+
break;
22208
}
23209
}
210+
do {
211+
retcode = close(fd);
212+
} while (retcode == -1 && errno == EINTR);
213+
if (strategy == PROC) {
214+
do {
215+
retcode = fclose(proc_file);
216+
} while (retcode == -1 && errno == EINTR);
217+
}
218+
if (retcode_error != 0)
219+
return retcode_error;
220+
return retcode;
221+
}
222+
#endif /* defined(__linux__) */
24223

25-
while (xlen > 0) {
26-
if (xlen < 1048576)
27-
i = xlen;
28-
else
29-
i = 1048576;
224+
static int randombytes_linux_randombytes_urandom(void *buf, size_t n)
225+
{
226+
int fd;
227+
size_t offset = 0, count;
228+
ssize_t tmp;
229+
do {
230+
fd = open("/dev/urandom", O_RDONLY);
231+
} while (fd == -1 && errno == EINTR);
232+
if (fd == -1)
233+
return -1;
234+
#if defined(__linux__)
235+
if (randombytes_linux_wait_for_entropy(fd) == -1)
236+
return -1;
237+
#endif
30238

31-
i = read(fd, x, (size_t) i);
32-
if (i < 1) {
33-
sleep(1);
239+
while (n > 0) {
240+
count = n <= SSIZE_MAX ? n : SSIZE_MAX;
241+
tmp = read(fd, (char *) buf + offset, count);
242+
if (tmp == -1 && (errno == EAGAIN || errno == EINTR)) {
34243
continue;
35244
}
36-
37-
x += i;
38-
xlen -= i;
245+
if (tmp == -1)
246+
return -1; /* Unrecoverable IO error */
247+
offset += tmp;
248+
n -= tmp;
39249
}
250+
close(fd);
251+
assert(n == 0);
252+
return 0;
253+
}
254+
#endif /* defined(__linux__) && !defined(SYS_getrandom) */
255+
256+
#if defined(BSD)
257+
#if defined(__APPLE__)
258+
#include <AvailabilityMacros.h>
259+
#if defined(MAC_OS_X_VERSION_10_10) && \
260+
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
261+
#include <CommonCrypto/CommonCryptoError.h>
262+
#include <CommonCrypto/CommonRandom.h>
263+
#endif
264+
#endif
265+
static int randombytes_bsd_randombytes(void *buf, size_t n)
266+
{
267+
#if defined(MAC_OS_X_VERSION_10_15) && \
268+
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
269+
/* We prefere CCRandomGenerateBytes as it returns an error code while
270+
* arc4random_buf may fail silently on macOS. See PR #390, and
271+
* <https://opensource.apple.com/source/Libc/Libc-1439.40.11/gen/FreeBSD/arc4random.c.auto.html>
272+
*/
273+
return (CCRandomGenerateBytes(buf, n) == kCCSuccess);
274+
#else
275+
arc4random_buf(buf, n);
276+
return 0;
277+
#endif
278+
}
279+
#endif
280+
281+
int randombytes(uint8_t *buf, size_t n)
282+
{
283+
#if defined(__linux__) || defined(__GNU__)
284+
#if defined(USE_GLIBC)
285+
/* Use getrandom system call */
286+
return randombytes_linux_randombytes_getrandom(buf, n);
287+
#elif defined(SYS_getrandom)
288+
/* Use getrandom system call */
289+
return randombytes_linux_randombytes_getrandom(buf, n);
290+
#else
291+
/* When we have enough entropy, we can read from /dev/urandom */
292+
return randombytes_linux_randombytes_urandom(buf, n);
293+
#endif
294+
#elif defined(BSD)
295+
return randombytes_bsd_randombytes(buf, n);
296+
#else
297+
#error "randombytes(...) is not supported on this platform"
298+
#endif
40299
}

random.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <stddef.h>
55
#include <stdint.h>
66

7-
void randombytes(uint8_t *x, size_t xlen);
7+
extern int randombytes(uint8_t *buf, size_t len);
88

99
static inline uint8_t randombit(void)
1010
{

scripts/aspell-pws

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,5 @@ rbtree
151151
vfprintf
152152
vla
153153
ctrl
154+
randombyte
155+
randombit

scripts/pre-commit.hook

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
CPPCHECK_suppresses="--inline-suppr harness.c --suppress=unmatchedSuppression:harness.c --suppress=missingIncludeSystem \
44
--suppress=unusedFunction:linenoise.c \
5+
--suppress=ConfigurationNotChecked:random.c \
56
--suppress=nullPointerRedundantCheck:report.c \
67
--suppress=nullPointerRedundantCheck:harness.c \
78
--suppress=nullPointer:qtest.c \

0 commit comments

Comments
 (0)