From 945571ae07f6efdebb7cf67b21e5e31b1b4b6ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szil=C3=A1rd=20Parrag?= Date: Mon, 3 Apr 2023 13:30:21 +0200 Subject: [PATCH] Attesteer RPC additions (#1201) * rpc: add attesteer_forward_dcap_quote call * rpc: add attesteer_forward_ias_attestation_report * remove unused variables * enclave-runtime/attestation: remove duplicate code * enclave-runtime/attestation: extract extrinsic sending * enclave-runtime/attestaion: refactor generate_dcap_ra_extrinsic_internal to reduce code duplication * attesteer: add attesteer RPC calls to the CLI * compiling * cli: attesteer dcap quote verification takes a filename (to a hex encoded quote) instead of contents * cli: attesteer ias attestation report takes a filename (to a hex encoded report) instead of its contents * clippy: fix needless borrow * rename: match substrate convention for RPC method names * rename: SendDCAPQuoteCmd -> SendDcapQuoteCmd * rename: SendIASAttestationReportCmd -> SendIasAttestationReportCmd * rename: attesteer_callForwardIASAttestationReport -> attesteer_ForwardIasAttestationReport * rename: attesteer_callForwardDCAPQuote -> attesteer_ForwardDcapQuote * cli: refactor attesteer comamnds to use a neat match expressions * cli: attesteer commands use let-else pattern * cli: attesteer commands send to chain * rename: attesteer_Forward -> attesteer_forward --- cli/src/attesteer/commands/mod.rs | 23 +++++ cli/src/attesteer/commands/send_dcap_quote.rs | 61 ++++++++++++ .../commands/send_ias_attestation.rs | 64 +++++++++++++ cli/src/attesteer/mod.rs | 41 ++++++++ cli/src/commands.rs | 7 ++ cli/src/main.rs | 1 + enclave-runtime/src/attestation.rs | 33 ++++--- enclave-runtime/src/rpc/worker_api_direct.rs | 95 +++++++++++++++++++ 8 files changed, 308 insertions(+), 17 deletions(-) create mode 100644 cli/src/attesteer/commands/mod.rs create mode 100644 cli/src/attesteer/commands/send_dcap_quote.rs create mode 100644 cli/src/attesteer/commands/send_ias_attestation.rs create mode 100644 cli/src/attesteer/mod.rs diff --git a/cli/src/attesteer/commands/mod.rs b/cli/src/attesteer/commands/mod.rs new file mode 100644 index 0000000000..70119bf399 --- /dev/null +++ b/cli/src/attesteer/commands/mod.rs @@ -0,0 +1,23 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod send_dcap_quote; +mod send_ias_attestation; + +pub use self::{ + send_dcap_quote::SendDcapQuoteCmd, send_ias_attestation::SendIasAttestationReportCmd, +}; diff --git a/cli/src/attesteer/commands/send_dcap_quote.rs b/cli/src/attesteer/commands/send_dcap_quote.rs new file mode 100644 index 0000000000..bfba053d36 --- /dev/null +++ b/cli/src/attesteer/commands/send_dcap_quote.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{command_utils::get_worker_api_direct, Cli}; +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{RpcRequest, RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use log::*; +use std::fs::read_to_string; + +/// Forward DCAP quote for verification. +#[derive(Debug, Clone, Parser)] +pub struct SendDcapQuoteCmd { + /// Hex encoded DCAP quote filename. + quote: String, +} + +impl SendDcapQuoteCmd { + pub fn run(&self, cli: &Cli) { + let direct_api = get_worker_api_direct(cli); + let hex_encoded_quote = match read_to_string(&self.quote) { + Ok(hex_encoded_quote) => hex_encoded_quote, + Err(e) => panic!("Opening hex encoded DCAP quote file failed: {:#?}", e), + }; + + let rpc_method = "attesteer_forwardDcapQuote".to_owned(); + let jsonrpc_call: String = + RpcRequest::compose_jsonrpc_call(rpc_method, vec![hex_encoded_quote]).unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { + panic!("Can't parse RPC response: '{rpc_response_str}'"); + }; + let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { + Ok(rpc_return_value) => rpc_return_value, + Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), + }; + + match rpc_return_value.status { + DirectRequestStatus::Ok => println!("DCAP quote verification succeded."), + _ => error!("DCAP quote verification failed"), + } + } +} diff --git a/cli/src/attesteer/commands/send_ias_attestation.rs b/cli/src/attesteer/commands/send_ias_attestation.rs new file mode 100644 index 0000000000..ba242a995f --- /dev/null +++ b/cli/src/attesteer/commands/send_ias_attestation.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{RpcRequest, RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use log::*; +use std::fs::read_to_string; + +use crate::{command_utils::get_worker_api_direct, Cli}; + +/// Forward IAS attestation report for verification. +#[derive(Debug, Clone, Parser)] +pub struct SendIasAttestationReportCmd { + /// Hex encoded IAS attestation report filename. + report: String, +} + +impl SendIasAttestationReportCmd { + pub fn run(&self, cli: &Cli) { + let direct_api = get_worker_api_direct(cli); + let hex_encoded_report = match read_to_string(&self.report) { + Ok(hex_encoded_report) => hex_encoded_report, + Err(e) => panic!("Opening hex encoded IAS attestation report file failed: {:#?}", e), + }; + + //let request = Request { shard, cyphertext: hex_encoded_quote.to_vec() }; + + let rpc_method = "attesteer_forwardIasAttestationReport".to_owned(); + let jsonrpc_call: String = + RpcRequest::compose_jsonrpc_call(rpc_method, vec![hex_encoded_report]).unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { + panic!("Can't parse RPC response: '{rpc_response_str}'"); + }; + let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { + Ok(rpc_return_value) => rpc_return_value, + Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), + }; + + match rpc_return_value.status { + DirectRequestStatus::Ok => println!("IAS attestation report verification succeded."), + _ => error!("IAS attestation report verification failed"), + } + } +} diff --git a/cli/src/attesteer/mod.rs b/cli/src/attesteer/mod.rs new file mode 100644 index 0000000000..9f03c59065 --- /dev/null +++ b/cli/src/attesteer/mod.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::Cli; + +use self::commands::{SendDcapQuoteCmd, SendIasAttestationReportCmd}; + +mod commands; + +/// Attesteer subcommands for the CLI. +#[derive(Debug, clap::Subcommand)] +pub enum AttesteerCommand { + /// Forward DCAP quote for verification. + SendDCAPQuote(SendDcapQuoteCmd), + + /// Forward IAS attestation report for verification. + SendIASAttestationReport(SendIasAttestationReportCmd), +} + +impl AttesteerCommand { + pub fn run(&self, cli: &Cli) { + match self { + AttesteerCommand::SendDCAPQuote(cmd) => cmd.run(cli), + AttesteerCommand::SendIASAttestationReport(cmd) => cmd.run(cli), + } + } +} diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 853c1d125d..5fdd6f0383 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -22,6 +22,8 @@ use clap::Subcommand; #[cfg(feature = "teeracle")] use crate::oracle::OracleCommand; +use crate::attesteer::AttesteerCommand; + #[derive(Subcommand)] pub enum Commands { #[clap(flatten)] @@ -35,6 +37,10 @@ pub enum Commands { #[cfg(feature = "teeracle")] #[clap(subcommand)] Oracle(OracleCommand), + + /// Subcommand for the attesteer. + #[clap(subcommand)] + Attesteer(AttesteerCommand), } pub fn match_command(cli: &Cli) { @@ -43,5 +49,6 @@ pub fn match_command(cli: &Cli) { Commands::Trusted(trusted_cli) => trusted_cli.run(cli), #[cfg(feature = "teeracle")] Commands::Oracle(cmd) => cmd.run(cli), + Commands::Attesteer(cmd) => cmd.run(cli), }; } diff --git a/cli/src/main.rs b/cli/src/main.rs index 0cb45e3e05..17f0ba5370 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -27,6 +27,7 @@ extern crate chrono; extern crate env_logger; extern crate log; +mod attesteer; mod base_cli; mod benchmark; mod command_utils; diff --git a/enclave-runtime/src/attestation.rs b/enclave-runtime/src/attestation.rs index 44b9629dad..cab21dea9f 100644 --- a/enclave-runtime/src/attestation.rs +++ b/enclave-runtime/src/attestation.rs @@ -177,17 +177,7 @@ pub fn generate_dcap_ra_extrinsic_internal( skip_ra, )?; - let extrinsics_factory = get_extrinsic_factory_from_solo_or_parachain()?; - let node_metadata_repo = get_node_metadata_repository_from_solo_or_parachain()?; - - let call_ids = node_metadata_repo - .get_from_metadata(|m| m.register_dcap_enclave_call_indexes())? - .map_err(MetadataProviderError::MetadataError)?; - info!(" [Enclave] Compose register enclave call DCAP IDs: {:?}", call_ids); - let call = OpaqueCall::from_tuple(&(call_ids, dcap_quote, url)); - - let extrinsic = extrinsics_factory.create_extrinsics(&[call], None)?; - Ok(extrinsic[0].clone()) + generate_dcap_ra_extrinsic_from_quote_internal(url, &dcap_quote) } #[no_mangle] @@ -270,7 +260,6 @@ pub fn generate_dcap_ra_extrinsic_from_quote_internal( url: String, quote: &[u8], ) -> EnclaveResult { - let extrinsics_factory = get_extrinsic_factory_from_solo_or_parachain()?; let node_metadata_repo = get_node_metadata_repository_from_solo_or_parachain()?; info!(" [Enclave] Compose register enclave getting callIDs:"); @@ -280,9 +269,8 @@ pub fn generate_dcap_ra_extrinsic_from_quote_internal( info!(" [Enclave] Compose register enclave call DCAP IDs: {:?}", call_ids); let call = OpaqueCall::from_tuple(&(call_ids, quote, url)); - let extrinsic = extrinsics_factory.create_extrinsics(&[call], None)?; info!(" [Enclave] Compose register enclave got extrinsic, returning"); - Ok(extrinsic[0].clone()) + create_extrinsics(call) } fn generate_ias_ra_extrinsic_internal( @@ -290,11 +278,17 @@ fn generate_ias_ra_extrinsic_internal( skip_ra: bool, ) -> EnclaveResult { let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; - let extrinsics_factory = get_extrinsic_factory_from_solo_or_parachain()?; - let node_metadata_repo = get_node_metadata_repository_from_solo_or_parachain()?; - let cert_der = attestation_handler.generate_ias_ra_cert(skip_ra)?; + generate_ias_ra_extrinsic_from_der_cert_internal(url, &cert_der) +} + +pub fn generate_ias_ra_extrinsic_from_der_cert_internal( + url: String, + cert_der: &[u8], +) -> EnclaveResult { + let node_metadata_repo = get_node_metadata_repository_from_solo_or_parachain()?; + info!(" [Enclave] Compose register enclave call"); let call_ids = node_metadata_repo .get_from_metadata(|m| m.register_ias_enclave_call_indexes())? @@ -302,6 +296,11 @@ fn generate_ias_ra_extrinsic_internal( let call = OpaqueCall::from_tuple(&(call_ids, cert_der, url)); + create_extrinsics(call) +} + +fn create_extrinsics(call: OpaqueCall) -> EnclaveResult { + let extrinsics_factory = get_extrinsic_factory_from_solo_or_parachain()?; let extrinsics = extrinsics_factory.create_extrinsics(&[call], None)?; Ok(extrinsics[0].clone()) diff --git a/enclave-runtime/src/rpc/worker_api_direct.rs b/enclave-runtime/src/rpc/worker_api_direct.rs index a746f5e2ea..9a1624ba89 100644 --- a/enclave-runtime/src/rpc/worker_api_direct.rs +++ b/enclave-runtime/src/rpc/worker_api_direct.rs @@ -15,9 +15,17 @@ */ +use crate::{ + attestation::{ + generate_dcap_ra_extrinsic_from_quote_internal, + generate_ias_ra_extrinsic_from_der_cert_internal, + }, + utils::get_validator_accessor_from_solo_or_parachain, +}; use codec::Encode; use core::result::Result; use ita_sgx_runtime::Runtime; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; use itp_primitives_cache::{GetPrimitives, GLOBAL_PRIMITIVES_CACHE}; use itp_rpc::RpcReturnValue; use itp_sgx_crypto::Rsa3072Seal; @@ -28,6 +36,7 @@ use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use its_primitives::types::block::SignedBlock; use its_sidechain::rpc_handler::{direct_top_pool_api, import_block_api}; use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; +use sp_runtime::OpaqueExtrinsic; use std::{borrow::ToOwned, format, str, string::String, sync::Arc, vec::Vec}; fn compute_hex_encoded_return_error(error_msg: &str) -> String { @@ -143,6 +152,38 @@ where Ok(json!(json_value)) }); + // attesteer_forward_dcap_quote + let attesteer_forward_dcap_quote: &str = "attesteer_forwardDcapQuote"; + io.add_sync_method(attesteer_forward_dcap_quote, move |params: Params| { + let json_value = match forward_dcap_quote_inner(params) { + Ok(val) => RpcReturnValue { + do_watch: false, + value: val.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + + Ok(json!(json_value)) + }); + + // attesteer_forward_ias_attestation_report + let attesteer_forward_ias_attestation_report: &str = "attesteer_forwardIasAttestationReport"; + io.add_sync_method(attesteer_forward_ias_attestation_report, move |params: Params| { + let json_value = match attesteer_forward_ias_attestation_report_inner(params) { + Ok(val) => RpcReturnValue { + do_watch: false, + value: val.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + + Ok(json!(json_value)) + }); + // system_health let state_health_name: &str = "system_health"; io.add_sync_method(state_health_name, |_: Params| { @@ -192,6 +233,60 @@ fn execute_getter_inner( Ok(getter_result) } +fn forward_dcap_quote_inner(params: Params) -> Result { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + + if hex_encoded_params.len() != 1 { + return Err(format!( + "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", + hex_encoded_params.len(), + 1 + )) + } + + let encoded_quote_to_forward: Vec = + itp_utils::hex::decode_hex(&hex_encoded_params[0]).map_err(|e| format!("{:?}", e))?; + + let url = String::new(); + let ext = generate_dcap_ra_extrinsic_from_quote_internal(url, &encoded_quote_to_forward) + .map_err(|e| format!("{:?}", e))?; + + let validator_access = get_validator_accessor_from_solo_or_parachain().unwrap(); + validator_access + .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) + .unwrap(); + + Ok(ext) +} + +fn attesteer_forward_ias_attestation_report_inner( + params: Params, +) -> Result { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + + if hex_encoded_params.len() != 1 { + return Err(format!( + "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", + hex_encoded_params.len(), + 1 + )) + } + + let ias_attestation_report = + itp_utils::hex::decode_hex(&hex_encoded_params[0]).map_err(|e| format!("{:?}", e))?; + + let url = String::new(); + let ext = generate_ias_ra_extrinsic_from_der_cert_internal(url, &ias_attestation_report) + .map_err(|e| format!("{:?}", e))?; + + let validator_access = get_validator_accessor_from_solo_or_parachain().unwrap(); + validator_access + .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) + .unwrap(); + + Ok(ext) +} + pub fn sidechain_io_handler(import_fn: ImportFn) -> IoHandler where ImportFn: Fn(SignedBlock) -> Result<(), Error> + Sync + Send + 'static,