From 3c1378b7eb274d5cd1a3a825c67b7a73f38d97a3 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Tue, 26 Nov 2019 13:30:13 +0000 Subject: [PATCH] Add ioctls on /dev/sgx for SGX remote attestation 1. Add ioctl command `SGXIOC_GET_EPID_GROUP_ID` for /dev/sgx 2. Add ioctl command `SGXIOC_GEN_QUOTE` for /dev/sgx 3. Add test cases --- src/Enclave.edl | 20 +- src/libos/Cargo.lock | 16 ++ src/libos/Cargo.toml | 2 + src/libos/Makefile | 2 +- src/libos/src/fs/dev_sgx.rs | 34 --- src/libos/src/fs/dev_sgx/attestation/mod.rs | 17 ++ .../attestation/sgx_attestation_agent.rs | 206 ++++++++++++++++++ .../src/fs/dev_sgx/attestation/sgx_quote.rs | 84 +++++++ src/libos/src/fs/dev_sgx/consts.rs | 22 ++ src/libos/src/fs/dev_sgx/mod.rs | 92 ++++++++ src/libos/src/lib.rs | 2 + src/pal/attestation.c | 45 ++++ test/ioctl/Makefile | 4 +- test/ioctl/main.c | 111 +++++++++- 14 files changed, 609 insertions(+), 48 deletions(-) delete mode 100644 src/libos/src/fs/dev_sgx.rs create mode 100644 src/libos/src/fs/dev_sgx/attestation/mod.rs create mode 100644 src/libos/src/fs/dev_sgx/attestation/sgx_attestation_agent.rs create mode 100644 src/libos/src/fs/dev_sgx/attestation/sgx_quote.rs create mode 100644 src/libos/src/fs/dev_sgx/consts.rs create mode 100644 src/libos/src/fs/dev_sgx/mod.rs create mode 100644 src/pal/attestation.c diff --git a/src/Enclave.edl b/src/Enclave.edl index 1d7545ac..2192fa8a 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -1,11 +1,13 @@ enclave { - from "sgx_stdio.edl" import *; from "sgx_backtrace.edl" import *; + from "sgx_stdio.edl" import *; from "sgx_tstdc.edl" import *; from "sgx_tstd.edl" import *; from "sgx_tprotected_fs.edl" import *; from "sgx_net.edl" import *; + include "sgx_quote.h" + trusted { /* define ECALLs here. */ public int libos_boot([in, string] const char* executable_path, [user_check] const char** argv); @@ -24,5 +26,19 @@ enclave { void ocall_sched_yield(void); int ocall_sched_getaffinity([out] int *error, int pid, size_t cpusize, [out, size=cpusize] unsigned char* buf); int ocall_sched_setaffinity([out] int *error, int pid, size_t cpusize, [in, size=cpusize] const unsigned char* buf); - }; + + sgx_status_t ocall_sgx_init_quote( + [out] sgx_target_info_t *target_info, + [out] sgx_epid_group_id_t *epid_group_id); + sgx_status_t ocall_sgx_get_quote( + [in, size=sigrl_len] uint8_t* sigrl, + uint32_t sigrl_len, + [in] sgx_report_t* report, + sgx_quote_sign_type_t quote_type, + [in] sgx_spid_t* spid, + [in] sgx_quote_nonce_t* nonce, + [out] sgx_report_t* qe_report, + [out, size=quote_buf_len] uint8_t* quote_buf, + uint32_t quote_buf_len); + }; }; diff --git a/src/libos/Cargo.lock b/src/libos/Cargo.lock index 6c5f14d6..68e7c303 100644 --- a/src/libos/Cargo.lock +++ b/src/libos/Cargo.lock @@ -14,7 +14,9 @@ dependencies = [ "rcore-fs-sefs 0.1.0", "serde 1.0.84", "serde_json 1.0.36", + "sgx_tcrypto 1.0.6", "sgx_trts 1.0.6", + "sgx_tse 1.0.6", "sgx_tstd 1.0.6", "sgx_types 1.0.6", "xmas-elf 0.6.2", @@ -354,6 +356,13 @@ dependencies = [ "sgx_types 1.0.6", ] +[[package]] +name = "sgx_tcrypto" +version = "1.0.6" +dependencies = [ + "sgx_types 1.0.6", +] + [[package]] name = "sgx_tprotected_fs" version = "1.0.6" @@ -370,6 +379,13 @@ dependencies = [ "sgx_types 1.0.6", ] +[[package]] +name = "sgx_tse" +version = "1.0.6" +dependencies = [ + "sgx_types 1.0.6", +] + [[package]] name = "sgx_tstd" version = "1.0.6" diff --git a/src/libos/Cargo.toml b/src/libos/Cargo.toml index 20a1538d..b54ca59e 100644 --- a/src/libos/Cargo.toml +++ b/src/libos/Cargo.toml @@ -29,3 +29,5 @@ xmas-elf = { path = "../../deps/xmas-elf" } sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" } sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] } sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" } +sgx_tse = { path = "../../deps/rust-sgx-sdk/sgx_tse" } +sgx_tcrypto = { path = "../../deps/rust-sgx-sdk/sgx_tcrypto" } diff --git a/src/libos/Makefile b/src/libos/Makefile index 5e196bb7..b4755cc0 100644 --- a/src/libos/Makefile +++ b/src/libos/Makefile @@ -30,7 +30,7 @@ LIBOS_RS_A := $(BUILD_DIR)/lib/libocclum_rs.a LIBCOMPILER_RT_PATCH_A := $(BUILD_DIR)/lib/libcompiler-rt-patch.a # All source code -RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs) +RUST_SRCS := $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs src/*/*/*/*.rs) RUST_TARGET_DIR := $(BUILD_DIR)/src/libos/cargo-target RUST_OUT_DIR := $(BUILD_DIR)/lib EDL_C_SRCS := $(addprefix $(BUILD_DIR)/src/libos/,src/Enclave_t.c src/Enclave_t.h) diff --git a/src/libos/src/fs/dev_sgx.rs b/src/libos/src/fs/dev_sgx.rs deleted file mode 100644 index 8cf7b841..00000000 --- a/src/libos/src/fs/dev_sgx.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::*; - -#[derive(Debug)] -pub struct DevSgx; - -const SGX_MAGIC_CHAR: u8 = 's' as u8; - -/// Ioctl to check if EDMM (Enclave Dynamic Memory Management) is supported -const SGX_CMD_NUM_IS_EDMM_SUPPORTED: u32 = - StructuredIoctlNum::new::(0, SGX_MAGIC_CHAR, StructuredIoctlArgType::Output).as_u32(); - -impl File for DevSgx { - fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> { - let nonbuiltin_cmd = match cmd { - IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, - _ => return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"), - }; - let cmd_num = nonbuiltin_cmd.cmd_num().as_u32(); - match cmd_num { - SGX_CMD_NUM_IS_EDMM_SUPPORTED => { - let arg = nonbuiltin_cmd.arg_mut::()?; - *arg = 0; // no support for now - } - _ => { - return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"); - } - } - Ok(()) - } - - fn as_any(&self) -> &Any { - self - } -} diff --git a/src/libos/src/fs/dev_sgx/attestation/mod.rs b/src/libos/src/fs/dev_sgx/attestation/mod.rs new file mode 100644 index 00000000..ebbbc428 --- /dev/null +++ b/src/libos/src/fs/dev_sgx/attestation/mod.rs @@ -0,0 +1,17 @@ +//! SGX attestation. + +use super::*; + +use sgx_tse::*; +use sgx_types::*; + +mod sgx_attestation_agent; +mod sgx_quote; + +pub use sgx_types::{ + sgx_epid_group_id_t, sgx_quote_nonce_t, sgx_quote_sign_type_t, sgx_quote_t, sgx_report_data_t, + sgx_spid_t, sgx_target_info_t, +}; + +pub use self::sgx_attestation_agent::SgxAttestationAgent; +pub use self::sgx_quote::SgxQuote; diff --git a/src/libos/src/fs/dev_sgx/attestation/sgx_attestation_agent.rs b/src/libos/src/fs/dev_sgx/attestation/sgx_attestation_agent.rs new file mode 100644 index 00000000..4077f7a3 --- /dev/null +++ b/src/libos/src/fs/dev_sgx/attestation/sgx_attestation_agent.rs @@ -0,0 +1,206 @@ +//! SGX EPID group ID retrieval and quote generation. + +use super::*; + +pub struct SgxAttestationAgent { + inner: Option, +} + +impl SgxAttestationAgent { + pub fn new() -> SgxAttestationAgent { + Self { inner: None } + } + + pub fn get_epid_group_id(&mut self) -> Result { + self.init_inner()?; + self.inner.as_mut().unwrap().get_epid_group_id() + } + + pub fn generate_quote( + &mut self, + sigrl: Option<&[u8]>, + report_data: &sgx_report_data_t, + quote_type: sgx_quote_sign_type_t, + spid: &sgx_spid_t, + nonce: &sgx_quote_nonce_t, + ) -> Result { + self.init_inner()?; + self.inner + .as_mut() + .unwrap() + .generate_quote(sigrl, report_data, quote_type, spid, nonce) + } + + fn init_inner(&mut self) -> Result<()> { + if self.inner.is_none() { + let inner = InnerAgent::new()?; + self.inner = Some(inner); + } + Ok(()) + } +} + +struct InnerAgent { + target_info: sgx_target_info_t, + epid_group_id: sgx_epid_group_id_t, +} + +impl InnerAgent { + pub fn new() -> Result { + let (target_info, epid_group_id) = Self::init_fields()?; + Ok(Self { + target_info, + epid_group_id, + }) + } + + fn init_fields() -> Result<(sgx_target_info_t, sgx_epid_group_id_t)> { + extern "C" { + pub fn ocall_sgx_init_quote( + retval: *mut sgx_status_t, + target_info: *mut sgx_target_info_t, + epid_group_id: *mut sgx_epid_group_id_t, + ) -> sgx_status_t; + } + + let mut target_info = Default::default(); + let mut epid_group_id = Default::default(); + unsafe { + let mut retval = Default::default(); + let status = ocall_sgx_init_quote( + &mut retval as *mut sgx_status_t, + &mut target_info as *mut sgx_target_info_t, + &mut epid_group_id as *mut sgx_epid_group_id_t, + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + + if (retval != sgx_status_t::SGX_SUCCESS) { + return_errno!(EINVAL, "ocall_sgx_init_quote failed"); + } + } + + Ok((target_info, epid_group_id)) + } + + pub fn get_epid_group_id(&self) -> Result { + Ok(self.epid_group_id) + } + + pub fn generate_quote( + &mut self, + sigrl: Option<&[u8]>, + report_data: &sgx_report_data_t, + quote_type: sgx_quote_sign_type_t, + spid: &sgx_spid_t, + nonce: &sgx_quote_nonce_t, + ) -> Result { + extern "C" { + pub fn ocall_sgx_get_quote( + retval: *mut sgx_status_t, // Output + sigrl: *const u8, // Input (optional) + sigrl_len: u32, // Input (optional) + report: *const sgx_report_t, // Input + quote_type: sgx_quote_sign_type_t, // Input + spid: *const sgx_spid_t, // Input + nonce: *const sgx_quote_nonce_t, // Input + qe_report: *mut sgx_report_t, // Output + quote_buf_ptr: *mut u8, // Output + quote_buf_len: u32, // Input + ) -> sgx_status_t; + } + + // Prepare argments for OCall + let (sigrl_ptr, sigrl_size): (*const u8, u32) = { + match sigrl { + Some(sigrl) => { + let sigrl_ptr = sigrl.as_ptr(); + let sigrl_size = { + // FIXME: u32::MAX is not provided by Rust SGX SDK + /* + if sigrl.len() > u32::MAX { + return_errno!(EINVAL, "sigrl is too large"); + } + */ + sigrl.len() as u32 + }; + (sigrl_ptr, sigrl_size) + } + None => (std::ptr::null(), 0), + } + }; + let report = rsgx_create_report(&self.target_info, report_data) + .map_err(|_e| errno!(EINVAL, "sgx_error"))?; + let mut qe_report = sgx_report_t::default(); + // TODO: what if quote_buf is not big enough? + let mut quote_buf = [0_u8; 4096]; + + // Do OCall + unsafe { + let mut retval = Default::default(); + let status = ocall_sgx_get_quote( + &mut retval as *mut sgx_status_t, + sigrl_ptr, + sigrl_size, + &report as *const sgx_report_t, + quote_type, + spid as *const sgx_spid_t, + nonce as *const sgx_quote_nonce_t, + &mut qe_report as *mut sgx_report_t, + quote_buf.as_mut_ptr() as *mut u8, + quote_buf.len() as u32, + ); + assert!(status == sgx_status_t::SGX_SUCCESS); + + if retval != sgx_status_t::SGX_SUCCESS { + match retval { + sgx_status_t::SGX_ERROR_BUSY => { + return_errno!(EBUSY, "ocall_sgx_get_quote is temporarily busy") + } + _ => return_errno!(EINVAL, "ocall_sgx_get_quote failed"), + } + } + } + + // Make sure the QE report is valid + SgxQeReportValidator::new(&self.target_info, nonce).validate(&qe_report)?; + + // Construct the resulting quote + let quote = SgxQuote::new("e_buf, &nonce, &qe_report)?; + + Ok(quote) + } +} + +/// Validating SGX Quoting Enclave (QE) report. +struct SgxQeReportValidator<'a> { + target_info: &'a sgx_target_info_t, + nonce: &'a sgx_quote_nonce_t, +} + +impl<'a> SgxQeReportValidator<'a> { + pub fn new(target_info: &'a sgx_target_info_t, nonce: &'a sgx_quote_nonce_t) -> Self { + SgxQeReportValidator { target_info, nonce } + } + + pub fn validate(&self, qe_report: &sgx_report_t) -> Result<()> { + self.validate_integrity(qe_report)?; + self.validate_platform(qe_report)?; + Ok(()) + } + + fn validate_integrity(&self, qe_report: &sgx_report_t) -> Result<()> { + rsgx_verify_report(qe_report) + .map_err(|_e| errno!(EINVAL, "quote report is NOT authentic"))?; + Ok(()) + } + + fn validate_platform(&self, qe_report: &sgx_report_t) -> Result<()> { + if self.target_info.mr_enclave.m != qe_report.body.mr_enclave.m + || self.target_info.attributes.flags != qe_report.body.attributes.flags + || self.target_info.attributes.xfrm != qe_report.body.attributes.xfrm + { + return_errno!(EINVAL, "quote report is NOT produced on the same platform"); + } + Ok(()) + } +} diff --git a/src/libos/src/fs/dev_sgx/attestation/sgx_quote.rs b/src/libos/src/fs/dev_sgx/attestation/sgx_quote.rs new file mode 100644 index 00000000..2e54cb1b --- /dev/null +++ b/src/libos/src/fs/dev_sgx/attestation/sgx_quote.rs @@ -0,0 +1,84 @@ +//! SGX Quote in a memory safe representation and with hash validation. + +use super::*; + +#[derive(Debug, Default)] +pub struct SgxQuote { + quote_buf: Vec, +} + +impl SgxQuote { + pub fn new( + quote_raw_buf: &[u8], + quote_nonce: &sgx_quote_nonce_t, + qe_report: &sgx_report_t, + ) -> Result { + let quote_buf = Self::new_buf(quote_raw_buf)?; + Self::validate_quote_buf("e_buf, quote_nonce, qe_report)?; + Ok(SgxQuote { quote_buf }) + } + + fn new_buf(quote_raw_buf: &[u8]) -> Result> { + if quote_raw_buf.len() < std::mem::size_of::() { + return_errno!(EINVAL, "buffer is too small for SGX quote itself"); + } + let quote = unsafe { &*(quote_raw_buf.as_ptr() as *const sgx_quote_t) }; + let quote_size = std::mem::size_of::() + quote.signature_len as usize; + if quote_size > quote_raw_buf.len() { + return_errno!(EINVAL, "buffer is too small for SGX quote with signature"); + } + let quote_buf = quote_raw_buf[..quote_size].to_vec(); + Ok(quote_buf) + } + + fn validate_quote_buf( + quote_buf: &[u8], + quote_nonce: &sgx_quote_nonce_t, + qe_report: &sgx_report_t, + ) -> Result<()> { + // According to Intel manual: + // The purpose of QE report (`qe_report`) is for the ISV enclave to confirm the + // quote (i.e., `quote_buf`) received is not modified by the untrusted SW stack, + // and not a replay. The implementation in QE is to generate a report targeting + // the ISV enclave (i.e., Occlum's enclave), with the lower 32 bytes in + // QE report (i.e., `qe_reprot.data.d[..32]`) equivalent to SHA256(quote_nonce||quote_buf). + let expected_hash = &qe_report.body.report_data.d[..32]; + let actual_hash = { + let mut quote_nonce_and_buf: Vec = quote_nonce + .rand + .iter() + .chain(quote_buf.iter()) + .cloned() + .collect(); + sgx_tcrypto::rsgx_sha256_slice("e_nonce_and_buf).unwrap() + }; + if expected_hash != actual_hash { + return_errno!(EINVAL, "invalid quote (unexpected hash)"); + } + Ok(()) + } + + pub fn get_fields(&self) -> &sgx_quote_t { + unsafe { &*(self.quote_buf.as_ptr() as *const sgx_quote_t) } + } + + pub fn get_signature(&self) -> &[u8] { + &self.quote_buf[std::mem::size_of::()..] + } + + pub fn get_size(&self) -> usize { + self.quote_buf.len() + } + + pub fn dump_to_buf(&self, dst_buf: &mut [u8]) -> Result<()> { + let src_buf = &self.quote_buf; + if src_buf.len() > dst_buf.len() { + return_errno!( + EINVAL, + "the given output buffer for quote is NOT big enough" + ); + } + dst_buf[..src_buf.len()].copy_from_slice(src_buf); + Ok(()) + } +} diff --git a/src/libos/src/fs/dev_sgx/consts.rs b/src/libos/src/fs/dev_sgx/consts.rs new file mode 100644 index 00000000..12c2b644 --- /dev/null +++ b/src/libos/src/fs/dev_sgx/consts.rs @@ -0,0 +1,22 @@ +use super::*; + +/// Ioctl to check if EDMM (Enclave Dynamic Memory Management) is supported +pub const SGX_CMD_NUM_IS_EDMM_SUPPORTED: u32 = + StructuredIoctlNum::new::(0, SGX_MAGIC_CHAR, StructuredIoctlArgType::Output).as_u32(); +/// Ioctl to get the EPID group ID +pub const SGX_CMD_NUM_GET_EPID_GROUP_ID: u32 = StructuredIoctlNum::new::( + 1, + SGX_MAGIC_CHAR, + StructuredIoctlArgType::Output, +) +.as_u32(); +/// Ioctl to get quote +pub const SGX_CMD_NUM_GEN_QUOTE: u32 = StructuredIoctlNum::new::( + 2, + SGX_MAGIC_CHAR, + StructuredIoctlArgType::InputOutput, +) +.as_u32(); + +/// A magical number that distinguishes SGX ioctls for other ioctls +const SGX_MAGIC_CHAR: u8 = 's' as u8; diff --git a/src/libos/src/fs/dev_sgx/mod.rs b/src/libos/src/fs/dev_sgx/mod.rs new file mode 100644 index 00000000..5a084f44 --- /dev/null +++ b/src/libos/src/fs/dev_sgx/mod.rs @@ -0,0 +1,92 @@ +//! SGX Device (/dev/sgx). + +use super::*; + +mod attestation; +mod consts; + +use self::attestation::*; +use self::consts::*; + +#[derive(Debug)] +pub struct DevSgx; + +impl File for DevSgx { + fn ioctl(&self, cmd: &mut IoctlCmd) -> Result<()> { + let nonbuiltin_cmd = match cmd { + IoctlCmd::NonBuiltin(nonbuiltin_cmd) => nonbuiltin_cmd, + _ => return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"), + }; + let cmd_num = nonbuiltin_cmd.cmd_num().as_u32(); + match cmd_num { + SGX_CMD_NUM_IS_EDMM_SUPPORTED => { + let arg = nonbuiltin_cmd.arg_mut::()?; + *arg = 0; // no support for now + } + SGX_CMD_NUM_GET_EPID_GROUP_ID => { + let arg = nonbuiltin_cmd.arg_mut::()?; + *arg = SGX_ATTEST_AGENT.lock().unwrap().get_epid_group_id()?; + } + SGX_CMD_NUM_GEN_QUOTE => { + // Prepare the arguments + let arg = nonbuiltin_cmd.arg_mut::()?; + let sigrl = { + let sigrl_ptr = arg.sigrl_ptr; + let sigrl_len = arg.sigrl_len as usize; + if !sigrl_ptr.is_null() && sigrl_len > 0 { + let sigrl_slice = + unsafe { std::slice::from_raw_parts(sigrl_ptr, sigrl_len) }; + Some(sigrl_slice) + } else { + None + } + }; + let mut quote_output_buf = unsafe { + let quote_ptr = arg.quote_buf; + if quote_ptr.is_null() { + return_errno!(EINVAL, "the output buffer for quote cannot point to NULL"); + } + let quote_len = arg.quote_buf_len as usize; + std::slice::from_raw_parts_mut(quote_ptr, quote_len) + }; + + // Generate the quote + let quote = SGX_ATTEST_AGENT.lock().unwrap().generate_quote( + sigrl, + &arg.report_data, + arg.quote_type, + &arg.spid, + &arg.nonce, + )?; + quote.dump_to_buf(quote_output_buf)?; + } + _ => { + return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx"); + } + } + Ok(()) + } + + fn as_any(&self) -> &Any { + self + } +} + +lazy_static! { + /// The root of file system + pub static ref SGX_ATTEST_AGENT: SgxMutex = { + SgxMutex::new(SgxAttestationAgent::new()) + }; +} + +#[repr(C)] +struct IoctlGenQuoteArg { + report_data: sgx_report_data_t, // Input + quote_type: sgx_quote_sign_type_t, // Input + spid: sgx_spid_t, // Input + nonce: sgx_quote_nonce_t, // Input + sigrl_ptr: *const u8, // Input (optional) + sigrl_len: u32, // Input (optional) + quote_buf_len: u32, // Input + quote_buf: *mut u8, // Output +} diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index 8a968ad8..a3c2ac6a 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -18,7 +18,9 @@ extern crate sgx_types; #[cfg(not(target_env = "sgx"))] #[macro_use] extern crate sgx_tstd as std; +extern crate sgx_tcrypto; extern crate sgx_trts; +extern crate sgx_tse; extern crate xmas_elf; #[macro_use] extern crate lazy_static; diff --git a/src/pal/attestation.c b/src/pal/attestation.c new file mode 100644 index 00000000..f8c3952c --- /dev/null +++ b/src/pal/attestation.c @@ -0,0 +1,45 @@ +#include "sgx_uae_service.h" + +sgx_status_t ocall_sgx_init_quote( + sgx_target_info_t *target_info, + sgx_epid_group_id_t *epid_group_id) +{ + return sgx_init_quote(target_info, epid_group_id); +} + +sgx_status_t ocall_sgx_get_quote( + uint8_t* sigrl, + uint32_t sigrl_len, + sgx_report_t* report, + sgx_quote_sign_type_t quote_type, + sgx_spid_t* spid, + sgx_quote_nonce_t* nonce, + sgx_report_t* qe_report, + sgx_quote_t* quote_buf, + uint32_t quote_buf_len) +{ + sgx_status_t ret = SGX_SUCCESS; + + uint32_t real_quote_len; + ret = sgx_calc_quote_size(sigrl, sigrl_len, &real_quote_len); + if (ret != SGX_SUCCESS) { + return ret; + } + if (quote_buf_len < real_quote_len) { + return SGX_ERROR_INVALID_PARAMETER; + } + + // Intel's manual: + // It's suggested that the caller should wait (typically several seconds + // to ten of seconds) and retry this API if SGX_ERROR_BUSY is returned. + ret = sgx_get_quote(report, + quote_type, + spid, + nonce, + sigrl, + sigrl_len, + qe_report, + quote_buf, + real_quote_len); + return ret; +} diff --git a/test/ioctl/Makefile b/test/ioctl/Makefile index 9e1b6dec..8645f1a9 100644 --- a/test/ioctl/Makefile +++ b/test/ioctl/Makefile @@ -1,5 +1,7 @@ include ../test_common.mk -EXTRA_C_FLAGS := +SGX_SDK ?= /opt/intel/sgxsdk + +EXTRA_C_FLAGS := -I$(SGX_SDK)/include EXTRA_LINK_FLAGS := BIN_ARGS := diff --git a/test/ioctl/main.c b/test/ioctl/main.c index c4dfdef8..3324a1d7 100644 --- a/test/ioctl/main.c +++ b/test/ioctl/main.c @@ -1,14 +1,17 @@ #include #include #include +#include #include #include +#include #include #include +#include #include "test.h" // ============================================================================ -// Test cases for TTY ioctl +// Test cases for TTY ioctls // ============================================================================ int test_tty_ioctl_TIOCGWINSZ(void) { @@ -20,17 +23,30 @@ int test_tty_ioctl_TIOCGWINSZ(void) { } // ============================================================================ -// Test cases for SGX ioctl +// Test cases for SGX ioctls // ============================================================================ +typedef struct { + sgx_report_data_t report_data; // input + sgx_quote_sign_type_t quote_type; // input + sgx_spid_t spid; // input + sgx_quote_nonce_t nonce; // input + const uint8_t* sigrl_ptr; // input (optional) + uint32_t sigrl_len; // input (optional) + uint32_t quote_buf_len; // input + union { + uint8_t* as_buf; + sgx_quote_t* as_quote; + } quote; // output +} sgxioc_gen_quote_arg_t; + #define SGXIOC_IS_EDDM_SUPPORTED _IOR('s', 0, int) +#define SGXIOC_GET_EPID_GROUP_ID _IOR('s', 1, sgx_epid_group_id_t) +#define SGXIOC_GEN_QUOTE _IOWR('s', 2, sgxioc_gen_quote_arg_t) -int test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED(void) { - int sgx_fd; - if ((sgx_fd = open("/dev/sgx", O_RDONLY)) < 0) { - THROW_ERROR("failed to open /dev/sgx "); - } +typedef int(*sgx_ioctl_test_body_t)(int sgx_fd); +static int do_SGXIOC_IS_EDDM_SUPPORTED(int sgx_fd) { int is_edmm_supported = 0; if (ioctl(sgx_fd, SGXIOC_IS_EDDM_SUPPORTED, &is_edmm_supported) < 0) { THROW_ERROR("failed to ioctl /dev/sgx"); @@ -38,18 +54,93 @@ int test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED(void) { if (is_edmm_supported != 0) { THROW_ERROR("SGX EDMM supported are not expected to be enabled"); } - - close(sgx_fd); return 0; } +static int do_SGXIOC_GET_EPID_GROUP_ID(int sgx_fd) { + sgx_epid_group_id_t epid_group_id = { 0 }; + if (ioctl(sgx_fd, SGXIOC_GET_EPID_GROUP_ID, &epid_group_id) < 0) { + THROW_ERROR("failed to ioctl /dev/sgx"); + } + return 0; +} + +static int do_SGXIOC_GEN_QUOTE(int sgx_fd) { + uint8_t quote_buf[2048] = { 0 }; + sgxioc_gen_quote_arg_t gen_quote_arg = { + .report_data = { { 0 } }, // input (empty is ok) + .quote_type = SGX_LINKABLE_SIGNATURE, // input + .spid = { { 0 } }, // input (empty is ok) + .nonce = { { 0 } }, // input (empty is ok) + .sigrl_ptr = NULL, // input (optional) + .sigrl_len = 0, // input (optional) + .quote_buf_len = sizeof(quote_buf), // input + .quote = { .as_buf = (uint8_t*) quote_buf } // output + }; + + while (1) { + int ret = ioctl(sgx_fd, SGXIOC_GEN_QUOTE, &gen_quote_arg); + if (ret == 0) { + break; + } + else if (errno == EAGAIN) { + printf("WARN: /dev/sgx is temporarily busy. Try again after 1 second."); + sleep(1); + } + else { + THROW_ERROR("failed to ioctl /dev/sgx"); + } + } + + sgx_quote_t* quote = (sgx_quote_t*)quote_buf; + if (quote->sign_type != SGX_LINKABLE_SIGNATURE) { + THROW_ERROR("invalid quote: wrong sign type"); + } + if (quote->signature_len == 0) { + THROW_ERROR("invalid quote: zero-length signature"); + } + if (memcmp(&gen_quote_arg.report_data, "e->report_body.report_data, sizeof(sgx_report_data_t)) != 0) { + THROW_ERROR("invalid quote: wrong report data"); + } + return 0; +} + +static int do_sgx_ioctl_test(sgx_ioctl_test_body_t test_body) { + // Init test + int sgx_fd; + if ((sgx_fd = open("/dev/sgx", O_RDONLY)) < 0) { + THROW_ERROR("failed to open /dev/sgx "); + } + + // Do test + int ret = test_body(sgx_fd); + + // Clean up test + close(sgx_fd); + return ret; +} + +int test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED(void) { + return do_sgx_ioctl_test(do_SGXIOC_IS_EDDM_SUPPORTED); +} + +int test_sgx_ioctl_SGXIOC_GET_EPID_GROUP_ID(void) { + return do_sgx_ioctl_test(do_SGXIOC_GET_EPID_GROUP_ID); +} + +int test_sgx_ioctl_SGXIOC_GEN_QUOTE(void) { + return do_sgx_ioctl_test(do_SGXIOC_GEN_QUOTE); +} + // ============================================================================ // Test suite // ============================================================================ static test_case_t test_cases[] = { TEST_CASE(test_tty_ioctl_TIOCGWINSZ), - TEST_CASE(test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED) + TEST_CASE(test_sgx_ioctl_SGXIOC_IS_EDDM_SUPPORTED), + TEST_CASE(test_sgx_ioctl_SGXIOC_GET_EPID_GROUP_ID), + TEST_CASE(test_sgx_ioctl_SGXIOC_GEN_QUOTE) }; int main() {