From 08d2ff6238810f62ee3dde779fd34c54bba7b93a Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 25 Jun 2023 02:31:57 +0000 Subject: [PATCH 1/2] pruntime: Configurable RA timeout --- Cargo.lock | 5 ++ Cargo.toml | 1 + crates/phactory/api/src/ecall_args.rs | 7 ++- crates/phactory/pal/src/lib.rs | 2 + crates/phactory/src/prpc_service.rs | 47 +++++++---------- crates/phala-clap-parsers/Cargo.toml | 10 ++++ crates/phala-clap-parsers/src/duration.rs | 63 +++++++++++++++++++++++ crates/phala-clap-parsers/src/lib.rs | 3 ++ standalone/phat-poller/Cargo.toml | 1 + standalone/phat-poller/src/args.rs | 61 +--------------------- standalone/pruntime/Cargo.lock | 5 ++ standalone/pruntime/Cargo.toml | 1 + standalone/pruntime/src/ias.rs | 17 ++++-- standalone/pruntime/src/main.rs | 7 +++ standalone/pruntime/src/pal_gramine.rs | 14 +++-- 15 files changed, 147 insertions(+), 97 deletions(-) create mode 100644 crates/phala-clap-parsers/Cargo.toml create mode 100644 crates/phala-clap-parsers/src/duration.rs create mode 100644 crates/phala-clap-parsers/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2f48cf1b05..0b717e3082 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7905,6 +7905,10 @@ dependencies = [ "futures", ] +[[package]] +name = "phala-clap-parsers" +version = "0.1.0" + [[package]] name = "phala-crypto" version = "0.1.0" @@ -8311,6 +8315,7 @@ dependencies = [ "pallet-contracts-primitives", "parity-scale-codec", "phactory-api", + "phala-clap-parsers", "phala-crypto", "phala-types", "phaxt", diff --git a/Cargo.toml b/Cargo.toml index a5484a2bb2..9cb2e88d0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ members = [ "crates/phala-allocator", "crates/phala-sanitized-logger", "crates/phala-wasm-checker", + "crates/phala-clap-parsers", "crates/wasmer-tunables", "crates/phala-rocket-middleware", "crates/pink/runner", diff --git a/crates/phactory/api/src/ecall_args.rs b/crates/phactory/api/src/ecall_args.rs index e2e2b9de47..dbaead1b20 100644 --- a/crates/phactory/api/src/ecall_args.rs +++ b/crates/phactory/api/src/ecall_args.rs @@ -1,8 +1,8 @@ use alloc::string::String; use parity_scale_codec::{Decode, Encode}; -use serde::{Deserialize, Serialize}; +use core::time::Duration; -#[derive(Serialize, Deserialize, Debug, Encode, Decode, Default, Clone)] +#[derive(Debug, Encode, Decode, Default, Clone)] pub struct InitArgs { /// The GK master key sealing path. pub sealing_path: String, @@ -42,6 +42,9 @@ pub struct InitArgs { /// Disable the RCU policy to update the Phactory state. pub no_rcu: bool, + + /// The timeout of getting the attestation report. + pub ra_timeout: Duration, } pub use phala_git_revision::git_revision; diff --git a/crates/phactory/pal/src/lib.rs b/crates/phactory/pal/src/lib.rs index 5fc7aac7f5..1479eba5f8 100644 --- a/crates/phactory/pal/src/lib.rs +++ b/crates/phactory/pal/src/lib.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::path::Path; +use core::time::Duration; use phala_types::AttestationProvider; @@ -24,6 +25,7 @@ pub trait RA { &self, provider: Option, data: &[u8], + timeout: Duration, ) -> Result, Self::Error>; fn quote_test(&self, provider: Option) -> Result<(), Self::Error>; fn measurement(&self) -> Option>; diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 9b4d2b75a6..4687c01d09 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -4,6 +4,7 @@ use std::future::Future; use std::io::Read; use std::str::FromStr; use std::sync::{Arc, Mutex, MutexGuard}; +use std::time::Duration; use crate::benchmark::Flags; use crate::system::{System, MAX_SUPPORTED_CONSENSUS_VERSION}; @@ -558,25 +559,13 @@ impl Phactory info!("Encoded runtime info"); info!("{:?}", hex::encode(&cached_resp.encoded_runtime_info)); - let encoded_report = match self - .platform - .create_attestation_report(self.attestation_provider, &runtime_info_hash) - { - Ok(r) => r, - Err(e) => { - let message = format!("Failed to create attestation report: {e:?}"); - error!("{}", message); - return Err(from_display(message)); - } - }; - - cached_resp.attestation = Some(pb::Attestation { - version: 1, - provider: serde_json::to_string(&self.attestation_provider).unwrap(), - payload: None, - encoded_report, - timestamp: now(), - }); + let report = create_attestation_report_on( + &self.platform, + self.attestation_provider, + &runtime_info_hash, + self.args.ra_timeout, + )?; + cached_resp.attestation = Some(report); } Ok(cached_resp.clone()) @@ -1245,15 +1234,17 @@ fn create_attestation_report_on( platform: &Platform, attestation_provider: Option, data: &[u8], + timeout: Duration, ) -> RpcResult { - let encoded_report = match platform.create_attestation_report(attestation_provider, data) { - Ok(r) => r, - Err(e) => { - let message = format!("Failed to create attestation report: {e:?}"); - error!("{}", message); - return Err(from_display(message)); - } - }; + let encoded_report = + match platform.create_attestation_report(attestation_provider, data, timeout) { + Ok(r) => r, + Err(e) => { + let message = format!("Failed to create attestation report: {e:?}"); + error!("{}", message); + return Err(from_display(message)); + } + }; Ok(pb::Attestation { version: 1, provider: serde_json::to_string(&attestation_provider).unwrap(), @@ -1633,6 +1624,7 @@ impl PhactoryApi for Rpc &phactory.platform, attestation_provider, &worker_key_hash, + phactory.args.ra_timeout, )?) } else { info!("Omit RA report in workerkey response in dev mode"); @@ -1689,6 +1681,7 @@ impl PhactoryApi for Rpc &phactory.platform, Some(AttestationProvider::Ias), &handler_hash, + phactory.args.ra_timeout, )?) } else { info!("Omit client RA report for dev mode challenge"); diff --git a/crates/phala-clap-parsers/Cargo.toml b/crates/phala-clap-parsers/Cargo.toml new file mode 100644 index 0000000000..685ea8a33c --- /dev/null +++ b/crates/phala-clap-parsers/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "phala-clap-parsers" +version = "0.1.0" +edition = "2021" +description = "Some custom value parsers for clap" +license = "MIT" +repository = "https://github.com/Phala-Network/phala-blockchain" +authors = ["Kevin Wang "] + +[dependencies] diff --git a/crates/phala-clap-parsers/src/duration.rs b/crates/phala-clap-parsers/src/duration.rs new file mode 100644 index 0000000000..4ccb1fb394 --- /dev/null +++ b/crates/phala-clap-parsers/src/duration.rs @@ -0,0 +1,63 @@ +#[derive(Debug)] +pub struct InvalidDuration; + +impl std::fmt::Display for InvalidDuration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Invalid duration") + } +} + +impl std::error::Error for InvalidDuration {} + +use core::time::Duration; + +pub fn parse_duration(s: &str) -> Result { + let mut num_str = s; + let mut unit = "s"; + + if let Some(idx) = s.find(|c: char| !c.is_numeric()) { + num_str = &s[..idx]; + unit = &s[idx..]; + } + + let num = num_str.parse::().or(Err(InvalidDuration))?; + + let num = match unit { + "s" | "" => num * 1000, + "m" => num * 60 * 1000, + "h" => num * 60 * 60 * 1000, + "d" => num * 60 * 60 * 24 * 1000, + "ms" => num, + _ => return Err(InvalidDuration), + }; + + Ok(Duration::from_millis(num)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_duration() { + assert_eq!(parse_duration("10ms").unwrap(), Duration::from_millis(10)); + assert_eq!(parse_duration("10m").unwrap(), Duration::from_secs(600)); + assert_eq!(parse_duration("10s").unwrap(), Duration::from_secs(10)); + assert_eq!(parse_duration("10m").unwrap(), Duration::from_secs(600)); + assert_eq!(parse_duration("10h").unwrap(), Duration::from_secs(36000)); + assert_eq!(parse_duration("10d").unwrap(), Duration::from_secs(864000)); + assert_eq!(parse_duration("1").unwrap(), Duration::from_secs(1)); + assert_eq!(parse_duration("100").unwrap(), Duration::from_secs(100)); + } + + #[test] + fn test_parse_duration_invalid() { + assert!(parse_duration("10x").is_err()); + assert!(parse_duration("ms").is_err()); + } + + #[test] + fn test_parse_duration_empty() { + assert!(parse_duration("").is_err()); + } +} diff --git a/crates/phala-clap-parsers/src/lib.rs b/crates/phala-clap-parsers/src/lib.rs new file mode 100644 index 0000000000..eb79019ec2 --- /dev/null +++ b/crates/phala-clap-parsers/src/lib.rs @@ -0,0 +1,3 @@ +pub use duration::{parse_duration, InvalidDuration}; + +mod duration; diff --git a/standalone/phat-poller/Cargo.toml b/standalone/phat-poller/Cargo.toml index 97011f11b9..cded42c478 100644 --- a/standalone/phat-poller/Cargo.toml +++ b/standalone/phat-poller/Cargo.toml @@ -8,6 +8,7 @@ phaxt = { path = "../../crates/phaxt" } phactory-api = { path = "../../crates/phactory/api", features = ["pruntime-client"] } phala-crypto = { path = "../../crates/phala-crypto" } phala-types = { path = "../../crates/phala-types" } +phala-clap-parsers = { path = "../../crates/phala-clap-parsers" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } diff --git a/standalone/phat-poller/src/args.rs b/standalone/phat-poller/src/args.rs index 2ec4c3f35b..df060d99a1 100644 --- a/standalone/phat-poller/src/args.rs +++ b/standalone/phat-poller/src/args.rs @@ -1,4 +1,5 @@ use clap::{Parser, Subcommand}; +use phala_clap_parsers::parse_duration; use sp_core::{crypto::SecretStringError, Pair, H256}; use std::time::Duration; @@ -66,43 +67,10 @@ pub struct RunArgs { pub caller: sp_core::sr25519::Pair, } -#[derive(Debug)] -pub struct InvalidDuration; - -impl std::fmt::Display for InvalidDuration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Invalid duration") - } -} - -impl std::error::Error for InvalidDuration {} - fn parse_sr25519(s: &str) -> Result { ::from_string(s, None) } -fn parse_duration(s: &str) -> Result { - let mut num_str = s; - let mut unit = "s"; - - if let Some(idx) = s.find(|c: char| !c.is_numeric()) { - num_str = &s[..idx]; - unit = &s[idx..]; - } - - let num = num_str.parse::().or(Err(InvalidDuration))?; - - let num = match unit { - "s" | "" => num, - "m" => num * 60, - "h" => num * 60 * 60, - "d" => num * 60 * 60 * 24, - _ => return Err(InvalidDuration), - }; - - Ok(Duration::from_secs(num)) -} - pub fn parse_hash(s: &str) -> Result { let s = s.trim_start_matches("0x"); if s.len() != 64 { @@ -112,30 +80,3 @@ pub fn parse_hash(s: &str) -> Result { hex::decode_to_slice(s, &mut buf)?; Ok(H256::from(buf)) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_duration() { - assert_eq!(parse_duration("10").unwrap(), Duration::from_secs(10)); - assert_eq!(parse_duration("10s").unwrap(), Duration::from_secs(10)); - assert_eq!(parse_duration("10m").unwrap(), Duration::from_secs(600)); - assert_eq!(parse_duration("10h").unwrap(), Duration::from_secs(36000)); - assert_eq!(parse_duration("10d").unwrap(), Duration::from_secs(864000)); - assert_eq!(parse_duration("1").unwrap(), Duration::from_secs(1)); - assert_eq!(parse_duration("100").unwrap(), Duration::from_secs(100)); - } - - #[test] - fn test_parse_duration_invalid() { - assert!(parse_duration("10x").is_err()); - assert!(parse_duration("ms").is_err()); - } - - #[test] - fn test_parse_duration_empty() { - assert!(parse_duration("").is_err()); - } -} diff --git a/standalone/pruntime/Cargo.lock b/standalone/pruntime/Cargo.lock index d7e1133fc2..7abd37b655 100644 --- a/standalone/pruntime/Cargo.lock +++ b/standalone/pruntime/Cargo.lock @@ -4523,6 +4523,10 @@ dependencies = [ "futures", ] +[[package]] +name = "phala-clap-parsers" +version = "0.1.0" + [[package]] name = "phala-crypto" version = "0.1.0" @@ -5204,6 +5208,7 @@ dependencies = [ "phactory-api", "phactory-pal", "phala-allocator", + "phala-clap-parsers", "phala-git-revision", "phala-rocket-middleware", "phala-sanitized-logger", diff --git a/standalone/pruntime/Cargo.toml b/standalone/pruntime/Cargo.toml index 0f2a206d2c..4a182a2038 100644 --- a/standalone/pruntime/Cargo.toml +++ b/standalone/pruntime/Cargo.toml @@ -38,6 +38,7 @@ phala-allocator = { path = "../../crates/phala-allocator" } phala-rocket-middleware = { path = "../../crates/phala-rocket-middleware" } phala-types = { path = "../../crates/phala-types", features = ["enable_serde", "sgx"] } phala-git-revision = { path = "../../crates/phala-git-revision" } +phala-clap-parsers = { path = "../../crates/phala-clap-parsers" } sgx-api-lite = { path = "../../crates/sgx-api-lite" } tracing = "0.1" hex_fmt = "0.3.0" diff --git a/standalone/pruntime/src/ias.rs b/standalone/pruntime/src/ias.rs index 3a88d40d37..52a6e2a560 100644 --- a/standalone/pruntime/src/ias.rs +++ b/standalone/pruntime/src/ias.rs @@ -7,17 +7,20 @@ use reqwest_env_proxy::EnvProxyBuilder as _; pub const IAS_HOST: &str = env!("IAS_HOST"); pub const IAS_REPORT_ENDPOINT: &str = env!("IAS_REPORT_ENDPOINT"); -fn get_report_from_intel(quote: &[u8], ias_key: &str) -> Result<(String, String, String)> { +fn get_report_from_intel( + quote: &[u8], + ias_key: &str, + timeout: Duration, +) -> Result<(String, String, String)> { let encoded_quote = base64::encode(quote); let encoded_json = format!("{{\"isvEnclaveQuote\":\"{encoded_quote}\"}}\r\n"); let mut res_body_buffer = Vec::new(); //container for body of a response - let timeout = Some(Duration::from_secs(8)); let url: reqwest::Url = format!("https://{IAS_HOST}{IAS_REPORT_ENDPOINT}").parse()?; info!(from=%url, "Getting RA report"); let mut res = reqwest::blocking::Client::builder() - .timeout(timeout) + .timeout(Some(timeout)) .env_proxy(url.domain().unwrap_or_default()) .build() .context("Failed to create http client, maybe invalid IAS URI")? @@ -93,9 +96,13 @@ pub fn create_quote_vec(data: &[u8]) -> Result> { Ok(fs::read("/dev/attestation/quote")?) } -pub fn create_attestation_report(data: &[u8], ias_key: &str) -> Result<(String, Vec, Vec)> { +pub fn create_attestation_report( + data: &[u8], + ias_key: &str, + timeout: Duration, +) -> Result<(String, Vec, Vec)> { let quote_vec = create_quote_vec(data)?; - let (attn_report, sig, cert) = get_report_from_intel("e_vec, ias_key)?; + let (attn_report, sig, cert) = get_report_from_intel("e_vec, ias_key, timeout)?; let sig = base64::decode(sig).expect("Sig should be a valid base64"); let cert = base64::decode(cert).expect("SigCert should be a valid base64"); diff --git a/standalone/pruntime/src/main.rs b/standalone/pruntime/src/main.rs index c46e8d92df..01716a5a85 100644 --- a/standalone/pruntime/src/main.rs +++ b/standalone/pruntime/src/main.rs @@ -4,12 +4,14 @@ mod pal_gramine; mod runtime; use std::{env, thread}; +use std::time::Duration; use clap::Parser; use tracing::{error, info, info_span, Instrument}; use phactory_api::ecall_args::InitArgs; use phala_git_revision::git_revision_with_ts; +use phala_clap_parsers::parse_duration; mod handover; use phala_sanitized_logger as logger; @@ -81,6 +83,10 @@ struct Args { /// Disable the RCU policy to update the Phactory state. #[arg(long)] no_rcu: bool, + + /// The timeout of getting the attestation report. (in seconds) + #[arg(long, value_parser = parse_duration, default_value = "8s")] + ra_timeout: Duration, } #[rocket::main] @@ -146,6 +152,7 @@ async fn serve(sgx: bool) -> Result<(), rocket::Error> { public_port: args.public_port, safe_mode_level: args.safe_mode_level, no_rcu: args.no_rcu, + ra_timeout: args.ra_timeout, } }; info!("init_args: {:#?}", init_args); diff --git a/standalone/pruntime/src/pal_gramine.rs b/standalone/pruntime/src/pal_gramine.rs index 92e774be38..ebb36142e3 100644 --- a/standalone/pruntime/src/pal_gramine.rs +++ b/standalone/pruntime/src/pal_gramine.rs @@ -7,6 +7,7 @@ use phactory_pal::{AppInfo, AppVersion, Machine, MemoryStats, MemoryUsage, Seali use phala_allocator::StatSizeAllocator; use std::io::ErrorKind; use std::str::FromStr as _; +use std::time::Duration; use crate::ias; @@ -46,6 +47,7 @@ impl RA for GraminePlatform { &self, provider: Option, data: &[u8], + timeout: Duration, ) -> Result, Self::Error> { match provider { Some(AttestationProvider::Ias) => { @@ -53,7 +55,7 @@ impl RA for GraminePlatform { const IAS_API_KEY_STR: &str = env!("IAS_API_KEY"); let (attn_report, sig, cert) = - ias::create_attestation_report(data, IAS_API_KEY_STR)?; + ias::create_attestation_report(data, IAS_API_KEY_STR, timeout)?; let attestation_report = Some(phala_types::AttestationReport::SgxIas { ra_report: attn_report.as_bytes().to_vec(), signature: sig, @@ -183,8 +185,14 @@ pub(crate) fn print_target_info() { sgx_api_lite::report(&target_info, &[0; 64]).expect("Failed to get sgx report"); println!("mr_enclave : 0x{}", HexFmt(&report.body.mr_enclave.m)); println!("mr_signer : 0x{}", HexFmt(&report.body.mr_signer.m)); - println!("isv_svn : 0x{:?}", HexFmt(report.body.isv_svn.to_ne_bytes())); - println!("isv_prod_id : 0x{:?}", HexFmt(report.body.isv_prod_id.to_ne_bytes())); + println!( + "isv_svn : 0x{:?}", + HexFmt(report.body.isv_svn.to_ne_bytes()) + ); + println!( + "isv_prod_id : 0x{:?}", + HexFmt(report.body.isv_prod_id.to_ne_bytes()) + ); } else { println!("Running in Native mode"); } From 81b6fb3f2046b160e193e6364fe64c791cdc2b51 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sun, 25 Jun 2023 03:01:11 +0000 Subject: [PATCH 2/2] pruntime: Configurable ra max retries --- crates/phactory/api/src/ecall_args.rs | 3 +++ crates/phactory/src/prpc_service.rs | 19 ++++++++++++++++--- standalone/pruntime/src/main.rs | 5 +++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/phactory/api/src/ecall_args.rs b/crates/phactory/api/src/ecall_args.rs index dbaead1b20..6f3b8f23b6 100644 --- a/crates/phactory/api/src/ecall_args.rs +++ b/crates/phactory/api/src/ecall_args.rs @@ -45,6 +45,9 @@ pub struct InitArgs { /// The timeout of getting the attestation report. pub ra_timeout: Duration, + + /// The max retry times of getting the attestation report. + pub ra_max_retries: u32, } pub use phala_git_revision::git_revision; diff --git a/crates/phactory/src/prpc_service.rs b/crates/phactory/src/prpc_service.rs index 4687c01d09..982ac84527 100644 --- a/crates/phactory/src/prpc_service.rs +++ b/crates/phactory/src/prpc_service.rs @@ -564,6 +564,7 @@ impl Phactory self.attestation_provider, &runtime_info_hash, self.args.ra_timeout, + self.args.ra_max_retries, )?; cached_resp.attestation = Some(report); } @@ -1235,16 +1236,26 @@ fn create_attestation_report_on( attestation_provider: Option, data: &[u8], timeout: Duration, + max_retries: u32, ) -> RpcResult { - let encoded_report = - match platform.create_attestation_report(attestation_provider, data, timeout) { + let mut tried = 0; + let encoded_report = loop { + break match platform.create_attestation_report(attestation_provider, data, timeout) { Ok(r) => r, Err(e) => { let message = format!("Failed to create attestation report: {e:?}"); error!("{}", message); - return Err(from_display(message)); + if tried >= max_retries { + return Err(from_display(message)); + } + let sleep_secs = (1 << tried).min(8); + info!("Retrying after {} seconds...", sleep_secs); + std::thread::sleep(Duration::from_secs(sleep_secs)); + tried += 1; + continue; } }; + }; Ok(pb::Attestation { version: 1, provider: serde_json::to_string(&attestation_provider).unwrap(), @@ -1625,6 +1636,7 @@ impl PhactoryApi for Rpc attestation_provider, &worker_key_hash, phactory.args.ra_timeout, + phactory.args.ra_max_retries, )?) } else { info!("Omit RA report in workerkey response in dev mode"); @@ -1682,6 +1694,7 @@ impl PhactoryApi for Rpc Some(AttestationProvider::Ias), &handler_hash, phactory.args.ra_timeout, + phactory.args.ra_max_retries, )?) } else { info!("Omit client RA report for dev mode challenge"); diff --git a/standalone/pruntime/src/main.rs b/standalone/pruntime/src/main.rs index 01716a5a85..398d887dcd 100644 --- a/standalone/pruntime/src/main.rs +++ b/standalone/pruntime/src/main.rs @@ -87,6 +87,10 @@ struct Args { /// The timeout of getting the attestation report. (in seconds) #[arg(long, value_parser = parse_duration, default_value = "8s")] ra_timeout: Duration, + + /// The max retry times of getting the attestation report. + #[arg(long, default_value = "1")] + ra_max_retries: u32, } #[rocket::main] @@ -153,6 +157,7 @@ async fn serve(sgx: bool) -> Result<(), rocket::Error> { safe_mode_level: args.safe_mode_level, no_rcu: args.no_rcu, ra_timeout: args.ra_timeout, + ra_max_retries: args.ra_max_retries, } }; info!("init_args: {:#?}", init_args);