Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.

Commit 1e4c558

Browse files
committed
Refs #164 -- expose the kernel's CSPRNG, safely
1 parent 11ae6d2 commit 1e4c558

File tree

8 files changed

+125
-0
lines changed

8 files changed

+125
-0
lines changed

build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ const INCLUDED_FUNCTIONS: &[&str] = &[
2525
"_copy_from_user",
2626
"alloc_chrdev_region",
2727
"unregister_chrdev_region",
28+
"wait_for_random_bytes",
29+
"get_random_bytes",
30+
"rng_is_initialized",
2831
];
2932
const INCLUDED_VARS: &[&str] = &[
3033
"EINVAL",
3134
"ENOMEM",
3235
"ESPIPE",
3336
"EFAULT",
37+
"EAGAIN",
3438
"__this_module",
3539
"FS_REQUIRES_DEV",
3640
"FS_BINARY_MOUNTDATA",

src/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <linux/cdev.h>
22
#include <linux/fs.h>
33
#include <linux/module.h>
4+
#include <linux/random.h>
45
#include <linux/slab.h>
56
#include <linux/uaccess.h>
67
#include <linux/version.h>

src/error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ impl Error {
1010
pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32));
1111
pub const EFAULT: Self = Error(-(bindings::EFAULT as i32));
1212
pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32));
13+
pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32));
1314

1415
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
1516
Error(errno)

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod error;
1313
pub mod file_operations;
1414
pub mod filesystem;
1515
pub mod printk;
16+
pub mod random;
1617
pub mod sysctl;
1718
mod types;
1819
pub mod user_ptr;

src/random.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use core::convert::TryInto;
2+
3+
use crate::{bindings, c_types, error};
4+
5+
/// Fills `dest` with random bytes generated from the kernel's CSPRNG. Ensures
6+
/// that the CSPRNG has been seeded before generating any random bytes, and
7+
/// will block until it's ready.
8+
pub fn getrandom(dest: &mut [u8]) -> error::KernelResult<()> {
9+
let res = unsafe { bindings::wait_for_random_bytes() };
10+
if res != 0 {
11+
return Err(error::Error::from_kernel_errno(res));
12+
}
13+
14+
unsafe {
15+
bindings::get_random_bytes(
16+
dest.as_mut_ptr() as *mut c_types::c_void,
17+
dest.len().try_into()?,
18+
);
19+
}
20+
Ok(())
21+
}
22+
23+
/// Fills `dest` with random bytes generated from the kernel's CSPRNG. If the
24+
/// CSPRNG is not yet seeded, returns an `Err(EAGAIN)` immediately. Only
25+
/// available on 4.19 and later kernels.
26+
#[cfg(kernel_4_19_0_or_greater)]
27+
pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult<()> {
28+
if !unsafe { bindings::rng_is_initialized() } {
29+
return Err(error::Error::EAGAIN);
30+
}
31+
getrandom(dest)
32+
}

tests/random/Cargo.toml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "random-tests"
3+
version = "0.1.0"
4+
authors = ["Alex Gaynor <[email protected]>", "Geoffrey Thomas <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
crate-type = ["staticlib"]
9+
test = false
10+
11+
[features]
12+
default = ["linux-kernel-module"]
13+
14+
[dependencies]
15+
linux-kernel-module = { path = "../..", optional = true }
16+
17+
[dev-dependencies]
18+
kernel-module-testlib = { path = "../../testlib" }

tests/random/src/lib.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#![no_std]
2+
3+
use alloc::vec;
4+
5+
use linux_kernel_module::sysctl::{Sysctl, SysctlStorage};
6+
use linux_kernel_module::{self, cstr, random, Mode};
7+
8+
struct EntropySource;
9+
10+
impl SysctlStorage for EntropySource {
11+
fn store_value(&self, _data: &[u8]) -> (usize, linux_kernel_module::KernelResult<()>) {
12+
(0, Err(linux_kernel_module::Error::EINVAL))
13+
}
14+
15+
fn read_value(
16+
&self,
17+
data: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter,
18+
) -> (usize, linux_kernel_module::KernelResult<()>) {
19+
let mut storage = vec![0; data.len()];
20+
if let Err(e) = random::getrandom(&mut storage) {
21+
return (0, Err(e));
22+
}
23+
(storage.len(), data.write(&storage))
24+
}
25+
}
26+
27+
struct RandomTestModule {
28+
_sysctl_entropy: Sysctl<EntropySource>,
29+
}
30+
31+
impl linux_kernel_module::KernelModule for RandomTestModule {
32+
fn init() -> linux_kernel_module::KernelResult<Self> {
33+
Ok(RandomTestModule {
34+
_sysctl_entropy: Sysctl::register(
35+
cstr!("rust/random-tests"),
36+
cstr!("entropy"),
37+
EntropySource,
38+
Mode::from_int(0o444),
39+
)?,
40+
})
41+
}
42+
}
43+
44+
linux_kernel_module::kernel_module!(
45+
RandomTestModule,
46+
author: "Fish in a Barrel Contributors",
47+
description: "A module for testing the CSPRNG",
48+
license: "GPL"
49+
);

tests/random/tests/tests.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::collections::HashSet;
2+
use std::fs;
3+
use std::io::Read;
4+
5+
use kernel_module_testlib::with_kernel_module;
6+
7+
#[test]
8+
fn test_random_entropy() {
9+
with_kernel_module(|| {
10+
let mut keys = HashSet::new();
11+
for _ in 0..1024 {
12+
let mut key = [0; 16];
13+
let mut f = fs::File::open("/proc/sys/rust/random-tests/entropy").unwrap();
14+
f.read_exact(&mut key).unwrap();
15+
keys.insert(key);
16+
}
17+
assert_eq!(keys.len(), 1024);
18+
});
19+
}

0 commit comments

Comments
 (0)