You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by ms...@apache.org on 2020/01/17 02:01:45 UTC

[incubator-teaclave] 01/02: [attestation] Polishing remote attestation and TLS-RA

This is an automated email from the ASF dual-hosted git repository.

mssun pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave.git

commit 5615c7136d675ef126eef5d70e66e69e4d077f67
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Thu Jan 16 16:30:16 2020 -0800

    [attestation] Polishing remote attestation and TLS-RA
---
 attestation/Cargo.toml                             |   1 +
 attestation/src/attestation.rs                     |  11 +-
 attestation/src/ias.rs                             | 110 +++--
 attestation/src/lib.rs                             |   8 +-
 attestation/src/platform.rs                        | 159 ++++++++
 attestation/src/quote.rs                           | 347 ----------------
 attestation/src/report.rs                          | 452 ++++++++++++++-------
 attestation/src/verifier.rs                        |  48 ++-
 rpc/Cargo.toml                                     |   1 +
 rpc/src/config.rs                                  |  15 +
 .../service_functional_test/enclave/src/lib.rs     |   1 +
 .../enclave/src/teaclave_authentication_service.rs |  20 +-
 12 files changed, 600 insertions(+), 573 deletions(-)

diff --git a/attestation/Cargo.toml b/attestation/Cargo.toml
index 28c1c15..6b2f965 100644
--- a/attestation/Cargo.toml
+++ b/attestation/Cargo.toml
@@ -27,6 +27,7 @@ log              = { version = "0.4.6" }
 num-bigint       = { version = "0.2.2" }
 percent-encoding = { version = "2.1.0" }
 rustls           = { version = "0.16.0", features = ["dangerous_configuration"] }
+serde            = { version = "1.0.92", features = ["derive"] }
 serde_json       = { version = "1.0.39" }
 thiserror        = { version = "1.0.9" }
 uuid             = { version = "0.7.4", features = ["v4"] }
diff --git a/attestation/src/attestation.rs b/attestation/src/attestation.rs
index 4a823c6..92d5e06 100644
--- a/attestation/src/attestation.rs
+++ b/attestation/src/attestation.rs
@@ -23,14 +23,11 @@ impl RemoteAttestation {
             IasReport::new(key_pair.pub_k, ias_key, ias_spid, false)?
         };
 
-        let cert_extension_payload =
-            [report.report, report.signature, report.signing_cert].join("|");
+        let cert_extension = serde_json::to_vec(&report)?;
 
-        let cert_der = key_pair.create_cert_with_extension(
-            "Teaclave",
-            "Teaclave",
-            &cert_extension_payload.as_bytes(),
-        );
+        let issuer = "Teaclave";
+        let subject = "CN=Teaclave";
+        let cert_der = key_pair.create_cert_with_extension(issuer, subject, &cert_extension);
         let prv_key_der = key_pair.private_key_into_der();
 
         let time = SystemTime::now();
diff --git a/attestation/src/ias.rs b/attestation/src/ias.rs
index 022b54d..9af7186 100644
--- a/attestation/src/ias.rs
+++ b/attestation/src/ias.rs
@@ -15,23 +15,52 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::report::IasReport;
 use crate::AttestationError;
 use anyhow::Error;
 use anyhow::Result;
+use anyhow::{anyhow, bail};
 use log::{debug, trace};
 use percent_encoding;
+use serde::{Deserialize, Serialize};
 use sgx_types::*;
 use std::collections::HashMap;
 use std::io::{Read, Write};
 use std::net::TcpStream;
+use std::os::unix::io::FromRawFd;
 use std::prelude::v1::*;
 use std::sync::Arc;
 
+#[cfg(feature = "mesalock_sgx")]
 extern "C" {
     fn ocall_sgx_get_ias_socket(p_retval: *mut i32) -> sgx_status_t;
 }
 
+#[derive(Default, Serialize, Deserialize)]
+pub struct IasReport {
+    pub report: Vec<u8>,
+    pub signature: Vec<u8>,
+    pub signing_cert: Vec<u8>,
+}
+
+impl IasReport {
+    #[cfg(feature = "mesalock_sgx")]
+    pub fn new(
+        pub_k: sgx_types::sgx_ec256_public_t,
+        ias_key: &str,
+        ias_spid: &str,
+        production: bool,
+    ) -> anyhow::Result<Self> {
+        use crate::platform;
+        let (target_info, epid_group_id) = platform::init_quote()?;
+        let mut ias_client = IasClient::new(ias_key, production);
+        let sigrl = ias_client.get_sigrl(u32::from_le_bytes(epid_group_id))?;
+        let sgx_report = platform::create_report(pub_k, target_info)?;
+        let quote = platform::get_quote(&sigrl, sgx_report, target_info, ias_spid)?;
+        let ias_report = ias_client.get_report(&quote)?;
+        Ok(ias_report)
+    }
+}
+
 pub struct IasClient {
     ias_key: String,
     ias_hostname: &'static str,
@@ -51,27 +80,15 @@ impl IasClient {
         }
     }
 
-    fn get_ias_socket() -> Result<c_int> {
-        debug!("get_ias_socket");
-        let mut fd: i32 = -1i32;
-        let res = unsafe { ocall_sgx_get_ias_socket(&mut fd as _) };
-
-        if res != sgx_status_t::SGX_SUCCESS || fd < 0 {
-            Err(Error::new(AttestationError::OCallError))
-        } else {
-            Ok(fd)
-        }
-    }
-
     fn new_tls_stream(&self) -> Result<rustls::StreamOwned<rustls::ClientSession, TcpStream>> {
-        let fd = Self::get_ias_socket()?;
         let dns_name = webpki::DNSNameRef::try_from_ascii_str(self.ias_hostname)?;
         let mut config = rustls::ClientConfig::new();
         config
             .root_store
             .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
         let client = rustls::ClientSession::new(&Arc::new(config), dns_name);
-        let socket = TcpStream::new(fd)?;
+        let fd = get_ias_socket()?;
+        let socket = unsafe { TcpStream::from_raw_fd(fd) };
         let stream = rustls::StreamOwned::new(client, socket);
 
         Ok(stream)
@@ -99,10 +116,10 @@ impl IasClient {
             .map_err(|_| Error::new(AttestationError::IasError))?
         {
             httparse::Status::Complete(s) => s,
-            _ => return Err(Error::new(AttestationError::IasError)),
+            _ => bail!(Error::new(AttestationError::IasError)),
         };
 
-        let header_map = Self::parse_headers(&http_response);
+        let header_map = parse_headers(&http_response);
 
         if !header_map.contains_key("Content-Length")
             || header_map
@@ -158,11 +175,10 @@ impl IasClient {
             .map_err(|_| Error::new(AttestationError::IasError))?
         {
             httparse::Status::Complete(s) => s,
-            _ => return Err(Error::new(AttestationError::IasError)),
+            _ => bail!(AttestationError::IasError),
         };
 
-        debug!("Self::parse_headers");
-        let header_map = Self::parse_headers(&http_response);
+        let header_map = parse_headers(&http_response);
 
         debug!("get_content_length");
         if !header_map.contains_key("Content-Length")
@@ -173,42 +189,64 @@ impl IasClient {
                 .unwrap_or(0)
                 == 0
         {
-            return Err(Error::new(AttestationError::IasError));
+            bail!(Error::new(AttestationError::IasError));
         }
 
         debug!("get_signature");
         let signature = header_map
             .get("X-IASReport-Signature")
-            .ok_or_else(|| Error::new(AttestationError::IasError))?
-            .to_owned();
+            .ok_or_else(|| Error::new(AttestationError::IasError))?;
+        let signature = base64::decode(signature)?;
         debug!("get_signing_cert");
         let signing_cert = {
             let cert_str = header_map
                 .get("X-IASReport-Signing-Certificate")
                 .ok_or_else(|| Error::new(AttestationError::IasError))?;
             let decoded_cert = percent_encoding::percent_decode_str(cert_str).decode_utf8()?;
-            // We should get two concatenated PEM files at this step.
-            let cert_content: Vec<&str> = decoded_cert.split("-----").collect();
-            cert_content[2].to_string()
+            let certs = rustls::internal::pemfile::certs(&mut decoded_cert.as_bytes())
+                .map_err(|_| anyhow!("pemfile error"))?;
+            certs[0].0.clone()
         };
 
-        let report = String::from_utf8_lossy(&response[header_len..]).into_owned();
+        let report = response[header_len..].to_vec();
         Ok(IasReport {
             report,
             signature,
             signing_cert,
         })
     }
+}
 
-    fn parse_headers(resp: &httparse::Response) -> HashMap<String, String> {
-        let mut header_map = HashMap::new();
-        for h in resp.headers.iter() {
-            header_map.insert(
-                h.name.to_owned(),
-                String::from_utf8_lossy(h.value).into_owned(),
-            );
-        }
+fn parse_headers(resp: &httparse::Response) -> HashMap<String, String> {
+    debug!("parse_headers");
+    let mut header_map = HashMap::new();
+    for h in resp.headers.iter() {
+        header_map.insert(
+            h.name.to_owned(),
+            String::from_utf8_lossy(h.value).into_owned(),
+        );
+    }
 
-        header_map
+    header_map
+}
+
+#[cfg(feature = "mesalock_sgx")]
+fn get_ias_socket() -> Result<c_int> {
+    debug!("get_ias_socket");
+    let mut fd: c_int = -1;
+    let res = unsafe { ocall_sgx_get_ias_socket(&mut fd as _) };
+
+    if res != sgx_status_t::SGX_SUCCESS || fd < 0 {
+        bail!(AttestationError::OCallError)
+    } else {
+        Ok(fd)
     }
 }
+
+#[cfg(not(feature = "mesalock_sgx"))]
+fn get_ias_socket() -> Result<c_int> {
+    use std::os::unix::io::IntoRawFd;
+    let ias_addr = "api.trustedservices.intel.com:443";
+    let stream = TcpStream::connect(ias_addr)?;
+    Ok(stream.into_raw_fd())
+}
diff --git a/attestation/src/lib.rs b/attestation/src/lib.rs
index 036f46f..81dd8eb 100644
--- a/attestation/src/lib.rs
+++ b/attestation/src/lib.rs
@@ -34,17 +34,17 @@ pub enum AttestationError {
 
 #[macro_use]
 mod cert;
-pub mod quote;
+pub mod ias;
+pub mod report;
 pub mod verifier;
 
 use cfg_if::cfg_if;
 cfg_if! {
     if #[cfg(feature = "mesalock_sgx")]  {
         pub mod key;
-        mod report;
-        mod ias;
+        mod platform;
         mod attestation;
-        pub use report::IasReport;
+        pub use ias::IasReport;
         pub use attestation::RemoteAttestation;
     } else {
     }
diff --git a/attestation/src/platform.rs b/attestation/src/platform.rs
new file mode 100644
index 0000000..fda919a
--- /dev/null
+++ b/attestation/src/platform.rs
@@ -0,0 +1,159 @@
+use crate::AttestationError;
+use anyhow::bail;
+use anyhow::Error;
+use anyhow::Result;
+use log::debug;
+use sgx_rand::os::SgxRng;
+use sgx_rand::Rng;
+use sgx_tcrypto::rsgx_sha256_slice;
+use sgx_tse::{rsgx_create_report, rsgx_verify_report};
+use sgx_types::sgx_ec256_public_t;
+use sgx_types::*;
+use std::prelude::v1::*;
+
+extern "C" {
+    fn ocall_sgx_init_quote(
+        p_retval: *mut sgx_status_t,
+        p_target_info: *mut sgx_target_info_t,
+        p_gid: *mut sgx_epid_group_id_t,
+    ) -> sgx_status_t;
+
+    fn ocall_sgx_calc_quote_size(
+        p_retval: *mut sgx_status_t,
+        p_sig_rl: *const u8,
+        sig_rl_size: u32,
+        p_quote_size: *mut u32,
+    ) -> sgx_status_t;
+
+    fn ocall_sgx_get_quote(
+        p_retval: *mut sgx_status_t,
+        p_report: *const sgx_report_t,
+        quote_type: sgx_quote_sign_type_t,
+        p_spid: *const sgx_spid_t,
+        p_nonce: *const sgx_quote_nonce_t,
+        p_sig_rl: *const u8,
+        sig_rl_size: u32,
+        p_qe_report: *mut sgx_report_t,
+        p_quote: *mut u8,
+        quote_size: u32,
+    ) -> sgx_status_t;
+}
+
+pub(crate) fn init_quote() -> Result<(sgx_target_info_t, sgx_epid_group_id_t)> {
+    debug!("init_quote");
+    let mut ti: sgx_target_info_t = sgx_target_info_t::default();
+    let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default();
+    let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
+
+    let res = unsafe { ocall_sgx_init_quote(&mut rt as _, &mut ti as _, &mut eg as _) };
+
+    if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
+        bail!(AttestationError::OCallError)
+    } else {
+        Ok((ti, eg))
+    }
+}
+
+pub(crate) fn create_report(
+    pub_k: sgx_ec256_public_t,
+    target_info: sgx_target_info_t,
+) -> Result<sgx_report_t> {
+    debug!("create_report");
+    let mut report_data: sgx_report_data_t = sgx_report_data_t::default();
+    let mut pub_k_gx = pub_k.gx;
+    pub_k_gx.reverse();
+    let mut pub_k_gy = pub_k.gy;
+    pub_k_gy.reverse();
+    report_data.d[..32].clone_from_slice(&pub_k_gx);
+    report_data.d[32..].clone_from_slice(&pub_k_gy);
+
+    rsgx_create_report(&target_info, &report_data)
+        .map_err(|_| Error::new(AttestationError::IasError))
+}
+
+pub(crate) fn get_quote(
+    sigrl: &[u8],
+    report: sgx_report_t,
+    target_info: sgx_target_info_t,
+    ias_spid_str: &str,
+) -> Result<Vec<u8>> {
+    let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
+    let (p_sigrl, sigrl_len) = if sigrl.is_empty() {
+        (std::ptr::null(), 0)
+    } else {
+        (sigrl.as_ptr(), sigrl.len() as u32)
+    };
+    let mut quote_len: u32 = 0;
+
+    let res =
+        unsafe { ocall_sgx_calc_quote_size(&mut rt as _, p_sigrl, sigrl_len, &mut quote_len as _) };
+
+    if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
+        bail!(AttestationError::OCallError);
+    }
+
+    let mut quote_nonce = sgx_quote_nonce_t { rand: [0; 16] };
+    let mut os_rng = SgxRng::new()?;
+    os_rng.fill_bytes(&mut quote_nonce.rand);
+    let mut qe_report = sgx_report_t::default();
+
+    let quote_type = sgx_quote_sign_type_t::SGX_LINKABLE_SIGNATURE;
+
+    let mut spid = sgx_types::sgx_spid_t::default();
+    let hex = hex::decode(ias_spid_str)?;
+    spid.id.copy_from_slice(&hex[..16]);
+
+    let mut quote = vec![0; quote_len as usize];
+
+    debug!("ocall_sgx_get_quote");
+    let res = unsafe {
+        ocall_sgx_get_quote(
+            &mut rt as _,
+            &report as _,
+            quote_type,
+            &spid as _,
+            &quote_nonce as _,
+            p_sigrl,
+            sigrl_len,
+            &mut qe_report as _,
+            quote.as_mut_ptr(),
+            quote_len,
+        )
+    };
+
+    if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
+        bail!(AttestationError::OCallError);
+    }
+
+    debug!("rsgx_verify_report");
+    // Perform a check on qe_report to verify if the qe_report is valid.
+    rsgx_verify_report(&qe_report).map_err(|_| Error::new(AttestationError::IasError))?;
+
+    // Check if the qe_report is produced on the same platform.
+    if target_info.mr_enclave.m != qe_report.body.mr_enclave.m
+        || target_info.attributes.flags != qe_report.body.attributes.flags
+        || target_info.attributes.xfrm != qe_report.body.attributes.xfrm
+    {
+        bail!(AttestationError::QuoteError);
+    }
+
+    // Check qe_report to defend against replay attack. The purpose of
+    // p_qe_report is for the ISV enclave to confirm the QUOTE it 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 (target info from p_report) , with the lower 32Bytes in
+    // report.data = SHA256(p_nonce||p_quote). The ISV enclave can verify
+    // the p_qe_report and report.data to confirm the QUOTE has not be
+    // modified and is not a replay. It is optional.
+    let mut rhs_vec: Vec<u8> = quote_nonce.rand.to_vec();
+    rhs_vec.extend(&quote);
+    debug!("rsgx_sha256_slice");
+    let rhs_hash =
+        rsgx_sha256_slice(&rhs_vec).map_err(|_| Error::new(AttestationError::IasError))?;
+    let lhs_hash = &qe_report.body.report_data.d[..32];
+    if rhs_hash != lhs_hash {
+        bail!(AttestationError::QuoteError);
+    }
+
+    Ok(quote)
+}
diff --git a/attestation/src/quote.rs b/attestation/src/quote.rs
deleted file mode 100644
index a389010..0000000
--- a/attestation/src/quote.rs
+++ /dev/null
@@ -1,347 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you 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.
-
-#![allow(clippy::redundant_closure)]
-
-// Insert std prelude in the top for the sgx feature
-#[cfg(feature = "mesalock_sgx")]
-use std::prelude::v1::*;
-
-use anyhow::anyhow;
-use anyhow::{Error, Result};
-use chrono::DateTime;
-use rustls;
-use serde_json;
-use serde_json::Value;
-use std::convert::TryFrom;
-use std::time::*;
-
-#[cfg(feature = "mesalock_sgx")]
-use std::untrusted::time::SystemTimeEx;
-
-use uuid::Uuid;
-
-type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm];
-static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
-    &webpki::ECDSA_P256_SHA256,
-    &webpki::ECDSA_P256_SHA384,
-    &webpki::ECDSA_P384_SHA256,
-    &webpki::ECDSA_P384_SHA384,
-    &webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
-    &webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
-    &webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
-    &webpki::RSA_PKCS1_2048_8192_SHA256,
-    &webpki::RSA_PKCS1_2048_8192_SHA384,
-    &webpki::RSA_PKCS1_2048_8192_SHA512,
-    &webpki::RSA_PKCS1_3072_8192_SHA384,
-];
-
-use thiserror::Error;
-
-#[derive(Error, Debug)]
-pub enum QuoteParsingError {
-    #[error("Invalid cert format")]
-    InvalidCertFormat,
-    #[error("Bad attestation report")]
-    BadAttnReport,
-}
-
-pub struct SgxReport {
-    pub cpu_svn: [u8; 16],
-    pub misc_select: u32,
-    pub attributes: [u8; 16],
-    pub mr_enclave: [u8; 32],
-    pub mr_signer: [u8; 32],
-    pub isv_prod_id: u16,
-    pub isv_svn: u16,
-    pub report_data: [u8; 64],
-}
-
-pub enum SgxQuoteVersion {
-    V1,
-    V2,
-}
-
-pub enum SgxQuoteSigType {
-    Unlinkable,
-    Linkable,
-}
-
-#[derive(PartialEq, Debug)]
-pub enum SgxQuoteStatus {
-    OK,
-    GroupOutOfDate,
-    ConfigurationNeeded,
-    UnknownBadStatus,
-}
-
-impl From<&str> for SgxQuoteStatus {
-    fn from(status: &str) -> Self {
-        match status {
-            "OK" => SgxQuoteStatus::OK,
-            "GROUP_OUT_OF_DATE" => SgxQuoteStatus::GroupOutOfDate,
-            "CONFIGURATION_NEEDED" => SgxQuoteStatus::ConfigurationNeeded,
-            _ => SgxQuoteStatus::UnknownBadStatus,
-        }
-    }
-}
-
-pub struct SgxQuoteBody {
-    pub version: SgxQuoteVersion,
-    pub signature_type: SgxQuoteSigType,
-    pub gid: u32,
-    pub isv_svn_qe: u16,
-    pub isv_svn_pce: u16,
-    pub qe_vendor_id: Uuid,
-    pub user_data: [u8; 20],
-    pub report_body: SgxReport,
-}
-
-impl SgxQuoteBody {
-    fn parse_from<'a>(bytes: &'a [u8]) -> Result<Self> {
-        let mut pos: usize = 0;
-        // TODO: It is really unnecessary to construct a Vec<u8> each time.
-        // Try to optimize this.
-        let mut take = |n: usize| -> Result<&'a [u8]> {
-            if n > 0 && bytes.len() >= pos + n {
-                let ret = &bytes[pos..pos + n];
-                pos += n;
-                Ok(ret)
-            } else {
-                Err(anyhow!("Quote parsing error."))
-            }
-        };
-
-        // off 0, size 2
-        let version = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
-            1 => SgxQuoteVersion::V1,
-            2 => SgxQuoteVersion::V2,
-            _ => return Err(anyhow!("Quote parsing error.")),
-        };
-
-        // off 2, size 2
-        let signature_type = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
-            0 => SgxQuoteSigType::Unlinkable,
-            1 => SgxQuoteSigType::Linkable,
-            _ => return Err(anyhow!("Quote parsing error.")),
-        };
-
-        // off 4, size 4
-        let gid = u32::from_le_bytes(<[u8; 4]>::try_from(take(4)?)?);
-
-        // off 8, size 2
-        let isv_svn_qe = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
-
-        // off 10, size 2
-        let isv_svn_pce = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
-
-        // off 12, size 16
-        let qe_vendor_id_raw = <[u8; 16]>::try_from(take(16)?)?;
-        let qe_vendor_id = Uuid::from_slice(&qe_vendor_id_raw)?;
-
-        // off 28, size 20
-        let user_data = <[u8; 20]>::try_from(take(20)?)?;
-
-        // off 48, size 16
-        let cpu_svn = <[u8; 16]>::try_from(take(16)?)?;
-
-        // off 64, size 4
-        let misc_select = u32::from_le_bytes(<[u8; 4]>::try_from(take(4)?)?);
-
-        // off 68, size 28
-        let _reserved = take(28)?;
-
-        // off 96, size 16
-        let attributes = <[u8; 16]>::try_from(take(16)?)?;
-
-        // off 112, size 32
-        let mr_enclave = <[u8; 32]>::try_from(take(32)?)?;
-
-        // off 144, size 32
-        let _reserved = take(32)?;
-
-        // off 176, size 32
-        let mr_signer = <[u8; 32]>::try_from(take(32)?)?;
-
-        // off 208, size 96
-        let _reserved = take(96)?;
-
-        // off 304, size 2
-        let isv_prod_id = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
-
-        // off 306, size 2
-        let isv_svn = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
-
-        // off 308, size 60
-        let _reserved = take(60)?;
-
-        // off 368, size 64
-        let mut report_data = [0u8; 64];
-        let _report_data = take(64)?;
-        let mut _it = _report_data.iter();
-        for i in report_data.iter_mut() {
-            *i = *_it.next().ok_or_else(|| anyhow!("Quote parsing error."))?;
-        }
-
-        if pos != bytes.len() {
-            return Err(anyhow!("Quote parsing error."));
-        }
-
-        Ok(Self {
-            version,
-            signature_type,
-            gid,
-            isv_svn_qe,
-            isv_svn_pce,
-            qe_vendor_id,
-            user_data,
-            report_body: SgxReport {
-                cpu_svn,
-                misc_select,
-                attributes,
-                mr_enclave,
-                mr_signer,
-                isv_prod_id,
-                isv_svn,
-                report_data,
-            },
-        })
-    }
-}
-
-pub struct SgxQuote {
-    pub freshness: Duration,
-    pub status: SgxQuoteStatus,
-    pub body: SgxQuoteBody,
-}
-
-impl SgxQuote {
-    pub fn extract_verified_quote(cert_der: &[u8], ias_report_ca_cert: &[u8]) -> Result<SgxQuote> {
-        // Before we reach here, Webpki already verifed the cert is properly signed
-        use super::cert::*;
-
-        let x509 = yasna::parse_der(cert_der, |reader| X509::load(reader))?;
-
-        let tbs_cert: <TbsCert as Asn1Ty>::ValueTy = x509.0;
-
-        let pub_key: <PubKey as Asn1Ty>::ValueTy = ((((((tbs_cert.1).1).1).1).1).1).0;
-        let pub_k = (pub_key.1).0;
-
-        let sgx_ra_cert_ext: <SgxRaCertExt as Asn1Ty>::ValueTy =
-            (((((((tbs_cert.1).1).1).1).1).1).1).0;
-
-        let payload: Vec<u8> = ((sgx_ra_cert_ext.0).1).0;
-
-        // Extract each field
-        let mut iter = payload.split(|x| *x == 0x7C);
-        let attn_report_raw = iter
-            .next()
-            .ok_or_else(|| Error::new(QuoteParsingError::InvalidCertFormat))?;
-        let sig_raw = iter
-            .next()
-            .ok_or_else(|| Error::new(QuoteParsingError::InvalidCertFormat))?;
-        let sig = base64::decode(&sig_raw)?;
-        let sig_cert_raw = iter
-            .next()
-            .ok_or_else(|| Error::new(QuoteParsingError::InvalidCertFormat))?;
-        let sig_cert_dec = base64::decode_config(&sig_cert_raw, base64::STANDARD)?;
-
-        let sig_cert = webpki::EndEntityCert::from(&sig_cert_dec)?;
-
-        let mut root_store = rustls::RootCertStore::empty();
-        root_store
-            .add(&rustls::Certificate(ias_report_ca_cert.to_vec()))
-            .expect("Failed to add CA");
-
-        let trust_anchors: Vec<webpki::TrustAnchor> = root_store
-            .roots
-            .iter()
-            .map(|cert| cert.to_trust_anchor())
-            .collect();
-
-        let chain: Vec<&[u8]> = vec![ias_report_ca_cert];
-
-        let now_func = webpki::Time::try_from(SystemTime::now())
-            .map_err(|_| anyhow!("Cannot convert time."))?;
-
-        sig_cert.verify_is_valid_tls_server_cert(
-            SUPPORTED_SIG_ALGS,
-            &webpki::TLSServerTrustAnchors(&trust_anchors),
-            &chain,
-            now_func,
-        )?;
-
-        // Verify the signature against the signing cert
-        sig_cert.verify_signature(&webpki::RSA_PKCS1_2048_8192_SHA256, &attn_report_raw, &sig)?;
-
-        // Verify attestation report
-        let attn_report: Value = serde_json::from_slice(attn_report_raw)?;
-
-        // 1. Check timestamp is within 24H (90day is recommended by Intel)
-        let quote_freshness = {
-            let time = attn_report["timestamp"]
-                .as_str()
-                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
-            let time_fixed = String::from(time) + "+0000";
-            let date_time = DateTime::parse_from_str(&time_fixed, "%Y-%m-%dT%H:%M:%S%.f%z")?;
-            let ts = date_time.naive_utc();
-            let now = DateTime::<chrono::offset::Utc>::from(SystemTime::now()).naive_utc();
-            u64::try_from((now - ts).num_seconds())?
-        };
-
-        // 2. Get quote status
-        let quote_status = {
-            let status_string = attn_report["isvEnclaveQuoteStatus"]
-                .as_str()
-                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
-
-            SgxQuoteStatus::from(status_string)
-        };
-
-        // 3. Get quote body
-        let quote_body = {
-            let quote_encoded = attn_report["isvEnclaveQuoteBody"]
-                .as_str()
-                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
-            let quote_raw = base64::decode(&(quote_encoded.as_bytes()))?;
-            SgxQuoteBody::parse_from(quote_raw.as_slice())?
-        };
-
-        let raw_pub_k = pub_k.to_bytes();
-
-        // According to RFC 5480 `Elliptic Curve Cryptography Subject Public Key Information',
-        // SEC 2.2:
-        // ``The first octet of the OCTET STRING indicates whether the key is
-        // compressed or uncompressed.  The uncompressed form is indicated
-        // by 0x04 and the compressed form is indicated by either 0x02 or
-        // 0x03 (see 2.3.3 in [SEC1]).  The public key MUST be rejected if
-        // any other value is included in the first octet.''
-        //
-        // We only accept the uncompressed form here.
-        let is_uncompressed = raw_pub_k[0] == 4;
-        let pub_k = &raw_pub_k.as_slice()[1..];
-        if !is_uncompressed || pub_k != &quote_body.report_body.report_data[..] {
-            return Err(Error::new(QuoteParsingError::BadAttnReport));
-        }
-
-        Ok(SgxQuote {
-            freshness: std::time::Duration::from_secs(quote_freshness),
-            status: quote_status,
-            body: quote_body,
-        })
-    }
-}
diff --git a/attestation/src/report.rs b/attestation/src/report.rs
index af9bece..71331b5 100644
--- a/attestation/src/report.rs
+++ b/attestation/src/report.rs
@@ -15,188 +15,326 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::ias::IasClient;
-use crate::AttestationError;
-use anyhow::Error;
-use anyhow::Result;
-use hex;
-use log::debug;
-use sgx_rand::os::SgxRng;
-use sgx_rand::Rng;
-use sgx_tcrypto::rsgx_sha256_slice;
-use sgx_tse::{rsgx_create_report, rsgx_verify_report};
-use sgx_types::sgx_ec256_public_t;
-use sgx_types::*;
+#![allow(clippy::redundant_closure)]
+
+// Insert std prelude in the top for the sgx feature
+#[cfg(feature = "mesalock_sgx")]
 use std::prelude::v1::*;
 
-extern "C" {
-    fn ocall_sgx_init_quote(
-        p_retval: *mut sgx_status_t,
-        p_target_info: *mut sgx_target_info_t,
-        p_gid: *mut sgx_epid_group_id_t,
-    ) -> sgx_status_t;
-
-    fn ocall_sgx_calc_quote_size(
-        p_retval: *mut sgx_status_t,
-        p_sig_rl: *const u8,
-        sig_rl_size: u32,
-        p_quote_size: *mut u32,
-    ) -> sgx_status_t;
-
-    fn ocall_sgx_get_quote(
-        p_retval: *mut sgx_status_t,
-        p_report: *const sgx_report_t,
-        quote_type: sgx_quote_sign_type_t,
-        p_spid: *const sgx_spid_t,
-        p_nonce: *const sgx_quote_nonce_t,
-        p_sig_rl: *const u8,
-        sig_rl_size: u32,
-        p_qe_report: *mut sgx_report_t,
-        p_quote: *mut u8,
-        quote_size: u32,
-    ) -> sgx_status_t;
+use crate::ias::IasReport;
+use anyhow::{anyhow, bail};
+use anyhow::{Error, Result};
+use chrono::DateTime;
+use rustls;
+use serde_json;
+use serde_json::Value;
+use std::convert::TryFrom;
+use std::time::*;
+
+#[cfg(feature = "mesalock_sgx")]
+use std::untrusted::time::SystemTimeEx;
+
+use uuid::Uuid;
+
+type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm];
+static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[
+    &webpki::ECDSA_P256_SHA256,
+    &webpki::ECDSA_P256_SHA384,
+    &webpki::ECDSA_P384_SHA256,
+    &webpki::ECDSA_P384_SHA384,
+    &webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
+    &webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
+    &webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
+    &webpki::RSA_PKCS1_2048_8192_SHA256,
+    &webpki::RSA_PKCS1_2048_8192_SHA384,
+    &webpki::RSA_PKCS1_2048_8192_SHA512,
+    &webpki::RSA_PKCS1_3072_8192_SHA384,
+];
+
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum QuoteParsingError {
+    #[error("Invalid cert format")]
+    InvalidCertFormat,
+    #[error("Bad attestation report")]
+    BadAttnReport,
 }
 
-#[derive(Default)]
-pub struct IasReport {
-    pub report: String,
-    pub signature: String,
-    pub signing_cert: String,
+pub struct SgxReport {
+    pub cpu_svn: [u8; 16],
+    pub misc_select: u32,
+    pub attributes: [u8; 16],
+    pub mr_enclave: [u8; 32],
+    pub mr_signer: [u8; 32],
+    pub isv_prod_id: u16,
+    pub isv_svn: u16,
+    pub report_data: [u8; 64],
 }
 
-impl IasReport {
-    pub fn new(
-        pub_k: sgx_ec256_public_t,
-        ias_key: &str,
-        ias_spid: &str,
-        production: bool,
-    ) -> Result<Self> {
-        let (target_info, epid_group_id) = Self::init_quote()?;
-        let mut ias_client = IasClient::new(ias_key, production);
-        let sigrl = ias_client.get_sigrl(u32::from_le_bytes(epid_group_id))?;
-        let report = Self::create_report(pub_k, target_info)?;
-        let quote = Self::get_quote(&sigrl, report, target_info, ias_spid)?;
-        let report = ias_client.get_report(&quote)?;
-        Ok(report)
-    }
+pub enum SgxQuoteVersion {
+    V1,
+    V2,
+}
 
-    fn init_quote() -> Result<(sgx_target_info_t, sgx_epid_group_id_t)> {
-        debug!("init_quote");
-        let mut ti: sgx_target_info_t = sgx_target_info_t::default();
-        let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default();
-        let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
+pub enum SgxQuoteSigType {
+    Unlinkable,
+    Linkable,
+}
 
-        let res = unsafe { ocall_sgx_init_quote(&mut rt as _, &mut ti as _, &mut eg as _) };
+#[derive(PartialEq, Debug)]
+pub enum SgxQuoteStatus {
+    OK,
+    GroupOutOfDate,
+    ConfigurationNeeded,
+    UnknownBadStatus,
+}
 
-        if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
-            Err(Error::new(AttestationError::OCallError))
-        } else {
-            Ok((ti, eg))
+impl From<&str> for SgxQuoteStatus {
+    fn from(status: &str) -> Self {
+        match status {
+            "OK" => SgxQuoteStatus::OK,
+            "GROUP_OUT_OF_DATE" => SgxQuoteStatus::GroupOutOfDate,
+            "CONFIGURATION_NEEDED" => SgxQuoteStatus::ConfigurationNeeded,
+            _ => SgxQuoteStatus::UnknownBadStatus,
         }
     }
+}
 
-    fn create_report(
-        pub_k: sgx_ec256_public_t,
-        target_info: sgx_target_info_t,
-    ) -> Result<sgx_report_t> {
-        debug!("create_report");
-        let mut report_data: sgx_report_data_t = sgx_report_data_t::default();
-        let mut pub_k_gx = pub_k.gx;
-        pub_k_gx.reverse();
-        let mut pub_k_gy = pub_k.gy;
-        pub_k_gy.reverse();
-        report_data.d[..32].clone_from_slice(&pub_k_gx);
-        report_data.d[32..].clone_from_slice(&pub_k_gy);
-
-        rsgx_create_report(&target_info, &report_data)
-            .map_err(|_| Error::new(AttestationError::IasError))
-    }
+pub struct SgxQuoteBody {
+    pub version: SgxQuoteVersion,
+    pub signature_type: SgxQuoteSigType,
+    pub gid: u32,
+    pub isv_svn_qe: u16,
+    pub isv_svn_pce: u16,
+    pub qe_vendor_id: Uuid,
+    pub user_data: [u8; 20],
+    pub report_body: SgxReport,
+}
+
+impl SgxQuoteBody {
+    fn parse_from<'a>(bytes: &'a [u8]) -> Result<Self> {
+        let mut pos: usize = 0;
+        // TODO: It is really unnecessary to construct a Vec<u8> each time.
+        // Try to optimize this.
+        let mut take = |n: usize| -> Result<&'a [u8]> {
+            if n > 0 && bytes.len() >= pos + n {
+                let ret = &bytes[pos..pos + n];
+                pos += n;
+                Ok(ret)
+            } else {
+                bail!("Quote parsing error.")
+            }
+        };
 
-    fn get_quote(
-        sigrl: &[u8],
-        report: sgx_report_t,
-        target_info: sgx_target_info_t,
-        ias_spid_str: &str,
-    ) -> Result<Vec<u8>> {
-        let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED;
-        let (p_sigrl, sigrl_len) = if sigrl.is_empty() {
-            (std::ptr::null(), 0)
-        } else {
-            (sigrl.as_ptr(), sigrl.len() as u32)
+        // off 0, size 2
+        let version = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
+            1 => SgxQuoteVersion::V1,
+            2 => SgxQuoteVersion::V2,
+            _ => bail!("Quote parsing error."),
         };
-        let mut quote_len: u32 = 0;
 
-        let res = unsafe {
-            ocall_sgx_calc_quote_size(&mut rt as _, p_sigrl, sigrl_len, &mut quote_len as _)
+        // off 2, size 2
+        let signature_type = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
+            0 => SgxQuoteSigType::Unlinkable,
+            1 => SgxQuoteSigType::Linkable,
+            _ => bail!("Quote parsing error."),
         };
 
-        if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
-            return Err(Error::new(AttestationError::OCallError));
+        // off 4, size 4
+        let gid = u32::from_le_bytes(<[u8; 4]>::try_from(take(4)?)?);
+
+        // off 8, size 2
+        let isv_svn_qe = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
+
+        // off 10, size 2
+        let isv_svn_pce = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
+
+        // off 12, size 16
+        let qe_vendor_id_raw = <[u8; 16]>::try_from(take(16)?)?;
+        let qe_vendor_id = Uuid::from_slice(&qe_vendor_id_raw)?;
+
+        // off 28, size 20
+        let user_data = <[u8; 20]>::try_from(take(20)?)?;
+
+        // off 48, size 16
+        let cpu_svn = <[u8; 16]>::try_from(take(16)?)?;
+
+        // off 64, size 4
+        let misc_select = u32::from_le_bytes(<[u8; 4]>::try_from(take(4)?)?);
+
+        // off 68, size 28
+        let _reserved = take(28)?;
+
+        // off 96, size 16
+        let attributes = <[u8; 16]>::try_from(take(16)?)?;
+
+        // off 112, size 32
+        let mr_enclave = <[u8; 32]>::try_from(take(32)?)?;
+
+        // off 144, size 32
+        let _reserved = take(32)?;
+
+        // off 176, size 32
+        let mr_signer = <[u8; 32]>::try_from(take(32)?)?;
+
+        // off 208, size 96
+        let _reserved = take(96)?;
+
+        // off 304, size 2
+        let isv_prod_id = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
+
+        // off 306, size 2
+        let isv_svn = u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?);
+
+        // off 308, size 60
+        let _reserved = take(60)?;
+
+        // off 368, size 64
+        let mut report_data = [0u8; 64];
+        let _report_data = take(64)?;
+        let mut _it = _report_data.iter();
+        for i in report_data.iter_mut() {
+            *i = *_it.next().ok_or_else(|| anyhow!("Quote parsing error."))?;
+        }
+
+        if pos != bytes.len() {
+            bail!("Quote parsing error.");
         }
 
-        let mut quote_nonce = sgx_quote_nonce_t { rand: [0; 16] };
-        let mut os_rng = SgxRng::new()?;
-        os_rng.fill_bytes(&mut quote_nonce.rand);
-        let mut qe_report = sgx_report_t::default();
-
-        let quote_type = sgx_quote_sign_type_t::SGX_LINKABLE_SIGNATURE;
-
-        let mut spid = sgx_types::sgx_spid_t::default();
-        let hex = hex::decode(ias_spid_str)?;
-        spid.id.copy_from_slice(&hex[..16]);
-
-        let mut quote = vec![0; quote_len as usize];
-
-        debug!("ocall_sgx_get_quote");
-        let res = unsafe {
-            ocall_sgx_get_quote(
-                &mut rt as _,
-                &report as _,
-                quote_type,
-                &spid as _,
-                &quote_nonce as _,
-                p_sigrl,
-                sigrl_len,
-                &mut qe_report as _,
-                quote.as_mut_ptr(),
-                quote_len,
-            )
+        Ok(Self {
+            version,
+            signature_type,
+            gid,
+            isv_svn_qe,
+            isv_svn_pce,
+            qe_vendor_id,
+            user_data,
+            report_body: SgxReport {
+                cpu_svn,
+                misc_select,
+                attributes,
+                mr_enclave,
+                mr_signer,
+                isv_prod_id,
+                isv_svn,
+                report_data,
+            },
+        })
+    }
+}
+
+pub struct AttestationReport {
+    pub freshness: Duration,
+    pub sgx_quote_status: SgxQuoteStatus,
+    pub sgx_quote_body: SgxQuoteBody,
+}
+
+impl AttestationReport {
+    pub fn from_cert(cert: &[u8], ias_report_ca_cert: &[u8]) -> Result<Self> {
+        // Before we reach here, Webpki already verifed the cert is properly signed
+        use super::cert::*;
+
+        let x509 = yasna::parse_der(cert, |reader| X509::load(reader))?;
+
+        let tbs_cert: <TbsCert as Asn1Ty>::ValueTy = x509.0;
+
+        let pub_key: <PubKey as Asn1Ty>::ValueTy = ((((((tbs_cert.1).1).1).1).1).1).0;
+        let pub_k = (pub_key.1).0;
+
+        let sgx_ra_cert_ext: <SgxRaCertExt as Asn1Ty>::ValueTy =
+            (((((((tbs_cert.1).1).1).1).1).1).1).0;
+
+        let payload: Vec<u8> = ((sgx_ra_cert_ext.0).1).0;
+
+        // Extract each field
+        let report: IasReport = serde_json::from_slice(&payload)?;
+        let signing_cert = webpki::EndEntityCert::from(&report.signing_cert)?;
+
+        let mut root_store = rustls::RootCertStore::empty();
+        root_store
+            .add(&rustls::Certificate(ias_report_ca_cert.to_vec()))
+            .expect("Failed to add CA");
+
+        let trust_anchors: Vec<webpki::TrustAnchor> = root_store
+            .roots
+            .iter()
+            .map(|cert| cert.to_trust_anchor())
+            .collect();
+
+        let chain: Vec<&[u8]> = vec![ias_report_ca_cert];
+
+        let time = webpki::Time::try_from(SystemTime::now())
+            .map_err(|_| anyhow!("Cannot convert time."))?;
+
+        signing_cert.verify_is_valid_tls_server_cert(
+            SUPPORTED_SIG_ALGS,
+            &webpki::TLSServerTrustAnchors(&trust_anchors),
+            &chain,
+            time,
+        )?;
+
+        // Verify the signature against the signing cert
+        signing_cert.verify_signature(
+            &webpki::RSA_PKCS1_2048_8192_SHA256,
+            &report.report,
+            &report.signature,
+        )?;
+
+        // Verify attestation report
+        let attn_report: Value = serde_json::from_slice(&report.report)?;
+
+        // 1. Check timestamp is within 24H (90day is recommended by Intel)
+        let quote_freshness = {
+            let time = attn_report["timestamp"]
+                .as_str()
+                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
+            let time_fixed = String::from(time) + "+0000";
+            let date_time = DateTime::parse_from_str(&time_fixed, "%Y-%m-%dT%H:%M:%S%.f%z")?;
+            let ts = date_time.naive_utc();
+            let now = DateTime::<chrono::offset::Utc>::from(SystemTime::now()).naive_utc();
+            u64::try_from((now - ts).num_seconds())?
         };
 
-        if res != sgx_status_t::SGX_SUCCESS || rt != sgx_status_t::SGX_SUCCESS {
-            return Err(Error::new(AttestationError::OCallError));
-        }
+        // 2. Get quote status
+        let sgx_quote_status = {
+            let status_string = attn_report["isvEnclaveQuoteStatus"]
+                .as_str()
+                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
 
-        debug!("rsgx_verify_report");
-        // Perform a check on qe_report to verify if the qe_report is valid.
-        rsgx_verify_report(&qe_report).map_err(|_| Error::new(AttestationError::IasError))?;
+            SgxQuoteStatus::from(status_string)
+        };
 
-        // Check if the qe_report is produced on the same platform.
-        if target_info.mr_enclave.m != qe_report.body.mr_enclave.m
-            || target_info.attributes.flags != qe_report.body.attributes.flags
-            || target_info.attributes.xfrm != qe_report.body.attributes.xfrm
-        {
-            return Err(Error::new(AttestationError::QuoteError));
-        }
+        // 3. Get quote body
+        let sgx_quote_body = {
+            let quote_encoded = attn_report["isvEnclaveQuoteBody"]
+                .as_str()
+                .ok_or_else(|| Error::new(QuoteParsingError::BadAttnReport))?;
+            let quote_raw = base64::decode(&(quote_encoded.as_bytes()))?;
+            SgxQuoteBody::parse_from(quote_raw.as_slice())?
+        };
+
+        let raw_pub_k = pub_k.to_bytes();
 
-        // Check qe_report to defend against replay attack. The purpose of
-        // p_qe_report is for the ISV enclave to confirm the QUOTE it 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 (target info from p_report) , with the lower 32Bytes in
-        // report.data = SHA256(p_nonce||p_quote). The ISV enclave can verify
-        // the p_qe_report and report.data to confirm the QUOTE has not be
-        // modified and is not a replay. It is optional.
-        let mut rhs_vec: Vec<u8> = quote_nonce.rand.to_vec();
-        rhs_vec.extend(&quote);
-        debug!("rsgx_sha256_slice");
-        let rhs_hash =
-            rsgx_sha256_slice(&rhs_vec).map_err(|_| Error::new(AttestationError::IasError))?;
-        let lhs_hash = &qe_report.body.report_data.d[..32];
-        if rhs_hash != lhs_hash {
-            return Err(Error::new(AttestationError::QuoteError));
+        // According to RFC 5480 `Elliptic Curve Cryptography Subject Public Key Information',
+        // SEC 2.2:
+        // ``The first octet of the OCTET STRING indicates whether the key is
+        // compressed or uncompressed.  The uncompressed form is indicated
+        // by 0x04 and the compressed form is indicated by either 0x02 or
+        // 0x03 (see 2.3.3 in [SEC1]).  The public key MUST be rejected if
+        // any other value is included in the first octet.''
+        //
+        // We only accept the uncompressed form here.
+        let is_uncompressed = raw_pub_k[0] == 4;
+        let pub_k = &raw_pub_k.as_slice()[1..];
+        if !is_uncompressed || pub_k != &sgx_quote_body.report_body.report_data[..] {
+            bail!(QuoteParsingError::BadAttnReport);
         }
 
-        Ok(quote)
+        Ok(Self {
+            freshness: std::time::Duration::from_secs(quote_freshness),
+            sgx_quote_status,
+            sgx_quote_body,
+        })
     }
 }
diff --git a/attestation/src/verifier.rs b/attestation/src/verifier.rs
index 4d0561e..aee8a8e 100644
--- a/attestation/src/verifier.rs
+++ b/attestation/src/verifier.rs
@@ -15,7 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::quote::SgxQuote;
+use crate::report::AttestationReport;
+use log::{debug, error};
 use std::hash::{Hash, Hasher};
 use std::vec::Vec;
 use teaclave_config::build_config::BUILD_CONFIG;
@@ -42,31 +43,31 @@ impl Hash for EnclaveAttr {
 }
 
 #[derive(Clone)]
-pub struct SgxQuoteVerifier {
+pub struct AttestationReportVerifier {
     pub enclave_attr: EnclaveAttr,
-    pub verifier: fn(&SgxQuote) -> bool,
+    pub verifier: fn(&AttestationReport) -> bool,
 }
 
-impl PartialEq for SgxQuoteVerifier {
-    fn eq(&self, other: &SgxQuoteVerifier) -> bool {
+impl PartialEq for AttestationReportVerifier {
+    fn eq(&self, other: &AttestationReportVerifier) -> bool {
         self.verifier as usize == other.verifier as usize && self.enclave_attr == other.enclave_attr
     }
 }
 
-impl Eq for SgxQuoteVerifier {}
+impl Eq for AttestationReportVerifier {}
 
-impl Hash for SgxQuoteVerifier {
+impl Hash for AttestationReportVerifier {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.enclave_attr.hash(state);
         (self.verifier as usize).hash(state);
     }
 }
 
-fn universal_quote_verifier(quote: &SgxQuote) -> bool {
-    quote.status != crate::quote::SgxQuoteStatus::UnknownBadStatus
+fn universal_quote_verifier(report: &AttestationReport) -> bool {
+    report.sgx_quote_status != crate::report::SgxQuoteStatus::UnknownBadStatus
 }
 
-impl SgxQuoteVerifier {
+impl AttestationReportVerifier {
     pub fn new(enclave_attr: EnclaveAttr) -> Self {
         Self {
             enclave_attr,
@@ -74,9 +75,13 @@ impl SgxQuoteVerifier {
         }
     }
 
-    fn verify_measures(&self, quote: &SgxQuote) -> bool {
-        let this_mr_signer = quote.body.report_body.mr_signer;
-        let this_mr_enclave = quote.body.report_body.mr_enclave;
+    fn verify_measures(&self, attestation_report: &AttestationReport) -> bool {
+        debug!("verify measures");
+        let this_mr_signer = attestation_report.sgx_quote_body.report_body.mr_signer;
+        let this_mr_enclave = attestation_report.sgx_quote_body.report_body.mr_enclave;
+        for m in self.enclave_attr.measures.iter() {
+            debug!("{:?}", m.mr_signer);
+        }
 
         self.enclave_attr
             .measures
@@ -85,14 +90,15 @@ impl SgxQuoteVerifier {
     }
 
     fn verify_cert(&self, cert_der: &[u8]) -> bool {
+        debug!("verify cert");
         if cfg!(sgx_sim) {
             return true;
         }
 
-        let quote = match SgxQuote::extract_verified_quote(&cert_der, BUILD_CONFIG.ias_root_ca_cert)
-        {
-            Ok(quote) => quote,
-            Err(_) => {
+        let report = match AttestationReport::from_cert(&cert_der, BUILD_CONFIG.ias_root_ca_cert) {
+            Ok(report) => report,
+            Err(e) => {
+                error!("{:?}", e);
                 return false;
             }
         };
@@ -100,14 +106,14 @@ impl SgxQuoteVerifier {
         // Enclave measures are not tested in test mode since we have
         // a dedicated test enclave not known to production enclaves
         if cfg!(test_mode) {
-            return (self.verifier)(&quote);
+            return (self.verifier)(&report);
         }
 
-        self.verify_measures(&quote) && (self.verifier)(&quote)
+        self.verify_measures(&report) && (self.verifier)(&report)
     }
 }
 
-impl rustls::ServerCertVerifier for SgxQuoteVerifier {
+impl rustls::ServerCertVerifier for AttestationReportVerifier {
     fn verify_server_cert(
         &self,
         _roots: &rustls::RootCertStore,
@@ -129,7 +135,7 @@ impl rustls::ServerCertVerifier for SgxQuoteVerifier {
     }
 }
 
-impl rustls::ClientCertVerifier for SgxQuoteVerifier {
+impl rustls::ClientCertVerifier for AttestationReportVerifier {
     fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames {
         rustls::DistinguishedNames::new()
     }
diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml
index f95fd2a..49800fd 100644
--- a/rpc/Cargo.toml
+++ b/rpc/Cargo.toml
@@ -26,6 +26,7 @@ threadpool = { version = "1.8.0" }
 webpki     = { version = "0.21.0" }
 
 teaclave_types       = { path = "../types" }
+teaclave_attestation = { path = "../attestation" }
 
 sgx_trts = { version = "1.1.0" }
 sgx_tstd = { version = "1.1.0", features = ["net", "backtrace"], optional = true }
diff --git a/rpc/src/config.rs b/rpc/src/config.rs
index 5ecfd7d..3395616 100644
--- a/rpc/src/config.rs
+++ b/rpc/src/config.rs
@@ -1,6 +1,8 @@
 use anyhow::Result;
 use rustls;
 use std::sync::Arc;
+use teaclave_attestation::verifier::AttestationReportVerifier;
+use teaclave_attestation::verifier::EnclaveAttr;
 
 pub struct SgxTrustedTlsServerConfig {
     pub config: Arc<rustls::ServerConfig>,
@@ -61,4 +63,17 @@ impl SgxTrustedTlsClientConfig {
             config: Arc::new(config),
         }
     }
+
+    pub fn new_with_attestation_report_verifier(enclave_attr: EnclaveAttr) -> Self {
+        let mut config = rustls::ClientConfig::new();
+        let verifier = Arc::new(AttestationReportVerifier::new(enclave_attr));
+
+        config.dangerous().set_certificate_verifier(verifier);
+        config.versions.clear();
+        config.versions.push(rustls::ProtocolVersion::TLSv1_2);
+
+        Self {
+            config: Arc::new(config),
+        }
+    }
 }
diff --git a/testsuite/service_functional_test/enclave/src/lib.rs b/testsuite/service_functional_test/enclave/src/lib.rs
index da1808a..e4b0f29 100644
--- a/testsuite/service_functional_test/enclave/src/lib.rs
+++ b/testsuite/service_functional_test/enclave/src/lib.rs
@@ -17,6 +17,7 @@
 
 #![cfg_attr(feature = "mesalock_sgx", no_std)]
 #[cfg(feature = "mesalock_sgx")]
+#[macro_use]
 extern crate sgx_tstd as std;
 
 #[macro_use]
diff --git a/testsuite/service_functional_test/enclave/src/teaclave_authentication_service.rs b/testsuite/service_functional_test/enclave/src/teaclave_authentication_service.rs
index 328bbf8..b3f46f2 100644
--- a/testsuite/service_functional_test/enclave/src/teaclave_authentication_service.rs
+++ b/testsuite/service_functional_test/enclave/src/teaclave_authentication_service.rs
@@ -1,8 +1,12 @@
 use sgx_tunittest::*;
 use std::prelude::v1::*;
+use teaclave_attestation::verifier::EnclaveAttr;
+use teaclave_config::runtime_config::RuntimeConfig;
 use teaclave_proto::teaclave_authentication_service::*;
 use teaclave_proto::teaclave_common::*;
+use teaclave_rpc::config::SgxTrustedTlsClientConfig;
 use teaclave_rpc::endpoint::Endpoint;
+use teaclave_types::EnclaveInfo;
 
 pub fn run_functional_tests() {
     rsgx_unit_tests!(
@@ -14,7 +18,21 @@ pub fn run_functional_tests() {
 }
 
 fn test_login_success() {
-    let channel = Endpoint::new("localhost:7776").connect().unwrap();
+    let runtime_config = RuntimeConfig::from_toml("runtime.config.toml").expect("runtime");
+    let enclave_info = EnclaveInfo::load_enclave_info(&runtime_config.audit.enclave_info);
+    let measure = enclave_info
+        .measurements
+        .get("teaclave_authentication_service")
+        .expect("authentication");
+    let enclave_attr = EnclaveAttr {
+        measures: vec![*measure],
+    };
+    let config = SgxTrustedTlsClientConfig::new_with_attestation_report_verifier(enclave_attr);
+
+    let channel = Endpoint::new("localhost:7776")
+        .config(config)
+        .connect()
+        .unwrap();
     let mut client = TeaclaveAuthenticationClient::new(channel).unwrap();
     let request = UserLoginRequest {
         id: "test_id".to_string(),


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org