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

feat(cli): print-fmspc command #247

Merged
merged 14 commits into from
Oct 10, 2024
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ homepage.workspace = true
categories = ["command-line-utilities", "cryptography::cryptocurrencies", "hardware-support", "wasm"]
keywords = ["cosmos", "cosmwasm", "cycles", "quartz", "sgx"]
readme = "README.md"
default-run = "quartz"
description = """
A CLI tool to streamline development and deployment of Quartz applications. Quartz is a flexible framework for privacy-preserving computation via Trusted Execution Environments (TEEs) organized and secured by smart contracts.
"""
Expand All @@ -18,6 +19,10 @@ A CLI tool to streamline development and deployment of Quartz applications. Quar
name = "quartz"
path = "src/main.rs"

[[bin]]
name = "gen-quote"
path = "src/bin/gen-quote.rs"

[dependencies]
async-trait.workspace = true
cargo-generate.workspace = true
Expand Down Expand Up @@ -53,6 +58,7 @@ figment = { version = "0.10.19", features = ["env", "toml"] }
clearscreen = "3.0.0"
cargo_metadata = "0.18.1"
serde_with = "3.10.0"
dcap-qvl = "0.1.0"

# cosmos
cosmrs.workspace = true
Expand Down
42 changes: 42 additions & 0 deletions crates/cli/src/bin/gen-quote.manifest.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Manifest file for creating dummy quotes

libos.entrypoint = "{{ gen_quote_bin_path }}"

loader.log_level = "{{ log_level }}"
loader.entrypoint = "file:{{ gramine.libos }}"
loader.env.LD_LIBRARY_PATH = "/lib:/usr/local/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}"
loader.env.HOME = "{{ home }}"
loader.env.INSIDE_SGX = "1"
loader.env.TLS = { passthrough = true }
loader.env.RA_TYPE = { passthrough = true }
loader.env.RA_TLS_ALLOW_DEBUG_ENCLAVE_INSECURE = { passthrough = true }
loader.env.RA_TLS_ALLOW_OUTDATED_TCB_INSECURE = { passthrough = true }
loader.env.RA_TLS_MRENCLAVE = { passthrough = true }
loader.env.RA_TLS_MRSIGNER = { passthrough = true }
loader.env.RA_TLS_ISV_SVN = { passthrough = true }
loader.env.RA_TLS_ISV_PROD_ID = { passthrough = true }
loader.env.MYAPP_DATA = { passthrough = true }

fs.mounts = [
{ uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },
{ uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
{ uri = "file:/usr/{{ arch_libdir }}", path = "/usr{{ arch_libdir }}" },
{ uri = "file:{{ gen_quote_bin_path }}", path = "{{ gen_quote_bin_path }}" },
]

sgx.enclave_size = "512M"
sgx.max_threads = 2
sgx.edmm_enable = {{ 'true' if env.get('EDMM', '0') == '1' else 'false' }}

sgx.remote_attestation = "{{ ra_type }}"
sgx.ra_client_linkable = {{ 'true' if ra_client_linkable == '1' else 'false' }}

sgx.trusted_files = [
"file:{{ gramine.libos }}",
"file:{{ gen_quote_bin_path }}",
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/usr/{{ arch_libdir }}/",
]

sys.enable_sigterm_injection = true
20 changes: 20 additions & 0 deletions crates/cli/src/bin/gen-quote.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::{
fs::File,
io::{self, Read, Write},
};

fn main() -> io::Result<()> {
let user_data = [0u8; 64];
let mut user_report_data = File::create("/dev/attestation/user_report_data")?;
user_report_data.write_all(user_data.as_slice())?;
user_report_data.flush()?;

let mut file = File::open("/dev/attestation/quote")?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;

let quote_hex = hex::encode(&buffer);
print!("{}", quote_hex);

Ok(())
}
4 changes: 4 additions & 0 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub enum Command {

/// Build, deploy, perform handshake, and run quartz app while listening for changes
Dev(DevArgs),

/// Print the FMSPC of the current platform (SGX only)
PrintFmspc,
}

#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -296,6 +299,7 @@ impl ToFigment for Command {
Command::Dev(args) => Figment::from(Serialized::defaults(args))
.merge(Serialized::defaults(&args.contract_deploy))
.merge(Serialized::defaults(&args.enclave_build)),
Command::PrintFmspc => Figment::default(),
}
}
}
2 changes: 2 additions & 0 deletions crates/cli/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
pub mod init;
pub mod print_fmspc;

#[async_trait]
pub trait Handler {
Expand All @@ -33,6 +34,7 @@ impl Handler for Request {
Request::EnclaveBuild(request) => request.handle(config).await,
Request::EnclaveStart(request) => request.handle(config).await,
Request::Dev(request) => request.handle(config).await,
Request::PrintFmspc(request) => request.handle(config).await,
}
.map(Into::into)
}
Expand Down
167 changes: 167 additions & 0 deletions crates/cli/src/handler/print_fmspc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
use std::{env, path::PathBuf, process::Stdio};

use async_trait::async_trait;
use color_eyre::{
eyre::{eyre, Context},
owo_colors::OwoColorize,
Report, Result,
};
use dcap_qvl::collateral::get_collateral;
use tempfile::tempdir;
use tokio::{fs::File, io::AsyncWriteExt, process::Command};
use tracing::{debug, info};

use crate::{
config::Config,
handler::Handler,
request::print_fmspc::PrintFmspcRequest,
response::{print_fmspc::PrintFmspcResponse, Response},
};

const GEN_QUOTE_MANIFEST_TEMPLATE: &str = include_str!("../bin/gen-quote.manifest.template");
const DEFAULT_PCCS_URL: &str = "https://localhost:8081/sgx/certification/v4/";

#[async_trait]
impl Handler for PrintFmspcRequest {
type Response = Response;

async fn handle<C: AsRef<Config> + Send>(self, config: C) -> Result<Self::Response, Report> {
let config = config.as_ref().clone();

if config.mock_sgx {
return Err(eyre!(
"MOCK_SGX is enabled! print-fmpsc is only available if SGX is enabled"
));
}

let current_exe_path =
env::current_exe().context("Failed to get current executable path")?;
let exe_path_str = current_exe_path.to_string_lossy();

if exe_path_str.contains("target") {
// i.e. this isn't a `cargo install` based installation

info!("{}", "\nBuilding dummy enclave".blue().bold());

let mut cargo = Command::new("cargo");
let command = cargo.arg("build");

if exe_path_str.contains("release") {
// add the release flag to make sure it's built in the right place
command.arg("--release");
}

let status = command.status().await?;

if !status.success() {
return Err(eyre!("Couldn't build enclave. {:?}", status));
}
}

debug!("{}", "\nGenerating SGX private key".blue().bold());

let _ = Command::new("gramine-sgx-gen-private-key")
.output()
.await
.map_err(|e| eyre!("Failed to execute gramine-sgx-gen-private-key: {}", e))?;

let host = target_lexicon::HOST;
let arch_libdir = format!(
"/lib/{}-{}-{}",
host.architecture, host.operating_system, host.environment
);

let home_dir = dirs::home_dir()
.ok_or_else(|| eyre!("Home directory not set"))?
.display()
.to_string();

let gen_quote_bin_path = file_path(current_exe_path.clone(), "gen-quote");

let temp_dir = tempdir()?;
let temp_dir_path = temp_dir.path();

let gen_quote_manifest_path = temp_dir_path.join("gen-quote.manifest.template");
let mut gen_quote_manifest_file = File::create(&gen_quote_manifest_path).await?;
gen_quote_manifest_file
.write_all(GEN_QUOTE_MANIFEST_TEMPLATE.as_bytes())
.await?;

let status = Command::new("gramine-manifest")
.arg("-Dlog_level=error")
.arg(format!("-Dhome={}", home_dir))
.arg(format!("-Darch_libdir={}", arch_libdir))
.arg("-Dra_type=dcap")
.arg("-Dra_client_linkable=1")
.arg(format!(
"-Dgen_quote_bin_path={}",
gen_quote_bin_path.display()
))
.arg(gen_quote_manifest_path)
.arg("gen-quote.manifest")
.current_dir(temp_dir_path)
.status()
.await
.map_err(|e| eyre!("Failed to execute gramine-manifest: {}", e))?;

if !status.success() {
return Err(eyre!(
"gramine-manifest command failed with status: {:?}",
status
));
}

let status = Command::new("gramine-sgx-sign")
.arg("--manifest")
.arg("gen-quote.manifest")
.arg("--output")
.arg("gen-quote.manifest.sgx")
.current_dir(temp_dir_path)
.status()
.await
.map_err(|e| eyre!("Failed to execute gramine-sgx-sign: {}", e))?;

if !status.success() {
return Err(eyre!(
"gramine-sgx-sign command failed with status: {:?}",
status
));
}

info!("{}", "\nGenerating dummy quote".blue().bold());

let child = Command::new("gramine-sgx")
.arg("./gen-quote")
.kill_on_drop(true)
.current_dir(temp_dir_path)
.stdout(Stdio::piped()) // Redirect stdout to a pipe
.stderr(Stdio::piped()) // Redirect stderr to a pipe
.spawn()
.map_err(|e| eyre!("Failed to spawn gramine-sgx child process: {}", e))?;

let output = child.wait_with_output().await?;
if !output.status.success() {
return Err(eyre!("Couldn't build enclave. {:?}", status));
}

let quote = hex::decode(output.stdout)?;

let collateral =
get_collateral(DEFAULT_PCCS_URL, &quote, std::time::Duration::from_secs(10))
.await
.expect("failed to get collateral");
let tcb_info: serde_json::Value = serde_json::from_str(&collateral.tcb_info)
.expect("Retrieved Tcbinfo is not valid JSON");

Ok(PrintFmspcResponse {
fmspc: tcb_info["fmspc"].to_string(),
}
.into())
}
}

fn file_path(mut current_exe_path: PathBuf, file_name: &str) -> PathBuf {
current_exe_path.pop();
current_exe_path.push(file_name);
current_exe_path
}
6 changes: 5 additions & 1 deletion crates/cli/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
request::{
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
dev::DevRequest, enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
handshake::HandshakeRequest, init::InitRequest,
handshake::HandshakeRequest, init::InitRequest, print_fmspc::PrintFmspcRequest,
},
};

Expand All @@ -17,6 +17,8 @@ pub mod enclave_start;
pub mod handshake;
pub mod init;

pub mod print_fmspc;

#[derive(Clone, Debug)]
pub enum Request {
Init(InitRequest),
Expand All @@ -26,6 +28,7 @@ pub enum Request {
EnclaveBuild(EnclaveBuildRequest),
EnclaveStart(EnclaveStartRequest),
Dev(DevRequest),
PrintFmspc(PrintFmspcRequest),
}

impl TryFrom<Command> for Request {
Expand Down Expand Up @@ -62,6 +65,7 @@ impl TryFrom<Command> for Request {
}
.into())
}
Command::PrintFmspc => Ok(Request::PrintFmspc(PrintFmspcRequest)),
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions crates/cli/src/request/print_fmspc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::request::Request;

#[derive(Clone, Debug)]
pub struct PrintFmspcRequest;

impl From<PrintFmspcRequest> for Request {
fn from(request: PrintFmspcRequest) -> Self {
Self::PrintFmspc(request)
}
}
5 changes: 4 additions & 1 deletion crates/cli/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::Serialize;
use crate::response::{
contract_build::ContractBuildResponse, contract_deploy::ContractDeployResponse,
dev::DevResponse, enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse,
handshake::HandshakeResponse, init::InitResponse,
handshake::HandshakeResponse, init::InitResponse, print_fmspc::PrintFmspcResponse,
};

pub mod contract_build;
Expand All @@ -14,6 +14,8 @@ pub mod enclave_start;
pub mod handshake;
pub mod init;

pub mod print_fmspc;

#[derive(Clone, Debug, Serialize)]
pub enum Response {
Init(InitResponse),
Expand All @@ -23,4 +25,5 @@ pub enum Response {
EnclaveBuild(EnclaveBuildResponse),
EnclaveStart(EnclaveStartResponse),
Dev(DevResponse),
PrintFmspc(PrintFmspcResponse),
}
14 changes: 14 additions & 0 deletions crates/cli/src/response/print_fmspc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use serde::Serialize;

use crate::response::Response;

#[derive(Clone, Debug, Serialize)]
pub struct PrintFmspcResponse {
pub fmspc: String,
}

impl From<PrintFmspcResponse> for Response {
fn from(response: PrintFmspcResponse) -> Self {
Self::PrintFmspc(response)
}
}
Loading