diff --git a/src/ios.rs b/src/ios.rs new file mode 100644 index 00000000..ed7f2cf1 --- /dev/null +++ b/src/ios.rs @@ -0,0 +1,41 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for iOS +extern crate std; + +use crate::Error; +use core::num::NonZeroU32; +use std::io; + +// TODO: Make extern once extern_types feature is stabilized. See: +// https://github.com/rust-lang/rust/issues/43467 +#[repr(C)] +struct SecRandom([u8; 0]); + +#[link(name = "Security", kind = "framework")] +extern "C" { + static kSecRandomDefault: *const SecRandom; + + fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> i32; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) }; + if ret == -1 { + error!("SecRandomCopyBytes call failed"); + Err(io::Error::last_os_error().into()) + } else { + Ok(()) + } +} + +#[inline(always)] +pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { + None +} diff --git a/src/lib.rs b/src/lib.rs index 063dffc9..9d3e583b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,8 @@ //! |------------------|--------------------------------------------------------- //! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once //! | Windows | [`RtlGenRandom`][3] -//! | macOS, iOS | [`SecRandomCopyBytes`][4] +//! | macOS | [`getentropy()`][19] if available, otherise [`/dev/random`][20] (identical to `/dev/urandom`) +//! | iOS | [`SecRandomCopyBytes`][4] //! | FreeBSD | [`kern.arandom`][5] //! | OpenBSD, Bitrig | [`getentropy`][6] //! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once @@ -115,6 +116,8 @@ //! [16]: #support-for-webassembly-and-amsjs //! [17]: https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-api.md#__wasi_random_get //! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide +//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/ +//! [20]: https://www.unix.com/man-page/mojave/4/random/ #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", @@ -167,6 +170,7 @@ mod error_impls; #[cfg(any( target_os = "android", target_os = "linux", + target_os = "macos", target_os = "solaris", target_os = "illumos", ))] @@ -181,7 +185,7 @@ mod_use!(cfg(target_os = "freebsd"), freebsd); mod_use!(cfg(target_os = "fuchsia"), fuchsia); mod_use!(cfg(target_os = "haiku"), use_file); mod_use!(cfg(target_os = "illumos"), solaris_illumos); -mod_use!(cfg(target_os = "ios"), macos); +mod_use!(cfg(target_os = "ios"), ios); mod_use!(cfg(target_os = "linux"), linux_android); mod_use!(cfg(target_os = "macos"), macos); mod_use!(cfg(target_os = "netbsd"), use_file); diff --git a/src/macos.rs b/src/macos.rs index 24157f9f..29078a86 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -1,4 +1,4 @@ -// Copyright 2018 Developers of the Rand project. +// Copyright 2019 Developers of the Rand project. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -6,32 +6,40 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Implementation for MacOS / iOS +//! Implementation for macOS extern crate std; -use crate::Error; +use crate::{use_file, Error}; +use core::mem; use core::num::NonZeroU32; +use lazy_static::lazy_static; use std::io; -// TODO: Make extern once extern_types feature is stabilized. See: -// https://github.com/rust-lang/rust/issues/43467 -#[repr(C)] -struct SecRandom([u8; 0]); +type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; -#[link(name = "Security", kind = "framework")] -extern "C" { - static kSecRandomDefault: *const SecRandom; - - fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> i32; +fn fetch_getentropy() -> Option { + let name = "getentropy\0"; + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) }; + unsafe { mem::transmute(addr) } } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) }; - if ret == -1 { - error!("SecRandomCopyBytes call failed"); - Err(io::Error::last_os_error().into()) - } else { + lazy_static! { + static ref GETENTROPY_FUNC: Option = fetch_getentropy(); + } + if let Some(fptr) = *GETENTROPY_FUNC { + for chunk in dest.chunks_mut(256) { + let ret = unsafe { fptr(chunk.as_mut_ptr(), chunk.len()) }; + if ret != 0 { + error!("getentropy syscall failed with ret={}", ret); + return Err(io::Error::last_os_error().into()); + } + } Ok(()) + } else { + // We fallback to reading from /dev/random instead of SecRandomCopyBytes + // to avoid high startup costs and linking the Security framework. + use_file::getrandom_inner(dest) } } diff --git a/src/use_file.rs b/src/use_file.rs index dc2ed8ff..d7c18a8a 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -25,6 +25,7 @@ const FILE_PATH: &str = "/dev/urandom"; target_os = "dragonfly", target_os = "emscripten", target_os = "haiku", + target_os = "macos", target_os = "solaris", target_os = "illumos" ))]