diff --git a/.travis.yml b/.travis.yml index 8200f2b4..36e2c60e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,6 +137,7 @@ matrix: - cargo xbuild --target=x86_64-unknown-uefi - cargo xbuild --target=x86_64-unknown-hermit - cargo xbuild --target=x86_64-unknown-l4re-uclibc + - cargo xbuild --target=x86_64-uwp-windows-gnu # Trust cross-built/emulated targets. We must repeat all non-default values. - rust: stable diff --git a/appveyor.yml b/appveyor.yml index b318c8e6..f124686b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,10 +21,21 @@ environment: matrix: - TARGET: x86_64-pc-windows-msvc + CHANNEL: 1.32.0 + - TARGET: x86_64-pc-windows-msvc + CHANNEL: stable + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly - TARGET: i686-pc-windows-msvc + CHANNEL: nightly + - TARGET: x86_64-pc-windows-gnu + CHANNEL: nightly + - TARGET: i686-pc-windows-gnu + CHANNEL: nightly + install: - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - rustup-init.exe -y --default-host %TARGET% + - rustup-init.exe -y --default-host %TARGET% --default-toolchain %CHANNEL% - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - rustc -V - cargo -V diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..1beb4ed8 --- /dev/null +++ b/build.rs @@ -0,0 +1,19 @@ +#![deny(warnings)] + +use std::env; + +fn main() { + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("-uwp-windows-") { + // for BCryptGenRandom + println!("cargo:rustc-link-lib=bcrypt"); + // to work around unavailability of `target_vendor` on Rust 1.33 + println!("cargo:rustc-cfg=getrandom_uwp"); + } else if target.contains("windows") { + // for RtlGenRandom (aka SystemFunction036) + println!("cargo:rustc-link-lib=advapi32"); + } else if target.contains("apple-ios") { + // for SecRandomCopyBytes and kSecRandomDefault + println!("cargo:rustc-link-lib=framework=Security"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 338e806d..3ce58a9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,14 @@ cfg_if! { macro_rules! error { ($($x:tt)*) => {}; } + #[allow(unused)] + macro_rules! warn { + ($($x:tt)*) => {}; + } + #[allow(unused)] + macro_rules! info { + ($($x:tt)*) => {}; + } } } @@ -216,6 +224,8 @@ cfg_if! { #[path = "solaris_illumos.rs"] mod imp; } else if #[cfg(target_os = "wasi")] { #[path = "wasi.rs"] mod imp; + } else if #[cfg(all(windows, getrandom_uwp))] { + #[path = "windows_uwp.rs"] mod imp; } else if #[cfg(windows)] { #[path = "windows.rs"] mod imp; } else if #[cfg(all(target_arch = "x86_64", any( diff --git a/src/windows_uwp.rs b/src/windows_uwp.rs new file mode 100644 index 00000000..bf15d1a2 --- /dev/null +++ b/src/windows_uwp.rs @@ -0,0 +1,59 @@ +// 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 Windows UWP targets. After deprecation of Windows XP +//! and Vista, this can superseed the `RtlGenRandom`-based implementation. +use crate::Error; +use core::{ffi::c_void, num::NonZeroU32, ptr}; + +const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; + +extern "system" { + fn BCryptGenRandom( + hAlgorithm: *mut c_void, + pBuffer: *mut u8, + cbBuffer: u32, + dwFlags: u32, + ) -> u32; +} + +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + // Prevent overflow of u32 + for chunk in dest.chunks_mut(u32::max_value() as usize) { + let ret = unsafe { + BCryptGenRandom( + ptr::null_mut(), + chunk.as_mut_ptr(), + chunk.len() as u32, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ) + }; + // NTSTATUS codes use two highest bits for severity status + match ret >> 30 { + 0b01 => { + info!("BCryptGenRandom: information code 0x{:08X}", ret); + } + 0b10 => { + warn!("BCryptGenRandom: warning code 0x{:08X}", ret); + } + 0b11 => { + error!("BCryptGenRandom: failed with 0x{:08X}", ret); + // We zeroize the highest bit, so the error code will reside + // inside the range of designated for OS codes. + let code = ret ^ (1 << 31); + // SAFETY: the second highest bit is always equal to one, + // so it's impossible to get zero. Unfortunately compiler + // is not smart enough to figure out it yet. + let code = unsafe { NonZeroU32::new_unchecked(code) }; + return Err(Error::from(code)); + } + _ => (), + } + } + Ok(()) +}