Merge branch 'kalfar' into 'master'

Fix randd TODOs for CSPRNG and allowing entropy write

See merge request redox-os/randd!10
This commit is contained in:
Jeremy Soller
2020-01-20 23:55:19 +00:00
3 changed files with 455 additions and 121 deletions
Generated
+87 -54
View File
@@ -6,56 +6,96 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.23"
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.6"
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_chacha"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "randd"
version = "0.1.0"
dependencies = [
"rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"raw-cpuid 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -66,49 +106,42 @@ dependencies = [
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
name = "sha2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
name = "typenum"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum raw-cpuid 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13b844e4049605ff38fed943f5c7b2c691fad68d9d5bf074d2720554c4e48246"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
+3 -1
View File
@@ -3,8 +3,10 @@ name = "randd"
version = "0.1.0"
[dependencies]
rand = "0.3"
rand_chacha = "0.2"
rand_core = "0.5"
redox_syscall = "0.1"
sha2 = "0.8"
[target.'cfg(target_arch = "x86_64")'.dependencies]
raw-cpuid = "2.0"
+365 -66
View File
@@ -2,56 +2,371 @@
extern crate syscall;
extern crate rand_chacha;
extern crate rand_core;
#[cfg(target_arch = "x86_64")]
extern crate raw_cpuid;
extern crate rand;
extern crate sha2;
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use rand::chacha::ChaChaRng;
use rand::Rng;
use rand_chacha::ChaCha20Rng;
use rand_core::RngCore;
pub const MODE_PERM: u16 = 0x0FFF;
pub const MODE_EXEC: u16 = 0o1;
pub const MODE_WRITE: u16 = 0o2;
pub const MODE_READ: u16 = 0o4;
#[cfg(target_arch = "x86_64")]
use raw_cpuid::CpuId;
use syscall::{Error, Result, SchemeMut, EINVAL, MODE_CHR};
use syscall::data::{Packet, Stat};
use syscall::flag::EVENT_READ;
use syscall::{
Error, Result, SchemeMut, EBADF, EBADFD, EEXIST, EINVAL, ENOENT, EPERM, MODE_CHR, O_CLOEXEC,
O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_STAT, O_WRONLY,
};
//TODO: Use a CSPRNG, allow write of entropy
// Create an RNG Seed to create initial seed from the rdrand intel instruction
use rand_core::SeedableRng;
use sha2::{Digest, Sha256};
use std::collections::BTreeMap;
use std::num::Wrapping;
// This Daemon implements a Cryptographically Secure Random Number Generator
// that does not block on read - i.e. it is equivalent to linux /dev/urandom
// We do not implement blocking reads as per linux /dev/random for the reasons outlined
// here: https://www.2uo.de/myths-about-urandom/
// Default file access mode for PRNG
const DEFAULT_PRNG_MODE: u16 = 0o644;
// Rand crate recommends at least 256 bits of entropy to seed the RNG
const SEED_BYTES: usize = 32;
/// Create a true random seed for the RNG from the Intel x64 rdrand instruction if present.
/// Will seed with a zero (insecure) if rdrand not present.
fn create_rdrand_seed() -> [u8; SEED_BYTES] {
let mut rng = [0; SEED_BYTES];
let mut have_seeded = false;
#[cfg(target_arch = "x86_64")]
{
if CpuId::new().get_feature_info().unwrap().has_rdrand() {
for i in 0..SEED_BYTES / 8 {
// We get 8 bytes at a time from rdrand instruction
let rand: u64;
unsafe {
asm!("rdrand rax"
: "={rax}"(rand)
:
:
: "intel", "volatile");
}
rng[i * 8..(i * 8 + 8)].copy_from_slice(&rand.to_le_bytes());
}
have_seeded = true;
}
} // TODO integrate alternative entropy sources
if !have_seeded {
println!("randd: Seeding failed, no entropy source. Random numbers on this platform are NOT SECURE");
}
rng
}
/// Contains information about an open file
struct OpenFileInfo {
o_flags: usize,
/// Flags used when opening file.
uid: u32,
gid: u32,
file_stat: Stat,
}
impl OpenFileInfo {
/// Tests if the current user has enough permissions to view the file, op is the operation,
/// like read and write, these modes are MODE_EXEC, MODE_READ, and MODE_WRITE
/// Copied from redoxfs
fn permission(&self, op: u16) -> bool {
let mut perm = self.file_stat.st_mode & 0o7;
if self.uid == self.file_stat.st_uid {
// If self.mode is 101100110, >> 6 would be 000000101
// 0o7 is octal for 111, or, when expanded to 9 digits is 000000111
perm |= (self.file_stat.st_mode >> 6) & 0o7;
// Since we erased the GID and OTHER bits when >>6'ing, |= will keep those bits in place.
}
if self.gid == self.file_stat.st_gid || self.file_stat.st_gid == 0 {
perm |= (self.file_stat.st_mode >> 3) & 0o7;
}
if self.uid == 0 {
//set the `other` bits to 111
perm |= 0o7;
}
perm & op == op
}
fn o_flag_set(&self, f: usize) -> bool {
return (f & self.o_flags) == f;
}
}
/// Struct to represent the rand scheme.
struct RandScheme {
socket: File,
prng: ChaChaRng
prng: ChaCha20Rng,
// ChaCha20 is a Cryptographically Secure PRNG
// https://docs.rs/rand/0.5.0/rand/prng/chacha/struct.ChaChaRng.html
// Allows 2^64 streams of random numbers, which we will equate with file numbers
prng_stat: Stat,
open_descriptors: BTreeMap<usize, OpenFileInfo>, // Cannot use HashMap as the implementation
// calls the system RNG (us) for entropy to protect against HashDOS attacks.
// Trying to create a HashMap causes a system crash.
// <file number, information about the open file>
next_fd: Wrapping<usize>,
}
impl RandScheme {
/// Create new rand scheme from a message socket
fn new(socket: File) -> RandScheme {
RandScheme {
socket,
prng: ChaCha20Rng::from_seed(create_rdrand_seed()),
prng_stat: Stat {
st_mode: MODE_CHR | DEFAULT_PRNG_MODE,
st_gid: 0,
st_uid: 0,
..Default::default()
},
open_descriptors: BTreeMap::new(),
next_fd: Wrapping(0),
}
}
/// Gets the open file info for a file descriptor if it is open - error otherwise.
fn get_fd(&self, fd: usize) -> Result<&OpenFileInfo> {
// Check we've got a valid file descriptor
let file_info = match self.open_descriptors.get(&fd) {
Some(m) => m,
None => return Err(Error::new(EBADF)),
};
Ok(file_info)
}
/// Checks to see if the op (MODE_READ, MODE_WRITE) can be performed on the open file
/// descriptor - Will return the open file info if successful, and error if the file
/// descriptor is invalid, or the permission is denied.
fn can_perform_op_on_fd(&self, fd: usize, op: u16) -> Result<&OpenFileInfo> {
let file_info = self.get_fd(fd)?;
if !file_info.permission(op) {
return Err(Error::new(EPERM));
}
Ok(file_info)
}
/// Reseed the CSPRNG with the supplied entropy.
/// TODO add this to an entropy pool and give a limited estimate to the amount of entropy
/// TODO consider having trusted and untrusted entropy URIs, with different permissions.
fn reseed_prng(&mut self, entropy: &[u8]) {
// Need to fill a fixed size array for the from_seed, so we'll do 256 bit
// array and has the entropy into it.
let mut digest = Sha256::new();
digest.input(entropy);
let hash = digest.result();
let mut entropy_array: [u8; SEED_BYTES] = [0; SEED_BYTES];
entropy_array.copy_from_slice(hash.as_slice());
self.prng = ChaCha20Rng::from_seed(entropy_array);
}
}
#[test]
fn test_scheme_perms() {
let mut scheme = RandScheme::new(File::open(".").unwrap());
scheme.prng_stat.st_mode = MODE_CHR | 0o200;
scheme.prng_stat.st_uid = 1;
scheme.prng_stat.st_gid = 1;
assert!(scheme.open("/".as_bytes(), O_RDWR, 1, 1).is_err());
assert!(scheme.open("/".as_bytes(), O_RDONLY, 1, 1).is_err());
scheme.prng_stat.st_mode = MODE_CHR | 0o400;
let mut fd = scheme.open("".as_bytes(), O_RDONLY, 1, 1).unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
assert!(scheme.close(fd).is_ok());
assert!(scheme.open("".as_bytes(), O_WRONLY, 1, 1).is_err());
assert!(scheme.open("".as_bytes(), O_RDWR, 1, 1).is_err());
scheme.prng_stat.st_mode = MODE_CHR | 0o600;
fd = scheme.open("".as_bytes(), O_RDWR, 1, 1).unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
assert!(scheme.close(fd).is_ok());
fd = scheme.open("".as_bytes(), O_STAT, 2, 2).unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
assert!(scheme.close(fd).is_ok());
fd = scheme
.open("".as_bytes(), O_STAT | O_CLOEXEC, 2, 2)
.unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
assert!(scheme.close(fd).is_ok());
// Try another user in group (no group perms)
fd = scheme
.open("".as_bytes(), O_STAT | O_CLOEXEC, 2, 1)
.unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err());
assert!(scheme.close(fd).is_ok());
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
fd = scheme
.open("".as_bytes(), O_STAT | O_CLOEXEC, 2, 1)
.unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
assert!(scheme.close(fd).is_ok());
// Check root can do anything
scheme.prng_stat.st_mode = MODE_CHR | 0o000;
fd = scheme
.open("".as_bytes(), O_STAT | O_CLOEXEC, 0, 0)
.unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
assert!(scheme.close(fd).is_ok());
// Check the rand:/urandom URL (Equivalent to rand:/)
scheme.prng_stat.st_mode = MODE_CHR | 0o660;
fd = scheme
.open("/urandom".as_bytes(), O_STAT | O_CLOEXEC, 2, 1)
.unwrap();
assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok());
assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok());
assert!(scheme.close(fd).is_ok());
}
impl SchemeMut for RandScheme {
fn open(&mut self, _path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
Ok(0)
fn open(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result<usize> {
// We are only allowing
// reads/writes from rand:/ and rand:/urandom - the root directory on its own is passed as an empty slice
if path != "".as_bytes() && path != "/urandom".as_bytes() {
return Err(Error::new(ENOENT));
}
if flags & (O_CREAT | O_EXCL) == O_CREAT | O_EXCL {
return Err(Error::new(EEXIST));
}
let fd = self.next_fd;
let open_file_info = OpenFileInfo {
o_flags: flags,
file_stat: self.prng_stat,
uid,
gid,
};
if (open_file_info.o_flag_set(O_RDONLY) || open_file_info.o_flag_set(O_RDWR))
&& !open_file_info.permission(MODE_READ)
{
return Err(Error::new(EPERM));
}
if (open_file_info.o_flag_set(O_WRONLY) || open_file_info.o_flag_set(O_RDWR))
&& !open_file_info.permission(MODE_WRITE)
{
return Err(Error::new(EPERM));
}
self.open_descriptors.insert(fd.0, open_file_info);
// Get the next file descriptor
self.next_fd += Wrapping(1);
// If we've looped round there's a small chance that the file descriptor still exists, so loop till we get one that doesn't
loop {
if !self.open_descriptors.contains_key(&self.next_fd.0) {
break;
} else {
self.next_fd += Wrapping(1);
}
}
Ok(fd.0)
}
fn chmod(&mut self, path: &[u8], mode: u16, uid: u32, gid: u32) -> Result<usize> {
// Defer to fchmod
let fd = self.open(path, O_WRONLY, uid, gid)?;
self.fchmod(fd, mode)
}
fn dup(&mut self, file: usize, buf: &[u8]) -> Result<usize> {
if ! buf.is_empty() {
if !buf.is_empty() {
return Err(Error::new(EINVAL));
}
Ok(file)
}
/* Resource operations */
fn read(&mut self, file: usize, buf: &mut [u8]) -> Result<usize> {
// Check fd and permissions
self.can_perform_op_on_fd(file, MODE_READ)?;
fn read(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
let mut i = 0;
for chunk in buf.chunks_mut(8) {
let mut rand = self.prng.next_u64();
for b in chunk.iter_mut() {
*b = rand as u8;
rand = rand >> 8;
i += 1;
}
}
Ok(i)
// Setting the stream will ensure that if two clients are reading concurrently, they won't get the same numbers
self.prng.set_stream(file as u64); // Should probably find a way to re-instate the counter for this stream, but
// not doing so won't make the output any less 'random'
self.prng.fill_bytes(buf);
Ok(buf.len())
}
fn write(&mut self, file: usize, buf: &[u8]) -> Result<usize> {
// Check fd and permissions
self.can_perform_op_on_fd(file, MODE_WRITE)?;
// TODO - when we support other entropy sources, just add this to an entropy pool
// TODO - consider having trusted and untrusted entropy writing paths
// We have a healthy mistrust of the entropy we're being given, so we won't seed just with
// that as the resulting numbers would be predictable based on this input
// we'll take 512 bits (arbitrary) from the current PRNG, and seed with that
// and the supplied data.
let mut rng_buf: [u8; SEED_BYTES] = [0; SEED_BYTES];
self.prng.fill_bytes(&mut rng_buf);
let mut rng_vec = Vec::new();
rng_vec.extend(&rng_buf);
rng_vec.extend(buf);
self.reseed_prng(&rng_vec);
Ok(buf.len())
}
fn fchmod(&mut self, file: usize, mode: u16) -> Result<usize> {
// Check fd and permissions
let file_info = self.get_fd(file)?;
// only root and owner can chmod
if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 {
return Err(Error::new(EPERM));
}
self.prng_stat.st_mode = MODE_CHR | mode;
Ok(0)
}
fn fchown(&mut self, file: usize, uid: u32, gid: u32) -> Result<usize> {
// Check fd and permissions
let file_info = self.get_fd(file)?;
// only root and owner can chmod
if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 {
return Err(Error::new(EPERM));
}
self.prng_stat.st_uid = uid;
self.prng_stat.st_gid = gid;
Ok(0)
}
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
// Just ignore this.
Ok(0)
}
fn fevent(&mut self, _id: usize, _flags: usize) -> Result<usize> {
Ok(EVENT_READ)
}
fn fpath(&mut self, _file: usize, buf: &mut [u8]) -> Result<usize> {
let mut i = 0;
let scheme_path = b"rand";
@@ -62,68 +377,52 @@ impl SchemeMut for RandScheme {
Ok(i)
}
fn fstat(&mut self, _id: usize, stat: &mut Stat) -> Result<usize> {
*stat = Stat {
st_mode: MODE_CHR | 0o666,
..Default::default()
};
fn fstat(&mut self, file: usize, stat: &mut Stat) -> Result<usize> {
// Check fd and permissions
self.can_perform_op_on_fd(file, MODE_READ)?;
*stat = self.prng_stat.clone();
Ok(0)
}
fn fevent(&mut self, _id: usize, _flags: usize) -> Result<usize> {
Ok(EVENT_READ)
}
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
Ok(0)
}
fn close(&mut self, _file: usize) -> Result<usize> {
Ok(0)
fn close(&mut self, file: usize) -> Result<usize> {
// just remove the file descriptor from the open descriptors
match self.open_descriptors.remove(&file) {
Some(_) => Ok(0),
None => Err(Error::new(EBADFD)),
}
}
}
fn main(){
let mut has_rdrand = false;
#[cfg(target_arch = "x86_64")]
{
has_rdrand = CpuId::new().get_feature_info().unwrap().has_rdrand();
}
fn main() {
// Daemonize
if unsafe { syscall::clone(0).unwrap() } == 0 {
let socket = File::create(":rand").expect("rand: failed to create rand scheme");
let mut rng = ChaChaRng::new_unseeded();
if has_rdrand {
println!("rand: seeding with rdrand");
let rand: u64;
unsafe {
asm!("rdrand rax"
: "={rax}"(rand)
:
:
: "intel", "volatile");
}
rng.set_counter(0, rand);
} else {
println!("rand: unseeded");
}
let mut scheme = RandScheme{socket, prng: rng};
let mut scheme = RandScheme::new(socket);
syscall::setrens(0, 0).expect("randd: failed to enter null namespace");
loop {
let mut packet = Packet::default();
if scheme.socket.read(&mut packet).expect("rand: failed to read events from rand scheme") == 0 {
break;
match scheme.socket.read(&mut packet) {
Ok(s) => {
if s == 0 {
break;
}
scheme.handle(&mut packet);
match scheme.socket.write(&packet) {
Err(e) => println!("Error writing packet {}", e),
_ => {}
}
}
Err(e) => println!("Error reading packet {}", e),
}
scheme.handle(&mut packet);
scheme.socket.write(&packet).expect("rand: failed to write responses to rand scheme");
scheme
.socket
.write(&packet)
.expect("rand: failed to write responses to rand scheme");
}
}
}