Skip to content

Commit 0462247

Browse files
committed
Use getrandom on Linux when available.
1 parent 5e8ca02 commit 0462247

File tree

6 files changed

+174
-66
lines changed

6 files changed

+174
-66
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ features = ["bigint"]
3030

3131
[features]
3232
# These features are documented in the top-level module's documentation.
33+
disable_dev_urandom_fallback = []
3334
no_heap = []
3435
test_logging = []
3536

crypto/rand/internal.h

-32
This file was deleted.

crypto/rand/sysrand.c

+68-13
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,35 @@
1212
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1313
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
1414

15+
/* Copyright 2016 Brian Smith.
16+
*
17+
* Permission to use, copy, modify, and/or distribute this software for any
18+
* purpose with or without fee is hereby granted, provided that the above
19+
* copyright notice and this permission notice appear in all copies.
20+
*
21+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
22+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
23+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24+
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
26+
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
27+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
28+
29+
#if defined(__linux__)
30+
#define _GNU_SOURCE
31+
#endif
32+
1533
#include <openssl/rand.h>
1634

35+
#include <assert.h>
36+
#include <stddef.h>
37+
38+
/* CRYPTO_sysrand_chunk fills |len| bytes at |buf| with entropy from the
39+
* operating system. |len| must be no more than |CRYPTO_sysrand_chunk_max_le|.
40+
* It returns one on success, -1 if it failed because the operating system
41+
* doesn't offer such an API, or zero otherwise. */
42+
int CRYPTO_sysrand_chunk(void *buf, size_t len);
43+
1744
#if defined(OPENSSL_WINDOWS)
1845

1946
#include <limits.h>
@@ -32,23 +59,51 @@
3259

3360
#pragma warning(pop)
3461

35-
#include "internal.h"
62+
const size_t CRYPTO_sysrand_chunk_max_len = ULONG_MAX;
3663

64+
int CRYPTO_sysrand_chunk(void *out, size_t requested) {
65+
assert(requested <= CRYPTO_sysrand_chunk_max_len);
66+
return RtlGenRandom(out, (ULONG)requested) ? 1 : 0;
67+
}
3768

38-
int CRYPTO_sysrand(void *out, size_t requested) {
39-
while (requested > 0) {
40-
ULONG output_bytes_this_pass = ULONG_MAX;
41-
if (requested < output_bytes_this_pass) {
42-
output_bytes_this_pass = requested;
43-
}
44-
if (!RtlGenRandom(out, output_bytes_this_pass)) {
45-
return 0;
69+
#elif defined(__linux__)
70+
71+
#include <errno.h>
72+
#include <unistd.h>
73+
#include <sys/syscall.h>
74+
75+
/* The getrandom syscall was added in Linux 3.17. For some important platforms,
76+
* we also support building against older kernels' headers. For other
77+
* platforms, the newer kernel's headers are required. */
78+
#if !defined(SYS_getrandom)
79+
#if defined(OPENSSL_AARCH64)
80+
#define SYS_getrandom 278
81+
#elif defined(OPENSSL_ARM)
82+
#define SYS_getrandom 384
83+
#elif defined(OPENSSL_X86)
84+
#define SYS_getrandom 355
85+
#elif defined(OPENSSL_X86_64)
86+
#define SYS_getrandom 318
87+
#else
88+
#error "Error: Kernel headers are too old; SYS_getrandom not defined."
89+
#endif
90+
#endif
91+
92+
93+
/* http://man7.org/linux/man-pages/man2/getrandom.2.html: "Calling
94+
* getrandom() to read /dev/urandom for small values (<= 256) of buflen is
95+
* the preferred mode of usage." */
96+
const size_t CRYPTO_sysrand_chunk_max_len = 256;
97+
98+
int CRYPTO_sysrand_chunk(void *out, size_t requested) {
99+
assert(requested <= CRYPTO_sysrand_chunk_max_len);
100+
if (syscall(SYS_getrandom, out, requested, 0u) < 0) {
101+
if (errno == ENOSYS) {
102+
return -1;
46103
}
47-
requested -= output_bytes_this_pass;
48-
out = (uint8_t *)out + output_bytes_this_pass;
104+
return 0;
49105
}
50-
51106
return 1;
52107
}
53108

54-
#endif /* OPENSSL_WINDOWS */
109+
#endif

mk/ring.mk

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ RING_SRCS = $(addprefix $(RING_PREFIX), \
5252
crypto/mem.c \
5353
crypto/modes/gcm.c \
5454
crypto/poly1305/poly1305.c \
55+
crypto/rand/sysrand.c \
5556
crypto/rsa/blinding.c \
5657
crypto/rsa/padding.c \
5758
crypto/rsa/rsa.c \

src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
//! <table>
2020
//! <tr><th>Feature
2121
//! <th>Description
22+
//! <tr><td><code>disable_dev_urandom_fallback</code>
23+
//! <td><p>On Linux, by default, `ring::rand::SystemRandom` will fall back
24+
//! to reading from `/dev/urandom` if the `getrandom()` syscall isn't
25+
//! supported at runtime. When the `disable_dev_urandom_fallback`
26+
//! feature is enabled, such fallback will not occur. See the
27+
//! documentation for `rand::SystemRandom` for more details.
2228
//! <tr><td><code>no_heap</code>
2329
//! <td>Disable all functionality that uses the heap. This is useful for
2430
//! code running in kernel space and some embedded applications. The

src/rand.rs

+98-21
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616
1717
#![allow(unsafe_code)]
1818

19-
#[cfg(unix)]
20-
extern crate std;
21-
2219
use c;
2320
use core;
2421

@@ -31,16 +28,27 @@ pub trait SecureRandom {
3128
/// A secure random number generator where the random values come directly
3229
/// from the operating system.
3330
///
34-
/// On "unix"-ish platforms, this is currently done by reading from
35-
/// `/dev/urandom`. A new file handle for `/dev/urandom/` is opened each time a
36-
/// `SystemRandom` is constructed and the file handle is closed each time.
31+
/// On non-Linux Unix-/Posix-ish platforms, `fill()` is currently always
32+
/// implemented by reading from `/dev/urandom`. (This is something that should
33+
/// be improved, at least for platforms that offer something better.)
34+
///
35+
/// On Linux, `fill()` will use the
36+
/// [`getrandom`](man7.org/linux/man-pages/man2/getrandom.2.html) syscall. If
37+
/// the kernel is too old to support `getrandom` then by default `fill()` falls
38+
/// back to reading from `/dev/urandom`. This decision is made the first time
39+
/// `fill` *succeeds*. The fallback to `/dev/urandom` can be disabled by
40+
/// disabling by enabling the *ring* crate's `disable_dev_urandom_fallback`
41+
/// feature; this should be done whenever the target system is known to support
42+
/// `getrandom`. Note that only application (binary) crates, and not library
43+
/// crates, should enable the `disable_dev_urandom_fallback` feature.
3744
///
38-
/// On other platforms, this is done using the platform's API for secure random
39-
/// number generation.
45+
/// On Windows, `fill` is implemented done using the platform's API for secure
46+
/// random number generation.
4047
///
41-
/// For efficiency's sake, it is recommend to create a single SystemRandom and
42-
/// then use it for all randomness generation, especially if the
43-
/// /dev/urandom-based `SystemRandom` implementation may be used.
48+
/// On Linux, to properly implement seccomp filtering when the
49+
/// `disable_dev_urandom_fallback` feature is enabled, allow `getrandom`
50+
/// through. Otherwise, allow file opening, `getrandom`, and `read` up until
51+
/// `fill()` succeeds. After that, allow `getrandom` and `read`.
4452
pub struct SystemRandom {
4553
impl_: Impl,
4654
}
@@ -60,13 +68,19 @@ impl SecureRandom for SystemRandom {
6068
}
6169
}
6270

63-
#[cfg(unix)]
71+
#[cfg(not(any(target_os = "linux", windows)))]
6472
type Impl = self::urandom::DevURandom;
6573

66-
#[cfg(windows)]
74+
#[cfg(any(all(target_os = "linux", feature = "disable_dev_urandom_fallback"),
75+
windows))]
6776
type Impl = self::sysrand::Sysrand;
6877

69-
#[cfg(unix)]
78+
#[cfg(all(target_os = "linux", not(feature = "disable_dev_urandom_fallback")))]
79+
type Impl = self::sysrand_or_urandom::SysRandOrDevURandom;
80+
81+
#[cfg(all(unix,
82+
not(all(target_os = "linux",
83+
feature = "disable_dev_urandom_fallback"))))]
7084
mod urandom {
7185
extern crate std;
7286

@@ -92,9 +106,9 @@ mod urandom {
92106
}
93107
}
94108

95-
#[cfg(windows)]
109+
#[cfg(any(target_os = "linux", windows))]
96110
mod sysrand {
97-
use {bssl, c};
111+
use bssl;
98112

99113
pub struct Sysrand;
100114

@@ -107,14 +121,70 @@ mod sysrand {
107121
impl super::SecureRandom for Sysrand {
108122
#[allow(unsafe_code)]
109123
fn fill(&mut self, dest: &mut [u8]) -> Result<(), ()> {
110-
bssl::map_result(unsafe {
111-
CRYPTO_sysrand(dest.as_mut_ptr(), dest.len())
112-
})
124+
for mut chunk in
125+
dest.chunks_mut(super::CRYPTO_sysrand_chunk_max_len) {
126+
try!(bssl::map_result(unsafe {
127+
super::CRYPTO_sysrand_chunk(chunk.as_mut_ptr(), chunk.len())
128+
}));
129+
}
130+
Ok(())
131+
}
132+
}
133+
}
134+
135+
#[cfg(all(target_os = "linux", not(feature = "disable_dev_urandom_fallback")))]
136+
mod sysrand_or_urandom {
137+
use core;
138+
use super::{sysrand, urandom};
139+
140+
pub enum SysRandOrDevURandom {
141+
Undecided,
142+
Sysrand(sysrand::Sysrand),
143+
DevURandom(urandom::DevURandom),
144+
}
145+
146+
impl SysRandOrDevURandom {
147+
pub fn new() -> Result<SysRandOrDevURandom, ()> {
148+
Ok(SysRandOrDevURandom::Undecided)
113149
}
114150
}
115151

116-
extern {
117-
fn CRYPTO_sysrand(buf: *mut u8, len: c::size_t) -> c::int;
152+
impl super::SecureRandom for SysRandOrDevURandom {
153+
fn fill(&mut self, dest: &mut [u8]) -> Result<(), ()> {
154+
match self {
155+
&mut SysRandOrDevURandom::Sysrand(ref mut i) => i.fill(dest),
156+
&mut SysRandOrDevURandom::DevURandom(ref mut i) => i.fill(dest),
157+
&mut SysRandOrDevURandom::Undecided => {
158+
let first_chunk_len =
159+
core::cmp::min(dest.len(),
160+
super::CRYPTO_sysrand_chunk_max_len);
161+
match unsafe {
162+
super::CRYPTO_sysrand_chunk(dest.as_mut_ptr(),
163+
first_chunk_len)
164+
} {
165+
1 => {
166+
let _ = core::mem::replace(self,
167+
SysRandOrDevURandom::Sysrand(
168+
try!(sysrand::Sysrand::new())));
169+
if first_chunk_len < dest.len() {
170+
self.fill(&mut dest[first_chunk_len..])
171+
} else {
172+
Ok(())
173+
}
174+
},
175+
-1 => {
176+
let _ = core::mem::replace(self,
177+
SysRandOrDevURandom::DevURandom(
178+
try!(urandom::DevURandom::new())));
179+
self.fill(dest)
180+
},
181+
_ => {
182+
Err(())
183+
}
184+
}
185+
}
186+
}
187+
}
118188
}
119189
}
120190

@@ -138,3 +208,10 @@ pub unsafe extern fn RAND_bytes(rng: *mut RAND, dest: *mut u8,
138208
_ => 0
139209
}
140210
}
211+
212+
213+
#[cfg(any(target_os = "linux", windows))]
214+
extern {
215+
pub static CRYPTO_sysrand_chunk_max_len: c::size_t;
216+
pub fn CRYPTO_sysrand_chunk(buf: *mut u8, len: c::size_t) -> c::int;
217+
}

0 commit comments

Comments
 (0)