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

Attesteer RPC additions #1201

Merged
merged 22 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
378652e
rpc: add attesteer_forward_dcap_quote call
OverOrion Mar 2, 2023
5862ee0
rpc: add attesteer_forward_ias_attestation_report
OverOrion Mar 2, 2023
3865f59
remove unused variables
OverOrion Mar 2, 2023
296ce9c
enclave-runtime/attestation: remove duplicate code
OverOrion Mar 2, 2023
ad76638
enclave-runtime/attestation: extract extrinsic sending
OverOrion Mar 2, 2023
3b21f0a
enclave-runtime/attestaion: refactor generate_dcap_ra_extrinsic_inter…
OverOrion Mar 2, 2023
5f1c56f
attesteer: add attesteer RPC calls to the CLI
OverOrion Mar 9, 2023
6e4a5c1
compiling
OverOrion Mar 13, 2023
9a11f6b
cli: attesteer dcap quote verification takes a filename (to a hex enc…
OverOrion Mar 13, 2023
3c184f4
cli: attesteer ias attestation report takes a filename (to a hex enco…
OverOrion Mar 13, 2023
74c7597
clippy: fix needless borrow
OverOrion Mar 13, 2023
376dc0b
rename: match substrate convention for RPC method names
OverOrion Mar 13, 2023
ac5fcc5
rename: SendDCAPQuoteCmd -> SendDcapQuoteCmd
OverOrion Mar 13, 2023
43aa983
rename: SendIASAttestationReportCmd -> SendIasAttestationReportCmd
OverOrion Mar 13, 2023
554eea9
rename: attesteer_callForwardIASAttestationReport -> attesteer_Forwar…
OverOrion Mar 13, 2023
a7e5f80
rename: attesteer_callForwardDCAPQuote -> attesteer_ForwardDcapQuote
OverOrion Mar 13, 2023
e6cdacc
cli: refactor attesteer comamnds to use a neat match expressions
OverOrion Mar 14, 2023
6875baa
cli: attesteer commands use let-else pattern
OverOrion Mar 14, 2023
c920561
cli: attesteer commands send to chain
OverOrion Mar 14, 2023
5b94a6c
rename: attesteer_Forward -> attesteer_forward
OverOrion Mar 16, 2023
2cb4162
Merge branch 'master' into szp/attesteer-rpc
OverOrion Mar 30, 2023
8d75580
Merge branch 'master' into szp/attesteer-rpc
OverOrion Apr 3, 2023
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
23 changes: 23 additions & 0 deletions cli/src/attesteer/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
OverOrion marked this conversation as resolved.
Show resolved Hide resolved
};
62 changes: 62 additions & 0 deletions cli/src/attesteer/commands/send_dcap_quote.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
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 = read_to_string(&self.quote)
.map_err(|e| error!("Opening hex encoded DCAP quote file failed: {:#?}", e))
.unwrap();

let rpc_method = "attesteer_callForwardDCAPQuote".to_owned();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as explained below for the other command.

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 rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).ok().unwrap();
let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result)
// Replace with `inspect_err` once it's stable.
.map_err(|e| {
error!("Failed to decode RpcReturnValue: {:?}", e);
e
})
.ok()
.unwrap();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as explained below for the other command.

match rpc_return_value.status {
DirectRequestStatus::Ok => println!("DCAP quote verification succeded."),
_ => error!("DCAP quote verification failed"),
}
}
}
65 changes: 65 additions & 0 deletions cli/src/attesteer/commands/send_ias_attestation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
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::error;
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 = read_to_string(&self.report)
.map_err(|e| error!("Opening hex encoded IAS attestation report file failed: {:#?}", e))
.unwrap();

//let request = Request { shard, cyphertext: hex_encoded_quote.to_vec() };

let rpc_method = "attesteer_callForwardIASAttestationReport".to_owned();
OverOrion marked this conversation as resolved.
Show resolved Hide resolved
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 rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).ok().unwrap();
let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result)
// Replace with `inspect_err` once it's stable.
.map_err(|e| {
error!("Failed to decode RpcReturnValue: {:?}", e);
e
})
.ok()
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw. I know this is a pattern that you find in our codebase, but I honestly don't know how brought this into existence. And with rust v1.6.5 we can use a beautiful pattern here:

Suggested change
let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result)
// Replace with `inspect_err` once it's stable.
.map_err(|e| {
error!("Failed to decode RpcReturnValue: {:?}", e);
e
})
.ok()
.unwrap();
let Ok(rpc_return_value) = match RpcReturnValue::from_hex(&rpc_response.result) else {
panic!("Failed to decode RpcReturnValue: {:?}", e);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not use the let-else pattern in some places (where the error would be reported as e would not be defined otherwise). I used match expressions there, hope that's alright.


match rpc_return_value.status {
DirectRequestStatus::Ok => println!("IAS attestation report verification succeded."),
_ => error!("IAS attestation report verification failed"),
clangenb marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
41 changes: 41 additions & 0 deletions cli/src/attesteer/mod.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
7 changes: 7 additions & 0 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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) {
Expand All @@ -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),
};
}
1 change: 1 addition & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern crate chrono;
extern crate env_logger;
extern crate log;

mod attesteer;
mod base_cli;
mod benchmark;
mod command_utils;
Expand Down
33 changes: 16 additions & 17 deletions enclave-runtime/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -270,7 +260,6 @@ pub fn generate_dcap_ra_extrinsic_from_quote_internal(
url: String,
quote: &[u8],
) -> EnclaveResult<OpaqueExtrinsic> {
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:");

Expand All @@ -280,28 +269,38 @@ 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(
url: String,
skip_ra: bool,
) -> EnclaveResult<OpaqueExtrinsic> {
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<OpaqueExtrinsic> {
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())?
.map_err(MetadataProviderError::MetadataError)?;

let call = OpaqueCall::from_tuple(&(call_ids, cert_der, url));

create_extrinsics(call)
}

fn create_extrinsics(call: OpaqueCall) -> EnclaveResult<OpaqueExtrinsic> {
let extrinsics_factory = get_extrinsic_factory_from_solo_or_parachain()?;
let extrinsics = extrinsics_factory.create_extrinsics(&[call], None)?;

Ok(extrinsics[0].clone())
Expand Down
70 changes: 70 additions & 0 deletions enclave-runtime/src/rpc/worker_api_direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

*/

use crate::attestation::{
generate_dcap_ra_extrinsic_from_quote_internal,
generate_ias_ra_extrinsic_from_der_cert_internal,
};
use codec::Encode;
use core::result::Result;
use ita_sgx_runtime::Runtime;
Expand All @@ -28,6 +32,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 {
Expand Down Expand Up @@ -143,6 +148,39 @@ where
Ok(json!(json_value))
});

// attesteer_forward_dcap_quote
let attesteer_forward_dcap_quote: &str = "attesteer_callForwardDCAPQuote";
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_callForwardIASAttestationReport";
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| {
Expand Down Expand Up @@ -192,6 +230,38 @@ fn execute_getter_inner<G: ExecuteGetter>(
Ok(getter_result)
}

fn forward_dcap_quote_inner(params: Params) -> Result<OpaqueExtrinsic, String> {
let hex_encoded_params = params.parse::<Vec<String>>().map_err(|e| format!("{:?}", e))?;

let request =
Request::from_hex(&hex_encoded_params[0].clone()).map_err(|e| format!("{:?}", e))?;

let encoded_quote_to_forward: Vec<u8> = request.cyphertext;

let url = String::new();
let ext = generate_dcap_ra_extrinsic_from_quote_internal(url, &encoded_quote_to_forward)
.map_err(|e| format!("{:?}", e))?;

Ok(ext)
OverOrion marked this conversation as resolved.
Show resolved Hide resolved
}

fn attesteer_forward_ias_attestation_report_inner(
params: Params,
) -> Result<OpaqueExtrinsic, String> {
let hex_encoded_params = params.parse::<Vec<String>>().map_err(|e| format!("{:?}", e))?;

let request =
Request::from_hex(&hex_encoded_params[0].clone()).map_err(|e| format!("{:?}", e))?;

let ias_attestation_report: Vec<u8> = request.cyphertext;

let url = String::new();
let ext = generate_ias_ra_extrinsic_from_der_cert_internal(url, &ias_attestation_report)
.map_err(|e| format!("{:?}", e))?;

Ok(ext)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you messed up some stuff here:

  1. The request will be decoded into the wrong thing because the first few bytes are interpreted as the ShardIdentifier, which you don't pass in the request (which is fine). So the hex encoded params does only contain the hex encoded remote attestation. Hence, what you need to do is simply:
// also check that `hex_encoded_params.length == 1`
let ias_attestation_report = itp_utils::hex::decode_hex(hex_encoded_params[0]).map_err(...);

Where are you sending the remote attestation to the chain? You just send back the ready to send extrinsic to the client. The attesteer should send it to the chain, though. Is this a programmatic error, or do we have a misunderstanding about what the attesteer should do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was the former, I fixed it accordingly in c920561.


pub fn sidechain_io_handler<ImportFn, Error>(import_fn: ImportFn) -> IoHandler
where
ImportFn: Fn(SignedBlock) -> Result<(), Error> + Sync + Send + 'static,
Expand Down