diff --git a/core-primitives/settings/src/lib.rs b/core-primitives/settings/src/lib.rs index 3383ea04e4..a22b565dd2 100644 --- a/core-primitives/settings/src/lib.rs +++ b/core-primitives/settings/src/lib.rs @@ -42,7 +42,6 @@ pub mod files { pub static SIDECHAIN_PURGE_LIMIT: u64 = 100; // keep the last.. sidechainblocks when purging // used by enclave - pub const RSA3072_SEALED_KEY_FILE: &str = "rsa3072_key_sealed.bin"; pub const SEALED_SIGNER_SEED_FILE: &str = "ed25519_key_sealed.bin"; pub const AES_KEY_FILE_AND_INIT_V: &str = "aes_key_sealed.bin"; pub const LIGHT_CLIENT_DB: &str = "light_client_db.bin"; diff --git a/core-primitives/sgx/crypto/src/key_repository.rs b/core-primitives/sgx/crypto/src/key_repository.rs index 626321d0f8..41ca5ae860 100644 --- a/core-primitives/sgx/crypto/src/key_repository.rs +++ b/core-primitives/sgx/crypto/src/key_repository.rs @@ -21,7 +21,10 @@ use std::sync::SgxRwLock as RwLock; #[cfg(feature = "std")] use std::sync::RwLock; -use crate::error::{Error, Result}; +use crate::{ + error::{Error, Result}, + ToPubkey, +}; use itp_sgx_io::SealedIO; use std::sync::Arc; @@ -32,6 +35,13 @@ pub trait AccessKey { fn retrieve_key(&self) -> Result; } +/// Access a cryptographic public key. +pub trait AccessPubkey { + type KeyType; + + fn retrieve_pubkey(&self) -> Result; +} + /// Mutate a cryptographic key. pub trait MutateKey { fn update_key(&self, key: KeyType) -> Result<()>; @@ -62,6 +72,18 @@ where } } +impl AccessPubkey for KeyRepository +where + Pair: ToPubkey + Clone, + SealedIo: SealedIO, +{ + type KeyType = ::Pubkey; + + fn retrieve_pubkey(&self) -> Result { + self.key_lock.read().map_err(|_| Error::LockPoisoning).map(|p| p.pubkey())? + } +} + impl MutateKey for KeyRepository where KeyType: Clone, diff --git a/core-primitives/sgx/crypto/src/rsa3072.rs b/core-primitives/sgx/crypto/src/rsa3072.rs index feb6d7204c..a529c75aac 100644 --- a/core-primitives/sgx/crypto/src/rsa3072.rs +++ b/core-primitives/sgx/crypto/src/rsa3072.rs @@ -20,6 +20,7 @@ use crate::sgx_reexport_prelude::*; use crate::{ error::{Error, Result}, traits::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}, + ToPubkey, }; use sgx_crypto_helper::{ rsa3072::{Rsa3072KeyPair, Rsa3072PubKey}, @@ -31,6 +32,9 @@ use std::vec::Vec; #[cfg(feature = "sgx")] pub use sgx::*; +/// File name of the sealed RSA key file. +pub const RSA3072_SEALED_KEY_FILE: &str = "rsa3072_key_sealed.bin"; + impl ShieldingCryptoEncrypt for Rsa3072KeyPair { type Error = Error; @@ -64,56 +68,87 @@ impl ShieldingCryptoEncrypt for Rsa3072PubKey { } } +impl ToPubkey for Rsa3072KeyPair { + type Error = Error; + type Pubkey = Rsa3072PubKey; + + fn pubkey(&self) -> Result { + self.export_pubkey().map_err(|e| Error::Other(format!("{:?}", e).into())) + } +} + +pub trait RsaSealing { + fn unseal_pubkey(&self) -> Result; + + fn unseal_pair(&self) -> Result; + + fn exists(&self) -> bool; + + fn create_sealed_if_absent(&self) -> Result<()>; + + fn create_sealed(&self) -> Result<()>; +} + #[cfg(feature = "sgx")] pub mod sgx { use super::*; - use derive_more::Display; - use itp_settings::files::RSA3072_SEALED_KEY_FILE; - use itp_sgx_io::{seal, unseal, SealedIO, StaticSealedIO}; + use crate::key_repository::KeyRepository; + use itp_sgx_io::{seal, unseal, SealedIO}; use log::*; - use std::sgxfs::SgxFile; + use std::path::PathBuf; + + /// Gets a repository for an Rsa3072 keypair and initializes + /// a fresh key pair if it doesn't exist at `path`. + pub fn get_rsa3072_repository( + path: PathBuf, + ) -> Result> { + let rsa_seal = Rsa3072Seal::new(path); + rsa_seal.create_sealed_if_absent()?; + let shielding_key = rsa_seal.unseal_pair()?; + Ok(KeyRepository::new(shielding_key, rsa_seal.into())) + } + + #[derive(Clone, Debug)] + pub struct Rsa3072Seal { + base_path: PathBuf, + } impl Rsa3072Seal { - pub fn unseal_pubkey() -> Result { - let pair = Self::unseal_from_static_file()?; - let pubkey = - pair.export_pubkey().map_err(|e| Error::Other(format!("{:?}", e).into()))?; - Ok(pubkey) + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } } - } - pub fn create_sealed_if_absent() -> Result<()> { - if SgxFile::open(RSA3072_SEALED_KEY_FILE).is_err() { - info!("[Enclave] Keyfile not found, creating new! {}", RSA3072_SEALED_KEY_FILE); - return create_sealed() + pub fn path(&self) -> PathBuf { + self.base_path.join(RSA3072_SEALED_KEY_FILE) } - Ok(()) } - pub fn create_sealed() -> Result<()> { - let rsa_keypair = - Rsa3072KeyPair::new().map_err(|e| Error::Other(format!("{:?}", e).into()))?; - // println!("[Enclave] generated RSA3072 key pair. Cleartext: {}", rsa_key_json); - Rsa3072Seal::seal_to_static_file(&rsa_keypair) - } + impl RsaSealing for Rsa3072Seal { + fn unseal_pubkey(&self) -> Result { + self.unseal()?.pubkey() + } - #[derive(Copy, Clone, Debug, Display)] - pub struct Rsa3072Seal; + fn unseal_pair(&self) -> Result { + self.unseal() + } - impl StaticSealedIO for Rsa3072Seal { - type Error = Error; - type Unsealed = Rsa3072KeyPair; - fn unseal_from_static_file() -> Result { - let raw = unseal(RSA3072_SEALED_KEY_FILE)?; - let key: Rsa3072KeyPair = serde_json::from_slice(&raw) - .map_err(|e| Error::Other(format!("{:?}", e).into()))?; - Ok(key.into()) + fn exists(&self) -> bool { + self.path().exists() } - fn seal_to_static_file(unsealed: &Self::Unsealed) -> Result<()> { - let key_json = serde_json::to_vec(&unsealed) - .map_err(|e| Error::Other(format!("{:?}", e).into()))?; - Ok(seal(&key_json, RSA3072_SEALED_KEY_FILE)?) + fn create_sealed_if_absent(&self) -> Result<()> { + if !self.exists() { + info!("Keyfile not found, creating new! {}", self.path().display()); + return self.create_sealed() + } + Ok(()) + } + + fn create_sealed(&self) -> Result<()> { + let rsa_keypair = + Rsa3072KeyPair::new().map_err(|e| Error::Other(format!("{:?}", e).into()))?; + info!("Generated RSA3072 key pair. PubKey: {:?}", rsa_keypair.pubkey()?); + self.seal(&rsa_keypair) } } @@ -122,11 +157,16 @@ pub mod sgx { type Unsealed = Rsa3072KeyPair; fn unseal(&self) -> Result { - Self::unseal_from_static_file() + let raw = unseal(self.path())?; + let key: Rsa3072KeyPair = serde_json::from_slice(&raw) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(key.into()) } fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { - Self::seal_to_static_file(unsealed) + let key_json = serde_json::to_vec(&unsealed) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(seal(&key_json, self.path())?) } } } diff --git a/core-primitives/sgx/crypto/src/traits.rs b/core-primitives/sgx/crypto/src/traits.rs index fde231ff33..1d0aef5798 100644 --- a/core-primitives/sgx/crypto/src/traits.rs +++ b/core-primitives/sgx/crypto/src/traits.rs @@ -33,3 +33,10 @@ pub trait ShieldingCryptoDecrypt { type Error: Debug; fn decrypt(&self, data: &[u8]) -> Result, Self::Error>; } + +pub trait ToPubkey { + type Error: Debug; + type Pubkey; + + fn pubkey(&self) -> Result; +} diff --git a/enclave-runtime/Cargo.lock b/enclave-runtime/Cargo.lock index 2dd17ab8ad..e9367bf0c2 100644 --- a/enclave-runtime/Cargo.lock +++ b/enclave-runtime/Cargo.lock @@ -701,6 +701,7 @@ dependencies = [ "itp-sgx-crypto", "itp-sgx-externalities", "itp-sgx-io", + "itp-sgx-temp-dir", "itp-stf-executor", "itp-stf-interface", "itp-stf-primitives", @@ -721,6 +722,7 @@ dependencies = [ "lazy_static", "log", "multibase", + "once_cell 1.4.0", "parity-scale-codec", "primitive-types", "rust-base58", diff --git a/enclave-runtime/Cargo.toml b/enclave-runtime/Cargo.toml index 59f8a908e3..a402543a4b 100644 --- a/enclave-runtime/Cargo.toml +++ b/enclave-runtime/Cargo.toml @@ -34,6 +34,7 @@ test = [ "itp-attestation-handler/test", "itp-extrinsics-factory/mocks", "itp-sgx-crypto/mocks", + "itp-sgx-temp-dir", "itp-stf-executor/test", "itp-stf-executor/mocks", "itp-stf-state-handler/test", @@ -74,6 +75,8 @@ jsonrpc-core = { default-features = false, git = "https://github.com/scs/jsonrpc # mesalock env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } log = { git = "https://github.com/integritee-network/log-sgx" } +# Todo #1313: use the `once_cell` included in rusts core library once we use rust v1.70.0 +once_cell = { git = "https://github.com/mesalock-linux/once_cell-sgx" } rustls = { rev = "sgx_1.1.3", features = ["dangerous_configuration"], git = "https://github.com/mesalock-linux/rustls" } serde = { tag = "sgx_1.1.3", git = "https://github.com/mesalock-linux/serde-sgx", features = ["alloc", "mesalock_sgx"] } serde_derive = { git = "https://github.com/mesalock-linux/serde-sgx" } @@ -134,6 +137,9 @@ sp-core = { default-features = false, features = ["full_crypto"], git = "https:/ sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.39" } +# test-deps +itp-sgx-temp-dir = { version = "0.1", default-features = false, optional = true, path = "../core-primitives/sgx/temp-dir" } + [patch.crates-io] env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } getrandom = { git = "https://github.com/integritee-network/getrandom-sgx", branch = "update-v2.3" } diff --git a/enclave-runtime/src/initialization/mod.rs b/enclave-runtime/src/initialization/mod.rs index aea45fcd23..0d4bd9c984 100644 --- a/enclave-runtime/src/initialization/mod.rs +++ b/enclave-runtime/src/initialization/mod.rs @@ -60,7 +60,7 @@ 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_sgx_crypto::{aes, ed25519, rsa3072, AesSeal, Ed25519Seal, Rsa3072Seal}; +use itp_sgx_crypto::{aes, ed25519, get_rsa3072_repository, AesSeal, Ed25519Seal}; use itp_sgx_io::StaticSealedIO; use itp_stf_state_handler::{ handle_state::HandleState, query_shard_state::QueryShardState, @@ -73,22 +73,18 @@ use itp_types::ShardIdentifier; use its_sidechain::block_composer::BlockComposer; use log::*; use sp_core::crypto::Pair; -use std::{collections::HashMap, string::String, sync::Arc}; - -pub(crate) fn init_enclave(mu_ra_url: String, untrusted_worker_url: String) -> EnclaveResult<()> { - // Initialize the logging environment in the enclave. - env_logger::init(); +use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc}; +pub(crate) fn init_enclave( + mu_ra_url: String, + untrusted_worker_url: String, + base_dir: PathBuf, +) -> EnclaveResult<()> { ed25519::create_sealed_if_absent().map_err(Error::Crypto)?; let signer = Ed25519Seal::unseal_from_static_file().map_err(Error::Crypto)?; info!("[Enclave initialized] Ed25519 prim raw : {:?}", signer.public().0); - rsa3072::create_sealed_if_absent()?; - - let shielding_key = Rsa3072Seal::unseal_from_static_file()?; - - let shielding_key_repository = - Arc::new(EnclaveShieldingKeyRepository::new(shielding_key, Arc::new(Rsa3072Seal))); + let shielding_key_repository = Arc::new(get_rsa3072_repository(base_dir)?); GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.initialize(shielding_key_repository.clone()); // Create the aes key that is used for state encryption such that a key is always present in tests. @@ -153,12 +149,13 @@ pub(crate) fn init_enclave(mu_ra_url: String, untrusted_worker_url: String) -> E connection_registry.clone(), state_handler, ocall_api.clone(), - shielding_key_repository, + shielding_key_repository.clone(), ); GLOBAL_TOP_POOL_AUTHOR_COMPONENT.initialize(top_pool_author.clone()); let getter_executor = Arc::new(EnclaveGetterExecutor::new(state_observer)); - let io_handler = public_api_rpc_handler(top_pool_author, getter_executor); + let io_handler = + public_api_rpc_handler(top_pool_author, getter_executor, shielding_key_repository); let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); GLOBAL_RPC_WS_HANDLER_COMPONENT.initialize(rpc_handler); diff --git a/enclave-runtime/src/lib.rs b/enclave-runtime/src/lib.rs index 91eee070bc..236e632ddb 100644 --- a/enclave-runtime/src/lib.rs +++ b/enclave-runtime/src/lib.rs @@ -33,7 +33,8 @@ use crate::{ error::{Error, Result}, initialization::global_components::{ GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT, GLOBAL_FULL_SOLOCHAIN_HANDLER_COMPONENT, - GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, }, rpc::worker_api_direct::sidechain_io_handler, utils::{ @@ -50,16 +51,17 @@ use itp_import_queue::PushToQueue; use itp_node_api::metadata::NodeMetadata; use itp_nonce_cache::{MutateNonce, Nonce, GLOBAL_NONCE_CACHE}; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; -use itp_sgx_crypto::{ed25519, Ed25519Seal, Rsa3072Seal}; +use itp_sgx_crypto::{ed25519, key_repository::AccessPubkey, Ed25519Seal}; use itp_sgx_io::StaticSealedIO; use itp_storage::{StorageProof, StorageProofChecker}; use itp_types::{ShardIdentifier, SignedBlock}; use itp_utils::write_slice_and_whitespace_pad; use log::*; +use once_cell::sync::OnceCell; use sgx_types::sgx_status_t; use sp_core::crypto::Pair; use sp_runtime::traits::BlakeTwo256; -use std::{boxed::Box, slice, vec::Vec}; +use std::{boxed::Box, path::PathBuf, slice, vec::Vec}; mod attestation; mod empty_impls; @@ -83,6 +85,8 @@ pub mod test; pub type Hash = sp_core::H256; pub type AuthorityPair = sp_core::ed25519::Pair; +static BASE_PATH: OnceCell = OnceCell::new(); + /// Initialize the enclave. #[no_mangle] pub unsafe extern "C" fn init( @@ -91,6 +95,18 @@ pub unsafe extern "C" fn init( untrusted_worker_addr: *const u8, untrusted_worker_addr_size: u32, ) -> sgx_status_t { + // Initialize the logging environment in the enclave. + env_logger::init(); + + // Todo: This will be changed to be a param of the `init` ecall: + // https://github.com/integritee-network/worker/issues/1292 + // + // Until the above task is finished, we just fall back to the + // static behaviour, which uses the PWD already. + let pwd = std::env::current_dir().expect("Works on all supported platforms; qed"); + info!("Setting base_dir to pwd: {}", pwd.display()); + BASE_PATH.set(pwd.clone()).expect("We only init this once here; qed."); + let mu_ra_url = match String::decode(&mut slice::from_raw_parts(mu_ra_addr, mu_ra_addr_size as usize)) .map_err(Error::Codec) @@ -109,7 +125,7 @@ pub unsafe extern "C" fn init( Err(e) => return e.into(), }; - match initialization::init_enclave(mu_ra_url, untrusted_worker_url) { + match initialization::init_enclave(mu_ra_url, untrusted_worker_url, pwd) { Err(e) => e.into(), Ok(()) => sgx_status_t::SGX_SUCCESS, } @@ -120,7 +136,15 @@ pub unsafe extern "C" fn get_rsa_encryption_pubkey( pubkey: *mut u8, pubkey_size: u32, ) -> sgx_status_t { - let rsa_pubkey = match Rsa3072Seal::unseal_pubkey() { + let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let rsa_pubkey = match shielding_key_repository.retrieve_pubkey() { Ok(key) => key, Err(e) => return e.into(), }; diff --git a/enclave-runtime/src/rpc/worker_api_direct.rs b/enclave-runtime/src/rpc/worker_api_direct.rs index 9a1624ba89..0a674e48ce 100644 --- a/enclave-runtime/src/rpc/worker_api_direct.rs +++ b/enclave-runtime/src/rpc/worker_api_direct.rs @@ -28,7 +28,7 @@ use ita_sgx_runtime::Runtime; use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; use itp_primitives_cache::{GetPrimitives, GLOBAL_PRIMITIVES_CACHE}; use itp_rpc::RpcReturnValue; -use itp_sgx_crypto::Rsa3072Seal; +use itp_sgx_crypto::key_repository::AccessPubkey; use itp_stf_executor::getter_executor::ExecuteGetter; use itp_top_pool_author::traits::AuthorApi; use itp_types::{DirectRequestStatus, Request, ShardIdentifier, H256}; @@ -36,6 +36,7 @@ use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use its_primitives::types::block::SignedBlock; use its_sidechain::rpc_handler::{direct_top_pool_api, import_block_api}; use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sp_runtime::OpaqueExtrinsic; use std::{borrow::ToOwned, format, str, string::String, sync::Arc, vec::Vec}; @@ -53,10 +54,15 @@ fn get_all_rpc_methods_string(io_handler: &IoHandler) -> String { format!("methods: [{}]", method_string) } -pub fn public_api_rpc_handler(top_pool_author: Arc, getter_executor: Arc) -> IoHandler +pub fn public_api_rpc_handler( + top_pool_author: Arc, + getter_executor: Arc, + shielding_key: Arc, +) -> IoHandler where - R: AuthorApi + Send + Sync + 'static, - G: ExecuteGetter + Send + Sync + 'static, + Author: AuthorApi + Send + Sync + 'static, + GetterExecutor: ExecuteGetter + Send + Sync + 'static, + AccessShieldingKey: AccessPubkey + Send + Sync + 'static, { let io = IoHandler::new(); @@ -66,7 +72,7 @@ where // author_getShieldingKey let rsa_pubkey_name: &str = "author_getShieldingKey"; io.add_sync_method(rsa_pubkey_name, move |_: Params| { - let rsa_pubkey = match Rsa3072Seal::unseal_pubkey() { + let rsa_pubkey = match shielding_key.retrieve_pubkey() { Ok(key) => key, Err(status) => { let error_msg: String = format!("Could not get rsa pubkey due to: {}", status); diff --git a/enclave-runtime/src/test/direct_rpc_tests.rs b/enclave-runtime/src/test/direct_rpc_tests.rs index 2f2d8c54b4..a4d79eca8e 100644 --- a/enclave-runtime/src/test/direct_rpc_tests.rs +++ b/enclave-runtime/src/test/direct_rpc_tests.rs @@ -25,6 +25,8 @@ use itc_direct_rpc_server::{ }; use itc_tls_websocket_server::{ConnectionToken, WebSocketMessageHandler}; use itp_rpc::{RpcRequest, RpcReturnValue}; +use itp_sgx_crypto::get_rsa3072_repository; +use itp_sgx_temp_dir::TempDir; use itp_stf_executor::{getter_executor::GetterExecutor, mocks::GetStateMock}; use itp_stf_state_observer::mock::ObserveStateMock; use itp_top_pool_author::mocks::AuthorApiMock; @@ -37,15 +39,20 @@ use std::{string::ToString, sync::Arc, vec::Vec}; pub fn get_state_request_works() { type TestState = u64; + let temp_dir = TempDir::with_prefix("get_state_request_works").unwrap(); + let connection_registry = Arc::new(ConnectionRegistry::::new()); let watch_extractor = Arc::new(create_determine_watch::()); + let rsa_repository = get_rsa3072_repository(temp_dir.path().to_path_buf()).unwrap(); let state: TestState = 78234u64; let state_observer = Arc::new(ObserveStateMock::::new(state)); let getter_executor = Arc::new(GetterExecutor::<_, GetStateMock>::new(state_observer)); let top_pool_author = Arc::new(AuthorApiMock::default()); - let io_handler = public_api_rpc_handler(top_pool_author, getter_executor); + + let io_handler = + public_api_rpc_handler(top_pool_author, getter_executor, Arc::new(rsa_repository)); let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); let getter = Getter::trusted(TrustedGetterSigned::new( diff --git a/local-setup/py/worker.py b/local-setup/py/worker.py index 132b3df9b0..3986f8a343 100644 --- a/local-setup/py/worker.py +++ b/local-setup/py/worker.py @@ -164,7 +164,7 @@ def run_in_background(self, log_file: TextIO, flags: [str] = None, subcommand_fl 'substrate_api_client=warn,' 'jsonrpsee_ws_client=warn,' 'jsonrpsee_ws_server=warn,' - 'enclave_runtime=warn,' + 'enclave_runtime=info,' 'integritee_service=warn,' 'ita_stf=debug')