From 72a4fb0bb78bfb1e2ee1d777e6b70d600a604905 Mon Sep 17 00:00:00 2001 From: Dominic Mulligan Date: Tue, 2 Nov 2021 13:51:09 +0000 Subject: [PATCH] runtime-manager-linux: implemented Linux port of Veracruz using CA attestation - Linux Root Enclave implemented for co-ordinating spawning of application enclaves. Note most attestation-related material is actually handled by the Linux Root Enclave, rather than the application (runtime) enclave, as attestation for Linux is "fake"/insecure, and the Root enclave seems a more convenient place to put it. - Proxy attestation is using Derek's new CA-based attestation system. - New build targets of note: linux linux-veracruz-server-test linux-veracruz-client-test linux-veracruz-test linux-cli. - Added Linux buildspec to the CI configuration. - Minor rearrangement of material in veracruz-utils to accommodate Linux-related material. Note that Derek's recent changes, removing the Nitro root enclaves, has led to some duplication being reintroduced into veracruz-utils (owing to slight differences between the messages being sent to various enclaves in Linux/Nitro. When the Linux root enclave is removed (TODO) these duplications will be eliminated, streamlining this material. - Slight change in the naming of features in e.g. proxy-attestation-server to mark the fact that the TrustZone backend is now not the only backend using PSA attestation (even if it is "fake"). --- .github/workflows/main.yml | 68 +- Makefile | 49 +- io-utils/Cargo.toml | 14 +- io-utils/src/error.rs | 18 +- io-utils/src/fd.rs | 76 ++ io-utils/src/http.rs | 317 ++++++ io-utils/src/lib.rs | 16 +- io-utils/src/tcp.rs | 65 ++ linux-root-enclave/Cargo.toml | 31 + linux-root-enclave/Makefile | 22 + linux-root-enclave/src/main.rs | 928 ++++++++++++++++++ platform-services/Cargo.toml | 1 + .../src/sgx_platform_services.rs | 4 +- policy-utils/src/lib.rs | 5 + policy-utils/src/policy.rs | 12 + proxy-attestation-server/Cargo.toml | 6 +- .../build-proxy-attestation-server-optee.sh | 2 +- proxy-attestation-server/src/attestation.rs | 159 +-- .../src/attestation/nitro.rs | 40 +- .../src/attestation/psa.rs | 15 +- proxy-attestation-server/src/server.rs | 111 ++- proxy-attestation-server/src/test.rs | 106 +- psa-attestation/Cargo.toml | 2 + psa-attestation/build.rs | 2 + runtime-manager/Cargo.toml | 5 +- runtime-manager/Makefile | 21 +- runtime-manager/src/main.rs | 15 +- runtime-manager/src/managers/error.rs | 22 +- .../src/managers/execution_engine_manager.rs | 2 +- runtime-manager/src/managers/mod.rs | 11 + runtime-manager/src/runtime_manager.rs | 10 +- runtime-manager/src/runtime_manager_linux.rs | 252 +++++ runtime-manager/src/runtime_manager_nitro.rs | 27 +- sdk/freestanding-execution-engine/Cargo.toml | 2 +- test-collateral/Makefile | 8 +- test-collateral/generate-policy/Cargo.toml | 2 +- test-collateral/generate-policy/src/main.rs | 43 +- veracruz-client/Cargo.toml | 5 +- veracruz-client/src/veracruz_client.rs | 40 +- veracruz-server-test/Cargo.toml | 5 +- veracruz-server-test/src/main.rs | 373 ++++--- veracruz-server/Cargo.toml | 11 +- veracruz-server/Makefile | 84 -- veracruz-server/src/lib.rs | 4 + veracruz-server/src/server.rs | 4 +- veracruz-server/src/veracruz_server.rs | 114 +-- veracruz-server/src/veracruz_server_icecap.rs | 31 +- veracruz-server/src/veracruz_server_linux.rs | 753 ++++++++++++++ veracruz-server/src/veracruz_server_nitro.rs | 173 +--- veracruz-server/src/veracruz_server_sgx.rs | 31 +- veracruz-server/src/veracruz_server_tz.rs | 35 +- veracruz-test/Cargo.toml | 7 +- veracruz-test/src/main.rs | 27 +- veracruz-utils/Cargo.toml | 12 +- veracruz-utils/src/lib.rs | 9 +- veracruz-utils/src/platform/linux.rs | 75 ++ veracruz-utils/src/platform/mod.rs | 4 + veracruz-utils/src/platform/nitro/nitro.rs | 92 +- veracruz-utils/src/platform/vm.rs | 126 +++ 59 files changed, 3595 insertions(+), 909 deletions(-) create mode 100644 io-utils/src/fd.rs create mode 100644 io-utils/src/http.rs create mode 100644 io-utils/src/tcp.rs create mode 100644 linux-root-enclave/Cargo.toml create mode 100644 linux-root-enclave/Makefile create mode 100644 linux-root-enclave/src/main.rs create mode 100644 runtime-manager/src/runtime_manager_linux.rs delete mode 100644 veracruz-server/Makefile create mode 100644 veracruz-server/src/veracruz_server_linux.rs create mode 100644 veracruz-utils/src/platform/linux.rs create mode 100644 veracruz-utils/src/platform/vm.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 844f58eb7..e383203e1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,10 +14,10 @@ on: jobs: check-repo-and-compile-sdk: runs-on: ubuntu-latest - container: + container: image: ghcr.io/veracruz-project/veracruz/veracruz:ci - volumes: - - ${{ github.workspace }}:/work/veracruz + volumes: + - ${{ github.workspace }}:/work/veracruz steps: @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v2 with: submodules: recursive - + # Compile all examples and data sources in the sdk - name: Compile SDK id: sdk @@ -46,19 +46,19 @@ jobs: uses: actions/upload-artifact@v2 with: name: veracruz.tar - path: /veracruz.tar + path: /veracruz.tar if-no-files-found: error sgx: runs-on: ubuntu-latest - container: + container: image: ghcr.io/veracruz-project/veracruz/veracruz:ci - volumes: - - ${{ github.workspace }}:/work/veracruz + volumes: + - ${{ github.workspace }}:/work/veracruz needs: [check-repo-and-compile-sdk] steps: - + # Download the artifact containing repo and sdk artifact, using the action from github - name: Download veracruz cache artifact uses: actions/download-artifact@v2 @@ -73,7 +73,7 @@ jobs: mkdir -p /work/veracruz tar -C /work/veracruz -xvf veracruz.tar rm veracruz.tar - + # Run the sgx test - name: Running sgx test script id: sgx-test @@ -83,16 +83,17 @@ jobs: make sgx-veracruz-server-test-dry-run make sgx-veracruz-test-dry-run make sgx-veracruz-client-test + tz: runs-on: ubuntu-latest - container: + container: image: ghcr.io/veracruz-project/veracruz/veracruz:ci - volumes: - - ${{ github.workspace }}:/work/veracruz + volumes: + - ${{ github.workspace }}:/work/veracruz needs: [check-repo-and-compile-sdk] steps: - + # Download the artifact containing repo and sdk artifact, using the action from github - name: Download veracruz cache artifact uses: actions/download-artifact@v2 @@ -107,7 +108,7 @@ jobs: mkdir -p /work/veracruz tar -C /work/veracruz -xvf veracruz.tar rm veracruz.tar - + - name: Running trustzone test script id: tz-test run: | @@ -119,6 +120,39 @@ jobs: make trustzone-veracruz-test make trustzone-veracruz-client-test + linux: + runs-on: ubuntu-latest + container: + image: ghcr.io/veracruz-project/veracruz/veracruz:ci + volumes: + - ${{ github.workspace }}:/work/veracruz + needs: [check-repo-and-compile-sdk] + steps: + # Download the artifact containing repo and sdk artifact, using the action from github + - name: Download veracruz cache artifact + uses: actions/download-artifact@v2 + with: + name: veracruz.tar + path: / + # Unpack + - name: Unpack veracruz cache artifact + id: tz-unpack + run: | + cd / + mkdir -p /work/veracruz + mv veracruz.tar /work/veracruz + cd /work/veracruz + tar -xvf veracruz.tar + rm veracruz.tar + - name: Running linux test script + id: linux-test + run: | + cd /work/veracruz + make linux + make linux-veracruz-server-test + make linux-veracruz-test + make linux-veracruz-client-test + icecap: runs-on: ubuntu-latest container: @@ -132,3 +166,7 @@ jobs: submodules: recursive - name: Build and test Veracruz on IceCap run: bash /work/veracruz/icecap/ci/run.sh + + + + diff --git a/Makefile b/Makefile index 42792959b..5e4810203 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,9 @@ # # See the `LICENSE_MIT.markdown` file in the Veracruz root directory for licensing # and copyright information. - -.PHONY: all sdk setup-githooks sgx-veracruz-client-test trustzone-veracruz-client-test nitro-veracruz-client-test sgx trustzone sgx-veracruz-server-test sgx-veracruz-server-performance sgx-veracruz-test sgx-psa-attestation tz-psa-attestationtrustzone-veracruz-server-test-setting trustzone-veracruz-test-setting trustzone-env sgx-env trustzone-test-env clean clean-cargo-lock fmt - +.PHONY: all sdk setup-githooks sgx-veracruz-client-test trustzone-veracruz-client-test nitro-veracruz-client-test sgx trustzone sgx-veracruz-server-test sgx-veracruz-server-performance sgx-veracruz-test sgx-psa-attestation tz-psa-attestationtrustzone-veracruz-server-test-setting trustzone-veracruz-test-setting trustzone-env sgx-env trustzone-test-env clean clean-cargo-lock fmt linux linux-veracruz-server-test linux-veracruz-server-test-dry-run linux-test-collateral linux-veracruz-client-test linux-veracruz-test-dry-run linux-veracruz-test linux-cli + WARNING_COLOR := "\e[1;33m" INFO_COLOR := "\e[1;32m" RESET_COLOR := "\e[0m" @@ -22,6 +21,7 @@ OPENSSL_INCLUDE_DIR ?= /usr/include/aarch64-linux-gnu OPENSSL_LIB_DIR ?= /usr/lib/aarch64-linux-gnu TRUSTZONE_C_INCLUDE_PATH ?= /usr/include/aarch64-linux-gnu:/usr/include NITRO_RUST_FLAG ?= "" +LINUX_RUST_FLAG ?= "" BIN_DIR ?= /usr/local/cargo/bin all: @@ -42,6 +42,9 @@ sgx-test-collateral: trustzone-test-collateral: TEE=tz $(MAKE) -C test-collateral +linux-test-collateral: + TEE=linux $(MAKE) -C test-collateral + # Test veracruz-client for sgx, due to the use of a mocked server with a fixed port, these tests must run in a single thread sgx-veracruz-client-test: sgx sgx-test-collateral cd veracruz-client && RUSTFLAGS=$(SGX_RUST_FLAG) cargo test --lib --features "mock sgx" -- --test-threads=1 @@ -53,6 +56,9 @@ trustzone-veracruz-client-test: trustzone trustzone-test-collateral nitro-veracruz-client-test: nitro nitro-test-collateral cd veracruz-client && cargo test --lib --features "mock" -- --test-threads=1 +linux-veracruz-client-test: linux linux-test-collateral + cd veracruz-client && cargo test --lib --features "mock linux" -- --test-threads=1 + nitro-test-collateral: TEE=nitro $(MAKE) -C test-collateral # Compile for sgx @@ -108,7 +114,7 @@ trustzone-cli: trustzone-env OPENSSL_INCLUDE_DIR=$(OPENSSL_INCLUDE_DIR) \ OPENSSL_LIB_DIR=$(OPENSSL_LIB_DIR) \ C_INCLUDE_PATH=$(TRUSTZONE_C_INCLUDE_PATH) \ - cargo build --target aarch64-unknown-linux-gnu --features psa --features cli + cargo build --target aarch64-unknown-linux-gnu --features tz --features cli cd veracruz-server && \ CC_aarch64_unknown_linux_gnu=$(AARCH64_GCC) \ OPENSSL_INCLUDE_DIR=$(OPENSSL_INCLUDE_DIR) \ @@ -153,6 +159,20 @@ veracruz-test/proxy-attestation-server.db: $(wildcard sgx-root-enclave/css.bin) veracruz-server-test/proxy-attestation-server.db: $(wildcard sgx-root-enclave/css.bin) cd veracruz-server-test && \ bash ../test-collateral/populate-test-database.sh +linux: sdk + pwd + RUSTFLAGS=$(LINUX_RUST_FLAG) $(MAKE) -C runtime-manager linux + RUSTFLAGS=$(LINUX_RUST_FLAG) $(MAKE) -C linux-root-enclave linux + +linux-cli: + # build CLIs in top-level crates + cd proxy-attestation-server && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo build --features linux --features cli + cd veracruz-server && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo build --features linux --features cli + cd veracruz-client && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo build --features linux --features cli + # build CLIs in the SDK/test-collateral + $(MAKE) -C sdk/freestanding-execution-engine + $(MAKE) -C sdk/wasm-checker + $(MAKE) -C test-collateral/generate-policy sgx-veracruz-server-test: sgx sgx-test-collateral veracruz-server-test/proxy-attestation-server.db cd veracruz-server-test \ @@ -209,6 +229,15 @@ nitro-veracruz-server-test: nitro nitro-test-collateral veracruz-server-test/pro cd veracruz-server-test \ && ./nitro-terminate.sh +linux-veracruz-server-test-dry-run: linux linux-test-collateral + cd veracruz-server-test \ + && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo test --features linux --no-run -- --nocapture + +linux-veracruz-server-test: linux linux-test-collateral veracruz-server-test/proxy-attestation-server.db + cd veracruz-server-test \ + && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo test --features linux -- --test-threads=1 --nocapture \ + && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo test test_debug --features linux -- --ignored --test-threads=1 + nitro-veracruz-server-test-dry-run: nitro nitro-test-collateral cd veracruz-server-test \ && RUSTFLAGS=$(NITRO_RUST_FLAG) cargo test --features sgx --no-run @@ -236,6 +265,14 @@ nitro-veracruz-test: nitro nitro-test-collateral veracruz-test/proxy-attestation nitro-psa-attestation: cd psa-attestation && cargo build --features nitro +linux-veracruz-test-dry-run: linux-test-collateral + cd veracruz-test \ + && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo test --features linux --no-run + +linux-veracruz-test: linux-test-collateral linux veracruz-test/proxy-attestation-server.db + cd veracruz-test \ + && RUSTFLAGS=$(LINUX_RUST_FLAG) cargo test --features linux -- --test-threads=1 + trustzone-env: unset CC rustup target add aarch64-unknown-linux-gnu arm-unknown-linux-gnueabihf @@ -257,14 +294,15 @@ clean: cd proxy-attestation-server && cargo clean cd session-manager && cargo clean cd veracruz-utils && cargo clean + cd veracruz-server && cargo clean cd veracruz-server-test && cargo clean cd veracruz-test && cargo clean && rm -f proxy-attestation-server.db $(MAKE) clean -C runtime-manager $(MAKE) clean -C sgx-root-enclave - $(MAKE) clean -C veracruz-server $(MAKE) clean -C test-collateral $(MAKE) clean -C trustzone-root-enclave $(MAKE) clean -C sdk + $(MAKE) clean -C linux-root-enclave rm -rf bin # NOTE: this target deletes ALL cargo.lock. @@ -304,4 +342,5 @@ fmt: cd veracruz-utils && cargo fmt cd trustzone-root-enclave && cargo fmt cd proxy-attestation-server && cargo fmt + cd linux-root-enclave && cargo fmt $(MAKE) -C sdk fmt diff --git a/io-utils/Cargo.toml b/io-utils/Cargo.toml index bd5c07181..075036898 100644 --- a/io-utils/Cargo.toml +++ b/io-utils/Cargo.toml @@ -6,12 +6,22 @@ edition = "2018" description = "Common IO-related code used by multiple Veracruz components." [features] -nitro = ["serde_json/std", "nix", "byteorder"] +sgx = ["base64/std"] +linux = ["bincode", "byteorder", "base64/std"] +icecap = ["serde/derive", "base64/std"] +nitro = ["serde_json/std", "base64/std", "nix", "byteorder"] +tz = ["base64/std"] [dependencies] -sgx_tstd = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +bincode = { git= "https://github.com/veracruz-project/bincode.git", branch= "veracruz", default-features = false, optional = true } serde = { git = "https://github.com/veracruz-project/serde.git", features=["derive"], branch = "veracruz" } serde_json = { git = "https://github.com/veracruz-project/json.git", branch = "veracruz", default-features = false } err-derive = "0.2" nix = { version = "0.15", optional = true } byteorder = { version = "1.3", optional = true } +log = "=0.4.13" +curl = "=0.4.35" +stringreader = "0.1" +transport-protocol = { path = "../transport-protocol" } +base64 = { version = "0.13.0", default-features = false, optional = true } + diff --git a/io-utils/src/error.rs b/io-utils/src/error.rs index 8bff58953..1b1e1dcf3 100644 --- a/io-utils/src/error.rs +++ b/io-utils/src/error.rs @@ -9,7 +9,11 @@ //! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for //! information on licensing and copyright. +#[cfg(feature = "linux")] +use bincode::Error as BincodeError; use err_derive::Error; +#[cfg(feature = "linux")] +use std::io::Error as IOError; //////////////////////////////////////////////////////////////////////////////// // Socket-related error types. @@ -18,7 +22,19 @@ use err_derive::Error; /// a enumerated type for Veracruz-specific io errors #[derive(Debug, Error)] pub enum SocketError { - /// An error was returned by nix + /// A Bincode-related (de)serialization error occurred. + #[cfg(feature = "linux")] + #[error( + display = "SocketError: a Bincode serialization error occurred: {:?}", + _0 + )] + BincodeError(BincodeError), + /// An IO error occurred when writing to e.g. an FD. + #[cfg(feature = "linux")] + #[error(display = "SocketError: an IO error occurred: {:?}.", _0)] + IOError(IOError), + /// An error was returned by the Unix libraries. + #[cfg(feature = "nitro")] #[error(display = "SocketError: a Unix error occurred: {:?}", _0)] NixError(#[error(source)] nix::Error), } diff --git a/io-utils/src/fd.rs b/io-utils/src/fd.rs new file mode 100644 index 000000000..062936d79 --- /dev/null +++ b/io-utils/src/fd.rs @@ -0,0 +1,76 @@ +//! Common file descriptor-related material +//! +//! # Authors +//! +//! The Veracruz Development Team. +//! +//! # Copyright and licensing +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for copyright +//! and licensing information. + +use byteorder::{ByteOrder, LittleEndian}; + +/// Sends a `buffer` of data (by first transmitting an encoded length followed by +/// the data proper) to the file descriptor `fd`. +pub fn send_buffer(mut fd: T, buffer: &[u8]) -> Result<(), std::io::Error> +where + T: std::io::Write, +{ + let len = buffer.len(); + + // 1: Encode the data length and send it. + { + let mut buff = [0u8; 9]; + LittleEndian::write_u64(&mut buff, len as u64); + + let mut sent_bytes = 0; + + while sent_bytes < 9 { + sent_bytes += fd.write(&buff[sent_bytes..9])?; + } + } + + // 2. Send the data proper. + { + let mut sent_bytes = 0; + + while sent_bytes < len { + sent_bytes += fd.write(&buffer[sent_bytes..len])?; + } + } + + Ok(()) +} + +/// Reads a buffer of data from a file descriptor `fd` by first reading a length +/// of data, followed by the data proper. +pub fn receive_buffer(mut fd: T) -> Result, std::io::Error> +where + T: std::io::Read, +{ + // 1. First read and decode the length of the data proper. + let length = { + let mut buff = [0u8; 9]; + let mut received_bytes = 0; + + while received_bytes < 9 { + received_bytes += fd.read(&mut buff[received_bytes..9])?; + } + + LittleEndian::read_u64(&buff) as usize + }; + + // 2. Next, read the data proper. + let mut buffer = vec![0u8; length]; + + { + let mut received_bytes = 0; + + while received_bytes < length { + received_bytes += fd.read(&mut buffer[received_bytes..length])?; + } + } + + Ok(buffer) +} diff --git a/io-utils/src/http.rs b/io-utils/src/http.rs new file mode 100644 index 000000000..16670a1e5 --- /dev/null +++ b/io-utils/src/http.rs @@ -0,0 +1,317 @@ +//! Common HTTP and proxy-attestation service-related functionality +//! +//! Provides material for posting buffers over HTTP, and for sending messages +//! to the proxy attestation service over a HTTP interface. +//! +//! # Authors +//! +//! The Veracruz Development Team. +//! +//! # Copyright and licensing +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for copyright +//! and licensing information. + +use curl::{ + easy::{Easy, List}, + Error as CurlError, +}; +use err_derive::Error; +use log::{error, info}; +use std::{io::Read, str::from_utf8, string::String, vec::Vec}; +use stringreader::StringReader; +use transport_protocol::{ + parse_proxy_attestation_server_response, parse_psa_attestation_init, + parse_sgx_attestation_init, serialize_start_msg, ProxyAttestationServerResponse, + TransportProtocolError, +}; + +/////////////////////////////////////////////////////////////////////////////// +// Errors. +/////////////////////////////////////////////////////////////////////////////// + +/// HTTP-related errors. +#[derive(Debug, Error)] +pub enum HttpError { + /// An error originating from Curl was raised. + #[error(display = "An error originating from Curl was raised: {}.", _0)] + CurlError(CurlError), + /// An unexpected HTTP status code was returned. + #[error(display = "An unexpected HTTP return code was returned.")] + HttpSuccess, + #[error( + display = "A transport protocol message could not be (de)serialized: {}.", + _0 + )] + SerializationError(TransportProtocolError), + #[error( + display = "A base64-encoded message could not be (de)serialized: {}.", + _0 + )] + Base64Error(base64::DecodeError), + #[error(display = "A transport protocol error occurred: {}.", _0)] + TransportProtocolError(TransportProtocolError), + #[error(display = "An attestation-related error occurred: {}.", _0)] + AttestationError(TransportProtocolError), + #[error(display = "The proxy attestation service issued an unexpected reply.")] + ProtocolError(ProxyAttestationServerResponse), +} + +/////////////////////////////////////////////////////////////////////////////// +// HTTP-related functionality. +/////////////////////////////////////////////////////////////////////////////// + +/// Sends an encoded `buffer` via HTTP to a server at `url`. Fails if the +/// Curl session fails for any reason, or if a non-success HTTP code is +/// returned. +pub fn post_buffer(url: U, buffer: B) -> Result +where + U: AsRef, + B: AsRef, +{ + let url = url.as_ref(); + let buffer = buffer.as_ref(); + + info!( + "Posting buffer {} ({} bytes) to {}.", + buffer, + buffer.len(), + url + ); + + let mut curl_request = Easy::new(); + + curl_request.url(url).map_err(|err| { + error!("Failed to set URL with Curl. Error produced: {:?}.", err); + + HttpError::CurlError(err) + })?; + + let mut headers = List::new(); + headers + .append("Content-Type: application/octet-stream") + .map_err(|err| { + error!( + "Failed to append `Content-Type` header. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + + curl_request.http_headers(headers).map_err(|err| { + error!( + "Failed to set HTTP headers with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + curl_request.post(true).map_err(|err| { + error!( + "Failed to set post field to `true` with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + curl_request + .post_field_size(buffer.len() as u64) + .map_err(|err| { + error!( + "Failed to set post field size with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + + let mut buffer_reader = StringReader::new(buffer); + let mut received_body = String::new(); + let mut received_header = String::new(); + + { + let mut transfer = curl_request.transfer(); + + transfer + .read_function(|buf| Ok(buffer_reader.read(buf).unwrap_or(0))) + .map_err(|err| { + error!( + "Failed to register read function with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + + transfer + .write_function(|buf| { + received_body.push_str(from_utf8(buf).expect({ + info!( + "Error converting data {:?} from UTF-8. Continuing with default value.", + buf + ); + + &format!("Error converting data {:?} from UTF-8.", buf) + })); + + Ok(buf.len()) + }) + .map_err(|err| { + error!( + "Failed to register write function with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + + info!("Received response body."); + + transfer + .header_function(|buf| { + received_header.push_str(from_utf8(buf).expect({ + info!( + "Error converting data {:?} from UTF-8. Continuing with default value.", + buf + ); + + &format!("Error converting data {:?} from UTF-8", buf) + })); + + true + }) + .map_err(|err| { + error!( + "Failed to register header function with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + + transfer.perform().map_err(|err| { + error!( + "Failed to perform data transfer with Curl. Error produced: {:?}.", + err + ); + + HttpError::CurlError(err) + })?; + } + + info!("Received response header: {}.", received_header); + + if !received_header.contains("HTTP/1.1 200 OK\r") { + return Err(HttpError::HttpSuccess); + } + + info!("Buffer successfully posted."); + + Ok(received_body) +} + +/////////////////////////////////////////////////////////////////////////////// +// Proxy-attestation server-related functionality. +/////////////////////////////////////////////////////////////////////////////// + +/// Sends the "Start" message to the Proxy Attestation Server via HTTP. +/// Returns a device ID and a generated challenge from the Proxy Attestation +/// Service, which is generated in response to the "Start" message, if the +/// message is successfully sent. +pub fn send_proxy_attestation_server_start( + proxy_attestation_server_url_base: U, + protocol_name: P, + firmware_version: F, +) -> Result<(i32, Vec), HttpError> +where + U: AsRef, + P: AsRef, + F: AsRef, +{ + let proxy_attestation_server_url_base = proxy_attestation_server_url_base.as_ref(); + let protocol_name = protocol_name.as_ref(); + let firmware_version = firmware_version.as_ref(); + + info!("Sending Start message to Proxy Attestation Service."); + + let start_msg = serialize_start_msg(protocol_name, firmware_version).map_err(|e| { + error!( + "Failed to serialize Start message. Error produced: {:?}.", + e + ); + + HttpError::SerializationError(e) + })?; + + let encoded_start_msg = base64::encode(&start_msg); + + let url = format!("{}/Start", proxy_attestation_server_url_base); + + let response = post_buffer(&url, &encoded_start_msg).map_err(|e| { + error!( + "Failed to send proxy attestation service start message. Error produced: {}.", + e + ); + + e + })?; + + info!("Response received from Proxy Attestation Service."); + + let response_body = base64::decode(&response).map_err(|e| { + error!( + "Failed to deserialize response from Proxy Attestation Service. Error produced: {:?}.", + e + ); + + HttpError::Base64Error(e) + })?; + + let response = parse_proxy_attestation_server_response(&response_body).map_err(|e| { + error!("Failed to parse response to Start message from Proxy Attestation Service. Error produced: {:?}.", e); + + HttpError::TransportProtocolError(e) + })?; + + info!("Response successfully parsed."); + + #[cfg(any( + feature = "linux", + feature = "tz", + feature = "nitro", + feature = "icecap" + ))] + if response.has_psa_attestation_init() { + let (challenge, device_id) = + parse_psa_attestation_init(response.get_psa_attestation_init()).map_err(|e| { + error!( + "Failed to parse PSA attestation initialization message. Error produced: {:?}.", + e + ); + + HttpError::AttestationError(e) + })?; + + info!("Device ID and challenge successfully obtained from Proxy Attestation Service."); + + Ok((device_id, challenge)) + } else { + error!("Unexpected response from Proxy Attestation Service. Expecting PSA attestation initialization message."); + + Err(HttpError::ProtocolError(response)) + } + #[cfg(feature = "sgx")] + if response.has_sgx_attestation_init() { + let (challenge, device_id) = + parse_sgx_attestation_init(response.get_sgx_attestation_init()); + + info!("Device ID and challenge successfully obtained from Proxy Attestation Service."); + + Ok((device_id, challenge)) + } else { + error!("Unexpected response from Proxy Attestation Service. Expecting SGX attestation initialization message."); + + Err(HttpError::ProtocolError(response)) + } +} diff --git a/io-utils/src/lib.rs b/io-utils/src/lib.rs index cfec934f2..801913d5d 100644 --- a/io-utils/src/lib.rs +++ b/io-utils/src/lib.rs @@ -14,13 +14,27 @@ //! information on licensing and copyright. /// IO-related error type. -#[cfg(feature = "nitro")] pub mod error; +#[cfg(any(feature = "nitro", feature = "linux"))] +/// FD-related material. +pub mod fd; +/// HTTP-related material. +#[cfg(any( + feature = "nitro", + feature = "linux", + feature = "sgx", + feature = "icecap", + feature = "tz" +))] +pub mod http; #[cfg(feature = "nitro")] pub mod nitro; /// Buffer send- and receive-related functionality for raw file descriptors. #[cfg(feature = "nitro")] pub mod raw_fd; +#[cfg(feature = "linux")] +/// TCP-socket related material. +pub mod tcp; /// A Nitro-specific abstraction over sockets. #[cfg(feature = "nitro")] pub mod vsocket; diff --git a/io-utils/src/tcp.rs b/io-utils/src/tcp.rs new file mode 100644 index 000000000..5fa36a245 --- /dev/null +++ b/io-utils/src/tcp.rs @@ -0,0 +1,65 @@ +//! Common TCP socket-related functionality +//! +//! # Authors +//! +//! The Veracruz Development Team. +//! +//! # Copyright and licensing +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for copyright +//! and licensing information. + +use super::{ + error::SocketError, + fd::{receive_buffer, send_buffer}, +}; +use bincode::{deserialize, serialize}; +use log::error; +use serde::{de::DeserializeOwned, Serialize}; +use std::net::TcpStream; + +/// Transmits a serialized message, `data`, via a socket. +/// +/// Fails if the message cannot be serialized, or if the serialized message +/// cannot be transmitted. +pub fn send_message(socket: &mut TcpStream, data: T) -> Result<(), SocketError> +where + T: Serialize, +{ + let message = serialize(&data).map_err(|e| { + error!("Failed to serialize message. Error produced: {}.", e); + + SocketError::BincodeError(e) + })?; + + send_buffer(socket, &message).map_err(|e| { + error!("Failed to transmit message. Error produced: {}.", e); + + SocketError::IOError(e) + })?; + + Ok(()) +} + +/// Receives and deserializes a message via a socket. +/// +/// Fails if no message can be received, or if the received message cannot be +/// deserialized. +pub fn receive_message(socket: &mut TcpStream) -> Result +where + T: DeserializeOwned, +{ + let response = receive_buffer(socket).map_err(|e| { + error!("Failed to receive response. Error produced: {}.", e); + + SocketError::IOError(e) + })?; + + let message: T = deserialize(&response).map_err(|e| { + error!("Failed to deserialize response. Error produced: {}.", e); + + SocketError::BincodeError(e) + })?; + + Ok(message) +} diff --git a/linux-root-enclave/Cargo.toml b/linux-root-enclave/Cargo.toml new file mode 100644 index 000000000..41852c643 --- /dev/null +++ b/linux-root-enclave/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "linux-root-enclave" +version = "0.3.0" +authors = ["The Veracruz Development Team"] +edition = "2018" +description = "The root enclave (read: application) for Veracruz when used as a Linux process." + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +base64 = "0.13.0" +bincode = { git= "https://github.com/veracruz-project/bincode.git", branch= "veracruz", default-features = false } +clap = "2.33" +env_logger = "0.8.3" +err-derive = "0.3.0" +hex = "0.4.2" +io-utils = {path = "../io-utils", features = ["linux"]} +lazy_static = "1.4.0" +log = "=0.4.13" +nix = "0.20.0" +net2 = "0.2.37" +psa-attestation = {path = "../psa-attestation", features = ["linux"]} +ring = {git = "https://github.com/veracruz-project/ring.git", branch = "veracruz", features = ["non_sgx"] } +serde = { git = "https://github.com/veracruz-project/serde.git", features=["derive"], branch = "veracruz" } +transport-protocol = { path = "../transport-protocol" } +veracruz-utils = {path = "../veracruz-utils/", features = ["linux"]} + +[profile.release] +opt-level = 3 +codegen-units = 1 +lto = true diff --git a/linux-root-enclave/Makefile b/linux-root-enclave/Makefile new file mode 100644 index 000000000..437e7bc66 --- /dev/null +++ b/linux-root-enclave/Makefile @@ -0,0 +1,22 @@ +# Makefile for the Linux root enclave (application). +# +# AUTHORS +# +# The Veracruz Development Team. +# +# COPYRIGHT +# +# See the `LICENSE_MIT.markdown` file in the Veracruz root directory for licensing +# and copyright information. + +.PHONY: linux fmt clean + +linux: + cargo build --release + +fmt: + cargo fmt + +clean: + cargo clean + $(RM) -f Cargo.lock diff --git a/linux-root-enclave/src/main.rs b/linux-root-enclave/src/main.rs new file mode 100644 index 000000000..b7a1f409a --- /dev/null +++ b/linux-root-enclave/src/main.rs @@ -0,0 +1,928 @@ +//! The root enclave (read: application) for Linux +//! +//! Interprets command messages sent over a TCP socket, acts on them, then sends +//! responses back. Command messages consist of: +//! +//! - Requests to shutdown the root enclave, which terminates the listening +//! loop, +//! - Requests to obtain the hash of the Linux root enclave server, +//! - Requests for proxy and native attestation tokens, +//! - A **hack** message which sets the hash of the runtime manager enclave to +//! a given value for attestation purposes. This is because the operating +//! system (Linux in this case) provides no way of reliably measuring a +//! loaded program. +//! +//! **NOTE**: the attestation flow defined in this file is completely insecure, +//! and can probably never be made really secure. +//! +//! As a result, we've cut a few corners implementing this (e.g. with the +//! pre-generated `LINUX_ROOT_ENCLAVE_PRIVATE_KEY` embedded in the source below) +//! which need fixing if they are to be used in a security-sensitive setting. +//! +//! See the comparable Intel SGX or AWS Nitro flows for a secure and reliable +//! implementation of attestation for Veracruz. +//! +//! # Authors +//! +//! The Veracruz Development Team. +//! +//! # Copyright and licensing +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for copyright +//! and licensing information. + +use bincode::{deserialize, serialize, Error as BincodeError}; +use clap::{App, Arg}; +use env_logger; +use err_derive::Error; +use hex::encode; +use io_utils::{ + fd::{receive_buffer, send_buffer}, + http::{post_buffer, send_proxy_attestation_server_start, HttpError}, +}; +use lazy_static::lazy_static; +use log::{error, info}; +use net2::{unix::UnixTcpBuilderExt, TcpBuilder}; +use psa_attestation::{psa_initial_attest_get_token, psa_initial_attest_load_key}; +use ring::{ + digest::{digest, SHA256}, + rand::{SecureRandom, SystemRandom}, + signature::{EcdsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING}, +}; +use std::{ + collections::HashMap, + fs::File, + io::{Error as IOError, Read}, + path::Path, + process::{exit, Child, Command}, + sync::{ + atomic::{AtomicI32, AtomicU32, Ordering}, + Mutex, + }, + thread::sleep, + time::Duration, +}; +use transport_protocol::{ + parse_proxy_attestation_server_response, serialize_native_psa_attestation_token, +}; +use veracruz_utils::{ + csr::{ + convert_csr_to_cert, generate_csr, COMPUTE_ENCLAVE_CERT_TEMPLATE, ROOT_ENCLAVE_CSR_TEMPLATE, + }, + platform::linux::{LinuxRootEnclaveMessage, LinuxRootEnclaveResponse}, +}; + +//////////////////////////////////////////////////////////////////////////////// +// Constants. +//////////////////////////////////////////////////////////////////////////////// + +/// Incoming address to listen on. Note that "0.0.0.0" means that we listen on +/// all addresses. +const INCOMING_ADDRESS: &'static str = "0.0.0.0"; +/// Incoming port to listen on. +const INCOMING_PORT: &'static str = "5021"; +/// Socket backlog for incoming connections. +const SOCKET_BACKLOG: i32 = 127; +/// The "about" string for the command-line interface. +const LINUX_ROOT_ENCLAVE_ABOUT: &'static str = "Root Enclave for Veracruz as a Linux process."; +/// Author information for the command-line interface. +const LINUX_ROOT_ENCLAVE_AUTHORS: &'static str = "The Veracruz Development Team."; +/// Path to the Runtime Manager binary. +const RUNTIME_MANAGER_ENCLAVE_PATH: &'static str = + "../runtime-manager/target/release/runtime_manager_enclave"; +/// The attestation protocol to use when communicating with the Proxy Attestation Server. +const PROXY_ATTESTATION_PROTOCOL: &'static str = "psa"; +/// Seconds to wait after spawning an enclave before proceeding. +const ENCLAVE_SPAWN_DELAY: u64 = 2; + +lazy_static! { + /// A private-key randomly generated when the Linux root enclave + /// initializes. + static ref DEVICE_PRIVATE_KEY: Mutex>> = Mutex::new(None); + /// The ID assigned to this device. + static ref DEVICE_ID: Mutex> = Mutex::new(None); + /// NOTE: this is hardcoded into the root enclave binary, which is + /// completely insecure. A better way of doing this would be to generate a + /// key at initialization time and share this with the proxy attestation + /// service. However, this Linux flow is a dummy attestation flow that has + /// limited value, anyway, given that Linux processes are not secured + /// against a malicious operating system. We therefore use this approach + /// instead, at least for the time being. + static ref LINUX_ROOT_ENCLAVE_PRIVATE_KEY: Vec = vec![ + 0xe6, 0xbf, 0x1e, 0x3d, 0xb4, 0x45, 0x42, 0xbe, 0xf5, 0x35, 0xe7, 0xac, 0xbc, 0x2d, 0x54, 0xd0, + 0xba, 0x94, 0xbf, 0xb5, 0x47, 0x67, 0x2c, 0x31, 0xc1, 0xd4, 0xee, 0x1c, 0x5, 0x76, 0xa1, 0x44, + ]; + /// Note that this should be measured at runtime, but we have no real way to + /// do this at present. + static ref LINUX_ROOT_ENCLAVE_MEASUREMENT: Vec = vec![222, 173, 190, 239, 222, 173, 190, 239, + 222, 173, 190, 239, 222, 173, 190, 239, 240, 13, 202, 254, 240, 13, 202, 254, 240, 13, 202, + 254, 240, 13, 202, 254 + ]; + /// Handles to all of the processes of the enclaves launched by the root + /// enclave. + static ref LAUNCHED_ENCLAVES: Mutex> = Mutex::new(Vec::new()); + /// A monotonically-increasing counter to keep track of which challenge IDs + /// were sent to which compute enclaves. + static ref CHALLENGE_ID: AtomicI32 = AtomicI32::new(0); + /// Mutex to hold the certificate chain provided by the Proxy Attestation + /// Service after a successful native attestation. + static ref CERTIFICATE_CHAIN: Mutex, Vec)>> = Mutex::new(None); + /// A hash map for storing challenge values associated with their IDs. + static ref CHALLENGE_HASHES: Mutex>> = + Mutex::new(HashMap::new()); + /// The next port to use to communicate with a newly-launched compute + /// enclave. + static ref ENCLAVE_PORT: AtomicU32 = AtomicU32::new(6000); +} + +//////////////////////////////////////////////////////////////////////////////// +// Errors. +//////////////////////////////////////////////////////////////////////////////// + +/// Captures all of the different errors that can be produced when trying to +/// listen on, and subsequently process, all of the root enclave messages. +#[derive(Debug, Error)] +enum LinuxRootEnclaveError { + #[error(display = "PSA attestation process failed.")] + /// Some aspect of the attestation process failed to complete correctly. + AttestationError, + #[error(display = "Base 64 decoding error.")] + /// There was an error deserializing a message from Base 64 encoding. + Base64Error, + #[error(display = "Cryptography key generation process failed.")] + /// Some aspect of the key generation process failed to complete correctly. + CryptographyError, + /// Bincode failed to serialize or deserialize a message or response. + #[error( + display = "Failed to serialize or deserialize a message or response. Error produced: {}.", + _0 + )] + BincodeError(BincodeError), + #[error( + display = "General IO error when reading or writing files. Error produced: {}.", + _0 + )] + /// There was an error related to the reading or writing of files needed by + /// the root enclave. + GeneralIOError(IOError), + #[error(display = "HTTP error: {}.", _0)] + /// A HTTP session raised an error. + HttpError(HttpError), + #[error(display = "An internal invariant failed.")] + /// An internal invariant failed, i.e. something that was not initialized that + /// should have been. + InvariantFailed, + #[error(display = "A lock on a global object could not be obtained.")] + /// A lock on a global object could not be obtained. + LockingError, + #[error(display = "Socket IO error. Error produced: {}.", _0)] + /// There was an error either opening, or working with, sockets. + SocketError(IOError), + #[error(display = "Transport Protocol (de)serialization error.")] + /// There was an error in serializing or deserializing a transport protocol + /// message. + TransportProtocolError, +} + +//////////////////////////////////////////////////////////////////////////////// +// Measurement. +//////////////////////////////////////////////////////////////////////////////// + +/// Computes a measurement, using SHA-256, of the binary at `path`. Fails if +/// the binary at the path cannot be read. +fn measure_binary(path: T) -> Result, LinuxRootEnclaveError> +where + T: AsRef, +{ + let path = path.as_ref(); + + info!("Computing measurement of binary: {:?}.", path); + + let mut file = File::open(path).map_err(|e| { + error!("Failed to open binary {:?}. Error produced: {}.", path, e); + + LinuxRootEnclaveError::GeneralIOError(e) + })?; + + let mut buffer = Vec::new(); + let length = file.read_to_end(&mut buffer).map_err(|e| { + error!( + "Failed to read contents of binary {:?}. Error produced: {}.", + path, e + ); + + LinuxRootEnclaveError::GeneralIOError(e) + })?; + + info!("Read {:?} bytes from binary.", length); + + let measurement = digest(&SHA256, &buffer).as_ref().to_vec(); + + info!("Measurement computed: {}.", encode(&measurement)); + + Ok(measurement) +} + +/// Returns the measurement of the Runtime Manager binary. +#[inline] +fn get_runtime_manager_hash() -> Result, LinuxRootEnclaveError> { + measure_binary(RUNTIME_MANAGER_ENCLAVE_PATH) +} + +//////////////////////////////////////////////////////////////////////////////// +// Proxy Attestation Service interaction. +//////////////////////////////////////////////////////////////////////////////// + +fn post_native_attestation_token( + proxy_attestation_server_base_url: &str, + token_buffer: &[u8], + csr: &[u8], + device_id: i32, +) -> Result<(Vec, Vec), LinuxRootEnclaveError> { + info!( + "Sending native attestation token ({} bytes) to Proxy Attestation Service.", + token_buffer.len() + ); + + let serialized_attestation_token = + serialize_native_psa_attestation_token(token_buffer, csr, device_id).map_err(|e| { + error!( + "Failed to serialize native PSA Attestation Token message. Error produced: {:?}.", + e + ); + + LinuxRootEnclaveError::TransportProtocolError + })?; + + let encoded_serialize_attestion_token = base64::encode(&serialized_attestation_token); + + let url = format!("{}/PSA/AttestationToken", proxy_attestation_server_base_url,); + + let received_buffer = post_buffer(&url, &encoded_serialize_attestion_token).map_err(|e| { + error!( + "Failed to transmit native PSA attestation token. Error produced: {:?}.", + e + ); + + LinuxRootEnclaveError::HttpError(e) + })?; + + let received_body = base64::decode(&received_buffer).map_err(|e| { + error!("Failed to Base 64 deserialize response from Proxy Attestation Service. Error produced: {:?}.", e); + + LinuxRootEnclaveError::Base64Error + })?; + + let response = parse_proxy_attestation_server_response(&received_body).map_err(|e| { + error!( + "Failed to deserialize response from Proxy Attestation Service. Error produced: {:?}.", + e + ); + + LinuxRootEnclaveError::TransportProtocolError + })?; + + if response.has_cert_chain() { + let certificate_chain = response.get_cert_chain(); + + info!("Certificate chain received from Proxy Attestation Service."); + + Ok(( + certificate_chain.get_enclave_cert().to_vec(), + certificate_chain.get_root_cert().to_vec(), + )) + } else { + error!("Unexpected response from Proxy Attestation Service."); + + Err(LinuxRootEnclaveError::AttestationError) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Responses to message stimuli. +//////////////////////////////////////////////////////////////////////////////// + +/// Launches a new instance of the Runtime Manager enclave. Assigns a fresh +/// port number to the enclave and returns it if the enclave is successfully +/// launched. Returns `Err(err)` with a suitable error if the Runtime Manager +/// enclave cannot be launched, or if the internal database of launched enclaves +/// cannot be locked. +fn launch_new_runtime_manager_enclave() -> Result { + info!("Launching new Runtime Manager enclave."); + + let port = ENCLAVE_PORT.fetch_add(1u32, Ordering::SeqCst); + + info!("Assigned port {} to new enclave.", port); + + let command = Command::new(RUNTIME_MANAGER_ENCLAVE_PATH) + .arg(format!("--port={}", port)) + .spawn() + .map_err(|e| { + error!( + "Failed to launch Runtime Manager enclave ({}). Error produced: {}.", + RUNTIME_MANAGER_ENCLAVE_PATH, e + ); + + LinuxRootEnclaveError::GeneralIOError(e) + })?; + + info!( + "New Runtime Manager enclave launched. Sleeping {} seconds...", + ENCLAVE_SPAWN_DELAY + ); + + sleep(Duration::from_secs(ENCLAVE_SPAWN_DELAY)); + + info!("Registering new enclave."); + + let mut children = LAUNCHED_ENCLAVES.lock().map_err(|e| { + error!( + "Failed to obtain lock on LAUNCHED_ENCLAVES. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + children.push(command); + + Ok(port) +} + +/// Kills all of the enclaves that the Linux root enclave has spawned. If any +/// process cannot be killed then this is logged on the error logger but no +/// further error is produced as we are in the process of exiting when this +/// function is called, anyway. +fn kill_all_enclaves() -> Result<(), LinuxRootEnclaveError> { + info!("Killing all launched Runtime Manager enclaves."); + + let mut children = LAUNCHED_ENCLAVES.lock().map_err(|e| { + error!( + "Failed to obtain lock on LAUNCHED_ENCLAVES. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + for child in children.iter_mut() { + info!("Killing process {}.", child.id()); + + let _result = child.kill().map_err(|_e| { + error!("Failed to kill process {}.", child.id()); + }); + } + + Ok(()) +} + +/// Returns the version of the trusted runtime's software stack. Note that on +/// Linux this is mocked up, as the attestation process is completely insecure. +#[inline] +fn get_firmware_version() -> String { + String::from(env!("CARGO_PKG_VERSION")) +} + +/// Produces a fresh 16-byte challenge value, indexed by a new challenge ID, and +/// stashes them in the `CHALLENGE_ID` table before returning them. +fn start_proxy_attestation() -> Result<(Vec, i32), LinuxRootEnclaveError> { + let challenge_id = CHALLENGE_ID.fetch_add(1, Ordering::SeqCst); + + info!("Fresh challenge ID generated: {}. ", challenge_id); + + let mut buffer = vec![0u8; 16]; + let rng = SystemRandom::new(); + + rng.fill(&mut buffer).map_err(|_e| { + error!("Failed to produced 16 bytes of random data for challenge."); + + LinuxRootEnclaveError::CryptographyError + })?; + + info!("Challenge generated: {:?}.", buffer); + + let mut challenge_hash_lock = CHALLENGE_HASHES.lock().map_err(|e| { + error!( + "Failed to obtain lock on CHALLENGE_HASHES. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + challenge_hash_lock.insert(challenge_id.clone(), buffer.clone()); + + info!("Fresh challenge and challenge ID generated."); + + Ok((buffer, challenge_id)) +} + +/// Caches the root enclave certificate and the root CA certificate for use +/// later in the attestation process. +fn install_certificate_chain( + root_enclave_certificate: Vec, + root_certificate: Vec, +) -> Result<(), LinuxRootEnclaveError> { + let mut certificate_chain_lock = CERTIFICATE_CHAIN.lock().map_err(|e| { + error!( + "Failed to obtain lock on CERTIFICATE_CHAIN. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + *certificate_chain_lock = Some((root_enclave_certificate, root_certificate)); + + info!("Certificate chain installed."); + + Ok(()) +} + +/// Generates the device private key pair over `device_private_key`. Fails with +/// a cryptography error if this is not possible. +fn generate_device_private_key_pair( + device_private_key: &[u8], +) -> Result { + Ok( + EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, device_private_key).map_err( + |e| { + error!( + "Failed to generate device private key pair. Error produced: {:?}.", + e + ); + + LinuxRootEnclaveError::CryptographyError + }, + )?, + ) +} + +/// Computes a native PSA attestation token from a Certificate Signing Request and +/// registers this with the Proxy Attestation Service. Registers the resulting +/// certificates returned from the Proxy Attestation Service. +fn native_attestation( + proxy_attestation_server_base_url: &str, +) -> Result<(), LinuxRootEnclaveError> { + /* 0. Load the root enclave private key. */ + + let mut root_enclave_key_handle = 0; + + if 0 != unsafe { + psa_initial_attest_load_key( + LINUX_ROOT_ENCLAVE_PRIVATE_KEY.as_ptr(), + LINUX_ROOT_ENCLAVE_PRIVATE_KEY.len() as u64, + &mut root_enclave_key_handle, + ) + } { + error!("Failed to load LINUX_ROOT_ENCLAVE_PRIVATE_KEY."); + + return Err(LinuxRootEnclaveError::CryptographyError); + } + + /* 1. Get the firmware version. */ + + let firmware_version = get_firmware_version(); + + /* 2. Send the Start message to the Proxy Attestation Service to obtain a device ID and + * challenge. + */ + + info!("Initializing Proxy Attestation Service."); + + let (device_id, challenge) = send_proxy_attestation_server_start( + proxy_attestation_server_base_url, + PROXY_ATTESTATION_PROTOCOL, + &firmware_version, + ) + .map_err(|e| { + error!( + "Failed to send proxy attestation service Start message. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::HttpError(e) + })?; + + /* 2. Save the Device ID. */ + + let mut device_id_lock = DEVICE_ID.lock().map_err(|e| { + error!( + "Failed to obtain lock on DEVICE_ID. Error produced: {}.", + e + ); + LinuxRootEnclaveError::LockingError + })?; + + *device_id_lock = Some(device_id.clone()); + + info!("Device ID {} saved.", device_id); + + /* 3. Lock the device private key. */ + + let device_private_key_lock = DEVICE_PRIVATE_KEY.lock().map_err(|e| { + error!( + "Failed to obtain lock on DEVICE_PRIVATE_KEY. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + let device_private_key = match &*device_private_key_lock { + Some(dpk) => dpk.clone(), + _otherwise => { + error!("Device private key not initialized."); + + return Err(LinuxRootEnclaveError::InvariantFailed); + } + }; + + info!("Generating device private key pair."); + + let device_private_key_pair = generate_device_private_key_pair(&device_private_key)?; + + /* 4. Compute the Root Enclave CSR. */ + + info!("Generating Root Enclave CSR."); + + let csr = generate_csr(&ROOT_ENCLAVE_CSR_TEMPLATE, &device_private_key_pair).map_err(|e| { + error!( + "Failed to generate Root Enclave CSR. Error produced: {:?}.", + e + ); + + LinuxRootEnclaveError::AttestationError + })?; + + /* 5. Compute the attestation token. */ + + let csr_hash = digest(&SHA256, &csr).as_ref().to_vec(); + + info!("CSR hash computed: {:?}.", csr_hash); + + let mut token_buffer = Vec::with_capacity(1024); + let mut token_size = 0u64; + + if 0 != unsafe { + psa_initial_attest_get_token( + LINUX_ROOT_ENCLAVE_MEASUREMENT.as_ptr() as *const u8, + LINUX_ROOT_ENCLAVE_MEASUREMENT.len() as u64, + csr_hash.as_ptr() as *const u8, + csr_hash.len() as u64, + std::ptr::null() as *const i8, + 0, + challenge.as_ptr() as *const u8, + challenge.len() as u64, + token_buffer.as_mut_ptr() as *mut u8, + token_buffer.capacity() as u64, + &mut token_size as *mut u64, + ) + } { + error!("Failed to produce native PSA attestation token."); + return Err(LinuxRootEnclaveError::CryptographyError); + } + + unsafe { + token_buffer.set_len(token_size as usize); + } + + info!("Native PSA attestation token successfully produced."); + + /* 6. Send the attestation token to the Proxy Attestation Service, receiving the certificates + * in response. + */ + + info!("Sending native attesation token to Proxy Attestation Service."); + + let (root_enclave_certificate, ca_certificate) = post_native_attestation_token( + proxy_attestation_server_base_url, + &token_buffer, + &csr, + device_id, + )?; + + info!("Root Enclave Certificate and CA Certificate received from Proxy Attestation Service."); + + /* 7. Install the certificate chain for use later. */ + + let _result = install_certificate_chain(root_enclave_certificate, ca_certificate)?; + + info!("Certificates successfully installed."); + + Ok(()) +} + +/// Computes a proxy attestation certificate chain from a Certificate Signing +/// Request, `csr`, and a challenge index, `challenge_id`. +fn get_proxy_attestation_certificate_chain( + csr: Vec, + challenge_id: i32, +) -> Result<(Vec, Vec, Vec), LinuxRootEnclaveError> { + let mut challenge_hashes_lock = CHALLENGE_HASHES.lock().map_err(|e| { + error!("Failed to lock CHALLENGE_HASHES. Error produced: {}.", e); + + LinuxRootEnclaveError::LockingError + })?; + + let _challenge = challenge_hashes_lock.remove(&challenge_id).ok_or_else(|| { + error!("Unknown challenge ID: {}.", challenge_id); + + LinuxRootEnclaveError::AttestationError + })?; + + info!("Challenge ID {} found.", challenge_id); + + let runtime_manager_hash = get_runtime_manager_hash()?; + + info!("Runtime Manager hash computed: {:?}.", runtime_manager_hash); + + let device_private_key_lock = DEVICE_PRIVATE_KEY.lock().map_err(|e| { + error!( + "Failed to obtain lock on DEVICE_PRIVATE_KEY. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + let device_private_key = match &*device_private_key_lock { + Some(k) => k.clone(), + None => { + error!("Device private key is not initialized."); + return Err(LinuxRootEnclaveError::InvariantFailed); + } + }; + + let private_key = + EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, &device_private_key).map_err( + |e| { + error!( + "Failed to obtain private key from PKCS8 data. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::CryptographyError + }, + )?; + + let runtime_manager_certificate = convert_csr_to_cert(&csr, &COMPUTE_ENCLAVE_CERT_TEMPLATE, &runtime_manager_hash, &private_key).map_err(|e| { + error!("Failed to convert Certificate Signing Request (CSR) into certificate. Error produced: {:?}.", e); + + LinuxRootEnclaveError::CryptographyError + })?; + + info!("Runtime Manager certificate generated."); + + let certificate_chain_lock = CERTIFICATE_CHAIN.lock().map_err(|e| { + error!( + "Failed to obtain lock on CERTIFICATE_CHAIN. Error produced: {}.", + e + ); + + LinuxRootEnclaveError::LockingError + })?; + + let (root_enclave_certificate, root_certificate) = match &*certificate_chain_lock { + Some((root_enclave_certificate, root_certificate)) => { + (root_enclave_certificate.clone(), root_certificate.clone()) + } + None => { + error!("Root Enclave and Root certificates not stored."); + + return Err(LinuxRootEnclaveError::InvariantFailed); + } + }; + + info!("Obtained Root Enclave certificate and Root certificate."); + + Ok(( + runtime_manager_certificate, + root_enclave_certificate, + root_certificate, + )) +} + +//////////////////////////////////////////////////////////////////////////////// +// Entry point. +//////////////////////////////////////////////////////////////////////////////// + +/// Generates a private key for the root enclave to use as part of the +/// attestation process. +fn generate_private_key() -> Result<(), LinuxRootEnclaveError> { + info!("Generating private key."); + + let rng = SystemRandom::new(); + + let pkcs8_bytes = + EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_ASN1_SIGNING, &rng).map_err(|err| { + error!("Failed to generate PKCS-8 key. Error produced: {:?}.", err); + LinuxRootEnclaveError::CryptographyError + })?; + + let mut private_key_lock = DEVICE_PRIVATE_KEY.lock().map_err(|_e| { + error!("Failed to obtain lock on DEVICE_PRIVATE_KEY."); + LinuxRootEnclaveError::LockingError + })?; + + *private_key_lock = Some(pkcs8_bytes.as_ref().to_vec()); + + Ok(()) +} + +/// Entry point for the root enclave. This sets up a TCP listener and processes +/// messages, deserializing them using Bincode. Can fail for a variety of +/// reasons, all of which are captured in the `LinuxRootEnclaveError` type. +fn entry_point(proxy_attestation_server_base_url: &str) -> Result<(), LinuxRootEnclaveError> { + info!("Linux root enclave initializing."); + + generate_private_key()?; + + let listen_on = format!("{}:{}", INCOMING_ADDRESS, INCOMING_PORT); + + info!("Starting listening on {}.", listen_on); + + let listener = TcpBuilder::new_v4() + .map_err(|e| { + error!("Failed to create new TCP builder. Error produed: {}.", e); + LinuxRootEnclaveError::GeneralIOError(e) + })? + .reuse_address(true) + .map_err(|e| { + error!( + "Failed to set Reuse Address option on socket. Error produced: {}.", + e + ); + LinuxRootEnclaveError::SocketError(e) + })? + .reuse_port(true) + .map_err(|e| { + error!( + "Failed to set Reuse Port option on socket. Error produced: {}.", + e + ); + LinuxRootEnclaveError::SocketError(e) + })? + .bind(&listen_on) + .map_err(|e| { + error!( + "Failed to bind socket on {}. Error produced: {}.", + listen_on, e + ); + LinuxRootEnclaveError::SocketError(e) + })? + .listen(SOCKET_BACKLOG) + .map_err(|e| { + error!("Failed to listen on {}. Error produced: {}.", listen_on, e); + LinuxRootEnclaveError::SocketError(e) + })?; + + info!("Started listening on {}.", listen_on); + + let (mut fd, client_addr) = listener.accept().map_err(|ioerr| { + error!( + "Failed to accept any incoming TCP connection. Error produced: {}.", + ioerr + ); + LinuxRootEnclaveError::SocketError(ioerr) + })?; + + info!("TCP listener connected on {:?}.", client_addr); + + /* Set to `true` when a request to shutdown is received, breaking the + message processing loop, below. + */ + let mut shutdown = false; + + while !shutdown { + let received_buffer: Vec = receive_buffer(&mut fd).map_err(|e| { + error!("Failed to receive message. Error produced: {}.", e); + LinuxRootEnclaveError::SocketError(e) + })?; + + let received_message = deserialize(&received_buffer).map_err(|e| { + error!( + "Failed to deserialize received message. Error produced: {}.", + e + ); + LinuxRootEnclaveError::BincodeError(e) + })?; + + info!("Received message: {:?}.", received_message); + + let response = match received_message { + LinuxRootEnclaveMessage::SpawnNewApplicationEnclave => { + info!("Spawning new application enclave."); + + Ok(LinuxRootEnclaveResponse::EnclaveSpawned( + launch_new_runtime_manager_enclave()?, + )) + } + LinuxRootEnclaveMessage::GetFirmwareVersion => { + info!("Computing firmware version."); + + Ok(LinuxRootEnclaveResponse::FirmwareVersion( + get_firmware_version(), + )) + } + LinuxRootEnclaveMessage::Shutdown => { + info!("Shutting down the Linux root enclave."); + + shutdown = true; + kill_all_enclaves()?; + + Ok(LinuxRootEnclaveResponse::ShuttingDown) + } + LinuxRootEnclaveMessage::GetNativeAttestation => { + info!("Computing a native attestation token."); + + let _result = native_attestation(proxy_attestation_server_base_url)?; + + Ok(LinuxRootEnclaveResponse::NativeAttestationTokenRegistered) + } + LinuxRootEnclaveMessage::GetProxyAttestation(csr, challenge_id) => { + info!( + "Computing a proxy attestation certificate chain (CSR {} bytes).", + csr.len() + ); + + let (runtime_manager_certificate, root_enclave_certificate, root_certificate) = + get_proxy_attestation_certificate_chain(csr, challenge_id)?; + + Ok(LinuxRootEnclaveResponse::CertificateChain( + runtime_manager_certificate, + root_enclave_certificate, + root_certificate, + )) + } + LinuxRootEnclaveMessage::StartProxyAttestation => { + info!("Generating challenge value and fresh challenge ID."); + + let (challenge, challenge_id) = start_proxy_attestation()?; + + Ok(LinuxRootEnclaveResponse::ChallengeGenerated( + challenge, + challenge_id, + )) + } + }?; + + info!("Producing response: {:?}.", response); + + let response_buffer = serialize(&response).map_err(|e| { + error!( + "Failed to serialize response message. Error produced: {}.", + e + ); + LinuxRootEnclaveError::BincodeError(e) + })?; + + info!("Sending message with length: {}.", response_buffer.len()); + + send_buffer(&mut fd, &response_buffer).map_err(|e| { + error!("Failed to send response. Error produced: {}.", e); + LinuxRootEnclaveError::SocketError(e) + })?; + } + + info!("Linux Root Enclave dead."); + + Ok(()) +} + +/// Main entry point for the program. Calls `entry_point` and pretty-prints +/// any error that was produced. Initializes the logging service. +fn main() { + env_logger::init(); + + let matches = App::new("linux-root-enclave") + .about(LINUX_ROOT_ENCLAVE_ABOUT) + .author(LINUX_ROOT_ENCLAVE_AUTHORS) + .arg( + Arg::with_name("proxy-attestation-server") + .required(true) + .short("p") + .long("proxy-attestation-server") + .takes_value(true) + .help("URL for the Proxy Attestation Service."), + ) + .get_matches(); + + if let Some(proxy_attestation_server_base_url) = matches.value_of("proxy-attestation-server") { + info!( + "Starting Linux Root Enclave with Proxy Attestation Service URL: {}.", + proxy_attestation_server_base_url + ); + + let _ignore = entry_point(proxy_attestation_server_base_url).map_err(|e| { + eprintln!( + "Linux root enclave runtime failure. Error produced: {:?}.", + e + ) + }); + } else { + eprintln!("No Proxy Attestation Service URL supplied."); + + exit(1) + } +} diff --git a/platform-services/Cargo.toml b/platform-services/Cargo.toml index b9f939476..812448232 100644 --- a/platform-services/Cargo.toml +++ b/platform-services/Cargo.toml @@ -25,6 +25,7 @@ nsm_lib = { git = "https://github.com/aws/aws-nitro-enclaves-nsm-api.git/", bran nsm_io = { git = "https://github.com/aws/aws-nitro-enclaves-nsm-api.git/", branch = "main", package = "nsm-io", optional = true } sgx_trts = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } optee-utee = { git = "https://github.com/veracruz-project/rust-optee-trustzone-sdk.git", branch = "veracruz", optional = true } +half = "=1.7.1" [profile.release] lto=true diff --git a/platform-services/src/sgx_platform_services.rs b/platform-services/src/sgx_platform_services.rs index 91a6e4a6c..642428723 100644 --- a/platform-services/src/sgx_platform_services.rs +++ b/platform-services/src/sgx_platform_services.rs @@ -28,12 +28,12 @@ pub fn platform_getrandom(buffer: &mut [u8]) -> result::Result<()> { /// Returns the clock resolution in nanoseconds. /// TODO: implement it -pub fn platform_getclockres(clock_id: u8) -> result::Result { +pub fn platform_getclockres(_clock_id: u8) -> result::Result { result::Result::Unavailable } /// Returns the clock time in nanoseconds. /// TODO: implement it -pub fn platform_getclocktime(clock_id: u8) -> result::Result { +pub fn platform_getclocktime(_clock_id: u8) -> result::Result { result::Result::Unavailable } diff --git a/policy-utils/src/lib.rs b/policy-utils/src/lib.rs index f49180db5..5ffd49f30 100644 --- a/policy-utils/src/lib.rs +++ b/policy-utils/src/lib.rs @@ -58,6 +58,9 @@ pub mod principal; /// A type capturing the platform the enclave is running on. #[derive(Debug)] pub enum Platform { + /// The enclave is running as a Linux process, either unprotected or as part of a + /// protected Virtual Machine-like enclaving mechanism. + Linux, /// The enclave is running under Intel SGX. SGX, /// The enclave is running under Arm TrustZone. @@ -79,6 +82,7 @@ impl FromStr for Platform { "trustzone" => Ok(Platform::TrustZone), "nitro" => Ok(Platform::Nitro), "icecap" => Ok(Platform::IceCap), + "linux" => Ok(Platform::Linux), _ => Err(PlatformError::InvalidPlatform(format!("{}", s))), } } @@ -88,6 +92,7 @@ impl FromStr for Platform { impl fmt::Display for Platform { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + Platform::Linux => write!(f, "linux"), Platform::SGX => write!(f, "sgx"), Platform::TrustZone => write!(f, "trustzone"), Platform::Nitro => write!(f, "nitro"), diff --git a/policy-utils/src/policy.rs b/policy-utils/src/policy.rs index 0049132d1..355f95756 100644 --- a/policy-utils/src/policy.rs +++ b/policy-utils/src/policy.rs @@ -73,6 +73,8 @@ pub struct Policy { /// The ciphersuite that will be used with the TLS connections between the /// principals of the computation and the enclave. ciphersuite: String, + /// The hash of the Veracruz trusted runtime for Linux applications. + runtime_manager_hash_linux: Option, /// The hash of the Veracruz trusted runtime for SGX enclaves. runtime_manager_hash_sgx: Option, /// The hash of the Veracruz trusted runtime for TrustZone TAs. @@ -113,6 +115,7 @@ impl Policy { veracruz_server_url: String, enclave_cert_expiry: Timepoint, ciphersuite: String, + runtime_manager_hash_linux: Option, runtime_manager_hash_sgx: Option, runtime_manager_hash_tz: Option, runtime_manager_hash_nitro: Option, @@ -131,6 +134,7 @@ impl Policy { veracruz_server_url, enclave_cert_expiry, ciphersuite, + runtime_manager_hash_linux, runtime_manager_hash_sgx, runtime_manager_hash_tz, runtime_manager_hash_nitro, @@ -199,6 +203,14 @@ impl Policy { #[inline] pub fn runtime_manager_hash(&self, platform: &Platform) -> Result<&String, PolicyError> { let hash = match platform { + Platform::Linux => match &self.runtime_manager_hash_linux { + Some(hash) => hash, + None => { + return Err(PolicyError::MissingPolicyFieldError( + "runtime_manager_hash_linux".to_string(), + )) + } + }, Platform::SGX => match &self.runtime_manager_hash_sgx { Some(hash) => hash, None => { diff --git a/proxy-attestation-server/Cargo.toml b/proxy-attestation-server/Cargo.toml index 367012f37..352753643 100644 --- a/proxy-attestation-server/Cargo.toml +++ b/proxy-attestation-server/Cargo.toml @@ -27,10 +27,10 @@ cli = ["structopt"] # a feature to enable PSA native attestation. # Note: Final attestation is always PSA. This is just to enable platforms # that use PSA for their native attestation. -psa = [] nitro = [ "serde_cbor", "nitro-enclave-attestation-document", "psa-attestation/nitro" ] -tz = ["psa"] -icecap = ["psa"] +tz = ["psa-attestation/tz"] +icecap = ["psa-attestation/icecap"] +linux = ["psa-attestation/linux"] [dependencies] rouille = "=3.2.1" diff --git a/proxy-attestation-server/build-proxy-attestation-server-optee.sh b/proxy-attestation-server/build-proxy-attestation-server-optee.sh index bcedaa1dd..d74854138 100644 --- a/proxy-attestation-server/build-proxy-attestation-server-optee.sh +++ b/proxy-attestation-server/build-proxy-attestation-server-optee.sh @@ -1,4 +1,4 @@ #pushd /work/rust-optee-trustzone-sdk #source environment #popd -cargo build -j 1 --features psa --target aarch64-unknown-linux-gnu +cargo build -j 1 --features tz --target aarch64-unknown-linux-gnu diff --git a/proxy-attestation-server/src/attestation.rs b/proxy-attestation-server/src/attestation.rs index 9875098e7..8e13f5082 100644 --- a/proxy-attestation-server/src/attestation.rs +++ b/proxy-attestation-server/src/attestation.rs @@ -9,12 +9,12 @@ //! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for //! information on licensing and copyright. -#[cfg(feature = "psa")] +#[cfg(feature = "nitro")] +pub mod nitro; +#[cfg(any(feature = "tz", feature = "linux", feature = "icecap"))] pub mod psa; #[cfg(feature = "sgx")] pub mod sgx; -#[cfg(feature = "nitro")] -pub mod nitro; use crate::error::*; use lazy_static::lazy_static; @@ -29,19 +29,16 @@ use openssl; lazy_static! { static ref DEVICE_ID: AtomicI32 = AtomicI32::new(1); - static ref CA_CERT_DER: std::sync::Mutex - >> = std::sync::Mutex::new(None); - static ref CA_KEY_PKEY: std::sync::Mutex - >> = std::sync::Mutex::new(None); + static ref CA_CERT_DER: std::sync::Mutex>> = std::sync::Mutex::new(None); + static ref CA_KEY_PKEY: std::sync::Mutex>> = + std::sync::Mutex::new(None); } /// Reads a PEM certificate from `pem_cert_path`, converts it to DER format, /// and stores it in CA_CERT_DER for use by the service pub fn load_ca_certificate

(pem_cert_path: P) -> Result<(), ProxyAttestationServerError> where - P: AsRef + P: AsRef, { let mut f = std::fs::File::open(pem_cert_path) .map_err(|err| ProxyAttestationServerError::IOError(err))?; @@ -71,7 +68,7 @@ fn get_ca_certificate() -> Result, ProxyAttestationServerError> { /// and stores it in CA_KEY_PKEY for use by the service pub fn load_ca_key

(pem_key_path: P) -> Result<(), ProxyAttestationServerError> where - P: AsRef + P: AsRef, { let mut f = std::fs::File::open(pem_key_path) .map_err(|err| ProxyAttestationServerError::IOError(err))?; @@ -88,7 +85,8 @@ where return Ok(()); } -fn get_ca_key() -> Result, ProxyAttestationServerError> { +fn get_ca_key() -> Result, ProxyAttestationServerError> +{ let guard = CA_KEY_PKEY.lock()?; match &*guard { None => return Err(ProxyAttestationServerError::BadStateError), @@ -115,12 +113,12 @@ pub async fn start(body_string: String) -> ProxyAttestationServerResponder { } let (protocol, firmware_version) = transport_protocol::parse_start_msg(&parsed); - let device_id = DEVICE_ID.fetch_add(1, Ordering::SeqCst); + let device_id = DEVICE_ID.fetch_add(1, Ordering::SeqCst); match protocol.as_str() { #[cfg(feature = "sgx")] "sgx" => sgx::start(&firmware_version, device_id), - #[cfg(feature = "psa")] + #[cfg(any(feature = "tz", feature = "linux", feature = "icecap"))] "psa" => psa::start(&firmware_version, device_id), #[cfg(feature = "nitro")] "nitro" => nitro::start(&firmware_version, device_id), @@ -130,78 +128,78 @@ pub async fn start(body_string: String) -> ProxyAttestationServerResponder { /// Convert a Certificate Signing Request (CSR) to an X.509 Certificate and /// sign it -fn convert_csr_to_certificate(csr_der: &[u8], is_ca: bool, enclave_hash: &[u8]) -> Result { +fn convert_csr_to_certificate( + csr_der: &[u8], + is_ca: bool, + enclave_hash: &[u8], +) -> Result { let csr = openssl::x509::X509Req::from_der(csr_der) .map_err(|err| { print!("proxy-attestation-server::attestation::convert_csr_to_certificate failed to get csr from der:{:?}", err); err })?; + // first, verify the signature on the CSR let public_key = csr.public_key()?; - let verify_result = csr.verify(&public_key) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate csr.verify failed:{:?}", err); - err - })?; + let verify_result = csr.verify(&public_key)?; if !verify_result { println!("proxy_attestation_server::convert_csr_to_certificate verify of CSR failed"); return Err(ProxyAttestationServerError::CsrVerifyError); } - let mut cert_builder = openssl::x509::X509Builder::new() - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate X509Builder::new failed:{:?}", err); - err - })?; + + let mut cert_builder = openssl::x509::X509Builder::new().map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate X509Builder::new failed:{:?}", err); + err + })?; cert_builder.set_version(2) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_version failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_version failed:{:?}", err); + err + })?; let now = openssl::asn1::Asn1Time::days_from_now(0) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate days_from_now failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate days_from_now failed:{:?}", err); + err + })?; cert_builder.set_not_before(&now) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_not_before failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_not_before failed:{:?}", err); + err + })?; // TODO: Currently setting the certificate expiry to a day from now. In the // future it would be good to make it configurable (also, 1 day seems a bit // long in this context) let expiry =openssl::asn1::Asn1Time::days_from_now(1) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate days_from_now failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate days_from_now failed:{:?}", err); + err + })?; cert_builder.set_not_after(&expiry) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_not_after failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_not_after failed:{:?}", err); + err + })?; - // Set the serial number of the certificate - // TODO: Do we want to manage serial numbers? Right now, they are all the same let serial_number = { let sn_bignum = openssl::bn::BigNum::from_u32(23) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate from_u32 failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate from_u32 failed:{:?}", err); + err + })?; openssl::asn1::Asn1Integer::from_bn(&sn_bignum) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate from_bn failed:{:?}", err); - err - })? + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate from_bn failed:{:?}", err); + err + })? }; + cert_builder.set_serial_number(&serial_number) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_serial_number failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_serial_number failed:{:?}", err); + err + })?; // construct and set the issuer name as the subject name of the CA cert let issuer_name = { @@ -214,35 +212,35 @@ fn convert_csr_to_certificate(csr_der: &[u8], is_ca: bool, enclave_hash: &[u8]) let mut issuer_name_builder = openssl::x509::X509NameBuilder::new()?; for entry in ca_cert.subject_name().entries() { - issuer_name_builder.append_entry_by_nid( - entry.object().nid(), - &entry.data().as_utf8()? - )?; + issuer_name_builder + .append_entry_by_nid(entry.object().nid(), &entry.data().as_utf8()?)?; } issuer_name_builder.build() }; + cert_builder.set_issuer_name(&issuer_name) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_issuer_name failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_issuer_name failed:{:?}", err); + err + })?; // set the subject name of the certificate to the subject name from the CSR cert_builder.set_subject_name(csr.subject_name()) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_subject_name failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_subject_name failed:{:?}", err); + err + })?; // set the public key of the certificate to the public key from the CSR cert_builder.set_pubkey(csr.public_key()?.as_ref()) - .map_err(|err| { - println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_pubkey failed:{:?}", err); - err - })?; + .map_err(|err| { + println!("proxy-attestation-server::attestation::convert_csr_to_certificate set_pubkey failed:{:?}", err); + err + })?; // The alt-name extension is required by our client. It basically sets the // URL that the certificat is valid for. let mut alt_name_extension = openssl::x509::extension::SubjectAlternativeName::new(); + alt_name_extension.dns("ComputeEnclave.dev"); let built_extension = alt_name_extension.build(&cert_builder.x509v3_context(None, None)) .map_err(|err| { @@ -275,10 +273,13 @@ fn convert_csr_to_certificate(csr_der: &[u8], is_ca: bool, enclave_hash: &[u8]) })?; // Add our custom extension to the certificate that contains the hash of the enclave - let extension_name = format!("{}.{}.{}.{}", VERACRUZ_RUNTIME_HASH_EXTENSION_ID[0], - VERACRUZ_RUNTIME_HASH_EXTENSION_ID[1], - VERACRUZ_RUNTIME_HASH_EXTENSION_ID[2], - VERACRUZ_RUNTIME_HASH_EXTENSION_ID[3]); + let extension_name = format!( + "{}.{}.{}.{}", + VERACRUZ_RUNTIME_HASH_EXTENSION_ID[0], + VERACRUZ_RUNTIME_HASH_EXTENSION_ID[1], + VERACRUZ_RUNTIME_HASH_EXTENSION_ID[2], + VERACRUZ_RUNTIME_HASH_EXTENSION_ID[3] + ); let extension_value = format!("DER:{}", hex::encode(enclave_hash)); let custom_extension = openssl::x509::X509Extension::new(None, None, &extension_name, &extension_value) .map_err(|err| { diff --git a/proxy-attestation-server/src/attestation/nitro.rs b/proxy-attestation-server/src/attestation/nitro.rs index 12961b3ad..b538c3ece 100644 --- a/proxy-attestation-server/src/attestation/nitro.rs +++ b/proxy-attestation-server/src/attestation/nitro.rs @@ -12,9 +12,8 @@ use crate::error::*; use lazy_static::lazy_static; use rand::Rng; -use std::{collections::HashMap, sync::Mutex}; use std::io::Write; -use std::sync::atomic::Ordering; +use std::{collections::HashMap, sync::Mutex}; use nitro_enclave_attestation_document::AttestationDocument; @@ -72,7 +71,7 @@ struct NitroAttestationContext { } lazy_static! { - /// A hash map containing a `NitroAttestationContext` for each of the + /// A hash map containing a `NitroAttestationContext` for each of the /// Nitro Root enclaves that we have started native attestation for static ref ATTESTATION_CONTEXT: Mutex> = Mutex::new(HashMap::new()); @@ -125,11 +124,20 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder let (att_doc_data, device_id) = transport_protocol::parse_nitro_attestation_doc(&parsed.get_nitro_attestation_doc()); - let attestation_document = AttestationDocument::authenticate(&att_doc_data, &AWS_NITRO_ROOT_CERTIFICATE).map_err(|err| { - println!("proxy-attestation-server::nitro::attestation_token authenticate_token failed:{:?}", err); - let _ignore = std::io::stdout().flush(); - ProxyAttestationServerError::CborError(format!("parse_nitro_token failed to parse token data:{:?}", err)) - })?; + let attestation_document = + AttestationDocument::authenticate(&att_doc_data, &AWS_NITRO_ROOT_CERTIFICATE).map_err( + |err| { + println!( + "proxy-attestation-server::nitro::attestation_token authenticate_token failed:{:?}", + err + ); + let _ignore = std::io::stdout().flush(); + ProxyAttestationServerError::CborError(format!( + "parse_nitro_token failed to parse token data:{:?}", + err + )) + }, + )?; let attestation_context = { let mut ac_hash = ATTESTATION_CONTEXT.lock() @@ -155,10 +163,13 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder println!("proxy-attestation-server::attestation::nitro::attestation_token attestation document did not contain a nonce. We require it."); let _ignore = std::io::stdout().flush(); return Err(ProxyAttestationServerError::MissingFieldError("nonce")); - }, + } Some(nonce) => { if nonce != attestation_context.challenge { - println!("Challenge failed to match. Wanted:{:02x?}, got:{:02x?}", nonce, attestation_context.challenge); + println!( + "Challenge failed to match. Wanted:{:02x?}, got:{:02x?}", + nonce, attestation_context.challenge + ); let _ignore = std::io::stdout().flush(); return Err(ProxyAttestationServerError::MismatchError { variable: "nonce/challenge", @@ -166,18 +177,17 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder received: nonce, }); } - }, + } } let received_enclave_hash = &attestation_document.pcrs[0]; - let mut csr = match attestation_document.user_data { + let csr = match attestation_document.user_data { Some(data) => data, None => { return Err(ProxyAttestationServerError::MissingFieldError("public_key")); - }, + } }; - // convert the CSR into a certificate let re_cert = crate::attestation::convert_csr_to_certificate(&csr, false, &received_enclave_hash[0..32]) @@ -193,7 +203,7 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder println!("proxy-attestation-server::attestation::nitro::attestation_token serialize_cert_chain failed:{:?}", err); err })?; - + let response_b64 = base64::encode(&response_bytes); return Ok(response_b64); diff --git a/proxy-attestation-server/src/attestation/psa.rs b/proxy-attestation-server/src/attestation/psa.rs index 03c8f1de7..8d870157c 100644 --- a/proxy-attestation-server/src/attestation/psa.rs +++ b/proxy-attestation-server/src/attestation/psa.rs @@ -18,7 +18,7 @@ use psa_attestation::{ t_cose_sign1_verify_init, t_cose_sign1_verify_load_public_key, }; use rand::Rng; -use std::{collections::HashMap, io::Read, ffi::c_void, sync::Mutex}; +use std::{collections::HashMap, ffi::c_void, sync::Mutex}; // Yes, I'm doing what you think I'm doing here. Each instance of the SGX root enclave // will have the same public key. Yes, I'm embedding that key in the source @@ -74,8 +74,9 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder "native_psa_attestation_token", )); } - let (token, csr, device_id) = - transport_protocol::parse_native_psa_attestation_token(&parsed.get_native_psa_attestation_token()); + let (token, csr, device_id) = transport_protocol::parse_native_psa_attestation_token( + &parsed.get_native_psa_attestation_token(), + ); let attestation_context = { let ac_hash = ATTESTATION_CONTEXT.lock()?; @@ -174,7 +175,9 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder &"psa".to_string(), &attestation_context.firmware_version, )? - .ok_or(ProxyAttestationServerError::MissingFieldError("firmware version"))? + .ok_or(ProxyAttestationServerError::MissingFieldError( + "firmware version", + ))? }; if expected_enclave_hash != received_enclave_hash { return Err(ProxyAttestationServerError::MismatchError { @@ -183,7 +186,7 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder received: received_enclave_hash.to_vec(), }); } - let is_ca = cfg!(feature = "tz"); + let is_ca = cfg!(any(feature = "tz", feature = "linux")); let cert = crate::attestation::convert_csr_to_certificate(&csr, is_ca, &received_enclave_hash) .map_err(|err| { println!("proxy-attestation-server::attestation::psa::attestation_token convert_csr_to_certificate failed:{:?}", err); @@ -193,7 +196,7 @@ pub fn attestation_token(body_string: String) -> ProxyAttestationServerResponder let root_cert_der = crate::attestation::get_ca_certificate()?; let response_bytes = transport_protocol::serialize_cert_chain(&cert.to_der()?, &root_cert_der)?; - + let response_b64 = base64::encode(&response_bytes); // clean up the Attestation Context by removing this context diff --git a/proxy-attestation-server/src/server.rs b/proxy-attestation-server/src/server.rs index fcb038457..15249d52a 100644 --- a/proxy-attestation-server/src/server.rs +++ b/proxy-attestation-server/src/server.rs @@ -10,12 +10,12 @@ //! information on licensing and copyright. use crate::attestation; -#[cfg(feature = "psa")] +#[cfg(feature = "nitro")] +use crate::attestation::nitro; +#[cfg(any(feature = "linux", feature = "tz", feature = "icecap"))] use crate::attestation::psa; #[cfg(feature = "sgx")] use crate::attestation::sgx; -#[cfg(feature = "nitro")] -use crate::attestation::nitro; use lazy_static::lazy_static; use std::net::ToSocketAddrs; @@ -38,14 +38,18 @@ use std::{ffi::c_void, path, ptr::null}; async fn verify_iat(input_data: String) -> ProxyAttestationServerResponder { if input_data.is_empty() { println!("proxy-attestation-server::verify_iat input_data is empty"); - return Err(ProxyAttestationServerError::MissingFieldError("proxy-attestation-server::verify_iat data")); + return Err(ProxyAttestationServerError::MissingFieldError( + "proxy-attestation-server::verify_iat data", + )); } - let proto_bytes = base64::decode(&input_data) - .map_err(|err| { - println!("proxy-attestation-server::verify_iat decode of input data failed:{:?}", err); + let proto_bytes = base64::decode(&input_data).map_err(|err| { + println!( + "proxy-attestation-server::verify_iat decode of input data failed:{:?}", err - })?; + ); + err + })?; let proto = transport_protocol::parse_proxy_attestation_server_request(&proto_bytes) .map_err(|err| { @@ -53,23 +57,30 @@ async fn verify_iat(input_data: String) -> ProxyAttestationServerResponder { err })?; if !proto.has_proxy_psa_attestation_token() { - println!("proxy-attestation-server::verify_iat proto does not have proxy psa attestation token"); + println!( + "proxy-attestation-server::verify_iat proto does not have proxy psa attestation token" + ); return Err(ProxyAttestationServerError::NoProxyPSAAttestationTokenError); } - let (token, pubkey, device_id) = - transport_protocol::parse_proxy_psa_attestation_token(proto.get_proxy_psa_attestation_token()); + let (token, pubkey, device_id) = transport_protocol::parse_proxy_psa_attestation_token( + proto.get_proxy_psa_attestation_token(), + ); let pubkey_hash = { - let conn = crate::orm::establish_connection() - .map_err(|err| { - println!("proxy-attestation-server::verify_iat orm::establish_connection failed:{:?}", err); + let conn = crate::orm::establish_connection().map_err(|err| { + println!( + "proxy-attestation-server::verify_iat orm::establish_connection failed:{:?}", err - })?; - crate::orm::query_device(&conn, device_id) - .map_err(|err| { - println!("proxy-attestation-server::verify_iat orm::query_device failed:{:?}", err); + ); + err + })?; + crate::orm::query_device(&conn, device_id).map_err(|err| { + println!( + "proxy-attestation-server::verify_iat orm::query_device failed:{:?}", err - })? + ); + err + })? }; // verify that the pubkey we received matches the hash we received @@ -96,7 +107,10 @@ async fn verify_iat(input_data: String) -> ProxyAttestationServerResponder { ) }; if lpk_ret != 0 { - println!("proxy-attestation-server::verify_iat t_cose_sign1_verify_load_public_key failed:{:?}", lpk_ret); + println!( + "proxy-attestation-server::verify_iat t_cose_sign1_verify_load_public_key failed:{:?}", + lpk_ret + ); return Err(ProxyAttestationServerError::UnsafeCallError( "proxy-attestation-server::server::verify_iat t_cose_sign1_verify_load_public_key", lpk_ret, @@ -147,7 +161,9 @@ async fn verify_iat(input_data: String) -> ProxyAttestationServerResponder { if payload.ptr == null() { println!("proxy-attestation-server::verify_iat payload.ptr is null"); - return Err(ProxyAttestationServerError::MissingFieldError("payload.ptr")); + return Err(ProxyAttestationServerError::MissingFieldError( + "payload.ptr", + )); } let payload_vec = @@ -157,7 +173,10 @@ async fn verify_iat(input_data: String) -> ProxyAttestationServerResponder { } #[allow(unused)] -async fn sgx_router(psa_request: web::Path, input_data: String) -> ProxyAttestationServerResponder { +async fn sgx_router( + psa_request: web::Path, + input_data: String, +) -> ProxyAttestationServerResponder { #[cfg(feature = "sgx")] match psa_request.into_inner().as_str() { "Msg1" => sgx::msg1(input_data), @@ -169,26 +188,35 @@ async fn sgx_router(psa_request: web::Path, input_data: String) -> Proxy } #[allow(unused)] -async fn psa_router(psa_request: web::Path, input_data: String) -> ProxyAttestationServerResponder { - #[cfg(feature = "psa")] +async fn psa_router( + psa_request: web::Path, + input_data: String, +) -> ProxyAttestationServerResponder { + #[cfg(any(feature = "linux", feature = "tz", feature = "icecap"))] if psa_request.into_inner().as_str() == "AttestationToken" { psa::attestation_token(input_data) } else { Err(ProxyAttestationServerError::UnsupportedRequestError) } - #[cfg(not(feature = "psa"))] + #[cfg(not(any(feature = "tz", feature = "linux", feature = "icecap")))] Err(ProxyAttestationServerError::UnimplementedRequestError) } #[allow(unused)] -async fn nitro_router(nitro_request: web::Path, input_data: String) -> ProxyAttestationServerResponder { +async fn nitro_router( + nitro_request: web::Path, + input_data: String, +) -> ProxyAttestationServerResponder { #[cfg(feature = "nitro")] { let inner = nitro_request.into_inner(); if inner.as_str() == "AttestationToken" { nitro::attestation_token(input_data) } else { - println!("proxy-attestation-server::nitro_router returning unsupported with into_inner:{:?}", inner.as_str()); + println!( + "proxy-attestation-server::nitro_router returning unsupported with into_inner:{:?}", + inner.as_str() + ); Err(ProxyAttestationServerError::UnsupportedRequestError) } } @@ -196,23 +224,32 @@ async fn nitro_router(nitro_request: web::Path, input_data: String) -> P Err(ProxyAttestationServerError::UnimplementedRequestError) } -pub fn server(url: U, ca_cert_path: P1, ca_key_path: P2, debug: bool) -> Result +pub fn server( + url: U, + ca_cert_path: P1, + ca_key_path: P2, + debug: bool, +) -> Result where U: ToSocketAddrs, P1: AsRef, - P2: AsRef + P2: AsRef, { if debug { DEBUG_MODE.store(true, Ordering::SeqCst); } - crate::attestation::load_ca_certificate(ca_cert_path) - .map_err(|err| { - format!("proxy-attestation-server::server::server load_ca_certificate returned an error:{:?}", err) - })?; - crate::attestation::load_ca_key(ca_key_path) - .map_err(|err| { - format!("proxy-attestation-server::server::server load_ca_key returned an error:{:?}", err) - })?; + crate::attestation::load_ca_certificate(ca_cert_path).map_err(|err| { + format!( + "proxy-attestation-server::server::server load_ca_certificate returned an error:{:?}", + err + ) + })?; + crate::attestation::load_ca_key(ca_key_path).map_err(|err| { + format!( + "proxy-attestation-server::server::server load_ca_key returned an error:{:?}", + err + ) + })?; let server = HttpServer::new(move || { App::new() .wrap(middleware::Logger::default()) diff --git a/proxy-attestation-server/src/test.rs b/proxy-attestation-server/src/test.rs index ee3b10f2d..669c92284 100644 --- a/proxy-attestation-server/src/test.rs +++ b/proxy-attestation-server/src/test.rs @@ -37,11 +37,15 @@ extern crate sgx_root_enclave_bind; use sgx_root_enclave_bind::{ _quote_nonce, _ra_msg2_t, _ra_msg3_t, _report_t, _sgx_ec256_public_t, _target_info_t, sgx_root_enclave_get_firmware_version, sgx_root_enclave_get_firmware_version_len, - sgx_root_enclave_init_remote_attestation_enc, sgx_root_enclave_sgx_get_pubkey_report, sgx_root_enclave_sgx_ra_get_ga, - sgx_root_enclave_sgx_ra_get_msg3_trusted, sgx_root_enclave_sgx_ra_proc_msg2_trusted, + sgx_root_enclave_init_remote_attestation_enc, sgx_root_enclave_sgx_get_pubkey_report, + sgx_root_enclave_sgx_ra_get_ga, sgx_root_enclave_sgx_ra_get_msg3_trusted, + sgx_root_enclave_sgx_ra_proc_msg2_trusted, }; -static ENCLAVE_FILE: &'static str = "/work/veracruz/trustzone-root-enclave/bin/sgx_root_enclave.signed.so"; +use veracruz_utils::http::post_buffer; + +static ENCLAVE_FILE: &'static str = + "/work/veracruz/trustzone-root-enclave/bin/sgx_root_enclave.signed.so"; #[test] fn test_sgx_attestation() { @@ -71,7 +75,11 @@ fn test_sgx_attestation() { let mut gfvl_ret: u32 = 0; let mut fv_length: u64 = 0; let gfvl_result = unsafe { - sgx_root_enclave_get_firmware_version_len(enclave.geteid(), &mut gfvl_ret, &mut fv_length) + sgx_root_enclave_get_firmware_version_len( + enclave.geteid(), + &mut gfvl_ret, + &mut fv_length, + ) }; assert!(gfvl_result == 0); assert!(gfvl_ret == 0); @@ -81,7 +89,12 @@ fn test_sgx_attestation() { let mut gfv_ret = sgx_status_t::SGX_SUCCESS as u32; let gfv_result = unsafe { - sgx_root_enclave_get_firmware_version(enclave.geteid(), &mut gfv_ret, p_output, fv_length) + sgx_root_enclave_get_firmware_version( + enclave.geteid(), + &mut gfv_ret, + p_output, + fv_length, + ) }; assert!(gfv_result == sgx_status_t::SGX_SUCCESS as u32); assert!(gfv_ret == sgx_status_t::SGX_SUCCESS as u32); @@ -224,13 +237,16 @@ fn test_psa_attestation() { assert!(status == 0); unsafe { token.set_len(token_size.try_into().unwrap()) } - let serialized_pat = - transport_protocol::serialize_psa_attestation_token(&token, public_key.as_ref(), fake_device_id); + let serialized_pat = transport_protocol::serialize_psa_attestation_token( + &token, + public_key.as_ref(), + fake_device_id, + ); let encoded_token = base64::encode(&serialized_pat); let url = "127.0.0.1:3016/VerifyPAT"; - let received_buffer = - post_buffer(&url, &encoded_token).expect("Failed to send buffer to proxy attestation server"); + let received_buffer = post_buffer(&url, &encoded_token) + .expect("Failed to send buffer to proxy attestation server"); } static SETUP: Once = Once::new(); @@ -476,75 +492,3 @@ fn attestation_challenge( )) } } -fn post_buffer(url: &str, data: &str) -> Result { - let mut data_reader = stringreader::StringReader::new(&data); - let mut curl_request = Easy::new(); - curl_request - .url(url) - .expect(&format!("Error completing CURL request to {}", url)); - - let mut headers = List::new(); - headers - .append("Content-Type: application/octet-stream") - .expect("Cannot append into CURL header list"); - - curl_request - .http_headers(headers) - .expect("Cannot append into CURL header list"); - - curl_request - .post(true) - .expect("Error setting POST for a curl request"); - curl_request - .post_field_size(data.len() as u64) - .expect(&format!("Error setting POST field size of {}", 0)); - curl_request - .fail_on_error(true) - .expect(&format!("Failed to set 'fail_on_error'")); - - let mut received_body = std::string::String::new(); - let mut received_header = std::string::String::new(); - { - let mut transfer = curl_request.transfer(); - - transfer - .read_function(|buf| Ok(data_reader.read(buf).unwrap_or(0))) - .expect("Error completing CURL transfer"); - - transfer - .write_function(|buf| { - received_body.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - Ok(buf.len()) - }) - .expect("Error completing CURL transfer"); - transfer - .header_function(|buf| { - received_header.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - true - }) - .expect("Error completing CURL transfer"); - - transfer - .perform() - .expect(format!("Error performing CURL transfer to url:{:}", url).as_str()); - } - - let header_lines: Vec<&str> = { - let lines = received_header.split("\n"); - lines.collect() - }; - let mut header_fields = std::collections::HashMap::new(); - for this_line in header_lines.iter() { - let fields: Vec<&str> = this_line.split(":").collect(); - if fields.len() == 2 { - header_fields.insert(fields[0], fields[1]); - } - } - Ok(received_body) -} diff --git a/psa-attestation/Cargo.toml b/psa-attestation/Cargo.toml index 0f53060b5..bd8bae94d 100644 --- a/psa-attestation/Cargo.toml +++ b/psa-attestation/Cargo.toml @@ -13,8 +13,10 @@ crate-type = ["rlib"] [features] # build.rs depends on features tz = [] +linux = [] sgx = [] nitro = [] +icecap = [] [dependencies] libc = { git = "https://github.com/veracruz-project/libc.git", branch = "veracruz" } diff --git a/psa-attestation/build.rs b/psa-attestation/build.rs index 969ade420..3de0524a7 100644 --- a/psa-attestation/build.rs +++ b/psa-attestation/build.rs @@ -21,6 +21,8 @@ fn main() { cfg_if::cfg_if! { if #[cfg(feature = "tz")] { "/work/rust-optee-trustzone-sdk/optee/toolchains/aarch64/bin/aarch64-linux-gnu-gcc".to_string() + } else if #[cfg(feature = "linux")] { + "gcc".to_string() } else if #[cfg(feature = "sgx")] { "gcc".to_string() } else if #[cfg(feature = "nitro")] { diff --git a/runtime-manager/Cargo.toml b/runtime-manager/Cargo.toml index dcd948694..adc452cad 100644 --- a/runtime-manager/Cargo.toml +++ b/runtime-manager/Cargo.toml @@ -20,8 +20,12 @@ sgx = ["transport-protocol/sgx", "session-manager/sgx", "veracruz-utils/sgx", "p tz = ["wasmi/std", "session-manager/tz", "execution-engine/tz", "veracruz-utils/tz", "policy-utils/tz", "libc", "optee-utee-sys", "optee-utee", "serde_json/alloc", "wasi-types/std"] nitro = [ "execution-engine/std", "execution-engine/nitro", "wasmi/non_sgx", "session-manager/nitro", "veracruz-utils/nitro", "policy-utils/std", "io-utils/nitro", "nsm_io", "nsm_lib", "ring/nitro", "nix", "byteorder", "bincode", "wasi-types/std"] icecap = ["veracruz-utils/icecap", "policy-utils/icecap", "transport-protocol/icecap", "execution-engine/icecap", "session-manager/icecap", "psa-attestation", "wasmi/non_sgx", "serde", "bincode", "log", "libm", "icecap-wrapper"] +linux = ["execution-engine/std", "wasmi/non_sgx", "session-manager/std", "veracruz-utils/linux", "policy-utils/std", "io-utils/linux", "byteorder", "clap", "nix", "bincode", "wasi-types/std", "log", "env_logger"] [dependencies] +clap = {version = "2.33", optional = true} +log = {version = "=0.4.13", optional = true } +env_logger = {version = "0.8.3", optional = true } session-manager = { path = "../session-manager"} lazy_static = {version = "1.4.0", features=["spin_no_std"] } execution-engine = { path = "../execution-engine" } @@ -54,7 +58,6 @@ wasi-types = { git = "https://github.com/veracruz-project/wasi-types.git", branc bitflags = "=1.2.1" psa-attestation = { path = "../psa-attestation", optional = true } icecap-wrapper = { path = "../icecap/crates-hack/crates/icecap-wrapper", optional = true } -log = { version = "0.4", optional = true } half = "=1.7.1" http = "=0.2.4" diff --git a/runtime-manager/Makefile b/runtime-manager/Makefile index f3eaa16f9..92ceda79b 100644 --- a/runtime-manager/Makefile +++ b/runtime-manager/Makefile @@ -12,7 +12,7 @@ OUT_DIR?=. FINAL_DIR?=. -.PHONY: all sgx trustzone clean deprecated nitro +.PHONY: all linux sgx trustzone clean deprecated nitro all: deprecated @@ -21,7 +21,7 @@ INFO_COLOR := "\e[1;32m" RESET_COLOR := "\e[0m" deprecated: - @echo $(WARNING_COLOR)"The default target, compiling sgx, is deprecated. Please explicitly choose target, sgx or trustzone." $(RESET_COLOR) + @echo $(WARNING_COLOR)"The default target, compiling sgx, is deprecated. Please explicitly choose target: linux, nitro, sgx, trustzone." $(RESET_COLOR) ############# SGX ################# Signed_RustEnclave_RootName := runtime_manager.signed.so @@ -81,7 +81,7 @@ $(Untrusted_Lib_Name): $(OUT_DIR)/runtime_manager_u.o $(AR) rcsD $@ $(OUT_DIR)/runtime_manager_u.o @echo $(INFO_COLOR) "AR >= $<" $(RESET_COLOR) -RustEnclave_Link_Libs = -L$(CUSTOM_LIBRARY_PATH) -L$(OUT_DIR) -lruntime_manager_enclave +RustEnclave_Link_Libs = -L$(CUSTOM_LIBRARY_PATH) -L$(OUT_DIR) -lruntime_manager_enclave RustEnclave_Link_Flags = $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ -Wl,--whole-archive -lsgx_trts -Wl,--no-whole-archive \ -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -lsgx_tkey_exchange -lsgx_tservice -lsgx_tcrypto $(RustEnclave_Link_Libs) -Wl,--end-group \ @@ -142,6 +142,9 @@ css-nitro.bin: PCR0 css-icecap.bin: touch $@ +css-linux.bin: target/release/runtime_manager_enclave + cp $< $@ + $(FINAL_DIR)/$(TZ_UUID).ta: $(OUT_DIR)/$(TZ_UUID).ta cp $< $@ @@ -171,6 +174,14 @@ runtime_manager.eif: target/x86_64-unknown-linux-musl/release/runtime_manager_en target/x86_64-unknown-linux-musl/release/runtime_manager_enclave: Cargo.toml $(Nitro_Src) cargo build --target x86_64-unknown-linux-musl --release --features nitro +####### Linux (AArch64 and X64) processes ######### +Linux_Src = $(COMMON_Src) src/runtime_manager_linux.rs src/main.rs + +target/release/runtime_manager_enclave: Cargo.toml $(Linux_Src) + cargo build --release --features linux + +linux: target/release/runtime_manager_enclave + clean: @cargo clean @xargo clean @@ -178,8 +189,8 @@ clean: @rm -f runtime_manager_t.? runtime_manager_u.? @rm -f $(FINAL_DIR)/$(Signed_RustEnclave_RootName) @rm -f $(SGX_Enclave_Name) - @rm -f $(FINAL_DIR)/$(TZ_UUID).ta - @rm -f $(OUT_DIR)/$(TZ_UUID).ta + @rm -f $(FINAL_DIR)/$(TZ_UUID).ta + @rm -f $(OUT_DIR)/$(TZ_UUID).ta @rm -f $(OUT_DIR)/stripped_ta @rm -f $(OUT_DIR)/runtime_manager_enclave @rm -f $(RustEnclave_Name) diff --git a/runtime-manager/src/main.rs b/runtime-manager/src/main.rs index 736942945..c1281fa56 100644 --- a/runtime-manager/src/main.rs +++ b/runtime-manager/src/main.rs @@ -1,5 +1,7 @@ //! The Runtime Manager enclave //! +//! Includes the entry point for the Nitro and Linux backends. +//! //! ## Authors //! //! The Veracruz Development Team. @@ -16,6 +18,8 @@ #[cfg(feature = "tz")] pub mod runtime_manager_trustzone; +#[cfg(feature = "linux")] +pub mod runtime_manager_linux; #[cfg(feature = "tz")] pub use crate::runtime_manager_trustzone::*; @@ -24,15 +28,20 @@ pub mod runtime_manager_icecap; #[cfg(feature = "icecap")] pub use crate::runtime_manager_icecap::*; -pub mod managers; - #[cfg(feature = "nitro")] pub mod runtime_manager_nitro; +pub mod managers; mod runtime_manager; #[cfg(feature = "nitro")] fn main() -> Result<(), String> { runtime_manager_nitro::nitro_main() - .map_err(|err| format!("Runtime Manager::main nitro_main returned error:{:?}", err)) + .map_err(|err| format!("Runtime Manager::main nitro_main returned error: {:?}", err)) +} + +#[cfg(feature = "linux")] +fn main() -> Result<(), String> { + runtime_manager_linux::linux_main() + .map_err(|err| format!("Runtime Manager::main linux_main returned error: {:?}", err)) } diff --git a/runtime-manager/src/managers/error.rs b/runtime-manager/src/managers/error.rs index 923398604..422e9962b 100644 --- a/runtime-manager/src/managers/error.rs +++ b/runtime-manager/src/managers/error.rs @@ -12,7 +12,9 @@ use err_derive::Error; #[cfg(feature = "nitro")] use nix; -#[cfg(any(feature = "tz", feature = "nitro", feature = "icecap"))] +#[cfg(feature = "linux")] +use std::io::Error as IOError; +#[cfg(any(feature = "tz", feature = "linux", feature = "nitro", feature = "icecap"))] use std::sync::PoisonError; #[cfg(feature = "sgx")] use std::sync::PoisonError; @@ -26,6 +28,9 @@ use veracruz_utils::platform::nitro::nitro::NitroRootEnclaveMessage; #[derive(Debug, Error)] pub enum RuntimeManagerError { + #[cfg(feature = "linux")] + #[error(display = "RuntimeManager: CommandLineArguments")] + CommandLineArguments, #[error(display = "RuntimeManager: SessionManagerError: {:?}.", _0)] SessionManagerError(#[error(source)] session_manager::SessionManagerError), #[error(display = "RuntimeManager: TransportProtocolError: {:?}.", _0)] @@ -63,19 +68,19 @@ pub enum RuntimeManagerError { #[error(display = "RuntimeManager: Socket Error: {:?}", _0)] SocketError(nix::Error), #[cfg(feature = "nitro")] - #[error(display = "RuntimeManager: Veracruz Socket error:{:?}", _0)] + #[error(display = "RuntimeManager: Veracruz Socket error: {:?}", _0)] VeracruzSocketError(SocketError), - #[cfg(any(feature = "nitro", feature = "icecap"))] - #[error(display = "RuntimeManager: Bincode error:{:?}", _0)] + #[cfg(any(feature = "linux", feature = "nitro", feature = "icecap"))] + #[error(display = "RuntimeManager: Bincode error: {:?}", _0)] BincodeError(bincode::Error), #[cfg(feature = "nitro")] - #[error(display = "RuntimeManager: NSM Lib error:{:?}", _0)] + #[error(display = "RuntimeManager: NSM Lib error: {:?}", _0)] NsmLibError(i32), #[cfg(feature = "nitro")] - #[error(display = "RuntimeManager: NSM Error code:{:?}", _0)] + #[error(display = "RuntimeManager: NSM Error code: {:?}", _0)] NsmErrorCode(nsm_io::ErrorCode), #[cfg(feature = "nitro")] - #[error(display = "RuntimeManager: wrong message type received:{:?}", _0)] + #[error(display = "RuntimeManager: wrong message type received: {:?}", _0)] WrongMessageTypeError(NitroRootEnclaveMessage), #[error( display = "RuntimeManager: Data wrong size for field {:?}. Wanted:{:?}, got:{:?}", @@ -88,6 +93,9 @@ pub enum RuntimeManagerError { RingKeyRejected(ring::error::KeyRejected), #[error(display = "RuntimeManager: Certificate error:{:?}", _0)] CertError(CertError), + #[cfg(feature = "linux")] + #[error(display = "RuntimeManager: IO error: {:?}", _0)] + IOError(IOError), } impl From> for RuntimeManagerError { diff --git a/runtime-manager/src/managers/execution_engine_manager.rs b/runtime-manager/src/managers/execution_engine_manager.rs index 12963e34b..d00137ec9 100644 --- a/runtime-manager/src/managers/execution_engine_manager.rs +++ b/runtime-manager/src/managers/execution_engine_manager.rs @@ -24,7 +24,7 @@ use transport_protocol::transport_protocol::{ //////////////////////////////////////////////////////////////////////////////// lazy_static! { - //TODO, wrap into a chihuahua management object. + // TODO: wrap into a runtime manager management object. static ref INCOMING_BUFFER_HASH: Mutex>> = Mutex::new(HashMap::new()); } diff --git a/runtime-manager/src/managers/mod.rs b/runtime-manager/src/managers/mod.rs index d383b5b3f..8ccddd30d 100644 --- a/runtime-manager/src/managers/mod.rs +++ b/runtime-manager/src/managers/mod.rs @@ -19,6 +19,7 @@ use sgx_types::sgx_status_t; #[cfg(feature = "sgx")] use std::ffi::CString; use std::sync::Mutex; + use std::{ collections::HashMap, string::String, @@ -310,4 +311,14 @@ fn print_message(message: String, code: u32) { message ); } + #[cfg(feature = "linux")] + if code == 0 { + eprintln!("Enclave debug message \"{}\"", message); + } else { + eprintln!( + "Enclave returns error code {} and message \"{}\"", + code, + message + ); + } } diff --git a/runtime-manager/src/runtime_manager.rs b/runtime-manager/src/runtime_manager.rs index 0dd31106e..83d1ca37c 100644 --- a/runtime-manager/src/runtime_manager.rs +++ b/runtime-manager/src/runtime_manager.rs @@ -9,13 +9,13 @@ //! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for //! information on licensing and copyright. +use std::vec::Vec; + use crate::managers::RuntimeManagerError; /// Break up a single array slice containing multiple certs, using the lengths in cert_lengths, into a 2D Vec -pub fn break_up_cert_array(cert_array: &[u8], cert_lengths: &[u32]) -> Result>, RuntimeManagerError> { - - let mut certs: std::vec::Vec > = std::vec::Vec::new(); - +pub fn break_up_cert_array(cert_array: &[u8], cert_lengths: &[u32]) -> Result>, RuntimeManagerError> { + let mut certs: Vec> = Vec::new(); let mut aggregate_length: usize = 0; // break the `cert_array` up according to the values in `certificate_lengths` // and place them in `certs` @@ -26,4 +26,4 @@ pub fn break_up_cert_array(cert_array: &[u8], cert_lengths: &[u32]) -> Result, + _challenge_id: i32, +) -> RuntimeManagerMessage { + if let Err(e) = init_session_manager() { + error!( + "Failed to initialize session manager. Error produced: {:?}.", + e + ); + + return RuntimeManagerMessage::Status(VMStatus::Fail); + } + + if let Err(e) = load_policy(&policy_json) { + error!("Failed to load policy. Error produced: {:?}.", e); + + return RuntimeManagerMessage::Status(VMStatus::Fail); + } + + info!("Session manager initialized with policy."); + + RuntimeManagerMessage::Status(VMStatus::Success) +} + +//////////////////////////////////////////////////////////////////////////////// +// Entry point and message dispatcher. +//////////////////////////////////////////////////////////////////////////////// + +/// Main entry point for Linux: parses command line arguments to find the port +/// number we should be listening on for incoming connections from the Veracruz +/// server. Parses incoming messages, and acts on them. +pub fn linux_main() -> Result<(), RuntimeManagerError> { + env_logger::init(); + + let matches = App::new("Linux runtime manager enclave") + .author("The Veracruz Development Team") + .arg( + Arg::with_name("port") + .short("p") + .long("port") + .takes_value(true) + .required(true) + .help("Port to listen for new connections on.") + .value_name("PORT"), + ) + .get_matches(); + + let port = if let Some(port) = matches.value_of("port") { + info!("Received {} as port to listen on.", port); + port + } else { + error!("Did not receive any port to listen on. Exiting..."); + return Err(RuntimeManagerError::CommandLineArguments); + }; + + let address = format!("{}:{}", INCOMING_ADDRESS, port); + + info!("Preparing to listen on {}.", address); + + let listener = TcpListener::bind(&address).map_err(|e| { + error!("Could not bind TCP listener. Error produced: {}.", e); + + RuntimeManagerError::IOError(e) + })?; + + info!("TCP listener created on {}.", address); + + let (mut fd, client_addr) = listener.accept().map_err(|ioerr| { + error!( + "Failed to accept any incoming TCP connection. Error produced: {}.", + ioerr + ); + RuntimeManagerError::IOError(ioerr) + })?; + + info!("TCP listener connected on {:?}.", client_addr); + + let mut abort = false; + + while !abort { + info!("Listening for incoming message..."); + + let received_buffer: Vec = receive_buffer(&mut fd).map_err(|err| { + error!("Failed to receive message. Error produced: {}.", err); + RuntimeManagerError::IOError(err) + })?; + + let received_message: RuntimeManagerMessage = + deserialize(&received_buffer).map_err(|derr| { + error!( + "Failed to deserialize received message. Error produced: {}.", + derr + ); + RuntimeManagerError::BincodeError(derr) + })?; + + info!("Received message: {:?}.", received_message); + + let return_message = match received_message { + RuntimeManagerMessage::Initialize(policy_json, challenge, challenge_id) => { + info!("Initializing enclave."); + + initialize(policy_json, challenge, challenge_id) + } + RuntimeManagerMessage::GetCSR => { + info!("Obtaining certificate signing request."); + + let csr = generate_csr().map_err(|e| { + error!( + "Failed to generate certificate signing request. Error produced: {:?}.", + e + ); + + e + })?; + + RuntimeManagerMessage::GeneratedCSR(csr) + } + RuntimeManagerMessage::SetCertificateChain(chain) => { + info!("Setting certificate chain."); + + load_cert_chain(&chain).map_err(|e| { + error!("Failed to set certificate chain. Error produced: {:?}.", e); + + e + })?; + + RuntimeManagerMessage::Status(VMStatus::Success) + } + RuntimeManagerMessage::NewTLSSession => { + info!("Initiating new TLS session."); + + new_session() + .map(|session_id| RuntimeManagerMessage::TLSSession(session_id)) + .unwrap_or_else(|e| { + error!( + "Could not initiate new TLS session. Error produced: {:?}.", + e + ); + RuntimeManagerMessage::Status(VMStatus::Fail) + }) + } + RuntimeManagerMessage::CloseTLSSession(session_id) => { + info!("Closing TLS session."); + + close_session(session_id) + .map(|_e| RuntimeManagerMessage::Status(VMStatus::Success)) + .unwrap_or_else(|e| { + error!("Failed to close TLS session. Error produced: {:?}.", e); + RuntimeManagerMessage::Status(VMStatus::Fail) + }) + } + RuntimeManagerMessage::GetTLSDataNeeded(session_id) => { + info!("Checking whether TLS data is needed."); + + get_data_needed(session_id) + .map(|needed| RuntimeManagerMessage::TLSDataNeeded(needed)) + .unwrap_or_else(|e|{ + error!("Failed to check whether further TLS data needed. Error produced: {:?}.", e); + RuntimeManagerMessage::Status(VMStatus::Fail) + }) + } + RuntimeManagerMessage::GetTLSData(session_id) => { + info!("Retrieving TLS data."); + + get_data(session_id) + .map(|(active, data)| RuntimeManagerMessage::TLSData(data, active)) + .unwrap_or_else(|e| { + error!("Failed to retrieve TLS data. Error produced: {:?}.", e); + RuntimeManagerMessage::Status(VMStatus::Fail) + }) + } + RuntimeManagerMessage::SendTLSData(session_id, tls_data) => { + info!("Sending TLS data."); + + send_data(session_id, &tls_data) + .map(|_| RuntimeManagerMessage::Status(VMStatus::Success)) + .unwrap_or_else(|e| { + error!("Failed to send TLS data. Error produced: {:?}.", e); + RuntimeManagerMessage::Status(VMStatus::Fail) + }) + } + RuntimeManagerMessage::ResetEnclave => { + info!("Shutting down enclave."); + + abort = true; + + RuntimeManagerMessage::Status(VMStatus::Success) + } + otherwise => { + error!("Received unknown or unimplemented opcode: {:?}.", otherwise); + RuntimeManagerMessage::Status(VMStatus::Unimplemented) + } + }; + + let return_buffer = serialize(&return_message).map_err(|serr| { + error!( + "Failed to serialize returned message. Error produced: {}.", + serr + ); + RuntimeManagerError::BincodeError(serr) + })?; + + info!("Sending message: {:?}.", return_message); + + send_buffer(&mut fd, &return_buffer).map_err(|e| { + error!("Failed to send message. Error produced: {}.", e); + RuntimeManagerError::IOError(e) + })?; + } + + Ok(()) +} diff --git a/runtime-manager/src/runtime_manager_nitro.rs b/runtime-manager/src/runtime_manager_nitro.rs index df0d0d51e..f060e7286 100644 --- a/runtime-manager/src/runtime_manager_nitro.rs +++ b/runtime-manager/src/runtime_manager_nitro.rs @@ -1,6 +1,6 @@ //! AWS Nitro-Enclaves-specific material for the Runtime Manager enclave //! -//! ## Authors +//! ## Authors //! //! The Veracruz Development Team. //! @@ -19,8 +19,9 @@ use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType}; use nsm_io; use nsm_lib; use std::os::unix::io::AsRawFd; -use veracruz_utils::platform::nitro::nitro::{ - NitroRootEnclaveMessage, NitroStatus, RuntimeManagerMessage, +use veracruz_utils::platform::{ + nitro::nitro::NitroRootEnclaveMessage, + vm::{RuntimeManagerMessage, VMStatus}, }; use crate::managers; @@ -84,7 +85,7 @@ pub fn nitro_main() -> Result<(), RuntimeManagerError> { let ns_result = managers::session_manager::new_session(); let return_message: RuntimeManagerMessage = match ns_result { Ok(session_id) => RuntimeManagerMessage::TLSSession(session_id), - Err(_) => RuntimeManagerMessage::Status(NitroStatus::Fail), + Err(_) => RuntimeManagerMessage::Status(VMStatus::Fail), }; return_message } @@ -92,8 +93,8 @@ pub fn nitro_main() -> Result<(), RuntimeManagerError> { println!("runtime_manager_nitro::main CloseTLSSession"); let cs_result = managers::session_manager::close_session(session_id); let return_message: RuntimeManagerMessage = match cs_result { - Ok(_) => RuntimeManagerMessage::Status(NitroStatus::Success), - Err(_) => RuntimeManagerMessage::Status(NitroStatus::Fail), + Ok(_) => RuntimeManagerMessage::Status(VMStatus::Success), + Err(_) => RuntimeManagerMessage::Status(VMStatus::Fail), }; return_message } @@ -101,7 +102,7 @@ pub fn nitro_main() -> Result<(), RuntimeManagerError> { println!("runtime_manager_nitro::main GetTLSDataNeeded"); let return_message = match managers::session_manager::get_data_needed(session_id) { Ok(needed) => RuntimeManagerMessage::TLSDataNeeded(needed), - Err(_) => RuntimeManagerMessage::Status(NitroStatus::Fail), + Err(_) => RuntimeManagerMessage::Status(VMStatus::Fail), }; return_message } @@ -109,8 +110,8 @@ pub fn nitro_main() -> Result<(), RuntimeManagerError> { println!("runtime_manager_nitro::main SendTLSData"); let return_message = match managers::session_manager::send_data(session_id, &tls_data) { - Ok(_) => RuntimeManagerMessage::Status(NitroStatus::Success), - Err(_) => RuntimeManagerMessage::Status(NitroStatus::Fail), + Ok(_) => RuntimeManagerMessage::Status(VMStatus::Success), + Err(_) => RuntimeManagerMessage::Status(VMStatus::Fail), }; return_message } @@ -120,18 +121,18 @@ pub fn nitro_main() -> Result<(), RuntimeManagerError> { Ok((active, output_data)) => { RuntimeManagerMessage::TLSData(output_data, active) } - Err(_) => RuntimeManagerMessage::Status(NitroStatus::Fail), + Err(_) => RuntimeManagerMessage::Status(VMStatus::Fail), }; return_message } RuntimeManagerMessage::ResetEnclave => { // Do nothing here for now println!("runtime_manager_nitro::main ResetEnclave"); - RuntimeManagerMessage::Status(NitroStatus::Success) + RuntimeManagerMessage::Status(VMStatus::Success) } _ => { println!("runtime_manager_nitro::main Unknown Opcode"); - RuntimeManagerMessage::Status(NitroStatus::Unimplemented) + RuntimeManagerMessage::Status(VMStatus::Unimplemented) } }; let return_buffer = bincode::serialize(&return_message) @@ -196,5 +197,5 @@ fn initialize( println!("runtime_manager_nitro::initialize started"); managers::session_manager::load_cert_chain(cert_chain)?; - return Ok(RuntimeManagerMessage::Status(NitroStatus::Success)); + return Ok(RuntimeManagerMessage::Status(VMStatus::Success)); } diff --git a/sdk/freestanding-execution-engine/Cargo.toml b/sdk/freestanding-execution-engine/Cargo.toml index f0b7399d2..8a4b28389 100644 --- a/sdk/freestanding-execution-engine/Cargo.toml +++ b/sdk/freestanding-execution-engine/Cargo.toml @@ -14,7 +14,7 @@ clap = "2.33.3" bitflags = "=1.2.1" env_logger = "0.7.1" http = "=0.2.4" -log = "0.4.8" +log = "=0.4.13" serde = { version = "1.0.103", features = ["derive"] } toml = "0.5.5" wast = "=35.0.0" diff --git a/test-collateral/Makefile b/test-collateral/Makefile index d2a3587d7..445962044 100644 --- a/test-collateral/Makefile +++ b/test-collateral/Makefile @@ -207,7 +207,7 @@ dual_parallel_policy.json: pgen css-$(TEE).bin program_client_cert.pem data_clie ./pgen --certificate program_client_cert.pem --capability "linear-regression.wasm : $(WRITE_RIGHT)" \ --certificate data_client_cert.pem --capability "input-0 : $(WRITE_RIGHT), output : $(READ_RIGHT), linear-regression.wasm : $(WRITE_RIGHT)" \ --binary linear-regression.wasm --capability "input-0 : $(READ_RIGHT), output : $(WRITE_RIGHT)" \ - --veracruz-server-ip 127.0.0.1:3015 --proxy-attestation-server-ip 127.0.0.1:3010 --output-policy-file $@ --certificate-expiry $(CERTIFICATE_EXPIRY) \ + --veracruz-server-ip 127.0.0.1:3015 --proxy-attestation-server-ip 127.0.0.1:3010 --output-policy-file $@ --certificate-expiry $(CERTIFICATE_EXPIRY) \ --execution-strategy Interpretation \ --stdin "stdin : $(READ_RIGHT)" --stdout "stdout : $(WRITE_RIGHT)" --stderr "stderr : $(WRITE_RIGHT)" \ --enable-clock true \ @@ -220,8 +220,8 @@ triple_policy.json: pgen css-$(TEE).bin program_client_cert.pem data_client_cert --certificate result_client_cert.pem --capability "output : $(READ_RIGHT), linear-regression.wasm : $(WRITE_RIGHT)" \ --binary linear-regression.wasm --capability "input-0 : $(READ_RIGHT), output : $(WRITE_RIGHT)" \ --veracruz-server-ip 127.0.0.1:3016 --proxy-attestation-server-ip 127.0.0.1:3010 --output-policy-file $@ --certificate-expiry $(CERTIFICATE_EXPIRY) \ - --execution-strategy Interpretation \ - --stdin "stdin : $(READ_RIGHT)" --stdout "stdout : $(WRITE_RIGHT)" --stderr "stderr : $(WRITE_RIGHT)" \ + --execution-strategy Interpretation \ + --stdin "stdin : $(READ_RIGHT)" --stdout "stdout : $(WRITE_RIGHT)" --stderr "stderr : $(WRITE_RIGHT)" \ --enable-clock true \ --css-file $(RUNTIME_MANAGER_CSS_BIN) --pcr-file $(RUNTIME_MANAGER_PCR0) \ --proxy-attestation-server-cert ../test-collateral/CACert.pem @@ -326,7 +326,7 @@ number-stream-accumulation.json: pgen css-$(TEE).bin program_client_cert.pem dat basic_file_read_write.json: pgen css-$(TEE).bin client_rsa_cert.pem read-file.wasm ./pgen --certificate client_rsa_cert.pem --capability "input.txt: $(WRITE_RIGHT), output : $(READ_RIGHT), read-file.wasm : $(WRITE_RIGHT)" \ --binary read-file.wasm --capability "input.txt: $(READ_RIGHT), output : $(WRITE_RIGHT)" \ - --veracruz-server-ip 127.0.0.1:3011 --proxy-attestation-server-ip 127.0.0.1:3010 --output-policy-file $@ --certificate-expiry $(CERTIFICATE_EXPIRY) \ + --veracruz-server-ip 127.0.0.1:3011 --proxy-attestation-server-ip 127.0.0.1:3010 --output-policy-file $@ --certificate-expiry $(CERTIFICATE_EXPIRY) \ --enclave-debug-mode true --execution-strategy Interpretation \ --stdin "stdin : $(READ_RIGHT)" --stdout "stdout : $(WRITE_RIGHT)" --stderr "stderr : $(WRITE_RIGHT)" \ --enable-clock true \ diff --git a/test-collateral/generate-policy/Cargo.toml b/test-collateral/generate-policy/Cargo.toml index 0accaf6d7..5717f9b27 100644 --- a/test-collateral/generate-policy/Cargo.toml +++ b/test-collateral/generate-policy/Cargo.toml @@ -14,7 +14,7 @@ clap = "2.33.3" data-encoding = "2.3.2" env_logger = "0.8.2" http = "=0.2.4" -log = "0.4.14" +log = "=0.4.13" ring = { git = "https://github.com/veracruz-project/ring.git", branch = "veracruz", features = ["non_sgx"] } policy-utils = {path = "../../policy-utils", features = ["std"]} serde = { git = "https://github.com/veracruz-project/serde.git", branch = "veracruz", features = ["std"] } diff --git a/test-collateral/generate-policy/src/main.rs b/test-collateral/generate-policy/src/main.rs index 23e279c24..945a104a4 100644 --- a/test-collateral/generate-policy/src/main.rs +++ b/test-collateral/generate-policy/src/main.rs @@ -543,14 +543,48 @@ fn compute_program_hash(argument: &PathBuf) -> String { if let Ok(mut file) = File::open(argument) { let mut buffer = vec![]; - file.read_to_end(&mut buffer).expect("Failed to read file."); + file.read_to_end(&mut buffer).unwrap_or_else(|e| { + abort_with(format!( + "Failed to read file: {:?}. Error produced: {}.", + argument, e + )); + }); return pretty_digest(&mut buffer); } else { - abort_with("Failed to open WASM program binary."); + abort_with(format!( + "Failed to open WASM program binary: {:?}.", + argument + )); } } +/// Computes the Linux hash of the Runtime Manager enclave using a SHA256 +/// digest of the runtime manager binary's content. +fn compute_linux_enclave_hash(arguments: &Arguments) -> Option { + info!("Computing Linux enclave hash."); + + let css_file = match &arguments.css_file { + None => { + warn!("No Linux CSS file specified."); + warn!("Continuing without computing a Linux runtime manager hash."); + + return None; + } + Some(css_file) => { + info!("Measuring content of: {:?}.", css_file); + + css_file + } + }; + + let hash = compute_program_hash(css_file); + + info!("Computed sha256sum of Linux CSS file: {:?}.", hash); + + Some(hash) +} + /// Computes the SGX hash of the Runtime Manager enclave making use of the external /// 'dd' and 'xxd' utilities, which are called as external processes. Returns /// `None` iff no `css.bin` file was provided as a command-line argument. @@ -849,6 +883,8 @@ fn serialize_json(arguments: &Arguments) -> Value { info!("Serializing JSON policy file."); let sgx_hash = compute_sgx_enclave_hash(arguments); + let linux_hash = compute_linux_enclave_hash(arguments); + let policy = Policy::new( serialize_identities(arguments), serialize_binaries(arguments), @@ -861,8 +897,9 @@ fn serialize_json(arguments: &Arguments) -> Value { ), serialize_enclave_certificate_timepoint(arguments), POLICY_CIPHERSUITE.to_string(), + linux_hash.clone(), sgx_hash.clone(), - // TODO should be tz_hash + // TODO: should be tz_hash sgx_hash.clone(), compute_nitro_enclave_hash(arguments), compute_icecap_enclave_hash(arguments), diff --git a/veracruz-client/Cargo.toml b/veracruz-client/Cargo.toml index 344c965ff..a4367f11c 100644 --- a/veracruz-client/Cargo.toml +++ b/veracruz-client/Cargo.toml @@ -15,10 +15,11 @@ sgx = ["sgx_types", "sgx_ucrypto", "transport-protocol/sgx_attestation"] tz = [] nitro = [] icecap = [] +linux = [] mock = ["mockall", "mockito"] # a feature to enable CLI-only dependencies # https://stackoverflow.com/questions/35711044/how-can-i-specify-binary-only-dependencies -cli = ["structopt", "env_logger", "log"] +cli = ["structopt", "env_logger"] [dependencies] # The cargo patch mechanism does NOT work when we add function into a macro_rules! @@ -47,7 +48,7 @@ sgx_types = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk. sgx_ucrypto = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } structopt = { version = "0.3", optional = true, features = ["wrap_help"] } env_logger = { version = "0.7", optional = true } -log = { version = "0.4", optional = true } +log = "=0.4.13" bitflags = "=1.2.1" backtrace = "=0.3.61" diff --git a/veracruz-client/src/veracruz_client.rs b/veracruz-client/src/veracruz_client.rs index 017959daf..1ad320d0d 100644 --- a/veracruz-client/src/veracruz_client.rs +++ b/veracruz-client/src/veracruz_client.rs @@ -11,6 +11,8 @@ use crate::error::VeracruzClientError; use policy_utils::{policy::Policy, Platform}; + +use log::{error, info}; use ring::signature::KeyPair; use rustls::Session; use std::{ @@ -248,8 +250,23 @@ impl VeracruzClient { file_name: &str, program: &Vec, ) -> Result<(), VeracruzClientError> { - self.check_policy_hash()?; - self.check_runtime_hash()?; + self.check_policy_hash().map_err(|e| { + error!( + "Policy hash incorrect when sending program. Error produced: {}.", + e + ); + + e + })?; + + self.check_runtime_hash().map_err(|e| { + error!( + "Runtime hash incorrect when sending program. Error produced: {}.", + e + ); + + e + })?; let serialized_program = transport_protocol::serialize_program(&program, file_name)?; let response = self.send(&serialized_program)?; @@ -336,6 +353,7 @@ impl VeracruzClient { fn compare_runtime_hash(&self, received: &[u8]) -> Result<(), VeracruzClientError> { let platforms = vec![ + Platform::Linux, Platform::SGX, Platform::TrustZone, Platform::Nitro, @@ -377,19 +395,29 @@ impl VeracruzClient { ]; match ues.get(&encoded_extension_id[..]) { None => { - println!("Our extension is not present. This should be fatal"); + error!("Our extension is not present. This should be fatal"); + return Err(VeracruzClientError::RuntimeHashExtensionMissingError); } Some(data) => { + info!("Certificate extension present."); + let extension_data = data .read_all(VeracruzClientError::UnableToReadError, |input| { Ok(input.read_bytes_to_end()) })?; + + info!("Certificate extension extracted correctly."); + match self.compare_runtime_hash(extension_data.as_slice_less_safe()) { - Ok(_) => return Ok(()), + Ok(_) => { + info!("Runtime hash matches."); + + return Ok(()); + } Err(err) => { - // None of the hashes matched - println!("None of the hashes matched."); + error!("Runtime hash mismatch: {}.", err); + return Err(err); } } diff --git a/veracruz-server-test/Cargo.toml b/veracruz-server-test/Cargo.toml index f9ff4c976..68b1d5eb1 100644 --- a/veracruz-server-test/Cargo.toml +++ b/veracruz-server-test/Cargo.toml @@ -10,7 +10,8 @@ description = "Veracruz integration test-suite. Tests trusted Veracruz runtime sgx = ["sgx_ucrypto", "sgx_urts", "sgx_types", "veracruz-server/sgx", "proxy-attestation-server/sgx", "transport-protocol/sgx_attestation", "psa-attestation/sgx", "policy-utils/std", "veracruz-utils/std"] tz = ["veracruz-server/tz", "proxy-attestation-server/tz", "transport-protocol/tz", "psa-attestation/tz", "policy-utils/std", "veracruz-utils/std"] nitro = ["veracruz-server/nitro", "proxy-attestation-server/nitro", "policy-utils/std", "veracruz-utils/nitro"] -icecap = ["veracruz-server/icecap", "proxy-attestation-server/icecap", "policy-utils/icecap", "veracruz-utils/icecap"] +icecap = ["veracruz-server/icecap", "proxy-attestation-server/icecap", "policy-utils/icecap", "veracruz-utils/icecap", "psa-attestation/icecap"] +linux = ["veracruz-server/linux", "policy-utils/std", "veracruz-utils/linux", "proxy-attestation-server/linux", "psa-attestation/linux"] # debug feature means the enclaves will be started in debug mode (when available) # which changes behaviors depending on the platform (for example in Debug mode, # Nitro enclave attestation documents have the PCRs zeroed out and the console @@ -32,7 +33,7 @@ untrusted = "0.6.2" base64 = "0.10.1" stringreader = "0.1.1" serde_json = "1.0" -serde = { version = "1.0.97", features = ["derive"] } +serde = { git = "https://github.com/veracruz-project/serde.git", features=["derive"], branch = "veracruz" } percent-encoding = "2.1.0" openssl = "0.10.24" rand = "0.7.0" diff --git a/veracruz-server-test/src/main.rs b/veracruz-server-test/src/main.rs index 03228850f..ddb2ecd3a 100644 --- a/veracruz-server-test/src/main.rs +++ b/veracruz-server-test/src/main.rs @@ -3,7 +3,7 @@ //! One of the main integration tests for Veracruz, as a lot of material is //! imported directly or indirectly via these tests. //! -//! ## Authors +//! ## Authors //! //! The Veracruz Development Team. //! @@ -22,14 +22,13 @@ mod tests { use lazy_static::lazy_static; use log::{debug, error, info, Level}; use rand; - use rand::Rng; use ring; use policy_utils::{policy::Policy, Platform}; use proxy_attestation_server; use serde::Deserialize; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, env, io::{Read, Write}, path::{Path, PathBuf}, @@ -45,6 +44,8 @@ mod tests { use veracruz_server::veracruz_server::*; #[cfg(feature = "icecap")] use veracruz_server::VeracruzServerIceCap as VeracruzServerEnclave; + #[cfg(feature = "linux")] + use veracruz_server::VeracruzServerLinux as VeracruzServerEnclave; #[cfg(feature = "nitro")] use veracruz_server::VeracruzServerNitro as VeracruzServerEnclave; #[cfg(feature = "sgx")] @@ -136,7 +137,8 @@ mod tests { let rst = NEXT_TICKET.fetch_add(1, Ordering::SeqCst); SETUP.call_once(|| { - println!("SETUP.call_once called"); + info!("SETUP.call_once called"); + let _main_loop_handle = std::thread::spawn(|| { let mut sys = System::new("Veracruz Proxy Attestation Server"); println!( @@ -294,7 +296,7 @@ mod tests { read_policy(policy_path(ONE_DATA_SOURCE_POLICY).as_path()).unwrap(); // start the proxy attestation server setup(policy.proxy_attestation_server_url().clone()); - let (veracruz_server, _) = init_veracruz_server_and_tls_session(&policy_json).unwrap(); + let _ = init_veracruz_server_and_tls_session(&policy_json).unwrap(); let client_cert_filename = trust_path("never_used_cert.pem"); let client_key_filename = trust_path("client_rsa_key.pem"); @@ -310,7 +312,7 @@ mod tests { /// computation: echoing /// data sources: a single input under filename `input.txt`. fn test_phase2_basic_file_read_write_no_attestation() { - let result = test_template::>( + let _result = test_template( policy_path(BASIC_FILE_READ_WRITE_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -328,8 +330,8 @@ mod tests { .as_str(), )], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] @@ -338,7 +340,7 @@ mod tests { /// computation: random-source, returning a vec of random u8 /// data sources: none fn test_phase2_random_source_no_data_no_attestation() { - let result = test_template::>( + let _result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -350,14 +352,14 @@ mod tests { ), &[], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] /// Attempt to fetch the result without program nor data fn test_phase2_random_source_no_program_no_data() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -365,13 +367,14 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } #[test] /// Attempt to provision a wrong program fn test_phase2_incorrect_program_no_attestation() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -384,13 +387,14 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } #[test] /// Attempt to use an unauthorized key fn test_phase2_random_source_no_data_no_attestation_unauthorized_key() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(UNAUTHORIZED_KEY).as_path(), @@ -403,13 +407,14 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } #[test] /// Attempt to use an unauthorized certificate fn test_phase2_random_source_no_data_no_attestation_unauthorized_certificate() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(UNAUTHORIZED_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -422,13 +427,14 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } #[test] - /// A unauthorized client attempt to connect the service + /// A unauthorized client attempted to connect the service fn test_phase2_random_source_no_data_no_attestation_unauthorized_client() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(UNAUTHORIZED_CERT).as_path(), trust_path(UNAUTHORIZED_KEY).as_path(), @@ -441,13 +447,14 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } #[test] /// Attempt to provision more data than expected fn test_phase2_random_source_one_data_no_attestation() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -466,26 +473,19 @@ mod tests { )], &[], ); - assert!(result.is_err(), "An error should occur"); - } - #[derive(Debug, Deserialize)] - struct LinearRegression { - /// Gradient of the linear relationship. - gradient: f64, - /// Y-intercept of the linear relationship. - intercept: f64, + assert!(result.is_err(), "An error should occur"); } #[test] /// Integration test: /// policy: PiProvider, DataProvider and ResultReader is the same party - /// computation: linear regression, computing the grandient and intercept, ie the LinearRegression struct, - /// given a series of point in the two-dimension space. - /// data sources: linear-regression, a vec of points in two-dimention space, representing by - /// Vec<(f64, f64)> + /// computation: linear regression, computing the gradient and intercept, + /// i.e. the LinearRegression struct, given a series of point in the + /// two-dimensional space. Data sources: linear-regression, a vec of points + /// in two-dimensional space, represented by Vec<(f64, f64)>. fn test_phase2_linear_regression_single_data_no_attestation() { - let result = test_template::( + let _result = test_template( policy_path(LINEAR_REGRESSION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -503,14 +503,14 @@ mod tests { .as_str(), )], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] /// Attempt to fetch result without data fn test_phase2_linear_regression_no_data_no_attestation() { - let result = test_template::( + let result = test_template( policy_path(LINEAR_REGRESSION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -523,6 +523,7 @@ mod tests { &[], &[], ); + assert!(result.is_err(), "An error should occur"); } @@ -540,7 +541,7 @@ mod tests { /// A standard two data source scenario, where the data provisioned in the /// reversed order (data 1, then data 0) fn test_phase2_intersection_sum_reversed_data_provisioning_two_data_no_attestation() { - let result = test_template::( + let _result = test_template( policy_path(TWO_DATA_SOURCE_INTERSECTION_SET_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -568,8 +569,8 @@ mod tests { ), ], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] @@ -578,7 +579,7 @@ mod tests { /// computation: string-edit-distance, computing the string edit distance. /// data sources: two strings fn test_phase2_string_edit_distance_two_data_no_attestation() { - let result = test_template::( + let _result = test_template( policy_path(TWO_DATA_SOURCE_STRING_EDIT_DISTANCE_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -605,20 +606,20 @@ mod tests { ), ], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] /// Integration test: /// policy: PiProvider, DataProvider and ResultReader is the same party - /// computation: linear regression, computing the grandient and intercept, ie the LinearRegression struct, - /// given a series of point in the two-dimension space. - /// data sources: linear-regression, a vec of points in two-dimention space, representing by - /// Vec<(f64, f64)> + /// computation: linear regression, computing the gradient and intercept, + /// i.e. the LinearRegression struct, given a series of point in the + /// two-dimensional space. Data sources: linear-regression, a vec of points + /// in two-dimensional space, represented by Vec<(f64, f64)> /// A standard one data source scenario with attestation. fn test_phase3_linear_regression_one_data_with_attestation() { - let result = test_template::( + let _result = test_template( policy_path(ONE_DATA_SOURCE_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -636,30 +637,18 @@ mod tests { .as_str(), )], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); - } - - #[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash)] - struct Person { - /// Name of the employee - name: String, - /// Internal ID of the employee - employee_id: String, - /// Age of the employee - age: u8, - /// Grade of the employee - grade: u8, + ) + .unwrap(); } #[test] /// Integration test: /// policy: PiProvider, DataProvider and ResultReader is the same party - /// compuatation: set intersection, computing the intersection of two sets of persons. + /// computation: set intersection, computing the intersection of two sets of persons. /// data sources: two vecs of persons, representing by Vec /// A standard two data sources scenario with attestation. fn test_phase3_private_set_intersection_two_data_with_attestation() { - let result = test_template::>( + let _result = test_template( policy_path(TWO_DATA_SOURCE_PRIVATE_SET_INTERSECTION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -686,18 +675,18 @@ mod tests { ), ], &[], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] /// Integration test: /// policy: PiProvider, DataProvider, StreamProvider and ResultReader is the same party - /// compuatation: sum of an initial f64 number and two streams of f64 numbers. + /// computation: sum of an initial f64 number and two streams of f64 numbers. /// data sources: an initial f64 value, and two vecs of f64, representing two streams. /// A standard one data source and two stream sources scenario with attestation. fn test_phase4_number_stream_accumulation_one_data_two_stream_with_attestation() { - let result = test_template::<(u64, f64)>( + let _result = test_template( policy_path(NUMBER_STREAM_ACCUMULATION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -730,14 +719,14 @@ mod tests { .as_str(), ), ], - ); - assert!(result.is_ok(), "error:{:?}", result); + ) + .unwrap(); } #[test] /// Attempt to fetch result without enough stream data. fn test_phase4_number_stream_accumulation_one_data_one_stream_with_attestation() { - let result = test_template::( + let result = test_template( policy_path(NUMBER_STREAM_ACCUMULATION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -768,7 +757,7 @@ mod tests { #[test] /// Attempt to provision stream data in the state of loading static data. fn test_phase4_number_stream_accumulation_no_data_two_stream_with_attestation() { - let result = test_template::( + let result = test_template( policy_path(NUMBER_STREAM_ACCUMULATION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -802,7 +791,7 @@ mod tests { #[test] /// Attempt to provision more stream data. fn test_phase4_number_stream_accumulation_no_data_three_stream_with_attestation() { - let result = test_template::( + let result = test_template( policy_path(NUMBER_STREAM_ACCUMULATION_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -850,7 +839,7 @@ mod tests { data_dir(LOGISTICS_REGRESSION_DATA_PATH).as_path(), |data_path| { info!("Data path: {}", data_path.to_string_lossy()); - let result = test_template::<(Vec, f64, f64)>( + let result = test_template( policy_path(IDASH2017_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -878,7 +867,7 @@ mod tests { info!("Data path: {}", data_path.to_string_lossy()); // call the test_template with info flag on, // which prints out the time - let result = test_template::>( + let result = test_template( policy_path(MACD_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -911,7 +900,7 @@ mod tests { iterate_over_data(data_dir(MACD_DATA_PATH).as_path(), |data_path| { // call the test_template with info flag on, // which prints out the time - let result = test_template::>( + let result = test_template( policy_path(MULTIPLE_KEY_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -938,7 +927,7 @@ mod tests { info!("Data path: {}", data_path.display()); // call the test_template with info flag on, // which prints out the time - let result = test_template::<(usize, u64)>( + let result = test_template( policy_path(PRIVATE_SET_INTER_SUM_POLICY).as_path(), trust_path(CLIENT_CERT).as_path(), trust_path(CLIENT_KEY).as_path(), @@ -951,6 +940,7 @@ mod tests { &[("input-0", data_path.to_string_lossy().into_owned().as_str())], &[], ); + assert!(result.is_ok(), "error:{:?}", result); }); } @@ -959,7 +949,7 @@ mod tests { /// ensuring it is a single client policy, /// and the client_cert and client_key match the policy /// The type T is the return type of the computation - fn test_template( + fn test_template( policy_path: &Path, client_cert_path: &Path, client_key_path: &Path, @@ -1002,6 +992,9 @@ mod tests { Ok(id) } })?; + + #[cfg(feature = "linux")] + let test_target_platform: Platform = Platform::Linux; #[cfg(feature = "nitro")] let test_target_platform: Platform = Platform::Nitro; #[cfg(feature = "sgx")] @@ -1015,7 +1008,7 @@ mod tests { let mut client_session = create_client_test_session(client_cert_path, client_key_path)?; info!( - " Initialasation time (μs): {}.", + " Initialization time (μs): {}.", time_init.elapsed().as_micros() ); @@ -1035,7 +1028,7 @@ mod tests { time_server_boot.elapsed().as_micros() ); - // Need to clone paths to concreate strings, + // Need to clone paths to concrete strings, // so the ownership can be transferred into a client thread. let program_path: Option = program_path.map(|p| p.to_string()); // Assuming we are using single data provider, @@ -1052,7 +1045,7 @@ mod tests { .collect(); // This is a closure, containing instructions from clients. - // A sperate thread is spawn and direcly call this closure. + // A separate thread is spawn and directly call this closure. // However if an Error pop up, the thread set the CONTINUE_FLAG to false, // hence stopping the server thread. let mut client_body = move || { @@ -1069,7 +1062,8 @@ mod tests { }; // if there is a program provided if let Some(path) = program_path.as_ref() { - let time_provosion_data = Instant::now(); + let time_provision_data = Instant::now(); + info!("Checking policy hash..."); check_policy_hash( &policy_hash, client_session_id, @@ -1078,8 +1072,13 @@ mod tests { &client_tls_tx, &client_tls_rx, )?; + + info!("Policy hash OK..."); + check_runtime_manager_hash(&policy, &client_session, &test_target_platform)?; + info!("Provisioning program..."); + let response = provision_program( Path::new(path), client_session_id, @@ -1094,7 +1093,7 @@ mod tests { ); info!( " Provisioning program time (μs): {}.", - time_provosion_data.elapsed().as_micros() + time_provision_data.elapsed().as_micros() ); } @@ -1279,8 +1278,10 @@ mod tests { time_result.elapsed().as_micros() ); info!("### Step 9. Client decodes the result."); - let result: T = pinecone::from_bytes(&response.as_slice())?; - info!(" Client received result: {:?},", result); + info!( + " Client received {} bytes as result.", + response.len() + ); } info!("------------ Stream-Result-Next End ------------"); } else { @@ -1327,9 +1328,11 @@ mod tests { time_result.elapsed().as_micros() ); info!("### Step 9. Client decodes the result."); - let result: T = pinecone::from_bytes(&response.as_slice())?; - info!(" Client received result: {:?},", result); - } + info!( + " Client received {} bytes as result.", + response.len() + ); + }; info!("### Step 10. Client shuts down Veracruz."); let time_shutdown = Instant::now(); @@ -1352,7 +1355,9 @@ mod tests { Ok::<(), VeracruzServerError>(()) }; - thread::spawn(move || { + info!("Preparing to spawn client body thread."); + + let _response = thread::spawn(move || { client_body().map_err(|e| { CONTINUE_FLAG_HASH.lock().unwrap().insert(ticket, false); e @@ -1362,10 +1367,15 @@ mod tests { // double `?` one for join and one for client_body .map_err(|e| VeracruzServerError::JoinError(e))??; + info!("Client body thread launched."); + // double `?` one for join and one for client_body server_loop_handle .join() .map_err(|e| VeracruzServerError::JoinError(e))??; + + info!("Server thread launched."); + Ok(()) } @@ -1431,7 +1441,7 @@ mod tests { fn init_veracruz_server_and_tls_session( policy_json: &str, ) -> Result<(VeracruzServerEnclave, u32), VeracruzServerError> { - let veracruz_server = VeracruzServerEnclave::new(&policy_json)?; + let mut veracruz_server = VeracruzServerEnclave::new(&policy_json)?; let one_tenth_sec = std::time::Duration::from_millis(100); std::thread::sleep(one_tenth_sec); // wait for the client to start @@ -1480,7 +1490,18 @@ mod tests { client_tls_tx: &std::sync::mpsc::Sender<(u32, std::vec::Vec)>, client_tls_rx: &std::sync::mpsc::Receiver>, ) -> Result<(), VeracruzServerError> { - let serialized_request_policy_hash = transport_protocol::serialize_request_policy_hash()?; + info!("Serializing policy hash request."); + + let serialized_request_policy_hash = transport_protocol::serialize_request_policy_hash() + .map_err(|e| { + error!( + "Failed to serialize request for policy hash. Error produced: {:?}.", + e + ); + + e + })?; + let response = client_tls_send( client_tls_tx, client_tls_rx, @@ -1488,25 +1509,41 @@ mod tests { client_session, ticket, &serialized_request_policy_hash[..], - )?; + ) + .map_err(|e| { + error!("Failed to send TLS data. Error produced: {:?}.", e); + + e + })?; + + info!("Reponse received: {:?}", response); + let parsed_response = transport_protocol::parse_runtime_manager_response(&response)?; let status = parsed_response.get_status(); + if status != transport_protocol::ResponseStatus::SUCCESS { + error!("Received non-Success status: {:?}.", status); return Err(VeracruzServerError::ResponseError( "check_policy_hash parse_runtime_manager_response", status, )); } let received_hash = std::str::from_utf8(&parsed_response.get_policy_hash().data)?; - if received_hash == expected_policy_hash { - return Ok(()); + info!("Received {:?} as hash.", received_hash); + return if received_hash == expected_policy_hash { + info!("Hash matches expected hash ({:?}).", expected_policy_hash); + Ok(()) } else { - return Err(VeracruzServerError::MismatchError { + error!( + "Hash does not match expected hash ({:?}).", + expected_policy_hash + ); + Err(VeracruzServerError::MismatchError { variable: "request_policy_hash", received: received_hash.as_bytes().to_vec(), expected: expected_policy_hash.as_bytes().to_vec(), - }); - } + }) + }; } fn compare_policy_hash(received: &[u8], policy: &Policy, platform: &Platform) -> bool { @@ -1526,9 +1563,15 @@ mod tests { Ok(bytes) => bytes, }; + info!("Comparing runtime manager hash {:?} (from policy) against {:?} (received) for platform {:?}.", expected_bytes, received, platform); + if &received[..] != expected_bytes.as_slice() { + error!("Runtime manager hash does not match."); + return false; } else { + info!("Runtime manager hash matches."); + return true; } } @@ -1539,15 +1582,18 @@ mod tests { client_session: &dyn rustls::Session, test_target_platform: &Platform, ) -> Result<(), VeracruzServerError> { - match client_session.get_peer_certificates() { + return match client_session.get_peer_certificates() { None => { - return Err(VeracruzServerError::MissingFieldError( + error!("No peer certificate found."); + + Err(VeracruzServerError::MissingFieldError( "NO PEER CERTIFICATES. WTF?", - )); + )) } Some(certs) => { let ee_cert = webpki::EndEntityCert::from(certs[0].as_ref()).unwrap(); let ues = ee_cert.unrecognized_extensions(); + // check for OUR extension let encoded_extension_id: [u8; 3] = [ VERACRUZ_RUNTIME_HASH_EXTENSION_ID[0] * 40 @@ -1557,32 +1603,36 @@ mod tests { ]; match ues.get(&encoded_extension_id[..]) { None => { - println!("Our extension is not present. This should be fatal"); - return Err(VeracruzServerError::MissingFieldError( - "MY CRAZY CUSTOM EXTENSION AIN'T TERE", - )); + error!("Our certificate extension is not present."); + + Err(VeracruzServerError::MissingFieldError( + "MY CRAZY CUSTOM EXTENSION AIN'T THERE", + )) } Some(data) => { + info!("Certificate extension found."); + let extension_data = data.read_all( VeracruzServerError::MissingFieldError( "CAN'T READ MY CRAZY CUSTOM EXTENSION", ), |input| Ok(input.read_bytes_to_end()), )?; + if !compare_policy_hash( extension_data.as_slice_less_safe(), &policy, test_target_platform, ) { - // The hashes didn't match - println!("None of the hashes matched."); + error!("None of the runtime manager hashes matched."); + return Err(VeracruzServerError::InvalidRuntimeManagerHash); } - return Ok(()); + Ok(()) } } } - } + }; } fn provision_data( @@ -1641,26 +1691,54 @@ mod tests { rx: std::sync::mpsc::Receiver<(u32, std::vec::Vec)>, ticket: u32, ) -> Result<(), VeracruzServerError> { - while *CONTINUE_FLAG_HASH.lock()?.get(&ticket).ok_or( - VeracruzServerError::MissingFieldError("CONTINUE_FLAG_HASH ticket"), - )? { + info!("Inside server TLS loop..."); + + while *CONTINUE_FLAG_HASH + .lock() + .map_err(|e| { + error!( + "Failed to obtain lock on CONTINUE_FLAG_HASH. Error produced: {:?}.", + e + ); + e + })? + .get(&ticket) + .ok_or(VeracruzServerError::MissingFieldError( + "CONTINUE_FLAG_HASH ticket", + ))? + { let received = rx.try_recv(); let (session_id, received_buffer) = received.unwrap_or_else(|_| (0, Vec::new())); if received_buffer.len() > 0 { - let (active_flag, output_data_option) = - veracruz_server.tls_data(session_id, received_buffer)?; + let (active_flag, output_data_option) = veracruz_server + .tls_data(session_id, received_buffer) + .map_err(|e| { + error!("Failed to send TLS data. Error produced: {:?}.", e); + e + })?; let output_data = output_data_option.unwrap_or_else(|| Vec::new()); + for output in output_data.iter() { if output.len() > 0 { - tx.send(output.clone())?; + tx.send(output.clone()).map_err(|e| { + error!( + "Failed to send data on TX channel. Error produced: {:?}.", + e + ); + e + })?; } } + if !active_flag { + info!("VeracruzServer TLS loop dieing due to lack of TLS data."); return Ok(()); } } } + error!("VeracruzServer TLS loop dieing due to no activity..."); + Err(VeracruzServerError::DirectStrError( "No message arrives server", )) @@ -1674,36 +1752,85 @@ mod tests { ticket: u32, send_data: &[u8], ) -> Result, VeracruzServerError> { - session.write_all(&send_data)?; + session.write_all(&send_data).map_err(|e| { + error!("Failed to send all data. Error produced: {:?}.", e); + e + })?; let mut output: std::vec::Vec = std::vec::Vec::new(); - session.write_tls(&mut output)?; + session.write_tls(&mut output).map_err(|e| { + error!("Failed to write TLS. Error produced: {:?}.", e); + e + })?; - tx.send((session_id, output))?; + tx.send((session_id, output)).map_err(|e| { + error!( + "Failed to send data on TX channel. Error produced: {:?}.", + e + ); + e + })?; - while *CONTINUE_FLAG_HASH.lock()?.get(&ticket).ok_or( - VeracruzServerError::MissingFieldError("CONTINUE_FLAG_HASH ticket"), - )? { + while *CONTINUE_FLAG_HASH + .lock() + .map_err(|e| { + error!( + "Failed to obtain lock on CONTINUE_FLAG_HASH. Error produced: {:?}.", + e + ); + e + })? + .get(&ticket) + .ok_or(VeracruzServerError::MissingFieldError( + "CONTINUE_FLAG_HASH ticket", + ))? + { let received = rx.try_recv(); if received.is_ok() && (!session.is_handshaking() || session.wants_read()) { - let received = received?; + info!("Received is OK, and we're not handshaking..."); + + let received = received.map_err(|e| { + error!("Invariant failed. Received was not OK."); + e + })?; let mut slice = &received[..]; - session.read_tls(&mut slice)?; - session.process_new_packets()?; + session.read_tls(&mut slice).map_err(|e| { + error!("Failed to read TLS. Error produced: {:?}.", e); + e + })?; + session.process_new_packets().map_err(|e| { + error!("Failed to process new packets. Error produced: {:?}.", e); + e + })?; let mut received_buffer: std::vec::Vec = std::vec::Vec::new(); - let num_bytes = session.read_to_end(&mut received_buffer)?; + let num_bytes = session.read_to_end(&mut received_buffer).map_err(|e| { + error!("Failed to read data to end. Error produced: {:?}.", e); + e + })?; + if num_bytes > 0 { + info!("Finished sending via TLS."); return Ok(received_buffer); } } else if session.wants_write() { + info!("Session wants write..."); let mut output: std::vec::Vec = std::vec::Vec::new(); - session.write_tls(&mut output)?; - let _res = tx.send((session_id, output))?; + session.write_tls(&mut output).map_err(|e| { + error!("Failed to write TLS. Error produced: {:?}.", e); + e + })?; + let _res = tx.send((session_id, output)).map_err(|e| { + error!( + "Failed to send data on TX channel. Error produced: {:?}.", + e + ); + e + })?; } } Err(VeracruzServerError::DirectStrError( diff --git a/veracruz-server/Cargo.toml b/veracruz-server/Cargo.toml index a36b54fd1..37bb3d0ba 100644 --- a/veracruz-server/Cargo.toml +++ b/veracruz-server/Cargo.toml @@ -11,10 +11,11 @@ path = "src/cli.rs" required-features = ["cli"] [features] -sgx = ["veracruz-utils/std","sgx_types", "sgx_urts", "transport-protocol/sgx_attestation", "runtime-manager-bind", "sgx-root-enclave-bind", "tempfile", "policy-utils/std"] -tz = ["veracruz-utils/std", "veracruz-utils/tz", "transport-protocol/tz", "optee-teec", "uuid", "policy-utils/tz"] +sgx = ["sgx_types", "sgx_urts", "transport-protocol/sgx_attestation", "runtime-manager-bind", "sgx-root-enclave-bind", "tempfile", "policy-utils/std", "io-utils/sgx"] +tz = ["veracruz-utils/std", "veracruz-utils/tz", "transport-protocol/tz", "optee-teec", "uuid", "policy-utils/tz", "io-utils/tz"] nitro = ["veracruz-utils/nitro", "bincode", "serde/derive", "byteorder", "nix", "ssh2", "policy-utils/std", "io-utils/nitro"] -icecap = ["veracruz-utils/icecap", "psa-attestation", "bincode", "serde/derive", "once_cell", "policy-utils/icecap"] +icecap = ["veracruz-utils/icecap", "psa-attestation", "bincode", "serde/derive", "once_cell", "policy-utils/icecap", "io-utils/icecap"] +linux = ["veracruz-utils/linux", "bincode", "serde/derive", "policy-utils/std", "io-utils/linux"] debug = [] # a feature to enable CLI-only dependencies # https://stackoverflow.com/questions/35711044/how-can-i-specify-binary-only-dependencies @@ -37,14 +38,14 @@ lazy_static = "1.4" actix-web = "3.3.2" actix-http = "2.2.0" futures = "0.3" -log = "0.4" +log = "=0.4.13" err-derive = "0.2" pinecone = "0.2" hex = "=0.4.2" webpki = { git = "https://github.com/veracruz-project/webpki.git", branch = "veracruz" } rustls = { git = "https://github.com/veracruz-project/rustls.git", branch = "veracruz" } bincode = { git = "https://github.com/veracruz-project/bincode.git", branch = "veracruz", default-features = false, optional = true } -serde = { git = "https://github.com/veracruz-project/serde.git", default-features = false, optional = true } +serde = { git = "https://github.com/veracruz-project/serde.git", default-features = false, optional = true} byteorder = { version = "1.3.2", optional = true } nix = { version = "0.15", optional = true } ssh2 = {version = "0.8.3", optional = true } diff --git a/veracruz-server/Makefile b/veracruz-server/Makefile deleted file mode 100644 index 75e7d68eb..000000000 --- a/veracruz-server/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# Makefile -# -# AUTHORS -# -# The Veracruz Development Team. -# -# COPYRIGHT -# -# See the `LICENSE_MIT.markdown` file in the Veracruz root director for licensing -# and copyright information. - -.PHONY: all policy-files sgx trustzone deprecated - -all: deprecated sgx - -WARNING_COLOR := "\e[1;33m" -INFO_COLOR := "\e[1;32m" -RESET_COLOR := "\e[0m" - -deprecated: - @echo $(WARNING_COLOR)"The default target, compiling sgx, is deprecated. Please explicitly choose target, sgx or trustzone."$(RESET_COLOR) - -################## sgx ################### -SGX_SDK ?= /work/sgxsdk - -SGX_COMMON_CFLAGS := -m64 -SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 -RUST_SGX_SDK_PATH = ../third-party/rust-sgx-sdk - -ifeq ($(SGX_DEBUG), 1) -ifeq ($(SGX_PRERELEASE), 1) -$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) -endif -endif - - -ifeq ($(SGX_DEBUG), 1) - SGX_COMMON_CFLAGS += -O0 -g -else - SGX_COMMON_CFLAGS += -O2 -endif - -CUSTOM_LIBRARY_PATH := ./lib -CUSTOM_BIN_PATH := ./bin -CUSTOM_COMMON_PATH := $(RUST_SGX_SDK_PATH)/common - - -Veracruz_Server_Rust_Flags := -Veracruz_Server_SRC_Files := $(shell find src/ -type f -name '*.rs') $(shell find src/ -type f -name 'Cargo.toml') -Veracruz_Server_Include_Paths := -I ./src -I$(SGX_SDK)/include -Veracruz_Server_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(Veracruz_Server_Include_Paths) - -Veracruz_Server_Rust_Path := ./target/debug -Veracruz_Server_Name := $(CUSTOM_BIN_PATH)/libveracruz_server.rlib - -######## Enclave Settings ######## - -Crypto_Library_Name := sgx_tcrypto - -RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lruntime_manager_enclave -L$(RUNTIME_MANAGER_PATH)/lib/ -Signed_RustEnclave_Name := $(CUSTOM_BIN_PATH)/runtime_manager.signed.so - -sgx: $(Veracruz_Server_Name) - -$(CUSTOM_LIBRARY_PATH): - mkdir -p $@ - -$(Veracruz_Server_Name): $(Veracruz_Server_SRC_Files) $(CUSTOM_LIBRARY_PATH) - @SGX_SDK=$(SGX_SDK) cargo build $(Veracruz_Server_Rust_Flags) --features sgx - @echo $(INFO_COLOR)"CARGO => $@"$(RESET_COLOR) - - -################## trustzone ################### -trustzone: $(Veracruz_Server_SRC_Files) - cargo build --features tz --target aarch64-unknown-linux-gnu --release - @echo $(INFO_COLOR)"CARGO => $^"$(RESET_COLOR) - -.PHONY: clean -clean: - @rm -f $(Veracruz_Server_Name) \ - $(RUNTIME_MANAGER_PATH)/*_t.* *_u.* lib/*.a ./bin/libveracruz_server_lib.rlib \ - Cargo.lock - @rm -rf target lib - @cargo clean diff --git a/veracruz-server/src/lib.rs b/veracruz-server/src/lib.rs index 93529d08f..3c79859d7 100644 --- a/veracruz-server/src/lib.rs +++ b/veracruz-server/src/lib.rs @@ -34,3 +34,7 @@ pub use self::veracruz_server_nitro::veracruz_server_nitro::*; mod veracruz_server_icecap; #[cfg(feature = "icecap")] pub use self::veracruz_server_icecap::*; +#[cfg(feature = "linux")] +pub mod veracruz_server_linux; +#[cfg(feature = "linux")] +pub use self::veracruz_server_linux::veracruz_server_linux::*; diff --git a/veracruz-server/src/server.rs b/veracruz-server/src/server.rs index bf857905c..6f2073fc2 100644 --- a/veracruz-server/src/server.rs +++ b/veracruz-server/src/server.rs @@ -1,6 +1,6 @@ //! The Veracruz server //! -//! ## Authors +//! ## Authors //! //! The Veracruz Development Team. //! @@ -12,6 +12,8 @@ use crate::veracruz_server::*; #[cfg(feature = "icecap")] use crate::veracruz_server_icecap::VeracruzServerIceCap as VeracruzServerEnclave; +#[cfg(feature = "linux")] +use crate::veracruz_server_linux::veracruz_server_linux::VeracruzServerLinux as VeracruzServerEnclave; #[cfg(feature = "nitro")] use crate::veracruz_server_nitro::veracruz_server_nitro::VeracruzServerNitro as VeracruzServerEnclave; #[cfg(feature = "sgx")] diff --git a/veracruz-server/src/veracruz_server.rs b/veracruz-server/src/veracruz_server.rs index b0c4a160c..8b77f1df9 100644 --- a/veracruz-server/src/veracruz_server.rs +++ b/veracruz-server/src/veracruz_server.rs @@ -1,6 +1,6 @@ //! Veracruz server //! -//! ## Authors +//! ## Authors //! //! The Veracruz Development Team. //! @@ -15,12 +15,14 @@ use actix_http::ResponseBuilder; use actix_web::{error, http::StatusCode, HttpResponse}; #[cfg(feature = "nitro")] use base64; -use curl::easy::{Easy, List}; use err_derive::Error; #[cfg(feature = "nitro")] use io_utils::nitro::NitroError; +use io_utils::{error::SocketError, http::HttpError}; use log::debug; use std::io::Read; +#[cfg(feature = "linux")] +use veracruz_utils::platform::linux::LinuxRootEnclaveResponse; pub type VeracruzServerResponder = Result; @@ -72,33 +74,35 @@ pub enum VeracruzServerError { MpscSendVecU8Error(#[error(source)] std::sync::mpsc::SendError>), #[error(display = "VeracruzServer: Mpsc TryRecvError: {}.", _0)] MpscTryRecvError(#[error(source)] std::sync::mpsc::TryRecvError), - #[error(display = "VeracruzServer: CurlError: {:?}.", _0)] - CurlError(#[error(source)] curl::Error), + /// A HTTP error was produced. + #[error(display = "Http error: {}.", _0)] + HttpError(HttpError), #[cfg(feature = "sgx")] #[error(display = "VeracruzServer: SGXError: {:?}.", _0)] SGXError(sgx_types::sgx_status_t), - #[cfg(feature = "nitro")] + #[cfg(any(feature = "linux", feature = "nitro"))] #[error(display = "VeracruzServer: BincodeError: {:?}", _0)] BincodeError(bincode::ErrorKind), - #[cfg(feature = "nitro")] + #[cfg(any(feature = "linux", feature = "nitro"))] #[error(display = "VeracruzServer: RuntimeManagerMessage::Status: {:?}", _0)] - RuntimeManagerMessageStatus(veracruz_utils::platform::nitro::nitro::RuntimeManagerMessage), - #[cfg(feature = "nitro")] - #[error(display = "VeracruzServer: NitroStatus: {:?}", _0)] - NitroStatus(veracruz_utils::platform::nitro::nitro::NitroStatus), - #[cfg(feature = "nitro")] + RuntimeManagerMessageStatus(veracruz_utils::platform::vm::RuntimeManagerMessage), + #[cfg(any(feature = "nitro", feature = "linux"))] + #[error(display = "VeracruzServer: VMStatus: {:?}", _0)] + VMStatus(veracruz_utils::platform::vm::VMStatus), + #[cfg(any(feature = "linux", feature = "nitro"))] #[error( display = "VeracruzServer: Received Invalid Runtime Manager Message: {:?}", _0 )] - InvalidRuntimeManagerMessage(veracruz_utils::platform::nitro::nitro::RuntimeManagerMessage), + InvalidRuntimeManagerMessage(veracruz_utils::platform::vm::RuntimeManagerMessage), #[cfg(feature = "nitro")] #[error( display = "VeracruzServer: Received Invalid Nitro Root Enclave Message: {:?}", _0 )] - InvalidNitroRootEnclaveMessage(veracruz_utils::platform::nitro::nitro::NitroRootEnclaveMessage), #[cfg(feature = "nitro")] + InvalidNitroRootEnclaveMessage(veracruz_utils::platform::nitro::nitro::NitroRootEnclaveMessage), + #[cfg(any(feature = "linux", feature = "nitro"))] #[error(display = "VeracruzServer: Received Invalid Protocol Buffer Message")] InvalidProtoBufMessage, #[cfg(feature = "nitro")] @@ -172,6 +176,12 @@ pub enum VeracruzServerError { DirectMessageError(String, StatusCode), #[error(display = "VeracruzServer: Error message {}.", _0)] DirectStrError(&'static str), + #[cfg(feature = "linux")] + #[error( + display = "VeracruzServer: Unexpected reply from Linux Root enclave {:?}.", + _0 + )] + LinuxRootEnclaveUnexpectedResponse(LinuxRootEnclaveResponse), #[error(display = "VeracruzServer: Unimplemented")] UnimplementedError, #[error(display = "VeracruzServer: Invalid runtime manager hash")] @@ -184,6 +194,9 @@ pub enum VeracruzServerError { // #[cfg(feature = "nitro")] #[error(display = "VeracruzServer: Base64 Decode error:{:?}", _0)] Base64Decode(base64::DecodeError), + /// Some socket-related functionality failed. + #[error(display = "VeracruzServer: socket error:{:?}", _0)] + SocketError(SocketError), /// A remote http server returned a non-success (200) status #[cfg(feature = "nitro")] #[error(display = "NitroServer: Non-Success HTTP Response received")] @@ -235,9 +248,9 @@ pub trait VeracruzServer { where Self: Sized; - fn plaintext_data(&self, data: Vec) -> Result>, VeracruzServerError>; + fn plaintext_data(&mut self, data: Vec) -> Result>, VeracruzServerError>; - fn new_tls_session(&self) -> Result; + fn new_tls_session(&mut self) -> Result; fn close_tls_session(&mut self, session_id: u32) -> Result<(), VeracruzServerError>; @@ -250,74 +263,3 @@ pub trait VeracruzServer { fn close(&mut self) -> Result; } - -pub fn send_proxy_attestation_server_start( - url_base: &str, - protocol: &str, - firmware_version: &str, -) -> Result { - let serialized_start_msg = transport_protocol::serialize_start_msg(protocol, firmware_version)?; - let encoded_start_msg: String = base64::encode(&serialized_start_msg); - let url = format!("{:}/Start", url_base); - - let received_body: String = post_buffer(&url, &encoded_start_msg)?; - - let body_vec = base64::decode(&received_body)?; - let response = transport_protocol::parse_proxy_attestation_server_response(&body_vec)?; - return Ok(response); -} - -pub fn post_buffer(url: &str, buffer: &String) -> Result { - let mut buffer_reader = stringreader::StringReader::new(buffer); - - let mut curl_request = Easy::new(); - curl_request.url(&url)?; - let mut headers = List::new(); - headers.append("Content-Type: application/octet-stream")?; - curl_request.http_headers(headers)?; - curl_request.post(true)?; - curl_request.post_field_size(buffer.len() as u64)?; - - let mut received_body = std::string::String::new(); - let mut received_header = std::string::String::new(); - { - let mut transfer = curl_request.transfer(); - - transfer.read_function(|buf| Ok(buffer_reader.read(buf).unwrap_or(0)))?; - transfer.write_function(|buf| { - received_body.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - Ok(buf.len()) - })?; - - transfer.header_function(|buf| { - received_header.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - true - })?; - - transfer.perform()?; - } - let header_lines: Vec<&str> = { - let lines = received_header.split("\n"); - lines.collect() - }; - println!( - "veracruz_server::post_buffer received header (from url:{:?}):{:?}", - url, received_header - ); - if !received_header.contains("HTTP/1.1 200 OK\r") { - return Err(VeracruzServerError::ReceivedNonSuccessPostStatusError); - } - - debug!( - "veracruz_server::post_buffer header_lines:{:?}", - header_lines - ); - - return Ok(received_body); -} diff --git a/veracruz-server/src/veracruz_server_icecap.rs b/veracruz-server/src/veracruz_server_icecap.rs index aa1ae11f7..9653145ae 100644 --- a/veracruz-server/src/veracruz_server_icecap.rs +++ b/veracruz-server/src/veracruz_server_icecap.rs @@ -9,12 +9,10 @@ //! See the `LICENSE.markdown` file in the Veracruz root directory for //! information on licensing and copyright. -use crate::{ - send_proxy_attestation_server_start, - veracruz_server::{VeracruzServer, VeracruzServerError}, -}; +use crate::veracruz_server::{VeracruzServer, VeracruzServerError}; use bincode::{deserialize, serialize}; use err_derive::Error; +use io_utils::http::{post_buffer, send_proxy_attestation_server_start}; use policy_utils::policy::Policy; use std::{ env, @@ -204,19 +202,12 @@ impl VeracruzServer for VeracruzServerIceCap { } } - let (challenge, device_id) = { - let resp = send_proxy_attestation_server_start( - policy.proxy_attestation_server_url(), - "psa", - FIRMWARE_VERSION, - )?; - if !resp.has_psa_attestation_init() { - return Err(VeracruzServerError::MissingFieldError( - "psa_attestation_init", - )); - } - transport_protocol::parse_psa_attestation_init(resp.get_psa_attestation_init())? - }; + let (device_id, challenge) = send_proxy_attestation_server_start( + policy.proxy_attestation_server_url(), + "psa", + FIRMWARE_VERSION, + ) + .map_err(VeracruzServerError::HttpError)?; let (token, csr) = match server.send(&Request::Attestation { challenge, @@ -239,7 +230,7 @@ impl VeracruzServer for VeracruzServerIceCap { "{:}/PSA/AttestationToken", policy.proxy_attestation_server_url() ); - let resp = crate::post_buffer(&url, &req)?; + let resp = post_buffer(&url, &req).map_err(VeracruzServerError::HttpError)?; let resp = base64::decode(&resp)?; let pasr = transport_protocol::parse_proxy_attestation_server_response(&resp) .map_err(VeracruzServerError::TransportProtocolError)?; @@ -264,11 +255,11 @@ impl VeracruzServer for VeracruzServerIceCap { Ok(server) } - fn plaintext_data(&self, _data: Vec) -> Result>> { + fn plaintext_data(&mut self, _data: Vec) -> Result>> { unimplemented!() } - fn new_tls_session(&self) -> Result { + fn new_tls_session(&mut self) -> Result { match self.send(&Request::NewTlsSession)? { Response::NewTlsSession(session_id) => Ok(session_id), resp => Err(VeracruzServerError::IceCapError( diff --git a/veracruz-server/src/veracruz_server_linux.rs b/veracruz-server/src/veracruz_server_linux.rs new file mode 100644 index 000000000..cc20a1a2b --- /dev/null +++ b/veracruz-server/src/veracruz_server_linux.rs @@ -0,0 +1,753 @@ +//! Linux-specific material for the Veracruz server +//! +//! ## Authors +//! +//! The Veracruz Development Team. +//! +//! ## Licensing and copyright notice +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for +//! information on licensing and copyright. + +#[cfg(feature = "linux")] +pub mod veracruz_server_linux { + + use log::{error, info}; + + use std::{ + net::{Shutdown, TcpStream}, + process::{Child, Command}, + thread::sleep, + time::Duration, + }; + + use crate::{veracruz_server::VeracruzServer, VeracruzServerError}; + use io_utils::tcp::{receive_message, send_message}; + use policy_utils::policy::Policy; + use veracruz_utils::platform::{ + linux::{LinuxRootEnclaveMessage, LinuxRootEnclaveResponse}, + vm::{RuntimeManagerMessage, VMStatus}, + }; + + //////////////////////////////////////////////////////////////////////////// + // Constants. + //////////////////////////////////////////////////////////////////////////// + + /// Path to the pre-built Linux root enclave. + const LINUX_ROOT_ENCLAVE_PATH: &'static str = + "../linux-root-enclave/target/release/linux-root-enclave"; + /// Port to communicate with the Linux root enclave on. + const LINUX_ROOT_ENCLAVE_PORT: &'static str = "5021"; + /// IP address to use when communicating with the Linux root enclave. + const LINUX_ROOT_ENCLAVE_ADDRESS: &'static str = "127.0.0.1"; + /// IP address to use when communicating with the Runtime Manager enclave. + const RUNTIME_MANAGER_ENCLAVE_ADDRESS: &'static str = "127.0.0.1"; + /// Delay (in seconds) to use when spawning the Linux root enclave to + /// ensure that everything is started before proceeding with communication + /// between the server and enclave. + const LINUX_ROOT_ENCLAVE_SPAWN_DELAY_SECONDS: u64 = 2; + + /// A struct capturing all the metadata needed to start and communicate with + /// the Linux root enclave. + pub struct VeracruzServerLinux { + /// A handle to the Linux root enclave's process. + linux_root_process: Child, + /// The socket used to communicate with the Runtime Manager enclave. + runtime_manager_socket: TcpStream, + /// The socket used to communicate with the Linux Root enclave. + linux_root_socket: TcpStream, + } + + impl VeracruzServerLinux { + /// Tears down the Linux Root enclave, and all spawned Runtime Manager enclaves creates + /// by it. Then closes TCP connections and kills the Linux Root enclave process. Tries + /// to be as liberal in ignoring erroneous conditions as possible in order to kill as + /// many things as we can. + fn teardown(&mut self) -> Result { + info!("Tearing down Linux Root enclave, and all Runtime Manager enclaves spawned."); + + info!("Sending shutdown message."); + + send_message( + &mut self.linux_root_socket, + &LinuxRootEnclaveMessage::Shutdown, + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Shutdown message successfully sent."); + + info!("Awaiting response..."); + + let response: LinuxRootEnclaveResponse = receive_message(&mut self.linux_root_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match response { + LinuxRootEnclaveResponse::ShuttingDown => { + info!("Linux Root enclave and Runtime Manager enclaves killed."); + info!("Closing TCP connections."); + + let _result = self.runtime_manager_socket.shutdown(Shutdown::Both); + let _result = self.linux_root_socket.shutdown(Shutdown::Both); + let _result = self.linux_root_process.kill(); + + info!("Connections and processes killed."); + + Ok(true) + } + otherwise => { + error!( + "Received unexpected response from Linux Root enclave: {:?}.", + otherwise + ); + + info!("Closing TCP connections anyway..."); + + let _result = self.runtime_manager_socket.shutdown(Shutdown::Both); + let _result = self.linux_root_socket.shutdown(Shutdown::Both); + let _result = self.linux_root_process.kill(); + + info!("Connections and processes killed."); + + Ok(true) + } + } + } + + /// Returns `Ok(true)` iff further TLS data can be read from the socket + /// connecting the Veracruz server and the Linux root enclave. + /// Returns `Ok(false)` iff no further TLS data can be read. + /// + /// Returns an appropriate error if: + /// + /// 1. The request could not be serialized, or sent to the enclave. + /// 2. The response could be not be received, or deserialized. + /// 3. The response was received and deserialized correctly, but was of + /// an unexpected form. + pub fn tls_data_needed(&mut self, session_id: u32) -> Result { + info!("Checking whether TLS data can be read from Runtime Manager enclave (with session: {}).", session_id); + + info!("Sending TLS data check message."); + + send_message( + &mut self.runtime_manager_socket, + &RuntimeManagerMessage::GetTLSDataNeeded(session_id), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("TLS data check message successfully sent."); + + info!("Awaiting response..."); + + let received: RuntimeManagerMessage = receive_message(&mut self.runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match received { + RuntimeManagerMessage::TLSDataNeeded(response) => { + info!( + "Runtime Manager enclave can have further TLS data read: {}.", + response + ); + + Ok(response) + } + otherwise => { + error!( + "Runtime Manager enclave returned unexpected response. Received: {:?}.", + otherwise + ); + + Err(VeracruzServerError::InvalidRuntimeManagerMessage(otherwise)) + } + } + } + + /// Reads TLS data from the Runtime Manager enclave. Implicitly assumes + /// that the Runtime Manager enclave has more data to be read. Returns + /// `Ok((alive_status, buffer))` if more TLS data could be read from the + /// enclave, where `buffer` is a buffer of TLS data and `alive_status` + /// captures the status of the TLS connection. + /// + /// Returns an appropriate error if: + /// + /// 1. The TLS data request message cannot be serialized, or transmitted + /// to the enclave. + /// 2. A response is not received back from the Enclave in response to + /// the message sent in (1) above, or the message cannot be + /// deserialized. + /// 3. The Runtime Manager enclave sends back a message indicating that + /// it was not expecting further TLS data to be requested. + pub fn read_tls_data( + &mut self, + session_id: u32, + ) -> Result<(bool, Vec), VeracruzServerError> { + info!( + "Reading TLS data from Runtime Manager enclave (with session: {}).", + session_id + ); + + info!("Sending get TLS data message."); + + send_message( + &mut self.runtime_manager_socket, + &RuntimeManagerMessage::GetTLSData(session_id), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Get TLS data message successfully sent."); + + info!("Awaiting response..."); + + let received: RuntimeManagerMessage = receive_message(&mut self.runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match received { + RuntimeManagerMessage::TLSData(buffer, alive) => { + info!("{} bytes of TLS data received from Runtime Manager enclave (alive status: {}).", buffer.len(), alive); + + Ok((alive, buffer)) + } + otherwise => { + error!("Unexpected reply received back from Runtime Manager enclave. Recevied: {:?}.", otherwise); + + Err(VeracruzServerError::InvalidRuntimeManagerMessage(otherwise)) + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // Trait implementations. + //////////////////////////////////////////////////////////////////////////// + + /// An implementation of the `Drop` trait that forcibly kills the runtime + /// manager enclave, and closes the socket used for communicating with it, when + /// a `VeracruzServerLinux` struct is about to go out of scope. + impl Drop for VeracruzServerLinux { + fn drop(&mut self) { + info!("Dropping VeracruzServerLinux object, shutting down enclaves..."); + if let Err(error) = self.teardown() { + error!( + "Failed to forcibly kill Runtime Manager and Linux Root enclave process. Error produced: {:?}.", + error + ); + } + info!("VeracruzServerLinux object killed."); + } + } + + impl VeracruzServer for VeracruzServerLinux { + /// Creates a new instance of the `VeracruzServerLinux` type. To do + /// this, we: + /// + /// 1. Spawn the Linux Root enclave, + /// 2. Establish a socket connection between us and the Linux Root enclave, + /// 3. Ask the Linux Root enclave to spawn a new Runtime Manager enclave, + /// 4. Establish a socket connection to the Runtime Manager enclave on + /// the port assigned to us by the Linux Root enclave, + /// 4. Send initializing messages to both enclaves. + /// 5. Start the proxy attestation process, getting the certificate + /// chain from the Linux Root Enclave (which handles this process on + /// Linux). + /// 6. Register the resulting certificate chain with the Runtime Manager + /// enclave, in preparation for TLS connections. + /// + /// Note that this process can fail for a number of reasons, e.g. the + /// enclaves may not be spawnable, socket connections can fail, the + /// initialization processes of the two enclaves may fail, and so on. + /// In those cases, an explicit error is returned. Otherwise, we return + /// `Ok(vsl)`. + fn new(policy: &str) -> Result + where + Self: Sized, + { + info!("Creating new Veracruz Server instance for Linux."); + + // TODO: add in dummy measurement and attestation token issuance here + // which will use fields from the JSON policy file. + let policy_json = Policy::from_json(policy).map_err(|e| { + error!( + "Failed to parse Veracruz policy file. Error produced: {:?}.", + e + ); + + VeracruzServerError::VeracruzUtilError(e) + })?; + + info!("Successfully parsed JSON policy file."); + + let proxy_attestation_server_url = policy_json.proxy_attestation_server_url(); + + info!( + "Launching Linux Root enclave: {} with proxy attestation server URL: {}.", + LINUX_ROOT_ENCLAVE_PATH, proxy_attestation_server_url + ); + + let mut linux_root_process = Command::new(LINUX_ROOT_ENCLAVE_PATH) + .arg("--proxy-attestation-server") + .arg(proxy_attestation_server_url) + .spawn() + .map_err(|e| { + error!( + "Failed to launch Linux Root enclave. Error produced: {:?}.", + e + ); + + VeracruzServerError::IOError(e) + })?; + + info!( + "Linux Root enclave spawned. Waiting {:?} seconds...", + LINUX_ROOT_ENCLAVE_SPAWN_DELAY_SECONDS + ); + + sleep(Duration::from_secs(LINUX_ROOT_ENCLAVE_SPAWN_DELAY_SECONDS)); + + let linux_root_enclave_address = + format!("{}:{}", LINUX_ROOT_ENCLAVE_ADDRESS, LINUX_ROOT_ENCLAVE_PORT); + + info!( + "Connecting to Linux Root enclave on {}.", + linux_root_enclave_address + ); + + let mut linux_root_socket = + TcpStream::connect(linux_root_enclave_address).map_err(|error| { + error!( + "Failed to connect to Linux Root enclave. Error produced: {:?}.", + error + ); + error!("Killing Linux Root enclave."); + + // NB: we're in the process of failing here anyway, so we eat any error returned + // from this subprocess kill command. + let _result = linux_root_process.kill(); + + error + })?; + + info!( + "Now connected to Linux Root enclave on: {:?}.", + linux_root_socket.peer_addr() + ); + + info!("Requesting spawning of new Runtime Enclave."); + + send_message( + &mut linux_root_socket, + &LinuxRootEnclaveMessage::SpawnNewApplicationEnclave, + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Spawn request sent."); + + info!("Awaiting response..."); + + let response: LinuxRootEnclaveResponse = receive_message(&mut linux_root_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + let runtime_manager_port = + if let LinuxRootEnclaveResponse::EnclaveSpawned(port) = response { + info!("Runtime Manager enclave assigned port: {}.", port); + port + } else { + error!( + "Unexpected response received from Linux Root enclave. Received: {:?}.", + response + ); + + return Err(VeracruzServerError::LinuxRootEnclaveUnexpectedResponse( + response, + )); + }; + + info!("Requesting proxy attestation start."); + + send_message( + &mut linux_root_socket, + &LinuxRootEnclaveMessage::StartProxyAttestation, + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Proxy attestation start message successfully sent."); + + info!("Awaiting response..."); + + let response: LinuxRootEnclaveResponse = receive_message(&mut linux_root_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + let (challenge, challenge_id) = match response { + LinuxRootEnclaveResponse::ChallengeGenerated(challenge, challenge_id) => { + (challenge, challenge_id) + } + otherwise => { + error!( + "Unexpected response received from Linux Root enclave. Received: {:?}.", + otherwise + ); + + return Err(VeracruzServerError::LinuxRootEnclaveUnexpectedResponse( + otherwise, + )); + } + }; + + let runtime_manager_address = format!( + "{}:{}", + RUNTIME_MANAGER_ENCLAVE_ADDRESS, runtime_manager_port + ); + + info!( + "Establishing connection with new Runtime Manager enclave on address: {}.", + runtime_manager_address + ); + + let mut runtime_manager_socket = TcpStream::connect(&runtime_manager_address).map_err(|e| { + error!("Failed to connect to Runtime Manager enclave at address {}. Error produced: {}.", runtime_manager_address, e); + + VeracruzServerError::IOError(e) + })?; + + info!( + "Connected to Runtime Manager enclave at address {}.", + runtime_manager_address + ); + + info!("Sending Initialize message."); + + send_message( + &mut runtime_manager_socket, + &RuntimeManagerMessage::Initialize(policy.to_string(), challenge, challenge_id), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Initialize message successfully sent."); + + info!("Awaiting response..."); + + let status: RuntimeManagerMessage = receive_message(&mut runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match status { + RuntimeManagerMessage::Status(VMStatus::Success) => { + info!("Enclaves successfully initialized."); + } + RuntimeManagerMessage::Status(status) => { + error!("Enclave sent status {:?}.", status); + + return Err(VeracruzServerError::VMStatus(status)); + } + otherwise => { + error!("Enclave sent unexpected message: {:?}.", otherwise); + + return Err(VeracruzServerError::RuntimeManagerMessageStatus(otherwise)); + } + }; + + info!("Requesting certificate signing request (CSR)."); + + send_message(&mut runtime_manager_socket, &RuntimeManagerMessage::GetCSR) + .map_err(VeracruzServerError::SocketError)?; + + info!("CSR request successfully sent."); + + info!("Awaiting response..."); + + let csr_response: RuntimeManagerMessage = receive_message(&mut runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + let csr = match csr_response { + RuntimeManagerMessage::GeneratedCSR(csr) => { + info!("CSR received ({} bytes).", csr.len()); + + csr.clone() + } + otherwise => { + error!( + "Received unexpected reponse from Linux runtime enclave: {:?}.", + otherwise + ); + + return Err(VeracruzServerError::RuntimeManagerMessageStatus(otherwise)); + } + }; + + info!("Requesting native attestation token."); + + send_message( + &mut linux_root_socket, + &LinuxRootEnclaveMessage::GetNativeAttestation, + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Get native attestation message successfully sent."); + + info!("Awaiting response..."); + + let native_attestation_response: LinuxRootEnclaveResponse = + receive_message(&mut linux_root_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match native_attestation_response { + LinuxRootEnclaveResponse::NativeAttestationTokenRegistered => { + info!("Native Attestation Token registered.") + } + otherwise => { + error!( + "Unexpected response from Linux Root Enclave. Received: {:?}.", + otherwise + ); + + return Err(VeracruzServerError::LinuxRootEnclaveUnexpectedResponse( + otherwise, + )); + } + }; + + info!("Requesting certificate chain."); + + send_message( + &mut linux_root_socket, + &LinuxRootEnclaveMessage::GetProxyAttestation(csr, challenge_id), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Certificate chain request successfully sent."); + + info!("Awaiting response..."); + + let response: LinuxRootEnclaveResponse = receive_message(&mut linux_root_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + let certificate_chain = match response { + LinuxRootEnclaveResponse::CertificateChain( + compute_enclave_certificate, + root_enclave_certificate, + root_certificate, + ) => { + info!("Certificate chain received."); + + vec![ + compute_enclave_certificate, + root_enclave_certificate, + root_certificate, + ] + } + otherwise => { + error!( + "Unexpected response received from Linux Root enclave. Received: {:?}.", + otherwise + ); + + return Err(VeracruzServerError::LinuxRootEnclaveUnexpectedResponse( + otherwise, + )); + } + }; + + info!("Registering server certificate chain with Linux Runtime Manager enclave."); + + send_message( + &mut runtime_manager_socket, + &RuntimeManagerMessage::SetCertificateChain(certificate_chain), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Server certificate chain message successfully sent."); + + info!("Awaiting response..."); + + let response: RuntimeManagerMessage = receive_message(&mut runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + return match response { + RuntimeManagerMessage::Status(VMStatus::Success) => { + info!("Certificate chain successfully installed."); + + Ok(VeracruzServerLinux { + linux_root_process, + linux_root_socket, + runtime_manager_socket, + }) + } + RuntimeManagerMessage::Status(otherwise) => { + error!("Enclave sent status {:?}.", otherwise); + + Err(VeracruzServerError::VMStatus(otherwise)) + } + otherwise => { + error!("Enclave sent unexpected message: {:?}.", otherwise); + + Err(VeracruzServerError::RuntimeManagerMessageStatus(otherwise)) + } + }; + } + + #[inline] + fn plaintext_data( + &mut self, + _data: Vec, + ) -> Result>, VeracruzServerError> { + return Err(VeracruzServerError::UnimplementedError); + } + + fn new_tls_session(&mut self) -> Result { + info!("Requesting new TLS session."); + + send_message( + &mut self.runtime_manager_socket, + &RuntimeManagerMessage::NewTLSSession, + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("New TLS session message successfully sent."); + + info!("Awaiting response..."); + + let message: RuntimeManagerMessage = receive_message(&mut self.runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + match message { + RuntimeManagerMessage::TLSSession(session_id) => { + info!("Enclave started new TLS session with ID: {}.", session_id); + Ok(session_id) + } + otherwise => { + error!( + "Unexpected response returned from enclave. Received: {:?}.", + otherwise + ); + Err(VeracruzServerError::InvalidRuntimeManagerMessage(otherwise)) + } + } + } + + fn close_tls_session(&mut self, session_id: u32) -> Result<(), VeracruzServerError> { + info!("Requesting close of TLS session with ID: {}.", session_id); + + send_message( + &mut self.runtime_manager_socket, + &RuntimeManagerMessage::CloseTLSSession(session_id), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("Close TLS session message successfully sent."); + + info!("Awaiting response..."); + + let message: RuntimeManagerMessage = receive_message(&mut self.runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match message { + RuntimeManagerMessage::Status(VMStatus::Success) => { + info!("TLS session successfully closed."); + Ok(()) + } + RuntimeManagerMessage::Status(status) => { + error!("TLS session close request resulted in unexpected status message. Received: {:?}.", status); + Err(VeracruzServerError::VMStatus(status)) + } + otherwise => { + error!( + "Unexpected response returned from enclave. Received: {:?}.", + otherwise + ); + Err(VeracruzServerError::InvalidRuntimeManagerMessage(otherwise)) + } + } + } + + fn tls_data( + &mut self, + session_id: u32, + input: Vec, + ) -> Result<(bool, Option>>), VeracruzServerError> { + info!( + "Sending TLS data to runtime manager enclave (with session {}).", + session_id + ); + + send_message( + &mut self.runtime_manager_socket, + &RuntimeManagerMessage::SendTLSData(session_id, input), + ) + .map_err(VeracruzServerError::SocketError)?; + + info!("TLS data successfully sent."); + + info!("Awaiting response..."); + + let message: RuntimeManagerMessage = receive_message(&mut self.runtime_manager_socket) + .map_err(VeracruzServerError::SocketError)?; + + info!("Response received."); + + match message { + RuntimeManagerMessage::Status(VMStatus::Success) => { + info!("Runtime Manager enclave successfully received TLS data.") + } + RuntimeManagerMessage::Status(otherwise) => { + error!("Runtime Manager enclave failed to receive TLS data. Response received: {:?}.", otherwise); + return Err(VeracruzServerError::VMStatus(otherwise)); + } + otherwise => { + error!("Runtime Manager enclave produced an unexpected response to TLS data. Response received: {:?}.", otherwise); + return Err(VeracruzServerError::InvalidRuntimeManagerMessage(otherwise)); + } + } + + let mut active = true; + let mut buffer = Vec::new(); + + info!("Reading TLS data..."); + + while self.tls_data_needed(session_id)? { + let (alive_status, received) = self.read_tls_data(session_id)?; + + active = alive_status; + buffer.push(received); + } + + info!( + "Finished reading TLS data (active = {}, received {} bytes).", + active, + buffer.len() + ); + + if buffer.is_empty() { + Ok((active, None)) + } else { + Ok((active, Some(buffer))) + } + } + + /// Kills the Linux Root enclave, all spawned Runtime Manager enclaves, and all open + /// TCP connections and processes that we have a handle to. + #[inline] + fn close(&mut self) -> Result { + info!("Closing..."); + self.teardown() + } + } +} diff --git a/veracruz-server/src/veracruz_server_nitro.rs b/veracruz-server/src/veracruz_server_nitro.rs index bf58aef35..8ff28c19c 100644 --- a/veracruz-server/src/veracruz_server_nitro.rs +++ b/veracruz-server/src/veracruz_server_nitro.rs @@ -1,6 +1,6 @@ //! Nitro-Enclave-specific material for the Veracruz server //! -//! ## Authors +//! ## Authors //! //! The Veracruz Development Team. //! @@ -13,12 +13,19 @@ pub mod veracruz_server_nitro { use crate::veracruz_server::{VeracruzServer, VeracruzServerError}; use curl::easy::{Easy, List}; - use io_utils::nitro::NitroEnclave; + use io_utils::{ + http::{post_buffer, send_proxy_attestation_server_start, HttpError}, + nitro::NitroEnclave, + }; use policy_utils::policy::Policy; use std::io::Read; - use veracruz_utils::platform::nitro::nitro::{NitroStatus, RuntimeManagerMessage}; + use veracruz_utils::platform::vm::{RuntimeManagerMessage, VMStatus}; const RUNTIME_MANAGER_EIF_PATH: &str = "../runtime-manager/runtime_manager.eif"; + /// The protocol to use when interacting with the proxy attestation server. + const PROXY_ATTESTATION_PROTOCOL: &str = "nitro"; + /// The protocol version we are using with the proxy attestation server. + const FIRMWARE_VERSION: &str = "0.0"; pub struct VeracruzServerNitro { enclave: NitroEnclave, @@ -29,8 +36,19 @@ pub mod veracruz_server_nitro { // Set up, initialize Nitro Root Enclave let policy: Policy = Policy::from_json(policy_json)?; - let (challenge, challenge_id) = - send_attestation_start(policy.proxy_attestation_server_url())?; + let (challenge_id, challenge) = send_proxy_attestation_server_start( + policy.proxy_attestation_server_url(), + PROXY_ATTESTATION_PROTOCOL, + FIRMWARE_VERSION, + ) + .map_err(|e| { + eprintln!( + "Failed to start proxy attestation process. Error produced: {}.", + e + ); + + VeracruzServerError::HttpError(e) + })?; println!("VeracruzServerNitro::new instantiating Runtime Manager"); #[cfg(feature = "debug")] @@ -89,18 +107,21 @@ pub mod veracruz_server_nitro { _ => return Err(VeracruzServerError::RuntimeManagerMessageStatus(message)), }; match status { - NitroStatus::Success => (), - _ => return Err(VeracruzServerError::NitroStatus(status)), + VMStatus::Success => (), + _ => return Err(VeracruzServerError::VMStatus(status)), } println!("VeracruzServerNitro::new complete. Returning"); return Ok(meta); } - fn plaintext_data(&self, _data: Vec) -> Result>, VeracruzServerError> { + fn plaintext_data( + &mut self, + _data: Vec, + ) -> Result>, VeracruzServerError> { return Err(VeracruzServerError::UnimplementedError); } - fn new_tls_session(&self) -> Result { + fn new_tls_session(&mut self) -> Result { let nls_message = RuntimeManagerMessage::NewTLSSession; let nls_buffer = bincode::serialize(&nls_message)?; self.enclave.send_buffer(&nls_buffer)?; @@ -130,7 +151,7 @@ pub mod veracruz_server_nitro { let received_message: RuntimeManagerMessage = bincode::deserialize(&received_buffer)?; return match received_message { RuntimeManagerMessage::Status(_status) => Ok(()), - _ => Err(VeracruzServerError::NitroStatus(NitroStatus::Fail)), + _ => Err(VeracruzServerError::VMStatus(VMStatus::Fail)), }; } @@ -150,8 +171,8 @@ pub mod veracruz_server_nitro { let received_message: RuntimeManagerMessage = bincode::deserialize(&received_buffer)?; match received_message { RuntimeManagerMessage::Status(status) => match status { - NitroStatus::Success => (), - _ => return Err(VeracruzServerError::NitroStatus(status)), + VMStatus::Success => (), + _ => return Err(VeracruzServerError::VMStatus(status)), }, _ => { return Err(VeracruzServerError::InvalidRuntimeManagerMessage( @@ -177,7 +198,7 @@ pub mod veracruz_server_nitro { active_flag = alive; ret_array.push(data); } - _ => return Err(VeracruzServerError::NitroStatus(NitroStatus::Fail)), + _ => return Err(VeracruzServerError::VMStatus(VMStatus::Fail)), } } @@ -222,7 +243,7 @@ pub mod veracruz_server_nitro { let received_message: RuntimeManagerMessage = bincode::deserialize(&received_buffer)?; let tls_data_needed = match received_message { RuntimeManagerMessage::TLSDataNeeded(needed) => needed, - _ => return Err(VeracruzServerError::NitroStatus(NitroStatus::Fail)), + _ => return Err(VeracruzServerError::VMStatus(VMStatus::Fail)), }; return Ok(tls_data_needed); } @@ -243,7 +264,14 @@ pub mod veracruz_server_nitro { "veracruz-server-nitro::post_native_attestation_token posting to URL{:?}", url ); - let received_body: String = post_buffer(&url, &encoded_str)?; + let received_body: String = post_buffer(&url, &encoded_str).map_err(|e| { + println!( + "Failed to post native attestation token. Error produced: {}.", + e + ); + + VeracruzServerError::HttpError(e) + })?; println!( "veracruz-server-nitro::post_psa_attestation_token received buffer:{:?}", @@ -266,119 +294,4 @@ pub mod veracruz_server_nitro { cert_chain.push(ca_cert.to_vec()); return Ok(cert_chain); } - - /// Send the start message to the proxy attestation server (this triggers the server to - /// send the challenge) and then handle the response - fn send_attestation_start(url_base: &str) -> Result<(Vec, i32), VeracruzServerError> { - let proxy_attestation_server_response = send_proxy_attestation_server_start(url_base)?; - if proxy_attestation_server_response.has_psa_attestation_init() { - let (challenge, device_id) = transport_protocol::parse_psa_attestation_init( - proxy_attestation_server_response.get_psa_attestation_init(), - ) - .map_err(|err| VeracruzServerError::TransportProtocol(err))?; - return Ok((challenge, device_id)); - } else { - return Err(VeracruzServerError::InvalidProtoBufMessage); - } - } - - /// Send start to the proxy attestation server. - fn send_proxy_attestation_server_start( - url_base: &str, - ) -> Result { - let serialized_start_msg = transport_protocol::serialize_start_msg("nitro", "0.0") - .map_err(|err| VeracruzServerError::TransportProtocol(err))?; - let encoded_start_msg: String = base64::encode(&serialized_start_msg); - let url = format!("{:}/Start", url_base); - - println!( - "veracruz-server-nitro::send_proxy_attestation_server_start sending to url:{:?}", - url - ); - let received_body: String = post_buffer(&url, &encoded_start_msg)?; - println!( - "veracruz-server-nitro::send_proxy_attestation_server_start completed post command" - ); - - let body_vec = - base64::decode(&received_body).map_err(|err| VeracruzServerError::Base64Decode(err))?; - let response = transport_protocol::parse_proxy_attestation_server_response(&body_vec) - .map_err(|err| VeracruzServerError::TransportProtocol(err))?; - println!( - "veracruz-server-nitro::send_proxy_attestation_server_start completed. Returning." - ); - return Ok(response); - } - - /// Post a buffer to a remote HTTP server - fn post_buffer(url: &str, buffer: &String) -> Result { - let mut buffer_reader = stringreader::StringReader::new(buffer); - - let mut curl_request = Easy::new(); - curl_request - .url(&url) - .map_err(|err| VeracruzServerError::CurlError(err))?; - let mut headers = List::new(); - headers - .append("Content-Type: application/octet-stream") - .map_err(|err| VeracruzServerError::CurlError(err))?; - curl_request - .http_headers(headers) - .map_err(|err| VeracruzServerError::CurlError(err))?; - curl_request - .post(true) - .map_err(|err| VeracruzServerError::CurlError(err))?; - curl_request - .post_field_size(buffer.len() as u64) - .map_err(|err| VeracruzServerError::CurlError(err))?; - - let mut received_body = String::new(); - let mut received_header = String::new(); - { - let mut transfer = curl_request.transfer(); - - transfer - .read_function(|buf| Ok(buffer_reader.read(buf).unwrap_or(0))) - .map_err(|err| VeracruzServerError::CurlError(err))?; - transfer - .write_function(|buf| { - received_body.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - Ok(buf.len()) - }) - .map_err(|err| VeracruzServerError::CurlError(err))?; - - transfer - .header_function(|buf| { - received_header.push_str( - std::str::from_utf8(buf) - .expect(&format!("Error converting data {:?} from UTF-8", buf)), - ); - true - }) - .map_err(|err| VeracruzServerError::CurlError(err))?; - - transfer - .perform() - .map_err(|err| VeracruzServerError::CurlError(err))?; - } - let header_lines: Vec<&str> = received_header.split("\n").collect(); - - println!( - "veracruz-server-nitro::post_buffer received header:{:?}", - received_header - ); - if !received_header.contains("HTTP/1.1 200 OK\r") { - return Err(VeracruzServerError::NonSuccessHttp); - } - - println!( - "veracruz-server-nitro::post_buffer header_lines:{:?}", - header_lines - ); - - return Ok(received_body); - } } diff --git a/veracruz-server/src/veracruz_server_sgx.rs b/veracruz-server/src/veracruz_server_sgx.rs index 3e23ffd0e..97908e0df 100644 --- a/veracruz-server/src/veracruz_server_sgx.rs +++ b/veracruz-server/src/veracruz_server_sgx.rs @@ -13,6 +13,7 @@ pub mod veracruz_server_sgx { use crate::veracruz_server::*; + use io_utils::http::{post_buffer, send_proxy_attestation_server_start}; use lazy_static::lazy_static; use log::{debug, error}; use policy_utils::policy::Policy; @@ -21,7 +22,6 @@ pub mod veracruz_server_sgx { runtime_manager_new_session_enc, runtime_manager_tls_get_data_enc, runtime_manager_tls_get_data_needed_enc, runtime_manager_tls_send_data_enc, }; - // NOTE: The `SgxEnclave` MUST appear before `sgx_root_enclave_bind` for stopping a linker error. #[rustfmt::skip] use sgx_urts::SgxEnclave; #[rustfmt::skip] @@ -137,24 +137,16 @@ pub mod veracruz_server_sgx { } impl VeracruzServerSGX { + #[inline] fn send_start( &mut self, url_base: &str, protocol: &str, firmware_version: &str, ) -> Result<(Vec, i32), VeracruzServerError> { - let proxy_attestation_server_response = - crate::send_proxy_attestation_server_start(url_base, protocol, firmware_version)?; - if proxy_attestation_server_response.has_sgx_attestation_init() { - let attestation_init = proxy_attestation_server_response.get_sgx_attestation_init(); - let (public_key, device_id) = - transport_protocol::parse_sgx_attestation_init(attestation_init); - Ok((public_key, device_id)) - } else { - Err(VeracruzServerError::MissingFieldError( - "sgx_attestation_init", - )) - } + send_proxy_attestation_server_start(url_base, protocol, firmware_version) + .map_err(VeracruzServerError::HttpError) + .map(|(i, v)| (v, i)) } } @@ -172,7 +164,8 @@ pub mod veracruz_server_sgx { let url = format!("{:}/SGX/Msg1", url_base); - let received_body = crate::post_buffer(&url, &encoded_msg1)?; + let received_body = + post_buffer(&url, &encoded_msg1).map_err(VeracruzServerError::HttpError)?; let body_vec = base64::decode(&received_body)?; let parsed = transport_protocol::parse_proxy_attestation_server_response(&body_vec)?; @@ -415,7 +408,8 @@ pub mod veracruz_server_sgx { let encoded_tokens = base64::encode(&serialized_tokens); let url = format!("{:}/SGX/Msg3", url_base); - let received_body = crate::post_buffer(&url, &encoded_tokens)?; + let received_body = + post_buffer(&url, &encoded_tokens).map_err(VeracruzServerError::HttpError)?; let received_bytes = base64::decode(&received_body).unwrap(); let parsed = transport_protocol::parse_proxy_attestation_server_response(&received_bytes)?; @@ -581,11 +575,14 @@ pub mod veracruz_server_sgx { } } - fn plaintext_data(&self, _data: Vec) -> Result>, VeracruzServerError> { + fn plaintext_data( + &mut self, + _data: Vec, + ) -> Result>, VeracruzServerError> { unreachable!("Unimplemented"); } - fn new_tls_session(&self) -> Result { + fn new_tls_session(&mut self) -> Result { let mut session_id: u32 = 0; let mut result: u32 = 0; let ret = unsafe { diff --git a/veracruz-server/src/veracruz_server_tz.rs b/veracruz-server/src/veracruz_server_tz.rs index aabdf80d3..7f4f8eafc 100644 --- a/veracruz-server/src/veracruz_server_tz.rs +++ b/veracruz-server/src/veracruz_server_tz.rs @@ -14,6 +14,7 @@ pub mod veracruz_server_tz { use crate::veracruz_server::*; use hex; + use io_utils::http::{post_buffer, send_proxy_attestation_server_start}; use lazy_static::lazy_static; use log::debug; use optee_teec::{ @@ -165,13 +166,16 @@ pub mod veracruz_server_tz { }) } - fn plaintext_data(&self, data: Vec) -> Result>, VeracruzServerError> { + fn plaintext_data( + &mut self, + data: Vec, + ) -> Result>, VeracruzServerError> { let parsed = transport_protocol::parse_runtime_manager_request(&data)?; unreachable!("Unimplemented"); } - fn new_tls_session(&self) -> Result { + fn new_tls_session(&mut self) -> Result { let mut context_opt = CONTEXT.lock()?; let context = context_opt .as_mut() @@ -324,11 +328,12 @@ pub mod veracruz_server_tz { &mut operation, )?; } - let (challenge, device_id) = VeracruzServerTZ::send_start( + let (device_id, challenge) = send_proxy_attestation_server_start( proxy_attestation_server_url, "psa", &firmware_version, - )?; + ) + .map_err(VeracruzServerError::HttpError)?; let p0 = ParamValue::new(device_id.try_into()?, 0, ParamType::ValueInout); let p1 = ParamTmpRef::new_input(&challenge); @@ -401,7 +406,8 @@ pub mod veracruz_server_tz { transport_protocol::serialize_native_psa_attestation_token(token, csr, device_id)?; let encoded_str = base64::encode(&proxy_attestation_server_request); let url = format!("{:}/PSA/AttestationToken", proxy_attestation_server_url); - let response = crate::post_buffer(&url, &encoded_str)?; + let response = + post_buffer(&url, &encoded_str).map_err(VeracruzServerError::HttpError)?; debug!( "veracruz_server_tz::post_psa_attestation_token received buffer:{:?}", @@ -440,24 +446,5 @@ pub mod veracruz_server_tz { }; return Ok(firmware_version); } - - fn send_start( - url_base: &str, - protocol: &str, - firmware_version: &str, - ) -> Result<(Vec, i32), VeracruzServerError> { - let proxy_attestation_server_response = - crate::send_proxy_attestation_server_start(url_base, protocol, firmware_version)?; - if proxy_attestation_server_response.has_psa_attestation_init() { - let (challenge, device_id) = transport_protocol::parse_psa_attestation_init( - proxy_attestation_server_response.get_psa_attestation_init(), - )?; - Ok((challenge, device_id)) - } else { - Err(VeracruzServerError::MissingFieldError( - "proxy_attestation_server_response psa_attestation_init", - )) - } - } } } diff --git a/veracruz-test/Cargo.toml b/veracruz-test/Cargo.toml index 16e61b7f4..5c02f2ab2 100644 --- a/veracruz-test/Cargo.toml +++ b/veracruz-test/Cargo.toml @@ -11,13 +11,15 @@ sgx = ["veracruz-client/sgx","veracruz-server/sgx", "sgx_types", "sgx_ucrypto", tz = ["veracruz-client/tz","veracruz-server/tz", "proxy-attestation-server/tz", "transport-protocol/tz"] nitro = ["veracruz-client/nitro", "veracruz-server/nitro", "proxy-attestation-server/nitro"] icecap = ["veracruz-client/icecap", "veracruz-server/icecap", "proxy-attestation-server/icecap", "transport-protocol/icecap"] +linux = ["veracruz-client/linux", "veracruz-server/linux", "proxy-attestation-server/linux"] [dependencies] veracruz-client = {path = "../veracruz-client"} veracruz-server = { path = "../veracruz-server"} policy-utils = { path = "../policy-utils", features = ["std"]} -serde = { version = "1.0.97", features = ["derive"], default-features = false } -proxy-attestation-server = { path = "../proxy-attestation-server", optional = true} +serde_json = "1.0" +serde = { git = "https://github.com/veracruz-project/serde.git", features=["derive"], branch = "veracruz" } +proxy-attestation-server = { path = "../proxy-attestation-server"} transport-protocol = { path = "../transport-protocol"} http = "=0.2.4" actix-rt = "1.1.1" @@ -38,4 +40,3 @@ sgx_alloc = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk. sgx_ucrypto = { git = "https://github.com/veracruz-project/incubator-teaclave-sgx-sdk.git", branch="veracruz", optional = true } sgx_types = { git = "https://github.com/veracruz-project/incubator-teaclave-sgx-sdk.git", branch="veracruz", optional = true } sgx_alloc = { git = "https://github.com/veracruz-project/incubator-teaclave-sgx-sdk.git", branch="veracruz", optional = true } - diff --git a/veracruz-test/src/main.rs b/veracruz-test/src/main.rs index 1876155c0..3f907ff8e 100644 --- a/veracruz-test/src/main.rs +++ b/veracruz-test/src/main.rs @@ -147,7 +147,7 @@ mod tests { /// A test of veracruz using network communication using a single session #[actix_rt::test] async fn veracruz_phase1_get_random_one_client() { - let result = test_template::>( + let result = test_template( policy_path(GET_RANDOM_POLICY).as_path(), &vec![( trust_path(CLIENT_CERT).as_path(), @@ -166,18 +166,10 @@ mod tests { ); } - #[derive(Debug, Deserialize)] - struct LinearRegression { - /// Gradient of the linear relationship. - gradient: f64, - /// Y-intercept of the linear relationship. - intercept: f64, - } - /// A test of veracruz using network communication using two sessions (one for program and one for data) #[actix_rt::test] async fn veracruz_phase1_linear_regression_two_clients() { - let result = test_template::( + let result = test_template( policy_path(LINEAR_REGRESSION_DUAL_POLICY).as_path(), &vec![ ( @@ -205,7 +197,7 @@ mod tests { /// A test of veracruz using network communication using three sessions (one for program, one for data, and one for retrieval) #[actix_rt::test] async fn veracruz_phase2_linear_regression_three_clients() { - let result = test_template::( + let result = test_template( policy_path(LINEAR_REGRESSION_TRIPLE_POLICY).as_path(), &vec![ ( @@ -238,7 +230,7 @@ mod tests { /// (one for program, one for the first data, and one for the second data and retrieval.) #[actix_rt::test] async fn veracruz_phase2_intersection_set_sum_three_clients() { - let result = test_template::( + let result = test_template( policy_path(INTERSECTION_SET_SUM_TRIPLE_POLICY).as_path(), &vec![ ( @@ -282,7 +274,7 @@ mod tests { /// (one for program, one for the first data, and one for the second data and retrieval.) #[actix_rt::test] async fn veracruz_phase2_intersection_set_sum_two_clients_reversed_data_provision() { - let result = test_template::( + let result = test_template( policy_path(PERMUTED_INTERSECTION_SET_SUM_TRIPLE_POLICY).as_path(), &vec![ ( @@ -322,7 +314,7 @@ mod tests { /// (one for program, one for the first data, and one for the second data and retrieval.) #[actix_rt::test] async fn veracruz_phase2_string_edit_distance_three_clients() { - let result = test_template::( + let result = test_template( policy_path(STRING_EDIT_DISTANCE_TRIPLE_POLICY).as_path(), &vec![ ( @@ -358,7 +350,7 @@ mod tests { /// (one for program, one for the first data, one for the second data, and one for retrieval.) #[actix_rt::test] async fn veracruz_phase3_string_edit_distance_four_clients() { - let result = test_template::( + let result = test_template( policy_path(STRING_EDIT_DISTANCE_QUADRUPLE_POLICY).as_path(), &vec![ ( @@ -458,7 +450,7 @@ mod tests { assert!(result.is_ok(), "error: {:?}", result); } - async fn test_template( + async fn test_template( // Policy files policy_path: &Path, // List of client's certificates and private keys @@ -539,8 +531,7 @@ mod tests { .get_mut(*result_retriever_index) .ok_or(VeracruzTestError::ClientIndexError(*result_retriever_index))?; let result = result_retriever_veracruz_client.get_results(program_name)?; - let result: T = pinecone::from_bytes(&result)?; - info!(" Result: {:?}", result); + info!(" Received {} bytes of result.", result.len()); } for client_index in 0..client_configs.len() { diff --git a/veracruz-utils/Cargo.toml b/veracruz-utils/Cargo.toml index 44796bb30..7fbb07bd1 100644 --- a/veracruz-utils/Cargo.toml +++ b/veracruz-utils/Cargo.toml @@ -10,23 +10,19 @@ description = "Miscellaneous and common code used by multiple Veracruz component sgx = ["sgx_tstd", "serde/mesalock_sgx", "serde_json/mesalock_sgx", "serde_json/alloc", "ring/mesalock_sgx"] tz = ["serde_json/std", "ring"] std = ["serde/std", "rustls","serde_json/std", "ring"] -nitro = ["serde_json/std", "nix", "byteorder", "ring/non_sgx"] -icecap = ["ring"] +nitro = ["serde/derive", "serde_json/std", "ring/non_sgx"] +icecap = ["serde/derive", "ring"] +linux = ["serde_json/std", "ring/non_sgx", "serde/derive"] [dependencies] sgx_tstd = { rev = "v1.1.2", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } -serde = { git = "https://github.com/veracruz-project/serde.git", features=["derive"], branch = "veracruz" } +serde = { git = "https://github.com/veracruz-project/serde.git", default-features = false, branch = "veracruz", optional = true} serde_json = { git = "https://github.com/veracruz-project/json.git", branch = "veracruz", default-features = false } # The cargo patch mechanism does NOT work when we add function into a macro_rules! rustls = { git = "https://github.com/veracruz-project/rustls.git", branch = "veracruz", optional = true } err-derive = "0.2" -nix = { version = "0.15", optional = true } -byteorder = { version = "1.3", optional = true } ring = { git = "https://github.com/veracruz-project/ring.git", version = "=0.16.11", branch = "veracruz", optional = true } -backtrace = "=0.3.61" -http = "=0.2.4" - [build-dependencies] uuid = { version = "0.7", features = ["v4"] } diff --git a/veracruz-utils/src/lib.rs b/veracruz-utils/src/lib.rs index 5710c7a49..1f4dd031b 100644 --- a/veracruz-utils/src/lib.rs +++ b/veracruz-utils/src/lib.rs @@ -6,10 +6,10 @@ //! //! The Veracruz Development Team. //! -//! ## Licensing and copyright notice +//! ## Licensing all copyright notice //! //! See the `LICENSE_MIT.markdown` file in the Veracruz root directory for -//! information on licensing and copyright. +//! information on licensing all copyright. #![cfg_attr(feature = "sgx", no_std)] @@ -18,16 +18,17 @@ extern crate sgx_tstd as std; /// Platform-specific material, or material that is common to all -/// platforms/backends that Veracruz supports and does not fit elsewhere. +/// platforms/backends that Veracruz supports all does not fit elsewhere. pub mod platform; #[cfg(feature = "nitro")] pub use crate::platform::nitro::*; +/// Material related to cerficate signing requests (CSR). pub mod csr; /// The ID of the Veracruz Runtime Hash Extension. -/// This value was made up, and can be changed to pretty much any valid +/// This value was made up, all can be changed to pretty much any valid /// ID as long as it doesn't collide with the ID of an extension in our /// certificates. pub static VERACRUZ_RUNTIME_HASH_EXTENSION_ID: [u8; 4] = [2, 5, 30, 1]; diff --git a/veracruz-utils/src/platform/linux.rs b/veracruz-utils/src/platform/linux.rs new file mode 100644 index 000000000..2863cc456 --- /dev/null +++ b/veracruz-utils/src/platform/linux.rs @@ -0,0 +1,75 @@ +//! Structs needed for Linux support, both inside and outside of the +//! trusted application. +//! +//! ## Authors +//! +//! The Veracruz Development Team. +//! +//! ## Licensing and copyright notice +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for +//! information on licensing and copyright. + +use serde::{Deserialize, Serialize}; + +//////////////////////////////////////////////////////////////////////////////// +// Linux root enclave messages. +//////////////////////////////////////////////////////////////////////////////// + +/// Incoming messages to the Linux root enclave, instructing it to perform some +/// act. These are sent serialized in `bincode` format. +#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq, PartialOrd, Ord)] +pub enum LinuxRootEnclaveMessage { + /// A request to get the firmware version of the software executing inside + /// the enclave. + GetFirmwareVersion, + /// A request to perform a native attestation of the runtime enclave. + /// Note that we use PSA attestation for this step, but the attestation is + /// "fake", offering no real value other than for demonstrative purposes. + GetNativeAttestation, + /// A request to perform a proxy attestation of the runtime enclave. + /// Note that we use PSA attestation, again, for this step, but the + /// attestation is "fake", offering no real value other than for + /// demonstrative purposes. + /// The fields of the message are (in order): + /// - A certificate signing request (CSR), + /// - A challenge ID. + GetProxyAttestation(Vec, i32), + /// A request to shutdown the root enclave and any enclaves that it has + /// launched. + Shutdown, + /// A request to spawn a new enclave containing an instance of Veracruz. + SpawnNewApplicationEnclave, + /// A request to generate a challenge and a fresh challenge ID, as part of + /// the proxy attestation process. + StartProxyAttestation, +} + +/// Responses produced by the Linux root enclave after receiving and processing +/// a `LinuxRootEnclaveMessage` element, above. +#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq, PartialOrd, Ord)] +pub enum LinuxRootEnclaveResponse { + /// The firmware version of the software executing inside the runtime + /// enclave. For Linux, this is mocked up. + FirmwareVersion(String), + /// The token produced by the native attestation process is now registered + /// with the Proxy Attestation Service. + NativeAttestationTokenRegistered, + /// Returns a certificate chain in response to a proxy attestation request. + /// Fields are, in order: + /// - An encoding of the compute enclave certificate, + /// - An encoding of the root enclave certificate, + /// - An encoding of the root certificate. + CertificateChain(Vec, Vec, Vec), + /// Success message upon installation of the certificate chain. + CertificateChainInstalled, + /// Acknowledgment that the root enclave is to shutdown. + ShuttingDown, + /// Indicates that a new Runtime Manager enclave has been spawned and this + /// new enclave should be contacted on `localhost` using the designated + /// port. + EnclaveSpawned(u32), + /// Reply with the content and the index of the freshly-generated challenge + /// value produced as a result of the proxy attestation process starting. + ChallengeGenerated(Vec, i32), +} diff --git a/veracruz-utils/src/platform/mod.rs b/veracruz-utils/src/platform/mod.rs index c904d6d64..b7c502d93 100644 --- a/veracruz-utils/src/platform/mod.rs +++ b/veracruz-utils/src/platform/mod.rs @@ -15,7 +15,11 @@ pub mod error; #[cfg(feature = "icecap")] pub mod icecap; +#[cfg(feature = "linux")] +pub mod linux; #[cfg(feature = "nitro")] pub mod nitro; #[cfg(feature = "tz")] pub mod tz; +#[cfg(any(feature = "nitro", feature = "linux"))] +pub mod vm; diff --git a/veracruz-utils/src/platform/nitro/nitro.rs b/veracruz-utils/src/platform/nitro/nitro.rs index bfe7fad25..5e2c4f160 100644 --- a/veracruz-utils/src/platform/nitro/nitro.rs +++ b/veracruz-utils/src/platform/nitro/nitro.rs @@ -12,21 +12,11 @@ use serde::{Deserialize, Serialize}; -/// The Status value returned by the Nitro enclave for operations -/// This is intended to be received as a bincode serialized -/// `NitroRootEnclaveMessage::Status` -#[derive(Serialize, Deserialize, Debug)] -pub enum NitroStatus { - /// The operation generating the message succeeded - Success, - /// The operation generating the message failed - Fail, - /// The requested operation is not yet implemented - Unimplemented, -} +use crate::platform::vm::VMStatus; -/// An enumerated type describing messages passed between the Runtime Manager -/// and the Nitro Root Enclave +/// An enumerated type describing messages passed between to/from the Runtime +/// Manager enclave (These originate from the Untrusted Pass-through (Veracruz +/// server) /// These messages are inteded to be serialized using bincode before transport, /// and deserialized using bincode after transport #[derive(Serialize, Deserialize, Debug)] @@ -34,11 +24,11 @@ pub enum NitroRootEnclaveMessage { /// A message generated by an operation that did not return data, but did /// return a status. /// Most operations return data, but if they fail, they will return a - /// status set to `NitroStatus::Fail` (or `NitroStatus::Unimplemented` if - /// it is not implemented). + /// status set to `VMStatus::Fail` (or `VMStatus::Unimplemented` if + /// it is not implmeneted). /// Parameters: - /// NitroStatus - the Status - Status(NitroStatus), + /// VMStatus - the Status + Status(VMStatus), /// A request to fetch the firmware version from the Nitro Root Enclave FetchFirmwareVersion, /// A response to the `FetchFirmwareVersion` message, it contains the @@ -89,69 +79,3 @@ pub enum NitroRootEnclaveMessage { /// (for example, a response to a SetCertChain request) Success, } - -/// An enumerated type describing messages passed between to/from the Runtime -/// Manager enclave (These originate from the Untrusted Pass-through (Veracruz -/// server) -/// These messages are inteded to be serialized using bincode before transport, -/// and deserialized using bincode after transport -#[derive(Serialize, Deserialize, Debug)] -pub enum RuntimeManagerMessage { - /// A message generated by an operation that did not return data, but did - /// return a status. - /// Most operations return data, but if they fail, they will return a - /// status set to `NitroStatus::Fail` (or `NitroStatus::Unimplemented` if - /// it is not implmeneted). - /// Parameters: - /// NitroStatus - the Status - Status(NitroStatus), - /// A request to start the attestation process - /// parameters: - /// Vec - the challenge value - /// i32 - the challenge ID - Attestation(Vec, i32), - /// The response to the `Attestation` request. - /// parameters: - /// Vec - The nitro attestation document from the enclave - AttestationData(Vec), - /// A request to initialize the Runtime Manager enclave with the provided - /// policy and certificate - /// parameters: - /// String - The policy, in JSON format - /// Vec> - The certificate chain for the enclave - Initialize(String, Vec>), - /// A request to establish a new TLS session with the enclave - NewTLSSession, - /// The response to the `NewTLSSession` message - /// Parameters: - /// u32 - The Session ID of the created TLS Session - TLSSession(u32), - /// A request to close an already established TLS session - /// Parameters: - /// u32 - The Session ID of the session to be closed - CloseTLSSession(u32), - /// Request to determine if the TLS Session needs data to be sent to it - /// Parameters: - /// u32 - The Session ID of the TLS session - GetTLSDataNeeded(u32), - /// Response to `GetTLSDataNeeded` message - /// Parameters: - /// bool - is data needed? - TLSDataNeeded(bool), - /// Request to send TLS data to the enclave - /// Parameters: - /// u32 - the Session ID of the TLS Session associated with the data - /// Vec - The TLS Data - SendTLSData(u32, Vec), - /// Request TLS Data from the enclave - /// Parameters: - /// u32 - the Session ID of the TLS Session to request data from - GetTLSData(u32), - /// Response to `GetTLSData` - /// Parameters: - /// Vec - The TLS Data. May be empty - /// bool - a flag indicating if the TLS session is still alive - TLSData(Vec, bool), // TLS Data, alive_flag - /// A request to reset the enclave - ResetEnclave, -} diff --git a/veracruz-utils/src/platform/vm.rs b/veracruz-utils/src/platform/vm.rs new file mode 100644 index 000000000..a57564931 --- /dev/null +++ b/veracruz-utils/src/platform/vm.rs @@ -0,0 +1,126 @@ +//! Shared material for all VM-like isolation mechanisms +//! +//! Material that is common to AWS Nitro Enclaves and Linux applications, +//! and similar backends. +//! +//! ## Authors +//! +//! The Veracruz Development Team. +//! +//! ## Licensing and copyright notice +//! +//! See the `LICENSE.markdown` file in the Veracruz root directory for +//! information on licensing and copyright. + +use serde::{Deserialize, Serialize}; + +/////////////////////////////////////////////////////////////////////////////// +// Status messages. +/////////////////////////////////////////////////////////////////////////////// + +/// The Status value returned by the enclave/application for operations. +/// This is intended to be received as a bincode serialized +/// `RootEnclaveMessage::Status` +#[derive(Serialize, Deserialize, Debug)] +pub enum VMStatus { + /// The operation generating the message succeeded + Success, + /// The operation generating the message failed + Fail, + /// The requested operation is not yet implemented + Unimplemented, +} + +/////////////////////////////////////////////////////////////////////////////// +// Command-and-control messages for the runtime manager. +/////////////////////////////////////////////////////////////////////////////// + +/// An enumerated type describing messages passed between to/from the Runtime +/// Manager enclave (These originate from the Untrusted Pass-through (Veracruz +/// server) +/// These messages are inteded to be serialized using bincode before transport, +/// and deserialized using bincode after transport +#[derive(Serialize, Deserialize, Debug)] +pub enum RuntimeManagerMessage { + /// A message generated by an operation that did not return data, but did + /// return a status. Most operations return data, but if they fail, they + /// will return a status set to `VMStatus::Fail` (or + /// `VMStatus::Unimplemented` if it is not implmeneted). Parameters in + /// order are: + /// - The status. + Status(VMStatus), + #[cfg(feature = "nitro")] + /// A request to initialize the Runtime Manager enclave with the provided + /// policy and certificate (for Nitro). + /// parameters: + /// String - The policy, in JSON format + /// Vec> - The certificate chain for the enclave + Initialize(String, Vec>), + #[cfg(feature = "linux")] + /// A request to initialize the Runtime Manager enclave with the provided + /// policy (for Linux). Parameters (in order): + /// + /// - The JSON policy in String format, + /// - The bytes of the challenge, + /// - The challenge ID. + Initialize(String, Vec, i32), + #[cfg(feature = "linux")] + /// A request to set the server certificate chain. Note that this is done + /// slightly differently for Linux when compared to Nitro, as most of the + /// dummy Linux attestation is actually done in the Linux Root Enclave + /// where e.g. the Linux runtime manager can be measured, without the use of + /// a hacky message needed to set the hash. As a result, the certificate + /// chain is computed in the Linux Root Enclave, before being forwarded to + /// the Runtime Manager enclave later, via this message. Parameters (in + /// order) are: + /// - The certificate chain. + SetCertificateChain(Vec>), + #[cfg(feature = "linux")] + /// A request to forward a certificate signing request (CSR). + GetCSR, + #[cfg(feature = "linux")] + /// The response to the `GetCSR` message, returning the byte encoding of a + /// generated certificate signing request. Parameters in order are: + /// - The byte encoding of the certificate signing request. + GeneratedCSR(Vec), + #[cfg(feature = "nitro")] + /// A request to start the attestation process + /// parameters: + /// Vec - the challenge value + /// i32 - the challenge ID + Attestation(Vec, i32), + #[cfg(feature = "nitro")] + /// The response to the `Attestation` request. + /// parameters: + /// Vec - The nitro attestation document from the enclave + AttestationData(Vec), + /// A request to establish a new TLS session with the enclave. + NewTLSSession, + /// The response to the `NewTLSSession` message. Parameters in order are: + /// - The Session ID of the created TLS session. + TLSSession(u32), + /// A request to close an already established TLS session. Parameters in + /// order are: + /// - The Session ID of the session to be closed. + CloseTLSSession(u32), + /// Request to determine if the TLS Session needs data to be sent to it. + /// Parameters in order are: + /// - The Session ID of the TLS session. + GetTLSDataNeeded(u32), + /// Request to send TLS data to the enclave. Parameters in order are: + /// - The Session ID of the TLS Session associated with the data, + /// - The TLS data. + SendTLSData(u32, Vec), + /// Response to `GetTLSDataNeeded` message. Parameters in order are: + /// - Is data needed? + TLSDataNeeded(bool), + /// Request TLS data from the enclave. Parameters in order are: + /// - The Session ID of the TLS session to request data from. + GetTLSData(u32), + /// Response to `GetTLSData`. Parameters in order are: + /// - The TLS data, which may be empty. + /// - A flag indicating if the TLS session is still alive. + TLSData(Vec, bool), + /// A request to reset the enclave. + ResetEnclave, +}