diff --git a/crates/phactory/api/proto b/crates/phactory/api/proto index 21d5484f21..e1aa03f135 160000 --- a/crates/phactory/api/proto +++ b/crates/phactory/api/proto @@ -1 +1 @@ -Subproject commit 21d5484f2126cbe0191eeae1d6a9248da57da7d5 +Subproject commit e1aa03f135fbc7f2b8d014dbcca6a41801692446 diff --git a/crates/phactory/pal/src/lib.rs b/crates/phactory/pal/src/lib.rs index 1479eba5f8..fedd6b0dff 100644 --- a/crates/phactory/pal/src/lib.rs +++ b/crates/phactory/pal/src/lib.rs @@ -1,8 +1,8 @@ //! Platform abstraction layer for Trusted Execution Environments +use core::time::Duration; use std::fmt::Debug; use std::path::Path; -use core::time::Duration; use phala_types::AttestationProvider; @@ -11,12 +11,26 @@ pub use phactory_api::prpc::MemoryUsage; pub trait ErrorType: Debug + Into {} impl> ErrorType for T {} +pub struct UnsealedData { + pub data: Vec, + pub svn: Vec, +} + pub trait Sealing { type SealError: ErrorType; type UnsealError: ErrorType; - fn seal_data(&self, path: impl AsRef, data: &[u8]) -> Result<(), Self::SealError>; - fn unseal_data(&self, path: impl AsRef) -> Result>, Self::UnsealError>; + fn current_svn(&self) -> Result, Self::SealError>; + fn seal_data( + &self, + path: impl AsRef, + data: &[u8], + svn: Option<&[u8]>, + ) -> Result<(), Self::SealError>; + fn unseal_data( + &self, + path: impl AsRef, + ) -> Result, Self::UnsealError>; } pub trait RA { diff --git a/crates/phactory/src/lib.rs b/crates/phactory/src/lib.rs index b023701776..94f74c9b78 100644 --- a/crates/phactory/src/lib.rs +++ b/crates/phactory/src/lib.rs @@ -338,14 +338,21 @@ impl Phactory { predefined_identity_key: Option, ) -> Result { let data = if let Some(identity_sk) = predefined_identity_key { - self.save_runtime_data(genesis_block_hash, para_id, identity_sk, false, true)? + self.save_runtime_data(genesis_block_hash, para_id, identity_sk, false, true, None)? } else { match Self::load_runtime_data(&self.platform, &self.args.sealing_path) { Ok(data) => data, Err(Error::PersistentRuntimeNotFound) => { warn!("Persistent data not found."); let identity_sk = new_sr25519_key(); - self.save_runtime_data(genesis_block_hash, para_id, identity_sk, true, false)? + self.save_runtime_data( + genesis_block_hash, + para_id, + identity_sk, + true, + false, + None, + )? } Err(err) => return Err(anyhow!("Failed to load persistent data: {}", err)), } @@ -375,6 +382,7 @@ impl Phactory { sr25519_sk: sr25519::Pair, trusted_sk: bool, dev_mode: bool, + svn: Option<&[u8]>, ) -> Result { // Put in PresistentRuntimeData let sk = sr25519_sk.dump_secret_key(); @@ -392,7 +400,7 @@ impl Phactory { info!("Length of encoded slice: {}", encoded_vec.len()); let filepath = PathBuf::from(&self.args.sealing_path).join(RUNTIME_SEALED_DATA_FILE); self.platform - .seal_data(filepath, &encoded_vec) + .seal_data(filepath, &encoded_vec, svn) .map_err(Into::into) .context("Failed to seal runtime data")?; info!("Persistent Runtime Data V2 saved"); @@ -401,22 +409,31 @@ impl Phactory { } /// Loads the persistent runtime data from the sealing path - fn persistent_runtime_data(&self) -> Result { - Self::load_runtime_data(&self.platform, &self.args.sealing_path) + fn load_persistent_runtime_data_with_svn( + &self, + ) -> Result<(PersistentRuntimeData, Vec), Error> { + Self::load_runtime_data_with_svn(&self.platform, &self.args.sealing_path) } fn load_runtime_data( platform: &Platform, sealing_path: &str, ) -> Result { + Self::load_runtime_data_with_svn(platform, sealing_path).map(|(data, _)| data) + } + + fn load_runtime_data_with_svn( + platform: &Platform, + sealing_path: &str, + ) -> Result<(PersistentRuntimeData, Vec), Error> { let filepath = PathBuf::from(sealing_path).join(RUNTIME_SEALED_DATA_FILE); - let data = platform + let pal::UnsealedData { data, svn } = platform .unseal_data(filepath) .map_err(Into::into)? .ok_or(Error::PersistentRuntimeNotFound)?; let data: RuntimeDataSeal = Decode::decode(&mut &data[..]).map_err(Error::DecodeError)?; match data { - RuntimeDataSeal::V1(data) => Ok(data), + RuntimeDataSeal::V1(data) => Ok((data, svn)), } } diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index de18f7637b..2e3f94c377 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -29,8 +29,9 @@ use phala_pallets::utils::attestation::{validate as validate_attestation_report, use phala_types::contract::contract_id_preimage; use phala_types::{ contract, messaging::EncryptedKey, wrap_content_to_sign, AttestationReport, - ChallengeHandlerInfo, EncryptedWorkerKey, HandoverChallenge, SignedContentType, - VersionedWorkerEndpoints, WorkerEndpointPayload, WorkerPublicKey, WorkerRegistrationInfoV2, + ChallengeHandlerInfo, EncryptedWorkerKeyV0, EncryptedWorkerKeyV1, HandoverChallenge, + SignedContentType, VersionedWorkerEndpoints, WorkerEndpointPayload, WorkerPublicKey, + WorkerRegistrationInfoV2, }; use sp_application_crypto::UncheckedFrom; use tracing::{error, info}; @@ -1637,7 +1638,9 @@ impl PhactoryApi for Rpc // Share the key with attestation let ecdh_pubkey = challenge_handler.ecdh_pubkey; let iv = crate::generate_random_iv(); - let runtime_data = phactory.persistent_runtime_data().map_err(from_display)?; + let (runtime_data, svn) = phactory + .load_persistent_runtime_data_with_svn() + .map_err(from_display)?; let (my_identity_key, _) = runtime_data.decode_keys(); let (ecdh_pubkey, encrypted_key) = key_share::encrypt_secret_to( &my_identity_key, @@ -1652,21 +1655,22 @@ impl PhactoryApi for Rpc encrypted_key, iv, }; - let runtime_state = phactory.runtime_state()?; - let genesis_block_hash = runtime_state.genesis_block_hash; - let encrypted_worker_key = EncryptedWorkerKey { + let genesis_block_hash = runtime_data.genesis_block_hash; + let encrypted_worker_key = EncryptedWorkerKeyV1 { genesis_block_hash, - para_id: runtime_state.para_id, + para_id: runtime_data.para_id, dev_mode, encrypted_key, + svn, }; - let worker_key_hash = sp_core::hashing::blake2_256(&encrypted_worker_key.encode()); + let encoded_worker_key = encrypted_worker_key.encode(); + let payload_hash = sp_core::hashing::blake2_256(&encoded_worker_key); let attestation = if !dev_mode && in_sgx { Some(create_attestation_report_on( &phactory.platform, attestation_provider, - &worker_key_hash, + &payload_hash, phactory.args.ra_timeout, phactory.args.ra_max_retries, )?) @@ -1675,10 +1679,11 @@ impl PhactoryApi for Rpc None }; - Ok(pb::HandoverWorkerKey::new( - encrypted_worker_key, + Ok(pb::HandoverWorkerKey { attestation, - )) + encoded_worker_key_v0: None, + encoded_worker_key_v1: Some(encoded_worker_key), + }) } // WorkerKey Handover Client @@ -1741,12 +1746,37 @@ impl PhactoryApi for Rpc async fn handover_receive(&mut self, request: pb::HandoverWorkerKey) -> RpcResult<()> { let mut phactory = self.lock_phactory(false, true)?; - let encrypted_worker_key = request.decode_worker_key().map_err(from_display)?; - - let dev_mode = encrypted_worker_key.dev_mode; + let received_key; + let payload_hash; + match ( + &request.encoded_worker_key_v0, + &request.encoded_worker_key_v1, + ) { + (None, None) => return Err(from_display("No worker key found")), + (Some(_), Some(_)) => return Err(from_display("Both v0 and v1 worker key found")), + (Some(encoded), None) => { + payload_hash = sp_core::hashing::blake2_256(encoded); + let v0 = EncryptedWorkerKeyV0::decode(&mut &encoded[..]) + .map_err(|_| from_display("Decode worker key failed"))?; + received_key = EncryptedWorkerKeyV1 { + genesis_block_hash: v0.genesis_block_hash, + para_id: v0.para_id, + dev_mode: v0.dev_mode, + encrypted_key: v0.encrypted_key, + // If the version of the key is v0, it must from pRuntime v2.0 or v2.1, which never load the + // keys with a different svn. Thus, we can safely set the svn to current svn. + svn: phactory.platform.current_svn().map_err(from_debug)?, + }; + } + (None, Some(encoded)) => { + payload_hash = sp_core::hashing::blake2_256(encoded); + received_key = EncryptedWorkerKeyV1::decode(&mut &encoded[..]) + .map_err(|_| from_display("Decode worker key failed"))?; + } + } + let dev_mode = received_key.dev_mode; // verify RA report if !dev_mode { - let worker_key_hash = sp_core::hashing::blake2_256(&encrypted_worker_key.encode()); let raw_attestation = request .attestation .ok_or_else(|| from_display("Server attestation not found"))?; @@ -1755,7 +1785,7 @@ impl PhactoryApi for Rpc .map_err(|_| from_display("Decode server attestation failed"))?; validate_attestation_report( attn_to_validate, - &worker_key_hash, + &payload_hash, now(), false, vec![], @@ -1766,7 +1796,7 @@ impl PhactoryApi for Rpc info!("Skip server RA report check for dev mode key"); } - let encrypted_key = encrypted_worker_key.encrypted_key; + let encrypted_key = received_key.encrypted_key; let my_ecdh_key = phactory .handover_ecdh_key .as_ref() @@ -1782,11 +1812,12 @@ impl PhactoryApi for Rpc // only seal if the key is successfully updated phactory .save_runtime_data( - encrypted_worker_key.genesis_block_hash, - encrypted_worker_key.para_id, + received_key.genesis_block_hash, + received_key.para_id, sr25519::Pair::restore_from_secret_key(&secret), false, // we are not sure whether this key is injected dev_mode, + Some(&received_key.svn), ) .map_err(from_display)?; diff --git a/crates/phactory/src/system/master_key.rs b/crates/phactory/src/system/master_key.rs index 061789a014..3ca760bf59 100644 --- a/crates/phactory/src/system/master_key.rs +++ b/crates/phactory/src/system/master_key.rs @@ -7,7 +7,7 @@ use sp_core::sr25519; use phala_crypto::sr25519::{Signature, Signing, Sr25519SecretKey}; -use crate::pal::Sealing; +use crate::pal::{Sealing, UnsealedData}; /// Master key filepath pub const MASTER_KEY_FILE: &str = "master_key.seal"; @@ -66,7 +66,7 @@ pub fn seal( info!("Seal master key to {}", filepath.as_path().display()); // TODO.shelven: seal with identity key so the newly handovered pRuntime do not need to do an extra sync to get master // key - sys.seal_data(filepath, &data.encode()) + sys.seal_data(filepath, &data.encode(), None) .expect("Seal master key failed"); } @@ -84,7 +84,7 @@ pub fn try_unseal( ) -> Vec { let filepath = master_key_file_path(&sealing_path); info!("Unseal master key from {}", filepath.as_path().display()); - let sealed_data = match sys + let UnsealedData { data, .. } = match sys .unseal_data(&filepath) .expect("Unseal master key failed") { @@ -96,7 +96,7 @@ pub fn try_unseal( }; let versioned_data = - MasterKeySeal::decode(&mut &sealed_data[..]).expect("Failed to decode sealed master key"); + MasterKeySeal::decode(&mut &data[..]).expect("Failed to decode sealed master key"); #[allow(clippy::infallible_destructuring_match)] let secrets = match versioned_data { diff --git a/crates/phala-types/src/lib.rs b/crates/phala-types/src/lib.rs index f922e3714b..4586c0a548 100644 --- a/crates/phala-types/src/lib.rs +++ b/crates/phala-types/src/lib.rs @@ -530,13 +530,22 @@ pub struct ChallengeHandlerInfo { } #[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)] -pub struct EncryptedWorkerKey { +pub struct EncryptedWorkerKeyV0 { pub genesis_block_hash: H256, pub para_id: u32, pub dev_mode: bool, pub encrypted_key: messaging::EncryptedKey, } +#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)] +pub struct EncryptedWorkerKeyV1 { + pub genesis_block_hash: H256, + pub para_id: u32, + pub dev_mode: bool, + pub encrypted_key: messaging::EncryptedKey, + pub svn: Vec, +} + #[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)] pub struct WorkerRegistrationInfo { pub version: u32, diff --git a/standalone/pruntime/src/pal_gramine.rs b/standalone/pruntime/src/pal_gramine.rs index 9368f52bbf..e5c1b30443 100644 --- a/standalone/pruntime/src/pal_gramine.rs +++ b/standalone/pruntime/src/pal_gramine.rs @@ -3,7 +3,9 @@ use parity_scale_codec::{Decode, Encode}; use std::alloc::System; use tracing::info; -use phactory_pal::{AppInfo, AppVersion, Machine, MemoryStats, MemoryUsage, Sealing, RA}; +use phactory_pal::{ + AppInfo, AppVersion, Machine, MemoryStats, MemoryUsage, Sealing, UnsealedData, RA, +}; use phala_allocator::StatSizeAllocator; use std::io::ErrorKind; use std::str::FromStr as _; @@ -17,9 +19,14 @@ use phala_types::AttestationProvider; pub(crate) struct GraminePlatform; #[derive(Encode, Decode)] -struct SealedData { +struct Svn { isvsvn: u16, cpusvn: [u8; 16], +} + +#[derive(Encode, Decode)] +struct SealedData { + svn: Svn, iv: [u8; 12], data: Vec, } @@ -34,28 +41,27 @@ fn get_sealing_key(isvsvn: u16, cpusvn: [u8; 16]) -> anyhow::Result<[u8; 32]> { Ok(key256) } -fn sgx_seal_data(data: &[u8]) -> anyhow::Result { +fn sgx_current_svn() -> anyhow::Result { let this_target_info = sgx_api_lite::target_info().or(Err(anyhow!("Failed to get target info")))?; let report = sgx_api_lite::report(&this_target_info, &[0; 64]) .or(Err(anyhow!("Failed to get SGX report")))?; let isvsvn = report.body.isv_svn; let cpusvn = report.body.cpu_svn.svn; - let key = get_sealing_key(isvsvn, cpusvn) - .or(Err(anyhow!("Failed to get sealing key")))?; + Ok(Svn { isvsvn, cpusvn }) +} + +fn sgx_seal_data(data: &[u8], svn: Svn) -> anyhow::Result { + let key = + get_sealing_key(svn.isvsvn, svn.cpusvn).or(Err(anyhow!("Failed to get sealing key")))?; let iv = phactory::generate_random_iv(); let mut data = data.to_vec(); phala_crypto::aead::encrypt(&iv, &key, &mut data).or(Err(anyhow!("Failed to encrypt data")))?; - Ok(SealedData { - isvsvn, - cpusvn, - iv, - data, - }) + Ok(SealedData { svn, iv, data }) } fn sgx_unseal_data(data: &SealedData) -> anyhow::Result> { - let key = get_sealing_key(data.isvsvn, data.cpusvn) + let key = get_sealing_key(data.svn.isvsvn, data.svn.cpusvn) .or(Err(anyhow!("Failed to get sealing key")))?; let mut enccypted_data = data.data.clone(); let decrypted = phala_crypto::aead::decrypt(&data.iv, &key, &mut enccypted_data[..]) @@ -71,13 +77,18 @@ impl Sealing for GraminePlatform { &self, path: impl AsRef, data: &[u8], + svn: Option<&[u8]>, ) -> Result<(), Self::SealError> { if !is_gramine() { std::fs::write(path, data)?; return Ok(()); } info!("Sealing data to {:?}", path.as_ref()); - let data = sgx_seal_data(data)?; + let svn = match svn { + Some(svn) => Svn::decode(&mut &svn[..])?, + None => sgx_current_svn()?, + }; + let data = sgx_seal_data(data, svn)?; let encoded = data.encode(); std::fs::write(path, encoded)?; Ok(()) @@ -86,20 +97,27 @@ impl Sealing for GraminePlatform { fn unseal_data( &self, path: impl AsRef, - ) -> Result>, Self::UnsealError> { + ) -> Result, Self::UnsealError> { match std::fs::read(path) { Err(err) if matches!(err.kind(), ErrorKind::NotFound) => Ok(None), Ok(data) => { if !is_gramine() { - return Ok(Some(data)); + return Ok(Some(UnsealedData { data, svn: vec![] })); } - let data = SealedData::decode(&mut &data[..])?; - let data = sgx_unseal_data(&data)?; - Ok(Some(data)) + let sealed_data = SealedData::decode(&mut &data[..])?; + let data = sgx_unseal_data(&sealed_data)?; + Ok(Some(UnsealedData { + data, + svn: sealed_data.svn.encode(), + })) } Err(err) => Err(err.into()), } } + + fn current_svn(&self) -> Result, Self::SealError> { + Ok(sgx_current_svn()?.encode()) + } } impl RA for GraminePlatform {