From 7a4e28fc7cf0596b0a6c94862b76a4bb3fa9b1e4 Mon Sep 17 00:00:00 2001 From: clangenb <37865735+clangenb@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:00:25 +0900 Subject: [PATCH] enable `cargo test` for environments without an sgx sdk, and extract the `cargo test` from the docker build command. (#1480) * [docker] Don't run cargo test as part of the build process, but add it as a separate command instead * [GHA] add --release to test command * [GHA] remove cargo test command, as it will not work anyhow. * remove unnecessary change * [GHA] fix workflow syntax * [GHA] fix docker flag * [service] remove static linking to `Enclave_u` for cargo tests * don't link against anything if we run tests * fix build.rs for test and builds * [GHA] fix cargo test flag * fix toml fmt * [GHA] run cargo test on integritee builder * [GHA] remove cargo test from the matrix * [GHA] fix syntax * [GHA] run cargo test not in container but on host directly * [buil_and_test] run without rustup * [buil_and_test] run cargo test on the github actions runner again * working `cargo test` implementation * [build_and_test] fix feature flag * [enclave-api] rename `real-ffi` to `implement-ffi` * [enclave-api] remove obsolete compiler error * toml fmt * fix clippy warnings in itp-enclave-api * [service] extract main file to a separate file * remove more clippy warnings from enclave-api * fix clippy warnings in setup file * allow unused functions due to link-binary flag * fix clippy * fix teeracle clippy * [integritee-service] fix feature gating * remove redundant feature flags * [GHA] execute cargo test with --release flag * [GHA] fix cargo test --- .github/workflows/build_and_test.yml | 6 +- Cargo.lock | 1 - Makefile | 8 +- build.Dockerfile | 2 +- core-primitives/enclave-api/Cargo.toml | 9 +- core-primitives/enclave-api/ffi/Cargo.toml | 5 + core-primitives/enclave-api/ffi/build.rs | 44 +- .../enclave-api/src/direct_request.rs | 56 +- .../enclave-api/src/enclave_base.rs | 570 ++++---- .../enclave-api/src/enclave_test.rs | 32 +- core-primitives/enclave-api/src/lib.rs | 11 +- .../enclave-api/src/remote_attestation.rs | 1239 +++++++++-------- core-primitives/enclave-api/src/sidechain.rs | 98 +- .../enclave-api/src/teeracle_api.rs | 133 +- service/Cargo.toml | 12 +- service/build.rs | 32 +- service/src/enclave/api.rs | 7 +- service/src/enclave/mod.rs | 1 + service/src/main.rs | 1094 +-------------- service/src/main_impl.rs | 1096 +++++++++++++++ service/src/ocall_bridge/component_factory.rs | 2 +- service/src/ocall_bridge/sidechain_ocall.rs | 2 +- service/src/setup.rs | 146 +- service/src/sidechain_setup.rs | 2 +- service/src/teeracle/teeracle_metrics.rs | 2 +- .../mocks/initialization_handler_mock.rs | 2 +- service/src/tests/mod.rs | 11 +- service/src/worker.rs | 2 +- 28 files changed, 2375 insertions(+), 2250 deletions(-) create mode 100644 service/src/main_impl.rs diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 15416508bf..1f48d7c41d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -77,7 +77,7 @@ jobs: buildkitd-flags: --debug driver: docker-container - - name: Build Worker & Run Cargo Test + - name: Build Worker env: DOCKER_BUILDKIT: 1 run: > @@ -98,7 +98,7 @@ jobs: - run: docker images --all - name: Test Enclave # cargo test is not supported in the enclave, see: https://github.com/apache/incubator-teaclave-sgx-sdk/issues/232 - run: docker run ${{ env.DOCKER_DEVICES }} ${{ env.DOCKER_VOLUMES }} integritee-worker-${{ env.IMAGE_SUFFIX }} test --all + run: docker run --rm ${{ env.DOCKER_DEVICES }} ${{ env.DOCKER_VOLUMES }} integritee-worker-${{ env.IMAGE_SUFFIX }} test --all - name: Export worker image(s) run: | @@ -145,6 +145,8 @@ jobs: fail-fast: false matrix: check: [ + # Workspace + cargo test --release, # Worker # Use release mode as the CI runs out of disk space otherwise. cargo clippy --release -- -D warnings, diff --git a/Cargo.lock b/Cargo.lock index 382688b96e..dd1c35e64c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2911,7 +2911,6 @@ dependencies = [ "sgx-verify", "sgx_crypto_helper", "sgx_types", - "sgx_urts", "sha2 0.7.1", "sp-consensus-grandpa", "sp-core", diff --git a/Makefile b/Makefile index 80dafb88ea..61e8ca0d53 100755 --- a/Makefile +++ b/Makefile @@ -76,13 +76,13 @@ ifeq ($(SGX_PRODUCTION), 1) SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.production.xml" SGX_SIGN_KEY = $(SGX_COMMERCIAL_KEY) SGX_SIGN_PASSFILE = $(SGX_PASSFILE) - WORKER_FEATURES := --features=production,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) + WORKER_FEATURES := --features=production,link-binary,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) else SGX_ENCLAVE_MODE = "Development Mode" SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.xml" SGX_SIGN_KEY = "enclave-runtime/Enclave_private.pem" SGX_SIGN_PASSFILE = "" - WORKER_FEATURES := --features=default,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) + WORKER_FEATURES := --features=default,link-binary,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) endif CLIENT_FEATURES = --features=$(WORKER_MODE),$(ADDITIONAL_FEATURES) @@ -170,7 +170,7 @@ $(Worker_Enclave_u_Object): service/Enclave_u.o $(Worker_Name): $(Worker_Enclave_u_Object) $(SRC_Files) @echo - @echo "Building the integritee-service" + @echo "Building the integritee-service: $(Worker_Rust_Flags)" @SGX_SDK=$(SGX_SDK) SGX_MODE=$(SGX_MODE) cargo build -p integritee-service $(Worker_Rust_Flags) @echo "Cargo => $@" cp $(Worker_Rust_Path)/integritee-service ./bin @@ -178,7 +178,7 @@ $(Worker_Name): $(Worker_Enclave_u_Object) $(SRC_Files) ######## Integritee-client objects ######## $(Client_Name): $(SRC_Files) @echo - @echo "Building the integritee-cli" + @echo "Building the integritee-cli $(Client_Rust_Flags)" @cargo build -p integritee-cli $(Client_Rust_Flags) @echo "Cargo => $@" cp $(Client_Rust_Path)/$(Client_Binary) ./bin diff --git a/build.Dockerfile b/build.Dockerfile index e3fd8ff4e2..0536f66925 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -70,7 +70,7 @@ RUN --mount=type=cache,id=cargo-registry-cache,target=/opt/rust/registry/cache,s --mount=type=cache,id=cargo-registry-index,target=/opt/rust/registry/index,sharing=private \ --mount=type=cache,id=cargo-git,target=/opt/rust/git/db,sharing=private \ --mount=type=cache,id=cargo-sccache-${WORKER_MODE}${ADDITIONAL_FEATURES},target=/home/ubuntu/.cache/sccache \ - echo ${FINGERPRINT} && make && make identity && cargo test --release && sccache --show-stats + echo ${FINGERPRINT} && make && make identity && sccache --show-stats ### Base Runner Stage ### The runner needs the aesmd service for the `SGX_MODE=HW`. diff --git a/core-primitives/enclave-api/Cargo.toml b/core-primitives/enclave-api/Cargo.toml index c1d4249a17..9f10fb3c1f 100644 --- a/core-primitives/enclave-api/Cargo.toml +++ b/core-primitives/enclave-api/Cargo.toml @@ -13,7 +13,7 @@ thiserror = "1.0.25" sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_urts = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_urts = { optional = true, branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } @@ -27,3 +27,10 @@ itp-enclave-api-ffi = { path = "ffi" } itp-settings = { path = "../settings" } itp-storage = { path = "../storage" } itp-types = { path = "../types" } + +[features] +default = [] +implement-ffi = [ + "sgx_urts", + "itp-enclave-api-ffi/link-sgx-libs", +] diff --git a/core-primitives/enclave-api/ffi/Cargo.toml b/core-primitives/enclave-api/ffi/Cargo.toml index d0b209d4d2..4ce7be0e66 100644 --- a/core-primitives/enclave-api/ffi/Cargo.toml +++ b/core-primitives/enclave-api/ffi/Cargo.toml @@ -7,3 +7,8 @@ edition = "2021" [dependencies] sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +[features] +# necessary to run cargo tests without any preliminaries +# See: https://github.com/rust-lang/cargo/issues/2549 +link-sgx-libs = [] diff --git a/core-primitives/enclave-api/ffi/build.rs b/core-primitives/enclave-api/ffi/build.rs index f4d28dc290..766abb3eb4 100644 --- a/core-primitives/enclave-api/ffi/build.rs +++ b/core-primitives/enclave-api/ffi/build.rs @@ -16,27 +16,29 @@ */ -use std::env; - fn main() { - let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/intel/sgxsdk".to_string()); - let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); - - // NOTE: if the crate is a workspace member rustc-paths are relative from the root directory - println!("cargo:rustc-link-search=native=./lib"); - println!("cargo:rustc-link-lib=static=Enclave_u"); - - println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); - println!("cargo:rustc-link-lib=static=sgx_uprotected_fs"); - match is_sim.as_ref() { - "SW" => { - println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim"); - }, - _ => { - // HW by default - println!("cargo:rustc-link-lib=dylib=sgx_urts"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service"); - }, + if cfg!(feature = "link-sgx-libs") { + use std::env; + + let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/intel/sgxsdk".to_string()); + let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); + + // NOTE: if the crate is a workspace member rustc-paths are relative from the root directory + println!("cargo:rustc-link-search=native=./lib"); + println!("cargo:rustc-link-lib=static=Enclave_u"); + + println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); + println!("cargo:rustc-link-lib=static=sgx_uprotected_fs"); + match is_sim.as_ref() { + "SW" => { + println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"); + println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim"); + }, + _ => { + // HW by default + println!("cargo:rustc-link-lib=dylib=sgx_urts"); + println!("cargo:rustc-link-lib=dylib=sgx_uae_service"); + }, + } } } diff --git a/core-primitives/enclave-api/src/direct_request.rs b/core-primitives/enclave-api/src/direct_request.rs index 570b27b484..f3fff3388a 100644 --- a/core-primitives/enclave-api/src/direct_request.rs +++ b/core-primitives/enclave-api/src/direct_request.rs @@ -16,10 +16,7 @@ */ -use crate::{error::Error, Enclave, EnclaveResult}; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use sgx_types::sgx_status_t; +use crate::EnclaveResult; pub trait DirectRequest: Send + Sync + 'static { // Todo: Vec shall be replaced by D: Decode, E: Encode but this is currently @@ -27,26 +24,35 @@ pub trait DirectRequest: Send + Sync + 'static { fn rpc(&self, request: Vec) -> EnclaveResult>; } -impl DirectRequest for Enclave { - fn rpc(&self, request: Vec) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let response_len = 8192; - let mut response: Vec = vec![0u8; response_len as usize]; - - let res = unsafe { - ffi::call_rpc_methods( - self.eid, - &mut retval, - request.as_ptr(), - request.len() as u32, - response.as_mut_ptr(), - response_len, - ) - }; - - ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(response) +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::DirectRequest; + use crate::{error::Error, Enclave, EnclaveResult}; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use sgx_types::sgx_status_t; + + impl DirectRequest for Enclave { + fn rpc(&self, request: Vec) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_len = 8192; + let mut response: Vec = vec![0u8; response_len as usize]; + + let res = unsafe { + ffi::call_rpc_methods( + self.eid, + &mut retval, + request.as_ptr(), + request.len() as u32, + response.as_mut_ptr(), + response_len, + ) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(response) + } } } diff --git a/core-primitives/enclave-api/src/enclave_base.rs b/core-primitives/enclave-api/src/enclave_base.rs index 6ef543656c..05df752cab 100644 --- a/core-primitives/enclave-api/src/enclave_base.rs +++ b/core-primitives/enclave-api/src/enclave_base.rs @@ -16,19 +16,12 @@ */ -use crate::{error::Error, Enclave, EnclaveResult}; -use codec::{Decode, Encode}; +use crate::EnclaveResult; +use codec::Decode; use core::fmt::Debug; -use frame_support::ensure; use itc_parentchain::primitives::{ParentchainId, ParentchainInitParams}; -use itp_enclave_api_ffi as ffi; -use itp_settings::worker::{ - HEADER_MAX_SIZE, MR_ENCLAVE_SIZE, SHIELDING_KEY_SIZE, SIGNING_KEY_SIZE, -}; use itp_types::ShardIdentifier; -use log::*; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sgx_types::*; use sp_core::ed25519; use teerex_primitives::EnclaveFingerprint; @@ -84,287 +77,308 @@ pub trait EnclaveBase: Send + Sync + 'static { } /// EnclaveApi implementation for Enclave struct -impl EnclaveBase for Enclave { - fn init( - &self, - mu_ra_addr: &str, - untrusted_worker_addr: &str, - base_dir: &str, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_mu_ra_addr = mu_ra_addr.encode(); - let encoded_untrusted_worker_addr = untrusted_worker_addr.encode(); - let encoded_base_dir = base_dir.encode(); - - let result = unsafe { - ffi::init( - self.eid, - &mut retval, - encoded_mu_ra_addr.as_ptr(), - encoded_mu_ra_addr.len() as u32, - encoded_untrusted_worker_addr.as_ptr(), - encoded_untrusted_worker_addr.len() as u32, - encoded_base_dir.as_ptr(), - encoded_base_dir.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_enclave_sidechain_components(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { ffi::init_enclave_sidechain_components(self.eid, &mut retval) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_rpc_server_addr = rpc_server_addr.encode(); - - let result = unsafe { - ffi::init_direct_invocation_server( - self.eid, - &mut retval, - encoded_rpc_server_addr.as_ptr(), - encoded_rpc_server_addr.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_parentchain_components( - &self, - params: ParentchainInitParams, - ) -> EnclaveResult
{ - let latest_header_encoded = init_parentchain_components_ffi(self.eid, params.encode())?; - - let latest = Header::decode(&mut latest_header_encoded.as_slice())?; - info!("Latest Header {:?}", latest); - - Ok(latest) - } - - fn init_shard(&self, shard: Vec) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = - unsafe { ffi::init_shard(self.eid, &mut retval, shard.as_ptr(), shard.len() as u32) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_proxied_shard_vault(&self, shard: &ShardIdentifier) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let shard_bytes = shard.encode(); - let result = unsafe { - ffi::init_proxied_shard_vault( - self.eid, - &mut retval, - shard_bytes.as_ptr(), - shard_bytes.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - fn trigger_parentchain_block_import( - &self, - parentchain_id: &ParentchainId, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::trigger_parentchain_block_import( - self.eid, - &mut retval, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::set_nonce( - self.eid, - &mut retval, - &nonce, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn set_node_metadata( - &self, - metadata: Vec, - parentchain_id: ParentchainId, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::set_node_metadata( - self.eid, - &mut retval, - metadata.as_ptr(), - metadata.len() as u32, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let pubkey_size = SHIELDING_KEY_SIZE; - let mut pubkey = vec![0u8; pubkey_size]; - - let result = unsafe { - ffi::get_rsa_encryption_pubkey( - self.eid, - &mut retval, - pubkey.as_mut_ptr(), - pubkey.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - let rsa_pubkey: Rsa3072PubKey = - serde_json::from_slice(pubkey.as_slice()).expect("Invalid public key"); - debug!("got RSA pubkey {:?}", rsa_pubkey); - Ok(rsa_pubkey) - } - - fn get_ecc_signing_pubkey(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut pubkey = [0u8; SIGNING_KEY_SIZE]; - - let result = unsafe { - ffi::get_ecc_signing_pubkey( - self.eid, - &mut retval, - pubkey.as_mut_ptr(), - pubkey.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(ed25519::Public::from_raw(pubkey)) +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::EnclaveBase; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::{Decode, Encode}; + use core::fmt::Debug; + use frame_support::ensure; + use itc_parentchain::primitives::{ParentchainId, ParentchainInitParams}; + use itp_enclave_api_ffi as ffi; + use itp_settings::worker::{ + HEADER_MAX_SIZE, MR_ENCLAVE_SIZE, SHIELDING_KEY_SIZE, SIGNING_KEY_SIZE, + }; + use itp_types::ShardIdentifier; + use log::*; + use sgx_crypto_helper::rsa3072::Rsa3072PubKey; + use sgx_types::*; + use sp_core::ed25519; + use teerex_primitives::EnclaveFingerprint; + + impl EnclaveBase for Enclave { + fn init( + &self, + mu_ra_addr: &str, + untrusted_worker_addr: &str, + base_dir: &str, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_mu_ra_addr = mu_ra_addr.encode(); + let encoded_untrusted_worker_addr = untrusted_worker_addr.encode(); + let encoded_base_dir = base_dir.encode(); + + let result = unsafe { + ffi::init( + self.eid, + &mut retval, + encoded_mu_ra_addr.as_ptr(), + encoded_mu_ra_addr.len() as u32, + encoded_untrusted_worker_addr.as_ptr(), + encoded_untrusted_worker_addr.len() as u32, + encoded_base_dir.as_ptr(), + encoded_base_dir.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_enclave_sidechain_components(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { ffi::init_enclave_sidechain_components(self.eid, &mut retval) }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_rpc_server_addr = rpc_server_addr.encode(); + + let result = unsafe { + ffi::init_direct_invocation_server( + self.eid, + &mut retval, + encoded_rpc_server_addr.as_ptr(), + encoded_rpc_server_addr.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_parentchain_components( + &self, + params: ParentchainInitParams, + ) -> EnclaveResult
{ + let latest_header_encoded = init_parentchain_components_ffi(self.eid, params.encode())?; + + let latest = Header::decode(&mut latest_header_encoded.as_slice())?; + info!("Latest Header {:?}", latest); + + Ok(latest) + } + + fn init_shard(&self, shard: Vec) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::init_shard(self.eid, &mut retval, shard.as_ptr(), shard.len() as u32) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_proxied_shard_vault(&self, shard: &ShardIdentifier) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let shard_bytes = shard.encode(); + let result = unsafe { + ffi::init_proxied_shard_vault( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + fn trigger_parentchain_block_import( + &self, + parentchain_id: &ParentchainId, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::trigger_parentchain_block_import( + self.eid, + &mut retval, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::set_nonce( + self.eid, + &mut retval, + &nonce, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn set_node_metadata( + &self, + metadata: Vec, + parentchain_id: ParentchainId, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::set_node_metadata( + self.eid, + &mut retval, + metadata.as_ptr(), + metadata.len() as u32, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let pubkey_size = SHIELDING_KEY_SIZE; + let mut pubkey = vec![0u8; pubkey_size]; + + let result = unsafe { + ffi::get_rsa_encryption_pubkey( + self.eid, + &mut retval, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + let rsa_pubkey: Rsa3072PubKey = + serde_json::from_slice(pubkey.as_slice()).expect("Invalid public key"); + debug!("got RSA pubkey {:?}", rsa_pubkey); + Ok(rsa_pubkey) + } + + fn get_ecc_signing_pubkey(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut pubkey = [0u8; SIGNING_KEY_SIZE]; + + let result = unsafe { + ffi::get_ecc_signing_pubkey( + self.eid, + &mut retval, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(ed25519::Public::from_raw(pubkey)) + } + + fn get_ecc_vault_pubkey(&self, shard: &ShardIdentifier) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut pubkey = [0u8; SIGNING_KEY_SIZE]; + let shard_bytes = shard.encode(); + + let result = unsafe { + ffi::get_ecc_vault_pubkey( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(ed25519::Public::from_raw(pubkey)) + } + + fn get_fingerprint(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut mr_enclave = [0u8; MR_ENCLAVE_SIZE]; + + let result = unsafe { + ffi::get_mrenclave( + self.eid, + &mut retval, + mr_enclave.as_mut_ptr(), + mr_enclave.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(mr_enclave.into()) + } } - fn get_ecc_vault_pubkey(&self, shard: &ShardIdentifier) -> EnclaveResult { + fn init_parentchain_components_ffi( + enclave_id: sgx_enclave_id_t, + params: Vec, + ) -> EnclaveResult> { let mut retval = sgx_status_t::SGX_SUCCESS; - let mut pubkey = [0u8; SIGNING_KEY_SIZE]; - let shard_bytes = shard.encode(); - - let result = unsafe { - ffi::get_ecc_vault_pubkey( - self.eid, - &mut retval, - shard_bytes.as_ptr(), - shard_bytes.len() as u32, - pubkey.as_mut_ptr(), - pubkey.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(ed25519::Public::from_raw(pubkey)) - } - - fn get_fingerprint(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut mr_enclave = [0u8; MR_ENCLAVE_SIZE]; + let latest_header_size = HEADER_MAX_SIZE; + let mut latest_header = vec![0u8; latest_header_size]; let result = unsafe { - ffi::get_mrenclave( - self.eid, + ffi::init_parentchain_components( + enclave_id, &mut retval, - mr_enclave.as_mut_ptr(), - mr_enclave.len() as u32, + params.as_ptr(), + params.len(), + latest_header.as_mut_ptr(), + latest_header.len(), ) }; ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(mr_enclave.into()) + Ok(latest_header) } } - -fn init_parentchain_components_ffi( - enclave_id: sgx_enclave_id_t, - params: Vec, -) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let latest_header_size = HEADER_MAX_SIZE; - let mut latest_header = vec![0u8; latest_header_size]; - - let result = unsafe { - ffi::init_parentchain_components( - enclave_id, - &mut retval, - params.as_ptr(), - params.len(), - latest_header.as_mut_ptr(), - latest_header.len(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(latest_header) -} diff --git a/core-primitives/enclave-api/src/enclave_test.rs b/core-primitives/enclave-api/src/enclave_test.rs index 6bdf85c789..aaf3a8e97d 100644 --- a/core-primitives/enclave-api/src/enclave_test.rs +++ b/core-primitives/enclave-api/src/enclave_test.rs @@ -16,27 +16,33 @@ */ -use crate::{error::Error, Enclave, EnclaveResult}; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use log::*; -use sgx_types::sgx_status_t; +use crate::EnclaveResult; pub trait EnclaveTest: Send + Sync + 'static { fn test_main_entrance(&self) -> EnclaveResult<()>; } -impl EnclaveTest for Enclave { - fn test_main_entrance(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::EnclaveTest; + use crate::{error::Error, Enclave, EnclaveResult}; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use log::*; + use sgx_types::sgx_status_t; - let result = unsafe { ffi::test_main_entrance(self.eid, &mut retval) }; + impl EnclaveTest for Enclave { + fn test_main_entrance(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + let result = unsafe { ffi::test_main_entrance(self.eid, &mut retval) }; - debug!("[+] successfully executed enclave test main"); + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(()) + debug!("[+] successfully executed enclave test main"); + + Ok(()) + } } } diff --git a/core-primitives/enclave-api/src/lib.rs b/core-primitives/enclave-api/src/lib.rs index c4e1ceb047..38c810624f 100644 --- a/core-primitives/enclave-api/src/lib.rs +++ b/core-primitives/enclave-api/src/lib.rs @@ -10,11 +10,8 @@ //! is implemented. Then we can replace the several ffi:: and the boilerplate code //! around it with a simple `fn ecall(call: CallEnum) -> Result`, which wraps one single //! ffi function. -//! use crate::error::Error; -use sgx_types::*; -use sgx_urts::SgxEnclave; pub mod direct_request; pub mod enclave_base; @@ -25,14 +22,22 @@ pub mod sidechain; pub mod teeracle_api; pub mod utils; +#[cfg(feature = "implement-ffi")] +pub use sgx_urts::SgxEnclave; + +#[cfg(feature = "implement-ffi")] +use sgx_types::sgx_enclave_id_t; + pub type EnclaveResult = Result; +#[cfg(feature = "implement-ffi")] #[derive(Clone, Debug, Default)] pub struct Enclave { eid: sgx_enclave_id_t, sgx_enclave: SgxEnclave, } +#[cfg(feature = "implement-ffi")] impl Enclave { pub fn new(sgx_enclave: SgxEnclave) -> Self { Enclave { eid: sgx_enclave.geteid(), sgx_enclave } diff --git a/core-primitives/enclave-api/src/remote_attestation.rs b/core-primitives/enclave-api/src/remote_attestation.rs index beecaa24f3..dd48f9b9f7 100644 --- a/core-primitives/enclave-api/src/remote_attestation.rs +++ b/core-primitives/enclave-api/src/remote_attestation.rs @@ -16,24 +16,11 @@ */ -use crate::{error::Error, utils, Enclave, EnclaveResult}; -use codec::Encode; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use itp_settings::worker::EXTRINSIC_MAX_SIZE; +use crate::EnclaveResult; use itp_types::ShardIdentifier; -use log::*; use sgx_types::*; use teerex_primitives::Fmspc; -const OS_SYSTEM_PATH: &str = "/usr/lib/x86_64-linux-gnu/"; -const C_STRING_ENDING: &str = "\0"; -const PCE_ENCLAVE: &str = "libsgx_pce.signed.so.1"; -const QE3_ENCLAVE: &str = "libsgx_qe3.signed.so.1"; -const ID_ENCLAVE: &str = "libsgx_id_enclave.signed.so.1"; -const LIBDCAP_QUOTEPROV: &str = "libdcap_quoteprov.so.1"; -const QVE_ENCLAVE: &str = "libsgx_qve.signed.so.1"; - /// Struct that unites all relevant data reported by the QVE pub struct QveReport { pub supplemental_data: Vec, @@ -131,306 +118,343 @@ pub trait TlsRemoteAttestation { ) -> EnclaveResult<()>; } -impl RemoteAttestation for Enclave { - fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - - trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); - - let url = w_url.encode(); - - let result = unsafe { - ffi::generate_ias_ra_extrinsic( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::{QveReport, RemoteAttestation, RemoteAttestationCallBacks, TlsRemoteAttestation}; + use crate::{error::Error, utils, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use itp_settings::worker::EXTRINSIC_MAX_SIZE; + use itp_types::ShardIdentifier; + use log::*; + use sgx_types::*; + use teerex_primitives::Fmspc; + + const OS_SYSTEM_PATH: &str = "/usr/lib/x86_64-linux-gnu/"; + const C_STRING_ENDING: &str = "\0"; + const PCE_ENCLAVE: &str = "libsgx_pce.signed.so.1"; + const QE3_ENCLAVE: &str = "libsgx_qe3.signed.so.1"; + const ID_ENCLAVE: &str = "libsgx_id_enclave.signed.so.1"; + const LIBDCAP_QUOTEPROV: &str = "libdcap_quoteprov.so.1"; + const QVE_ENCLAVE: &str = "libsgx_qve.signed.so.1"; + + impl RemoteAttestation for Enclave { + fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + + trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); + + let url = w_url.encode(); + + let result = unsafe { + ffi::generate_ias_ra_extrinsic( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(unchecked_extrinsic) + } + fn generate_dcap_ra_extrinsic_from_quote( + &self, + url: String, + quote: &[u8], + ) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let url = url.encode(); + + let result = unsafe { + ffi::generate_dcap_ra_extrinsic_from_quote( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + quote.as_ptr(), + quote.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(unchecked_extrinsic.to_vec()) + } - Ok(unchecked_extrinsic) - } - fn generate_dcap_ra_extrinsic_from_quote( - &self, - url: String, - quote: &[u8], - ) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let url = url.encode(); - - let result = unsafe { - ffi::generate_dcap_ra_extrinsic_from_quote( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - quote.as_ptr(), - quote.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(unchecked_extrinsic.to_vec()) - } + fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let quoting_enclave_target_info = self.qe_get_target_info()?; + let quote_size = self.qe_get_quote_size()?; + + let mut dcap_quote_vec: Vec = vec![0; quote_size as usize]; + let (dcap_quote_p, dcap_quote_size) = + (dcap_quote_vec.as_mut_ptr(), dcap_quote_vec.len() as u32); + + let result = unsafe { + ffi::generate_dcap_ra_quote( + self.eid, + &mut retval, + skip_ra.into(), + "ing_enclave_target_info, + quote_size, + dcap_quote_p, + dcap_quote_size, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + unsafe { + trace!("Generating DCAP RA Quote: {}", *dcap_quote_p); + } - fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let quoting_enclave_target_info = self.qe_get_target_info()?; - let quote_size = self.qe_get_quote_size()?; - - let mut dcap_quote_vec: Vec = vec![0; quote_size as usize]; - let (dcap_quote_p, dcap_quote_size) = - (dcap_quote_vec.as_mut_ptr(), dcap_quote_vec.len() as u32); - - let result = unsafe { - ffi::generate_dcap_ra_quote( - self.eid, - &mut retval, - skip_ra.into(), - "ing_enclave_target_info, - quote_size, - dcap_quote_p, - dcap_quote_size, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - unsafe { - trace!("Generating DCAP RA Quote: {}", *dcap_quote_p); + Ok(dcap_quote_vec) } - Ok(dcap_quote_vec) - } - - fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; + fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; - self.set_ql_qe_enclave_paths()?; - let quoting_enclave_target_info = if !skip_ra { - match self.qe_get_target_info() { - Ok(target_info) => Some(target_info), - Err(e) => return Err(e), - } - } else { - None - }; - let quote_size = if !skip_ra { - match self.qe_get_quote_size() { - Ok(quote_size) => Some(quote_size), - Err(e) => return Err(e), - } - } else { - None - }; - info!("Retrieved quote size of {:?}", quote_size); - - trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); - - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - - let url = w_url.encode(); - - let result = unsafe { - ffi::generate_dcap_ra_extrinsic( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - skip_ra.into(), - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(unchecked_extrinsic) - } - - fn generate_register_quoting_enclave_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - - trace!("Generating register quoting enclave"); - - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - - let result = unsafe { - ffi::generate_register_quoting_enclave_extrinsic( - self.eid, - &mut retval, - collateral_ptr, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - ) - }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!(free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(free_status)); - - Ok(unchecked_extrinsic) - } + self.set_ql_qe_enclave_paths()?; + let quoting_enclave_target_info = if !skip_ra { + match self.qe_get_target_info() { + Ok(target_info) => Some(target_info), + Err(e) => return Err(e), + } + } else { + None + }; + let quote_size = if !skip_ra { + match self.qe_get_quote_size() { + Ok(quote_size) => Some(quote_size), + Err(e) => return Err(e), + } + } else { + None + }; + info!("Retrieved quote size of {:?}", quote_size); + + trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); + + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + + let url = w_url.encode(); + + let result = unsafe { + ffi::generate_dcap_ra_extrinsic( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + skip_ra.into(), + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(unchecked_extrinsic) + } - fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - - trace!("Generating tcb_info registration"); - - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - - let result = unsafe { - ffi::generate_register_tcb_info_extrinsic( - self.eid, - &mut retval, - collateral_ptr, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - ) - }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!(free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(free_status)); - - Ok(unchecked_extrinsic) - } + fn generate_register_quoting_enclave_extrinsic( + &self, + fmspc: Fmspc, + ) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + + trace!("Generating register quoting enclave"); + + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + + let result = unsafe { + ffi::generate_register_quoting_enclave_extrinsic( + self.eid, + &mut retval, + collateral_ptr, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + ) + }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); - fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; + Ok(unchecked_extrinsic) + } - let result = unsafe { ffi::dump_ias_ra_cert_to_disk(self.eid, &mut retval) }; + fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + + trace!("Generating tcb_info registration"); + + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + + let result = unsafe { + ffi::generate_register_tcb_info_extrinsic( + self.eid, + &mut retval, + collateral_ptr, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + ) + }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + Ok(unchecked_extrinsic) + } - Ok(()) - } + fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; - fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; + let result = unsafe { ffi::dump_ias_ra_cert_to_disk(self.eid, &mut retval) }; - self.set_ql_qe_enclave_paths()?; - let quoting_enclave_target_info = self.qe_get_target_info()?; - let quote_size = self.qe_get_quote_size()?; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - let result = unsafe { - ffi::dump_dcap_ra_cert_to_disk( - self.eid, - &mut retval, - "ing_enclave_target_info, - quote_size, - ) - }; + Ok(()) + } - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; - Ok(()) - } + self.set_ql_qe_enclave_paths()?; + let quoting_enclave_target_info = self.qe_get_target_info()?; + let quote_size = self.qe_get_quote_size()?; - fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()> { - set_ql_path(sgx_ql_path_type_t::SGX_QL_PCE_PATH, PCE_ENCLAVE)?; - set_ql_path(sgx_ql_path_type_t::SGX_QL_QE3_PATH, QE3_ENCLAVE)?; - set_ql_path(sgx_ql_path_type_t::SGX_QL_IDE_PATH, ID_ENCLAVE)?; - if set_ql_path(sgx_ql_path_type_t::SGX_QL_QPL_PATH, LIBDCAP_QUOTEPROV).is_err() { - // Ignore the error, because user may want to get cert type=3 quote. - warn!("Cannot set QPL directory, you may get ECDSA quote with `Encrypted PPID` cert type.\n"); - }; - set_qv_path(sgx_qv_path_type_t::SGX_QV_QVE_PATH, QVE_ENCLAVE)?; + let result = unsafe { + ffi::dump_dcap_ra_cert_to_disk( + self.eid, + &mut retval, + "ing_enclave_target_info, + quote_size, + ) + }; - Ok(()) - } + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - fn set_sgx_qpl_logging(&self) -> EnclaveResult<()> { - let log_level = sgx_ql_log_level_t::SGX_QL_LOG_INFO; - let res = unsafe { sgx_ql_set_logging_callback(forward_qpl_log, log_level) }; - if res == sgx_quote3_error_t::SGX_QL_SUCCESS { Ok(()) - } else { - error!("Setting logging function failed with: {:?}", res); - Err(Error::SgxQuote(res)) } - } - fn qe_get_target_info(&self) -> EnclaveResult { - let mut quoting_enclave_target_info: sgx_target_info_t = sgx_target_info_t::default(); - let qe3_ret = unsafe { sgx_qe_get_target_info(&mut quoting_enclave_target_info as *mut _) }; - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); + fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()> { + set_ql_path(sgx_ql_path_type_t::SGX_QL_PCE_PATH, PCE_ENCLAVE)?; + set_ql_path(sgx_ql_path_type_t::SGX_QL_QE3_PATH, QE3_ENCLAVE)?; + set_ql_path(sgx_ql_path_type_t::SGX_QL_IDE_PATH, ID_ENCLAVE)?; + if set_ql_path(sgx_ql_path_type_t::SGX_QL_QPL_PATH, LIBDCAP_QUOTEPROV).is_err() { + // Ignore the error, because user may want to get cert type=3 quote. + warn!("Cannot set QPL directory, you may get ECDSA quote with `Encrypted PPID` cert type.\n"); + }; + set_qv_path(sgx_qv_path_type_t::SGX_QV_QVE_PATH, QVE_ENCLAVE)?; - Ok(quoting_enclave_target_info) - } - - fn qe_get_quote_size(&self) -> EnclaveResult { - let mut quote_size: u32 = 0; - let qe3_ret = unsafe { sgx_qe_get_quote_size(&mut quote_size as *mut _) }; - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - - Ok(quote_size) - } + Ok(()) + } - fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - let result = - unsafe { ffi::dump_dcap_collateral_to_disk(self.eid, &mut retval, collateral_ptr) }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(free_status)); - Ok(()) - } + fn set_sgx_qpl_logging(&self) -> EnclaveResult<()> { + let log_level = sgx_ql_log_level_t::SGX_QL_LOG_INFO; + let res = unsafe { sgx_ql_set_logging_callback(forward_qpl_log, log_level) }; + if res == sgx_quote3_error_t::SGX_QL_SUCCESS { + Ok(()) + } else { + error!("Setting logging function failed with: {:?}", res); + Err(Error::SgxQuote(res)) + } + } - fn get_dcap_collateral(&self, fmspc: Fmspc) -> EnclaveResult<*const sgx_ql_qve_collateral_t> { - let pck_ra = b"processor\x00"; + fn qe_get_target_info(&self) -> EnclaveResult { + let mut quoting_enclave_target_info: sgx_target_info_t = sgx_target_info_t::default(); + let qe3_ret = + unsafe { sgx_qe_get_target_info(&mut quoting_enclave_target_info as *mut _) }; + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - // SAFETY: Just get a nullptr for the FFI to overwrite later - let mut collateral_ptr: *mut sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() }; + Ok(quoting_enclave_target_info) + } - let collateral_ptr_ptr: *mut *mut sgx_ql_qve_collateral_t = &mut collateral_ptr; - // SAFETY: All parameters are properly initialized so the FFI call should be fine - let sgx_status = unsafe { - sgx_ql_get_quote_verification_collateral( - fmspc.as_ptr(), - fmspc.len() as uint16_t, //fmspc len is fixed in the function signature - pck_ra.as_ptr() as _, - collateral_ptr_ptr, - ) - }; + fn qe_get_quote_size(&self) -> EnclaveResult { + let mut quote_size: u32 = 0; + let qe3_ret = unsafe { sgx_qe_get_quote_size(&mut quote_size as *mut _) }; + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - trace!("FMSPC: {:?}", hex::encode(fmspc)); + Ok(quote_size) + } - if collateral_ptr.is_null() { - error!("PCK quote collateral data is null, sgx_status is: {}", sgx_status); - return Err(Error::SgxQuote(sgx_status)) + fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + let result = + unsafe { ffi::dump_dcap_collateral_to_disk(self.eid, &mut retval, collateral_ptr) }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); + Ok(()) } - trace!("collateral:"); - // SAFETY: the previous block checks for `collateral_ptr` being null. - // SAFETY: the fields should be nul terminated C strings. - unsafe { - let collateral = &*collateral_ptr; - trace!( - "version: {}\n, \ + fn get_dcap_collateral( + &self, + fmspc: Fmspc, + ) -> EnclaveResult<*const sgx_ql_qve_collateral_t> { + let pck_ra = b"processor\x00"; + + // SAFETY: Just get a nullptr for the FFI to overwrite later + let mut collateral_ptr: *mut sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() }; + + let collateral_ptr_ptr: *mut *mut sgx_ql_qve_collateral_t = &mut collateral_ptr; + // SAFETY: All parameters are properly initialized so the FFI call should be fine + let sgx_status = unsafe { + sgx_ql_get_quote_verification_collateral( + fmspc.as_ptr(), + fmspc.len() as uint16_t, //fmspc len is fixed in the function signature + pck_ra.as_ptr() as _, + collateral_ptr_ptr, + ) + }; + + trace!("FMSPC: {:?}", hex::encode(fmspc)); + + if collateral_ptr.is_null() { + error!("PCK quote collateral data is null, sgx_status is: {}", sgx_status); + return Err(Error::SgxQuote(sgx_status)) + } + + trace!("collateral:"); + // SAFETY: the previous block checks for `collateral_ptr` being null. + // SAFETY: the fields should be nul terminated C strings. + unsafe { + let collateral = &*collateral_ptr; + trace!( + "version: {}\n, \ tee_type: {}\n, \ pck_crl_issuer_chain: {:?}\n, \ pck_crl_issuer_chain_size: {}\n, \ @@ -446,364 +470,379 @@ impl RemoteAttestation for Enclave { qe_identity_issuer_chain_size: {}\n, \ qe_identity: {}\n, \ qe_identity_size: {}\n", - collateral.version, - collateral.tee_type, - std::ffi::CStr::from_ptr(collateral.pck_crl_issuer_chain).to_string_lossy(), - collateral.pck_crl_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.root_ca_crl).to_string_lossy(), - collateral.root_ca_crl_size, - std::ffi::CStr::from_ptr(collateral.pck_crl).to_string_lossy(), - collateral.pck_crl_size, - std::ffi::CStr::from_ptr(collateral.tcb_info_issuer_chain).to_string_lossy(), - collateral.tcb_info_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.tcb_info).to_string_lossy(), - collateral.tcb_info_size, - std::ffi::CStr::from_ptr(collateral.qe_identity_issuer_chain).to_string_lossy(), - collateral.qe_identity_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.qe_identity).to_string_lossy(), - collateral.qe_identity_size, - ); - }; + collateral.version, + collateral.tee_type, + std::ffi::CStr::from_ptr(collateral.pck_crl_issuer_chain).to_string_lossy(), + collateral.pck_crl_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.root_ca_crl).to_string_lossy(), + collateral.root_ca_crl_size, + std::ffi::CStr::from_ptr(collateral.pck_crl).to_string_lossy(), + collateral.pck_crl_size, + std::ffi::CStr::from_ptr(collateral.tcb_info_issuer_chain).to_string_lossy(), + collateral.tcb_info_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.tcb_info).to_string_lossy(), + collateral.tcb_info_size, + std::ffi::CStr::from_ptr(collateral.qe_identity_issuer_chain).to_string_lossy(), + collateral.qe_identity_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.qe_identity).to_string_lossy(), + collateral.qe_identity_size, + ); + }; - ensure!(sgx_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(sgx_status)); - Ok(collateral_ptr) + ensure!(sgx_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(sgx_status)); + Ok(collateral_ptr) + } } -} -impl RemoteAttestationCallBacks for Enclave { - fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)> { - let mut ti: sgx_target_info_t = sgx_target_info_t::default(); - let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); + #[cfg(feature = "implement-ffi")] + impl RemoteAttestationCallBacks for Enclave { + fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + let mut ti: sgx_target_info_t = sgx_target_info_t::default(); + let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); - let result = unsafe { - sgx_init_quote(&mut ti as *mut sgx_target_info_t, &mut eg as *mut sgx_epid_group_id_t) - }; + let result = unsafe { + sgx_init_quote( + &mut ti as *mut sgx_target_info_t, + &mut eg as *mut sgx_epid_group_id_t, + ) + }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - Ok((ti, eg)) - } + Ok((ti, eg)) + } - fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult { - let mut real_quote_len: u32 = 0; + fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult { + let mut real_quote_len: u32 = 0; - let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); + let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); - let result = - unsafe { sgx_calc_quote_size(p_sig_rl, sig_rl_size, &mut real_quote_len as *mut u32) }; + let result = unsafe { + sgx_calc_quote_size(p_sig_rl, sig_rl_size, &mut real_quote_len as *mut u32) + }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - Ok(real_quote_len) - } - - fn get_quote( - &self, - revocation_list: Vec, - report: sgx_report_t, - quote_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - quote_length: u32, - ) -> EnclaveResult<(sgx_report_t, Vec)> { - let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); - let p_report = &report as *const sgx_report_t; - let p_spid = &spid as *const sgx_spid_t; - let p_nonce = "e_nonce as *const sgx_quote_nonce_t; - - let mut qe_report = sgx_report_t::default(); - let p_qe_report = &mut qe_report as *mut sgx_report_t; - - let mut return_quote_buf = vec![0u8; quote_length as usize]; - let p_quote = return_quote_buf.as_mut_ptr(); - - let ret = unsafe { - sgx_get_quote( - p_report, - quote_type, - p_spid, - p_nonce, - p_sig_rl, - sig_rl_size, - p_qe_report, - p_quote as *mut sgx_quote_t, - quote_length, - ) - }; - - ensure!(ret == sgx_status_t::SGX_SUCCESS, Error::Sgx(ret)); - - Ok((qe_report, return_quote_buf)) - } + Ok(real_quote_len) + } - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult> { - let mut quote_vec: Vec = vec![0; quote_size as usize]; - let qe3_ret = unsafe { sgx_qe_get_quote(&report, quote_size, quote_vec.as_mut_ptr() as _) }; + fn get_quote( + &self, + revocation_list: Vec, + report: sgx_report_t, + quote_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + quote_length: u32, + ) -> EnclaveResult<(sgx_report_t, Vec)> { + let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); + let p_report = &report as *const sgx_report_t; + let p_spid = &spid as *const sgx_spid_t; + let p_nonce = "e_nonce as *const sgx_quote_nonce_t; + + let mut qe_report = sgx_report_t::default(); + let p_qe_report = &mut qe_report as *mut sgx_report_t; + + let mut return_quote_buf = vec![0u8; quote_length as usize]; + let p_quote = return_quote_buf.as_mut_ptr(); + + let ret = unsafe { + sgx_get_quote( + p_report, + quote_type, + p_spid, + p_nonce, + p_sig_rl, + sig_rl_size, + p_qe_report, + p_quote as *mut sgx_quote_t, + quote_length, + ) + }; + + ensure!(ret == sgx_status_t::SGX_SUCCESS, Error::Sgx(ret)); + + Ok((qe_report, return_quote_buf)) + } - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult> { + let mut quote_vec: Vec = vec![0; quote_size as usize]; + let qe3_ret = + unsafe { sgx_qe_get_quote(&report, quote_size, quote_vec.as_mut_ptr() as _) }; - Ok(quote_vec) - } + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: &sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> EnclaveResult { - let mut collateral_expiration_status = 1u32; - let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK; - let mut supplemental_data: Vec = vec![0; supplemental_data_size as usize]; - let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; - - // Set QvE (Quote verification Enclave) loading policy. - let dcap_ret = - unsafe { sgx_qv_set_enclave_load_policy(sgx_ql_request_policy_t::SGX_QL_EPHEMERAL) }; - - if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("sgx_qv_set_enclave_load_policy failed: {:#04x}", dcap_ret as u32); - return Err(Error::SgxQuote(dcap_ret)) + Ok(quote_vec) } - // Retrieve supplemental data size from QvE. - let mut qve_supplemental_data_size = 0u32; - let dcap_ret = - unsafe { sgx_qv_get_quote_supplemental_data_size(&mut qve_supplemental_data_size) }; + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: &sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> EnclaveResult { + let mut collateral_expiration_status = 1u32; + let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK; + let mut supplemental_data: Vec = vec![0; supplemental_data_size as usize]; + let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; + + // Set QvE (Quote verification Enclave) loading policy. + let dcap_ret = unsafe { + sgx_qv_set_enclave_load_policy(sgx_ql_request_policy_t::SGX_QL_EPHEMERAL) + }; + + if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("sgx_qv_set_enclave_load_policy failed: {:#04x}", dcap_ret as u32); + return Err(Error::SgxQuote(dcap_ret)) + } - if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("sgx_qv_get_quote_supplemental_data_size failed: {:?}", dcap_ret); - return Err(Error::SgxQuote(dcap_ret)) - } - if qve_supplemental_data_size != supplemental_data_size { - warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release."); - return Err(Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER)) - } + // Retrieve supplemental data size from QvE. + let mut qve_supplemental_data_size = 0u32; + let dcap_ret = + unsafe { sgx_qv_get_quote_supplemental_data_size(&mut qve_supplemental_data_size) }; - // Check if a collateral has been given, or if it's a simple zero assignment. - // If it's zero, let the pointer point to null. The collateral will then be retrieved - // directly by the QvE in `sgx_qv_verify_quote`. - let p_quote_collateral: *const sgx_ql_qve_collateral_t = if quote_collateral.version == 0 { - std::ptr::null() - } else { - quote_collateral as *const sgx_ql_qve_collateral_t - }; - - // Call the QvE for quote verification - // here you can choose 'trusted' or 'untrusted' quote verification by specifying parameter '&qve_report_info' - // if '&qve_report_info' is NOT NULL, this API will call Intel QvE to verify quote - // if '&qve_report_info' is NULL, this API will call 'untrusted quote verify lib' to verify quote, - // this mode doesn't rely on SGX capable system, but the results can not be cryptographically authenticated - let dcap_ret = unsafe { - sgx_qv_verify_quote( - quote.as_ptr(), - quote.len() as u32, - p_quote_collateral, - current_time, - &mut collateral_expiration_status as *mut u32, - &mut quote_verification_result as *mut sgx_ql_qv_result_t, - &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, - supplemental_data_size, - supplemental_data.as_mut_ptr(), - ) - }; - - if sgx_quote3_error_t::SGX_QL_SUCCESS != dcap_ret { - error!("sgx_qv_verify_quote failed: {:?}", dcap_ret); - error!("quote_verification_result: {:?}", quote_verification_result); - return Err(Error::SgxQuote(dcap_ret)) - } + if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("sgx_qv_get_quote_supplemental_data_size failed: {:?}", dcap_ret); + return Err(Error::SgxQuote(dcap_ret)) + } + if qve_supplemental_data_size != supplemental_data_size { + warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release."); + return Err(Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER)) + } - // Check and print verification result. - match quote_verification_result { - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => { - // Check verification collateral expiration status. - // This value should be considered in your own attestation/verification policy. - if 0u32 == collateral_expiration_status { - info!("QvE verification completed successfully."); + // Check if a collateral has been given, or if it's a simple zero assignment. + // If it's zero, let the pointer point to null. The collateral will then be retrieved + // directly by the QvE in `sgx_qv_verify_quote`. + let p_quote_collateral: *const sgx_ql_qve_collateral_t = + if quote_collateral.version == 0 { + std::ptr::null() } else { - warn!("QvE verification completed, but collateral is out of date based on 'expiration_check_date' you provided."); - } - }, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { - warn!( - "QvE verification completed with Non-terminal result: {:?}", - quote_verification_result - ); - }, - _ => { - error!( - "QvE verification completed with Terminal result: {:?}", - quote_verification_result + quote_collateral as *const sgx_ql_qve_collateral_t + }; + + // Call the QvE for quote verification + // here you can choose 'trusted' or 'untrusted' quote verification by specifying parameter '&qve_report_info' + // if '&qve_report_info' is NOT NULL, this API will call Intel QvE to verify quote + // if '&qve_report_info' is NULL, this API will call 'untrusted quote verify lib' to verify quote, + // this mode doesn't rely on SGX capable system, but the results can not be cryptographically authenticated + let dcap_ret = unsafe { + sgx_qv_verify_quote( + quote.as_ptr(), + quote.len() as u32, + p_quote_collateral, + current_time, + &mut collateral_expiration_status as *mut u32, + &mut quote_verification_result as *mut sgx_ql_qv_result_t, + &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, + supplemental_data_size, + supplemental_data.as_mut_ptr(), + ) + }; + + if sgx_quote3_error_t::SGX_QL_SUCCESS != dcap_ret { + error!("sgx_qv_verify_quote failed: {:?}", dcap_ret); + error!("quote_verification_result: {:?}", quote_verification_result); + return Err(Error::SgxQuote(dcap_ret)) + } + + // Check and print verification result. + match quote_verification_result { + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => { + // Check verification collateral expiration status. + // This value should be considered in your own attestation/verification policy. + if 0u32 == collateral_expiration_status { + info!("QvE verification completed successfully."); + } else { + warn!("QvE verification completed, but collateral is out of date based on 'expiration_check_date' you provided."); + } + }, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { + warn!( + "QvE verification completed with Non-terminal result: {:?}", + quote_verification_result + ); + }, + _ => { + error!( + "QvE verification completed with Terminal result: {:?}", + quote_verification_result + ); + }, + } + + // Check supplemental data. + if supplemental_data_size > 0 { + // For now we simply print it, no checks done. + let p_supplemental_data: *const sgx_ql_qv_supplemental_t = + supplemental_data.as_ptr() as *const sgx_ql_qv_supplemental_t; + let qv_supplemental_data: sgx_ql_qv_supplemental_t = + unsafe { *p_supplemental_data }; + info!( + "QvE verification: Supplemental data version: {}", + qv_supplemental_data.version ); - }, - } + } - // Check supplemental data. - if supplemental_data_size > 0 { - // For now we simply print it, no checks done. - let p_supplemental_data: *const sgx_ql_qv_supplemental_t = - supplemental_data.as_ptr() as *const sgx_ql_qv_supplemental_t; - let qv_supplemental_data: sgx_ql_qv_supplemental_t = unsafe { *p_supplemental_data }; - info!("QvE verification: Supplemental data version: {}", qv_supplemental_data.version); + Ok(QveReport { + collateral_expiration_status, + quote_verification_result, + qve_report_info_return_value, + supplemental_data, + }) } - Ok(QveReport { - collateral_expiration_status, - quote_verification_result, - qve_report_info_return_value, - supplemental_data, - }) - } + fn get_update_info( + &self, + platform_blob: sgx_platform_info_t, + enclave_trusted: i32, + ) -> EnclaveResult { + let mut update_info: sgx_update_info_bit_t = sgx_update_info_bit_t::default(); - fn get_update_info( - &self, - platform_blob: sgx_platform_info_t, - enclave_trusted: i32, - ) -> EnclaveResult { - let mut update_info: sgx_update_info_bit_t = sgx_update_info_bit_t::default(); + let result = unsafe { + sgx_report_attestation_status( + &platform_blob as *const sgx_platform_info_t, + enclave_trusted, + &mut update_info as *mut sgx_update_info_bit_t, + ) + }; - let result = unsafe { - sgx_report_attestation_status( - &platform_blob as *const sgx_platform_info_t, - enclave_trusted, - &mut update_info as *mut sgx_update_info_bit_t, - ) - }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - - Ok(update_info) + Ok(update_info) + } } -} -impl TlsRemoteAttestation for Enclave { - fn run_state_provisioning_server( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: bool, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { - ffi::run_state_provisioning_server( - self.eid, - &mut retval, - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + #[cfg(feature = "implement-ffi")] + impl TlsRemoteAttestation for Enclave { + fn run_state_provisioning_server( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: bool, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::run_state_provisioning_server( + self.eid, + &mut retval, + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(()) - } + Ok(()) + } - fn request_state_provisioning( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: &ShardIdentifier, - skip_ra: bool, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_shard = shard.encode(); - - let result = unsafe { - ffi::request_state_provisioning( - self.eid, - &mut retval, - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - encoded_shard.as_ptr(), - encoded_shard.len() as u32, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + fn request_state_provisioning( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: &ShardIdentifier, + skip_ra: bool, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_shard = shard.encode(); + + let result = unsafe { + ffi::request_state_provisioning( + self.eid, + &mut retval, + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + encoded_shard.as_ptr(), + encoded_shard.len() as u32, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(()) + Ok(()) + } } -} -fn create_system_path(file_name: &str) -> String { - info!("create_system_path:: file_name={}", &file_name); - let default_path = format!("{}{}", OS_SYSTEM_PATH, file_name); + fn create_system_path(file_name: &str) -> String { + info!("create_system_path:: file_name={}", &file_name); + let default_path = format!("{}{}", OS_SYSTEM_PATH, file_name); - let full_path = find_library_by_name(file_name).unwrap_or(default_path); + let full_path = find_library_by_name(file_name).unwrap_or(default_path); - let c_terminated_path = format!("{}{}", full_path, C_STRING_ENDING); - info!("create_system_path:: created path={}", &c_terminated_path); - c_terminated_path -} -fn find_library_by_name(lib_name: &str) -> Option { - use std::process::Command; - // ldconfig -p | grep libsgx_pce_logic.so.1 - - let ldconfig_output = Command::new("ldconfig").args(["-p"]).output().ok()?; - let possible_path = String::from_utf8(ldconfig_output.stdout) - .ok()? - .lines() - .filter(|line| line.contains(lib_name)) - .map(|lib_name_and_path| { - lib_name_and_path - .rsplit_once("=>") - .map(|(_, lib_path)| lib_path.trim().to_owned()) - }) - .next()?; - - possible_path -} + let c_terminated_path = format!("{}{}", full_path, C_STRING_ENDING); + info!("create_system_path:: created path={}", &c_terminated_path); + c_terminated_path + } -fn set_ql_path(path_type: sgx_ql_path_type_t, path: &str) -> EnclaveResult<()> { - let ret_val = unsafe { sgx_ql_set_path(path_type, create_system_path(path).as_ptr() as _) }; - if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("Could not set {:?}", path_type); - return Err(Error::SgxQuote(ret_val)) + fn find_library_by_name(lib_name: &str) -> Option { + use std::process::Command; + // ldconfig -p | grep libsgx_pce_logic.so.1 + + let ldconfig_output = Command::new("ldconfig").args(["-p"]).output().ok()?; + let possible_path = String::from_utf8(ldconfig_output.stdout) + .ok()? + .lines() + .filter(|line| line.contains(lib_name)) + .map(|lib_name_and_path| { + lib_name_and_path + .rsplit_once("=>") + .map(|(_, lib_path)| lib_path.trim().to_owned()) + }) + .next()?; + + possible_path } - Ok(()) -} -fn set_qv_path(path_type: sgx_qv_path_type_t, path: &str) -> EnclaveResult<()> { - let ret_val = unsafe { sgx_qv_set_path(path_type, create_system_path(path).as_ptr() as _) }; - if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("Could not set {:?}", path_type); - return Err(Error::SgxQuote(ret_val)) + fn set_ql_path(path_type: sgx_ql_path_type_t, path: &str) -> EnclaveResult<()> { + let ret_val = unsafe { sgx_ql_set_path(path_type, create_system_path(path).as_ptr() as _) }; + if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("Could not set {:?}", path_type); + return Err(Error::SgxQuote(ret_val)) + } + Ok(()) } - Ok(()) -} -#[allow(clippy::not_unsafe_ptr_arg_deref)] -/// Make sure that the `log_slice_ptr` points to a null terminated string. -// This function must not be marked as `unsafe`, because `sgx_ql_set_logging_callback` expects a safe (i.e. not `unsafe`) function. -pub extern "C" fn forward_qpl_log(log_level: sgx_ql_log_level_t, log_slice_ptr: *const c_char) { - if log_slice_ptr.is_null() { - error!("[QPL - ERROR], slice to print was NULL"); - return + fn set_qv_path(path_type: sgx_qv_path_type_t, path: &str) -> EnclaveResult<()> { + let ret_val = unsafe { sgx_qv_set_path(path_type, create_system_path(path).as_ptr() as _) }; + if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("Could not set {:?}", path_type); + return Err(Error::SgxQuote(ret_val)) + } + Ok(()) } - // This is safe, as the previous block checks for `NULL` pointer. - let slice = unsafe { core::ffi::CStr::from_ptr(log_slice_ptr) }; - match log_level { - sgx_ql_log_level_t::SGX_QL_LOG_INFO => info!("[QPL - INFO], {:?}", slice), - sgx_ql_log_level_t::SGX_QL_LOG_ERROR => error!("[QPL - ERROR], {:?}", slice), + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + /// Make sure that the `log_slice_ptr` points to a null terminated string. + // This function must not be marked as `unsafe`, because `sgx_ql_set_logging_callback` expects a safe (i.e. not `unsafe`) function. + pub extern "C" fn forward_qpl_log(log_level: sgx_ql_log_level_t, log_slice_ptr: *const c_char) { + if log_slice_ptr.is_null() { + error!("[QPL - ERROR], slice to print was NULL"); + return + } + // This is safe, as the previous block checks for `NULL` pointer. + let slice = unsafe { core::ffi::CStr::from_ptr(log_slice_ptr) }; + match log_level { + sgx_ql_log_level_t::SGX_QL_LOG_INFO => info!("[QPL - INFO], {:?}", slice), + sgx_ql_log_level_t::SGX_QL_LOG_ERROR => error!("[QPL - ERROR], {:?}", slice), + } } } diff --git a/core-primitives/enclave-api/src/sidechain.rs b/core-primitives/enclave-api/src/sidechain.rs index c1db6f2293..1203a2d234 100644 --- a/core-primitives/enclave-api/src/sidechain.rs +++ b/core-primitives/enclave-api/src/sidechain.rs @@ -16,13 +16,10 @@ */ -use crate::{error::Error, Enclave, EnclaveResult}; +use crate::EnclaveResult; use codec::Encode; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; use itp_storage::StorageProof; use itp_types::parentchain::ParentchainId; -use sgx_types::sgx_status_t; use sp_runtime::generic::SignedBlock; /// trait for handling blocks on the side chain @@ -40,49 +37,62 @@ pub trait Sidechain: Send + Sync + 'static { fn execute_trusted_calls(&self) -> EnclaveResult<()>; } -impl Sidechain for Enclave { - fn sync_parentchain( - &self, - blocks: &[SignedBlock], - events: &[Vec], - events_proofs: &[StorageProof], - parentchain_id: &ParentchainId, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let blocks_enc = blocks.encode(); - let events_enc = events.encode(); - let events_proofs_enc = events_proofs.encode(); - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::sync_parentchain( - self.eid, - &mut retval, - blocks_enc.as_ptr(), - blocks_enc.len(), - events_enc.as_ptr(), - events_enc.len(), - events_proofs_enc.as_ptr(), - events_proofs_enc.len(), - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::Sidechain; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use itp_storage::StorageProof; + use itp_types::parentchain::ParentchainId; + use sgx_types::sgx_status_t; + use sp_runtime::generic::SignedBlock; + + impl Sidechain for Enclave { + fn sync_parentchain( + &self, + blocks: &[SignedBlock], + events: &[Vec], + events_proofs: &[StorageProof], + parentchain_id: &ParentchainId, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let blocks_enc = blocks.encode(); + let events_enc = events.encode(); + let events_proofs_enc = events_proofs.encode(); + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::sync_parentchain( + self.eid, + &mut retval, + blocks_enc.as_ptr(), + blocks_enc.len(), + events_enc.as_ptr(), + events_enc.len(), + events_proofs_enc.as_ptr(), + events_proofs_enc.len(), + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } - fn execute_trusted_calls(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; + fn execute_trusted_calls(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; - let result = unsafe { ffi::execute_trusted_calls(self.eid, &mut retval) }; + let result = unsafe { ffi::execute_trusted_calls(self.eid, &mut retval) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(()) + Ok(()) + } } } diff --git a/core-primitives/enclave-api/src/teeracle_api.rs b/core-primitives/enclave-api/src/teeracle_api.rs index 54165fd3ec..742a9325e3 100644 --- a/core-primitives/enclave-api/src/teeracle_api.rs +++ b/core-primitives/enclave-api/src/teeracle_api.rs @@ -15,12 +15,7 @@ */ -use crate::{error::Error, Enclave, EnclaveResult}; -use codec::Encode; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use log::*; -use sgx_types::*; +use crate::EnclaveResult; pub trait TeeracleApi: Send + Sync + 'static { /// Update the currency market data for the token oracle. @@ -34,68 +29,82 @@ pub trait TeeracleApi: Send + Sync + 'static { fn update_weather_data_xt(&self, longitude: &str, latitude: &str) -> EnclaveResult>; } -impl TeeracleApi for Enclave { - fn update_market_data_xt( - &self, - crypto_currency: &str, - fiat_currency: &str, - ) -> EnclaveResult> { - info!( - "TeeracleApi update_market_data_xt in with crypto {} and fiat {}", - crypto_currency, fiat_currency - ); - let mut retval = sgx_status_t::SGX_SUCCESS; - let response_len = 8192; - let mut response: Vec = vec![0u8; response_len as usize]; +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::TeeracleApi; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use log::*; + use sgx_types::*; + impl TeeracleApi for Enclave { + fn update_market_data_xt( + &self, + crypto_currency: &str, + fiat_currency: &str, + ) -> EnclaveResult> { + info!( + "TeeracleApi update_market_data_xt in with crypto {} and fiat {}", + crypto_currency, fiat_currency + ); + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_len = 8192; + let mut response: Vec = vec![0u8; response_len as usize]; - let crypto_curr = crypto_currency.encode(); - let fiat_curr = fiat_currency.encode(); + let crypto_curr = crypto_currency.encode(); + let fiat_curr = fiat_currency.encode(); - let res = unsafe { - ffi::update_market_data_xt( - self.eid, - &mut retval, - crypto_curr.as_ptr(), - crypto_curr.len() as u32, - fiat_curr.as_ptr(), - fiat_curr.len() as u32, - response.as_mut_ptr(), - response_len, - ) - }; + let res = unsafe { + ffi::update_market_data_xt( + self.eid, + &mut retval, + crypto_curr.as_ptr(), + crypto_curr.len() as u32, + fiat_curr.as_ptr(), + fiat_curr.len() as u32, + response.as_mut_ptr(), + response_len, + ) + }; - ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(response) - } - fn update_weather_data_xt(&self, longitude: &str, latitude: &str) -> EnclaveResult> { - info!( - "TeeracleApi update_weather_data_xt in with latitude: {}, longitude: {}", - latitude, longitude - ); - let mut retval = sgx_status_t::SGX_SUCCESS; - let response_len = 8192; - let mut response: Vec = vec![0u8; response_len as usize]; + Ok(response) + } + fn update_weather_data_xt( + &self, + longitude: &str, + latitude: &str, + ) -> EnclaveResult> { + info!( + "TeeracleApi update_weather_data_xt in with latitude: {}, longitude: {}", + latitude, longitude + ); + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_len = 8192; + let mut response: Vec = vec![0u8; response_len as usize]; - let longitude_encoded: Vec = longitude.encode(); - let latitude_encoded: Vec = latitude.encode(); + let longitude_encoded: Vec = longitude.encode(); + let latitude_encoded: Vec = latitude.encode(); - let res = unsafe { - ffi::update_weather_data_xt( - self.eid, - &mut retval, - longitude_encoded.as_ptr(), - longitude_encoded.len() as u32, - latitude_encoded.as_ptr(), - latitude_encoded.len() as u32, - response.as_mut_ptr(), - response_len, - ) - }; + let res = unsafe { + ffi::update_weather_data_xt( + self.eid, + &mut retval, + longitude_encoded.as_ptr(), + longitude_encoded.len() as u32, + latitude_encoded.as_ptr(), + latitude_encoded.len() as u32, + response.as_mut_ptr(), + response_len, + ) + }; - ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Ok(response) + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + Ok(response) + } } } diff --git a/service/Cargo.toml b/service/Cargo.toml index 4837a86491..482ff433fe 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -37,7 +37,6 @@ primitive-types = { version = "0.12.1", default-features = false, features = ["c sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_urts = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } # local itc-parentchain = { path = "../core/parentchain/parentchain-crate" } @@ -86,6 +85,17 @@ production = ["itp-settings/production"] teeracle = ["itp-settings/teeracle"] dcap = [] attesteer = ["dcap"] +# Must be enabled to build a binary and link it with the enclave successfully. +# This flag is set in the makefile. +# +# Must not be enabled to run cargo test without an sgx-sdk providing environment +# https://github.com/rust-lang/cargo/issues/2549. +# +# It has been chosen to not make this a default feature because this makes test execution +# more ergonomic as we can simply do `cargo test` on the whole workspace like this. +link-binary = [ + "itp-enclave-api/implement-ffi", +] [dev-dependencies] # crates.io diff --git a/service/build.rs b/service/build.rs index 8f18978c62..1fb664ecc0 100644 --- a/service/build.rs +++ b/service/build.rs @@ -26,36 +26,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::env; - fn main() { - let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/intel/sgxsdk".to_string()); - let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); - - // NOTE: if the crate is a workspace member rustc-paths are relative from the root directory - println!("cargo:rustc-link-search=native=./lib"); - println!("cargo:rustc-link-lib=static=Enclave_u"); - - println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); - println!("cargo:rustc-link-lib=static=sgx_uprotected_fs"); - // if the linker failed to find libsgx_dcap_ql.so, please make sure that - // (1) libsgx-dcap-ql is installed - // (2) libsgx_dcap_ql.so exists. typicall at /usr/lib/x86_64-linux-gnu - // if libsgx_dcap_ql.so.1 is there, but no libsgx-dcap_ql, - // just create a symlink by - // ln -s libsgx_dcap_ql.so.1 libsgx_dcap_ql.so - println!("cargo:rustc-link-lib=dylib=sgx_dcap_ql"); - println!("cargo:rustc-link-lib=dylib=sgx_dcap_quoteverify"); - println!("cargo:rustc-link-lib=dylib=dcap_quoteprov"); - match is_sim.as_ref() { - "SW" => { - println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim"); - }, - _ => { - // HW by default - println!("cargo:rustc-link-lib=dylib=sgx_urts"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service"); - }, - } + // All the linker options are now defined in `itp-enclave-api-ffi` } diff --git a/service/src/enclave/api.rs b/service/src/enclave/api.rs index 860fd3e0bb..e3901672d0 100644 --- a/service/src/enclave/api.rs +++ b/service/src/enclave/api.rs @@ -16,19 +16,18 @@ */ use crate::config::Config; -use itp_enclave_api::{ - enclave_base::EnclaveBase, error::Error as EnclaveApiError, Enclave, EnclaveResult, -}; +use itp_enclave_api::{enclave_base::EnclaveBase, error::Error as EnclaveApiError, EnclaveResult}; use itp_settings::files::{ENCLAVE_FILE, ENCLAVE_TOKEN}; use log::*; use sgx_types::*; -use sgx_urts::SgxEnclave; use std::{ fs::File, io::{Read, Write}, path::PathBuf, }; +use itp_enclave_api::{Enclave, SgxEnclave}; + pub fn enclave_init(config: &Config) -> EnclaveResult { const LEN: usize = 1024; let mut launch_token = [0; LEN]; diff --git a/service/src/enclave/mod.rs b/service/src/enclave/mod.rs index e9e8984207..bb9ba4fe84 100644 --- a/service/src/enclave/mod.rs +++ b/service/src/enclave/mod.rs @@ -15,5 +15,6 @@ */ +#[cfg(feature = "link-binary")] pub mod api; pub mod tls_ra; diff --git a/service/src/main.rs b/service/src/main.rs index df121b5ef6..2f9961ccb5 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -16,77 +16,7 @@ */ #![cfg_attr(test, feature(assert_matches))] - -#[cfg(feature = "teeracle")] -use crate::teeracle::{schedule_periodic_reregistration_thread, start_periodic_market_update}; - -#[cfg(not(feature = "dcap"))] -use crate::utils::check_files; - -use crate::{ - account_funding::{setup_account_funding, EnclaveAccountInfoProvider}, - error::Error, - globals::tokio_handle::{GetTokioHandle, GlobalTokioHandle}, - initialized_service::{ - start_is_initialized_server, InitializationHandler, IsInitialized, TrackInitialization, - }, - ocall_bridge::{ - bridge_api::Bridge as OCallBridge, component_factory::OCallBridgeComponentFactory, - }, - parentchain_handler::{HandleParentchain, ParentchainHandler}, - prometheus_metrics::{start_metrics_server, EnclaveMetricsReceiver, MetricsHandler}, - sidechain_setup::{sidechain_init_block_production, sidechain_start_untrusted_rpc_server}, - sync_block_broadcaster::SyncBlockBroadcaster, - utils::extract_shard, - worker::Worker, - worker_peers_updater::WorkerPeersUpdater, -}; -use base58::ToBase58; -use clap::{load_yaml, App}; -use codec::{Decode, Encode}; -use config::Config; -use enclave::{ - api::enclave_init, - tls_ra::{enclave_request_state_provisioning, enclave_run_state_provisioning_server}, -}; -use itp_enclave_api::{ - direct_request::DirectRequest, - enclave_base::EnclaveBase, - remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, - sidechain::Sidechain, - teeracle_api::TeeracleApi, - Enclave, -}; -use itp_node_api::{ - api_client::{AccountApi, PalletTeerexApi, ParentchainApi}, - metadata::NodeMetadata, - node_api_factory::{CreateNodeApi, NodeApiFactory}, -}; -use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; -use its_peer_fetch::{ - block_fetch_client::BlockFetcher, untrusted_peer_fetch::UntrustedPeerFetcher, -}; -use its_primitives::types::block::SignedBlock as SignedSidechainBlock; -use its_storage::{interface::FetchBlocks, BlockPruner, SidechainStorageLock}; -use log::*; -use my_node_runtime::{Hash, Header, RuntimeEvent}; -use sgx_types::*; -use sp_runtime::traits::Header as HeaderT; -use substrate_api_client::{ - api::XtStatus, rpc::HandleSubscription, GetChainInfo, SubmitAndWatch, SubscribeChain, - SubscribeEvents, -}; -use teerex_primitives::AnySigner; - -#[cfg(feature = "dcap")] -use sgx_verify::extract_tcb_info_from_raw_dcap_quote; - -use enclave_bridge_primitives::ShardIdentifier; -use itc_parentchain::primitives::ParentchainId; -use sp_core::crypto::{AccountId32, Ss58Codec}; -use sp_keyring::AccountKeyring; -use sp_runtime::MultiSigner; -use std::{str, sync::Arc, thread, time::Duration}; +#![allow(unused)] mod account_funding; mod config; @@ -108,1023 +38,15 @@ mod utils; mod worker; mod worker_peers_updater; -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub type EnclaveWorker = - Worker>; -pub type Event = substrate_api_client::ac_node_api::EventRecord; +#[cfg(feature = "link-binary")] +mod main_impl; +#[cfg(feature = "link-binary")] fn main() { - // Setup logging - env_logger::init(); - - let yml = load_yaml!("cli.yml"); - let matches = App::from_yaml(yml).get_matches(); - - let config = Config::from(&matches); - - GlobalTokioHandle::initialize(); - - // log this information, don't println because some python scripts for GA rely on the - // stdout from the service - #[cfg(feature = "production")] - info!("*** Starting service in SGX production mode"); - #[cfg(not(feature = "production"))] - info!("*** Starting service in SGX debug mode"); - - info!("*** Running worker in mode: {:?} \n", WorkerModeProvider::worker_mode()); - - let clean_reset = matches.is_present("clean-reset"); - if clean_reset { - setup::purge_files_from_dir(config.data_dir()).unwrap(); - } - - // build the entire dependency tree - let tokio_handle = Arc::new(GlobalTokioHandle {}); - let sidechain_blockstorage = Arc::new( - SidechainStorageLock::::from_base_path( - config.data_dir().to_path_buf(), - ) - .unwrap(), - ); - let node_api_factory = Arc::new(NodeApiFactory::new( - config.integritee_rpc_endpoint(), - AccountKeyring::Alice.pair(), - )); - let enclave = Arc::new(enclave_init(&config).unwrap()); - let initialization_handler = Arc::new(InitializationHandler::default()); - let worker = Arc::new(EnclaveWorker::new( - config.clone(), - enclave.clone(), - node_api_factory.clone(), - initialization_handler.clone(), - Vec::new(), - )); - let sync_block_broadcaster = - Arc::new(SyncBlockBroadcaster::new(tokio_handle.clone(), worker.clone())); - let peer_updater = Arc::new(WorkerPeersUpdater::new(worker)); - let untrusted_peer_fetcher = UntrustedPeerFetcher::new(node_api_factory.clone()); - let peer_sidechain_block_fetcher = - Arc::new(BlockFetcher::::new(untrusted_peer_fetcher)); - let enclave_metrics_receiver = Arc::new(EnclaveMetricsReceiver {}); - - let maybe_target_a_parentchain_api_factory = config - .target_a_parentchain_rpc_endpoint() - .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); - - let maybe_target_b_parentchain_api_factory = config - .target_b_parentchain_rpc_endpoint() - .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); - - // initialize o-call bridge with a concrete factory implementation - OCallBridge::initialize(Arc::new(OCallBridgeComponentFactory::new( - node_api_factory.clone(), - maybe_target_a_parentchain_api_factory, - maybe_target_b_parentchain_api_factory, - sync_block_broadcaster, - enclave.clone(), - sidechain_blockstorage.clone(), - peer_updater, - peer_sidechain_block_fetcher, - tokio_handle.clone(), - enclave_metrics_receiver, - ))); - - let quoting_enclave_target_info = match enclave.qe_get_target_info() { - Ok(target_info) => Some(target_info), - Err(e) => { - warn!("Setting up DCAP - qe_get_target_info failed with error: {:?}, continuing.", e); - None - }, - }; - let quote_size = match enclave.qe_get_quote_size() { - Ok(size) => Some(size), - Err(e) => { - warn!("Setting up DCAP - qe_get_quote_size failed with error: {:?}, continuing.", e); - None - }, - }; - - if let Some(run_config) = config.run_config() { - let shard = extract_shard(run_config.shard(), enclave.as_ref()); - - println!("Worker Config: {:?}", config); - - if clean_reset { - setup::initialize_shard_and_keys(enclave.as_ref(), &shard).unwrap(); - } - - let node_api = - node_api_factory.create_api().expect("Failed to create parentchain node API"); - - if run_config.request_state() { - sync_state::sync_state::<_, _, WorkerModeProvider>( - &node_api, - &shard, - enclave.as_ref(), - run_config.skip_ra(), - ); - } - - start_worker::<_, _, _, _, WorkerModeProvider>( - config, - &shard, - enclave, - sidechain_blockstorage, - node_api, - tokio_handle, - initialization_handler, - quoting_enclave_target_info, - quote_size, - ); - } else if let Some(smatches) = matches.subcommand_matches("request-state") { - println!("*** Requesting state from a registered worker \n"); - let node_api = - node_api_factory.create_api().expect("Failed to create parentchain node API"); - sync_state::sync_state::<_, _, WorkerModeProvider>( - &node_api, - &extract_shard(smatches.value_of("shard"), enclave.as_ref()), - enclave.as_ref(), - smatches.is_present("skip-ra"), - ); - } else if matches.is_present("shielding-key") { - setup::generate_shielding_key_file(enclave.as_ref()); - } else if matches.is_present("signing-key") { - setup::generate_signing_key_file(enclave.as_ref()); - } else if matches.is_present("dump-ra") { - info!("*** Perform RA and dump cert to disk"); - #[cfg(not(feature = "dcap"))] - enclave.dump_ias_ra_cert_to_disk().unwrap(); - #[cfg(feature = "dcap")] - { - let skip_ra = false; - let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); - let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); - enclave.dump_dcap_collateral_to_disk(fmspc).unwrap(); - enclave.dump_dcap_ra_cert_to_disk().unwrap(); - } - } else if matches.is_present("mrenclave") { - println!("{}", enclave.get_fingerprint().unwrap().encode().to_base58()); - } else if let Some(sub_matches) = matches.subcommand_matches("init-shard") { - setup::init_shard( - enclave.as_ref(), - &extract_shard(sub_matches.value_of("shard"), enclave.as_ref()), - ); - } else if let Some(sub_matches) = matches.subcommand_matches("test") { - if sub_matches.is_present("provisioning-server") { - println!("*** Running Enclave MU-RA TLS server\n"); - enclave_run_state_provisioning_server( - enclave.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - &config.mu_ra_url(), - sub_matches.is_present("skip-ra"), - ); - println!("[+] Done!"); - } else if sub_matches.is_present("provisioning-client") { - println!("*** Running Enclave MU-RA TLS client\n"); - let shard = extract_shard(sub_matches.value_of("shard"), enclave.as_ref()); - enclave_request_state_provisioning( - enclave.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - &config.mu_ra_url_external(), - &shard, - sub_matches.is_present("skip-ra"), - ) - .unwrap(); - println!("[+] Done!"); - } else { - tests::run_enclave_tests(sub_matches); - } - } else { - println!("For options: use --help"); - } -} - -/// FIXME: needs some discussion (restructuring?) -#[allow(clippy::too_many_arguments)] -fn start_worker( - config: Config, - shard: &ShardIdentifier, - enclave: Arc, - sidechain_storage: Arc, - integritee_rpc_api: ParentchainApi, - tokio_handle_getter: Arc, - initialization_handler: Arc, - quoting_enclave_target_info: Option, - quote_size: Option, -) where - T: GetTokioHandle, - E: EnclaveBase - + DirectRequest - + Sidechain - + RemoteAttestation - + TlsRemoteAttestation - + TeeracleApi - + Clone, - D: BlockPruner + FetchBlocks + Sync + Send + 'static, - InitializationHandler: TrackInitialization + IsInitialized + Sync + Send + 'static, - WorkerModeProvider: ProvideWorkerMode, -{ - let run_config = config.run_config().clone().expect("Run config missing"); - let skip_ra = run_config.skip_ra(); - - #[cfg(feature = "teeracle")] - let flavor_str = "teeracle"; - #[cfg(feature = "sidechain")] - let flavor_str = "sidechain"; - #[cfg(feature = "offchain-worker")] - let flavor_str = "offchain-worker"; - #[cfg(not(any(feature = "offchain-worker", feature = "sidechain", feature = "teeracle")))] - let flavor_str = "offchain-worker"; - - println!("Integritee Worker for {} v{}", flavor_str, VERSION); - - #[cfg(feature = "dcap")] - println!(" DCAP is enabled"); - #[cfg(not(feature = "dcap"))] - println!(" DCAP is disabled"); - #[cfg(feature = "production")] - println!(" Production Mode is enabled"); - #[cfg(not(feature = "production"))] - println!(" Production Mode is disabled"); - #[cfg(feature = "evm")] - println!(" EVM is enabled"); - #[cfg(not(feature = "evm"))] - println!(" EVM is disabled"); - - info!("starting worker on shard {}", shard.encode().to_base58()); - // ------------------------------------------------------------------------ - // check for required files - if !skip_ra { - #[cfg(not(feature = "dcap"))] - check_files(); - } - // ------------------------------------------------------------------------ - // initialize the enclave - let mrenclave = enclave.get_fingerprint().unwrap(); - println!("MRENCLAVE={}", mrenclave.0.to_base58()); - println!("MRENCLAVE in hex {:?}", hex::encode(mrenclave)); - - // ------------------------------------------------------------------------ - // let new workers call us for key provisioning - println!("MU-RA server listening on {}", config.mu_ra_url()); - let is_development_mode = run_config.dev(); - let ra_url = config.mu_ra_url(); - let enclave_api_key_prov = enclave.clone(); - thread::spawn(move || { - enclave_run_state_provisioning_server( - enclave_api_key_prov.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - &ra_url, - skip_ra, - ); - info!("State provisioning server stopped."); - }); - - let tokio_handle = tokio_handle_getter.get_handle(); - - #[cfg(feature = "teeracle")] - let teeracle_tokio_handle = tokio_handle.clone(); - - // ------------------------------------------------------------------------ - // Get the public key of our TEE. - let tee_accountid = enclave_account(enclave.as_ref()); - println!("Enclave account {:} ", &tee_accountid.to_ss58check()); - - // ------------------------------------------------------------------------ - // Start `is_initialized` server. - let untrusted_http_server_port = config - .try_parse_untrusted_http_server_port() - .expect("untrusted http server port to be a valid port number"); - let initialization_handler_clone = initialization_handler.clone(); - tokio_handle.spawn(async move { - if let Err(e) = - start_is_initialized_server(initialization_handler_clone, untrusted_http_server_port) - .await - { - error!("Unexpected error in `is_initialized` server: {:?}", e); - } - }); - - // ------------------------------------------------------------------------ - // Start prometheus metrics server. - if config.enable_metrics_server() { - let enclave_wallet = Arc::new(EnclaveAccountInfoProvider::new( - integritee_rpc_api.clone(), - tee_accountid.clone(), - )); - let metrics_handler = Arc::new(MetricsHandler::new(enclave_wallet)); - let metrics_server_port = config - .try_parse_metrics_server_port() - .expect("metrics server port to be a valid port number"); - tokio_handle.spawn(async move { - if let Err(e) = start_metrics_server(metrics_handler, metrics_server_port).await { - error!("Unexpected error in Prometheus metrics server: {:?}", e); - } - }); - } - - // ------------------------------------------------------------------------ - // Start trusted worker rpc server - if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain - || WorkerModeProvider::worker_mode() == WorkerMode::OffChainWorker - { - let direct_invocation_server_addr = config.trusted_worker_url_internal(); - let enclave_for_direct_invocation = enclave.clone(); - thread::spawn(move || { - println!( - "[+] Trusted RPC direct invocation server listening on {}", - direct_invocation_server_addr - ); - enclave_for_direct_invocation - .init_direct_invocation_server(direct_invocation_server_addr) - .unwrap(); - println!("[+] RPC direct invocation server shut down"); - }); - } - - // ------------------------------------------------------------------------ - // Start untrusted worker rpc server. - // i.e move sidechain block importing to trusted worker. - if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { - sidechain_start_untrusted_rpc_server( - &config, - enclave.clone(), - sidechain_storage.clone(), - tokio_handle, - ); - } - - // ------------------------------------------------------------------------ - // Init parentchain specific stuff. Needed for parentchain communication. - - let (parentchain_handler, last_synced_header) = - init_parentchain(&enclave, &integritee_rpc_api, &tee_accountid, ParentchainId::Integritee); - - #[cfg(feature = "dcap")] - register_collateral( - &integritee_rpc_api, - &*enclave, - &tee_accountid, - is_development_mode, - skip_ra, - ); - - let trusted_url = config.trusted_worker_url_external(); - - #[cfg(feature = "attesteer")] - fetch_marblerun_events_every_hour( - integritee_rpc_api.clone(), - enclave.clone(), - tee_accountid.clone(), - is_development_mode, - trusted_url.clone(), - run_config.marblerun_base_url().to_string(), - ); - - // ------------------------------------------------------------------------ - // Perform a remote attestation and get an unchecked extrinsic back. - - if skip_ra { - println!( - "[!] skipping remote attestation. Registering enclave without attestation report." - ); - } else { - println!("[!] creating remote attestation report and create enclave register extrinsic."); - }; - - #[cfg(feature = "dcap")] - enclave.set_sgx_qpl_logging().expect("QPL logging setup failed"); - - let enclave2 = enclave.clone(); - #[cfg(not(feature = "dcap"))] - let register_xt = move || enclave2.generate_ias_ra_extrinsic(&trusted_url, skip_ra).unwrap(); - #[cfg(feature = "dcap")] - let register_xt = move || enclave2.generate_dcap_ra_extrinsic(&trusted_url, skip_ra).unwrap(); - - // clones because of the move - let node_api2 = integritee_rpc_api.clone(); - let tee_accountid_clone = tee_accountid.clone(); - let send_register_xt = move || { - println!("[+] Send register enclave extrinsic"); - send_extrinsic(register_xt(), &node_api2, &tee_accountid_clone, is_development_mode) - }; - - // Todo: Can't unwrap here because the extrinsic is for some reason not found in the block - // even if it was successful: https://github.com/scs/substrate-api-client/issues/624. - let register_enclave_block_hash = send_register_xt(); - let api_register_enclave_xt_header = - integritee_rpc_api.get_header(register_enclave_block_hash).unwrap().unwrap(); - - // TODO: #1451: Fix api-client type hacks - let register_enclave_xt_header = - Header::decode(&mut api_register_enclave_xt_header.encode().as_slice()) - .expect("Can decode previously encoded header; qed"); - - println!( - "[+] Enclave registered at block number: {:?}, hash: {:?}", - register_enclave_xt_header.number(), - register_enclave_xt_header.hash() - ); - - let we_are_primary_validateer = - we_are_primary_worker(&integritee_rpc_api, shard, &tee_accountid).unwrap(); - - if we_are_primary_validateer { - println!("[+] We are the primary worker"); - } else { - println!("[+] We are NOT the primary worker"); - } - - initialization_handler.registered_on_parentchain(); - - // ------------------------------------------------------------------------ - // initialize teeracle interval - #[cfg(feature = "teeracle")] - if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { - schedule_periodic_reregistration_thread( - send_register_xt, - run_config.reregister_teeracle_interval(), - ); - - start_periodic_market_update( - &integritee_rpc_api, - run_config.teeracle_update_interval(), - enclave.as_ref(), - &teeracle_tokio_handle, - ); - } - - if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { - println!("*** [+] Finished initializing light client, syncing parentchain..."); - - // Syncing all parentchain blocks, this might take a while.. - let mut last_synced_header = - parentchain_handler.sync_parentchain(last_synced_header).unwrap(); - - // ------------------------------------------------------------------------ - // Initialize the sidechain - if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { - last_synced_header = sidechain_init_block_production( - enclave.clone(), - ®ister_enclave_xt_header, - we_are_primary_validateer, - parentchain_handler.clone(), - sidechain_storage, - &last_synced_header, - ) - .unwrap(); - } - - // ------------------------------------------------------------------------ - // start parentchain syncing loop (subscribe to header updates) - thread::Builder::new() - .name("parentchain_sync_loop".to_owned()) - .spawn(move || { - if let Err(e) = - subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header) - { - error!("Parentchain block syncing terminated with a failure: {:?}", e); - } - println!("[!] Parentchain block syncing has terminated"); - }) - .unwrap(); - - if WorkerModeProvider::worker_mode() == WorkerMode::OffChainWorker { - info!("skipping shard vault check because not yet supported for offchain worker"); - } else if let Ok(shard_vault) = enclave.get_ecc_vault_pubkey(shard) { - println!( - "shard vault account is already initialized in state: {}", - shard_vault.to_ss58check() - ); - } else if we_are_primary_validateer { - println!("initializing proxied shard vault account now"); - enclave.init_proxied_shard_vault(shard).unwrap(); - println!( - "initialized shard vault account: : {}", - enclave.get_ecc_vault_pubkey(shard).unwrap().to_ss58check() - ); - } else { - panic!("no vault account has been initialized and we are not the primary worker"); - } - } - - // ------------------------------------------------------------------------ - if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { - spawn_worker_for_shard_polling(shard, integritee_rpc_api.clone(), initialization_handler); - } - - if let Some(url) = config.target_a_parentchain_rpc_endpoint() { - init_target_parentchain( - &enclave, - &tee_accountid, - url, - ParentchainId::TargetA, - is_development_mode, - ) - } - - if let Some(url) = config.target_b_parentchain_rpc_endpoint() { - init_target_parentchain( - &enclave, - &tee_accountid, - url, - ParentchainId::TargetB, - is_development_mode, - ) - } - - // ------------------------------------------------------------------------ - // Subscribe to events and print them. - println!("*** [{:?}] Subscribing to events", ParentchainId::Integritee); - let mut subscription = integritee_rpc_api.subscribe_events().unwrap(); - println!("[+] [{:?}] Subscribed to events. waiting...", ParentchainId::Integritee); - loop { - if let Some(Ok(events)) = subscription.next_events::() { - print_events(events) - } - } -} - -fn init_target_parentchain( - enclave: &Arc, - tee_account_id: &AccountId32, - url: String, - parentchain_id: ParentchainId, - is_development_mode: bool, -) where - E: EnclaveBase + Sidechain, -{ - println!("Initializing parentchain {:?} with url: {}", parentchain_id, url); - let node_api = NodeApiFactory::new(url, AccountKeyring::Alice.pair()) - .create_api() - .unwrap_or_else(|_| panic!("[{:?}] Failed to create parentchain node API", parentchain_id)); - - // some random bytes not too small to ensure that the enclave has enough funds - setup_account_funding(&node_api, tee_account_id, [0u8; 100].into(), is_development_mode) - .unwrap_or_else(|_| { - panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) - }); - - let (parentchain_handler, last_synched_header) = - init_parentchain(enclave, &node_api, tee_account_id, parentchain_id); - - if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { - println!( - "*** [+] [{:?}] Finished initializing light client, syncing parentchain...", - parentchain_id - ); - - // Syncing all parentchain blocks, this might take a while.. - let last_synched_header = - parentchain_handler.sync_parentchain(last_synched_header).unwrap(); - - // start parentchain syncing loop (subscribe to header updates) - thread::Builder::new() - .name(format!("{:?}_parentchain_sync_loop", parentchain_id)) - .spawn(move || { - if let Err(e) = - subscribe_to_parentchain_new_headers(parentchain_handler, last_synched_header) - { - error!( - "[{:?}] parentchain block syncing terminated with a failure: {:?}", - parentchain_id, e - ); - } - println!("[!] [{:?}] parentchain block syncing has terminated", parentchain_id); - }) - .unwrap(); - } - - // Subscribe to events and print them. - println!("*** [{:?}] Subscribing to events...", parentchain_id); - let mut subscription = node_api.subscribe_events().unwrap(); - println!("[+] [{:?}] Subscribed to events. waiting...", parentchain_id); - - thread::Builder::new() - .name(format!("{:?}_parentchain_event_subscription", parentchain_id)) - .spawn(move || loop { - if let Some(Ok(events)) = subscription.next_events::() { - print_events(events) - } - }) - .unwrap(); -} - -fn init_parentchain( - enclave: &Arc, - node_api: &ParentchainApi, - tee_account_id: &AccountId32, - parentchain_id: ParentchainId, -) -> (Arc>, Header) -where - E: EnclaveBase + Sidechain, -{ - let parentchain_handler = Arc::new( - ParentchainHandler::new_with_automatic_light_client_allocation( - node_api.clone(), - enclave.clone(), - parentchain_id, - ) - .unwrap(), - ); - let last_synced_header = parentchain_handler.init_parentchain_components().unwrap(); - println!("[{:?}] last synced parentchain block: {}", parentchain_id, last_synced_header.number); - - let nonce = node_api.get_nonce_of(tee_account_id).unwrap(); - info!("[{:?}] Enclave nonce = {:?}", parentchain_id, nonce); - enclave.set_nonce(nonce, parentchain_id).unwrap_or_else(|_| { - panic!("[{:?}] Could not set nonce of enclave. Returning here...", parentchain_id) - }); - - let metadata = node_api.metadata().clone(); - let runtime_spec_version = node_api.runtime_version().spec_version; - let runtime_transaction_version = node_api.runtime_version().transaction_version; - enclave - .set_node_metadata( - NodeMetadata::new(metadata, runtime_spec_version, runtime_transaction_version).encode(), - parentchain_id, - ) - .unwrap_or_else(|_| { - panic!("[{:?}] Could not set the node metadata in the enclave", parentchain_id) - }); - - (parentchain_handler, last_synced_header) -} - -/// Start polling loop to wait until we have a worker for a shard registered on -/// the parentchain (TEEREX WorkerForShard). This is the pre-requisite to be -/// considered initialized and ready for the next worker to start (in sidechain mode only). -/// considered initialized and ready for the next worker to start. -fn spawn_worker_for_shard_polling( - shard: &ShardIdentifier, - node_api: ParentchainApi, - initialization_handler: Arc, -) where - InitializationHandler: TrackInitialization + Sync + Send + 'static, -{ - let shard_for_initialized = *shard; - thread::spawn(move || { - const POLL_INTERVAL_SECS: u64 = 2; - - loop { - info!("Polling for worker for shard ({} seconds interval)", POLL_INTERVAL_SECS); - if let Ok(Some(enclave)) = - node_api.primary_worker_for_shard(&shard_for_initialized, None) - { - // Set that the service is initialized. - initialization_handler.worker_for_shard_registered(); - println!( - "[+] Found `WorkerForShard` on parentchain state: {:?}", - enclave.instance_signer() - ); - break - } - thread::sleep(Duration::from_secs(POLL_INTERVAL_SECS)); - } - }); -} - -fn print_events(events: Vec) { - for evr in &events { - debug!("Decoded: phase = {:?}, event = {:?}", evr.phase, evr.event); - match &evr.event { - RuntimeEvent::Balances(be) => { - info!("[+] Received balances event"); - debug!("{:?}", be); - match &be { - pallet_balances::Event::Transfer { - from: transactor, - to: dest, - amount: value, - } => { - debug!(" Transactor: {:?}", transactor.to_ss58check()); - debug!(" Destination: {:?}", dest.to_ss58check()); - debug!(" Value: {:?}", value); - }, - _ => { - trace!("Ignoring unsupported balances event"); - }, - } - }, - RuntimeEvent::Teerex(re) => { - debug!("{:?}", re); - match &re { - my_node_runtime::pallet_teerex::Event::AddedSgxEnclave { - registered_by, - worker_url, - .. - } => { - println!("[+] Received AddedEnclave event"); - println!(" Sender (Worker): {:?}", registered_by); - println!( - " Registered URL: {:?}", - str::from_utf8(&worker_url.clone().unwrap_or("none".into())).unwrap() - ); - }, - _ => { - trace!("Ignoring unsupported pallet_teerex event"); - }, - } - }, - RuntimeEvent::EnclaveBridge(re) => { - debug!("{:?}", re); - match &re { - my_node_runtime::pallet_enclave_bridge::Event::IndirectInvocationRegistered( - shard, - ) => { - println!( - "[+] Received trusted call for shard {}", - shard.encode().to_base58() - ); - }, - my_node_runtime::pallet_enclave_bridge::Event::ProcessedParentchainBlock { - shard, - block_hash, - trusted_calls_merkle_root, - block_number, - } => { - info!("[+] Received ProcessedParentchainBlock event"); - debug!(" for shard: {:?}", shard); - debug!(" Block Hash: {:?}", hex::encode(block_hash)); - debug!(" Merkle Root: {:?}", hex::encode(trusted_calls_merkle_root)); - debug!(" Block Number: {:?}", block_number); - }, - my_node_runtime::pallet_enclave_bridge::Event::ShieldFunds { - shard, - encrypted_beneficiary, - amount, - } => { - info!("[+] Received ShieldFunds event"); - debug!(" for shard: {:?}", shard); - debug!(" for enc. beneficiary: {:?}", encrypted_beneficiary); - debug!(" Amount: {:?}", amount); - }, - my_node_runtime::pallet_enclave_bridge::Event::UnshieldedFunds { - shard, - beneficiary, - amount, - } => { - info!("[+] Received UnshieldedFunds event"); - debug!(" for shard: {:?}", shard); - debug!(" beneficiary: {:?}", beneficiary); - debug!(" Amount: {:?}", amount); - }, - _ => { - trace!("Ignoring unsupported pallet_enclave_bridge event"); - }, - } - }, - #[cfg(feature = "teeracle")] - RuntimeEvent::Teeracle(re) => { - debug!("{:?}", re); - match &re { - my_node_runtime::pallet_teeracle::Event::ExchangeRateUpdated { - data_source, - trading_pair, - exchange_rate, - } => { - println!("[+] Received ExchangeRateUpdated event"); - println!(" Data source: {}", data_source); - println!(" trading pair: {}", trading_pair); - println!(" Exchange rate: {:?}", exchange_rate); - }, - my_node_runtime::pallet_teeracle::Event::ExchangeRateDeleted { - data_source, - trading_pair, - } => { - println!("[+] Received ExchangeRateDeleted event"); - println!(" Data source: {}", data_source); - println!(" trading pair: {}", trading_pair); - }, - my_node_runtime::pallet_teeracle::Event::AddedToWhitelist { - data_source, - enclave_fingerprint, - } => { - println!("[+] Received AddedToWhitelist event"); - println!(" Data source: {}", data_source); - println!(" fingerprint: {:?}", enclave_fingerprint); - }, - my_node_runtime::pallet_teeracle::Event::RemovedFromWhitelist { - data_source, - enclave_fingerprint, - } => { - println!("[+] Received RemovedFromWhitelist event"); - println!(" Data source: {}", data_source); - println!(" fingerprint: {:?}", enclave_fingerprint); - }, - _ => { - trace!("Ignoring unsupported pallet_teeracle event"); - }, - } - }, - #[cfg(feature = "sidechain")] - RuntimeEvent::Sidechain(re) => match &re { - my_node_runtime::pallet_sidechain::Event::FinalizedSidechainBlock { - shard, - block_header_hash, - validateer, - } => { - info!("[+] Received FinalizedSidechainBlock event"); - debug!(" for shard: {:?}", shard); - debug!(" From: {:?}", hex::encode(block_header_hash)); - debug!(" validateer: {:?}", validateer); - }, - _ => { - trace!("Ignoring unsupported pallet_sidechain event"); - }, - }, - _ => { - trace!("Ignoring event {:?}", evr); - }, - } - } -} - -#[cfg(feature = "attesteer")] -fn fetch_marblerun_events_every_hour( - api: ParentchainApi, - enclave: Arc, - accountid: AccountId32, - is_development_mode: bool, - url: String, - marblerun_base_url: String, -) where - E: RemoteAttestation + Clone + Sync + Send + 'static, -{ - let enclave = enclave.clone(); - let handle = thread::spawn(move || { - const POLL_INTERVAL_5_MINUTES_IN_SECS: u64 = 5 * 60; - loop { - info!("Polling marblerun events for quotes to register"); - register_quotes_from_marblerun( - &api, - enclave.clone(), - &accountid, - is_development_mode, - url.clone(), - &marblerun_base_url, - ); - - thread::sleep(Duration::from_secs(POLL_INTERVAL_5_MINUTES_IN_SECS)); - } - }); - - handle.join().unwrap() -} -#[cfg(feature = "attesteer")] -fn register_quotes_from_marblerun( - api: &ParentchainApi, - enclave: Arc, - accountid: &AccountId32, - is_development_mode: bool, - url: String, - marblerun_base_url: &str, -) { - let enclave = enclave.as_ref(); - let events = prometheus_metrics::fetch_marblerun_events(marblerun_base_url) - .map_err(|e| { - info!("Fetching events from Marblerun failed with: {:?}, continuing with 0 events.", e); - }) - .unwrap_or_default(); - let quotes: Vec<&[u8]> = - events.iter().map(|event| event.get_quote_without_prepended_bytes()).collect(); - - for quote in quotes { - match enclave.generate_dcap_ra_extrinsic_from_quote(url.clone(), "e) { - Ok(xt) => { - send_extrinsic(xt, api, accountid, is_development_mode); - }, - Err(e) => { - error!("Extracting information from quote failed: {}", e) - }, - } - } -} -#[cfg(feature = "dcap")] -fn register_collateral( - api: &ParentchainApi, - enclave: &dyn RemoteAttestation, - accountid: &AccountId32, - is_development_mode: bool, - skip_ra: bool, -) { - //TODO generate_dcap_ra_quote() does not really need skip_ra, rethink how many layers skip_ra should be passed along - if !skip_ra { - let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); - let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); - println!("[>] DCAP setup: register QE collateral"); - let uxt = enclave.generate_register_quoting_enclave_extrinsic(fmspc).unwrap(); - send_extrinsic(uxt, api, accountid, is_development_mode); - - println!("[>] DCAP setup: register TCB info"); - let uxt = enclave.generate_register_tcb_info_extrinsic(fmspc).unwrap(); - send_extrinsic(uxt, api, accountid, is_development_mode); - } + main_impl::main(); } -fn send_extrinsic( - extrinsic: Vec, - api: &ParentchainApi, - fee_payer: &AccountId32, - is_development_mode: bool, -) -> Option { - // ensure account funds - if let Err(x) = setup_account_funding(api, fee_payer, extrinsic.clone(), is_development_mode) { - error!("Ensure enclave funding failed: {:?}", x); - // Return without registering the enclave. This will fail and the transaction will be banned for 30min. - return None - } - - info!("[>] send extrinsic"); - trace!(" encoded extrinsic: 0x{:}", hex::encode(extrinsic.clone())); - - // fixme: wait ...until_success doesn't work due to https://github.com/scs/substrate-api-client/issues/624 - // fixme: currently, we don't verify if the extrinsic was a success here - match api.submit_and_watch_opaque_extrinsic_until(&extrinsic.into(), XtStatus::Finalized) { - Ok(xt_report) => { - info!( - "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", - xt_report.extrinsic_hash, xt_report.status - ); - xt_report.block_hash - }, - Err(e) => { - error!("ExtrinsicFailed {:?}", e); - None - }, - } -} - -/// Subscribe to the node API finalized heads stream and trigger a parent chain sync -/// upon receiving a new header. -fn subscribe_to_parentchain_new_headers( - parentchain_handler: Arc>, - mut last_synced_header: Header, -) -> Result<(), Error> { - // TODO: this should be implemented by parentchain_handler directly, and not via - // exposed parentchain_api - let mut subscription = parentchain_handler - .parentchain_api() - .subscribe_finalized_heads() - .map_err(Error::ApiClient)?; - - loop { - let new_header = subscription - .next() - .ok_or(Error::ApiSubscriptionDisconnected)? - .map_err(|e| Error::ApiClient(e.into()))?; - - println!( - "[+] Received finalized header update ({}), syncing parent chain...", - new_header.number - ); - - last_synced_header = parentchain_handler.sync_parentchain(last_synced_header)?; - } -} - -/// Get the public signing key of the TEE. -fn enclave_account(enclave_api: &E) -> AccountId32 { - let tee_public = enclave_api.get_ecc_signing_pubkey().unwrap(); - trace!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); - AccountId32::from(*tee_public.as_array_ref()) -} - -/// Checks if we are the first validateer to register on the parentchain. -fn we_are_primary_worker( - node_api: &ParentchainApi, - shard: &ShardIdentifier, - enclave_account: &AccountId32, -) -> Result { - // are we registered? else fail. - node_api - .enclave(enclave_account, None)? - .expect("our enclave should be registered at this point"); - trace!("our enclave is registered"); - match node_api.primary_worker_for_shard(shard, None).unwrap() { - Some(enclave) => - match enclave.instance_signer() { - AnySigner::Known(MultiSigner::Ed25519(primary)) => - if primary.encode() == enclave_account.encode() { - debug!("We are primary worker on this shard adn we have been previously running."); - Ok(true) - } else { - debug!("The primary worker is {}", primary.to_ss58check()); - Ok(false) - }, - _ => { - warn!("the primary worker is of unknown type"); - Ok(false) - }, - }, - None => { - debug!("We are the primary worker on this shard and the shard is untouched"); - Ok(true) - }, - } +#[cfg(not(feature = "link-binary"))] +fn main() { + panic!("tried to run the binary without linking. Make sure to pass `--feature link-binary`") } diff --git a/service/src/main_impl.rs b/service/src/main_impl.rs new file mode 100644 index 0000000000..5c1c0beb2c --- /dev/null +++ b/service/src/main_impl.rs @@ -0,0 +1,1096 @@ +#[cfg(feature = "teeracle")] +use crate::teeracle::{schedule_periodic_reregistration_thread, start_periodic_market_update}; + +#[cfg(not(feature = "dcap"))] +use crate::utils::check_files; + +use crate::{ + account_funding::{setup_account_funding, EnclaveAccountInfoProvider}, + config::Config, + enclave::{ + api::enclave_init, + tls_ra::{enclave_request_state_provisioning, enclave_run_state_provisioning_server}, + }, + error::Error, + globals::tokio_handle::{GetTokioHandle, GlobalTokioHandle}, + initialized_service::{ + start_is_initialized_server, InitializationHandler, IsInitialized, TrackInitialization, + }, + ocall_bridge::{ + bridge_api::Bridge as OCallBridge, component_factory::OCallBridgeComponentFactory, + }, + parentchain_handler::{HandleParentchain, ParentchainHandler}, + prometheus_metrics::{start_metrics_server, EnclaveMetricsReceiver, MetricsHandler}, + setup, + sidechain_setup::{sidechain_init_block_production, sidechain_start_untrusted_rpc_server}, + sync_block_broadcaster::SyncBlockBroadcaster, + sync_state, tests, + utils::extract_shard, + worker::Worker, + worker_peers_updater::WorkerPeersUpdater, +}; +use base58::ToBase58; +use clap::{load_yaml, App, ArgMatches}; +use codec::{Decode, Encode}; +use itp_enclave_api::{ + direct_request::DirectRequest, + enclave_base::EnclaveBase, + remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, + sidechain::Sidechain, + teeracle_api::TeeracleApi, +}; +use itp_node_api::{ + api_client::{AccountApi, PalletTeerexApi, ParentchainApi}, + metadata::NodeMetadata, + node_api_factory::{CreateNodeApi, NodeApiFactory}, +}; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; +use its_peer_fetch::{ + block_fetch_client::BlockFetcher, untrusted_peer_fetch::UntrustedPeerFetcher, +}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use its_storage::{interface::FetchBlocks, BlockPruner, SidechainStorageLock}; +use log::*; +use my_node_runtime::{Hash, Header, RuntimeEvent}; +use sgx_types::*; +use sp_runtime::traits::Header as HeaderT; +use substrate_api_client::{ + api::XtStatus, rpc::HandleSubscription, GetChainInfo, SubmitAndWatch, SubscribeChain, + SubscribeEvents, +}; + +use teerex_primitives::AnySigner; + +#[cfg(feature = "dcap")] +use sgx_verify::extract_tcb_info_from_raw_dcap_quote; + +use itp_enclave_api::Enclave; + +use enclave_bridge_primitives::ShardIdentifier; +use itc_parentchain::primitives::ParentchainId; +use sp_core::crypto::{AccountId32, Ss58Codec}; +use sp_keyring::AccountKeyring; +use sp_runtime::MultiSigner; +use std::{str, sync::Arc, thread, time::Duration}; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg(feature = "link-binary")] +pub type EnclaveWorker = + Worker>; +pub type Event = substrate_api_client::ac_node_api::EventRecord; + +pub(crate) fn main() { + // Setup logging + env_logger::init(); + + let yml = load_yaml!("cli.yml"); + let matches = App::from_yaml(yml).get_matches(); + + let config = Config::from(&matches); + + GlobalTokioHandle::initialize(); + + // log this information, don't println because some python scripts for GA rely on the + // stdout from the service + #[cfg(feature = "production")] + info!("*** Starting service in SGX production mode"); + #[cfg(not(feature = "production"))] + info!("*** Starting service in SGX debug mode"); + + info!("*** Running worker in mode: {:?} \n", WorkerModeProvider::worker_mode()); + + let clean_reset = matches.is_present("clean-reset"); + if clean_reset { + crate::setup::purge_files_from_dir(config.data_dir()).unwrap(); + } + + // build the entire dependency tree + let tokio_handle = Arc::new(GlobalTokioHandle {}); + let sidechain_blockstorage = Arc::new( + SidechainStorageLock::::from_base_path( + config.data_dir().to_path_buf(), + ) + .unwrap(), + ); + let node_api_factory = Arc::new(NodeApiFactory::new( + config.integritee_rpc_endpoint(), + AccountKeyring::Alice.pair(), + )); + let enclave = Arc::new(enclave_init(&config).unwrap()); + let initialization_handler = Arc::new(InitializationHandler::default()); + let worker = Arc::new(EnclaveWorker::new( + config.clone(), + enclave.clone(), + node_api_factory.clone(), + initialization_handler.clone(), + Vec::new(), + )); + let sync_block_broadcaster = + Arc::new(SyncBlockBroadcaster::new(tokio_handle.clone(), worker.clone())); + let peer_updater = Arc::new(WorkerPeersUpdater::new(worker)); + let untrusted_peer_fetcher = UntrustedPeerFetcher::new(node_api_factory.clone()); + let peer_sidechain_block_fetcher = + Arc::new(BlockFetcher::::new(untrusted_peer_fetcher)); + let enclave_metrics_receiver = Arc::new(EnclaveMetricsReceiver {}); + + let maybe_target_a_parentchain_api_factory = config + .target_a_parentchain_rpc_endpoint() + .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); + + let maybe_target_b_parentchain_api_factory = config + .target_b_parentchain_rpc_endpoint() + .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); + + // initialize o-call bridge with a concrete factory implementation + OCallBridge::initialize(Arc::new(OCallBridgeComponentFactory::new( + node_api_factory.clone(), + maybe_target_a_parentchain_api_factory, + maybe_target_b_parentchain_api_factory, + sync_block_broadcaster, + enclave.clone(), + sidechain_blockstorage.clone(), + peer_updater, + peer_sidechain_block_fetcher, + tokio_handle.clone(), + enclave_metrics_receiver, + ))); + + let quoting_enclave_target_info = match enclave.qe_get_target_info() { + Ok(target_info) => Some(target_info), + Err(e) => { + warn!("Setting up DCAP - qe_get_target_info failed with error: {:?}, continuing.", e); + None + }, + }; + let quote_size = match enclave.qe_get_quote_size() { + Ok(size) => Some(size), + Err(e) => { + warn!("Setting up DCAP - qe_get_quote_size failed with error: {:?}, continuing.", e); + None + }, + }; + + if let Some(run_config) = config.run_config() { + let shard = extract_shard(run_config.shard(), enclave.as_ref()); + + println!("Worker Config: {:?}", config); + + if clean_reset { + setup::initialize_shard_and_keys(enclave.as_ref(), &shard).unwrap(); + } + + let node_api = + node_api_factory.create_api().expect("Failed to create parentchain node API"); + + if run_config.request_state() { + sync_state::sync_state::<_, _, WorkerModeProvider>( + &node_api, + &shard, + enclave.as_ref(), + run_config.skip_ra(), + ); + } + + start_worker::<_, _, _, _, WorkerModeProvider>( + config, + &shard, + enclave, + sidechain_blockstorage, + node_api, + tokio_handle, + initialization_handler, + quoting_enclave_target_info, + quote_size, + ); + } else if let Some(smatches) = matches.subcommand_matches("request-state") { + println!("*** Requesting state from a registered worker \n"); + let node_api = + node_api_factory.create_api().expect("Failed to create parentchain node API"); + sync_state::sync_state::<_, _, WorkerModeProvider>( + &node_api, + &extract_shard(smatches.value_of("shard"), enclave.as_ref()), + enclave.as_ref(), + smatches.is_present("skip-ra"), + ); + } else if matches.is_present("shielding-key") { + setup::generate_shielding_key_file(enclave.as_ref()); + } else if matches.is_present("signing-key") { + setup::generate_signing_key_file(enclave.as_ref()); + } else if matches.is_present("dump-ra") { + info!("*** Perform RA and dump cert to disk"); + #[cfg(not(feature = "dcap"))] + enclave.dump_ias_ra_cert_to_disk().unwrap(); + #[cfg(feature = "dcap")] + { + let skip_ra = false; + let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); + let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); + enclave.dump_dcap_collateral_to_disk(fmspc).unwrap(); + enclave.dump_dcap_ra_cert_to_disk().unwrap(); + } + } else if matches.is_present("mrenclave") { + println!("{}", enclave.get_fingerprint().unwrap().encode().to_base58()); + } else if let Some(sub_matches) = matches.subcommand_matches("init-shard") { + setup::init_shard( + enclave.as_ref(), + &extract_shard(sub_matches.value_of("shard"), enclave.as_ref()), + ); + } else if let Some(sub_matches) = matches.subcommand_matches("test") { + if sub_matches.is_present("provisioning-server") { + println!("*** Running Enclave MU-RA TLS server\n"); + enclave_run_state_provisioning_server( + enclave.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + &config.mu_ra_url(), + sub_matches.is_present("skip-ra"), + ); + println!("[+] Done!"); + } else if sub_matches.is_present("provisioning-client") { + println!("*** Running Enclave MU-RA TLS client\n"); + let shard = extract_shard(sub_matches.value_of("shard"), enclave.as_ref()); + enclave_request_state_provisioning( + enclave.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + &config.mu_ra_url_external(), + &shard, + sub_matches.is_present("skip-ra"), + ) + .unwrap(); + println!("[+] Done!"); + } else { + tests::run_enclave_tests(sub_matches); + } + } else { + println!("For options: use --help"); + } +} + +/// FIXME: needs some discussion (restructuring?) +#[allow(clippy::too_many_arguments)] +fn start_worker( + config: Config, + shard: &ShardIdentifier, + enclave: Arc, + sidechain_storage: Arc, + integritee_rpc_api: ParentchainApi, + tokio_handle_getter: Arc, + initialization_handler: Arc, + quoting_enclave_target_info: Option, + quote_size: Option, +) where + T: GetTokioHandle, + E: EnclaveBase + + DirectRequest + + Sidechain + + RemoteAttestation + + TlsRemoteAttestation + + TeeracleApi + + Clone, + D: BlockPruner + FetchBlocks + Sync + Send + 'static, + InitializationHandler: TrackInitialization + IsInitialized + Sync + Send + 'static, + WorkerModeProvider: ProvideWorkerMode, +{ + let run_config = config.run_config().clone().expect("Run config missing"); + let skip_ra = run_config.skip_ra(); + + #[cfg(feature = "teeracle")] + let flavor_str = "teeracle"; + #[cfg(feature = "sidechain")] + let flavor_str = "sidechain"; + #[cfg(feature = "offchain-worker")] + let flavor_str = "offchain-worker"; + #[cfg(not(any(feature = "offchain-worker", feature = "sidechain", feature = "teeracle")))] + let flavor_str = "offchain-worker"; + + println!("Integritee Worker for {} v{}", flavor_str, VERSION); + + #[cfg(feature = "dcap")] + println!(" DCAP is enabled"); + #[cfg(not(feature = "dcap"))] + println!(" DCAP is disabled"); + #[cfg(feature = "production")] + println!(" Production Mode is enabled"); + #[cfg(not(feature = "production"))] + println!(" Production Mode is disabled"); + #[cfg(feature = "evm")] + println!(" EVM is enabled"); + #[cfg(not(feature = "evm"))] + println!(" EVM is disabled"); + + info!("starting worker on shard {}", shard.encode().to_base58()); + // ------------------------------------------------------------------------ + // check for required files + if !skip_ra { + #[cfg(not(feature = "dcap"))] + check_files(); + } + // ------------------------------------------------------------------------ + // initialize the enclave + let mrenclave = enclave.get_fingerprint().unwrap(); + println!("MRENCLAVE={}", mrenclave.0.to_base58()); + println!("MRENCLAVE in hex {:?}", hex::encode(mrenclave)); + + // ------------------------------------------------------------------------ + // let new workers call us for key provisioning + println!("MU-RA server listening on {}", config.mu_ra_url()); + let is_development_mode = run_config.dev(); + let ra_url = config.mu_ra_url(); + let enclave_api_key_prov = enclave.clone(); + thread::spawn(move || { + enclave_run_state_provisioning_server( + enclave_api_key_prov.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + &ra_url, + skip_ra, + ); + info!("State provisioning server stopped."); + }); + + let tokio_handle = tokio_handle_getter.get_handle(); + + #[cfg(feature = "teeracle")] + let teeracle_tokio_handle = tokio_handle.clone(); + + // ------------------------------------------------------------------------ + // Get the public key of our TEE. + let tee_accountid = enclave_account(enclave.as_ref()); + println!("Enclave account {:} ", &tee_accountid.to_ss58check()); + + // ------------------------------------------------------------------------ + // Start `is_initialized` server. + let untrusted_http_server_port = config + .try_parse_untrusted_http_server_port() + .expect("untrusted http server port to be a valid port number"); + let initialization_handler_clone = initialization_handler.clone(); + tokio_handle.spawn(async move { + if let Err(e) = + start_is_initialized_server(initialization_handler_clone, untrusted_http_server_port) + .await + { + error!("Unexpected error in `is_initialized` server: {:?}", e); + } + }); + + // ------------------------------------------------------------------------ + // Start prometheus metrics server. + if config.enable_metrics_server() { + let enclave_wallet = Arc::new(EnclaveAccountInfoProvider::new( + integritee_rpc_api.clone(), + tee_accountid.clone(), + )); + let metrics_handler = Arc::new(MetricsHandler::new(enclave_wallet)); + let metrics_server_port = config + .try_parse_metrics_server_port() + .expect("metrics server port to be a valid port number"); + tokio_handle.spawn(async move { + if let Err(e) = start_metrics_server(metrics_handler, metrics_server_port).await { + error!("Unexpected error in Prometheus metrics server: {:?}", e); + } + }); + } + + // ------------------------------------------------------------------------ + // Start trusted worker rpc server + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain + || WorkerModeProvider::worker_mode() == WorkerMode::OffChainWorker + { + let direct_invocation_server_addr = config.trusted_worker_url_internal(); + let enclave_for_direct_invocation = enclave.clone(); + thread::spawn(move || { + println!( + "[+] Trusted RPC direct invocation server listening on {}", + direct_invocation_server_addr + ); + enclave_for_direct_invocation + .init_direct_invocation_server(direct_invocation_server_addr) + .unwrap(); + println!("[+] RPC direct invocation server shut down"); + }); + } + + // ------------------------------------------------------------------------ + // Start untrusted worker rpc server. + // i.e move sidechain block importing to trusted worker. + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + sidechain_start_untrusted_rpc_server( + &config, + enclave.clone(), + sidechain_storage.clone(), + tokio_handle, + ); + } + + // ------------------------------------------------------------------------ + // Init parentchain specific stuff. Needed for parentchain communication. + + let (parentchain_handler, last_synced_header) = + init_parentchain(&enclave, &integritee_rpc_api, &tee_accountid, ParentchainId::Integritee); + + #[cfg(feature = "dcap")] + register_collateral( + &integritee_rpc_api, + &*enclave, + &tee_accountid, + is_development_mode, + skip_ra, + ); + + let trusted_url = config.trusted_worker_url_external(); + + #[cfg(feature = "attesteer")] + fetch_marblerun_events_every_hour( + integritee_rpc_api.clone(), + enclave.clone(), + tee_accountid.clone(), + is_development_mode, + trusted_url.clone(), + run_config.marblerun_base_url().to_string(), + ); + + // ------------------------------------------------------------------------ + // Perform a remote attestation and get an unchecked extrinsic back. + + if skip_ra { + println!( + "[!] skipping remote attestation. Registering enclave without attestation report." + ); + } else { + println!("[!] creating remote attestation report and create enclave register extrinsic."); + }; + + #[cfg(feature = "dcap")] + enclave.set_sgx_qpl_logging().expect("QPL logging setup failed"); + + let enclave2 = enclave.clone(); + #[cfg(not(feature = "dcap"))] + let register_xt = move || enclave2.generate_ias_ra_extrinsic(&trusted_url, skip_ra).unwrap(); + #[cfg(feature = "dcap")] + let register_xt = move || enclave2.generate_dcap_ra_extrinsic(&trusted_url, skip_ra).unwrap(); + + // clones because of the move + let node_api2 = integritee_rpc_api.clone(); + let tee_accountid_clone = tee_accountid.clone(); + let send_register_xt = move || { + println!("[+] Send register enclave extrinsic"); + send_extrinsic(register_xt(), &node_api2, &tee_accountid_clone, is_development_mode) + }; + + // Todo: Can't unwrap here because the extrinsic is for some reason not found in the block + // even if it was successful: https://github.com/scs/substrate-api-client/issues/624. + let register_enclave_block_hash = send_register_xt(); + let api_register_enclave_xt_header = + integritee_rpc_api.get_header(register_enclave_block_hash).unwrap().unwrap(); + + // TODO: #1451: Fix api-client type hacks + let register_enclave_xt_header = + Header::decode(&mut api_register_enclave_xt_header.encode().as_slice()) + .expect("Can decode previously encoded header; qed"); + + println!( + "[+] Enclave registered at block number: {:?}, hash: {:?}", + register_enclave_xt_header.number(), + register_enclave_xt_header.hash() + ); + + let we_are_primary_validateer = + we_are_primary_worker(&integritee_rpc_api, shard, &tee_accountid).unwrap(); + + if we_are_primary_validateer { + println!("[+] We are the primary worker"); + } else { + println!("[+] We are NOT the primary worker"); + } + + initialization_handler.registered_on_parentchain(); + + // ------------------------------------------------------------------------ + // initialize teeracle interval + #[cfg(feature = "teeracle")] + if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { + schedule_periodic_reregistration_thread( + send_register_xt, + run_config.reregister_teeracle_interval(), + ); + + start_periodic_market_update( + &integritee_rpc_api, + run_config.teeracle_update_interval(), + enclave.as_ref(), + &teeracle_tokio_handle, + ); + } + + if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { + println!("*** [+] Finished initializing light client, syncing parentchain..."); + + // Syncing all parentchain blocks, this might take a while.. + let mut last_synced_header = + parentchain_handler.sync_parentchain(last_synced_header).unwrap(); + + // ------------------------------------------------------------------------ + // Initialize the sidechain + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + last_synced_header = sidechain_init_block_production( + enclave.clone(), + ®ister_enclave_xt_header, + we_are_primary_validateer, + parentchain_handler.clone(), + sidechain_storage, + &last_synced_header, + ) + .unwrap(); + } + + // ------------------------------------------------------------------------ + // start parentchain syncing loop (subscribe to header updates) + thread::Builder::new() + .name("parentchain_sync_loop".to_owned()) + .spawn(move || { + if let Err(e) = + subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header) + { + error!("Parentchain block syncing terminated with a failure: {:?}", e); + } + println!("[!] Parentchain block syncing has terminated"); + }) + .unwrap(); + + if WorkerModeProvider::worker_mode() == WorkerMode::OffChainWorker { + info!("skipping shard vault check because not yet supported for offchain worker"); + } else if let Ok(shard_vault) = enclave.get_ecc_vault_pubkey(shard) { + println!( + "shard vault account is already initialized in state: {}", + shard_vault.to_ss58check() + ); + } else if we_are_primary_validateer { + println!("initializing proxied shard vault account now"); + enclave.init_proxied_shard_vault(shard).unwrap(); + println!( + "initialized shard vault account: : {}", + enclave.get_ecc_vault_pubkey(shard).unwrap().to_ss58check() + ); + } else { + panic!("no vault account has been initialized and we are not the primary worker"); + } + } + + // ------------------------------------------------------------------------ + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + spawn_worker_for_shard_polling(shard, integritee_rpc_api.clone(), initialization_handler); + } + + if let Some(url) = config.target_a_parentchain_rpc_endpoint() { + init_target_parentchain( + &enclave, + &tee_accountid, + url, + ParentchainId::TargetA, + is_development_mode, + ) + } + + if let Some(url) = config.target_b_parentchain_rpc_endpoint() { + init_target_parentchain( + &enclave, + &tee_accountid, + url, + ParentchainId::TargetB, + is_development_mode, + ) + } + + // ------------------------------------------------------------------------ + // Subscribe to events and print them. + println!("*** [{:?}] Subscribing to events", ParentchainId::Integritee); + let mut subscription = integritee_rpc_api.subscribe_events().unwrap(); + println!("[+] [{:?}] Subscribed to events. waiting...", ParentchainId::Integritee); + loop { + if let Some(Ok(events)) = subscription.next_events::() { + print_events(events) + } + } +} + +fn init_target_parentchain( + enclave: &Arc, + tee_account_id: &AccountId32, + url: String, + parentchain_id: ParentchainId, + is_development_mode: bool, +) where + E: EnclaveBase + Sidechain, +{ + println!("Initializing parentchain {:?} with url: {}", parentchain_id, url); + let node_api = NodeApiFactory::new(url, AccountKeyring::Alice.pair()) + .create_api() + .unwrap_or_else(|_| panic!("[{:?}] Failed to create parentchain node API", parentchain_id)); + + // some random bytes not too small to ensure that the enclave has enough funds + setup_account_funding(&node_api, tee_account_id, [0u8; 100].into(), is_development_mode) + .unwrap_or_else(|_| { + panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) + }); + + let (parentchain_handler, last_synched_header) = + init_parentchain(enclave, &node_api, tee_account_id, parentchain_id); + + if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { + println!( + "*** [+] [{:?}] Finished initializing light client, syncing parentchain...", + parentchain_id + ); + + // Syncing all parentchain blocks, this might take a while.. + let last_synched_header = + parentchain_handler.sync_parentchain(last_synched_header).unwrap(); + + // start parentchain syncing loop (subscribe to header updates) + thread::Builder::new() + .name(format!("{:?}_parentchain_sync_loop", parentchain_id)) + .spawn(move || { + if let Err(e) = + subscribe_to_parentchain_new_headers(parentchain_handler, last_synched_header) + { + error!( + "[{:?}] parentchain block syncing terminated with a failure: {:?}", + parentchain_id, e + ); + } + println!("[!] [{:?}] parentchain block syncing has terminated", parentchain_id); + }) + .unwrap(); + } + + // Subscribe to events and print them. + println!("*** [{:?}] Subscribing to events...", parentchain_id); + let mut subscription = node_api.subscribe_events().unwrap(); + println!("[+] [{:?}] Subscribed to events. waiting...", parentchain_id); + + thread::Builder::new() + .name(format!("{:?}_parentchain_event_subscription", parentchain_id)) + .spawn(move || loop { + if let Some(Ok(events)) = subscription.next_events::() { + print_events(events) + } + }) + .unwrap(); +} + +fn init_parentchain( + enclave: &Arc, + node_api: &ParentchainApi, + tee_account_id: &AccountId32, + parentchain_id: ParentchainId, +) -> (Arc>, Header) +where + E: EnclaveBase + Sidechain, +{ + let parentchain_handler = Arc::new( + ParentchainHandler::new_with_automatic_light_client_allocation( + node_api.clone(), + enclave.clone(), + parentchain_id, + ) + .unwrap(), + ); + let last_synced_header = parentchain_handler.init_parentchain_components().unwrap(); + println!("[{:?}] last synced parentchain block: {}", parentchain_id, last_synced_header.number); + + let nonce = node_api.get_nonce_of(tee_account_id).unwrap(); + info!("[{:?}] Enclave nonce = {:?}", parentchain_id, nonce); + enclave.set_nonce(nonce, parentchain_id).unwrap_or_else(|_| { + panic!("[{:?}] Could not set nonce of enclave. Returning here...", parentchain_id) + }); + + let metadata = node_api.metadata().clone(); + let runtime_spec_version = node_api.runtime_version().spec_version; + let runtime_transaction_version = node_api.runtime_version().transaction_version; + enclave + .set_node_metadata( + NodeMetadata::new(metadata, runtime_spec_version, runtime_transaction_version).encode(), + parentchain_id, + ) + .unwrap_or_else(|_| { + panic!("[{:?}] Could not set the node metadata in the enclave", parentchain_id) + }); + + (parentchain_handler, last_synced_header) +} + +/// Start polling loop to wait until we have a worker for a shard registered on +/// the parentchain (TEEREX WorkerForShard). This is the pre-requisite to be +/// considered initialized and ready for the next worker to start (in sidechain mode only). +/// considered initialized and ready for the next worker to start. +fn spawn_worker_for_shard_polling( + shard: &ShardIdentifier, + node_api: ParentchainApi, + initialization_handler: Arc, +) where + InitializationHandler: TrackInitialization + Sync + Send + 'static, +{ + let shard_for_initialized = *shard; + thread::spawn(move || { + const POLL_INTERVAL_SECS: u64 = 2; + + loop { + info!("Polling for worker for shard ({} seconds interval)", POLL_INTERVAL_SECS); + if let Ok(Some(enclave)) = + node_api.primary_worker_for_shard(&shard_for_initialized, None) + { + // Set that the service is initialized. + initialization_handler.worker_for_shard_registered(); + println!( + "[+] Found `WorkerForShard` on parentchain state: {:?}", + enclave.instance_signer() + ); + break + } + thread::sleep(Duration::from_secs(POLL_INTERVAL_SECS)); + } + }); +} + +fn print_events(events: Vec) { + for evr in &events { + debug!("Decoded: phase = {:?}, event = {:?}", evr.phase, evr.event); + match &evr.event { + RuntimeEvent::Balances(be) => { + info!("[+] Received balances event"); + debug!("{:?}", be); + match &be { + pallet_balances::Event::Transfer { + from: transactor, + to: dest, + amount: value, + } => { + debug!(" Transactor: {:?}", transactor.to_ss58check()); + debug!(" Destination: {:?}", dest.to_ss58check()); + debug!(" Value: {:?}", value); + }, + _ => { + trace!("Ignoring unsupported balances event"); + }, + } + }, + RuntimeEvent::Teerex(re) => { + debug!("{:?}", re); + match &re { + my_node_runtime::pallet_teerex::Event::AddedSgxEnclave { + registered_by, + worker_url, + .. + } => { + println!("[+] Received AddedEnclave event"); + println!(" Sender (Worker): {:?}", registered_by); + println!( + " Registered URL: {:?}", + str::from_utf8(&worker_url.clone().unwrap_or("none".into())).unwrap() + ); + }, + _ => { + trace!("Ignoring unsupported pallet_teerex event"); + }, + } + }, + RuntimeEvent::EnclaveBridge(re) => { + debug!("{:?}", re); + match &re { + my_node_runtime::pallet_enclave_bridge::Event::IndirectInvocationRegistered( + shard, + ) => { + println!( + "[+] Received trusted call for shard {}", + shard.encode().to_base58() + ); + }, + my_node_runtime::pallet_enclave_bridge::Event::ProcessedParentchainBlock { + shard, + block_hash, + trusted_calls_merkle_root, + block_number, + } => { + info!("[+] Received ProcessedParentchainBlock event"); + debug!(" for shard: {:?}", shard); + debug!(" Block Hash: {:?}", hex::encode(block_hash)); + debug!(" Merkle Root: {:?}", hex::encode(trusted_calls_merkle_root)); + debug!(" Block Number: {:?}", block_number); + }, + my_node_runtime::pallet_enclave_bridge::Event::ShieldFunds { + shard, + encrypted_beneficiary, + amount, + } => { + info!("[+] Received ShieldFunds event"); + debug!(" for shard: {:?}", shard); + debug!(" for enc. beneficiary: {:?}", encrypted_beneficiary); + debug!(" Amount: {:?}", amount); + }, + my_node_runtime::pallet_enclave_bridge::Event::UnshieldedFunds { + shard, + beneficiary, + amount, + } => { + info!("[+] Received UnshieldedFunds event"); + debug!(" for shard: {:?}", shard); + debug!(" beneficiary: {:?}", beneficiary); + debug!(" Amount: {:?}", amount); + }, + _ => { + trace!("Ignoring unsupported pallet_enclave_bridge event"); + }, + } + }, + #[cfg(feature = "teeracle")] + RuntimeEvent::Teeracle(re) => { + debug!("{:?}", re); + match &re { + my_node_runtime::pallet_teeracle::Event::ExchangeRateUpdated { + data_source, + trading_pair, + exchange_rate, + } => { + println!("[+] Received ExchangeRateUpdated event"); + println!(" Data source: {}", data_source); + println!(" trading pair: {}", trading_pair); + println!(" Exchange rate: {:?}", exchange_rate); + }, + my_node_runtime::pallet_teeracle::Event::ExchangeRateDeleted { + data_source, + trading_pair, + } => { + println!("[+] Received ExchangeRateDeleted event"); + println!(" Data source: {}", data_source); + println!(" trading pair: {}", trading_pair); + }, + my_node_runtime::pallet_teeracle::Event::AddedToWhitelist { + data_source, + enclave_fingerprint, + } => { + println!("[+] Received AddedToWhitelist event"); + println!(" Data source: {}", data_source); + println!(" fingerprint: {:?}", enclave_fingerprint); + }, + my_node_runtime::pallet_teeracle::Event::RemovedFromWhitelist { + data_source, + enclave_fingerprint, + } => { + println!("[+] Received RemovedFromWhitelist event"); + println!(" Data source: {}", data_source); + println!(" fingerprint: {:?}", enclave_fingerprint); + }, + _ => { + trace!("Ignoring unsupported pallet_teeracle event"); + }, + } + }, + #[cfg(feature = "sidechain")] + RuntimeEvent::Sidechain(re) => match &re { + my_node_runtime::pallet_sidechain::Event::FinalizedSidechainBlock { + shard, + block_header_hash, + validateer, + } => { + info!("[+] Received FinalizedSidechainBlock event"); + debug!(" for shard: {:?}", shard); + debug!(" From: {:?}", hex::encode(block_header_hash)); + debug!(" validateer: {:?}", validateer); + }, + _ => { + trace!("Ignoring unsupported pallet_sidechain event"); + }, + }, + _ => { + trace!("Ignoring event {:?}", evr); + }, + } + } +} + +#[cfg(feature = "attesteer")] +fn fetch_marblerun_events_every_hour( + api: ParentchainApi, + enclave: Arc, + accountid: AccountId32, + is_development_mode: bool, + url: String, + marblerun_base_url: String, +) where + E: RemoteAttestation + Clone + Sync + Send + 'static, +{ + let enclave = enclave.clone(); + let handle = thread::spawn(move || { + const POLL_INTERVAL_5_MINUTES_IN_SECS: u64 = 5 * 60; + loop { + info!("Polling marblerun events for quotes to register"); + register_quotes_from_marblerun( + &api, + enclave.clone(), + &accountid, + is_development_mode, + url.clone(), + &marblerun_base_url, + ); + + thread::sleep(Duration::from_secs(POLL_INTERVAL_5_MINUTES_IN_SECS)); + } + }); + + handle.join().unwrap() +} +#[cfg(feature = "attesteer")] +fn register_quotes_from_marblerun( + api: &ParentchainApi, + enclave: Arc, + accountid: &AccountId32, + is_development_mode: bool, + url: String, + marblerun_base_url: &str, +) { + let enclave = enclave.as_ref(); + let events = prometheus_metrics::fetch_marblerun_events(marblerun_base_url) + .map_err(|e| { + info!("Fetching events from Marblerun failed with: {:?}, continuing with 0 events.", e); + }) + .unwrap_or_default(); + let quotes: Vec<&[u8]> = + events.iter().map(|event| event.get_quote_without_prepended_bytes()).collect(); + + for quote in quotes { + match enclave.generate_dcap_ra_extrinsic_from_quote(url.clone(), "e) { + Ok(xt) => { + send_extrinsic(xt, api, accountid, is_development_mode); + }, + Err(e) => { + error!("Extracting information from quote failed: {}", e) + }, + } + } +} +#[cfg(feature = "dcap")] +fn register_collateral( + api: &ParentchainApi, + enclave: &dyn RemoteAttestation, + accountid: &AccountId32, + is_development_mode: bool, + skip_ra: bool, +) { + //TODO generate_dcap_ra_quote() does not really need skip_ra, rethink how many layers skip_ra should be passed along + if !skip_ra { + let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); + let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); + println!("[>] DCAP setup: register QE collateral"); + let uxt = enclave.generate_register_quoting_enclave_extrinsic(fmspc).unwrap(); + send_extrinsic(uxt, api, accountid, is_development_mode); + + println!("[>] DCAP setup: register TCB info"); + let uxt = enclave.generate_register_tcb_info_extrinsic(fmspc).unwrap(); + send_extrinsic(uxt, api, accountid, is_development_mode); + } +} + +fn send_extrinsic( + extrinsic: Vec, + api: &ParentchainApi, + fee_payer: &AccountId32, + is_development_mode: bool, +) -> Option { + // ensure account funds + if let Err(x) = setup_account_funding(api, fee_payer, extrinsic.clone(), is_development_mode) { + error!("Ensure enclave funding failed: {:?}", x); + // Return without registering the enclave. This will fail and the transaction will be banned for 30min. + return None + } + + info!("[>] send extrinsic"); + trace!(" encoded extrinsic: 0x{:}", hex::encode(extrinsic.clone())); + + // fixme: wait ...until_success doesn't work due to https://github.com/scs/substrate-api-client/issues/624 + // fixme: currently, we don't verify if the extrinsic was a success here + match api.submit_and_watch_opaque_extrinsic_until(&extrinsic.into(), XtStatus::Finalized) { + Ok(xt_report) => { + info!( + "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", + xt_report.extrinsic_hash, xt_report.status + ); + xt_report.block_hash + }, + Err(e) => { + error!("ExtrinsicFailed {:?}", e); + None + }, + } +} + +/// Subscribe to the node API finalized heads stream and trigger a parent chain sync +/// upon receiving a new header. +fn subscribe_to_parentchain_new_headers( + parentchain_handler: Arc>, + mut last_synced_header: Header, +) -> Result<(), Error> { + // TODO: this should be implemented by parentchain_handler directly, and not via + // exposed parentchain_api + let mut subscription = parentchain_handler + .parentchain_api() + .subscribe_finalized_heads() + .map_err(Error::ApiClient)?; + + loop { + let new_header = subscription + .next() + .ok_or(Error::ApiSubscriptionDisconnected)? + .map_err(|e| Error::ApiClient(e.into()))?; + + println!( + "[+] Received finalized header update ({}), syncing parent chain...", + new_header.number + ); + + last_synced_header = parentchain_handler.sync_parentchain(last_synced_header)?; + } +} + +/// Get the public signing key of the TEE. +fn enclave_account(enclave_api: &E) -> AccountId32 { + let tee_public = enclave_api.get_ecc_signing_pubkey().unwrap(); + trace!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); + AccountId32::from(*tee_public.as_array_ref()) +} + +/// Checks if we are the first validateer to register on the parentchain. +fn we_are_primary_worker( + node_api: &ParentchainApi, + shard: &ShardIdentifier, + enclave_account: &AccountId32, +) -> Result { + // are we registered? else fail. + node_api + .enclave(enclave_account, None)? + .expect("our enclave should be registered at this point"); + trace!("our enclave is registered"); + match node_api.primary_worker_for_shard(shard, None).unwrap() { + Some(enclave) => + match enclave.instance_signer() { + AnySigner::Known(MultiSigner::Ed25519(primary)) => + if primary.encode() == enclave_account.encode() { + debug!("We are primary worker on this shard adn we have been previously running."); + Ok(true) + } else { + debug!("The primary worker is {}", primary.to_ss58check()); + Ok(false) + }, + _ => { + warn!("the primary worker is of unknown type"); + Ok(false) + }, + }, + None => { + debug!("We are the primary worker on this shard and the shard is untouched"); + Ok(true) + }, + } +} diff --git a/service/src/ocall_bridge/component_factory.rs b/service/src/ocall_bridge/component_factory.rs index 83bc4425cd..12631a42a5 100644 --- a/service/src/ocall_bridge/component_factory.rs +++ b/service/src/ocall_bridge/component_factory.rs @@ -17,6 +17,7 @@ */ use crate::{ + globals::tokio_handle::GetTokioHandle, ocall_bridge::{ bridge_api::{ GetOCallBridgeComponents, IpfsBridge, MetricsBridge, RemoteAttestationBridge, @@ -31,7 +32,6 @@ use crate::{ prometheus_metrics::ReceiveEnclaveMetrics, sync_block_broadcaster::BroadcastBlocks, worker_peers_updater::UpdateWorkerPeers, - GetTokioHandle, }; use itp_enclave_api::remote_attestation::RemoteAttestationCallBacks; use itp_node_api::node_api_factory::CreateNodeApi; diff --git a/service/src/ocall_bridge/sidechain_ocall.rs b/service/src/ocall_bridge/sidechain_ocall.rs index fe648eb44e..a16ee4a6ce 100644 --- a/service/src/ocall_bridge/sidechain_ocall.rs +++ b/service/src/ocall_bridge/sidechain_ocall.rs @@ -17,10 +17,10 @@ */ use crate::{ + globals::tokio_handle::GetTokioHandle, ocall_bridge::bridge_api::{OCallBridgeError, OCallBridgeResult, SidechainBridge}, sync_block_broadcaster::BroadcastBlocks, worker_peers_updater::UpdateWorkerPeers, - GetTokioHandle, }; use codec::{Decode, Encode}; use itp_types::{BlockHash, ShardIdentifier}; diff --git a/service/src/setup.rs b/service/src/setup.rs index d58dac8820..15250642bb 100644 --- a/service/src/setup.rs +++ b/service/src/setup.rs @@ -17,80 +17,100 @@ */ use crate::error::{Error, ServiceResult}; -use base58::ToBase58; -use codec::Encode; -use itp_enclave_api::{enclave_base::EnclaveBase, Enclave}; use itp_settings::files::{ - INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SHARDS_PATH, SHIELDING_KEY_FILE, - SIDECHAIN_STORAGE_PATH, SIGNING_KEY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, - TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SHARDS_PATH, SIDECHAIN_STORAGE_PATH, + TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, }; -use itp_types::ShardIdentifier; -use log::*; -use std::{fs, fs::File, path::Path}; +use std::{fs, path::Path}; -/// Purge all worker files from `dir`. -pub(crate) fn purge_files_from_dir(dir: &Path) -> ServiceResult<()> { - println!("[+] Performing a clean reset of the worker"); - - println!("[+] Purge all files from previous runs"); - purge_files(dir)?; - - Ok(()) -} - -/// Initializes the shard and generates the key files. -pub(crate) fn initialize_shard_and_keys( - enclave: &Enclave, - shard_identifier: &ShardIdentifier, -) -> ServiceResult<()> { - println!("[+] Initialize the shard"); - init_shard(enclave, shard_identifier); +#[cfg(feature = "link-binary")] +pub(crate) use needs_enclave::{ + generate_shielding_key_file, generate_signing_key_file, init_shard, initialize_shard_and_keys, +}; - println!("[+] Generate key files"); - generate_signing_key_file(enclave); - generate_shielding_key_file(enclave); +#[cfg(feature = "link-binary")] +mod needs_enclave { + use crate::error::{Error, ServiceResult}; + use codec::Encode; + use itp_enclave_api::{enclave_base::EnclaveBase, Enclave}; + use itp_settings::files::{ + INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SHARDS_PATH, SHIELDING_KEY_FILE, + SIDECHAIN_STORAGE_PATH, SIGNING_KEY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + }; + use itp_types::ShardIdentifier; + use log::*; + use std::{fs, fs::File, path::Path}; + + /// Initializes the shard and generates the key files. + pub(crate) fn initialize_shard_and_keys( + enclave: &Enclave, + shard_identifier: &ShardIdentifier, + ) -> ServiceResult<()> { + println!("[+] Initialize the shard"); + init_shard(enclave, shard_identifier); + + println!("[+] Generate key files"); + generate_signing_key_file(enclave); + generate_shielding_key_file(enclave); + + Ok(()) + } - Ok(()) -} + pub(crate) fn init_shard(enclave: &Enclave, shard_identifier: &ShardIdentifier) { + use base58::ToBase58; + + match enclave.init_shard(shard_identifier.encode()) { + Err(e) => { + println!( + "Failed to initialize shard {:?}: {:?}", + shard_identifier.0.to_base58(), + e + ); + }, + Ok(_) => { + println!("Successfully initialized shard {:?}", shard_identifier.0.to_base58()); + }, + } + } -pub(crate) fn init_shard(enclave: &Enclave, shard_identifier: &ShardIdentifier) { - match enclave.init_shard(shard_identifier.encode()) { - Err(e) => { - println!("Failed to initialize shard {:?}: {:?}", shard_identifier.0.to_base58(), e); - }, - Ok(_) => { - println!("Successfully initialized shard {:?}", shard_identifier.0.to_base58()); - }, + pub(crate) fn generate_signing_key_file(enclave: &Enclave) { + info!("*** Get the signing key from the TEE\n"); + let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); + debug!("[+] Signing key raw: {:?}", pubkey); + match fs::write(SIGNING_KEY_FILE, pubkey) { + Err(x) => { + error!("[-] Failed to write '{}'. {}", SIGNING_KEY_FILE, x); + }, + _ => { + println!("[+] File '{}' written successfully", SIGNING_KEY_FILE); + }, + } } -} -pub(crate) fn generate_signing_key_file(enclave: &Enclave) { - info!("*** Get the signing key from the TEE\n"); - let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); - debug!("[+] Signing key raw: {:?}", pubkey); - match fs::write(SIGNING_KEY_FILE, pubkey) { - Err(x) => { - error!("[-] Failed to write '{}'. {}", SIGNING_KEY_FILE, x); - }, - _ => { - println!("[+] File '{}' written successfully", SIGNING_KEY_FILE); - }, + pub(crate) fn generate_shielding_key_file(enclave: &Enclave) { + info!("*** Get the public key from the TEE\n"); + let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); + let file = File::create(SHIELDING_KEY_FILE).unwrap(); + match serde_json::to_writer(file, &pubkey) { + Err(x) => { + error!("[-] Failed to write '{}'. {}", SHIELDING_KEY_FILE, x); + }, + _ => { + println!("[+] File '{}' written successfully", SHIELDING_KEY_FILE); + }, + } } } -pub(crate) fn generate_shielding_key_file(enclave: &Enclave) { - info!("*** Get the public key from the TEE\n"); - let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); - let file = File::create(SHIELDING_KEY_FILE).unwrap(); - match serde_json::to_writer(file, &pubkey) { - Err(x) => { - error!("[-] Failed to write '{}'. {}", SHIELDING_KEY_FILE, x); - }, - _ => { - println!("[+] File '{}' written successfully", SHIELDING_KEY_FILE); - }, - } +/// Purge all worker files from `dir`. +pub(crate) fn purge_files_from_dir(dir: &Path) -> ServiceResult<()> { + println!("[+] Performing a clean reset of the worker"); + + println!("[+] Purge all files from previous runs"); + purge_files(dir)?; + + Ok(()) } /// Purge all worker files in a given path. diff --git a/service/src/sidechain_setup.rs b/service/src/sidechain_setup.rs index 9827cd53a2..be1ea3771f 100644 --- a/service/src/sidechain_setup.rs +++ b/service/src/sidechain_setup.rs @@ -16,9 +16,9 @@ */ use crate::{ + config::Config, error::{Error, ServiceResult}, parentchain_handler::HandleParentchain, - Config, }; use futures::executor::block_on; use itp_enclave_api::{ diff --git a/service/src/teeracle/teeracle_metrics.rs b/service/src/teeracle/teeracle_metrics.rs index 495e6e648f..8fe62c2092 100644 --- a/service/src/teeracle/teeracle_metrics.rs +++ b/service/src/teeracle/teeracle_metrics.rs @@ -15,7 +15,7 @@ */ -use crate::{error::ServiceResult, Error}; +use crate::error::{Error, ServiceResult}; use itp_enclave_metrics::ExchangeRateOracleMetric; use lazy_static::lazy_static; use prometheus::{ diff --git a/service/src/tests/mocks/initialization_handler_mock.rs b/service/src/tests/mocks/initialization_handler_mock.rs index f65439d64b..e4539afc0e 100644 --- a/service/src/tests/mocks/initialization_handler_mock.rs +++ b/service/src/tests/mocks/initialization_handler_mock.rs @@ -15,7 +15,7 @@ */ -use crate::{IsInitialized, TrackInitialization}; +use crate::initialized_service::{IsInitialized, TrackInitialization}; pub struct TrackInitializationMock; diff --git a/service/src/tests/mod.rs b/service/src/tests/mod.rs index d0cc7881a1..0ef2c4f253 100644 --- a/service/src/tests/mod.rs +++ b/service/src/tests/mod.rs @@ -15,10 +15,6 @@ */ -use crate::{config::Config, enclave::api::*, setup}; -use clap::ArgMatches; -use itp_enclave_api::enclave_test::EnclaveTest; - pub mod commons; pub mod mock; @@ -29,7 +25,14 @@ pub mod mocks; // #[cfg(test)] // pub mod parentchain_handler_test; +#[cfg(feature = "link-binary")] +use clap::ArgMatches; + +#[cfg(feature = "link-binary")] pub fn run_enclave_tests(matches: &ArgMatches) { + use crate::{config::Config, enclave::api::*, setup}; + use itp_enclave_api::enclave_test::EnclaveTest; + println!("*** Starting Test enclave"); let config = Config::from(matches); setup::purge_files_from_dir(config.data_dir()).unwrap(); diff --git a/service/src/worker.rs b/service/src/worker.rs index a052245a14..a01fdd9888 100644 --- a/service/src/worker.rs +++ b/service/src/worker.rs @@ -20,7 +20,7 @@ /// This should serve as a proof of concept for a potential refactoring design. Ultimately, everything /// from the main.rs should be covered by the worker struct here - hidden and split across /// multiple traits. -use crate::{config::Config, error::Error, TrackInitialization}; +use crate::{config::Config, error::Error, initialized_service::TrackInitialization}; use async_trait::async_trait; use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; use itp_node_api::{api_client::PalletTeerexApi, node_api_factory::CreateNodeApi};