Skip to content

Crate implementation #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 14, 2019
22 changes: 18 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
[package]
name = "getrandom"
version = "0.0.0"
version = "0.1.0"
authors = ["The Rand Project Developers"]
license = "MIT/Apache-2.0"
edition = "2015"
license = "MIT OR Apache-2.0"
description = "A small cross-platform library to securely get random data (entropy)"

[dependencies]
[badges]
travis-ci = { repository = "rust-random/getrandom" }
appveyor = { repository = "rust-random/getrandom" }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "winnt"] }

[target.'cfg(fuchsia)'.dependencies]
fuchsia-cprng = "0.1"

[target.wasm32-unknown-unknown.dependencies]
wasm-bindgen = { version = "0.2.12", optional = true }
stdweb = { version = "0.4", optional = true }
24 changes: 24 additions & 0 deletions benches/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(test)]
extern crate test;
extern crate getrandom;

#[bench]
fn bench_64(b: &mut test::Bencher) {
let mut buf = [0u8; 64];
b.iter(|| {
getrandom::getrandom(&mut buf[..]).unwrap();
test::black_box(&buf);
});
b.bytes = buf.len() as u64;
}

#[bench]
fn bench_65536(b: &mut test::Bencher) {
let mut buf = [0u8; 65536];
b.iter(|| {
getrandom::getrandom(&mut buf[..]).unwrap();
test::black_box(&buf);
});
b.bytes = buf.len() as u64;
}

23 changes: 23 additions & 0 deletions src/cloudabi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use error::Error;

extern "C" {
fn cloudabi_sys_random_get(buf: *mut u8, len: usize) -> u16;
}

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
let errno = unsafe { cloudabi_sys_random_get(dest.as_ptr(), dest.len()) };
if errno == 0 {
Ok(())
} else {
Err(Error(unsafe {
NonZeroU32::new_unchecked(errno as u32)
}))
}
}
25 changes: 25 additions & 0 deletions src/dragonfly_haiku.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for DragonFly / Haiku
use super::Error;
use super::utils::use_init;
use std::fs::File;
use std::io::Read;
use std::cell::RefCell;

thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f,
|| File::open("/dev/random").map_err(From::from),
|f| f.read_exact(dest).map_err(From::from),
)
})
}
15 changes: 15 additions & 0 deletions src/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A dummy implementation for unsupported targets which always returns
//! `Err(UNAVAILABLE_ERROR)`
use super::UNAVAILABLE_ERROR;

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
Err(UNAVAILABLE_ERROR)
}
31 changes: 31 additions & 0 deletions src/emscripten.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for Emscripten
use super::Error;
use std::fs::File;
use std::io::Read;
use std::cell::RefCell;
use super::utils::use_init;

thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
// `Crypto.getRandomValues` documents `dest` should be at most 65536
// bytes. `crypto.randomBytes` documents: "To minimize threadpool
// task length variation, partition large randomBytes requests when
// doing so as part of fulfilling a client request.
RNG_FILE.with(|f| {
use_init(f, || File::open("/dev/random").map_err(From::from), |f| {
for chunk in dest.chunks_mut(65536) {
f.read_exact(chunk)?;
}
Ok(())
})
})
}
67 changes: 67 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::num::NonZeroU32;
use core::convert::From;
use core::fmt;
#[cfg(not(target_env = "sgx"))]
use std::{io, error};

pub const UNKNOWN_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x756e6b6e) // "unkn"
});

pub const UNAVAILABLE_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x4e416e61) // "NAna"
});

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Error(NonZeroU32);

impl Error {
pub fn code(&self) -> NonZeroU32 {
self.0
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
UNKNOWN_ERROR => write!(f, "Getrandom Error: unknown"),
UNAVAILABLE_ERROR => write!(f, "Getrandom Error: unavailable"),
code => write!(f, "Getrandom Error: {}", code.0.get()),
}
}
}

#[cfg(not(target_env = "sgx"))]
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
err.raw_os_error()
.and_then(|code| NonZeroU32::new(code as u32))
.map(|code| Error(code))
// in practice this should never happen
.unwrap_or(UNKNOWN_ERROR)
}
}

#[cfg(not(target_env = "sgx"))]
impl Into<io::Error> for Error {
fn into(self) -> io::Error {
match self {
UNKNOWN_ERROR => io::Error::new(io::ErrorKind::Other,
"getrandom error: unknown"),
UNAVAILABLE_ERROR => io::Error::new(io::ErrorKind::Other,
"getrandom error: entropy source is unavailable"),
code => io::Error::from_raw_os_error(code.0.get() as i32),
}
}
}

#[cfg(not(target_env = "sgx"))]
impl error::Error for Error { }
32 changes: 32 additions & 0 deletions src/freebsd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for FreeBSD
extern crate libc;

use super::Error;
use core::ptr;
use std::io;

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
let mib = [libc::CTL_KERN, libc::KERN_ARND];
// kern.arandom permits a maximum buffer size of 256 bytes
for chunk in dest.chunks_mut(256) {
let mut len = chunk.len();
let ret = unsafe {
libc::sysctl(
mib.as_ptr(), mib.len() as libc::c_uint,
chunk.as_mut_ptr() as *mut _, &mut len, ptr::null(), 0,
)
};
if ret == -1 || len != chunk.len() {
return Err(io::Error::last_os_error().into());
}
}
Ok(())
}
17 changes: 17 additions & 0 deletions src/fuchsia.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation for Fuchsia Zircon
extern crate fuchsia_cprng;

use super::Error;

pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
fuchsia_cprng::cprng_draw(dest);
Ok(())
}
95 changes: 95 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,98 @@
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![no_std]

#[cfg(not(target_env = "sgx"))]
#[macro_use] extern crate std;

#[cfg(any(
target_os = "android",
target_os = "netbsd",
target_os = "solaris",
target_os = "redox",
target_os = "dragonfly",
target_os = "haiku",
target_os = "emscripten",
target_os = "linux",
))]
mod utils;
mod error;
pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};

macro_rules! mod_use {
($cond:meta, $module:ident) => {
#[$cond]
mod $module;
#[$cond]
pub use $module::getrandom;
}
}

mod_use!(cfg(target_os = "android"), linux_android);
mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig);
mod_use!(cfg(target_os = "cloudabi"), cloudabi);
mod_use!(cfg(target_os = "dragonfly"), dragonfly_haiku);
mod_use!(cfg(target_os = "emscripten"), emscripten);
mod_use!(cfg(target_os = "freebsd"), freebsd);
mod_use!(cfg(target_os = "fuchsia"), fuchsia);
mod_use!(cfg(target_os = "haiku"), dragonfly_haiku);
mod_use!(cfg(target_os = "ios"), macos);
mod_use!(cfg(target_os = "linux"), linux_android);
mod_use!(cfg(target_os = "macos"), macos);
mod_use!(cfg(target_os = "netbsd"), netbsd);
mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig);
mod_use!(cfg(target_os = "redox"), redox);
mod_use!(cfg(target_os = "solaris"), solaris);
mod_use!(cfg(windows), windows);
mod_use!(cfg(target_env = "sgx"), sgx);

mod_use!(
cfg(all(
target_arch = "wasm32",
not(target_os = "emscripten"),
feature = "wasm-bindgen"
)),
wasm32_bindgen
);

mod_use!(
cfg(all(
target_arch = "wasm32",
not(target_os = "emscripten"),
not(feature = "wasm-bindgen"),
feature = "stdweb",
)),
wasm32_stdweb
);

mod_use!(
cfg(not(any(
target_os = "android",
target_os = "bitrig",
target_os = "cloudabi",
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
target_env = "sgx",
windows,
all(
target_arch = "wasm32",
any(
target_os = "emscripten",
feature = "wasm-bindgen",
feature = "stdweb",
),
),
))),
dummy
);
Loading