From b506f7cf8bdc2ea35e80d1a78b5ff9ca23659ef2 Mon Sep 17 00:00:00 2001 From: Tomas Tauber <2410580+tomtau@users.noreply.github.com> Date: Mon, 24 Aug 2020 14:23:05 +0800 Subject: [PATCH] Problem: no tx-validation enclave attested TLS listener for receiving secrets from TDBE (fixes #1994) Solution: made parts of TDBE helpers reusable + a simple listening for mock key being pushed by incomplete TDBE enclave code (launched in chain-abci) + build modifications --- .github/workflows/rust.yml | 4 + .travis.yml | 1 + Cargo.lock | 9 ++ chain-abci/src/enclave_bridge/edp/mod.rs | 73 +++++++--- chain-abci/src/enclave_bridge/edp/tdbe.rs | 81 +++++------ chain-abci/src/enclave_bridge/mock.rs | 4 + chain-abci/src/main.rs | 36 +++-- chain-storage/src/lib.rs | 15 ++ .../enclave-ra/ra-client/src/config.rs | 19 +++ .../enclave-ra/ra-enclave/src/context.rs | 14 ++ .../enclave-utils/Cargo.toml | 5 + .../enclave-utils/src/lib.rs | 2 + .../enclave-utils/src/tls.rs | 101 ++++++++++++++ .../tdbe/enclave-app/Cargo.toml | 1 + .../tdbe/enclave-app/src/main.rs | 2 + .../tdbe/enclave-app/src/sgx_module.rs | 130 ++++++------------ .../tx-validation-next/.cargo/config | 2 + .../tx-validation-next/Cargo.toml | 5 +- .../tx-validation-next/src/sgx_module.rs | 65 ++++++++- .../src/sgx_module/obfuscate.rs | 27 ++-- .../src/sgx_module/validate.rs | 12 +- chain-tx-enclave/enclave-macro/Cargo.toml | 6 +- chain-tx-enclave/enclave-macro/src/lib.rs | 25 ++-- docker/build.sh | 4 + docker/sgx_test.sh | 1 + 25 files changed, 451 insertions(+), 193 deletions(-) create mode 100644 chain-tx-enclave-next/enclave-utils/src/tls.rs create mode 100644 chain-tx-enclave-next/tx-validation-next/.cargo/config diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 815ea94bd..ee81e4aec 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -53,6 +53,7 @@ jobs: NETWORK_ID: "ab" MRSIGNER: "0000000000000000000000000000000000000000000000000000000000000000" TQE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" + TDBE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" security_audit: runs-on: ubuntu-latest @@ -89,6 +90,7 @@ jobs: NETWORK_ID: "ab" MRSIGNER: "0000000000000000000000000000000000000000000000000000000000000000" TQE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" + TDBE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" - name: test-stable uses: actions-rs/cargo@v1 with: @@ -98,6 +100,7 @@ jobs: NETWORK_ID: "ab" MRSIGNER: "0000000000000000000000000000000000000000000000000000000000000000" TQE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" + TDBE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" test-nightly-coverage: runs-on: ubuntu-latest @@ -121,6 +124,7 @@ jobs: NETWORK_ID: "ab" MRSIGNER: "0000000000000000000000000000000000000000000000000000000000000000" TQE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" + TDBE_MRENCLAVE: "0000000000000000000000000000000000000000000000000000000000000000" - uses: actions-rs/grcov@v0.1 - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 diff --git a/.travis.yml b/.travis.yml index 5c4b35c75..52b6f4bd0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,7 @@ rust: &rust - SGX_SDK=/opt/sgxsdk - MRSIGNER=0000000000000000000000000000000000000000000000000000000000000000 - TQE_MRENCLAVE=0000000000000000000000000000000000000000000000000000000000000000 + - TDBE_MRENCLAVE=0000000000000000000000000000000000000000000000000000000000000000 - NETWORK_ID=ab before_install: # versions from https://github.com/erickt/rust-zmq/blob/master/.travis.yml - | diff --git a/Cargo.lock b/Cargo.lock index d79666fae..388275000 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,9 +1442,14 @@ version = "0.6.0" dependencies = [ "aead", "aes-gcm", + "chrono", "log", + "ra-client", + "ra-enclave", "rand 0.7.3", + "rustls", "sgx-isa", + "webpki", "zeroize", ] @@ -4400,6 +4405,7 @@ version = "0.6.0" dependencies = [ "chain-core", "chrono", + "enclave-macro", "enclave-protocol", "enclave-utils", "env_logger", @@ -5025,9 +5031,12 @@ dependencies = [ "env_logger", "log", "parity-scale-codec", + "ra-client", + "ra-enclave", "rand 0.7.3", "rs-libc", "secp256k1", + "sgx-isa", "zeroize", ] diff --git a/chain-abci/src/enclave_bridge/edp/mod.rs b/chain-abci/src/enclave_bridge/edp/mod.rs index 3dc6e751a..31a263be4 100644 --- a/chain-abci/src/enclave_bridge/edp/mod.rs +++ b/chain-abci/src/enclave_bridge/edp/mod.rs @@ -22,36 +22,55 @@ use tokio::net::{TcpListener, TcpStream}; use std::os::unix::net::UnixStream; +const REMOTE_ATTESTATION_PROXY: &str = "ra-sp-server"; + /// pair of unix domain sockets -/// enclave_stream is only needed / passed in `connect_stream` +/// enclave_stream / tdbe_stream is only needed / passed in `connect_stream` /// `runner_stream` is shared in chain-abci app +/// TODO: separate out the "chain-abci-side" runner_stream and "usercall extensions"-side +/// (enclave_stream / tdbe_stream) #[derive(Debug)] pub struct TxValidationApp { enclave_stream: Option, runner_stream: Arc>, + /// `ra-sp-server` address for remote attestation. E.g. `0.0.0.0:8989` + /// FIXME: enclave direct connection -- not via TCP proxy + sp_address: Option, + tdbe_stream: Option, } -impl Clone for TxValidationApp { - fn clone(&self) -> Self { +impl TxValidationApp { + /// only used for `TxValidationServer`/`chain-abci` having access to "runner_stream", + /// _not for enclave launching_ + pub fn get_comm_only(&self) -> Self { Self { enclave_stream: None, runner_stream: self.runner_stream.clone(), + sp_address: None, + tdbe_stream: None, } } -} -impl Default for TxValidationApp { - fn default() -> Self { - let (sender, receiver) = UnixStream::pair().expect("init tx validation socket"); - Self { - enclave_stream: Some(receiver), - runner_stream: Arc::new(Mutex::new(sender)), - } + fn new(sp_address: String) -> (Self, UnixStream) { + let (sender, receiver) = UnixStream::pair().expect("init chain-abci<->tve socket"); + let (from_tdbe_to_tve, from_tve_to_tdbe) = + UnixStream::pair().expect("init tve<->tdbe socket"); + + ( + Self { + enclave_stream: Some(receiver), + runner_stream: Arc::new(Mutex::new(sender)), + sp_address: Some(sp_address), + tdbe_stream: Some(from_tve_to_tdbe), + }, + from_tdbe_to_tve, + ) } } -type UserCallStream = io::Result>>; -type UserCallListener = io::Result>>; +/// type aliases for outputs in UsercallExtension async return types +pub type UserCallStream = io::Result>>; +pub type UserCallListener = io::Result>>; impl UsercallExtension for TxValidationApp { fn connect_stream<'future>( @@ -70,6 +89,22 @@ impl UsercallExtension for TxValidationApp { Ok(None) } } + REMOTE_ATTESTATION_PROXY => { + if let Some(ra_address) = this.sp_address.as_ref() { + let stream = TcpStream::connect(ra_address).await?; + Ok(Some(Box::new(stream))) + } else { + Ok(None) + } + } + "tdbe" => { + if let Some(enclave_stream) = this.tdbe_stream.as_ref() { + let stream = tokio::net::UnixStream::from_std(enclave_stream.try_clone()?)?; + Ok(Some(Box::new(stream))) + } else { + Ok(None) + } + } _ => Ok(None), } } @@ -113,9 +148,11 @@ impl EnclaveProxy for TxValidationApp { /// Launches tx-validation enclave -- /// it expects "tx-validation-next.sgxs" (+ signature) /// to be in the same directory as chain-abci -pub fn launch_tx_validation() -> TxValidationApp { - let app = TxValidationApp::default(); - let app2 = app.clone(); +/// it returns the "copied" app (for `TxValidationServer` / chain-abci) +/// + the unix stream for transaction data bootstrapping enclave +pub fn launch_tx_validation(ra_proxy_address: String) -> (TxValidationApp, UnixStream) { + let (app, from_tdbe_to_tve) = TxValidationApp::new(ra_proxy_address); + let app2 = app.get_comm_only(); let mut device = Device::new() .expect("SGX device was not found") .einittoken_provider(AesmClient::new()) @@ -134,7 +171,7 @@ pub fn launch_tx_validation() -> TxValidationApp { log::info!("starting tx validation enclave"); enclave.run().expect("Failed to start enclave") }); - app2 + (app2, from_tdbe_to_tve) } /// Temporary tx query launching options @@ -167,7 +204,7 @@ impl UsercallExtension for TempTxQueryOptions { tokio::net::UnixStream::from_std(this.chain_abci_data.try_clone()?)?; Ok(Some(Box::new(stream))) } - "ra-sp-server" => { + REMOTE_ATTESTATION_PROXY => { let stream = TcpStream::connect(&this.sp_address).await?; Ok(Some(Box::new(stream))) } diff --git a/chain-abci/src/enclave_bridge/edp/tdbe.rs b/chain-abci/src/enclave_bridge/edp/tdbe.rs index e5dd7f476..e44bfbe28 100644 --- a/chain-abci/src/enclave_bridge/edp/tdbe.rs +++ b/chain-abci/src/enclave_bridge/edp/tdbe.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, future::Future, - io::{self, Cursor, Seek, SeekFrom}, + io::{Cursor, Seek, SeekFrom}, os::unix::net::UnixStream, pin::Pin, sync::Arc, @@ -9,10 +9,7 @@ use std::{ }; use aesm_client::AesmClient; -use enclave_runner::{ - usercalls::{AsyncListener, AsyncStream, UsercallExtension}, - EnclaveBuilder, -}; +use enclave_runner::{usercalls::UsercallExtension, EnclaveBuilder}; use kvdb::KeyValueDB; use sgxs_loaders::isgx::Device; use tdbe_common::TdbeStartupConfig; @@ -27,7 +24,10 @@ use enclave_protocol::{ }; use ra_sp_server::config::SpRaConfig; -use crate::enclave_bridge::TdbeConfig; +use crate::enclave_bridge::{ + edp::{UserCallListener, UserCallStream}, + TdbeConfig, +}; #[derive(Debug)] pub struct TdbeApp { @@ -42,6 +42,8 @@ pub struct TdbeApp { remote_rpc_address: Option, /// Local TDBE server address to listen on. E.g. `127.0.0.1:3445` local_listen_address: String, + /// UDS to push secrets to tx-validation enclave + tve_stream: UnixStream, } impl TdbeApp { @@ -49,20 +51,22 @@ impl TdbeApp { pub fn new( tdbe_config: &TdbeConfig, ra_config: &SpRaConfig, - storage: Arc, + _storage: Arc, + tve_stream: UnixStream, ) -> std::io::Result { // - `chain_abci_stream` is passed to enclave. Encalve can send requests to chain-abci // using this // - `chain_abci_receiver` listens to the requests sent by enclave and responds to them - let (chain_abci_stream, chain_abci_receiver) = UnixStream::pair()?; + let (chain_abci_stream, _chain_abci_receiver) = UnixStream::pair()?; // - `persistence_stream` is passed to enclave. Encalve can send requests to chain-storage // using this // - `persistence_receiver` listens to the requests sent by enclave and responds to them - let (persistence_stream, persistence_receiver) = UnixStream::pair()?; + let (persistence_stream, _persistence_receiver) = UnixStream::pair()?; - spawn_chain_abci_thread(chain_abci_receiver, storage.clone()); - spawn_persistence_thread(persistence_receiver, storage); + // FIXME: spawn these when they actually do something + // spawn_chain_abci_thread(chain_abci_receiver, storage.clone()); + // spawn_persistence_thread(persistence_receiver, storage); Ok(Self { chain_abci_stream, @@ -70,30 +74,30 @@ impl TdbeApp { sp_address: ra_config.address.clone(), remote_rpc_address: tdbe_config.remote_rpc_address.clone(), local_listen_address: tdbe_config.local_listen_address.clone(), + tve_stream, }) } pub fn spawn(self) { - thread::spawn(move || { - let mut device = Device::new() - .expect("SGX device was not found") - .einittoken_provider(AesmClient::new()) - .build(); - let mut enclave_builder = EnclaveBuilder::new("tdb-enclave-app.sgxs".as_ref()); - - enclave_builder - .coresident_signature() - .expect("Enclave signature file not found"); - enclave_builder.usercall_extension(self); - - let enclave = enclave_builder - .build(&mut device) - .expect("Failed to build enclave"); - enclave.run().expect("Failed to start enclave") - }); + let mut device = Device::new() + .expect("SGX device was not found") + .einittoken_provider(AesmClient::new()) + .build(); + let mut enclave_builder = EnclaveBuilder::new("tdb-enclave-app.sgxs".as_ref()); + + enclave_builder + .coresident_signature() + .expect("Enclave signature file not found"); + enclave_builder.usercall_extension(self); + + let enclave = enclave_builder + .build(&mut device) + .expect("Failed to build enclave"); + enclave.run().expect("Failed to start enclave") } } +#[allow(dead_code)] fn spawn_chain_abci_thread(mut receiver: UnixStream, storage: Arc) { let _ = thread::spawn(move || { let storage = chain_storage::ReadOnlyStorage::new_db(storage); @@ -127,6 +131,8 @@ fn get_sealed_tx_data(txids: Vec, storage: &ReadOnlyStorage) -> Option) { let _ = thread::spawn(move || { let mut storage = chain_storage::Storage::new_db(storage); @@ -151,18 +157,14 @@ fn spawn_persistence_thread(mut receiver: UnixStream, storage: Arc( &'future self, addr: &'future str, _local_addr: Option<&'future mut String>, _peer_addr: Option<&'future mut String>, - ) -> Pin>>> + 'future>> { - async fn connect_stream_inner( - this: &TdbeApp, - addr: &str, - ) -> io::Result>> { + ) -> Pin + 'future>> { + async fn connect_stream_inner(this: &TdbeApp, addr: &str) -> UserCallStream { match addr { // Passes initial startup configuration to enclave "init" => { @@ -192,6 +194,10 @@ impl UsercallExtension for TdbeApp { let stream = TcpStream::connect(&this.sp_address).await?; Ok(Some(Box::new(stream))) } + "tx-validation" => { + let stream = tokio::net::UnixStream::from_std(this.tve_stream.try_clone()?)?; + Ok(Some(Box::new(stream))) + } _ => Ok(None), } } @@ -203,11 +209,8 @@ impl UsercallExtension for TdbeApp { &'future self, addr: &'future str, _local_addr: Option<&'future mut String>, - ) -> Pin>>> + 'future>> { - async fn bind_stream_inner( - this: &TdbeApp, - addr: &str, - ) -> io::Result>> { + ) -> Pin + 'future>> { + async fn bind_stream_inner(this: &TdbeApp, addr: &str) -> UserCallListener { match addr { // Binds TCP listener for TDBE server "tdbe" => { diff --git a/chain-abci/src/enclave_bridge/mock.rs b/chain-abci/src/enclave_bridge/mock.rs index 4f77b6798..75224bdc3 100644 --- a/chain-abci/src/enclave_bridge/mock.rs +++ b/chain-abci/src/enclave_bridge/mock.rs @@ -25,6 +25,10 @@ impl Clone for MockClient { } impl MockClient { + pub fn get_comm_only(&self) -> Self { + self.clone() + } + pub fn new(chain_hex_id: u8) -> Self { MockClient { chain_hex_id, diff --git a/chain-abci/src/main.rs b/chain-abci/src/main.rs index 7d474555c..4cf96a352 100644 --- a/chain-abci/src/main.rs +++ b/chain-abci/src/main.rs @@ -1,7 +1,8 @@ use chain_abci::app::{sanity_check_enabled, ChainNodeApp}; #[cfg(all(not(feature = "mock-enclave"), feature = "edp", target_os = "linux"))] use chain_abci::enclave_bridge::edp::{ - launch_tx_validation, temp_start_up_ra_tx_query, TempTxQueryOptions, TxValidationApp, + launch_tx_validation, tdbe::TdbeApp, temp_start_up_ra_tx_query, TempTxQueryOptions, + TxValidationApp, }; #[cfg(any(feature = "mock-enclave", not(target_os = "linux")))] use chain_abci::enclave_bridge::mock::MockClient; @@ -9,6 +10,7 @@ use chain_abci::enclave_bridge::{EnclaveProxy, TdbeConfig}; use chain_core::init::network::{get_network, get_network_id, init_chain_id}; use chain_storage::ReadOnlyStorage; use chain_storage::{Storage, StorageConfig, StorageType}; +use kvdb::KeyValueDB; use log::{error, info, warn}; use ra_sp_server::config::SpRaConfig; use serde::{Deserialize, Serialize}; @@ -19,6 +21,7 @@ use std::net::SocketAddr; #[cfg(all(not(feature = "mock-enclave"), feature = "edp", target_os = "linux"))] use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; +use std::sync::Arc; use structopt::StructOpt; /// TODO: should this also set the tx-query enclave file path @@ -177,13 +180,22 @@ pub struct AbciOpt { /// edp #[cfg(all(not(feature = "mock-enclave"), feature = "edp", target_os = "linux"))] -fn get_enclave_proxy() -> TxValidationApp { - launch_tx_validation() +fn get_enclave_proxy(config: &Config, storage: Arc) -> TxValidationApp { + let (app, stream_from_tdbe) = launch_tx_validation(config.remote_attestation.address.clone()); + let tdbe_app = TdbeApp::new( + &config.data_bootstrap, + &config.remote_attestation, + storage, + stream_from_tdbe, + ) + .expect("create tdbe app"); + tdbe_app.spawn(); + app } /// for development #[cfg(any(feature = "mock-enclave", not(target_os = "linux")))] -fn get_enclave_proxy() -> MockClient { +fn get_enclave_proxy(_config: &Config, _storage: Arc) -> MockClient { warn!("Using mock (non-enclave) infrastructure"); MockClient::new(get_network_id()) } @@ -278,15 +290,21 @@ fn main() { get_network(), get_network_id() ); - let tx_validator = get_enclave_proxy(); - if sanity_check_enabled() { - warn!("Enabled sanity checks"); - } let host = config.host.parse().expect("invalid host"); let addr = SocketAddr::new(host, config.port); let storage = Storage::new(&StorageConfig::new(&opt.data, StorageType::Node)); - start_up_ra_tx_query(&config, tx_validator.clone(), storage.get_read_only()); + + let tx_validator = get_enclave_proxy(&config, storage.temp_hack_for_tdbe()); + if sanity_check_enabled() { + warn!("Enabled sanity checks"); + } + + start_up_ra_tx_query( + &config, + tx_validator.get_comm_only(), + storage.get_read_only(), + ); info!("starting up"); abci::run( addr, diff --git a/chain-storage/src/lib.rs b/chain-storage/src/lib.rs index 02e5415c0..af165fcb3 100644 --- a/chain-storage/src/lib.rs +++ b/chain-storage/src/lib.rs @@ -156,6 +156,21 @@ pub enum LookupItem { } impl Storage { + /// current TDBE app code takes directly `Arc` + /// for some reason, + /// which defeats the purpose of encapsulation / that Storage is exposed + /// in a controlled way and random threads aren't writing to it. + /// -- + /// As tdbe<->chain-abci persistence seems to be only required on a "one-off" basis + /// during the initial data boostrap, + /// it's unclear why it needs to spin off a background thread + /// (instead of handling this one-off data fetch on the main thread) + /// FIXME: once the TDBE persistence of initially fetched sealed tx is actually used, + /// remove / rethink this + pub fn temp_hack_for_tdbe(&self) -> Arc { + self.db.clone() + } + pub fn get_read_only(&self) -> ReadOnlyStorage { ReadOnlyStorage { db: self.db.clone(), diff --git a/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs b/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs index de725d502..765152bc7 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs @@ -77,6 +77,25 @@ pub struct EnclaveInfo { } impl EnclaveInfo { + /// Creates an `Enclave` object from enclave's own `Report`, but replaces MRENCLAVE value (as that's a different enclave code) + pub fn from_report_other_enclave(report: Report, mr_enclave: Option<[u8; 32]>) -> Self { + let mut attributes = [0; 16]; + + // This will never panic because `UNPADDED_SIZE` for attributes in `Report` is 16 bytes. See + // here: https://github.com/fortanix/rust-sgx/blob/master/sgx-isa/src/lib.rs#L385 + attributes.copy_from_slice(&report.attributes.as_ref()); + + Self { + mr_signer: report.mrsigner, + mr_enclave, + previous_mr_enclave: None, + cpu_svn: report.cpusvn, + isv_svn: report.isvsvn, + isv_prod_id: report.isvprodid, + attributes, + } + } + /// Creates an `EncalveInfo` object from enclave `Report` pub fn from_report(report: Report, previous_mr_enclave: Option<[u8; 32]>) -> Self { let mut attributes = [0; 16]; diff --git a/chain-tx-enclave-next/enclave-ra/ra-enclave/src/context.rs b/chain-tx-enclave-next/enclave-ra/ra-enclave/src/context.rs index e46f186b9..8f3e2a284 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-enclave/src/context.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-enclave/src/context.rs @@ -21,6 +21,20 @@ use crate::{ use ra_common::DEFAULT_EXPIRATION_SECS; /// Wraps all the in-enclave operations required for remote attestation +/// explanations of confusing names: +/// "Certificate" isn't really a certificate -- it's a wrapper with the private key +/// validity vs expiration (by devashishdxt): +/// "We can set both of them as same. "certificate validity (1 day)" +/// means that each enclave will generate a new certificate each day. +/// "certificate expiration time" is the time until which a certificate will be valid. +/// "certificate expiration time" is a configuration on client side (client decides for how much time they consider a certificate as valid)." +/// +/// Also in `EnclaveCertVerifierConfig` (client), there's "report_validity_secs" which +/// determines the validity of the attestation report (in the cert extension). +/// +/// FIXME: these naming confusions start to look like potential footguns; +/// it's unclear whether one wants to be able to individually configure all these. +/// https://github.com/crypto-com/chain/issues/2028 + potential simplification + more meaningful name pub struct EnclaveRaContext { certificate: Arc>>, sp_ra_client: SpRaClient, diff --git a/chain-tx-enclave-next/enclave-utils/Cargo.toml b/chain-tx-enclave-next/enclave-utils/Cargo.toml index 37236d932..797d2fa65 100644 --- a/chain-tx-enclave-next/enclave-utils/Cargo.toml +++ b/chain-tx-enclave-next/enclave-utils/Cargo.toml @@ -13,6 +13,11 @@ aead = "0.3" zeroize = "1.1" rand = "0.7" log = "0.4" +rustls = "0.18" +webpki = "0.21" +chrono = "0.4" +ra-client = { path = "../enclave-ra/ra-client" } +ra-enclave = { path = "../enclave-ra/ra-enclave" } [features] sgxstd = ["sgx-isa/sgxstd"] diff --git a/chain-tx-enclave-next/enclave-utils/src/lib.rs b/chain-tx-enclave-next/enclave-utils/src/lib.rs index 935d6f39b..067102493 100644 --- a/chain-tx-enclave-next/enclave-utils/src/lib.rs +++ b/chain-tx-enclave-next/enclave-utils/src/lib.rs @@ -11,6 +11,8 @@ use sgx_isa::{ErrorCode, Keyname, Keypolicy, Report}; use std::convert::TryFrom; #[cfg(all(feature = "sgxstd", target_env = "sgx"))] use zeroize::Zeroize; +#[cfg(all(feature = "sgxstd", target_env = "sgx"))] +pub mod tls; /// Cryptographic payload in the sealed log of Intel SGX SDK /// (tag is detached, unlike in RFC-5116) diff --git a/chain-tx-enclave-next/enclave-utils/src/tls.rs b/chain-tx-enclave-next/enclave-utils/src/tls.rs new file mode 100644 index 000000000..01d5e9622 --- /dev/null +++ b/chain-tx-enclave-next/enclave-utils/src/tls.rs @@ -0,0 +1,101 @@ +use chrono::Duration; +use ra_client::EnclaveCertVerifier; +use ra_enclave::{EnclaveRaConfig, EnclaveRaContext, DEFAULT_EXPIRATION_SECS}; +use rustls::{ClientSession, ServerSession, StreamOwned}; +use std::net::TcpStream; +use std::sync::Arc; +use webpki::DNSNameRef; + +/// create TLS stream connecting to remote address +/// (uses the client-side certificate from EnclaveRaContext +/// and includes EnclaveCertVerifier for verifying the attestation payload) +pub fn create_tls_client_stream( + context: &EnclaveRaContext, + verifier: EnclaveCertVerifier, + dns_name: &str, + address: &str, +) -> std::io::Result> { + log::info!("Creating enclave-to-enclave attested TLS client stream"); + let certificate = context + .get_certificate() + .expect("Unable to generate remote attestation certificate"); + + let mut client_config = verifier + .into_client_config() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + certificate + .configure_client_config(&mut client_config) + .expect("Unable to configure TLS client config with certificate"); + let client_config = Arc::new(client_config); + + let dns_name_ref = DNSNameRef::try_from_ascii_str(dns_name).expect("Invalid DNS name"); + + let client_session = ClientSession::new(&client_config, dns_name_ref); + let tcp_stream = match TcpStream::connect(address) { + Ok(tcp_stream) => tcp_stream, + Err(err) => { + log::error!("Error while connecting to TCP stream"); + return Err(err); + } + }; + + log::info!("Created enclave-to-enclave TLS client stream"); + + Ok(StreamOwned::new(client_session, tcp_stream)) +} + +/// create TLS stream listening to other connections +/// (uses the client-side certificate from EnclaveRaContext +/// and includes EnclaveCertVerifier for verifying the attestation payload) +pub fn create_tls_server_stream( + context: &EnclaveRaContext, + verifier: EnclaveCertVerifier, + stream: TcpStream, + verify_client_mrenclave: bool, +) -> std::io::Result> { + log::info!("Creating enclave-to-enclave attested TLS server stream"); + let certificate = context + .get_certificate() + .expect("Unable to create remote attestation certificate"); + let mut tls_server_config = verifier + .into_client_verifying_server_config(verify_client_mrenclave) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + certificate + .configure_server_config(&mut tls_server_config) + .expect("Unable to create TLS server config"); + + let tls_server_config = Arc::new(tls_server_config); + + let tls_session = ServerSession::new(&tls_server_config); + + log::info!("Created enclave-to-enclave attested TLS server stream"); + + Ok(StreamOwned::new(tls_session, stream)) +} + +/// TODO: aesm used directly inside an enclave +pub fn create_ra_context() -> Arc { + log::info!("Creating enclave remote attestation context"); + + let certificate_expiration_time = { + option_env!("CERTIFICATE_EXPIRATION_SECS").map(|s| { + let sec = s + .parse() + .expect("invalid CERTIFICATE_EXPIRATION_SECS, expect u64"); + Duration::seconds(sec) + }) + }; + let config = EnclaveRaConfig { + sp_addr: "ra-sp-server".to_string(), + certificate_validity_secs: DEFAULT_EXPIRATION_SECS as u32, + certificate_expiration_time, + }; + + let enclave_ra_context = + EnclaveRaContext::new(&config).expect("Unable to create new remote attestation context"); + + log::info!("Created enclave remote attestation context"); + + Arc::new(enclave_ra_context) +} diff --git a/chain-tx-enclave-next/tdbe/enclave-app/Cargo.toml b/chain-tx-enclave-next/tdbe/enclave-app/Cargo.toml index b300b565e..38eca5839 100644 --- a/chain-tx-enclave-next/tdbe/enclave-app/Cargo.toml +++ b/chain-tx-enclave-next/tdbe/enclave-app/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] [target.'cfg(target_env = "sgx")'.dependencies] +enclave-macro = { path = "../../../chain-tx-enclave/enclave-macro", default-features = false } env_logger = { version = "0.7", default-features = false } log = "0.4" parity-scale-codec = "1.3" diff --git a/chain-tx-enclave-next/tdbe/enclave-app/src/main.rs b/chain-tx-enclave-next/tdbe/enclave-app/src/main.rs index 91e1351d2..89d68cd27 100644 --- a/chain-tx-enclave-next/tdbe/enclave-app/src/main.rs +++ b/chain-tx-enclave-next/tdbe/enclave-app/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(target_env = "sgx", feature(proc_macro_hygiene))] + #[cfg(target_env = "sgx")] mod sgx_module; diff --git a/chain-tx-enclave-next/tdbe/enclave-app/src/sgx_module.rs b/chain-tx-enclave-next/tdbe/enclave-app/src/sgx_module.rs index c42a67c95..3c539bad6 100644 --- a/chain-tx-enclave-next/tdbe/enclave-app/src/sgx_module.rs +++ b/chain-tx-enclave-next/tdbe/enclave-app/src/sgx_module.rs @@ -16,10 +16,12 @@ use thread_pool::ThreadPool; use webpki::DNSNameRef; use chain_core::tx::data::TxId; +use enclave_macro::mock_key; use enclave_protocol::{ codec::{StreamRead, StreamWrite}, tdbe_protocol::{PersistenceCommand, TrustedTdbeRequest, TrustedTdbeResponse}, }; +use enclave_utils::tls::{create_ra_context, create_tls_client_stream, create_tls_server_stream}; use enclave_utils::SealedData; use ra_client::{EnclaveCertVerifier, EnclaveCertVerifierConfig, EnclaveInfo}; use ra_enclave::{EnclaveRaConfig, EnclaveRaContext, DEFAULT_EXPIRATION_SECS}; @@ -27,11 +29,9 @@ use tdbe_common::TdbeStartupConfig; const THREAD_POOL_SIZE: usize = 4; -pub fn entry() -> std::io::Result<()> { - // Initialize logger - std::env::set_var("RUST_LOG", "debug"); - env_logger::init(); - +/// for reference -- to be changed with +/// #2182 #2183 +fn old_dummy_fetching_code() -> std::io::Result<()> { // Get enclave certificate verifier let verifier = get_enclave_verifier(); @@ -83,6 +83,7 @@ pub fn entry() -> std::io::Result<()> { &context, verifier, stream.expect("Error while conntecting to incoming stream"), + true, ) .expect("Unable to create TLS server stream"); @@ -94,6 +95,26 @@ pub fn entry() -> std::io::Result<()> { // Shut down thread pool when the listener is closed thread_pool.shutdown(); + Ok(()) +} + +pub fn entry() -> std::io::Result<()> { + // Initialize logger + std::env::set_var("RUST_LOG", "debug"); + env_logger::init(); + + // FIXME: init state / fetch old tx, ... + + // Connect to tx-validation + log::info!("Connecting to tx-validation"); + let tve_verifier = get_tve_enclave_verifier(); + let context = create_ra_context(); + let tve_uds = TcpStream::connect("tx-validation")?; + let mut tve_stream = create_tls_server_stream(&context, tve_verifier, tve_uds, false)?; + + /// FIXME: this should be generated using "MLS-Exporter": https://github.com/crypto-com/chain-docs/blob/master/docs/modules/tdbe.md#new-obfuscation-key + const MOCK_KEY: [u8; 16] = mock_key!(); + tve_stream.write_all(&MOCK_KEY); Ok(()) } @@ -144,6 +165,19 @@ fn get_enclave_verifier() -> EnclaveCertVerifier { verifier } +fn get_tve_enclave_verifier() -> EnclaveCertVerifier { + log::info!("Creating enclave certificate verifier for transaction validation"); + + let enclave_info = EnclaveInfo::from_report_other_enclave(Report::for_self(), None); + let verifier_config = EnclaveCertVerifierConfig::new_with_enclave_info(enclave_info); + let verifier = EnclaveCertVerifier::new(verifier_config) + .expect("Unable to create enclave certificate verifier"); + + log::info!("Created enclave certificate verifier for transaction validation"); + + verifier +} + fn get_tdbe_config() -> TdbeStartupConfig { log::info!("Fetching initial TDBE configuration"); @@ -157,31 +191,6 @@ fn get_tdbe_config() -> TdbeStartupConfig { tdbe_config } -fn create_ra_context() -> Arc { - log::info!("Creating enclave remote attestation context"); - - let certificate_expiration_time = { - option_env!("CERTIFICATE_EXPIRATION_SECS").map(|s| { - let sec = s - .parse() - .expect("invalid CERTIFICATE_EXPIRATION_SECS, expect u64"); - Duration::seconds(sec) - }) - }; - let config = EnclaveRaConfig { - sp_addr: "ra-sp-server".to_string(), - certificate_validity_secs: DEFAULT_EXPIRATION_SECS as u32, - certificate_expiration_time, - }; - - let enclave_ra_context = - EnclaveRaContext::new(&config).expect("Unable to create new remote attestation context"); - - log::info!("Created enclave remote attestation context"); - - Arc::new(enclave_ra_context) -} - /// Fetches connection details of remote TDBE server using TM RPC fn fetch_remote_tdbe_connection_details( _remote_rpc_address: &str, @@ -253,64 +262,3 @@ fn fetch_initial_data( Ok(()) } - -fn create_tls_client_stream( - context: &EnclaveRaContext, - verifier: EnclaveCertVerifier, - dns_name: &str, - address: &str, -) -> std::io::Result> { - log::info!("Creating enclave-to-enclave attested TLS client stream"); - let certificate = context - .get_certificate() - .expect("Unable to generate remote attestation certificate"); - - let mut client_config = verifier - .into_client_config() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - certificate - .configure_client_config(&mut client_config) - .expect("Unable to configure TLS client config with certificate"); - let client_config = Arc::new(client_config); - - let dns_name_ref = DNSNameRef::try_from_ascii_str(dns_name).expect("Invalid DNS name"); - - let client_session = ClientSession::new(&client_config, dns_name_ref); - let tcp_stream = match TcpStream::connect(address) { - Ok(tcp_stream) => tcp_stream, - Err(err) => { - log::error!("Error while connecting to TCP stream"); - return Err(err); - } - }; - - log::info!("Created enclave-to-enclave TLS client stream"); - - Ok(StreamOwned::new(client_session, tcp_stream)) -} - -fn create_tls_server_stream( - context: &EnclaveRaContext, - verifier: EnclaveCertVerifier, - stream: TcpStream, -) -> std::io::Result> { - log::info!("Creating enclave-to-enclave attested TLS server stream"); - let certificate = context - .get_certificate() - .expect("Unable to create remote attestation certificate"); - let mut tls_server_config = verifier - .into_client_verifying_server_config(true) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; - - certificate - .configure_server_config(&mut tls_server_config) - .expect("Unable to create TLS server config"); - - let tls_server_config = Arc::new(tls_server_config); - - let tls_session = ServerSession::new(&tls_server_config); - - log::info!("Created enclave-to-enclave attested TLS server stream"); - - Ok(StreamOwned::new(tls_session, stream)) -} diff --git a/chain-tx-enclave-next/tx-validation-next/.cargo/config b/chain-tx-enclave-next/tx-validation-next/.cargo/config new file mode 100644 index 000000000..6a8d6b2fc --- /dev/null +++ b/chain-tx-enclave-next/tx-validation-next/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "x86_64-fortanix-unknown-sgx" diff --git a/chain-tx-enclave-next/tx-validation-next/Cargo.toml b/chain-tx-enclave-next/tx-validation-next/Cargo.toml index 7115e39cb..e319d0e79 100644 --- a/chain-tx-enclave-next/tx-validation-next/Cargo.toml +++ b/chain-tx-enclave-next/tx-validation-next/Cargo.toml @@ -23,7 +23,10 @@ env_logger = { version = "0.7", default-features = false } log = "0.4" rs-libc = "0.2" rand = "0.7" -enclave-utils = { path = "../../chain-tx-enclave-next/enclave-utils", features = ["sgxstd"] } +enclave-utils = { path = "../enclave-utils", features = ["sgxstd"] } +ra-enclave = { path = "../enclave-ra/ra-enclave" } +ra-client = { path = "../enclave-ra/ra-client" } +sgx-isa = { version = "0.3", features = ["sgxstd"] } [package.metadata.fortanix-sgx] # stack size (in bytes) for each thread, the default stack size is 0x20000. diff --git a/chain-tx-enclave-next/tx-validation-next/src/sgx_module.rs b/chain-tx-enclave-next/tx-validation-next/src/sgx_module.rs index ccf958e61..72f5e8f1b 100644 --- a/chain-tx-enclave-next/tx-validation-next/src/sgx_module.rs +++ b/chain-tx-enclave-next/tx-validation-next/src/sgx_module.rs @@ -5,12 +5,17 @@ mod validate; #[allow(unused_imports)] use rs_libc::alloc::*; +use aead::{generic_array::GenericArray, NewAead}; +use aes_gcm_siv::Aes128GcmSiv; use chain_core::tx::TX_AUX_SIZE; use chain_tx_filter::BlockFilter; use chain_tx_validation::Error; -use enclave_macro::get_network_id; +use enclave_macro::{get_network_id, get_tdbe_mrenclave}; use enclave_protocol::{IntraEnclaveRequest, IntraEnclaveResponse, IntraEnclaveResponseOk}; +use enclave_utils::tls::{create_ra_context, create_tls_client_stream}; use parity_scale_codec::{Decode, Encode}; +use ra_client::{EnclaveCertVerifier, EnclaveCertVerifierConfig, EnclaveInfo}; +use sgx_isa::Report; use std::io::{Read, Write}; use std::net::TcpStream; use std::sync::atomic::{AtomicBool, Ordering}; @@ -26,16 +31,42 @@ pub(crate) fn write_response(response: IntraEnclaveResponse, output: & } } +fn get_tdbe_enclave_verifier() -> EnclaveCertVerifier { + log::info!("Creating enclave certificate verifier for transaction data bootstrapping"); + + let enclave_info = + EnclaveInfo::from_report_other_enclave(Report::for_self(), Some(get_tdbe_mrenclave!())); + let verifier_config = EnclaveCertVerifierConfig::new_with_enclave_info(enclave_info); + let verifier = EnclaveCertVerifier::new(verifier_config) + .expect("Unable to create enclave certificate verifier"); + + log::info!("Created enclave certificate verifier for transaction data bootstrapping"); + + verifier +} + /// `process_signal` is used in unit tests /// where the bool is used to stop the thread /// and sender is used for signalling that response was written -fn handling_loop( +fn handling_loop( mut chain_abci: I, + mut tdbe_tls: J, process_signal: Option<(Arc, Sender<()>)>, ) { let mut filter = BlockFilter::default(); let mut request_buf = vec![0u8; 2 * TX_AUX_SIZE]; log::debug!("waiting for chain-abci requests"); + + let mut key = GenericArray::default(); + // FIXME: also read the "epoch" / know which generation this key refers to + if tdbe_tls.read_exact(&mut key).is_err() { + log::error!("failed to read the mock key"); + unreachable!(); + } else { + log::debug!("mock key pushed"); + } + let aead = Aes128GcmSiv::new(&key); + loop { if let Some((ref b, _)) = process_signal { if b.load(Ordering::Relaxed) { @@ -59,7 +90,13 @@ fn handling_loop( } Ok(IntraEnclaveRequest::ValidateTx { request, tx_inputs }) => { log::debug!("validate tx request"); - validate::handle_validate_tx(request, tx_inputs, &mut filter, &mut chain_abci); + validate::handle_validate_tx( + &aead, + request, + tx_inputs, + &mut filter, + &mut chain_abci, + ); if let Some((_, ref s)) = process_signal { let _ = s.send(()); } @@ -81,7 +118,7 @@ fn handling_loop( } } Ok(IntraEnclaveRequest::Encrypt(request)) => { - obfuscate::handle_encrypt_request(request, &mut chain_abci); + obfuscate::handle_encrypt_request(&aead, request, &mut chain_abci); if let Some((_, ref s)) = process_signal { let _ = s.send(()); } @@ -110,7 +147,13 @@ pub fn entry() -> std::io::Result<()> { log::info!("Connecting to chain-abci"); // not really TCP -- stream provided by the runner let chain_abci = TcpStream::connect("chain-abci")?; - handling_loop(chain_abci, None); + + log::info!("Connecting to tx data bootstrapping enclave"); + let tdbe_verifier = get_tdbe_enclave_verifier(); + let context = create_ra_context(); + let tdbe_stream = create_tls_client_stream(&context, tdbe_verifier, "tdbe", "tdbe")?; + + handling_loop(chain_abci, tdbe_stream, None); Ok(()) } @@ -144,6 +187,7 @@ mod tests { use chain_core::tx::{PlainTxAux, TxToObfuscate}; use chain_core::ChainInfo; use chain_tx_validation::Error; + use enclave_macro::mock_key; use enclave_protocol::{IntraEnclaveRequest, IntraEnclaveResponseOk, VerifyTxRequest}; use log::debug; use parity_scale_codec::{Decode, Encode}; @@ -257,9 +301,15 @@ mod tests { let stream = SyncStream::default(); let stream2 = stream.stream.clone(); push_bytes(stream2.clone(), &IntraEnclaveRequest::EndBlock.encode()); + const MOCK_KEY: [u8; 16] = mock_key!(); + let key = GenericArray::clone_from_slice(&MOCK_KEY); + let aead = Aes128GcmSiv::new(&key); + + let keystream = SyncStream::default(); + push_bytes(keystream.stream.clone(), &MOCK_KEY); let _handler = std::thread::spawn(move || { - handling_loop(stream, Some((stop2, sender))); + handling_loop(stream, keystream, Some((stop2, sender))); }); let _ = receiver.recv().unwrap(); @@ -301,6 +351,7 @@ mod tests { no_of_outputs: tx0.outputs.len() as TxoSize, witness: witness0.clone(), payload: crate::sgx_module::obfuscate::encrypt( + &aead, TxToObfuscate::from(PlainTxAux::WithdrawUnbondedStakeTx(tx0.clone()), *txid) .expect("tx"), ), @@ -372,6 +423,7 @@ mod tests { inputs: tx1.inputs.clone(), no_of_outputs: tx1.outputs.len() as TxoSize, payload: crate::sgx_module::obfuscate::encrypt( + &aead, TxToObfuscate::from(PlainTxAux::TransferTx(tx1.clone(), witness1.clone()), txid1) .expect("tx"), ), @@ -417,6 +469,7 @@ mod tests { inputs: tx2.inputs.clone(), no_of_outputs: tx2.outputs.len() as TxoSize, payload: crate::sgx_module::obfuscate::encrypt( + &aead, TxToObfuscate::from(PlainTxAux::TransferTx(tx2.clone(), witness2.clone()), txid2) .expect("tx"), ), diff --git a/chain-tx-enclave-next/tx-validation-next/src/sgx_module/obfuscate.rs b/chain-tx-enclave-next/tx-validation-next/src/sgx_module/obfuscate.rs index e7b2dddc1..52d217ae2 100644 --- a/chain-tx-enclave-next/tx-validation-next/src/sgx_module/obfuscate.rs +++ b/chain-tx-enclave-next/tx-validation-next/src/sgx_module/obfuscate.rs @@ -1,6 +1,6 @@ // TODO: remove, as it's not required on newer nightly use crate::sgx_module::write_response; -use aead::{generic_array::GenericArray, Aead, NewAead}; +use aead::{generic_array::GenericArray, Aead}; use aes_gcm_siv::Aes128GcmSiv; use chain_core::state::tendermint::BlockHeight; use chain_core::tx::data::TxId; @@ -12,7 +12,6 @@ use chain_tx_validation::{ verify_bonded_deposit_core, verify_transfer, verify_unbonded_withdraw_core, witness::verify_tx_recover_address, }; -use enclave_macro::mock_key; use enclave_protocol::{EncryptionRequest, IntraEncryptRequest}; use enclave_protocol::{IntraEnclaveResponse, IntraEnclaveResponseOk}; use enclave_utils::SealedData; @@ -21,15 +20,10 @@ use std::io::Write; use std::prelude::v1::Box; use zeroize::Zeroize; -/// this will be injected by TDBE connection -const MOCK_KEY: [u8; 16] = mock_key!(); - -pub(crate) fn encrypt(tx: TxToObfuscate) -> TxObfuscated { +pub(crate) fn encrypt(alg: &Aes128GcmSiv, tx: TxToObfuscate) -> TxObfuscated { let init_vector: [u8; 12] = rand::random(); - let key = GenericArray::clone_from_slice(&MOCK_KEY); - let aead = Aes128GcmSiv::new(&key); let nonce = GenericArray::from_slice(&init_vector); - let ciphertext = aead.encrypt(nonce, &tx).expect("encryption failure!"); + let ciphertext = alg.encrypt(nonce, &tx).expect("encryption failure!"); TxObfuscated { key_from: BlockHeight::genesis(), init_vector, @@ -38,11 +32,9 @@ pub(crate) fn encrypt(tx: TxToObfuscate) -> TxObfuscated { } } -pub(crate) fn decrypt(tx: &TxObfuscated) -> Result { - let key = GenericArray::clone_from_slice(&MOCK_KEY); - let aead = Aes128GcmSiv::new(&key); +pub(crate) fn decrypt(alg: &Aes128GcmSiv, tx: &TxObfuscated) -> Result { let nonce = GenericArray::from_slice(&tx.init_vector); - let plaintext = aead.decrypt(nonce, tx).map_err(|_| ())?; + let plaintext = alg.decrypt(nonce, tx).map_err(|_| ())?; let result = PlainTxAux::decode(&mut plaintext.as_slice()); result.map_err(|_| ()) } @@ -100,7 +92,11 @@ where } #[inline] -pub(crate) fn handle_encrypt_request(request: Box, output: &mut I) { +pub(crate) fn handle_encrypt_request( + alg: &Aes128GcmSiv, + request: Box, + output: &mut I, +) { match (unseal_request(&request), request.tx_inputs) { (Some(EncryptionRequest::TransferTx(tx, witness)), Some(sealed_inputs)) => { let unsealed_inputs = check_unseal(tx.inputs.iter().map(|x| x.id), sealed_inputs); @@ -109,6 +105,7 @@ pub(crate) fn handle_encrypt_request(request: Box let txid = tx.id(); let response: IntraEnclaveResponse = result.map(|_| { IntraEnclaveResponseOk::Encrypt(encrypt( + alg, TxToObfuscate::from(PlainTxAux::TransferTx(tx, witness), txid) .expect("construct plain payload"), )) @@ -126,6 +123,7 @@ pub(crate) fn handle_encrypt_request(request: Box let txid = tx.id(); let response: IntraEnclaveResponse = result.map(|_| { IntraEnclaveResponseOk::Encrypt(encrypt( + alg, TxToObfuscate::from(PlainTxAux::DepositStakeTx(witness), txid) .expect("construct plain payload"), )) @@ -145,6 +143,7 @@ pub(crate) fn handle_encrypt_request(request: Box let result = verify_unbonded_withdraw_core(&tx, &request.info, &account); let response: IntraEnclaveResponse = result.map(|_| { IntraEnclaveResponseOk::Encrypt(encrypt( + alg, TxToObfuscate::from(PlainTxAux::WithdrawUnbondedStakeTx(tx), txid) .expect("construct plain payload"), )) diff --git a/chain-tx-enclave-next/tx-validation-next/src/sgx_module/validate.rs b/chain-tx-enclave-next/tx-validation-next/src/sgx_module/validate.rs index 58aa636de..764f3bafc 100644 --- a/chain-tx-enclave-next/tx-validation-next/src/sgx_module/validate.rs +++ b/chain-tx-enclave-next/tx-validation-next/src/sgx_module/validate.rs @@ -1,5 +1,6 @@ use crate::sgx_module::obfuscate::check_unseal; use crate::sgx_module::write_response; +use aes_gcm_siv::Aes128GcmSiv; use chain_core::init::coin::Coin; use chain_core::tx::data::TxId; use chain_core::tx::fee::Fee; @@ -63,13 +64,14 @@ fn construct_simple_response( } #[inline] -fn decrypt(payload: &TxObfuscated) -> Result { - crate::sgx_module::obfuscate::decrypt(payload) +fn decrypt(alg: &Aes128GcmSiv, payload: &TxObfuscated) -> Result { + crate::sgx_module::obfuscate::decrypt(alg, payload) } /// takes a request to verify transaction and writes back the result #[inline] pub(crate) fn handle_validate_tx( + alg: &Aes128GcmSiv, request: Box, tx_inputs: Option>>, filter: &mut BlockFilter, @@ -89,7 +91,7 @@ pub(crate) fn handle_validate_tx( inputs, }, ) => { - let plaintx = decrypt(&payload); + let plaintx = decrypt(alg, &payload); let unsealed_inputs = check_unseal(inputs.iter().map(|x| x.id), sealed_inputs); match (plaintx, unsealed_inputs) { (Ok(PlainTxAux::TransferTx(tx, witness)), Some(inputs)) => { @@ -113,7 +115,7 @@ pub(crate) fn handle_validate_tx( } } (Some(sealed_inputs), TxEnclaveAux::DepositStakeTx { tx, payload }) => { - let plaintx = decrypt(&payload); + let plaintx = decrypt(alg, &payload); let inputs = check_unseal(tx.inputs.iter().map(|x| x.id), sealed_inputs); match (plaintx, inputs) { (Ok(PlainTxAux::DepositStakeTx(witness)), Some(inputs)) => { @@ -143,7 +145,7 @@ pub(crate) fn handle_validate_tx( log::error!("get recover address failed: {:?}", e); write_response(Err(Error::EnclaveRejected), output); } else { - let plaintx = decrypt(&payload); + let plaintx = decrypt(alg, &payload); match (plaintx, request.account) { (Ok(PlainTxAux::WithdrawUnbondedStakeTx(tx)), Some(account)) => { if tx.id() != payload.txid diff --git a/chain-tx-enclave/enclave-macro/Cargo.toml b/chain-tx-enclave/enclave-macro/Cargo.toml index 3d05ae059..daddf5a9d 100644 --- a/chain-tx-enclave/enclave-macro/Cargo.toml +++ b/chain-tx-enclave/enclave-macro/Cargo.toml @@ -11,4 +11,8 @@ proc-macro = true [dependencies] rand = "0.7.2" -hex = "0.4" \ No newline at end of file +hex = "0.4" + +[features] +default = ["tdbe-macro"] +tdbe-macro = [] \ No newline at end of file diff --git a/chain-tx-enclave/enclave-macro/src/lib.rs b/chain-tx-enclave/enclave-macro/src/lib.rs index e4cce2f48..89078ce4f 100644 --- a/chain-tx-enclave/enclave-macro/src/lib.rs +++ b/chain-tx-enclave/enclave-macro/src/lib.rs @@ -12,20 +12,27 @@ pub fn mock_key(_input: TokenStream) -> TokenStream { format!("{:?}", random_bytes).parse().unwrap() } -#[proc_macro] -pub fn get_mrsigner(_input: TokenStream) -> TokenStream { - let mrsigner: Vec = hex::decode(env! {"MRSIGNER"}).unwrap(); +#[inline] +fn get_32byte_from_hex(data: &str) -> TokenStream { + let mrsigner: Vec = hex::decode(data).unwrap(); if mrsigner.len() != 32 { - panic!("mrsigner incorrect length"); + panic!("mrsigner or mrenclave incorrect length"); } format!("{:?}", mrsigner).parse().unwrap() } +#[proc_macro] +pub fn get_mrsigner(_input: TokenStream) -> TokenStream { + get_32byte_from_hex(env! {"MRSIGNER"}) +} + #[proc_macro] pub fn get_tqe_mrenclave(_input: TokenStream) -> TokenStream { - let mrenclave: Vec = hex::decode(env! {"TQE_MRENCLAVE"}).unwrap(); - if mrenclave.len() != 32 { - panic!("mrenclave incorrect length"); - } - format!("{:?}", mrenclave).parse().unwrap() + get_32byte_from_hex(env! {"TQE_MRENCLAVE"}) +} + +#[proc_macro] +#[cfg(feature = "tdbe-macro")] +pub fn get_tdbe_mrenclave(_input: TokenStream) -> TokenStream { + get_32byte_from_hex(env! {"TDBE_MRENCLAVE"}) } diff --git a/docker/build.sh b/docker/build.sh index 25c661537..938ee30a6 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -41,6 +41,10 @@ if [ $BUILD_MODE == "sgx" ]; then RUSTFLAGS="-Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3,+pclmul" cargo build --target x86_64-fortanix-unknown-sgx --package tdb-enclave-app ftxsgx-elf2sgxs $EDP_TARGET_DIR/tdb-enclave-app --heap-size 0x2000000 --stack-size 0x80000 --threads 6 $EDP_ARGS sgxs-sign --key DEV_ONLY_KEY.kem $EDP_TARGET_DIR/tdb-enclave-app.sgxs $EDP_TARGET_DIR/tdb-enclave-app.sig -d --xfrm 7/0 --isvprodid $(( 16#$NETWORK_ID )) --isvsvn 0 + + export TDBE_SIGSTRUCT=$EDP_TARGET_DIR/tdb-enclave-app.sig + export TDBE_MRENCLAVE=$(od -A none -t x1 --read-bytes=32 -j 960 -w32 $TDBE_SIGSTRUCT | tr -d ' ') + # tx-validation enclave RUSTFLAGS="-Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3,+pclmul,+sha" cargo build --target x86_64-fortanix-unknown-sgx --package tx-validation-next ftxsgx-elf2sgxs $EDP_TARGET_DIR/tx-validation-next --heap-size 0x20000000 --stack-size 0x40000 --threads 2 $EDP_ARGS diff --git a/docker/sgx_test.sh b/docker/sgx_test.sh index 932a30ead..29200a463 100755 --- a/docker/sgx_test.sh +++ b/docker/sgx_test.sh @@ -19,6 +19,7 @@ fi # for unit tests; it may not appply, as it'll be signed by runner's dummy key export MRSIGNER="0000000000000000000000000000000000000000000000000000000000000000" export TQE_MRENCLAVE="0000000000000000000000000000000000000000000000000000000000000000" +export TDBE_MRENCLAVE="0000000000000000000000000000000000000000000000000000000000000000" # Add a test runner mkdir .cargo