diff --git a/demos/remote_attestation/dcap/README.md b/demos/remote_attestation/dcap/README.md new file mode 100644 index 00000000..1318f12d --- /dev/null +++ b/demos/remote_attestation/dcap/README.md @@ -0,0 +1,38 @@ +# SGX DCAP Remote Attestation Demo in Rust + +This project demonstrates how to do Intel SGX DCAP (Datacenter Attestation +Primitives) remote attestation on Occlum. Occlum provides SGX capabilities to +applications through ioctls on device `/dev/sgx`. + +## Prerequisites + +- Platform: Intel SGX enabled platform with DCAP installed. Follow [DCAP + Quick Install + Guide](https://software.intel.com/content/www/us/en/develop/articles/intel-software-guard-extensions-data-center-attestation-primitives-quick-install-guide.html) + for the detailed installation procedure. + +- Occlum: Compile Occlum on a DCAP-installed platform by invoking `make`. The + compilation will look for the needed DCAP libraries. The needed libraries + include `libsgx_quote_ex, libsgx_quote_ex_sim, libsgx_dcap_tvl, + libsgx_dcap_ql and libsgx_dcap_quoteverify`. + +## Run this demo on Occlum + +You can run the DCAP quote generation and verification demo, including dcap library build, rust test demo and C test demo on Occlum via +``` +./run_dcap_quote_on_occlum.sh +``` + +## Preinstalled DCAP package in Ubuntu 18.04 and CentOS 8.1 +The DCAP package has been preinstalled in the Occlum official docker images +including Ubuntu 18.04 and CentOS 8.1 since Occlum 0.19.0. The versions of DCAP +package and PCCS should keep the same to avoid incompatibility. The demo is verified +in Occlum 0.23.1 in which the DCAP version is 1.10, so PCCS should also be version 1.10 +to work with the preinstalled DCAP package. Remember to configure `/etc/sgx_default_qcnl.conf` +in the container according to your PCCS setting after running the docker image. + +As DCAP 1.10 is not the latest, the demo application running in the container of +the official image will output a warning: `WARN: App: Verification completed +with Non-terminal result: a002`. The `a002` of type `sgx_ql_qv_result_t` in the +warning indicates the quote is good but TCB level of the platform is out of +date. diff --git a/demos/remote_attestation/dcap/c_app/Makefile b/demos/remote_attestation/dcap/c_app/Makefile new file mode 100644 index 00000000..601d66f3 --- /dev/null +++ b/demos/remote_attestation/dcap/c_app/Makefile @@ -0,0 +1,12 @@ +CC := gcc +LIBPATH := ../dcap_lib/target/debug + +.PHONY: all clean + +all: dcap_c_test + +dcap_c_test: dcap_c_test.c + $(CC) $^ -fPIE -pie -o $@ -L $(LIBPATH) -ldcap_quote + +clean: + rm -rf dcap_c_test diff --git a/demos/remote_attestation/dcap/c_app/dcap_c_test.c b/demos/remote_attestation/dcap/c_app/dcap_c_test.c new file mode 100644 index 00000000..0f7f0ff2 --- /dev/null +++ b/demos/remote_attestation/dcap/c_app/dcap_c_test.c @@ -0,0 +1,122 @@ +#include +#include +#include + +#include "sgx_quote_3.h" +#include "dcap_quote.h" + +void main() { + void *handle; + uint32_t quote_size, supplemental_size; + uint8_t *p_quote_buffer, *p_supplemental_buffer; + sgx_quote3_t *p_quote; + sgx_report_body_t *p_rep_body; + sgx_report_data_t *p_rep_data; + sgx_ql_auth_data_t *p_auth_data; + sgx_ql_ecdsa_sig_data_t *p_sig_data; + sgx_ql_certification_data_t *p_cert_data; + int32_t ret; + + handle = dcap_quote_open(); + quote_size = dcap_get_quote_size(handle); + printf("quote size = %d\n", quote_size); + + p_quote_buffer = (uint8_t*)malloc(quote_size); + if (NULL == p_quote_buffer) { + printf("Couldn't allocate quote_buffer\n"); + goto CLEANUP; + } + memset(p_quote_buffer, 0, quote_size); + + sgx_report_data_t report_data = { 0 }; + char *data = "ioctl DCAP report data example"; + memcpy(report_data.d, data, strlen(data)); + + // Get the Quote + ret = dcap_generate_quote(handle, p_quote_buffer, &report_data); + if (0 != ret) { + printf( "Error in dcap_generate_quote.\n"); + goto CLEANUP; + } + + printf("DCAP generate quote successfully\n"); + + p_quote = (sgx_quote3_t *)p_quote_buffer; + p_rep_body = (sgx_report_body_t *)(&p_quote->report_body); + p_rep_data = (sgx_report_data_t *)(&p_rep_body->report_data); + p_sig_data = (sgx_ql_ecdsa_sig_data_t *)p_quote->signature_data; + p_auth_data = (sgx_ql_auth_data_t*)p_sig_data->auth_certification_data; + p_cert_data = (sgx_ql_certification_data_t *)((uint8_t *)p_auth_data + sizeof(*p_auth_data) + p_auth_data->size); + + if (memcmp((void *)p_rep_data, (void *)&report_data, sizeof(sgx_report_data_t)) != 0) { + printf("mismathced report data\n"); + goto CLEANUP; + } + + printf("cert_key_type = 0x%x\n", p_cert_data->cert_key_type); + + supplemental_size = dcap_get_supplemental_data_size(handle); + printf("supplemental_size size = %d\n", supplemental_size); + p_supplemental_buffer = (uint8_t *)malloc(supplemental_size); + if (NULL == p_supplemental_buffer) { + printf("Couldn't allocate supplemental buffer\n"); + goto CLEANUP; + } + memset(p_supplemental_buffer, 0, supplemental_size); + + uint32_t collateral_expiration_status = 1; + sgx_ql_qv_result_t quote_verification_result = SGX_QL_QV_RESULT_UNSPECIFIED; + + ret = dcap_verify_quote( + handle, + p_quote_buffer, + quote_size, + &collateral_expiration_status, + "e_verification_result, + supplemental_size, + p_supplemental_buffer + ); + + if (0 != ret) { + printf( "Error in dcap_verify_quote.\n"); + goto CLEANUP; + } + + if (collateral_expiration_status != 0) { + printf("the verification collateral has expired\n"); + } + + switch (quote_verification_result) { + case SGX_QL_QV_RESULT_OK: + printf("Succeed to verify the quote!\n"); + break; + case SGX_QL_QV_RESULT_CONFIG_NEEDED: + case SGX_QL_QV_RESULT_OUT_OF_DATE: + case SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED: + case SGX_QL_QV_RESULT_SW_HARDENING_NEEDED: + case SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED: + printf("WARN: App: Verification completed with Non-terminal result: %x\n", + quote_verification_result); + break; + case SGX_QL_QV_RESULT_INVALID_SIGNATURE: + case SGX_QL_QV_RESULT_REVOKED: + case SGX_QL_QV_RESULT_UNSPECIFIED: + default: + printf("\tError: App: Verification completed with Terminal result: %x\n", + quote_verification_result); + goto CLEANUP; + } + + printf("DCAP verify quote successfully\n"); + +CLEANUP: + if (NULL != p_quote_buffer) { + free(p_quote_buffer); + } + + if (NULL != p_supplemental_buffer) { + free(p_supplemental_buffer); + } + + dcap_quote_close(handle); +} diff --git a/demos/remote_attestation/dcap/c_app/dcap_quote.h b/demos/remote_attestation/dcap/c_app/dcap_quote.h new file mode 100644 index 00000000..a31cd8e5 --- /dev/null +++ b/demos/remote_attestation/dcap/c_app/dcap_quote.h @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "sgx_urts.h" +#include "sgx_report.h" +#include "sgx_qve_header.h" +#include "sgx_dcap_ql_wrapper.h" +#include "sgx_pce.h" +#include "sgx_error.h" + +void *dcap_quote_open(void); + +uint32_t dcap_get_quote_size(void *handle); + +int32_t dcap_generate_quote(void *handle, uint8_t *quote_buf, const sgx_report_data_t *report_data); + +uint32_t dcap_get_supplemental_data_size(void *handle); + +int32_t dcap_verify_quote(void *handle, + const uint8_t *quote_buf, + uint32_t quote_size, + uint32_t *collateral_expiration_status, + sgx_ql_qv_result_t *quote_verification_result, + uint32_t supplemental_data_size, + uint8_t *supplemental_data); + + +void dcap_quote_close(void *handle); diff --git a/demos/remote_attestation/dcap/dcap_lib/Cargo.toml b/demos/remote_attestation/dcap/dcap_lib/Cargo.toml new file mode 100644 index 00000000..048305de --- /dev/null +++ b/demos/remote_attestation/dcap/dcap_lib/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "dcap_quote" +version = "0.1.0" +authors = ["Zheng, Qi "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sgx_types = { path = "../../../../deps/rust-sgx-sdk/sgx_types" } +libc = "0.2" + +[lib] +crate-type = ["cdylib", "rlib"] diff --git a/demos/remote_attestation/dcap/dcap_lib/examples/dcap_test.rs b/demos/remote_attestation/dcap/dcap_lib/examples/dcap_test.rs new file mode 100644 index 00000000..ecf24919 --- /dev/null +++ b/demos/remote_attestation/dcap/dcap_lib/examples/dcap_test.rs @@ -0,0 +1,123 @@ +extern crate dcap_quote; +use std::str; +use dcap_quote::*; +use sgx_types::{ + sgx_report_data_t, sgx_ql_qv_result_t, sgx_report_body_t, sgx_quote3_t +}; + +struct DcapDemo { + dcap_quote: DcapQuote, + quote_size: u32, + quote_buf_ptr: *mut u8, + req_data: sgx_report_data_t, + supplemental_size: u32, + suppl_buf_ptr: *mut u8, +} + +impl DcapDemo { + pub fn new(report_data: &str) -> Self { + let mut dcap = DcapQuote::new(); + let quote_size = dcap.get_quote_size(); + let supplemental_size = dcap.get_supplemental_data_size(); + let mut quote_buf: Vec = vec![0; quote_size as usize]; + let quote_ptr = quote_buf.as_mut_ptr(); + let mut suppl_buf: Vec = vec![0; supplemental_size as usize]; + let suppl_ptr = suppl_buf.as_mut_ptr(); + let mut req_data = sgx_report_data_t::default(); + + //fill in the report data array + for (pos, val) in report_data.as_bytes().iter().enumerate() { + req_data.d[pos] = *val; + } + + Self { + dcap_quote: dcap, + quote_size: quote_size, + quote_buf_ptr: quote_ptr, + req_data: req_data, + supplemental_size: supplemental_size, + suppl_buf_ptr: suppl_ptr + } + } + + fn dcap_quote_gen(&mut self) -> Result { + self.dcap_quote.generate_quote(self.quote_buf_ptr, &mut self.req_data).unwrap(); + + println!("DCAP generate quote successfully"); + + Ok( 0 ) + } + + fn dcap_quote_get_report_body(&mut self) -> Result<*const sgx_report_body_t, &'static str> { + let quote3: *mut sgx_quote3_t = self.quote_buf_ptr as *mut sgx_quote3_t; + let report_body = unsafe { &((*quote3).report_body) }; + + Ok(report_body) + } + + fn dcap_quote_get_report_data(&mut self) -> Result<*const sgx_report_data_t, &'static str> { + let report_body_ptr = self.dcap_quote_get_report_body().unwrap(); + let report_data_ptr = unsafe { &(*report_body_ptr).report_data }; + + Ok(report_data_ptr) + } + + fn dcap_quote_ver(&mut self) -> Result { + let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED; + let mut status = 1; + + let mut verify_arg = IoctlVerDCAPQuoteArg { + quote_buf: self.quote_buf_ptr, + quote_size: self.quote_size, + collateral_expiration_status: &mut status, + quote_verification_result: &mut quote_verification_result, + supplemental_data_size: self.supplemental_size, + supplemental_data: self.suppl_buf_ptr, + }; + + self.dcap_quote.verify_quote(&mut verify_arg).unwrap(); + println!("DCAP verify quote successfully"); + + Ok( quote_verification_result ) + } +} + +impl Drop for DcapDemo { + fn drop(&mut self) { + self.dcap_quote.close(); + } +} + +fn main() { + let report_str = "Dcap demo sample"; + let mut dcap_demo = DcapDemo::new(report_str); + + println!("Generate quote with report data : {}", report_str); + dcap_demo.dcap_quote_gen().unwrap(); + + // compare the report data in quote buffer + let report_data_ptr = dcap_demo.dcap_quote_get_report_data().unwrap(); + let string = str::from_utf8( unsafe { &(*report_data_ptr).d } ).unwrap(); + + if report_str == &string[..report_str.len()] { + println!("Report data from Quote: '{}' exactly matches.", string); + } else { + println!("Report data from Quote: '{}' doesn't match !!!", string); + } + + let result = dcap_demo.dcap_quote_ver().unwrap(); + match result { + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => { + println!("Succeed to verify the quote!"); + }, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED | + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE | + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED | + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED | + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { + println!("WARN: App: Verification completed with Non-terminal result: {}", result); + }, + _ => println!("Error: App: Verification completed with Terminal result: {}", result), + } + +} diff --git a/demos/remote_attestation/dcap/dcap_lib/src/dcap_quote.rs b/demos/remote_attestation/dcap/dcap_lib/src/dcap_quote.rs new file mode 100644 index 00000000..c83d710b --- /dev/null +++ b/demos/remote_attestation/dcap/dcap_lib/src/dcap_quote.rs @@ -0,0 +1,118 @@ +use libc::*; +use std::ffi::CString; + +use sgx_types::{ + sgx_report_data_t, sgx_ql_qv_result_t +}; + +const SGXIOC_GET_DCAP_QUOTE_SIZE: c_ulong = 0x80047307; +const SGXIOC_GEN_DCAP_QUOTE: c_ulong = 0xc0187308; +const SGXIOC_GET_DCAP_SUPPLEMENTAL_SIZE: c_ulong = 0x80047309; +const SGXIOC_VER_DCAP_QUOTE: c_ulong = 0xc030730a; + + +// Copy from occlum/src/libos/src/fs/dev_fs/dev_sgx/mod.rs +//#[allow(dead_code)] +#[repr(C)] +pub struct IoctlGenDCAPQuoteArg { + pub report_data: *const sgx_report_data_t, // Input + pub quote_size: *mut u32, // Input/output + pub quote_buf: *mut u8, // Output +} + +// Copy from occlum/src/libos/src/fs/dev_fs/dev_sgx/mod.rs +//#[allow(dead_code)] +#[repr(C)] +pub struct IoctlVerDCAPQuoteArg { + pub quote_buf: *const u8, // Input + pub quote_size: u32, // Input + pub collateral_expiration_status: *mut u32, // Output + pub quote_verification_result: *mut sgx_ql_qv_result_t, // Output + pub supplemental_data_size: u32, // Input (optional) + pub supplemental_data: *mut u8, // Output (optional) +} + +pub struct DcapQuote { + fd: c_int, + quote_size: u32, + supplemental_size: u32, +} + +impl DcapQuote { + pub fn new() -> Self { + println!("DcapQuote: new"); + + let path = CString::new("/dev/sgx").unwrap(); + let fd = unsafe { libc::open(path.as_ptr(), O_RDONLY) }; + if fd > 0 { + Self { + fd: fd, + quote_size: 0, + supplemental_size: 0, + } + } else { + panic!("Open /dev/sgx failed") + } + } + + pub fn get_quote_size(&mut self) -> u32 { + println!("DcapQuote: get_quote_size"); + + let size: u32 = 0; + let ret = unsafe { libc::ioctl(self.fd, SGXIOC_GET_DCAP_QUOTE_SIZE, &size) }; + if ret < 0 { + panic!("IOCTRL SGXIOC_GET_DCAP_QUOTE_SIZE failed"); + } else { + self.quote_size = size; + size + } + } + + pub fn generate_quote(&mut self, quote_buf: *mut u8, report_data: *const sgx_report_data_t) -> Result { + println!("DcapQuote: generate_quote"); + + let quote_arg: IoctlGenDCAPQuoteArg = IoctlGenDCAPQuoteArg { + report_data: report_data, + quote_size: &mut self.quote_size, + quote_buf: quote_buf, + }; + + let ret = unsafe { libc::ioctl(self.fd, SGXIOC_GEN_DCAP_QUOTE, "e_arg) }; + if ret < 0 { + Err("IOCTRL SGXIOC_GEN_DCAP_QUOTE failed") + } else { + Ok( 0 ) + } + } + + pub fn get_supplemental_data_size(&mut self) -> u32 { + println!("DcapQuote: get_supplemental_data_size"); + + let size: u32 = 0; + let ret = unsafe { libc::ioctl(self.fd, SGXIOC_GET_DCAP_SUPPLEMENTAL_SIZE, &size) }; + if ret < 0 { + panic!("IOCTRL SGXIOC_GET_DCAP_SUPPLEMENTAL_SIZE failed"); + } else { + self.supplemental_size = size; + size + } + } + + pub fn verify_quote(&mut self, verify_arg: *mut IoctlVerDCAPQuoteArg) -> Result { + println!("DcapQuote: verify_quote"); + + let ret = unsafe { libc::ioctl(self.fd, SGXIOC_VER_DCAP_QUOTE, verify_arg) }; + if ret < 0 { + println!("ret = {}", ret); + Err("IOCTRL SGXIOC_VER_DCAP_QUOTE failed") + } else { + Ok( 0 ) + } + } + + pub fn close(&mut self) { + println!("DcapQuote: close"); + unsafe { libc::close(self.fd) }; + } +} + diff --git a/demos/remote_attestation/dcap/dcap_lib/src/lib.rs b/demos/remote_attestation/dcap/dcap_lib/src/lib.rs new file mode 100644 index 00000000..5275b70b --- /dev/null +++ b/demos/remote_attestation/dcap/dcap_lib/src/lib.rs @@ -0,0 +1,109 @@ +use std::boxed::Box; +use libc::{c_void}; + +use sgx_types::{ + sgx_report_data_t, sgx_ql_qv_result_t +}; + +mod dcap_quote; +pub use crate::dcap_quote::*; + +#[no_mangle] +pub extern "C" fn dcap_quote_open() -> *mut c_void { + Box::into_raw(Box::new(DcapQuote::new())) as *mut c_void +} + +#[no_mangle] +pub extern "C" fn dcap_get_quote_size(handle: *mut c_void) -> u32 { + if handle.is_null() { + return 0 + } + + let dcap = unsafe { + &mut *(handle as *mut DcapQuote) + }; + + dcap.get_quote_size() +} + +#[no_mangle] +pub extern "C" fn dcap_generate_quote( + handle: *mut c_void, + quote_buf: *mut u8, + report_data: *const sgx_report_data_t) -> i32 +{ + if handle.is_null() { + return -1 + } + + let dcap = unsafe { + &mut *(handle as *mut DcapQuote) + }; + + dcap.generate_quote(quote_buf, report_data).unwrap(); + + 0 +} + +#[no_mangle] +pub extern "C" fn dcap_get_supplemental_data_size(handle: *mut c_void) -> u32 { + if handle.is_null() { + return 0 + } + + let dcap = unsafe { + &mut *(handle as *mut DcapQuote) + }; + + dcap.get_supplemental_data_size() +} + +#[no_mangle] +pub extern "C" fn dcap_verify_quote( + handle: *mut c_void, + quote_buf: *const u8, + quote_size: u32, + collateral_expiration_status: *mut u32, + quote_verification_result: *mut sgx_ql_qv_result_t, + supplemental_data_size: u32, + supplemental_data: *mut u8) -> i32 +{ + if handle.is_null() { + return -1 + } + + let dcap = unsafe { + &mut *(handle as *mut DcapQuote) + }; + + let mut verify_arg = IoctlVerDCAPQuoteArg { + quote_buf: quote_buf, + quote_size: quote_size, + collateral_expiration_status: collateral_expiration_status, + quote_verification_result: quote_verification_result, + supplemental_data_size: supplemental_data_size, + supplemental_data: supplemental_data, + }; + + dcap.verify_quote(&mut verify_arg).unwrap(); + + 0 +} + + +#[no_mangle] +pub extern "C" fn dcap_quote_close(handle: *mut c_void) { + if handle.is_null() { + return + } + + let dcap = unsafe { + &mut *(handle as *mut DcapQuote) + }; + + dcap.close(); + + unsafe { + Box::from_raw(handle); + } +} diff --git a/demos/remote_attestation/dcap/run_dcap_quote_on_occlum.sh b/demos/remote_attestation/dcap/run_dcap_quote_on_occlum.sh new file mode 100755 index 00000000..e21190cc --- /dev/null +++ b/demos/remote_attestation/dcap/run_dcap_quote_on_occlum.sh @@ -0,0 +1,32 @@ +#!/bin/bash +occlum_glibc=/opt/occlum/glibc/lib/ + +set -e + +BLUE='\033[1;34m' +NC='\033[0m' +INSTANCE_DIR="occlum_instance" + +pushd dcap_lib +cargo build --all-targets +popd + +make -C c_app + +rm -rf ${INSTANCE_DIR} && occlum new ${INSTANCE_DIR} +cd ${INSTANCE_DIR} +cp ../dcap_lib/target/debug/examples/dcap_test image/bin +cp ../dcap_lib/target/debug/libdcap_quote.so image/$occlum_glibc +cp ../c_app/dcap_c_test image/bin +cp $occlum_glibc/libdl.so.2 image/$occlum_glibc +cp $occlum_glibc/librt.so.1 image/$occlum_glibc + +occlum build + +echo -e "${BLUE}occlum run rust test /bin/dcap_test${NC}" +occlum run /bin/dcap_test + +echo -e "************" + +echo -e "${BLUE}occlum run C test /bin/dcap_c_test${NC}" +occlum run /bin/dcap_c_test