Skip to content

Commit cc83d51

Browse files
committed
random-util: optionally allow randomness to be generated via RDRAND
We only use this when we don't require the best randomness. The primary usecase for this is UUID generation, as this means we don't drain randomness from the kernel pool for them. Since UUIDs are usually not secrets RDRAND should be goot enough for them to avoid real-life collisions.
1 parent 6fb6f13 commit cc83d51

File tree

4 files changed

+53
-28
lines changed

4 files changed

+53
-28
lines changed

src/basic/random-util.c

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,52 @@ int rdrand64(uint64_t *ret) {
6767

6868
int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
6969
static int have_syscall = -1;
70-
7170
_cleanup_close_ int fd = -1;
71+
bool got_some = false;
7272
int r;
7373

74-
/* Gathers some randomness from the kernel. This call won't block, unless the RANDOM_BLOCK flag is set. If
75-
* RANDOM_DONT_DRAIN is set, an error is returned if the random pool is not initialized. Otherwise it will
76-
* always return some data from the kernel, regardless of whether the random pool is fully initialized or
77-
* not. */
74+
/* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't
75+
* block, unless the RANDOM_BLOCK flag is set. If RANDOM_DONT_DRAIN is set, an error is returned if the random
76+
* pool is not initialized. Otherwise it will always return some data from the kernel, regardless of whether
77+
* the random pool is fully initialized or not. */
7878

7979
if (n == 0)
8080
return 0;
8181

82+
if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND))
83+
/* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is not
84+
* required, as we don't trust it (who does?). Note that we only do a single iteration of RDRAND here,
85+
* even though the Intel docs suggest calling this in a tight loop of 10 invocations or so. That's
86+
* because we don't really care about the quality here. We generally prefer using RDRAND if the caller
87+
* allows us too, since this way we won't drain the kernel randomness pool if we don't need it, as the
88+
* pool's entropy is scarce. */
89+
for (;;) {
90+
uint64_t u;
91+
size_t m;
92+
93+
if (rdrand64(&u) < 0) {
94+
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
95+
/* Fill in the remaining bytes using pseudo-random values */
96+
pseudo_random_bytes(p, n);
97+
return 0;
98+
}
99+
100+
/* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */
101+
break;
102+
}
103+
104+
m = MIN(sizeof(u), n);
105+
memcpy(p, &u, m);
106+
107+
p = (uint8_t*) p + m;
108+
n -= m;
109+
110+
if (n == 0)
111+
return 0; /* Yay, success! */
112+
113+
got_some = true;
114+
}
115+
82116
/* Use the getrandom() syscall unless we know we don't have it. */
83117
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
84118

@@ -100,6 +134,8 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
100134
return 0;
101135
}
102136

137+
got_some = true;
138+
103139
/* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */
104140
if (FLAGS_SET(flags, RANDOM_BLOCK))
105141
continue;
@@ -125,30 +161,15 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
125161
* but the kernel will produce some bytes for us on a best-effort basis. */
126162
have_syscall = true;
127163

128-
if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
129-
return -ENODATA;
130-
131-
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
132-
uint64_t u;
133-
size_t k;
134-
135-
/* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality
136-
* randomness is not required, as we don't trust it (who does?). Note that we only do a
137-
* single iteration of RDRAND here, even though the Intel docs suggest calling this in
138-
* a tight loop of 10 invocatins or so. That's because we don't really care about the
139-
* quality here. */
140-
141-
if (rdrand64(&u) < 0)
142-
return -ENODATA;
143-
144-
k = MIN(n, sizeof(u));
145-
memcpy(p, &u, k);
146-
147-
/* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */
148-
pseudo_random_bytes((uint8_t*) p + k, n - k);
164+
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
165+
/* Fill in the remaining bytes using pseudorandom values */
166+
pseudo_random_bytes(p, n);
149167
return 0;
150168
}
151169

170+
if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
171+
return -ENODATA;
172+
152173
/* Use /dev/urandom instead */
153174
break;
154175
} else
@@ -229,7 +250,7 @@ void pseudo_random_bytes(void *p, size_t n) {
229250

230251
void random_bytes(void *p, size_t n) {
231252

232-
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN) >= 0)
253+
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN|RANDOM_ALLOW_RDRAND) >= 0)
233254
return;
234255

235256
/* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */

src/basic/random-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ typedef enum RandomFlags {
99
RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */
1010
RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
1111
RANDOM_DONT_DRAIN = 1 << 2, /* If we can't get any randomness at all, return early with -EAGAIN */
12+
RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */
1213
} RandomFlags;
1314

1415
int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */

src/libsystemd/sd-id128/sd-id128.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,9 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
272272

273273
assert_return(ret, -EINVAL);
274274

275-
r = genuine_random_bytes(&t, sizeof t, 0);
275+
/* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
276+
* fine for UUIDS. */
277+
r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
276278
if (r < 0)
277279
return r;
278280

src/test/test-random-util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ int main(int argc, char **argv) {
5757
test_genuine_random_bytes(RANDOM_EXTEND_WITH_PSEUDO);
5858
test_genuine_random_bytes(0);
5959
test_genuine_random_bytes(RANDOM_BLOCK);
60+
test_genuine_random_bytes(RANDOM_ALLOW_RDRAND);
6061

6162
test_pseudo_random_bytes();
6263

0 commit comments

Comments
 (0)