diff --git a/.travis.yml b/.travis.yml index 56a738fc00..4346001c9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -416,6 +416,38 @@ matrix: sources: - ubuntu-toolchain-r-test + - env: TARGET_X=x86_64-fortanix-unknown-sgx FEATURES_X= MODE_X=DEBUG KCOV=0 + rust: nightly + os: linux + dist: trusty + + - env: TARGET_X=x86_64-fortanix-unknown-sgx FEATURES_X= MODE_X=RELWITHDEBINFO KCOV=0 + rust: nightly + os: linux + dist: trusty + + - env: TARGET_X=x86_64-fortanix-unknown-sgx CC_X=gcc-7 FEATURES_X= MODE_X=DEBUG KCOV=0 + rust: nightly + os: linux + dist: trusty + addons: + apt: + packages: + - gcc-7 + sources: + - ubuntu-toolchain-r-test + + - env: TARGET_X=x86_64-fortanix-unknown-sgx CC_X=gcc-7 FEATURES_X= MODE_X=RELWITHDEBINFO KCOV=0 + rust: nightly + os: linux + dist: trusty + addons: + apt: + packages: + - gcc-7 + sources: + - ubuntu-toolchain-r-test + - env: TARGET_X=x86_64-apple-darwin FEATURES_X= MODE_X=DEBUG KCOV=0 rust: beta os: osx diff --git a/Cargo.toml b/Cargo.toml index 4a66b38f77..6e905cbaa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ include = [ "crypto/fipsmodule/modes/asm/ghashv8-armx.pl", "crypto/fipsmodule/modes/gcm.c", "crypto/fipsmodule/modes/internal.h", + "crypto/fipsmodule/rand/asm/rdrand-x86_64.pl", "crypto/fipsmodule/sha/asm/sha256-586.pl", "crypto/fipsmodule/sha/asm/sha256-armv4.pl", "crypto/fipsmodule/sha/asm/sha512-586.pl", @@ -324,6 +325,7 @@ internal_benches = [] slow_tests = [] test_logging = [] use_heap = [] +force_std_detection = [] # XXX: debug = false because of https://github.com/rust-lang/rust/issues/34122 diff --git a/build.rs b/build.rs index 45cfab7ec7..3cafc7fc6a 100644 --- a/build.rs +++ b/build.rs @@ -91,6 +91,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[ (&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"), (&[X86_64], "crypto/poly1305/asm/poly1305-x86_64.pl"), (&[X86_64], SHA512_X86_64), + (&[X86_64], "crypto/fipsmodule/rand/asm/rdrand-x86_64.pl"), (&[AARCH64, ARM], "crypto/fipsmodule/aes/asm/aesv8-armx.pl"), (&[AARCH64, ARM], "crypto/fipsmodule/modes/asm/ghashv8-armx.pl"), 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..056dd74878 --- /dev/null +++ b/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl @@ -0,0 +1,79 @@ +#!/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. */ + +$flavour = shift; +$output = shift; +if ($flavour =~ /\./) { $output = $flavour; undef $flavour; } + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $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; + +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,\@function,1 +.align 16 +CRYPTO_rdrand: +.cfi_startproc + xorq %rax, %rax + # This is rdrand %rcx. It sets rcx to a random value and sets the carry + # flag on success. + .byte 0x48, 0x0f, 0xc7, 0xf1 + # An add-with-carry of zero effectively sets %rax to the carry flag. + adcq %rax, %rax + movq %rcx, 0(%rdi) + retq +.cfi_endproc + +# 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,\@function,2 +.align 16 +CRYPTO_rdrand_multiple8_buf: +.cfi_startproc + test %rsi, %rsi + jz .Lout + movq \$8, %rdx +.Lloop: + # This is rdrand %rcx. It sets rcx to a random value and sets the carry + # flag on success. + .byte 0x48, 0x0f, 0xc7, 0xf1 + jnc .Lerr + movq %rcx, 0(%rdi) + addq %rdx, %rdi + subq %rdx, %rsi + jnz .Lloop +.Lout: + movq \$1, %rax + retq +.Lerr: + xorq %rax, %rax + retq +.cfi_endproc +___ + +close STDOUT; # flush diff --git a/mk/travis.sh b/mk/travis.sh index 20545eb387..639f03ad75 100755 --- a/mk/travis.sh +++ b/mk/travis.sh @@ -47,7 +47,7 @@ if [[ "$TARGET_X" =~ ^(arm|aarch64) && ! "$TARGET_X" =~ android ]]; then sudo apt-get install --no-install-recommends binfmt-support qemu-user-binfmt -y fi -if [[ ! "$TARGET_X" =~ "x86_64-" ]]; then +if [[ "$TARGET_X" != "$(rustc --version --verbose|sed -n 's/^host: //p')" ]]; then rustup target add "$TARGET_X" # By default cargo/rustc seems to use cc for linking, We installed the @@ -122,6 +122,10 @@ armv7-linux-androideabi) adb emu kill + ;; +x86_64-fortanix-unknown-sgx) + # Can't run SGX in Travis. Only build, but don't run, the tests + RUSTFLAGS="-C target-feature=+aes,+pclmul" cargo test -vv -j2 --no-run ${mode-} ${FEATURES_X-} --target=$TARGET_X ;; *) cargo test -vv -j2 ${mode-} ${FEATURES_X-} --target=$TARGET_X diff --git a/mk/update-travis-yml.py b/mk/update-travis-yml.py index 88b4177e1d..1222d68fd3 100755 --- a/mk/update-travis-yml.py +++ b/mk/update-travis-yml.py @@ -47,6 +47,7 @@ "arm-unknown-linux-gnueabihf" : [ "arm-linux-gnueabihf-gcc" ], "i686-unknown-linux-gnu" : linux_compilers, "x86_64-unknown-linux-gnu" : linux_compilers, + "x86_64-fortanix-unknown-sgx" : linux_compilers, "x86_64-apple-darwin" : osx_compilers, } @@ -76,17 +77,20 @@ "aarch64-unknown-linux-gnu", "i686-unknown-linux-gnu", "arm-unknown-linux-gnueabihf", + "x86_64-fortanix-unknown-sgx", ], } def format_entries(): - return "\n".join([format_entry(os, target, compiler, rust, mode, features) - for rust in rusts - for os in oss - for target in targets[os] - for compiler in compilers[target] - for mode in modes - for features in feature_sets]) + return "\n".join([entry for entry in + (format_entry(os, target, compiler, rust, mode, features) + for rust in rusts + for os in oss + for target in targets[os] + for compiler in compilers[target] + for mode in modes + for features in feature_sets) + if entry is not None]) # We use alternative names (the "_X" suffix) so that, in mk/travis.sh, we can # ensure that we set the specific variables we want and that no relevant @@ -111,6 +115,12 @@ def format_entries(): %(sources)s""" def format_entry(os, target, compiler, rust, mode, features): + if target == "x86_64-fortanix-unknown-sgx" and rust != "nightly": + return + # Tracked in https://github.com/fortanix/rust-sgx/issues/64 + if target == "x86_64-fortanix-unknown-sgx" and compiler == "clang": + return + target_words = target.split("-") arch = target_words[0] vendor = target_words[1] @@ -127,13 +137,11 @@ def format_entry(os, target, compiler, rust, mode, features): mode == "DEBUG") if sys == "darwin": - abi = sys sys = "macos" elif sys == "androideabi": - abi = sys sys = "linux" - else: - abi = target_words[3] + elif target == "x86_64-fortanix-unknown-sgx": + sys = "linux" def prefix_all(prefix, xs): return [prefix + x for x in xs] @@ -263,7 +271,7 @@ def main(): file.seek(0) file.write(new_contents) file.truncate() - print new_contents + print new_contents, if __name__ == '__main__': main() diff --git a/src/aead/aes.rs b/src/aead/aes.rs index 5bd20eb7d3..203e99a139 100644 --- a/src/aead/aes.rs +++ b/src/aead/aes.rs @@ -74,6 +74,7 @@ impl Key { })?; }, + #[cfg(not(target_env = "sgx"))] _ => { extern "C" { fn GFp_aes_nohw_set_encrypt_key( @@ -121,6 +122,7 @@ impl Key { } }, + #[cfg(not(target_env = "sgx"))] _ => { extern "C" { fn GFp_aes_nohw_encrypt(a: *const Block, r: *mut Block, key: &AES_KEY); @@ -246,6 +248,7 @@ pub enum Implementation { #[cfg(target_arch = "arm")] BSAES = 3, + #[cfg(not(target_env = "sgx"))] Fallback = 4, } @@ -268,7 +271,15 @@ fn detect_implementation(cpu_features: cpu::Features) -> Implementation { } } - Implementation::Fallback + #[cfg(not(target_env = "sgx"))] + { + Implementation::Fallback + } + + #[cfg(target_env = "sgx")] + { + panic!("No AES implementation available!") + } } #[must_use] diff --git a/src/aead/gcm.rs b/src/aead/gcm.rs index 46522c2905..4a350aedc3 100644 --- a/src/aead/gcm.rs +++ b/src/aead/gcm.rs @@ -57,6 +57,7 @@ impl Key { } }, + #[cfg(not(target_env = "sgx"))] Implementation::Fallback => { extern "C" { fn GFp_gcm_init_4bit(key: &mut Key, h: &[u64; 2]); @@ -139,6 +140,7 @@ impl Context { } }, + #[cfg(not(target_env = "sgx"))] Implementation::Fallback => { extern "C" { fn GFp_gcm_ghash_4bit( @@ -177,6 +179,7 @@ impl Context { } }, + #[cfg(not(target_env = "sgx"))] Implementation::Fallback => { extern "C" { fn GFp_gcm_gmult_4bit(ctx: &mut Context, Htable: *const GCM128_KEY); @@ -199,6 +202,7 @@ impl Context { pub(super) fn is_avx2(&self, cpu_features: cpu::Features) -> bool { match detect_implementation(cpu_features) { Implementation::CLMUL => has_avx_movbe(self.cpu_features), + #[cfg(not(target_env = "sgx"))] _ => false, } } @@ -234,6 +238,7 @@ enum Implementation { #[cfg(target_arch = "arm")] NEON, + #[cfg(not(target_env = "sgx"))] Fallback, } @@ -252,7 +257,15 @@ fn detect_implementation(cpu: cpu::Features) -> Implementation { } } - Implementation::Fallback + #[cfg(not(target_env = "sgx"))] + { + Implementation::Fallback + } + + #[cfg(target_env = "sgx")] + { + panic!("No GCM implementation available!") + } } #[cfg(target_arch = "x86_64")] diff --git a/src/cpu.rs b/src/cpu.rs index 7b1b299401..0794f315f7 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -32,11 +32,145 @@ pub(crate) fn features() -> Features { let () = INIT.call_once(|| { #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { - extern "C" { - fn GFp_cpuid_setup(); + #[cfg(any(feature = "force_std_detection", all(target_env = "sgx" /*, target_vendor = "fortanix"*/)))] + { + extern "C" { + static mut GFp_ia32cap_P: [u32; 4]; + } + let [l1edx, l1ecx, l7ebx, l7ecx] = unsafe { &mut GFp_ia32cap_P }; + + if is_x86_feature_detected!("aes") { + *l1ecx |= 1<<25; + } + if is_x86_feature_detected!("pclmulqdq") { + *l1ecx |= 1<<1; + } + if is_x86_feature_detected!("rdrand") { + *l1ecx |= 1<<30; + } + if is_x86_feature_detected!("rdseed") { + *l7ebx |= 1<<18; + } + if is_x86_feature_detected!("tsc") { + *l1edx |= 1<<4; + } + if is_x86_feature_detected!("mmx") { + *l1edx |= 1<<23; + } + if is_x86_feature_detected!("sse") { + *l1edx |= 1<<25; + } + if is_x86_feature_detected!("sse2") { + *l1edx |= 1<<26; + } + if is_x86_feature_detected!("sse3") { + *l1ecx |= 1<<0; + } + if is_x86_feature_detected!("ssse3") { + *l1ecx |= 1<<9; + } + if is_x86_feature_detected!("sse4.1") { + *l1ecx |= 1<<19; + } + if is_x86_feature_detected!("sse4.2") { + *l1ecx |= 1<<20; + } + if is_x86_feature_detected!("sha") { + *l7ebx |= 1<<29; + } + if is_x86_feature_detected!("avx") { + *l1ecx |= 1<<28; + } + if is_x86_feature_detected!("avx2") { + *l7ebx |= 1<<5; + } + if is_x86_feature_detected!("avx512f") { + *l7ebx |= 1<<16; + } + if is_x86_feature_detected!("avx512cd") { + *l7ebx |= 1<<28; + } + if is_x86_feature_detected!("avx512er") { + *l7ebx |= 1<<27; + } + if is_x86_feature_detected!("avx512pf") { + *l7ebx |= 1<<26; + } + if is_x86_feature_detected!("avx512bw") { + *l7ebx |= 1<<30; + } + if is_x86_feature_detected!("avx512dq") { + *l7ebx |= 1<<17; + } + if is_x86_feature_detected!("avx512vl") { + *l7ebx |= 1<<31; + } + if is_x86_feature_detected!("avx512ifma") { + *l7ebx |= 1<<21; + } + if is_x86_feature_detected!("avx512vbmi") { + *l7ecx |= 1<<1; + } + if is_x86_feature_detected!("avx512vpopcntdq") { + *l7ecx |= 1<<14; + } + if is_x86_feature_detected!("fma") { + *l1ecx |= 1<<12; + } + if is_x86_feature_detected!("bmi1") { + *l7ebx |= 1<<3; + } + if is_x86_feature_detected!("bmi2") { + *l7ebx |= 1<<8; + } + if is_x86_feature_detected!("popcnt") { + *l1ecx |= 1<<23; + } + if is_x86_feature_detected!("fxsr") { + *l1edx |= 1<<24; + } + if is_x86_feature_detected!("xsave") { + *l1ecx |= 1<<26; + } + /* will be stable on 1.33.0 + if is_x86_feature_detected!("cmpxchg16b") { + *l1ecx |= 1<<13; + } + if is_x86_feature_detected!("adx") { + *l7ebx |= 1<<19; + } + */ + + // Rust can't detect the MOVBE feature yet, but it's widely + // available. + *l1ecx |= 1<<22; + + // This bit is reserved in the CPUID specification, but the + // BoringSSL detection code uses it to represent that this + // is an Intel CPU. However, this bit is only used in + // conjunction with the AVX bit to test for presence of + // AVX, thus serving no purpose. Always set it. + *l1edx |= 1<<30; + + // Features that don't map to leaf 1 or leaf 7: + // Leaf 0xd: + // * xsaveopt + // * xsaves + // * xsavec + // Leaf 0x8000_0001: + // * sse4a + // * abm + // * lzcnt + // * tbm } - unsafe { - GFp_cpuid_setup(); + #[cfg(not(any(feature = "force_std_detection", all(target_env = "sgx" /*, target_vendor = "fortanix"*/))))] + { + extern "C" { + fn GFp_cpuid_setup(); + } + unsafe { + GFp_cpuid_setup(); + } } } diff --git a/src/lib.rs b/src/lib.rs index 62e956bba3..e2d6155ccd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,12 @@ //! dev_urandom_fallback feature is disabled, such //! fallbacks will not occur. See the documentation for //! rand::SystemRandom for more details. +//! force_std_detection +//! This is only applicable to x86. By default, ring will use +//! custom logic with the CPUID instruction to figure out which CPU +//! features are available. With this feature, the standard +//! std::is_x86_feature_detected macro will be used +//! instead. //! use_heap (default) //! Enable features that require use of the heap, RSA in particular. //! @@ -131,3 +137,41 @@ mod sealed { // ``` pub trait Sealed {} } + +/// # Information about using *ring* in SGX +/// +/// ## CPU feature detection +/// On `x86_64-fortanix-unknown-sgx`, feature detection is done using the +/// `std::is_x86_feature_detected` macro, which currently only supports +/// features enabled at compile-time. You must enable at least the `aes` and +/// `pclmul` features, otherwise *ring* will panic at runtime. See the [GitHub +/// issue](https://github.com/fortanix/rust-sgx/issues/26) for more +/// information. +/// +/// To set compile-time features, you can either specify them as an environment +/// variable: +/// +/// ```text +/// RUSTFLAGS="-C target-feature=+aes,+pclmul" +/// ``` +/// +/// Or you may configure them per target in [`.cargo/config`]. +/// +/// [`.cargo/config`]: https://doc.rust-lang.org/cargo/reference/config.html#configuration-keys +/// +/// ## Entropy source +/// The entropy source used in SGX is the hardware random number generator +/// provided by the RDRAND instruction. +/// +/// ## Nightly only +/// The `x86_64-fortanix-unknown-sgx` target is only available on nightly, and +/// *ring* Continuous Builds only build it for nightly. See the [GitHub +/// issue](https://github.com/briansmith/ring/issues/779) for more information. +/// +/// ## Continuous Testing +/// While the *ring* test suite works in SGX, and it is run manually from time +/// to time, it doesn't run automatically as part of a Continuous Testing +/// setup. See the [GitHub issue](https://github.com/briansmith/ring/issues/778) +/// for more information. +#[cfg(target_env = "sgx")] +pub mod sgx {} diff --git a/src/rand.rs b/src/rand.rs index b6fb6bc4ca..f6a7c24dae 100644 --- a/src/rand.rs +++ b/src/rand.rs @@ -60,6 +60,8 @@ pub trait SecureRandom: sealed::Sealed { /// On Windows, `fill` is implemented using the platform's API for secure /// random number generation. /// +/// On SGX, `fill()` is implemented using the `RDRAND` instruction. +/// /// Otherwise, `fill()` is implemented by reading from `/dev/urandom`. (This is /// something that should be improved for any platform that adds something /// better.) @@ -100,6 +102,7 @@ impl sealed::Sealed for SystemRandom {} target_os = "macos", target_os = "ios", target_os = "fuchsia", + target_env = "sgx", windows )) ))] @@ -120,6 +123,9 @@ use self::darwin::fill as fill_impl; #[cfg(any(target_os = "fuchsia"))] use self::fuchsia::fill as fill_impl; +#[cfg(target_env = "sgx")] +use self::rdrandom::fill as fill_impl; + use crate::sealed; #[cfg(target_os = "linux")] @@ -312,6 +318,36 @@ mod fuchsia { } } +#[cfg(all(target_env = "sgx", target_feature = "rdrand"))] +mod rdrandom { + use crate::{bssl, error}; + + fn rdrand_loop() -> Result<[u8; 8], error::Unspecified> { + extern "C" { + fn CRYPTO_rdrand(out: &mut [u8; 8]) -> bssl::Result; + } + + for _ in 0..10 { + let mut buf = [0u8; 8]; + match Result::from(unsafe { CRYPTO_rdrand(&mut buf) }) { + Ok(()) => return Ok(buf), + Err(_) => continue + } + } + Err(error::Unspecified) + } + + pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> { + for dst in dest.chunks_mut(8) { + let src = rdrand_loop()?; + let dst_len = dst.len(); + dst.copy_from_slice(&src[..dst_len]) + } + + Ok(()) + } +} + #[cfg(test)] mod tests { use crate::rand::{self, SecureRandom};