diff --git a/crates/phactory/api/proto b/crates/phactory/api/proto index d0237b8b9..053fc8f1c 160000 --- a/crates/phactory/api/proto +++ b/crates/phactory/api/proto @@ -1 +1 @@ -Subproject commit d0237b8b91ccb91c20529642331b94ffdf5bdb8e +Subproject commit 053fc8f1ceef0ddefcd0f4b0fd106a2e5072e019 diff --git a/crates/phactory/api/src/crypto.rs b/crates/phactory/api/src/crypto.rs index 01d99f730..cb23d136b 100644 --- a/crates/phactory/api/src/crypto.rs +++ b/crates/phactory/api/src/crypto.rs @@ -61,8 +61,10 @@ impl From for SignatureVerifyError { } } +#[derive(Default, Clone, Encode, Decode, Debug)] pub enum MessageType { Certificate { ttl: u32 }, + #[default] ContractQuery, } @@ -216,38 +218,39 @@ impl CertificateBody { sig_type: SignatureType, signature: &[u8], ) -> Result { - let signer = match sig_type { - SignatureType::Ed25519 => { - recover::(&self.pubkey, signature, msg)?.into() - } - SignatureType::Sr25519 => { - recover::(&self.pubkey, signature, msg)?.into() - } - SignatureType::Ecdsa => account_id_from_evm_pubkey(recover::( - &self.pubkey, - signature, - msg, - )?), - SignatureType::Ed25519WrapBytes => { - let wrapped = wrap_bytes(msg); - recover::(&self.pubkey, signature, &wrapped)?.into() - } - SignatureType::Sr25519WrapBytes => { - let wrapped = wrap_bytes(msg); - recover::(&self.pubkey, signature, &wrapped)?.into() - } - SignatureType::EcdsaWrapBytes => { - let wrapped = wrap_bytes(msg); - account_id_from_evm_pubkey(recover::( - &self.pubkey, - signature, - &wrapped, - )?) - } - SignatureType::Eip712 => { - account_id_from_evm_pubkey(eip712::recover(&self.pubkey, signature, msg, msg_type)?) - } - }; - Ok(signer) + recover_signer_account(&self.pubkey, msg, msg_type, sig_type, signature) } } + +pub fn recover_signer_account( + pubkey: &[u8], + msg: &[u8], + msg_type: MessageType, + sig_type: SignatureType, + signature: &[u8], +) -> Result { + use account_id_from_evm_pubkey as evm_account; + let signer = match sig_type { + SignatureType::Ed25519 => recover::(pubkey, signature, msg)?.into(), + SignatureType::Sr25519 => recover::(pubkey, signature, msg)?.into(), + SignatureType::Ecdsa => { + evm_account(recover::(pubkey, signature, msg)?) + } + SignatureType::Ed25519WrapBytes => { + let wrapped = wrap_bytes(msg); + recover::(pubkey, signature, &wrapped)?.into() + } + SignatureType::Sr25519WrapBytes => { + let wrapped = wrap_bytes(msg); + recover::(pubkey, signature, &wrapped)?.into() + } + SignatureType::EcdsaWrapBytes => { + let wrapped = wrap_bytes(msg); + evm_account(recover::( + pubkey, signature, &wrapped, + )?) + } + SignatureType::Eip712 => evm_account(eip712::recover(pubkey, signature, msg, msg_type)?), + }; + Ok(signer) +} diff --git a/crates/phactory/src/lib.rs b/crates/phactory/src/lib.rs index ba84d13f3..b5a3dac18 100644 --- a/crates/phactory/src/lib.rs +++ b/crates/phactory/src/lib.rs @@ -323,6 +323,11 @@ pub struct Phactory { #[serde(skip)] #[serde(default = "sidevm_helper::create_sidevm_service_default")] sidevm_spawner: sidevm::service::Spawner, + + /// The pRuntime measurement that allowed by the Council. + #[codec(skip)] + #[serde(skip)] + allow_handover_to: Option>, } mod sidevm_helper { @@ -409,6 +414,7 @@ impl Phactory { args.cores as _, create_sidevm_outgoing_channel(weak_self), ), + allow_handover_to: None, }; me.init(args); me diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index cac3dfd36..20ea04065 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -1592,24 +1592,7 @@ impl PhactoryApi for Rpc } // 5. verify pruntime launch date, never handover to old pruntime if !dev_mode && in_sgx { - let my_la_report = { - // target_info and reportdata not important, we just need the report metadata - let target_info = - sgx_api_lite::target_info().expect("should not fail in SGX; qed."); - sgx_api_lite::report(&target_info, &[0; 64]) - .map_err(|_| from_display("Cannot read server pRuntime info"))? - }; - let my_runtime_hash = { - let ias_fields = IasFields { - mr_enclave: my_la_report.body.mr_enclave.m, - mr_signer: my_la_report.body.mr_signer.m, - isv_prod_id: my_la_report.body.isv_prod_id.to_ne_bytes(), - isv_svn: my_la_report.body.isv_svn.to_ne_bytes(), - report_data: [0; 64], - confidence_level: 0, - }; - ias_fields.extend_mrenclave() - }; + let my_runtime_hash = my_measurement()?; let runtime_state = phactory.runtime_state()?; let my_runtime_timestamp = runtime_state .chain_storage @@ -1633,13 +1616,15 @@ impl PhactoryApi for Rpc collateral: _, } => todo!(), }; - let req_runtime_timestamp = runtime_state + if let Some(req_runtime_timestamp) = runtime_state .chain_storage .get_pruntime_added_at(&runtime_hash) - .ok_or_else(|| from_display("Client pRuntime not allowed on chain"))?; - - if my_runtime_timestamp >= req_runtime_timestamp { - return Err(from_display("No handover for old pRuntime")); + { + if my_runtime_timestamp >= req_runtime_timestamp { + return Err(from_display("No handover for old pRuntime")); + } + } else if phactory.allow_handover_to != Some(runtime_hash) { + return Err(from_display("Client pRuntime not allowed on chain")); } } else { info!("Skip pRuntime timestamp check in dev mode"); @@ -2046,4 +2031,60 @@ impl PhactoryApi for Rpc cluster.on_idle(block_number); Ok(()) } + + async fn allow_handover_to( + &mut self, + request: pb::AllowHandoverToRequest, + ) -> Result<(), prpc::server::Error> { + let mut phactory = self.lock_phactory(false, true)?; + let runtime_state = phactory.runtime_state()?; + let council_members = runtime_state.chain_storage.council_members(); + let genesis_hash = hex::encode(runtime_state.genesis_block_hash); + let mr_to = hex::encode(&request.measurement); + let mr_from = hex::encode(my_measurement()?); + let signed_message = format!("Allow pRuntime to handover from 0x{mr_from} to 0x{mr_to} on chain of genesis 0x{genesis_hash}").into_bytes(); + for sig in &request.signatures { + let sig_type = pb::SignatureType::from_i32(sig.signature_type) + .ok_or_else(|| from_display("Invalid signature type"))?; + let signer = crypto::recover_signer_account( + &sig.pubkey, + &signed_message, + Default::default(), + sig_type, + &sig.signature, + ) + .map_err(|_| from_display("Invalid signature"))?; + if !council_members.contains(&signer) { + return Err(from_display("Not a council member")); + } + } + let percent = request.signatures.len() * 100 / council_members.len(); + if percent < 50 { + return Err(from_display("Not enough signatures")); + } + phactory.allow_handover_to = Some(request.measurement); + Ok(()) + } +} + +fn my_measurement() -> Result, RpcError> { + let my_la_report = { + // target_info and reportdata not important, we just need the report metadata + let target_info = + sgx_api_lite::target_info().or(Err(from_display("Failed to get SGX info")))?; + sgx_api_lite::report(&target_info, &[0; 64]) + .or(Err(from_display("Cannot read server pRuntime info")))? + }; + let mrenclave = { + let ias_fields = IasFields { + mr_enclave: my_la_report.body.mr_enclave.m, + mr_signer: my_la_report.body.mr_signer.m, + isv_prod_id: my_la_report.body.isv_prod_id.to_ne_bytes(), + isv_svn: my_la_report.body.isv_svn.to_ne_bytes(), + report_data: [0; 64], + confidence_level: 0, + }; + ias_fields.extend_mrenclave() + }; + Ok(mrenclave) } diff --git a/crates/phactory/src/storage.rs b/crates/phactory/src/storage.rs index a47f8ce5f..1d1815c30 100644 --- a/crates/phactory/src/storage.rs +++ b/crates/phactory/src/storage.rs @@ -36,7 +36,7 @@ impl BlockValidator for LightValidation { mod storage_ext { use crate::chain; - use chain::{pallet_computation, pallet_mq, pallet_phat, pallet_registry}; + use chain::{pallet_computation, pallet_mq, pallet_phat, pallet_registry, AccountId}; use phala_mq::{ContractClusterId, Message, MessageOrigin}; use phala_trie_storage::TrieStorage; use phala_types::messaging::TokenomicParameters; @@ -178,5 +178,9 @@ mod storage_ext { ) -> Option { self.execute_with(|| pallet_phat::ClusterByWorkers::::get(worker)) } + + pub(crate) fn council_members(&self) -> Vec { + self.execute_with(chain::Council::members) + } } } diff --git a/standalone/pruntime/src/api_server.rs b/standalone/pruntime/src/api_server.rs index 7107fde40..afd4a991f 100644 --- a/standalone/pruntime/src/api_server.rs +++ b/standalone/pruntime/src/api_server.rs @@ -209,6 +209,7 @@ fn rpc_type(method: &str) -> RpcType { SaveClusterState => Public, LoadClusterState => Private, TryUpgradePinkRuntime => Private, + AllowHandoverTo => Private, }, } } @@ -254,6 +255,7 @@ fn default_payload_limit_for_method(method: PhactoryAPIMethod) -> ByteUnit { SaveClusterState => 1.kibibytes(), LoadClusterState => 1.kibibytes(), TryUpgradePinkRuntime => 1.kibibytes(), + AllowHandoverTo => 64.kibibytes(), } }