diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 128fb9ba3c1..74fd71db4df 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -64,7 +64,7 @@ jobs: uses: actions/cache@v2 env: # Increment this value to invalidate previous cache entries. - CACHE_VERSION: 3 + CACHE_VERSION: 4 with: path: | ./cargo-cache/bin diff --git a/experimental/uefi/app/.cargo/config.toml b/experimental/uefi/app/.cargo/config.toml index 79e4e493c16..0bb001dca52 100644 --- a/experimental/uefi/app/.cargo/config.toml +++ b/experimental/uefi/app/.cargo/config.toml @@ -2,7 +2,7 @@ target = "x86_64-unknown-uefi" [target.x86_64-unknown-uefi] -runner = "qemu-system-x86_64 -nodefaults -nographic -bios /usr/share/OVMF/OVMF_CODE.fd -serial file:target/console.log -serial stdio -machine q35 -device isa-debug-exit,iobase=0xf4,iosize=0x04 -kernel" +runner = "./runner" [unstable] build-std = ["core", "alloc"] diff --git a/experimental/uefi/app/Cargo.lock b/experimental/uefi/app/Cargo.lock index 84331add7f4..d423236b7e4 100644 --- a/experimental/uefi/app/Cargo.lock +++ b/experimental/uefi/app/Cargo.lock @@ -213,9 +213,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd5316aa8f5c82add416dfbc25116b84b748a21153f512917e8143640a71bbd" +checksum = "a07b0857a71a8cb765763950499cae2413c3f9cede1133478c43600d9e146890" dependencies = [ "bytes", "prost-derive", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328f9f29b82409216decb172d81e936415d21245befa79cd34c3f29d87d1c50b" +checksum = "120fbe7988713f39d780a58cf1a7ef0d7ef66c6d87e5aa3438940c05357929f4" dependencies = [ "bytes", "cfg-if", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df35198f0777b75e9ff669737c6da5136b59dba33cf5a010a6d1cc4d56defc6f" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" dependencies = [ "anyhow", "itertools", @@ -258,9 +258,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926681c118ae6e512a3ccefd4abbe5521a14f4cc1e207356d4d00c0b7f2006fd" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" dependencies = [ "bytes", "prost", @@ -425,6 +425,7 @@ dependencies = [ name = "uefi-simple" version = "0.1.0" dependencies = [ + "anyhow", "ciborium", "ciborium-io", "log", diff --git a/experimental/uefi/app/Cargo.toml b/experimental/uefi/app/Cargo.toml index f8a4c340d4b..a251d6b37a0 100644 --- a/experimental/uefi/app/Cargo.toml +++ b/experimental/uefi/app/Cargo.toml @@ -5,6 +5,10 @@ authors = ["Andri Saar "] edition = "2021" license = "Apache-2.0" +[features] +default = ["support_emulated_runner"] +support_emulated_runner = [] + [dependencies] uefi = { version = "*", features = ["exts"] } uefi-services = "*" @@ -14,4 +18,5 @@ ciborium-io = "*" oak_remote_attestation = { path = "../../../remote_attestation/rust" } [dev-dependencies] +anyhow = { version = "*", default-features = false } uefi-services = { version = "*", features = ["qemu"] } diff --git a/experimental/uefi/app/runner b/experimental/uefi/app/runner new file mode 100755 index 00000000000..50a0529bad1 --- /dev/null +++ b/experimental/uefi/app/runner @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Thin shell script invoked as a cargo runner to run the compiled efi firmware +# in QEMU. Detects if kvm is supported, and sets qemu flags based on that. +# Instead of this single runner script it would be preferable to use a different +# runner based on whether the kvm feature is set. However, cargo does not +# currently allow this. Ref: https://github.com/rust-lang/cargo/issues/8170 + +readonly TARGET=$1 + +qemu_flags=( + '-nodefaults' + '-nographic' + '-bios' '/usr/share/OVMF/OVMF_CODE.fd' + '-serial' 'file:target/console.log' + '-serial' 'stdio' + '-machine' 'q35' + '-device' 'isa-debug-exit,iobase=0xf4,iosize=0x04' +) + +# Use kvm if supported, as it is required for certain features. Note +# that hosts that support kvm still need to disable the default +# `support_emulated_runner` feature in cargo to include code that requires kvm +# in the compiled firmware. Ideally this check would rely on the cargo +# flag itself, enabling kvm if it is disabled. However cargo does not expose +# which flags are set to the runner. Ref: https://doc.rust-lang.org/cargo/reference/environment-variables.html +if [[ -e "/dev/kvm" ]]; then + qemu_flags+=( + '-enable-kvm' + '-cpu' 'Broadwell-IBRS' + ) +fi + +qemu-system-x86_64 "${qemu_flags[@]}" -kernel "${TARGET}" + diff --git a/experimental/uefi/app/src/main.rs b/experimental/uefi/app/src/main.rs index e0201ba3452..c46e0f00e8b 100644 --- a/experimental/uefi/app/src/main.rs +++ b/experimental/uefi/app/src/main.rs @@ -109,3 +109,6 @@ fn test_simple() { let x = 1; assert_eq!(x, 1); } + +#[cfg(test)] +mod tests; diff --git a/experimental/uefi/app/src/tests/mod.rs b/experimental/uefi/app/src/tests/mod.rs new file mode 100644 index 00000000000..5000bc72246 --- /dev/null +++ b/experimental/uefi/app/src/tests/mod.rs @@ -0,0 +1,20 @@ +// +// Copyright 2022 The Project Oak Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Uses advanced CPU features not available when using running tests in qemu +// with CPU emulation. +#[cfg(not(feature = "support_emulated_runner"))] +mod remote_attestation; diff --git a/experimental/uefi/app/src/tests/remote_attestation.rs b/experimental/uefi/app/src/tests/remote_attestation.rs new file mode 100644 index 00000000000..63bc1e473dd --- /dev/null +++ b/experimental/uefi/app/src/tests/remote_attestation.rs @@ -0,0 +1,110 @@ +// +// Copyright 2022 The Project Oak Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//! Integration Test of Remote Attestation in UEFI. +//! +//! This tests that remote attestion works inside the UEFI app. While the test +//! code is identical to (a subset of) the tests in the remote attestation crate +//! they here utilize the qemu runner configured in the UEFI app. This means +//! that test code actually compiled to a UEFI target, which changes the +//! underlying implementation of the remote attestation crate. +//! TODO(#2654): It would be preferable to remove the test here, and instead +//! run the tests in the oak_remote_attestation crate itself for both standard +//! and UEFI targets. Due to concerns related to the workspace this is presently +//! not possible. Ref: https://github.com/project-oak/oak/issues/2654 + +extern crate alloc; + +use alloc::{boxed::Box, sync::Arc}; +use oak_remote_attestation::handshaker::{AttestationBehavior, ClientHandshaker, ServerHandshaker}; + +const TEE_MEASUREMENT: &str = "Test TEE measurement"; +const DATA: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + +fn create_handshakers() -> (ClientHandshaker, ServerHandshaker) { + let bidirectional_attestation = + AttestationBehavior::create_bidirectional_attestation(&[], TEE_MEASUREMENT.as_bytes()) + .unwrap(); + let client_handshaker = ClientHandshaker::new( + bidirectional_attestation, + Box::new(|server_identity| { + if !server_identity.additional_info.is_empty() { + Ok(()) + } else { + anyhow::bail!("No additional info provided.") + } + }), + ); + + let bidirectional_attestation = + AttestationBehavior::create_bidirectional_attestation(&[], TEE_MEASUREMENT.as_bytes()) + .unwrap(); + + let additional_info = br"Additional Info".to_vec(); + let server_handshaker = + ServerHandshaker::new(bidirectional_attestation, Arc::new(additional_info)); + + (client_handshaker, server_handshaker) +} + +#[test_case] +fn test_handshake() { + let (mut client_handshaker, mut server_handshaker) = create_handshakers(); + + let client_hello = client_handshaker + .create_client_hello() + .expect("Couldn't create client hello message"); + + let server_identity = server_handshaker + .next_step(&client_hello) + .expect("Couldn't process client hello message") + .expect("Empty server identity message"); + + let client_identity = client_handshaker + .next_step(&server_identity) + .expect("Couldn't process server identity message") + .expect("Empty client identity message"); + assert!(client_handshaker.is_completed()); + + let result = server_handshaker + .next_step(&client_identity) + .expect("Couldn't process client identity message"); + assert_eq!(result, None); + assert!(server_handshaker.is_completed()); + + let mut client_encryptor = client_handshaker + .get_encryptor() + .expect("Couldn't get client encryptor"); + let mut server_encryptor = server_handshaker + .get_encryptor() + .expect("Couldn't get server encryptor"); + + let encrypted_client_data = client_encryptor + .encrypt(&DATA) + .expect("Couldn't encrypt client data"); + let decrypted_client_data = server_encryptor + .decrypt(&encrypted_client_data) + .expect("Couldn't decrypt client data"); + assert_eq!(decrypted_client_data, DATA); + + let encrypted_server_data = server_encryptor + .encrypt(&DATA) + .expect("Couldn't encrypt server data"); + let decrypted_server_data = client_encryptor + .decrypt(&encrypted_server_data) + .expect("Couldn't decrypt server data"); + assert_eq!(decrypted_server_data, DATA); +} diff --git a/remote_attestation/rust/Cargo.toml b/remote_attestation/rust/Cargo.toml index 87692263b70..f15064be569 100644 --- a/remote_attestation/rust/Cargo.toml +++ b/remote_attestation/rust/Cargo.toml @@ -14,7 +14,7 @@ anyhow = { version = "*", default-features = false } bytes = { version = "*", default-features = false } log = "*" prost = { version = "*", default-features = false, features = ["prost-derive"] } -ring = { path = "../../third_party/ring" } +ring = { path = "../../third_party/ring", default-features = false } [build-dependencies] prost-build = "*" diff --git a/third_party/ring/Cargo.toml b/third_party/ring/Cargo.toml index 81b800b51f9..05fedf0fbca 100644 --- a/third_party/ring/Cargo.toml +++ b/third_party/ring/Cargo.toml @@ -33,6 +33,8 @@ include = [ "benches/*.rs", "build.rs", + "stubs.c", + "crypto/chacha/asm/chacha-armv4.pl", "crypto/chacha/asm/chacha-armv8.pl", "crypto/chacha/asm/chacha-x86.pl", diff --git a/third_party/ring/build.rs b/third_party/ring/build.rs index 4e569b42f72..bf134fda63e 100644 --- a/third_party/ring/build.rs +++ b/third_party/ring/build.rs @@ -55,6 +55,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[ (&[X86], "crypto/chacha/asm/chacha-x86.pl"), (&[X86], "crypto/fipsmodule/modes/asm/ghash-x86.pl"), + (&[X86_64], "stubs.c"), (&[X86_64], "crypto/chacha/asm/chacha-x86_64.pl"), (&[X86_64], "crypto/fipsmodule/aes/asm/aesni-x86_64.pl"), (&[X86_64], "crypto/fipsmodule/aes/asm/vpaes-x86_64.pl"), diff --git a/third_party/ring/stubs.c b/third_party/ring/stubs.c new file mode 100644 index 00000000000..0141367c2be --- /dev/null +++ b/third_party/ring/stubs.c @@ -0,0 +1,30 @@ +#include + +/** + * + Stub function for win64 error handler API call inserted by nasm. + Stubbed as it is unavailable in UEFI. It appears unlikely this call will + ever be invoked in deployment. + Ref: https://github.com/openssl/openssl/issues/12712. + Inspired by: https://github.com/tianocore/edk2/blob/7c0ad2c33810ead45b7919f8f8d0e282dae52e71/CryptoPkg/Library/OpensslLib/X64/ApiHooks.c +**/ +void * +__imp_RtlVirtualUnwind ( + void *Args + ) +{ + return NULL; +} + +/** + Stub function for win64 routine used for exceedingly large variables. + Inserted nasm, stubbed as it is unavailable in UEFI. Given that this + routine is used for very large variable it appears unlikely to ever be + invoked in deployment. + Ref: https://github.com/golang/go/issues/6305 + Inspired by: https://android.googlesource.com/platform/external/compiler-rt/+/ccaafe6%5E%21/#F1 +**/ +void ___chkstk_ms(void) +{ +} + diff --git a/xtask/src/main.rs b/xtask/src/main.rs index bb3db6de03e..e8b3bea6470 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -585,6 +585,9 @@ fn run_clang_format(mode: FormatMode) -> Step { "--recursive", "--exclude", "*/node_modules", + // TODO(#2654): Remove once all crates are part of the same workspace again + "--exclude", + "*/target", "--exclude", "third_party", "oak_functions",