Skip to main content

nitro_cli/enclave_proc/
commands.rs

1// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3#![deny(missing_docs)]
4#![deny(warnings)]
5
6use aws_nitro_enclaves_image_format::defs::EifIdentityInfo;
7use aws_nitro_enclaves_image_format::utils::eif_reader::EifReader;
8use aws_nitro_enclaves_image_format::utils::{get_pcrs, PcrSignatureChecker};
9use log::debug;
10use sha2::{Digest, Sha384};
11use std::collections::BTreeMap;
12use std::fs::File;
13use std::thread::JoinHandle;
14
15use crate::common::commands_parser::RunEnclavesArgs;
16use crate::common::construct_error_message;
17use crate::common::json_output::EnclaveTerminateInfo;
18use crate::common::{NitroCliErrorEnum, NitroCliFailure, NitroCliResult};
19use crate::enclave_proc::connection::Connection;
20use crate::enclave_proc::connection::{safe_conn_eprintln, safe_conn_println};
21use crate::enclave_proc::cpu_info::CpuInfo;
22use crate::enclave_proc::resource_manager::{EnclaveManager, EnclaveState};
23use crate::enclave_proc::utils::get_enclave_describe_info;
24use crate::new_nitro_cli_failure;
25
26/// Result of thread to calculate PCRs and fetch EIF metadata
27pub struct DescribeThreadResult {
28    /// PCR measurements
29    pub measurements: BTreeMap<String, String>,
30    /// EIF metadata
31    pub metadata: Option<EifIdentityInfo>,
32}
33
34/// Thread handle from parallel computing of PCRs and fetching of metadata
35pub type DescribeThread = Option<JoinHandle<NitroCliResult<DescribeThreadResult>>>;
36
37/// Information returned by run_enclave function.
38pub struct RunEnclaveResult {
39    /// Manager structure describing the enclave.
40    pub enclave_manager: EnclaveManager,
41    /// Handle of the thread that computes PCRs.
42    pub describe_thread: DescribeThread,
43}
44
45/// Launch an enclave with the specified arguments and provide the launch status through the given connection.
46pub fn run_enclaves(
47    args: &RunEnclavesArgs,
48    connection: Option<&Connection>,
49) -> NitroCliResult<RunEnclaveResult> {
50    debug!("run_enclaves");
51
52    let eif_file = File::open(&args.eif_path).map_err(|e| {
53        new_nitro_cli_failure!(
54            &format!("Failed to open the EIF file: {e:?}"),
55            NitroCliErrorEnum::FileOperationFailure
56        )
57        .add_info(vec![&args.eif_path, "Open"])
58    })?;
59
60    let cpu_ids = CpuInfo::new()
61        .map_err(|e| e.add_subaction("Failed to construct CPU information".to_string()))?
62        .get_cpu_config(args)
63        .map_err(|e| e.add_subaction("Failed to get CPU configuration".to_string()))?;
64
65    let enclave_name = match &args.enclave_name {
66        Some(enclave_name) => enclave_name,
67        None => {
68            return Err(new_nitro_cli_failure!(
69                "Failed to set enclave name in the manager".to_string(),
70                NitroCliErrorEnum::FileOperationFailure
71            ))
72        }
73    };
74
75    let mut enclave_manager = EnclaveManager::new(
76        args.enclave_cid,
77        args.memory_mib,
78        cpu_ids,
79        eif_file,
80        args.debug_mode,
81        enclave_name.to_string(),
82    )
83    .map_err(|e| {
84        e.add_subaction("Failed to construct EnclaveManager with given arguments".to_string())
85    })?;
86
87    let mut signature_checker = PcrSignatureChecker::from_eif(&args.eif_path).map_err(|e| {
88        new_nitro_cli_failure!(
89            &format!("Failed to create EIF signature checker: {e:?}"),
90            NitroCliErrorEnum::EIFSignatureCheckerError
91        )
92    })?;
93
94    // Verify the certificate only if signature section exists
95    if !signature_checker.is_empty() {
96        signature_checker.verify().map_err(|e| {
97            new_nitro_cli_failure!(
98                &format!("Invalid signing certificate: {e:?}"),
99                NitroCliErrorEnum::EIFSignatureCheckerError
100            )
101        })?;
102    }
103
104    // Launch parallel computing of PCRs
105    let path = args.eif_path.clone();
106    let handle = std::thread::spawn(move || {
107        let mut eif_reader = EifReader::from_eif(path).map_err(|e| {
108            new_nitro_cli_failure!(
109                &format!("Failed initialize EIF reader: {e:?}"),
110                NitroCliErrorEnum::EifParsingError
111            )
112        })?;
113        let measurements = get_pcrs(
114            &mut eif_reader.image_hasher,
115            &mut eif_reader.bootstrap_hasher,
116            &mut eif_reader.app_hasher,
117            &mut eif_reader.cert_hasher,
118            Sha384::new(),
119            eif_reader.signature_section.is_some(),
120        )
121        .map_err(|e| {
122            new_nitro_cli_failure!(
123                &format!("Failed to calculate PCRs: {e:?}"),
124                NitroCliErrorEnum::EifParsingError
125            )
126        })?;
127        Ok(DescribeThreadResult {
128            measurements,
129            metadata: eif_reader.get_metadata(),
130        })
131    });
132    enclave_manager
133        .run_enclave(connection)
134        .map_err(|e| e.add_subaction("Failed to run enclave".to_string()))?;
135    enclave_manager
136        .update_state(EnclaveState::Running)
137        .map_err(|e| e.add_subaction("Failed to update enclave state".to_string()))?;
138
139    Ok(RunEnclaveResult {
140        enclave_manager,
141        describe_thread: Some(handle),
142    })
143}
144
145/// Terminate an enclave and provide the termination status through the given connection.
146pub fn terminate_enclaves(
147    enclave_manager: &mut EnclaveManager,
148    connection: Option<&Connection>,
149) -> NitroCliResult<()> {
150    let enclave_id = enclave_manager.enclave_id.clone();
151    let enclave_name = Some(enclave_manager.enclave_name.clone());
152
153    debug!("terminate_enclaves");
154    enclave_manager
155        .update_state(EnclaveState::Terminating)
156        .map_err(|e| e.add_subaction("Failed to update enclave state".to_string()))?;
157    if let Err(error_info) = enclave_manager.terminate_enclave() {
158        safe_conn_eprintln(
159            connection,
160            format!(
161                "Warning: Failed to stop enclave {}\nError message: {:?}",
162                enclave_manager.enclave_id,
163                construct_error_message(&error_info).as_str()
164            )
165            .as_str(),
166        )?;
167        return Err(error_info);
168    }
169
170    enclave_manager.update_state(EnclaveState::Empty)?;
171    safe_conn_eprintln(
172        connection,
173        format!(
174            "Successfully terminated enclave {}.",
175            enclave_manager.enclave_id
176        )
177        .as_str(),
178    )?;
179
180    // We notify the CLI of the termination's status.
181    safe_conn_println(
182        connection,
183        serde_json::to_string_pretty(&EnclaveTerminateInfo::new(enclave_name, enclave_id, true))
184            .map_err(|err| {
185                new_nitro_cli_failure!(
186                    &format!("Failed to display enclave termination data: {err:?}"),
187                    NitroCliErrorEnum::SerdeError
188                )
189            })?
190            .as_str(),
191    )
192}
193
194/// Obtain an enclave's description and provide it through the given connection.
195pub fn describe_enclaves(
196    enclave_manager: &EnclaveManager,
197    connection: &Connection,
198    with_metadata: bool,
199) -> NitroCliResult<()> {
200    debug!("describe_enclaves");
201
202    let info = get_enclave_describe_info(enclave_manager, with_metadata)
203        .map_err(|e| e.add_subaction(String::from("Execute Describe Enclave command")))?;
204
205    connection.println(
206        serde_json::to_string_pretty(&info)
207            .map_err(|err| {
208                new_nitro_cli_failure!(
209                    &format!("Failed to display enclave describe data: {err:?}"),
210                    NitroCliErrorEnum::SerdeError
211                )
212            })?
213            .as_str(),
214    )
215}