From 13266bca61e2e9fd1c81f99589fa96c56a13f09e Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 26 Dec 2023 11:20:48 +0000 Subject: [PATCH 01/10] Add pRPC AllowHandoverTo --- Cargo.lock | 3 + crates/phactory/api/Cargo.toml | 1 + crates/phactory/api/build.rs | 7 +++ crates/phactory/api/proto | 2 +- crates/phactory/api/src/crypto.rs | 91 ++++++++++++++------------- crates/phactory/src/lib.rs | 6 ++ crates/phactory/src/prpc_service.rs | 87 ++++++++++++++++++------- crates/phactory/src/storage.rs | 6 +- standalone/pruntime/src/api_server.rs | 2 + 9 files changed, 138 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae79aa9f13..d64858c0d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5466,6 +5466,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" diff --git a/crates/phactory/api/Cargo.toml b/crates/phactory/api/Cargo.toml index 5400ae5593..9f6874aa9b 100644 --- a/crates/phactory/api/Cargo.toml +++ b/crates/phactory/api/Cargo.toml @@ -36,6 +36,7 @@ ethers = "2.0.8" hex-literal = "0.4.1" secp256k1 = "0.28.0" +hex = { version = "0.4", default-features = false, features = ["alloc", "serde"] } [dev-dependencies] insta = "1.13.0" diff --git a/crates/phactory/api/build.rs b/crates/phactory/api/build.rs index 8382217f5f..8c07d6f5d4 100644 --- a/crates/phactory/api/build.rs +++ b/crates/phactory/api/build.rs @@ -41,6 +41,13 @@ fn main() { ] { builder = builder.field_attribute(field, "#[serde(default)]"); } + for field in [ + "AllowHandoverToRequest.measurement", + "SigInfo.pubkey", + "SigInfo.signature", + ] { + builder = builder.field_attribute(field, "#[serde(with=\"hex::serde\")]"); + } builder .compile(&["pruntime_rpc.proto"], &[render_dir]) .unwrap(); diff --git a/crates/phactory/api/proto b/crates/phactory/api/proto index ac7fc9f973..d84798eeb9 160000 --- a/crates/phactory/api/proto +++ b/crates/phactory/api/proto @@ -1 +1 @@ -Subproject commit ac7fc9f97368ffe0565b59a78d35f96a68516ef9 +Subproject commit d84798eeb9c7c69495bccd9c67b041b8c18a2d43 diff --git a/crates/phactory/api/src/crypto.rs b/crates/phactory/api/src/crypto.rs index 5c8f3fbc26..a92069e505 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,49 +218,54 @@ 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 => sp_core::blake2_256( - recover::(&self.pubkey, signature, msg)?.as_ref(), + 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 => sp_core::blake2_256( + recover::(pubkey, signature, msg)?.as_ref(), + ) + .into(), + 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); + sp_core::blake2_256( + recover::(pubkey, signature, &wrapped)?.as_ref(), ) - .into(), - 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); - sp_core::blake2_256( - recover::(&self.pubkey, signature, &wrapped)?.as_ref(), - ) - .into() - } - SignatureType::Eip712 => { - account_id_from_evm_pubkey(eip712::recover(&self.pubkey, signature, msg, msg_type)?) - } - SignatureType::EvmEcdsa => account_id_from_evm_pubkey(recover::( + .into() + } + SignatureType::Eip712 => evm_account(eip712::recover(pubkey, signature, msg, msg_type)?), + SignatureType::EvmEcdsa => evm_account(recover::( + &self.pubkey, + signature, + msg, + )?), + SignatureType::EvmEcdsaWrapBytes => { + let wrapped = wrap_bytes(msg); + evm_account(recover::( &self.pubkey, signature, - msg, - )?), - SignatureType::EvmEcdsaWrapBytes => { - let wrapped = wrap_bytes(msg); - account_id_from_evm_pubkey(recover::( - &self.pubkey, - signature, - &wrapped, - )?) - } - }; - Ok(signer) - } + &wrapped, + )?) + } + }; + Ok(signer) } diff --git a/crates/phactory/src/lib.rs b/crates/phactory/src/lib.rs index ba84d13f31..b5a3dac183 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 cac3dfd364..20ea040656 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 a47f8ce5fb..1d1815c302 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 7107fde40d..afd4a991ff 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(), } } From e71c7f7627e0d940ea24698aa1a6919250ec06e4 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 27 Dec 2023 12:06:31 +0800 Subject: [PATCH 02/10] Print pruntime mr --- standalone/pruntime/Cargo.lock | 4 ++++ standalone/pruntime/gramine-build/.gitignore | 1 + standalone/pruntime/gramine-build/Makefile | 3 ++- .../pruntime/gramine-build/parse-sigs.py | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100755 standalone/pruntime/gramine-build/parse-sigs.py diff --git a/standalone/pruntime/Cargo.lock b/standalone/pruntime/Cargo.lock index fe0874aea1..c773051ac0 100644 --- a/standalone/pruntime/Cargo.lock +++ b/standalone/pruntime/Cargo.lock @@ -3391,6 +3391,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -5917,6 +5920,7 @@ dependencies = [ "derive_more", "ethers", "frame-system", + "hex", "hex-literal", "im", "log", diff --git a/standalone/pruntime/gramine-build/.gitignore b/standalone/pruntime/gramine-build/.gitignore index 7f29bc21a2..430b6fc87c 100644 --- a/standalone/pruntime/gramine-build/.gitignore +++ b/standalone/pruntime/gramine-build/.gitignore @@ -4,6 +4,7 @@ /pruntime.manifest /pruntime.manifest.sgx /pruntime.sig +/pruntime.sig.txt /pruntime.token /bin /data diff --git a/standalone/pruntime/gramine-build/Makefile b/standalone/pruntime/gramine-build/Makefile index 878a833d6d..053052b11b 100644 --- a/standalone/pruntime/gramine-build/Makefile +++ b/standalone/pruntime/gramine-build/Makefile @@ -142,7 +142,8 @@ dist: pre-dist ${BIN_NAME}.manifest signed-sgx-artifacts cp ${BIN_NAME}.manifest.sgx ${PREFIX}/ cp ${BIN_NAME}.sig ${PREFIX}/ cp gramine-sgx ${PREFIX}/ - gramine-sgx-sigstruct-view ${BIN_NAME}.sig + gramine-sgx-sigstruct-view ${BIN_NAME}.sig | ./parse-sigs.py | tee ${BIN_NAME}.sig.txt + cp ${BIN_NAME}.sig.txt ${PREFIX}/ else dist: pre-dist ${BIN_NAME}.manifest cp ${BIN_NAME}.manifest ${PREFIX}/ diff --git a/standalone/pruntime/gramine-build/parse-sigs.py b/standalone/pruntime/gramine-build/parse-sigs.py new file mode 100755 index 0000000000..8c57988435 --- /dev/null +++ b/standalone/pruntime/gramine-build/parse-sigs.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +import sys +import codecs + +attrs = {} +for line in sys.stdin: + print(line.rstrip()) + key, value = line.split(':', 1) + attrs[key.strip()] = value.strip() + +def hex16(n): + n = int(n) + l = n & 0xff + h = (n > 8 )& 0xff + return codecs.encode(bytes([l, h]), 'hex').decode() + +measurement = attrs['mr_enclave'] + hex16(attrs['isv_prod_id']) + hex16(attrs['isv_svn']) + attrs['mr_signer'] +print("pruntime_finger_print: " + measurement) From 6f5b5f004b3681383a0ec6fb5c37e48108055d76 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 27 Dec 2023 18:30:42 +0800 Subject: [PATCH 03/10] Add some logs --- crates/phactory/src/prpc_service.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 20ea040656..61facad907 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -389,7 +389,7 @@ impl Phactory let chain_storage = ChainStorage::from_pairs(genesis_state.into_iter()); let para_id = chain_storage.para_id(); info!( - "Genesis state loaded: root={:?}, para_id={para_id}", + "Genesis state loaded: root={:?}, para_id={para_id}, genesis_hash={genesis_block_hash:?}", chain_storage.root() ); @@ -2043,6 +2043,7 @@ impl PhactoryApi for Rpc 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(); + debug!("Signed message : {:?}", hex::encode(&signed_message)); for sig in &request.signatures { let sig_type = pb::SignatureType::from_i32(sig.signature_type) .ok_or_else(|| from_display("Invalid signature type"))?; @@ -2053,7 +2054,7 @@ impl PhactoryApi for Rpc sig_type, &sig.signature, ) - .map_err(|_| from_display("Invalid signature"))?; + .map_err(|_| from_display(format!("Invalid signature of {:?}", &sig.pubkey)))?; if !council_members.contains(&signer) { return Err(from_display("Not a council member")); } From a7021dadeed880d01e58ff7cf4ff400e9e3da399 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 27 Dec 2023 19:00:45 +0800 Subject: [PATCH 04/10] Add council handover doc --- docs/pruntime-handover-by-council.md | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/pruntime-handover-by-council.md diff --git a/docs/pruntime-handover-by-council.md b/docs/pruntime-handover-by-council.md new file mode 100644 index 0000000000..90296be64c --- /dev/null +++ b/docs/pruntime-handover-by-council.md @@ -0,0 +1,41 @@ +PR https://github.com/Phala-Network/phala-blockchain/pull/1500 + +Suppose we have two version of pRuntime A and B, where A is stucked, and we want to force handover to B. + +SGX MR of A: 0x10c24c0e6bf8a86634417fcd8f934e62439c62907a6f1bc726906a50b054ddf10000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e +SGX MR of B: 0xf42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e +Genisis block hash: 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded (This can be get in the log of pruntime) + +Then the workflow would be: + +1. Ask at least half of the council members to sign a message as below: +``` +Allow pRuntime to handover from 0x10c24c0e6bf8a86634417fcd8f934e62439c62907a6f1bc726906a50b054ddf10000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e to 0xf42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e on chain of genesis 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded +``` + See https://files.kvin.wang:8443/signit/ for an example + +2. Collect the signatures and assamble them into a rpc request like this: + ``` + $ cat sigs.json + { + "measurement": "f42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e", + "signatures": [ + { + "signature": "fe6eeb25c088975df9bd136cc29c01a1b0bec3c4a58027efd7ca2908b233983c908a7159b81e265948a45e2c9129560f96aef24b93612f1dd4fc9aa40880ff88", + "signature_type": 4, + "pubkey": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" + }, + { + "signature": "22591a9f308e9d1a2af2ad103334cf8ab3674a2dab9e9a6372cf1e09c8671066668ed90af1c88ad7c5c280b8e5dfb043402774cf59e38d312ee107bd8aee2f8c", + "signature_type": 4, + "pubkey": "8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48" + } + ] + } + ``` +3. Load the sigs.json to pruntime A + ``` + $ curl -d @sigs.json localhost:8000/prpc/PhactoryAPI.AllowHandoverTo?json + ``` +4. Run a new pruntime B instance to start the handover + `$ ./gramine-sgx pruntime --request-handover-from http://localhost:8000` \ No newline at end of file From 545ad566668362845c356617c9bdf4b32ff0fef2 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 27 Dec 2023 19:18:40 +0800 Subject: [PATCH 05/10] Fix counting signatures --- crates/phactory/src/prpc_service.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 61facad907..a1e5fa3f33 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -2044,6 +2044,7 @@ impl PhactoryApi for Rpc 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(); debug!("Signed message : {:?}", hex::encode(&signed_message)); + let mut indivaduals = std::collections::BTreeSet::new(); for sig in &request.signatures { let sig_type = pb::SignatureType::from_i32(sig.signature_type) .ok_or_else(|| from_display("Invalid signature type"))?; @@ -2058,8 +2059,9 @@ impl PhactoryApi for Rpc if !council_members.contains(&signer) { return Err(from_display("Not a council member")); } + indivaduals.insert(signer); } - let percent = request.signatures.len() * 100 / council_members.len(); + let percent = indivaduals.len() * 100 / council_members.len(); if percent < 50 { return Err(from_display("Not enough signatures")); } From 9ef32d1b5efd717fd3b769f348a9e0d2ef1fd1bd Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 27 Dec 2023 19:42:15 +0800 Subject: [PATCH 06/10] Add log --- crates/phactory/src/prpc_service.rs | 14 +++++++++----- docs/pruntime-handover-by-council.md | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index a1e5fa3f33..043a8d0ba4 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -2039,12 +2039,15 @@ impl PhactoryApi for Rpc let mut phactory = self.lock_phactory(false, true)?; let runtime_state = phactory.runtime_state()?; let council_members = runtime_state.chain_storage.council_members(); + if request.signatures.len() > council_members.len() { + return Err(from_display("Too many signatures")); + } 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(); - debug!("Signed message : {:?}", hex::encode(&signed_message)); - let mut indivaduals = std::collections::BTreeSet::new(); + debug!("Signed message: {:?}", hex::encode(&signed_message)); + let mut signers = std::collections::BTreeSet::new(); for sig in &request.signatures { let sig_type = pb::SignatureType::from_i32(sig.signature_type) .ok_or_else(|| from_display("Invalid signature type"))?; @@ -2055,13 +2058,14 @@ impl PhactoryApi for Rpc sig_type, &sig.signature, ) - .map_err(|_| from_display(format!("Invalid signature of {:?}", &sig.pubkey)))?; + .map_err(|_| from_display("Invalid signature"))?; if !council_members.contains(&signer) { return Err(from_display("Not a council member")); } - indivaduals.insert(signer); + debug!("Signed by {signer:?}"); + signers.insert(signer); } - let percent = indivaduals.len() * 100 / council_members.len(); + let percent = signers.len() * 100 / council_members.len(); if percent < 50 { return Err(from_display("Not enough signatures")); } diff --git a/docs/pruntime-handover-by-council.md b/docs/pruntime-handover-by-council.md index 90296be64c..d026be4166 100644 --- a/docs/pruntime-handover-by-council.md +++ b/docs/pruntime-handover-by-council.md @@ -4,9 +4,9 @@ Suppose we have two version of pRuntime A and B, where A is stucked, and we want SGX MR of A: 0x10c24c0e6bf8a86634417fcd8f934e62439c62907a6f1bc726906a50b054ddf10000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e SGX MR of B: 0xf42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e -Genisis block hash: 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded (This can be get in the log of pruntime) +Genisis block hash: 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded (Can be found in prpc::get_info) -Then the workflow would be: +Then the steps would be: 1. Ask at least half of the council members to sign a message as below: ``` From 46c117ab031c7a4eaf8d0275717082c2675c6022 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 28 Dec 2023 10:02:36 +0800 Subject: [PATCH 07/10] Add line break in council signed message --- crates/phactory/src/prpc_service.rs | 2 +- docs/pruntime-handover-by-council.md | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 043a8d0ba4..7148a9c13b 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -2045,7 +2045,7 @@ impl PhactoryApi for Rpc 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(); + let signed_message = format!("Allow pRuntime to handover\n from: 0x{mr_from}\n to: 0x{mr_to}\n genesis: 0x{genesis_hash}").into_bytes(); debug!("Signed message: {:?}", hex::encode(&signed_message)); let mut signers = std::collections::BTreeSet::new(); for sig in &request.signatures { diff --git a/docs/pruntime-handover-by-council.md b/docs/pruntime-handover-by-council.md index d026be4166..4d56f83c45 100644 --- a/docs/pruntime-handover-by-council.md +++ b/docs/pruntime-handover-by-council.md @@ -10,7 +10,10 @@ Then the steps would be: 1. Ask at least half of the council members to sign a message as below: ``` -Allow pRuntime to handover from 0x10c24c0e6bf8a86634417fcd8f934e62439c62907a6f1bc726906a50b054ddf10000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e to 0xf42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e on chain of genesis 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded +Allow pRuntime to handover + from: 0x10c24c0e6bf8a86634417fcd8f934e62439c62907a6f1bc726906a50b054ddf10000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e + to: 0xf42f7e095735702d1d3c6ac5fa3b4581d3c3673d3c5ce261a43fe782ccb3e1dc0000000083d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e + genesis: 0x0a15d23307d533d581291ff6dedca9ca10927c7dff6f4df9e8c3bf00bc5a6ded ``` See https://files.kvin.wang:8443/signit/ for an example From c36aaa5ce9cf2c9f36bdb66f86c8146d4ec95ddc Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Thu, 4 Jan 2024 08:03:48 +0000 Subject: [PATCH 08/10] Fix compile error --- crates/phactory/api/src/crypto.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/phactory/api/src/crypto.rs b/crates/phactory/api/src/crypto.rs index a92069e505..464f364f28 100644 --- a/crates/phactory/api/src/crypto.rs +++ b/crates/phactory/api/src/crypto.rs @@ -233,10 +233,10 @@ pub fn recover_signer_account( let signer = match sig_type { SignatureType::Ed25519 => recover::(pubkey, signature, msg)?.into(), SignatureType::Sr25519 => recover::(pubkey, signature, msg)?.into(), - SignatureType::Ecdsa => sp_core::blake2_256( - recover::(pubkey, signature, msg)?.as_ref(), - ) - .into(), + SignatureType::Ecdsa => { + sp_core::blake2_256(recover::(pubkey, signature, msg)?.as_ref()) + .into() + } SignatureType::Ed25519WrapBytes => { let wrapped = wrap_bytes(msg); recover::(pubkey, signature, &wrapped)?.into() @@ -253,17 +253,13 @@ pub fn recover_signer_account( .into() } SignatureType::Eip712 => evm_account(eip712::recover(pubkey, signature, msg, msg_type)?), - SignatureType::EvmEcdsa => evm_account(recover::( - &self.pubkey, - signature, - msg, - )?), + SignatureType::EvmEcdsa => { + evm_account(recover::(pubkey, signature, msg)?) + } SignatureType::EvmEcdsaWrapBytes => { let wrapped = wrap_bytes(msg); evm_account(recover::( - &self.pubkey, - signature, - &wrapped, + pubkey, signature, &wrapped, )?) } }; From 050dcb95b212aa8fce19d41e31c29dcb0d2b2c6e Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 5 Jan 2024 12:17:54 +0800 Subject: [PATCH 09/10] More restricted council handover conditions --- crates/phactory/src/prpc_service.rs | 7 ++++++- docs/pruntime-handover-by-council.md | 1 + pallets/phala/src/utils/attestation.rs | 2 +- standalone/pruntime/Cargo.lock | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 7148a9c13b..34bc7c2ed7 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -263,6 +263,9 @@ impl Phactory ), "dispatch_block", ); + + self.allow_handover_to = None; + let counters = self.runtime_state()?.storage_synchronizer.counters(); blocks.retain(|b| b.block_header.number >= counters.next_block_number); @@ -2066,7 +2069,9 @@ impl PhactoryApi for Rpc signers.insert(signer); } let percent = signers.len() * 100 / council_members.len(); - if percent < 50 { + // At least 7 of 8 members. 6/8 = 75%, 7/8 = 87.5%. + let threshold = 80; + if percent < threshold { return Err(from_display("Not enough signatures")); } phactory.allow_handover_to = Some(request.measurement); diff --git a/docs/pruntime-handover-by-council.md b/docs/pruntime-handover-by-council.md index 4d56f83c45..111618d738 100644 --- a/docs/pruntime-handover-by-council.md +++ b/docs/pruntime-handover-by-council.md @@ -40,5 +40,6 @@ Allow pRuntime to handover ``` $ curl -d @sigs.json localhost:8000/prpc/PhactoryAPI.AllowHandoverTo?json ``` + **Note**: Don't sync any chain state to the pruntime A after this step, otherwise the handover will be rejected. 4. Run a new pruntime B instance to start the handover `$ ./gramine-sgx pruntime --request-handover-from http://localhost:8000` \ No newline at end of file diff --git a/pallets/phala/src/utils/attestation.rs b/pallets/phala/src/utils/attestation.rs index 2ad60f9a0c..0a68b7aaeb 100644 --- a/pallets/phala/src/utils/attestation.rs +++ b/pallets/phala/src/utils/attestation.rs @@ -185,7 +185,7 @@ pub fn validate_ias_report( } // Validate time - if (now as i64 - report_timestamp) >= 7200 { + if (now as i64 - report_timestamp).abs() >= 7200 { return Err(Error::OutdatedIASReport); } diff --git a/standalone/pruntime/Cargo.lock b/standalone/pruntime/Cargo.lock index c773051ac0..a26681c1d5 100644 --- a/standalone/pruntime/Cargo.lock +++ b/standalone/pruntime/Cargo.lock @@ -5935,6 +5935,7 @@ dependencies = [ "prpc-build", "reqwest", "scale-info", + "secp256k1 0.28.0", "serde", "serde_json", "sp-consensus-grandpa", From d1b76dc3bf2c4762eee4fc521f75250841635606 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 5 Jan 2024 16:49:11 +0800 Subject: [PATCH 10/10] Loose invalid block time --- pallets/phala/src/utils/attestation.rs | 31 ++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pallets/phala/src/utils/attestation.rs b/pallets/phala/src/utils/attestation.rs index 0a68b7aaeb..01d809e590 100644 --- a/pallets/phala/src/utils/attestation.rs +++ b/pallets/phala/src/utils/attestation.rs @@ -185,7 +185,11 @@ pub fn validate_ias_report( } // Validate time - if (now as i64 - report_timestamp).abs() >= 7200 { + if (now as i64 - report_timestamp) >= 7200 { + return Err(Error::OutdatedIASReport); + } + + if (report_timestamp - now as i64) >= 3600 * 24 * 7 { return Err(Error::OutdatedIASReport); } @@ -269,7 +273,7 @@ mod test { use frame_support::assert_ok; pub const ATTESTATION_SAMPLE: &[u8] = include_bytes!("../../sample/ias_attestation.json"); - pub const ATTESTATION_TIMESTAMP: u64 = 1631441180; // 2021-09-12T18:06:20.402478 + pub const ATTESTATION_TIMESTAMP: u64 = 1631469980; // 2021-09-12T18:06:20.402478 pub const PRUNTIME_HASH: &str = "518422fa769d2d55982015a0e0417c6a8521fdfc7308f5ec18aaa1b6924bd0f300000000815f42f11cf64430c30bab7816ba596a1da0130c3b028b673133a66cf9a3e0e6"; #[test] @@ -311,6 +315,29 @@ mod test { Err(Error::OutdatedIASReport) ); + assert_ok!(validate_ias_report( + commit, + report, + &signature, + &raw_signing_cert, + ATTESTATION_TIMESTAMP - 3600*24*7 + 1, + false, + vec![] + )); + + assert_eq!( + validate_ias_report( + commit, + report, + &signature, + &raw_signing_cert, + ATTESTATION_TIMESTAMP - 3600*24*7, + false, + vec![] + ), + Err(Error::OutdatedIASReport) + ); + assert_eq!( validate_ias_report( commit,