Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pRuntime: Manually encrypted worker privkey and gk master key #1370

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/phactory/api/proto
Submodule proto updated 1 files
+4 −2 pruntime_rpc.proto
20 changes: 17 additions & 3 deletions crates/phactory/pal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -11,12 +11,26 @@ pub use phactory_api::prpc::MemoryUsage;
pub trait ErrorType: Debug + Into<anyhow::Error> {}
impl<T: Debug + Into<anyhow::Error>> ErrorType for T {}

pub struct UnsealedData {
pub data: Vec<u8>,
pub svn: Vec<u8>,
}

pub trait Sealing {
type SealError: ErrorType;
type UnsealError: ErrorType;

fn seal_data(&self, path: impl AsRef<Path>, data: &[u8]) -> Result<(), Self::SealError>;
fn unseal_data(&self, path: impl AsRef<Path>) -> Result<Option<Vec<u8>>, Self::UnsealError>;
fn current_svn(&self) -> Result<Vec<u8>, Self::SealError>;
fn seal_data(
&self,
path: impl AsRef<Path>,
data: &[u8],
svn: Option<&[u8]>,
) -> Result<(), Self::SealError>;
fn unseal_data(
&self,
path: impl AsRef<Path>,
) -> Result<Option<UnsealedData>, Self::UnsealError>;
}

pub trait RA {
Expand Down
33 changes: 25 additions & 8 deletions crates/phactory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,21 @@ impl<Platform: pal::Platform> Phactory<Platform> {
predefined_identity_key: Option<sr25519::Pair>,
) -> Result<PersistentRuntimeData> {
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)),
}
Expand Down Expand Up @@ -375,6 +382,7 @@ impl<Platform: pal::Platform> Phactory<Platform> {
sr25519_sk: sr25519::Pair,
trusted_sk: bool,
dev_mode: bool,
svn: Option<&[u8]>,
) -> Result<PersistentRuntimeData> {
// Put in PresistentRuntimeData
let sk = sr25519_sk.dump_secret_key();
Expand All @@ -392,7 +400,7 @@ impl<Platform: pal::Platform> Phactory<Platform> {
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");
Expand All @@ -401,22 +409,31 @@ impl<Platform: pal::Platform> Phactory<Platform> {
}

/// Loads the persistent runtime data from the sealing path
fn persistent_runtime_data(&self) -> Result<PersistentRuntimeData, Error> {
Self::load_runtime_data(&self.platform, &self.args.sealing_path)
fn load_persistent_runtime_data_with_svn(
&self,
) -> Result<(PersistentRuntimeData, Vec<u8>), Error> {
Self::load_runtime_data_with_svn(&self.platform, &self.args.sealing_path)
}

fn load_runtime_data(
platform: &Platform,
sealing_path: &str,
) -> Result<PersistentRuntimeData, Error> {
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<u8>), 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)),
}
}

Expand Down Expand Up @@ -840,7 +857,7 @@ fn new_sr25519_key() -> sr25519::Pair {
}

// TODO.kevin: Move to phactory-api when the std ready.
fn generate_random_iv() -> aead::IV {
pub fn generate_random_iv() -> aead::IV {
let mut nonce_vec = [0u8; aead::IV_BYTES];
let rand = ring::rand::SystemRandom::new();
rand.fill(&mut nonce_vec).unwrap();
Expand Down
71 changes: 51 additions & 20 deletions crates/phactory/src/prpc_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -1637,7 +1638,9 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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,
Expand All @@ -1652,21 +1655,22 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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,
)?)
Expand All @@ -1675,10 +1679,11 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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
Expand Down Expand Up @@ -1741,12 +1746,37 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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"))?;
Expand All @@ -1755,7 +1785,7 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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![],
Expand All @@ -1766,7 +1796,7 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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()
Expand All @@ -1782,11 +1812,12 @@ impl<Platform: pal::Platform + Serialize + DeserializeOwned> 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)?;

Expand Down
8 changes: 4 additions & 4 deletions crates/phactory/src/system/master_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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");
}

Expand All @@ -84,7 +84,7 @@ pub fn try_unseal(
) -> Vec<RotatedMasterKey> {
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")
{
Expand All @@ -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 {
Expand Down
11 changes: 10 additions & 1 deletion crates/phala-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,13 +530,22 @@ pub struct ChallengeHandlerInfo<BlockNumber> {
}

#[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<u8>,
}

#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)]
pub struct WorkerRegistrationInfo<AccountId> {
pub version: u32,
Expand Down
1 change: 1 addition & 0 deletions crates/sgx-api-lite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
cmac = "0.7.1"
aes = "0.8.1"
bitflags = "2"
Loading