#![deny(missing_docs)]
#![deny(warnings)]
use aws_nitro_enclaves_image_format::defs::EifIdentityInfo;
use aws_nitro_enclaves_image_format::utils::eif_reader::EifReader;
use aws_nitro_enclaves_image_format::utils::{get_pcrs, PcrSignatureChecker};
use log::debug;
use sha2::{Digest, Sha384};
use std::collections::BTreeMap;
use std::fs::File;
use std::thread::JoinHandle;
use crate::common::commands_parser::RunEnclavesArgs;
use crate::common::construct_error_message;
use crate::common::json_output::EnclaveTerminateInfo;
use crate::common::{NitroCliErrorEnum, NitroCliFailure, NitroCliResult};
use crate::enclave_proc::connection::Connection;
use crate::enclave_proc::connection::{safe_conn_eprintln, safe_conn_println};
use crate::enclave_proc::cpu_info::CpuInfo;
use crate::enclave_proc::resource_manager::{EnclaveManager, EnclaveState};
use crate::enclave_proc::utils::get_enclave_describe_info;
use crate::new_nitro_cli_failure;
pub struct DescribeThreadResult {
pub measurements: BTreeMap<String, String>,
pub metadata: Option<EifIdentityInfo>,
}
pub type DescribeThread = Option<JoinHandle<NitroCliResult<DescribeThreadResult>>>;
pub struct RunEnclaveResult {
pub enclave_manager: EnclaveManager,
pub describe_thread: DescribeThread,
}
pub fn run_enclaves(
args: &RunEnclavesArgs,
connection: Option<&Connection>,
) -> NitroCliResult<RunEnclaveResult> {
debug!("run_enclaves");
let eif_file = File::open(&args.eif_path).map_err(|e| {
new_nitro_cli_failure!(
&format!("Failed to open the EIF file: {e:?}"),
NitroCliErrorEnum::FileOperationFailure
)
.add_info(vec![&args.eif_path, "Open"])
})?;
let cpu_ids = CpuInfo::new()
.map_err(|e| e.add_subaction("Failed to construct CPU information".to_string()))?
.get_cpu_config(args)
.map_err(|e| e.add_subaction("Failed to get CPU configuration".to_string()))?;
let enclave_name = match &args.enclave_name {
Some(enclave_name) => enclave_name,
None => {
return Err(new_nitro_cli_failure!(
"Failed to set enclave name in the manager".to_string(),
NitroCliErrorEnum::FileOperationFailure
))
}
};
let mut enclave_manager = EnclaveManager::new(
args.enclave_cid,
args.memory_mib,
cpu_ids,
eif_file,
args.debug_mode,
enclave_name.to_string(),
)
.map_err(|e| {
e.add_subaction("Failed to construct EnclaveManager with given arguments".to_string())
})?;
let mut signature_checker = PcrSignatureChecker::from_eif(&args.eif_path).map_err(|e| {
new_nitro_cli_failure!(
&format!("Failed to create EIF signature checker: {e:?}"),
NitroCliErrorEnum::EIFSignatureCheckerError
)
})?;
if !signature_checker.is_empty() {
signature_checker.verify().map_err(|e| {
new_nitro_cli_failure!(
&format!("Invalid signing certificate: {e:?}"),
NitroCliErrorEnum::EIFSignatureCheckerError
)
})?;
}
let path = args.eif_path.clone();
let handle = std::thread::spawn(move || {
let mut eif_reader = EifReader::from_eif(path).map_err(|e| {
new_nitro_cli_failure!(
&format!("Failed initialize EIF reader: {e:?}"),
NitroCliErrorEnum::EifParsingError
)
})?;
let measurements = get_pcrs(
&mut eif_reader.image_hasher,
&mut eif_reader.bootstrap_hasher,
&mut eif_reader.app_hasher,
&mut eif_reader.cert_hasher,
Sha384::new(),
eif_reader.signature_section.is_some(),
)
.map_err(|e| {
new_nitro_cli_failure!(
&format!("Failed to calculate PCRs: {e:?}"),
NitroCliErrorEnum::EifParsingError
)
})?;
Ok(DescribeThreadResult {
measurements,
metadata: eif_reader.get_metadata(),
})
});
enclave_manager
.run_enclave(connection)
.map_err(|e| e.add_subaction("Failed to run enclave".to_string()))?;
enclave_manager
.update_state(EnclaveState::Running)
.map_err(|e| e.add_subaction("Failed to update enclave state".to_string()))?;
Ok(RunEnclaveResult {
enclave_manager,
describe_thread: Some(handle),
})
}
pub fn terminate_enclaves(
enclave_manager: &mut EnclaveManager,
connection: Option<&Connection>,
) -> NitroCliResult<()> {
let enclave_id = enclave_manager.enclave_id.clone();
let enclave_name = Some(enclave_manager.enclave_name.clone());
debug!("terminate_enclaves");
enclave_manager
.update_state(EnclaveState::Terminating)
.map_err(|e| e.add_subaction("Failed to update enclave state".to_string()))?;
if let Err(error_info) = enclave_manager.terminate_enclave() {
safe_conn_eprintln(
connection,
format!(
"Warning: Failed to stop enclave {}\nError message: {:?}",
enclave_manager.enclave_id,
construct_error_message(&error_info).as_str()
)
.as_str(),
)?;
return Err(error_info);
}
enclave_manager.update_state(EnclaveState::Empty)?;
safe_conn_eprintln(
connection,
format!(
"Successfully terminated enclave {}.",
enclave_manager.enclave_id
)
.as_str(),
)?;
safe_conn_println(
connection,
serde_json::to_string_pretty(&EnclaveTerminateInfo::new(enclave_name, enclave_id, true))
.map_err(|err| {
new_nitro_cli_failure!(
&format!("Failed to display enclave termination data: {err:?}"),
NitroCliErrorEnum::SerdeError
)
})?
.as_str(),
)
}
pub fn describe_enclaves(
enclave_manager: &EnclaveManager,
connection: &Connection,
with_metadata: bool,
) -> NitroCliResult<()> {
debug!("describe_enclaves");
let info = get_enclave_describe_info(enclave_manager, with_metadata)
.map_err(|e| e.add_subaction(String::from("Execute Describe Enclave command")))?;
connection.println(
serde_json::to_string_pretty(&info)
.map_err(|err| {
new_nitro_cli_failure!(
&format!("Failed to display enclave describe data: {err:?}"),
NitroCliErrorEnum::SerdeError
)
})?
.as_str(),
)
}