diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92068a845a..d0b168a9f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -453,3 +453,55 @@ jobs: directory: ./target/${{ matrix.target }}/debug/coverage/reports fail_ci_if_error: true verbose: true + + # The x86_64-unknown-uefi targets doesn't have std crate. + # It can only be compiled under `no_std`. + test_uefi: + # Don't run duplicate `push` jobs for the repo owner's PRs. + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + + runs-on: ${{ matrix.host_os }} + + strategy: + matrix: + features: + - # Default + target: + - x86_64-unknown-uefi + + mode: + - # debug + - --release + + # Only nightly support no std build. + rust_channel: + - nightly + + host_os: + - windows-latest + - ubuntu-18.04 + + steps: + - if: ${{ contains(matrix.host_os, 'ubuntu') }} + run: sudo apt-get update -y + + - uses: briansmith/actions-checkout@v2 + with: + persist-credentials: false + + - if: ${{ !contains(matrix.host_os, 'windows') }} + run: mk/install-build-tools.sh --target=${{ matrix.target }} ${{ matrix.features }} + + - if: ${{ contains(matrix.host_os, 'windows') }} + run: ./mk/install-build-tools.ps1 + + - uses: briansmith/toolchain@v1 + with: + toolchain: nightly + override: true + components: rust-src + + # Currently, there is no 'rust-std' for target 'x86_64-unknown-uefi'. + # Build-only approach for now. + # TODO: no std tests + - run: mk/cargo.sh build -Zbuild-std="core,alloc" --target=${{ matrix.target }} diff --git a/Cargo.toml b/Cargo.toml index 736bd9f06b..14e95158a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ include = [ "crypto/fipsmodule/modes/asm/ghash-x86.pl", "crypto/fipsmodule/modes/asm/ghash-x86_64.pl", "crypto/fipsmodule/modes/asm/ghashv8-armx.pl", + "crypto/fipsmodule/rand/asm/rdrand-x86_64.pl", "crypto/fipsmodule/sha/asm/sha256-armv4.pl", "crypto/fipsmodule/sha/asm/sha512-armv4.pl", "crypto/fipsmodule/sha/asm/sha512-armv8.pl", diff --git a/build.rs b/build.rs index 3087ba7b5d..351611f78e 100644 --- a/build.rs +++ b/build.rs @@ -64,6 +64,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[ (&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"), (&[X86_64], "crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl"), (&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"), + (&[X86_64], "crypto/fipsmodule/rand/asm/rdrand-x86_64.pl"), (&[X86_64], "crypto/poly1305/poly1305_vec.c"), (&[X86_64], SHA512_X86_64), (&[X86_64], "crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl"), @@ -222,7 +223,7 @@ const ASM_TARGETS: &[AsmTarget] = &[ preassemble: true, }, AsmTarget { - oss: &[WINDOWS], + oss: &[WINDOWS, UEFI], arch: "x86_64", perlasm_format: "nasm", asm_extension: "asm", @@ -279,6 +280,8 @@ const MACOS_ABI: &[&str] = &["ios", "macos"]; const WINDOWS: &str = "windows"; +const UEFI: &str = "uefi"; + /// Read an environment variable and tell Cargo that we depend on it. /// /// This needs to be used for any environment variable that isn't a standard @@ -381,8 +384,9 @@ fn pregenerate_asm_main() { perlasm(&perlasm_src_dsts, asm_target); if asm_target.preassemble { - // Preassembly is currently only done for Windows targets. - assert_eq!(&asm_target.oss, &[WINDOWS]); + // Preassembly is currently done for Windows and UEFI targets. + assert!(asm_target.oss.contains(&WINDOWS) || asm_target.oss.contains(&UEFI)); + let os = WINDOWS; let srcs = asm_srcs(perlasm_src_dsts); @@ -460,7 +464,7 @@ fn build_c_code( // For Windows we also pregenerate the object files for non-Git builds so // the user doesn't need to install the assembler. - if use_pregenerated && target.os == WINDOWS { + if use_pregenerated && supports_preassembly(&target.arch, &target.os) { asm_srcs = asm_srcs .iter() .map(|src| obj_path(&pregenerated, src.as_path())) @@ -554,7 +558,7 @@ fn compile(p: &Path, target: &Target, include_dir: &Path, out_dir: &Path) -> Str p.to_str().expect("Invalid path").into() } else { let out_file = obj_path(out_dir, p); - let cmd = if target.os != WINDOWS || ext != "asm" { + let cmd = if !supports_preassembly(&target.arch, &target.os) || ext != "asm" { cc(p, ext, target, include_dir, &out_file) } else { nasm(p, &target.arch, include_dir, &out_file) @@ -602,6 +606,7 @@ fn cc(file: &Path, ext: &str, target: &Target, include_dir: &Path, out_file: &Pa && target.os != "redox" && target.os != "windows" && target.arch != "wasm32" + && target.os != UEFI { let _ = c.flag("-fstack-protector"); } @@ -627,17 +632,25 @@ fn cc(file: &Path, ext: &str, target: &Target, include_dir: &Path, out_file: &Pa } } + // UEFI is a baremental freestanding environment without stdlib. + let freestanding = target.os == UEFI; + // Allow cross-compiling without a target sysroot for these targets. // // poly1305_vec.c requires which requires . if (target.arch == "wasm32" && target.os == "unknown") || (target.os == "linux" && target.is_musl && target.arch != "x86_64") + || freestanding { if let Ok(compiler) = c.try_get_compiler() { // TODO: Expand this to non-clang compilers in 0.17.0 if practical. if compiler.is_like_clang() { let _ = c.flag("-nostdlibinc"); let _ = c.define("RING_CORE_NOSTDLIBINC", "1"); + + if freestanding { + let _ = c.flag("-ffreestanding"); + } } } } @@ -680,7 +693,10 @@ fn nasm(file: &Path, arch: &str, include_dir: &Path, out_file: &Path) -> Command std::path::MAIN_SEPARATOR, ))); - let mut c = Command::new("./target/tools/windows/nasm/nasm"); + let mut c = Command::new(&get_command( + "NASM_EXECUTABLE", + "./target/tools/windows/nasm/nasm", + )); let _ = c .arg("-o") .arg(out_file.to_str().expect("Invalid path")) @@ -917,6 +933,8 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String { "CRYPTO_poly1305_init_neon", "CRYPTO_poly1305_update", "CRYPTO_poly1305_update_neon", + "CRYPTO_rdrand", + "CRYPTO_rdrand_multiple8_buf", "ChaCha20_ctr32", "LIMBS_add_mod", "LIMBS_are_even", @@ -1027,3 +1045,9 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String { out } + +fn supports_preassembly(arch: &str, os: &str) -> bool { + ASM_TARGETS.iter().any(|asm_target| { + asm_target.preassemble && asm_target.arch == arch && asm_target.oss.contains(&os) + }) +} diff --git a/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl b/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl new file mode 100644 index 0000000000..ac442a95b8 --- /dev/null +++ b/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl @@ -0,0 +1,87 @@ +#!/usr/bin/env perl + +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +use strict; + +my $flavour = shift; +my $output = shift; +if ($flavour =~ /\./) { $output = $flavour; undef $flavour; } + +my $win64 = 0; +$win64 = 1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/); + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; +my $dir = $1; +my $xlate; +( $xlate="${dir}../../../perlasm/x86_64-xlate.pl" and -f $xlate) or +die "can't locate x86_64-xlate.pl"; + +open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\""; +*STDOUT=*OUT; + +my ($out, $len, $tmp1, $tmp2) = $win64 ? ("%rcx", "%rdx", "%r8", "%r9") + : ("%rdi", "%rsi", "%rdx", "%rcx"); + +print<<___; +.text + +# CRYPTO_rdrand writes eight bytes of random data from the hardware RNG to +# |out|. It returns one on success or zero on hardware failure. +# int CRYPTO_rdrand(uint8_t out[8]); +.globl CRYPTO_rdrand +.type CRYPTO_rdrand,\@abi-omnipotent +.align 16 +CRYPTO_rdrand: +.cfi_startproc + xorq %rax, %rax + rdrand $tmp1 + # An add-with-carry of zero effectively sets %rax to the carry flag. + adcq %rax, %rax + movq $tmp1, 0($out) + retq +.cfi_endproc +.size CRYPTO_rdrand,.-CRYPTO_rdrand + +# CRYPTO_rdrand_multiple8_buf fills |len| bytes at |buf| with random data from +# the hardware RNG. The |len| argument must be a multiple of eight. It returns +# one on success and zero on hardware failure. +# int CRYPTO_rdrand_multiple8_buf(uint8_t *buf, size_t len); +.globl CRYPTO_rdrand_multiple8_buf +.type CRYPTO_rdrand_multiple8_buf,\@abi-omnipotent +.align 16 +CRYPTO_rdrand_multiple8_buf: +.cfi_startproc + test $len, $len + jz .Lout + movq \$8, $tmp1 +.Lloop: + rdrand $tmp2 + jnc .Lerr + movq $tmp2, 0($out) + addq $tmp1, $out + subq $tmp1, $len + jnz .Lloop +.Lout: + movq \$1, %rax + retq +.Lerr: + xorq %rax, %rax + retq +.cfi_endproc +.size CRYPTO_rdrand_multiple8_buf,.-CRYPTO_rdrand_multiple8_buf +___ + +close STDOUT or die "error closing STDOUT: $!"; # flush diff --git a/mk/cargo.sh b/mk/cargo.sh index 85a1cbc385..184052aab2 100755 --- a/mk/cargo.sh +++ b/mk/cargo.sh @@ -97,6 +97,11 @@ case $target in export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS="$rustflags_self_contained" fi ;; + x86_64-unknown-uefi) + export CC_x86_64_unknown_uefi=clang-$llvm_version + export AR_x86_64_unknown_uefi=llvm-ar-$llvm_version + export NASM_EXECUTABLE=nasm + ;; wasm32-unknown-unknown) # The first two are only needed for when the "wasm_c" feature is enabled. export CC_wasm32_unknown_unknown=clang-$llvm_version diff --git a/mk/install-build-tools.sh b/mk/install-build-tools.sh index db50246e8d..34c090e124 100755 --- a/mk/install-build-tools.sh +++ b/mk/install-build-tools.sh @@ -65,6 +65,11 @@ case $target in --target=i686-unknown-linux-musl|--target=x86_64-unknown-linux-musl) use_clang=1 ;; +--target=x86_64-unknown-uefi) + use_clang=1 + install_packages \ + nasm + ;; --target=wasm32-unknown-unknown) # The version of wasm-bindgen-cli must match the wasm-bindgen version. wasm_bindgen_version=$(cargo metadata --format-version 1 | jq -r '.packages | map(select( .name == "wasm-bindgen")) | map(.version) | .[0]') diff --git a/src/rand.rs b/src/rand.rs index 4be359cc59..56f30f7ef4 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -143,6 +143,10 @@ impl RandomlyConstructable for T where T: self::sealed::RandomlyConstructable /// random number generation. /// /// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html +/// +/// On UEFI, `fill` is implemented using `CRYPTO_rdrand` +/// & `CRYPTO_rdrand_multiple8_buf` which provided by BoringSSL. +/// #[derive(Clone, Debug)] pub struct SystemRandom(()); @@ -196,6 +200,9 @@ use self::darwin::fill as fill_impl; #[cfg(any(target_os = "fuchsia"))] use self::fuchsia::fill as fill_impl; +#[cfg(any(target_os = "uefi"))] +use self::uefi::fill as fill_impl; + #[cfg(any(target_os = "android", target_os = "linux"))] mod sysrand_chunk { use crate::{c, error}; @@ -419,3 +426,66 @@ mod fuchsia { fn zx_cprng_draw(buffer: *mut u8, length: usize); } } + +#[cfg(any(target_os = "uefi"))] +mod uefi { + use crate::error; + + pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> { + fill_impl(dest) + } + + #[cfg(not(any(target_arch = "x86_64")))] + fn fill_impl(dest: &mut [u8]) -> Result<(), error::Unspecified> { + Err(error::Unspecified) + } + + #[cfg(any(target_arch = "x86_64"))] + fn fill_impl(dest: &mut [u8]) -> Result<(), error::Unspecified> { + fn is_avaiable() -> bool { + // TODO(xiaoyuxlu): use cpu::intel::RDRAND.avaiable when cpu.rs updated + // https://github.com/briansmith/ring/pull/1406#discussion_r720394928 + // Current implementation may cause problem on AMD cpu. REF: + // https://github.com/nagisa/rust_rdrand/blob/f2fdd528a6103c946a2e9d0961c0592498b36493/src/lib.rs#L161 + prefixed_extern! { + static mut OPENSSL_ia32cap_P: [u32; 4]; + } + const FLAG: u32 = 1 << 30; + unsafe { OPENSSL_ia32cap_P[1] & FLAG == FLAG } + } + + // We must make sure current cpu support `rdrand` + if !is_avaiable() { + return Err(error::Unspecified); + } + + use crate::c; + prefixed_extern! { + fn CRYPTO_rdrand_multiple8_buf(buffer: *mut u8, length: c::size_t) -> c::int; + } + prefixed_extern! { + fn CRYPTO_rdrand(dest: *mut u8) -> c::int; + } + + let len = dest.len(); + let len_multiple8 = len & (!7usize); + let remainder = len - len_multiple8; + + let mut res = 1; + if res != 0 && len_multiple8 != 0 { + res = unsafe { CRYPTO_rdrand_multiple8_buf(dest.as_mut_ptr(), len_multiple8) }; + } + if res != 0 && remainder != 0 { + let mut rand_buf = [0u8; 8]; + res = unsafe { CRYPTO_rdrand(rand_buf.as_mut_ptr()) }; + if res != 0 { + dest[len_multiple8..].copy_from_slice(&rand_buf[..remainder]); + } + } + if res == 1 { + Ok(()) + } else { + Err(error::Unspecified) + } + } +}