From f5e059f7d7aef17775dfe55eee1e29e34f17cd61 Mon Sep 17 00:00:00 2001 From: clangenb <37865735+clangenb@users.noreply.github.com> Date: Tue, 1 Aug 2023 11:11:59 +0200 Subject: [PATCH] Provision light-client in mutual remote attestation (#1399) * [block-import] add `ImportType` to distinguish sync and block-production import type. * Revert "[block-import] add `ImportType` to distinguish sync and block-production import type." This reverts commit c3c46fd205c382bff5f1af92da0ad435170f1a6b. * [light-validation] add genesis_hash field and introduce header prunging * [light-client] add `LightClientStateSealSync` which has some mechanisms such that it can be shared across threads in an arc. [light-client] add constructor to `LightClientStateSealSync` fix light-client adjustments remove pending wip of sealing adjustements * provision light-client state wip * [tls-ra] provision light-client state too * fix clippy * fix cargo test compilation --- .../light-client/src/concurrent_access.rs | 10 ++-- core/parentchain/light-client/src/io.rs | 54 ++++++++++++++++-- core/parentchain/light-client/src/lib.rs | 8 ++- .../light-client/src/light_validation.rs | 4 +- .../src/light_validation_state.rs | 4 +- .../src/mocks/validator_mock_seal.rs | 4 +- core/parentchain/light-client/src/state.rs | 40 +++++++++++-- .../src/initialization/global_components.rs | 16 ++++-- enclave-runtime/src/initialization/mod.rs | 14 +++-- .../initialization/parentchain/parachain.rs | 20 +++---- .../initialization/parentchain/solochain.rs | 17 +++--- enclave-runtime/src/tls_ra/mocks.rs | 13 ++++- enclave-runtime/src/tls_ra/mod.rs | 16 ++++-- enclave-runtime/src/tls_ra/seal_handler.rs | 56 ++++++++++++------- enclave-runtime/src/tls_ra/tests.rs | 12 +++- enclave-runtime/src/tls_ra/tls_ra_client.rs | 19 ++++++- enclave-runtime/src/tls_ra/tls_ra_server.rs | 32 +++++++++-- 17 files changed, 247 insertions(+), 92 deletions(-) diff --git a/core/parentchain/light-client/src/concurrent_access.rs b/core/parentchain/light-client/src/concurrent_access.rs index e5596a7d55..f6482c9233 100644 --- a/core/parentchain/light-client/src/concurrent_access.rs +++ b/core/parentchain/light-client/src/concurrent_access.rs @@ -31,7 +31,7 @@ use crate::{ }; use finality_grandpa::BlockNumberOps; use sp_runtime::traits::{Block as ParentchainBlockTrait, NumberFor}; -use std::marker::PhantomData; +use std::{marker::PhantomData, sync::Arc}; /// Retrieve an exclusive lock on a validator for either read or write access. /// @@ -62,7 +62,7 @@ where /// Implementation of a validator access based on a global lock and corresponding file. #[derive(Debug)] pub struct ValidatorAccessor { - seal: LightClientSeal, + seal: Arc, light_validation: RwLock, _phantom: PhantomData<(LightClientSeal, Validator, ParentchainBlock)>, } @@ -70,7 +70,7 @@ pub struct ValidatorAccessor { impl ValidatorAccessor { - pub fn new(validator: Validator, seal: LightClientSeal) -> Self { + pub fn new(validator: Validator, seal: Arc) -> Self { ValidatorAccessor { light_validation: RwLock::new(validator), seal, @@ -85,7 +85,7 @@ where Validator: ValidatorTrait + LightClientState + ExtrinsicSenderTrait, - Seal: LightClientSealing>, + Seal: LightClientSealing>, ParentchainBlock: ParentchainBlockTrait, NumberFor: BlockNumberOps, { @@ -126,7 +126,7 @@ mod tests { fn execute_with_and_without_mut_in_single_thread_works() { let validator_mock = ValidatorMock::default(); let seal = LightValidationStateSealMock::new(); - let accessor = TestAccessor::new(validator_mock, seal); + let accessor = TestAccessor::new(validator_mock, seal.into()); let _read_result = accessor.execute_on_validator(|_v| Ok(())).unwrap(); let _write_result = accessor.execute_mut_on_validator(|_v| Ok(())).unwrap(); diff --git a/core/parentchain/light-client/src/io.rs b/core/parentchain/light-client/src/io.rs index 645615b142..9937729c15 100644 --- a/core/parentchain/light-client/src/io.rs +++ b/core/parentchain/light-client/src/io.rs @@ -16,7 +16,7 @@ */ use crate::{ - error::Result, + error::{Error, Result}, finality::{Finality, GrandpaFinality, ParachainFinality}, light_client_init_params::{GrandpaParams, SimpleParams}, light_validation::{check_validator_set_proof, LightValidation}, @@ -37,6 +37,12 @@ use std::{ sync::Arc, }; +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + pub const DB_FILE: &str = "db.bin"; pub const BACKUP_FILE: &str = "db.bin.backup"; @@ -81,9 +87,11 @@ impl LightClientStateSeal { } } -impl LightClientSealing +impl LightClientSealing for LightClientStateSeal { + type LightClientState = LightClientState; + fn seal(&self, unsealed: &LightClientState) -> Result<()> { trace!("Backup light client state"); @@ -108,6 +116,44 @@ impl LightClientSealing { + seal: LightClientStateSeal, + _rw_lock: RwLock<()>, +} + +impl LightClientStateSealSync { + pub fn new(base_path: PathBuf) -> Result { + Ok(Self { seal: LightClientStateSeal::new(base_path)?, _rw_lock: RwLock::new(()) }) + } +} + +impl LightClientSealing + for LightClientStateSealSync +{ + type LightClientState = LightClientState; + + fn seal(&self, unsealed: &LightClientState) -> Result<()> { + let _lock = self._rw_lock.write().map_err(|_| Error::PoisonedLock)?; + self.seal.seal(unsealed) + } + + fn unseal(&self) -> Result { + let _lock = self._rw_lock.read().map_err(|_| Error::PoisonedLock)?; + self.seal.unseal() + } + + fn exists(&self) -> bool { + self.seal.exists() + } + + fn path(&self) -> &Path { + self.seal.path() + } +} + // FIXME: This is a lot of duplicate code for the initialization of two // different but sameish light clients. Should be tackled with #1081 pub fn read_or_init_grandpa_validator( @@ -119,7 +165,7 @@ where B: Block, NumberFor: finality_grandpa::BlockNumberOps, OCallApi: EnclaveOnChainOCallApi, - LightClientSeal: LightClientSealing>, + LightClientSeal: LightClientSealing>, { check_validator_set_proof::( params.genesis_header.state_root(), @@ -168,7 +214,7 @@ where B: Block, NumberFor: finality_grandpa::BlockNumberOps, OCallApi: EnclaveOnChainOCallApi, - LightClientSeal: LightClientSealing>, + LightClientSeal: LightClientSealing>, { if !seal.exists() { info!("[Enclave] ChainRelay DB not found, creating new! {}", seal.path().display()); diff --git a/core/parentchain/light-client/src/lib.rs b/core/parentchain/light-client/src/lib.rs index dda3cd557b..29f235ab82 100644 --- a/core/parentchain/light-client/src/lib.rs +++ b/core/parentchain/light-client/src/lib.rs @@ -95,9 +95,11 @@ pub trait LightClientState { fn penultimate_finalized_block_header(&self) -> Result; } -pub trait LightClientSealing { - fn seal(&self, state: &LightClientState) -> Result<(), Error>; - fn unseal(&self) -> Result; +pub trait LightClientSealing { + type LightClientState; + + fn seal(&self, state: &Self::LightClientState) -> Result<(), Error>; + fn unseal(&self) -> Result; fn exists(&self) -> bool; fn path(&self) -> &Path; } diff --git a/core/parentchain/light-client/src/light_validation.rs b/core/parentchain/light-client/src/light_validation.rs index e4e74633b2..ea8bd53ad9 100644 --- a/core/parentchain/light-client/src/light_validation.rs +++ b/core/parentchain/light-client/src/light_validation.rs @@ -105,8 +105,8 @@ impl } // A valid grandpa proof proves finalization of all previous unjustified blocks. - relay.header_hashes.append(&mut relay.unjustified_headers); - relay.header_hashes.push(header.hash()); + relay.justify_headers(); + relay.push_header_hash(header.hash()); relay.set_last_finalized_block_header(header); diff --git a/core/parentchain/light-client/src/light_validation_state.rs b/core/parentchain/light-client/src/light_validation_state.rs index aaea10f28a..502aefb053 100644 --- a/core/parentchain/light-client/src/light_validation_state.rs +++ b/core/parentchain/light-client/src/light_validation_state.rs @@ -58,9 +58,7 @@ where } fn genesis_hash(&self) -> Result, Error> { - let relay = self.get_relay(); - let hash = relay.header_hashes.get(0).ok_or(Error::NoGenesis)?; - Ok(*hash) + Ok(self.get_relay().genesis_hash) } fn latest_finalized_header(&self) -> Result { diff --git a/core/parentchain/light-client/src/mocks/validator_mock_seal.rs b/core/parentchain/light-client/src/mocks/validator_mock_seal.rs index 6a5f895bd3..4c7e4f25d3 100644 --- a/core/parentchain/light-client/src/mocks/validator_mock_seal.rs +++ b/core/parentchain/light-client/src/mocks/validator_mock_seal.rs @@ -40,7 +40,9 @@ impl Default for LightValidationStateSealMock { } } -impl LightClientSealing> for LightValidationStateSealMock { +impl LightClientSealing for LightValidationStateSealMock { + type LightClientState = LightValidationState; + fn unseal(&self) -> Result, Error> { Ok(LightValidationState::new(RelayState::new( ParentchainHeaderBuilder::default().build(), diff --git a/core/parentchain/light-client/src/state.rs b/core/parentchain/light-client/src/state.rs index 743e7eed63..720d5240ef 100644 --- a/core/parentchain/light-client/src/state.rs +++ b/core/parentchain/light-client/src/state.rs @@ -21,20 +21,47 @@ use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT}, OpaqueExtrinsic, }; -use std::{fmt, vec::Vec}; +use std::{collections::VecDeque, fmt, vec::Vec}; + +/// Defines the amount of parentchain headers to keep. +pub const PARENTCHAIN_HEADER_PRUNING: u64 = 1000; #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct RelayState { + pub genesis_hash: Block::Hash, pub last_finalized_block_header: Block::Header, pub penultimate_finalized_block_header: Block::Header, pub current_validator_set: AuthorityList, pub current_validator_set_id: SetId, - pub header_hashes: Vec, + header_hashes: VecDeque, pub unjustified_headers: Vec, // Finalized headers without grandpa proof pub verify_tx_inclusion: Vec, // Transactions sent by the relay pub scheduled_change: Option>, // Scheduled Authorities change as indicated in the header's digest. } +impl RelayState { + pub fn push_header_hash(&mut self, header: Block::Hash) { + self.header_hashes.push_back(header); + + if self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize { + self.header_hashes.pop_front().expect("Tested above that is not empty; qed"); + } + } + + pub fn justify_headers(&mut self) { + self.header_hashes.extend(&mut self.unjustified_headers.iter()); + self.unjustified_headers.clear(); + + while self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize { + self.header_hashes.pop_front().expect("Tested above that is not empty; qed"); + } + } + + pub fn header_hashes(&self) -> &VecDeque { + &self.header_hashes + } +} + #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct ScheduledChangeAtBlock { pub at_block: Header::Number, @@ -42,12 +69,13 @@ pub struct ScheduledChangeAtBlock { } impl RelayState { - pub fn new(block_header: Block::Header, validator_set: AuthorityList) -> Self { + pub fn new(genesis: Block::Header, validator_set: AuthorityList) -> Self { RelayState { - header_hashes: vec![block_header.hash()], - last_finalized_block_header: block_header.clone(), + genesis_hash: genesis.hash(), + header_hashes: vec![genesis.hash()].into(), + last_finalized_block_header: genesis.clone(), // is it bad to initialize with the same? Header trait does no implement default... - penultimate_finalized_block_header: block_header, + penultimate_finalized_block_header: genesis, current_validator_set: validator_set, current_validator_set_id: 0, unjustified_headers: Vec::new(), diff --git a/enclave-runtime/src/initialization/global_components.rs b/enclave-runtime/src/initialization/global_components.rs index e4197a43ba..67934d926b 100644 --- a/enclave-runtime/src/initialization/global_components.rs +++ b/enclave-runtime/src/initialization/global_components.rs @@ -46,7 +46,7 @@ use itc_parentchain::{ IndirectCallsExecutor, }, light_client::{ - concurrent_access::ValidatorAccessor, io::LightClientStateSeal, + concurrent_access::ValidatorAccessor, io::LightClientStateSealSync, light_validation::LightValidation, light_validation_state::LightValidationState, }, }; @@ -132,7 +132,7 @@ pub type EnclaveSidechainApi = SidechainApi; // Parentchain types pub type EnclaveLightClientSeal = - LightClientStateSeal>; + LightClientStateSealSync>; pub type EnclaveExtrinsicsFactory = ExtrinsicsFactory; pub type EnclaveIndirectCallsExecutor = IndirectCallsExecutor< @@ -215,8 +215,12 @@ pub type EnclaveSidechainBlockImportQueueWorker = BlockImportQueueWorker< EnclaveSidechainBlockImportQueue, EnclaveSidechainBlockSyncer, >; -pub type EnclaveSealHandler = - SealHandler; +pub type EnclaveSealHandler = SealHandler< + EnclaveShieldingKeyRepository, + EnclaveStateKeyRepository, + EnclaveStateHandler, + EnclaveLightClientSeal, +>; pub type EnclaveOffchainWorkerExecutor = itc_offchain_worker_executor::executor::Executor< ParentchainBlock, EnclaveTopPoolAuthor, @@ -244,6 +248,10 @@ pub static GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT: ComponentContainer< EnclaveSigningKeyRepository, > = ComponentContainer::new("Signing key repository"); +/// Light client db seal. +pub static GLOBAL_LIGHT_CLIENT_SEAL: ComponentContainer = + ComponentContainer::new("EnclaveLightClientSealSync"); + /// O-Call API pub static GLOBAL_OCALL_API_COMPONENT: ComponentContainer = ComponentContainer::new("O-call API"); diff --git a/enclave-runtime/src/initialization/mod.rs b/enclave-runtime/src/initialization/mod.rs index 98fb9bc8f2..d2e113fa7d 100644 --- a/enclave-runtime/src/initialization/mod.rs +++ b/enclave-runtime/src/initialization/mod.rs @@ -21,14 +21,14 @@ pub mod parentchain; use crate::{ error::{Error, Result as EnclaveResult}, initialization::global_components::{ - EnclaveBlockImportConfirmationHandler, EnclaveGetterExecutor, EnclaveOCallApi, - EnclaveRpcConnectionRegistry, EnclaveRpcResponder, EnclaveShieldingKeyRepository, - EnclaveSidechainApi, EnclaveSidechainBlockImportQueue, + EnclaveBlockImportConfirmationHandler, EnclaveGetterExecutor, EnclaveLightClientSeal, + EnclaveOCallApi, EnclaveRpcConnectionRegistry, EnclaveRpcResponder, + EnclaveShieldingKeyRepository, EnclaveSidechainApi, EnclaveSidechainBlockImportQueue, EnclaveSidechainBlockImportQueueWorker, EnclaveSidechainBlockImporter, EnclaveSidechainBlockSyncer, EnclaveStateFileIo, EnclaveStateHandler, EnclaveStateInitializer, EnclaveStateObserver, EnclaveStateSnapshotRepository, EnclaveStfEnclaveSigner, EnclaveTopPool, EnclaveTopPoolAuthor, - GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, @@ -59,7 +59,7 @@ use itc_tls_websocket_server::{ use itp_attestation_handler::IntelAttestationHandler; use itp_component_container::{ComponentGetter, ComponentInitializer}; use itp_primitives_cache::GLOBAL_PRIMITIVES_CACHE; -use itp_settings::files::STATE_SNAPSHOTS_CACHE_SIZE; +use itp_settings::files::{LIGHT_CLIENT_DB_PATH, STATE_SNAPSHOTS_CACHE_SIZE}; use itp_sgx_crypto::{ get_aes_repository, get_ed25519_repository, get_rsa3072_repository, key_repository::AccessKey, }; @@ -94,6 +94,10 @@ pub(crate) fn init_enclave( let state_key_repository = Arc::new(get_aes_repository(base_dir.clone())?); GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.initialize(state_key_repository.clone()); + let light_client_seal = + Arc::new(EnclaveLightClientSeal::new(base_dir.join(LIGHT_CLIENT_DB_PATH))?); + GLOBAL_LIGHT_CLIENT_SEAL.initialize(light_client_seal); + let state_file_io = Arc::new(EnclaveStateFileIo::new(state_key_repository, StateDir::new(base_dir))); let state_initializer = diff --git a/enclave-runtime/src/initialization/parentchain/parachain.rs b/enclave-runtime/src/initialization/parentchain/parachain.rs index 2d0b4854eb..7b43450e6b 100644 --- a/enclave-runtime/src/initialization/parentchain/parachain.rs +++ b/enclave-runtime/src/initialization/parentchain/parachain.rs @@ -19,10 +19,10 @@ use crate::{ error::Result, initialization::{ global_components::{ - EnclaveExtrinsicsFactory, EnclaveLightClientSeal, EnclaveNodeMetadataRepository, - EnclaveOCallApi, EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor, - EnclaveValidatorAccessor, GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor, EnclaveValidatorAccessor, + GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, }, parentchain::common::{ create_extrinsics_factory, create_offchain_immediate_import_dispatcher, @@ -33,12 +33,10 @@ use crate::{ use codec::Encode; use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; use itp_component_container::{ComponentGetter, ComponentInitializer}; -use itp_settings::{ - files::LIGHT_CLIENT_DB_PATH, - worker_mode::{ProvideWorkerMode, WorkerMode}, -}; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; use std::{path::PathBuf, sync::Arc, vec::Vec}; +use crate::initialization::global_components::GLOBAL_LIGHT_CLIENT_SEAL; pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams}; #[derive(Clone)] @@ -54,7 +52,7 @@ pub struct FullParachainHandler { impl FullParachainHandler { pub fn init( - base_path: PathBuf, + _base_path: PathBuf, params: ParachainParams, ) -> Result> { let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; @@ -63,12 +61,12 @@ impl FullParachainHandler { let genesis_header = params.genesis_header.clone(); - let light_client_seal = EnclaveLightClientSeal::new(base_path.join(LIGHT_CLIENT_DB_PATH))?; + let light_client_seal = GLOBAL_LIGHT_CLIENT_SEAL.get()?; let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< ParachainBlock, EnclaveOCallApi, _, - >(params, ocall_api.clone(), &light_client_seal)?; + >(params, ocall_api.clone(), &*light_client_seal)?; let latest_header = validator.latest_finalized_header()?; let validator_accessor = Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); diff --git a/enclave-runtime/src/initialization/parentchain/solochain.rs b/enclave-runtime/src/initialization/parentchain/solochain.rs index d02e5d918f..87efa644a5 100644 --- a/enclave-runtime/src/initialization/parentchain/solochain.rs +++ b/enclave-runtime/src/initialization/parentchain/solochain.rs @@ -19,9 +19,9 @@ use crate::{ error::Result, initialization::{ global_components::{ - EnclaveExtrinsicsFactory, EnclaveLightClientSeal, EnclaveNodeMetadataRepository, - EnclaveOCallApi, EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor, - EnclaveValidatorAccessor, GLOBAL_FULL_SOLOCHAIN_HANDLER_COMPONENT, + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor, EnclaveValidatorAccessor, + GLOBAL_FULL_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, }, parentchain::common::{ @@ -33,10 +33,7 @@ use crate::{ use codec::Encode; use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; use itp_component_container::{ComponentGetter, ComponentInitializer}; -use itp_settings::{ - files::LIGHT_CLIENT_DB_PATH, - worker_mode::{ProvideWorkerMode, WorkerMode}, -}; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; use std::{path::PathBuf, sync::Arc, vec::Vec}; pub use itc_parentchain::primitives::{SolochainBlock, SolochainHeader, SolochainParams}; @@ -53,21 +50,21 @@ pub struct FullSolochainHandler { impl FullSolochainHandler { pub fn init( - base_path: PathBuf, + _base_path: PathBuf, params: SolochainParams, ) -> Result> { let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let light_client_seal = GLOBAL_LIGHT_CLIENT_SEAL.get()?; let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); let genesis_header = params.genesis_header.clone(); - let light_client_seal = EnclaveLightClientSeal::new(base_path.join(LIGHT_CLIENT_DB_PATH))?; let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< SolochainBlock, EnclaveOCallApi, _, - >(params, ocall_api.clone(), &light_client_seal)?; + >(params, ocall_api.clone(), &*light_client_seal)?; let latest_header = validator.latest_finalized_header()?; let validator_accessor = Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); diff --git a/enclave-runtime/src/tls_ra/mocks.rs b/enclave-runtime/src/tls_ra/mocks.rs index 2a918f48e0..e7f6900a0f 100644 --- a/enclave-runtime/src/tls_ra/mocks.rs +++ b/enclave-runtime/src/tls_ra/mocks.rs @@ -28,6 +28,7 @@ pub struct SealHandlerMock { pub shielding_key: Arc>>, pub state_key: Arc>>, pub state: Arc>>, + pub light_client_state: Arc>>, } impl SealHandlerMock { @@ -35,8 +36,9 @@ impl SealHandlerMock { shielding_key: Arc>>, state_key: Arc>>, state: Arc>>, + light_client_state: Arc>>, ) -> Self { - Self { shielding_key, state_key, state } + Self { shielding_key, state_key, state, light_client_state } } } @@ -59,6 +61,11 @@ impl SealStateAndKeys for SealHandlerMock { fn seal_new_empty_state(&self, _shard: &ShardIdentifier) -> EnclaveResult<()> { Ok(()) } + + fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()> { + *self.light_client_state.write().unwrap() = bytes.to_vec(); + Ok(()) + } } impl UnsealStateAndKeys for SealHandlerMock { @@ -73,4 +80,8 @@ impl UnsealStateAndKeys for SealHandlerMock { fn unseal_state(&self, _shard: &ShardIdentifier) -> EnclaveResult> { Ok(self.state.read().unwrap().clone()) } + + fn unseal_light_client_state(&self) -> EnclaveResult> { + Ok(self.light_client_state.read().unwrap().clone()) + } } diff --git a/enclave-runtime/src/tls_ra/mod.rs b/enclave-runtime/src/tls_ra/mod.rs index 8bb672b5f3..90724cdfe5 100644 --- a/enclave-runtime/src/tls_ra/mod.rs +++ b/enclave-runtime/src/tls_ra/mod.rs @@ -18,6 +18,8 @@ //! Contains all logic of the state provisioning mechanism //! including the remote attestation and tls / tcp connection part. +use codec::{Decode, Encode, MaxEncodedLen}; + mod authentication; pub mod seal_handler; mod tls_ra_client; @@ -29,9 +31,9 @@ pub mod tests; #[cfg(feature = "test")] pub mod mocks; -/// Header of an accompanied payloard. Indicates the +/// Header of an accompanied payload. Indicates the /// length an the type (opcode) of the following payload. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Decode, Encode, MaxEncodedLen)] pub struct TcpHeader { pub opcode: Opcode, pub payload_length: u64, @@ -44,11 +46,12 @@ impl TcpHeader { } /// Indicates the payload content type. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode, MaxEncodedLen)] pub enum Opcode { - ShieldingKey = 0, - StateKey = 1, - State = 2, + ShieldingKey, + StateKey, + State, + LightClient, } impl From for Opcode { @@ -57,6 +60,7 @@ impl From for Opcode { 0 => Opcode::ShieldingKey, 1 => Opcode::StateKey, 2 => Opcode::State, + 3 => Opcode::LightClient, _ => unimplemented!("Unsupported/unknown Opcode for MU-RA exchange"), } } diff --git a/enclave-runtime/src/tls_ra/seal_handler.rs b/enclave-runtime/src/tls_ra/seal_handler.rs index 05f83dbd42..bb7828dd57 100644 --- a/enclave-runtime/src/tls_ra/seal_handler.rs +++ b/enclave-runtime/src/tls_ra/seal_handler.rs @@ -21,6 +21,7 @@ use crate::error::{Error as EnclaveError, Result as EnclaveResult}; use codec::{Decode, Encode}; use ita_stf::{State as StfState, StateType as StfStateType}; +use itc_parentchain::light_client::LightClientSealing; use itp_sgx_crypto::{ key_repository::{AccessKey, MutateKey}, Aes, @@ -34,32 +35,23 @@ use std::{sync::Arc, vec::Vec}; /// Handles the sealing and unsealing of the shielding key, state key and the state. #[derive(Default)] -pub struct SealHandler -where - ShieldingKeyRepository: AccessKey + MutateKey, - StateKeyRepository: AccessKey + MutateKey, - // Constraint StateT = StfState currently necessary because SgxExternalities Encode/Decode does not work. - // See https://github.com/integritee-network/sgx-runtime/issues/46. - StateHandler: HandleState, -{ +pub struct SealHandler { state_handler: Arc, state_key_repository: Arc, shielding_key_repository: Arc, + light_client_seal: Arc, } -impl - SealHandler -where - ShieldingKeyRepository: AccessKey + MutateKey, - StateKeyRepository: AccessKey + MutateKey, - StateHandler: HandleState, +impl + SealHandler { pub fn new( state_handler: Arc, state_key_repository: Arc, shielding_key_repository: Arc, + light_client_seal: Arc, ) -> Self { - Self { state_handler, state_key_repository, shielding_key_repository } + Self { state_handler, state_key_repository, shielding_key_repository, light_client_seal } } } @@ -68,20 +60,24 @@ pub trait SealStateAndKeys { fn seal_state_key(&self, bytes: &[u8]) -> EnclaveResult<()>; fn seal_state(&self, bytes: &[u8], shard: &ShardIdentifier) -> EnclaveResult<()>; fn seal_new_empty_state(&self, shard: &ShardIdentifier) -> EnclaveResult<()>; + fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()>; } pub trait UnsealStateAndKeys { fn unseal_shielding_key(&self) -> EnclaveResult>; fn unseal_state_key(&self) -> EnclaveResult>; fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult>; + fn unseal_light_client_state(&self) -> EnclaveResult>; } -impl SealStateAndKeys - for SealHandler +impl SealStateAndKeys + for SealHandler where ShieldingKeyRepository: AccessKey + MutateKey, StateKeyRepository: AccessKey + MutateKey, StateHandler: HandleState, + LightClientSeal: LightClientSealing, + LightClientSeal::LightClientState: Decode, { fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()> { let key: Rsa3072KeyPair = serde_json::from_slice(bytes).map_err(|e| { @@ -109,6 +105,13 @@ where Ok(()) } + fn seal_light_client_state(&self, mut bytes: &[u8]) -> EnclaveResult<()> { + let state = ::LightClientState::decode(&mut bytes)?; + self.light_client_seal.seal(&state)?; + info!("Successfully sealed light client state"); + Ok(()) + } + /// Seal an empty, newly initialized state. /// /// Requires the shielding key to be sealed and updated before calling this. @@ -123,12 +126,14 @@ where } } -impl UnsealStateAndKeys - for SealHandler +impl UnsealStateAndKeys + for SealHandler where ShieldingKeyRepository: AccessKey + MutateKey, StateKeyRepository: AccessKey + MutateKey, StateHandler: HandleState, + LightClientSeal: LightClientSealing, + LightClientSeal::LightClientState: Encode, { fn unseal_shielding_key(&self) -> EnclaveResult> { let shielding_key = self @@ -148,19 +153,28 @@ where fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult> { Ok(self.state_handler.execute_on_current(shard, |state, _| state.state.encode())?) } + + fn unseal_light_client_state(&self) -> EnclaveResult> { + Ok(self.light_client_seal.unseal()?.encode()) + } } #[cfg(feature = "test")] pub mod test { use super::*; + use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; use itp_sgx_crypto::mocks::KeyRepositoryMock; use itp_test::mock::handle_state_mock::HandleStateMock; type StateKeyRepositoryMock = KeyRepositoryMock; type ShieldingKeyRepositoryMock = KeyRepositoryMock; - type SealHandlerMock = - SealHandler; + type SealHandlerMock = SealHandler< + ShieldingKeyRepositoryMock, + StateKeyRepositoryMock, + HandleStateMock, + LightValidationStateSealMock, + >; pub fn seal_shielding_key_works() { let seal_handler = SealHandlerMock::default(); diff --git a/enclave-runtime/src/tls_ra/tests.rs b/enclave-runtime/src/tls_ra/tests.rs index 1540279b17..beae945656 100644 --- a/enclave-runtime/src/tls_ra/tests.rs +++ b/enclave-runtime/src/tls_ra/tests.rs @@ -26,6 +26,7 @@ use crate::{ tls_ra::seal_handler::{SealHandler, SealStateAndKeys, UnsealStateAndKeys}, }; use ita_stf::State; +use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; use itp_sgx_crypto::{mocks::KeyRepositoryMock, Aes}; use itp_stf_interface::InitState; @@ -74,22 +75,28 @@ pub fn test_tls_ra_server_client_networking() { let shielding_key_encoded = vec![1, 2, 3]; let state_key_encoded = vec![5, 2, 3, 7]; let state_encoded = Vec::from([1u8; 26000]); // Have a decently sized state, so read() must be called multiple times. + let light_client_state_encoded = Vec::from([1u8; 10000]); // Have a decently sized state, so read() must be called multiple times. let server_seal_handler = SealHandlerMock::new( Arc::new(RwLock::new(shielding_key_encoded.clone())), Arc::new(RwLock::new(state_key_encoded.clone())), Arc::new(RwLock::new(state_encoded.clone())), + Arc::new(RwLock::new(light_client_state_encoded.clone())), ); let initial_client_state = vec![0, 0, 1]; let initial_client_state_key = vec![0, 0, 2]; + let initial_client_light_client_state = vec![0, 0, 3]; let client_shielding_key = Arc::new(RwLock::new(Vec::new())); let client_state_key = Arc::new(RwLock::new(initial_client_state_key.clone())); let client_state = Arc::new(RwLock::new(initial_client_state.clone())); + let client_light_client_state = + Arc::new(RwLock::new(initial_client_light_client_state.clone())); let client_seal_handler = SealHandlerMock::new( client_shielding_key.clone(), client_state_key.clone(), client_state.clone(), + client_light_client_state.clone(), ); let port: u16 = 3149; @@ -118,6 +125,7 @@ pub fn test_tls_ra_server_client_networking() { assert!(result.is_ok()); assert_eq!(*client_shielding_key.read().unwrap(), shielding_key_encoded); + assert_eq!(*client_light_client_state.read().unwrap(), light_client_state_encoded); // State and state-key are provisioned only in sidechain mode if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { @@ -179,5 +187,7 @@ fn create_seal_handler( Arc::new(KeyRepositoryMock::::new(shielding_key)); let state_handler = Arc::new(HandleStateMock::default()); state_handler.reset(state, shard).unwrap(); - SealHandler::new(state_handler, state_key_repository, shielding_key_repository) + let seal = Arc::new(LightValidationStateSealMock::new()); + + SealHandler::new(state_handler, state_key_repository, shielding_key_repository, seal) } diff --git a/enclave-runtime/src/tls_ra/tls_ra_client.rs b/enclave-runtime/src/tls_ra/tls_ra_client.rs index aa1f71651a..9eb1546b2a 100644 --- a/enclave-runtime/src/tls_ra/tls_ra_client.rs +++ b/enclave-runtime/src/tls_ra/tls_ra_client.rs @@ -22,7 +22,7 @@ use crate::{ attestation::create_ra_report_and_signature, error::{Error as EnclaveError, Result as EnclaveResult}, initialization::global_components::{ - EnclaveSealHandler, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + EnclaveSealHandler, GLOBAL_LIGHT_CLIENT_SEAL, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, }, ocall::OcallApi, @@ -128,6 +128,7 @@ where Opcode::ShieldingKey => self.seal_handler.seal_shielding_key(&bytes)?, Opcode::StateKey => self.seal_handler.seal_state_key(&bytes)?, Opcode::State => self.seal_handler.seal_state(&bytes, &self.shard)?, + Opcode::LightClient => self.seal_handler.seal_light_client_state(&bytes)?, }; Ok(Some(header.opcode)) } @@ -192,8 +193,20 @@ pub unsafe extern "C" fn request_state_provisioning( }, }; - let seal_handler = - EnclaveSealHandler::new(state_handler, state_key_repository, shielding_key_repository); + let light_client_seal = match GLOBAL_LIGHT_CLIENT_SEAL.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let seal_handler = EnclaveSealHandler::new( + state_handler, + state_key_repository, + shielding_key_repository, + light_client_seal, + ); if let Err(e) = request_state_provisioning_internal( socket_fd, diff --git a/enclave-runtime/src/tls_ra/tls_ra_server.rs b/enclave-runtime/src/tls_ra/tls_ra_server.rs index 5e34367643..836b146b9d 100644 --- a/enclave-runtime/src/tls_ra/tls_ra_server.rs +++ b/enclave-runtime/src/tls_ra/tls_ra_server.rs @@ -22,7 +22,7 @@ use crate::{ attestation::create_ra_report_and_signature, error::{Error as EnclaveError, Result as EnclaveResult}, initialization::global_components::{ - EnclaveSealHandler, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + EnclaveSealHandler, GLOBAL_LIGHT_CLIENT_SEAL, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, }, ocall::OcallApi, @@ -47,14 +47,14 @@ use std::{ #[derive(Clone, Eq, PartialEq, Debug)] enum ProvisioningPayload { Everything, - ShieldingKeyOnly, + ShieldingKeyAndLightClient, } impl From for ProvisioningPayload { fn from(m: WorkerMode) -> Self { match m { WorkerMode::OffChainWorker | WorkerMode::Teeracle => - ProvisioningPayload::ShieldingKeyOnly, + ProvisioningPayload::ShieldingKeyAndLightClient, WorkerMode::Sidechain => ProvisioningPayload::Everything, } } @@ -107,9 +107,11 @@ where self.write_shielding_key()?; self.write_state_key()?; self.write_state(shard)?; + self.write_light_client_state()?; }, - ProvisioningPayload::ShieldingKeyOnly => { + ProvisioningPayload::ShieldingKeyAndLightClient => { self.write_shielding_key()?; + self.write_light_client_state()?; }, } @@ -135,6 +137,12 @@ where Ok(()) } + fn write_light_client_state(&mut self) -> EnclaveResult<()> { + let state = self.seal_handler.unseal_light_client_state()?; + self.write(Opcode::LightClient, &state)?; + Ok(()) + } + /// Sends the header followed by the payload. fn write(&mut self, opcode: Opcode, bytes: &[u8]) -> EnclaveResult<()> { let payload_length = bytes.len() as u64; @@ -190,8 +198,20 @@ pub unsafe extern "C" fn run_state_provisioning_server( }, }; - let seal_handler = - EnclaveSealHandler::new(state_handler, state_key_repository, shielding_key_repository); + let light_client_seal = match GLOBAL_LIGHT_CLIENT_SEAL.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let seal_handler = EnclaveSealHandler::new( + state_handler, + state_key_repository, + shielding_key_repository, + light_client_seal, + ); if let Err(e) = run_state_provisioning_server_internal::<_, WorkerModeProvider>( socket_fd,