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
This commit is contained in:
Tate, Hongliang Tian 2019-11-26 13:30:13 +00:00
parent 7024fa81ec
commit 3c1378b7eb
14 changed files with 609 additions and 48 deletions

@ -1,11 +1,13 @@
enclave { enclave {
from "sgx_stdio.edl" import *;
from "sgx_backtrace.edl" import *; from "sgx_backtrace.edl" import *;
from "sgx_stdio.edl" import *;
from "sgx_tstdc.edl" import *; from "sgx_tstdc.edl" import *;
from "sgx_tstd.edl" import *; from "sgx_tstd.edl" import *;
from "sgx_tprotected_fs.edl" import *; from "sgx_tprotected_fs.edl" import *;
from "sgx_net.edl" import *; from "sgx_net.edl" import *;
include "sgx_quote.h"
trusted { trusted {
/* define ECALLs here. */ /* define ECALLs here. */
public int libos_boot([in, string] const char* executable_path, [user_check] const char** argv); 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); 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_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); 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);
}; };
}; };

16
src/libos/Cargo.lock generated

@ -14,7 +14,9 @@ dependencies = [
"rcore-fs-sefs 0.1.0", "rcore-fs-sefs 0.1.0",
"serde 1.0.84", "serde 1.0.84",
"serde_json 1.0.36", "serde_json 1.0.36",
"sgx_tcrypto 1.0.6",
"sgx_trts 1.0.6", "sgx_trts 1.0.6",
"sgx_tse 1.0.6",
"sgx_tstd 1.0.6", "sgx_tstd 1.0.6",
"sgx_types 1.0.6", "sgx_types 1.0.6",
"xmas-elf 0.6.2", "xmas-elf 0.6.2",
@ -354,6 +356,13 @@ dependencies = [
"sgx_types 1.0.6", "sgx_types 1.0.6",
] ]
[[package]]
name = "sgx_tcrypto"
version = "1.0.6"
dependencies = [
"sgx_types 1.0.6",
]
[[package]] [[package]]
name = "sgx_tprotected_fs" name = "sgx_tprotected_fs"
version = "1.0.6" version = "1.0.6"
@ -370,6 +379,13 @@ dependencies = [
"sgx_types 1.0.6", "sgx_types 1.0.6",
] ]
[[package]]
name = "sgx_tse"
version = "1.0.6"
dependencies = [
"sgx_types 1.0.6",
]
[[package]] [[package]]
name = "sgx_tstd" name = "sgx_tstd"
version = "1.0.6" version = "1.0.6"

@ -29,3 +29,5 @@ xmas-elf = { path = "../../deps/xmas-elf" }
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" } sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] } sgx_tstd = { path = "../../deps/rust-sgx-sdk/sgx_tstd", features = ["backtrace"] }
sgx_trts = { path = "../../deps/rust-sgx-sdk/sgx_trts" } 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" }

@ -30,7 +30,7 @@ LIBOS_RS_A := $(BUILD_DIR)/lib/libocclum_rs.a
LIBCOMPILER_RT_PATCH_A := $(BUILD_DIR)/lib/libcompiler-rt-patch.a LIBCOMPILER_RT_PATCH_A := $(BUILD_DIR)/lib/libcompiler-rt-patch.a
# All source code # 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_TARGET_DIR := $(BUILD_DIR)/src/libos/cargo-target
RUST_OUT_DIR := $(BUILD_DIR)/lib RUST_OUT_DIR := $(BUILD_DIR)/lib
EDL_C_SRCS := $(addprefix $(BUILD_DIR)/src/libos/,src/Enclave_t.c src/Enclave_t.h) EDL_C_SRCS := $(addprefix $(BUILD_DIR)/src/libos/,src/Enclave_t.c src/Enclave_t.h)

@ -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::<i32>(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::<i32>()?;
*arg = 0; // no support for now
}
_ => {
return_errno!(EINVAL, "unknown ioctl cmd for /dev/sgx");
}
}
Ok(())
}
fn as_any(&self) -> &Any {
self
}
}

@ -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;

@ -0,0 +1,206 @@
//! SGX EPID group ID retrieval and quote generation.
use super::*;
pub struct SgxAttestationAgent {
inner: Option<InnerAgent>,
}
impl SgxAttestationAgent {
pub fn new() -> SgxAttestationAgent {
Self { inner: None }
}
pub fn get_epid_group_id(&mut self) -> Result<sgx_epid_group_id_t> {
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<SgxQuote> {
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<Self> {
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<sgx_epid_group_id_t> {
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<SgxQuote> {
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(&quote_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(())
}
}

@ -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<u8>,
}
impl SgxQuote {
pub fn new(
quote_raw_buf: &[u8],
quote_nonce: &sgx_quote_nonce_t,
qe_report: &sgx_report_t,
) -> Result<SgxQuote> {
let quote_buf = Self::new_buf(quote_raw_buf)?;
Self::validate_quote_buf(&quote_buf, quote_nonce, qe_report)?;
Ok(SgxQuote { quote_buf })
}
fn new_buf(quote_raw_buf: &[u8]) -> Result<Vec<u8>> {
if quote_raw_buf.len() < std::mem::size_of::<sgx_quote_t>() {
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::<sgx_quote_t>() + 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<u8> = quote_nonce
.rand
.iter()
.chain(quote_buf.iter())
.cloned()
.collect();
sgx_tcrypto::rsgx_sha256_slice(&quote_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::<sgx_quote_t>()..]
}
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(())
}
}

@ -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::<i32>(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::<sgx_epid_group_id_t>(
1,
SGX_MAGIC_CHAR,
StructuredIoctlArgType::Output,
)
.as_u32();
/// Ioctl to get quote
pub const SGX_CMD_NUM_GEN_QUOTE: u32 = StructuredIoctlNum::new::<IoctlGenQuoteArg>(
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;

@ -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::<i32>()?;
*arg = 0; // no support for now
}
SGX_CMD_NUM_GET_EPID_GROUP_ID => {
let arg = nonbuiltin_cmd.arg_mut::<sgx_epid_group_id_t>()?;
*arg = SGX_ATTEST_AGENT.lock().unwrap().get_epid_group_id()?;
}
SGX_CMD_NUM_GEN_QUOTE => {
// Prepare the arguments
let arg = nonbuiltin_cmd.arg_mut::<IoctlGenQuoteArg>()?;
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<SgxAttestationAgent> = {
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
}

@ -18,7 +18,9 @@ extern crate sgx_types;
#[cfg(not(target_env = "sgx"))] #[cfg(not(target_env = "sgx"))]
#[macro_use] #[macro_use]
extern crate sgx_tstd as std; extern crate sgx_tstd as std;
extern crate sgx_tcrypto;
extern crate sgx_trts; extern crate sgx_trts;
extern crate sgx_tse;
extern crate xmas_elf; extern crate xmas_elf;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;

45
src/pal/attestation.c Normal file

@ -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;
}

@ -1,5 +1,7 @@
include ../test_common.mk include ../test_common.mk
EXTRA_C_FLAGS := SGX_SDK ?= /opt/intel/sgxsdk
EXTRA_C_FLAGS := -I$(SGX_SDK)/include
EXTRA_LINK_FLAGS := EXTRA_LINK_FLAGS :=
BIN_ARGS := BIN_ARGS :=

@ -1,14 +1,17 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <sgx_quote.h>
#include "test.h" #include "test.h"
// ============================================================================ // ============================================================================
// Test cases for TTY ioctl // Test cases for TTY ioctls
// ============================================================================ // ============================================================================
int test_tty_ioctl_TIOCGWINSZ(void) { 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_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) { typedef int(*sgx_ioctl_test_body_t)(int sgx_fd);
int sgx_fd;
if ((sgx_fd = open("/dev/sgx", O_RDONLY)) < 0) {
THROW_ERROR("failed to open /dev/sgx ");
}
static int do_SGXIOC_IS_EDDM_SUPPORTED(int sgx_fd) {
int is_edmm_supported = 0; int is_edmm_supported = 0;
if (ioctl(sgx_fd, SGXIOC_IS_EDDM_SUPPORTED, &is_edmm_supported) < 0) { if (ioctl(sgx_fd, SGXIOC_IS_EDDM_SUPPORTED, &is_edmm_supported) < 0) {
THROW_ERROR("failed to ioctl /dev/sgx"); 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) { if (is_edmm_supported != 0) {
THROW_ERROR("SGX EDMM supported are not expected to be enabled"); THROW_ERROR("SGX EDMM supported are not expected to be enabled");
} }
close(sgx_fd);
return 0; 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, &quote->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 // Test suite
// ============================================================================ // ============================================================================
static test_case_t test_cases[] = { static test_case_t test_cases[] = {
TEST_CASE(test_tty_ioctl_TIOCGWINSZ), 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() { int main() {