Skip to content

Commit

Permalink
Collateral handling for DCAP (#1134)
Browse files Browse the repository at this point in the history
* First version that manages to register a quoting enclave

* Move collateral to attestation-handler

* Work on collateral handling

* Use the real collateral data for quoting enclave

* Make the dump-ra CLI command work

* Work towards register_tcb_info

* Add all the boilerplate code to register TCB info

* Reduce code duplication for extrinsic encoding

* Reduce code duplication for extrinsic sending

* Extract method for collateral

* Remove duplicated code

* Fix clippy issues

* Fix compilation error in teeracle

* Return certificate and dcap quote

* Cleanup

* Switch to updated docker image

* Disable DCAP for now

* Register collateral only for DCAP

* Register collateral only for DCAP

* Cleanup

* Update core-primitives/attestation-handler/src/attestation_handler.rs

Co-authored-by: Szilárd Parrag <szilard.parrag@gmail.com>

* Update enclave-runtime/src/attestation.rs

Co-authored-by: Szilárd Parrag <szilard.parrag@gmail.com>

* Extract shared logic into separate method

* Improve documentation

* Extract DCAP logic into separate function

* Get rid of two unwrap() calls

* Move getting the call_ids into the shared function

* Use the correct Error for metadata

* Add type alias for Fmspc

* Improve separate_json_data_and_signature and add unit test

* Fix clippy issues

* Incorporate review feedback

* Fix unsafe

* Switch implementation of separate_json_data_and_signature

* Make clippy happy

* Add missing `use`

* Add missing feature `preserve_order` to `serde_json_sgx`

* Add compiler flag for std case

* Make separate_json_data_and_signature robust to potential C-style null terminators

Co-authored-by: Szilárd Parrag <szilard.parrag@gmail.com>
  • Loading branch information
Niederb and OverOrion authored Jan 26, 2023
1 parent 948b9cf commit 39624bc
Show file tree
Hide file tree
Showing 22 changed files with 578 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:

clippy:
runs-on: ubuntu-latest
container: "integritee/integritee-dev:0.1.10"
container: "integritee/integritee-dev:0.1.11"
steps:
- uses: actions/checkout@v3
- name: init rust
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM integritee/integritee-dev:0.1.10
FROM integritee/integritee-dev:0.1.11
LABEL maintainer="zoltan@integritee.network"

# By default we warp the service
Expand Down
4 changes: 2 additions & 2 deletions build.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

### Builder Stage
##################################################
FROM integritee/integritee-dev:0.1.10 AS builder
FROM integritee/integritee-dev:0.1.11 AS builder
LABEL maintainer="zoltan@integritee.network"

# set environment variables
Expand Down Expand Up @@ -49,7 +49,7 @@ RUN cargo test --release
# A builder stage that uses sccache to speed up local builds with docker
# Installation and setup of sccache should be moved to the integritee-dev image, so we don't
# always need to compile and install sccache on CI (where we have no caching so far).
FROM integritee/integritee-dev:0.1.10 AS cached-builder
FROM integritee/integritee-dev:0.1.11 AS cached-builder
LABEL maintainer="zoltan@integritee.network"

# set environment variables
Expand Down
5 changes: 2 additions & 3 deletions core-primitives/attestation-handler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ log = { version = "0.4", default-features = false }
base64 = { version = "0.13", features = ["alloc"], optional = true }
chrono = { version = "0.4.19", features = ["alloc"], optional = true }
rustls = { version = "0.19", optional = true }
serde_json = { version = "1.0", optional = true }
serde_json = { version = "1.0", features = ["preserve_order"], optional = true }
thiserror = { version = "1.0", optional = true }
webpki = { version = "0.21", optional = true }

Expand All @@ -26,7 +26,7 @@ base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/
chrono_sgx = { package = "chrono", git = "https://github.com/mesalock-linux/chrono-sgx", optional = true }
num-bigint = { optional = true, git = "https://github.com/mesalock-linux/num-bigint-sgx" }
rustls_sgx = { package = "rustls", rev = "sgx_1.1.3", features = ["dangerous_configuration"], git = "https://github.com/mesalock-linux/rustls", optional = true }
serde_json_sgx = { package = "serde_json", tag = "sgx_1.1.3", git = "https://github.com/mesalock-linux/serde-json-sgx", optional = true }
serde_json_sgx = { package = "serde_json", tag = "sgx_1.1.3", features = ["preserve_order"], git = "https://github.com/mesalock-linux/serde-json-sgx", optional = true }
thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true }
webpki-roots = { git = "https://github.com/mesalock-linux/webpki-roots", branch = "mesalock_sgx" }
webpki_sgx = { package = "webpki", git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx", optional = true }
Expand All @@ -50,7 +50,6 @@ itp-types = { path = "../types", default-features = false }
# integritee
httparse = { default-features = false, git = "https://github.com/integritee-network/httparse-sgx", branch = "sgx-experimental" }


# substrate deps
sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36" }
Expand Down
25 changes: 14 additions & 11 deletions core-primitives/attestation-handler/src/attestation_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,14 @@ pub const REPORT_SUFFIX: &str = "/sgx/dev/attestation/v4/report";

/// Trait to provide an abstraction to the attestation logic
pub trait AttestationHandler {
/// Generates an encoded remote attestation certificate.
/// Generates an encoded remote attestation certificate. Returns DER encoded certificate.
/// If skip_ra is set, it will not perform a remote attestation via IAS
/// but instead generate a mock certificate.
fn generate_ias_ra_cert(&self, skip_ra: bool) -> EnclaveResult<Vec<u8>>;

/// Returns the DER encoded certificate and the raw DCAP quote.
/// If skip_ra is set, it will not perform a remote attestation via IAS
/// but instead generate a mock certificate.
fn generate_dcap_ra_cert(
&self,
quoting_enclave_target_info: &sgx_target_info_t,
Expand Down Expand Up @@ -170,13 +173,13 @@ where
quoting_enclave_target_info: &sgx_target_info_t,
quote_size: u32,
) -> EnclaveResult<()> {
let (_key_der, cert_der) =
let (_cert_der, dcap_quote) =
match self.generate_dcap_ra_cert(quoting_enclave_target_info, quote_size, false) {
Ok(r) => r,
Err(e) => return Err(e),
};

if let Err(err) = io::write(&cert_der, RA_DUMP_CERT_DER_FILE) {
if let Err(err) = io::write(&dcap_quote, RA_DUMP_CERT_DER_FILE) {
error!(
" [Enclave] failed to write RA file ({}), status: {:?}",
RA_DUMP_CERT_DER_FILE, err
Expand Down Expand Up @@ -254,7 +257,7 @@ where
let (prv_k, pub_k) = ecc_handle.create_key_pair()?;
info!("Enclave Attestation] Generated ephemeral ECDSA keypair:");

let payload = if !skip_ra {
let qe_quote = if !skip_ra {
let qe_quote = match self.retrieve_qe_dcap_quote(
&chain_signer.public().0,
quoting_enclave_target_info,
Expand All @@ -266,15 +269,15 @@ where
return Err(e.into())
},
};
// Verify the quote via qve enclave
self.ecdsa_quote_verification(qe_quote)?
qe_quote
} else {
Default::default()
};

// generate an ECC certificate
debug!("[Enclave] Generate ECC Certificate");
let (key_der, cert_der) = match cert::gen_ecc_cert(&payload, &prv_k, &pub_k, &ecc_handle) {
let (_key_der, cert_der) = match cert::gen_ecc_cert(&qe_quote, &prv_k, &pub_k, &ecc_handle)
{
Ok(r) => r,
Err(e) => {
error!("[Enclave] gen_ecc_cert failed: {:?}", e);
Expand All @@ -284,7 +287,7 @@ where

let _ = ecc_handle.close();

Ok((key_der, cert_der))
Ok((cert_der, qe_quote))
}
}

Expand Down Expand Up @@ -640,7 +643,8 @@ where
.map_err(|e| EnclaveError::Other(e.into()))
}

pub fn ecdsa_quote_verification(&self, quote: Vec<u8>) -> SgxResult<Vec<u8>> {
/// Returns Ok if the verification of the quote by the quote verification enclave (QVE) was successful
pub fn ecdsa_quote_verification(&self, quote: Vec<u8>) -> SgxResult<()> {
let mut app_enclave_target_info: sgx_target_info_t = unsafe { std::mem::zeroed() };
let quote_collateral: sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() };
let mut qve_report_info: sgx_ql_qe_report_info_t = unsafe { std::mem::zeroed() };
Expand Down Expand Up @@ -724,8 +728,7 @@ where
return Err(sgx_status_t::SGX_ERROR_UNEXPECTED)
}

// TODO. What to send to our teerex pallet?
Ok(vec![])
Ok(())
}

pub fn retrieve_qe_dcap_quote(
Expand Down
158 changes: 158 additions & 0 deletions core-primitives/attestation-handler/src/collateral.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright 2022 Integritee AG and Supercomputing Systems AG
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#[cfg(all(not(feature = "std"), feature = "sgx"))]
use crate::sgx_reexport_prelude::serde_json;
use sgx_types::sgx_ql_qve_collateral_t;
use std::{io::Write, string::String, vec::Vec};

/// This is a rust-ified version of the type sgx_ql_qve_collateral_t.
/// See Appendix A.3 in the document
/// "Intel® Software Guard Extensions (Intel® SGX) Data Center Attestation Primitives: ECDSA Quote Library API"
/// https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf
pub struct SgxQlQveCollateral {
pub version: u32, // version = 1. PCK Cert chain is in the Quote.
/* intel DCAP 1.13 */
pub tee_type: u32, // 0x00000000: SGX or 0x00000081: TDX
pub pck_crl_issuer_chain: Vec<u8>,
pub root_ca_crl: Vec<u8>,
pub pck_crl: Vec<u8>,
pub tcb_info_issuer_chain: Vec<u8>,
pub tcb_info: Vec<u8>,
pub qe_identity_issuer_chain: Vec<u8>,
pub qe_identity: Vec<u8>,
}

impl SgxQlQveCollateral {
/// # Safety
///
/// The caller is in charge of ensuring that `c` is properly initialized and all
/// its members have a value that is not nullptr
pub unsafe fn from_c_type(c: &sgx_ql_qve_collateral_t) -> Self {
let pck_crl_issuer_chain = std::slice::from_raw_parts(
c.pck_crl_issuer_chain as *const u8,
c.pck_crl_issuer_chain_size as usize,
)
.to_vec();
let root_ca_crl =
std::slice::from_raw_parts(c.root_ca_crl as *const u8, c.root_ca_crl_size as usize)
.to_vec();
let pck_crl =
std::slice::from_raw_parts(c.pck_crl as *const u8, c.pck_crl_size as usize).to_vec();
let tcb_info_issuer_chain = std::slice::from_raw_parts(
c.tcb_info_issuer_chain as *const u8,
c.tcb_info_issuer_chain_size as usize,
)
.to_vec();
let tcb_info =
std::slice::from_raw_parts(c.tcb_info as *const u8, c.tcb_info_size as usize).to_vec();
let qe_identity_issuer_chain = std::slice::from_raw_parts(
c.qe_identity_issuer_chain as *const u8,
c.qe_identity_issuer_chain_size as usize,
)
.to_vec();
let qe_identity =
std::slice::from_raw_parts(c.qe_identity as *const u8, c.qe_identity_size as usize)
.to_vec();
SgxQlQveCollateral {
version: c.version,
tee_type: c.tee_type,
pck_crl_issuer_chain,
root_ca_crl,
pck_crl,
tcb_info_issuer_chain,
tcb_info,
qe_identity_issuer_chain,
qe_identity,
}
}

pub fn dump_to_disk(&self) {
Self::write_data_to_disk("pck_crl_issuer_chain", &self.pck_crl_issuer_chain);
Self::write_data_to_disk("root_ca_crl", &self.root_ca_crl);
Self::write_data_to_disk("pck_crl", &self.pck_crl);
Self::write_data_to_disk("tcb_info_issuer_chain", &self.tcb_info_issuer_chain);
Self::write_data_to_disk("tcb_info", &self.tcb_info);
Self::write_data_to_disk("qe_identity_issuer_chain", &self.qe_identity_issuer_chain);
Self::write_data_to_disk("qe_identity", &self.qe_identity);
}

/// Returns the tcb_info split into two parts: json_data and signature
pub fn get_tcb_info_split(&self) -> Option<(String, Vec<u8>)> {
let (json_data, signature) =
Self::separate_json_data_and_signature("tcbInfo", &self.tcb_info)?;
match hex::decode(signature) {
Ok(hex_signature) => Some((json_data, hex_signature)),
Err(_) => None,
}
}

/// Returns the tcb_info split into two parts: json_data and signature
pub fn get_quoting_enclave_split(&self) -> Option<(String, Vec<u8>)> {
let (json_data, signature) =
Self::separate_json_data_and_signature("enclaveIdentity", &self.qe_identity)?;
match hex::decode(signature) {
Ok(hex_signature) => Some((json_data, hex_signature)),
Err(_) => None,
}
}

/// Separates the actual data part from the signature for an Intel collateral in JSON format
/// Returns the data part and signature as a pair
fn separate_json_data_and_signature(data_name: &str, data: &[u8]) -> Option<(String, String)> {
let json = String::from_utf8_lossy(data);
// Remove potential C-style null terminators
let json = json.trim_matches(char::from(0));
let value: serde_json::Value = serde_json::from_str(json).ok()?;
if value[data_name].is_null() || value["signature"].is_null() {
return None
}
let data_json = serde_json::to_string(&value[data_name]).ok()?;
let signature = serde_json::to_string(&value["signature"]).ok()?;
// We want the signature without leading/ending "
let signature = signature.replace('\"', "");
Some((data_json, signature))
}

fn write_data_to_disk(filename: &str, contents: &[u8]) {
let mut file = std::fs::File::create(filename).unwrap();
file.write_all(contents).unwrap();
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn separate_json_data_and_signature() {
// A bit more complex json to ensure the ordering stays the same
let json = br#"{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2022-11-17T12:45:32Z"},"signature":"71746f2"}"#;
let (data, signature) =
SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).unwrap();
assert_eq!(data, r#"{"id":"SGX","version":3,"issueDate":"2022-11-17T12:45:32Z"}"#);
assert_eq!(signature, "71746f2");

let json = br#"{"tcbInfo":{not_a_valid_json},"nosignature":"thesignature"}"#;
assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none());

let json = br#"{"tcbInfo":{"id":"SGX"},"nosignature":"thesignature"}"#;
assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none());

let json = br#"{"tcbInfo":{"id":"SGX"},"signature":""#;
assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none());
}
}
3 changes: 3 additions & 0 deletions core-primitives/attestation-handler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ pub mod sgx_reexport_prelude {
#[cfg(all(not(feature = "std"), feature = "sgx"))]
pub mod attestation_handler;

pub mod collateral;

pub mod cert;

pub mod error;

#[cfg(all(not(feature = "std"), feature = "sgx"))]
pub use attestation_handler::{AttestationHandler, IntelAttestationHandler, DEV_HOSTNAME};
pub use collateral::SgxQlQveCollateral;

pub use error::{Error, Result};
1 change: 1 addition & 0 deletions core-primitives/enclave-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ fn main() {
// ln -s libsgx_dcap_ql.so.1 libsgx_dcap_ql.so
println!("cargo:rustc-link-lib=dylib=sgx_dcap_ql");
println!("cargo:rustc-link-lib=dylib=sgx_dcap_quoteverify");
println!("cargo:rustc-link-lib=dylib=dcap_quoteprov");
}
27 changes: 26 additions & 1 deletion core-primitives/enclave-api/ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
///! FFI's that call into the enclave. These functions need to be added to the
/// enclave edl file and be implemented within the enclave.
use sgx_types::{c_int, sgx_enclave_id_t, sgx_quote_sign_type_t, sgx_status_t, sgx_target_info_t};
use sgx_types::{
c_int, sgx_enclave_id_t, sgx_ql_qve_collateral_t, sgx_quote_sign_type_t, sgx_status_t,
sgx_target_info_t,
};

extern "C" {

Expand Down Expand Up @@ -112,6 +115,22 @@ extern "C" {
quote_size: u32,
) -> sgx_status_t;

pub fn generate_register_quoting_enclave_extrinsic(
eid: sgx_enclave_id_t,
retval: *mut sgx_status_t,
collateral: *const sgx_ql_qve_collateral_t,
unchecked_extrinsic: *mut u8,
unchecked_extrinsic_size: u32,
) -> sgx_status_t;

pub fn generate_register_tcb_info_extrinsic(
eid: sgx_enclave_id_t,
retval: *mut sgx_status_t,
collateral: *const sgx_ql_qve_collateral_t,
unchecked_extrinsic: *mut u8,
unchecked_extrinsic_size: u32,
) -> sgx_status_t;

pub fn dump_ias_ra_cert_to_disk(
eid: sgx_enclave_id_t,
retval: *mut sgx_status_t,
Expand All @@ -124,6 +143,12 @@ extern "C" {
quote_size: u32,
) -> sgx_status_t;

pub fn dump_dcap_collateral_to_disk(
eid: sgx_enclave_id_t,
retval: *mut sgx_status_t,
collateral: *const sgx_ql_qve_collateral_t,
) -> sgx_status_t;

pub fn test_main_entrance(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t;

pub fn call_rpc_methods(
Expand Down
Loading

0 comments on commit 39624bc

Please sign in to comment.