blob: 5b8d758bbf4bb5c7693eb8d293dd1c5aad665b09 [file] [log] [blame]
// 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.
//! Types that contain information about attestation report.
//! The implementation is based on Attestation Service API version 4.
//! https://api.trustedservices.intel.com/documents/sgx-attestation-api-spec.pdf
#[cfg(feature = "mesalock_sgx")]
use std::prelude::v1::*;
use crate::AttestationError;
use crate::EndorsedAttestationReport;
use std::convert::TryFrom;
use std::time::*;
#[cfg(feature = "mesalock_sgx")]
use std::untrusted::time::SystemTimeEx;
use anyhow::{anyhow, bail, ensure, Error, Result};
use chrono::DateTime;
use serde_json::Value;
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,
];
/// A report generated by an enclave that contains measurement, identity and
/// other data related to enclave.
///
/// # Note
///
/// Do not confuse `SgxEnclaveReport` with `AttestationReport`.
/// `SgxEnclaveReport` is generated by SGX hardware and endorsed by Quoting
/// Enclave through local attestation. The endorsed `SgxEnclaveReport` is an
/// `SgxQuote`. The quote is then sent to some attestation service (IAS or
/// DCAP-based AS). The endorsed `SgxQuote` is an attestation report signed by
/// attestation service's private key, a.k.a., `EndorsedAttestationReport`.
pub struct SgxEnclaveReport {
/// Security version number of host system's CPU
pub cpu_svn: [u8; 16],
/// Misc select bits for the target enclave. Reserved for future function
/// extension.
pub misc_select: u32,
/// Attributes of the enclave, for example, whether the enclave is running
/// in debug mode.
pub attributes: [u8; 16],
/// Measurement value of the enclave. See
/// [`EnclaveMeasurement`](../types/struct.EnclaveMeasurement.html)
pub mr_enclave: [u8; 32],
/// Measurement value of the public key that verified the enclave. See
/// [`EnclaveMeasurement`](../types/struct.EnclaveMeasurement.html)
pub mr_signer: [u8; 32],
/// Product ID of the enclave
pub isv_prod_id: u16,
/// Security version number of the enclave
pub isv_svn: u16,
/// Set of data used for communication between enclave and target enclave
pub report_data: [u8; 64],
}
impl std::fmt::Debug for SgxEnclaveReport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "cpu_svn: {:?}", self.cpu_svn)?;
writeln!(f, "misc_select: {:?}", self.misc_select)?;
writeln!(f, "attributes: {:?}", self.attributes)?;
writeln!(f, "mr_enclave: {:?}", self.mr_enclave)?;
writeln!(f, "mr_signer: {:?}", self.mr_signer)?;
writeln!(f, "isv_prod_id: {}", self.isv_prod_id)?;
writeln!(f, "isv_svn: {}", self.isv_svn)?;
writeln!(f, "report_data: {:?}", &self.report_data.to_vec())
}
}
impl SgxEnclaveReport {
/// Parse bytes of report into `SgxEnclaveReport`.
pub fn parse_from<'a>(bytes: &'a [u8]) -> Result<Self> {
let mut pos: usize = 0;
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.")
}
};
// Start parsing report by bytes following specifications. Don't
// transmute directly, since there may cause endianness issue.
// 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."))?;
}
ensure!(pos == bytes.len(), "Quote parsing error.");
Ok(SgxEnclaveReport {
cpu_svn,
misc_select,
attributes,
mr_enclave,
mr_signer,
isv_prod_id,
isv_svn,
report_data,
})
}
}
/// SGX Quote structure version
#[derive(Debug, PartialEq)]
pub enum SgxQuoteVersion {
/// EPID quote version
V1(SgxEpidQuoteSigType),
/// EPID quote version
V2(SgxEpidQuoteSigType),
/// ECDSA quote version
V3(SgxEcdsaQuoteAkType),
}
/// Intel EPID attestation signature type
#[derive(Debug, PartialEq)]
pub enum SgxEpidQuoteSigType {
Unlinkable,
Linkable,
}
/// ECDSA attestation key type
#[derive(Debug, PartialEq)]
pub enum SgxEcdsaQuoteAkType {
/// ECDSA-256-with-P-256 curve
P256_256,
/// ECDSA-384-with-P-384 curve
P384_384,
}
/// SGX Quote status
#[derive(PartialEq, Debug)]
pub enum SgxQuoteStatus {
/// EPID signature of the ISV enclave QUOTE was verified correctly and the
/// TCB level of the SGX platform is up-to-date.
OK,
/// EPID signature of the ISV enclave QUOTE was invalid. The content of the
/// QUOTE is not trustworthy.
///
/// For DCAP, the signature over the application report is invalid.
SignatureInvalid,
/// The EPID group has been revoked. When this value is returned, the
/// revocation Reason field of the Attestation Verification Report will
/// contain revocation reason code for this EPID group as reported in the
/// EPID Group CRL. The content of the QUOTE is not trustworthy.
GroupRevoked,
/// The EPID private key used to sign the QUOTE has been revoked by
/// signature. The content of the QUOTE is not trustworthy.
SignatureRevoked,
/// The EPID private key used to sign the QUOTE has been directly revoked
/// (not by signature). The content of the QUOTE is not trustworthy.
///
/// For DCAP, the attestation key or platform has been revoked.
KeyRevoked,
/// SigRL version in ISV enclave QUOTE does not match the most recent
/// version of the SigRL. In rare situations, after SP retrieved the SigRL
/// from IAS and provided it to the platform, a newer version of the SigRL
/// is madeavailable. As a result, the Attestation Verification Report will
/// indicate SIGRL_VERSION_MISMATCH. SP can retrieve the most recent version
/// of SigRL from the IAS and request the platform to perform remote
/// attestation again with the most recent version of SigRL. If the platform
/// keeps failing to provide a valid QUOTE matching with the most recent
/// version of the SigRL, the content of the QUOTE is not trustworthy.
SigrlVersionMismatch,
/// The EPID signature of the ISV enclave QUOTE has been verified correctly,
/// but the TCB level of SGX platform is outdated (for further details see
/// Advisory IDs). The platform has not been identified as compromised and
/// thus it is not revoked. It is up to the Service Provider to decide
/// whether or not to trust the content of the QUOTE, andwhether or not to
/// trust the platform performing the attestation to protect specific
/// sensitive information.
GroupOutOfDate,
/// The EPID signature of the ISV enclave QUOTE has been verified correctly,
/// but additional configuration of SGX platform may beneeded(for further
/// details see Advisory IDs). The platform has not been identified as
/// compromised and thus it is not revoked. It is up to the Service Provider
/// to decide whether or not to trust the content of the QUOTE, and whether
/// or not to trust the platform performing the attestation to protect
/// specific sensitive information.
///
/// For DCAP, The Quote verification passed and the platform is patched to
/// the latest TCB level but additional configuration of the SGX
/// platform may be needed.
ConfigurationNeeded,
/// The EPID signature of the ISV enclave QUOTE has been verified correctly
/// but due to certain issues affecting the platform, additional SW
/// Hardening in the attesting SGX enclaves may be needed.The relying party
/// should evaluate the potential risk of an attack leveraging the relevant
/// issues on the attesting enclave, and whether the attesting enclave
/// employs adequate software hardening to mitigate the risk.
SwHardeningNeeded,
/// The EPID signature of the ISV enclave QUOTE has been verified correctly
/// but additional configuration for the platform and SW Hardening in the
/// attesting SGX enclaves may be needed. The platform has not been
/// identified as compromised and thus it is not revoked. It is up to the
/// Service Provider to decide whether or not to trust the content of the
/// QUOTE. The relying party should also evaluate the potential risk of an
/// attack leveraging the relevant issues on the attestation enclave, and
/// whether the attesting enclave employs adequate software hardening to
/// mitigate the risk.
ConfigurationAndSwHardeningNeeded,
/// DCAP specific quote status. The Quote is good but TCB level of the
/// platform is out of date. The platform needs patching to be at the latest
/// TCB level.
OutOfDate,
/// DCAP specific quote status. The Quote is good but the TCB level of the
/// platform is out of date and additional configuration of the SGX Platform
/// at its current patching level may be needed. The platform needs patching
/// to be at the latest TCB level.
OutOfDateConfigurationNeeded,
/// DCAP specific quote status. The signature over the application report is
/// invalid.
InvalidSignature,
/// Other unknown bad status.
UnknownBadStatus,
}
impl From<&str> for SgxQuoteStatus {
/// Convert from str status from the report to enum.
fn from(status: &str) -> Self {
match status {
"OK" => SgxQuoteStatus::OK,
"SIGNATURE_INVALID" => SgxQuoteStatus::SignatureInvalid,
"GROUP_REVOKED" => SgxQuoteStatus::GroupRevoked,
"SIGNATURE_REVOKED" => SgxQuoteStatus::SignatureRevoked,
"KEY_REVOKED" => SgxQuoteStatus::KeyRevoked,
"SIGRL_VERSION_MISMATCH" => SgxQuoteStatus::SigrlVersionMismatch,
"GROUP_OUT_OF_DATE" => SgxQuoteStatus::GroupOutOfDate,
"OUT_OF_DATE" => SgxQuoteStatus::OutOfDate,
"OUT_OF_DATE_CONFIGURATION_NEEDED" => SgxQuoteStatus::OutOfDateConfigurationNeeded,
"CONFIGURATION_NEEDED" => SgxQuoteStatus::ConfigurationNeeded,
"SW_HARDENING_NEEDED" => SgxQuoteStatus::SwHardeningNeeded,
"CONFIGURATION_AND_SW_HARDENING_NEEDED" => {
SgxQuoteStatus::ConfigurationAndSwHardeningNeeded
}
_ => SgxQuoteStatus::UnknownBadStatus,
}
}
}
/// An application that hosts an enclave can ask the enclave to produce a report
/// (`SgxEnclaveReport`) and then pass this report to a platform service
/// (Quoting Enclave) to produce a type of credential that reflects the enclave
/// and platform state. The quote can be passed to entities off the platform,
/// and verified using Intel EPID signature verification techniques.
pub struct SgxQuote {
/// Version of the quote structure
pub version: SgxQuoteVersion,
/// ID of the Intel EPID group of the platform belongs to
pub gid: u32,
/// Security version number of Quoting Enclave
pub isv_svn_qe: u16,
/// Security version number of PCE
pub isv_svn_pce: u16,
/// Vendor ID of Quoting Enclave
pub qe_vendor_id: Uuid,
/// User data
pub user_data: [u8; 20],
/// Report generated by the enclave
pub isv_enclave_report: SgxEnclaveReport,
}
impl std::fmt::Debug for SgxQuote {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "version: {:?}", self.version)?;
writeln!(f, "gid: {}", self.gid)?;
writeln!(f, "isv_svn_qe: {}", self.isv_svn_qe)?;
writeln!(f, "isv_svn_pce: {}", self.isv_svn_pce)?;
writeln!(f, "qe_vendor_id: {}", self.qe_vendor_id)?;
writeln!(f, "user_data: {:?}", &self.user_data)?;
writeln!(f, "isv_enclave_report: \n{:?}", self.isv_enclave_report)
}
}
impl SgxQuote {
/// Parse from bytes to `SgxQuote`.
pub fn parse_from<'a>(bytes: &'a [u8]) -> Result<Self> {
let mut pos: usize = 0;
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.")
}
};
// Parse by bytes according to specifications.
// off 0, size 2 + 2
let version = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
1 => {
let signature_type = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
0 => SgxEpidQuoteSigType::Unlinkable,
1 => SgxEpidQuoteSigType::Linkable,
_ => bail!("Quote parsing error."),
};
SgxQuoteVersion::V1(signature_type)
}
2 => {
let signature_type = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?) {
0 => SgxEpidQuoteSigType::Unlinkable,
1 => SgxEpidQuoteSigType::Linkable,
_ => bail!("Quote parsing error."),
};
SgxQuoteVersion::V2(signature_type)
}
3 => {
let attestation_key_type = match u16::from_le_bytes(<[u8; 2]>::try_from(take(2)?)?)
{
2 => SgxEcdsaQuoteAkType::P256_256,
3 => SgxEcdsaQuoteAkType::P384_384,
_ => bail!("Quote parsing error."),
};
SgxQuoteVersion::V3(attestation_key_type)
}
_ => bail!("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 384
let isv_enclave_report = SgxEnclaveReport::parse_from(take(384)?)?;
ensure!(pos == bytes.len(), "Quote parsing error.");
Ok(Self {
version,
gid,
isv_svn_qe,
isv_svn_pce,
qe_vendor_id,
user_data,
isv_enclave_report,
})
}
}
/// A report that can be signed by Intel EPID (which generates
/// `EndorsedAttestationReport`) and then sent off of the platform to be
/// verified by remote client.
#[derive(Debug)]
pub struct AttestationReport {
/// The freshness of the report, i.e., elapsed time after acquiring the
/// report in seconds.
pub freshness: Duration,
/// Quote status
pub sgx_quote_status: SgxQuoteStatus,
/// Content of the quote
pub sgx_quote_body: SgxQuote,
}
impl AttestationReport {
/// Construct a AttestationReport from a X509 certificate and verify
/// attestation report with the report_ca_cert which is from the attestation
/// service provider.
pub fn from_cert(cert: &[u8], report_ca_cert: &[u8]) -> Result<Self> {
// Before we reach here, Webpki already verifed the cert is properly signed.
use crate::cert::*;
// Extract information for attestation from TLS certification.
let x509 = yasna::parse_der(cert, X509::load)?;
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 cert_ext: <SgxRaCertExt as Asn1Ty>::ValueTy = (((((((tbs_cert.1).1).1).1).1).1).1).0;
let cert_ext_payload: Vec<u8> = ((cert_ext.0).1).0;
// Convert to endorsed report
let report: EndorsedAttestationReport = serde_json::from_slice(&cert_ext_payload)?;
// Verify report's signature
let signing_cert = webpki::EndEntityCert::from(&report.signing_cert)?;
let root_store = {
let mut root_store = rustls::RootCertStore::empty();
root_store.add(&rustls::Certificate(report_ca_cert.to_vec()))?;
root_store
};
let trust_anchors: Vec<webpki::TrustAnchor> = root_store
.roots
.iter()
.map(|cert| cert.to_trust_anchor())
.collect();
let chain = vec![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 and extract information from attestation report
let attn_report: Value = serde_json::from_slice(&report.report)?;
log::trace!("attn_report: {}", attn_report);
// Verify API version is supported
let version = attn_report["version"]
.as_u64()
.ok_or_else(|| Error::new(AttestationError::ReportError))?;
ensure!(version == 4, AttestationError::ApiVersionNotCompatible);
// Get quote freshness
let freshness = {
let time = attn_report["timestamp"]
.as_str()
.ok_or_else(|| Error::new(AttestationError::ReportError))?;
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();
let quote_freshness = u64::try_from((now - ts).num_seconds())?;
std::time::Duration::from_secs(quote_freshness)
};
// Get quote status
let sgx_quote_status = {
let status_string = attn_report["isvEnclaveQuoteStatus"]
.as_str()
.ok_or_else(|| Error::new(AttestationError::ReportError))?;
SgxQuoteStatus::from(status_string)
};
// Get quote body
let sgx_quote_body = {
let quote_encoded = attn_report["isvEnclaveQuoteBody"]
.as_str()
.ok_or_else(|| Error::new(AttestationError::ReportError))?;
let quote_raw = base64::decode(&quote_encoded.as_bytes())?;
SgxQuote::parse_from(quote_raw.as_slice())?
};
// 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 raw_pub_k = pub_k.to_bytes();
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.isv_enclave_report.report_data[..] {
bail!(AttestationError::ReportError);
}
Ok(Self {
freshness,
sgx_quote_status,
sgx_quote_body,
})
}
}
#[cfg(all(feature = "enclave_unit_test", feature = "mesalock_sgx"))]
pub mod tests {
use super::*;
use serde_json::json;
use std::io::Read;
use std::untrusted::fs::File;
use teaclave_test_utils::*;
fn tls_ra_cert_der_v3() -> Vec<u8> {
let mut cert = vec![];
let mut f = File::open("fixtures/tls_ra_cert_v3.der").unwrap();
f.read_to_end(&mut cert).unwrap();
cert
}
fn tls_ra_cert_der_v4() -> Vec<u8> {
let mut cert = vec![];
let mut f = File::open("fixtures/tls_ra_cert_v4.der").unwrap();
f.read_to_end(&mut cert).unwrap();
cert
}
fn ias_root_ca_cert_der() -> Vec<u8> {
let mut cert = vec![];
let mut f = File::open("fixtures/ias_root_ca_cert.der").unwrap();
f.read_to_end(&mut cert).unwrap();
cert
}
fn attesation_report() -> Value {
let report = json!({
"version": 3,
"timestamp": "2020-02-11T22:25:59.682915",
"platformInfoBlob": "1502006504000900000D0D02040180030000000000000000000\
A00000B000000020000000000000B2FE0AE0F7FD4D552BF7EF4\
C938D44E349F1BD0E76F041362DC52B43B7B25994978D792137\
90362F6DAE91797ACF5BD5072E45F9A60795D1FFB10140421D8\
691FFD",
"isvEnclaveQuoteStatus": "GROUP_OUT_OF_DATE",
"isvEnclaveQuoteBody": "AgABAC8LAAAKAAkAAAAAAK1zRQOIpndiP4IhlnW2AkwAAAAA\
AAAAAAAAAAAAAAAABQ4CBf+AAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHAAAA\
AAAAADMKqRCjd2eA4gAmrj2sB68OWpMfhPH4MH27hZAvWGlT\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD1xnn\
ferKFHD2uvYqTXdDA8iZ22kCD5xw7h38CMfOngAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAADYIY9k0MVmCdIDUuFLf/2bGIHAfPjO9nvC7fgz\
rQedeA3WW4dFeI6oe+RCLdV3XYD1n6lEZjITOzPPLWDxulGz",
"id": "53530608302195762335736519878284384788",
"epidPseudonym": "NRksaQej8R/SyyHpZXzQGNBXqfrzPy5KCxcmJrEjupXrq3xrm2y2+J\
p0IBVtcW15MCekYs9K3UH82fPyj6F5ciJoMsgEMEIvRR+csX9uyd54\
p+m+/RVyuGYhWbhUcpJigdI5Q3x04GG/A7EP10j/zypwqhYLQh0qN1\
ykYt1N1P0="
});
report
}
pub fn run_tests() -> bool {
run_tests!(
test_sgx_quote_parse_from,
test_attestation_report_from_cert,
test_attestation_report_from_cert_api_version_not_compatible
)
}
fn test_sgx_quote_parse_from() {
let attn_report = attesation_report();
let sgx_quote_body_encoded = attn_report["isvEnclaveQuoteBody"].as_str().unwrap();
let quote_raw = base64::decode(&sgx_quote_body_encoded.as_bytes()).unwrap();
let sgx_quote = SgxQuote::parse_from(quote_raw.as_slice()).unwrap();
assert_eq!(
sgx_quote.version,
SgxQuoteVersion::V2(SgxEpidQuoteSigType::Linkable)
);
assert_eq!(sgx_quote.gid, 2863);
assert_eq!(sgx_quote.isv_svn_qe, 10);
assert_eq!(sgx_quote.isv_svn_pce, 9);
assert_eq!(
sgx_quote.qe_vendor_id,
Uuid::parse_str("00000000-ad73-4503-88a6-77623f822196").unwrap()
);
assert_eq!(
sgx_quote.user_data,
[117, 182, 2, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
let isv_enclave_report = sgx_quote.isv_enclave_report;
assert_eq!(
isv_enclave_report.cpu_svn,
[5, 14, 2, 5, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(isv_enclave_report.misc_select, 0);
assert_eq!(
isv_enclave_report.attributes,
[7, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
isv_enclave_report.mr_enclave,
[
51, 10, 169, 16, 163, 119, 103, 128, 226, 0, 38, 174, 61, 172, 7, 175, 14, 90, 147,
31, 132, 241, 248, 48, 125, 187, 133, 144, 47, 88, 105, 83
]
);
assert_eq!(
isv_enclave_report.mr_signer,
[
131, 215, 25, 231, 125, 234, 202, 20, 112, 246, 186, 246, 42, 77, 119, 67, 3, 200,
153, 219, 105, 2, 15, 156, 112, 238, 29, 252, 8, 199, 206, 158
]
);
assert_eq!(isv_enclave_report.isv_prod_id, 0);
assert_eq!(isv_enclave_report.isv_svn, 0);
assert_eq!(
isv_enclave_report.report_data.to_vec(),
[
216, 33, 143, 100, 208, 197, 102, 9, 210, 3, 82, 225, 75, 127, 253, 155, 24, 129,
192, 124, 248, 206, 246, 123, 194, 237, 248, 51, 173, 7, 157, 120, 13, 214, 91,
135, 69, 120, 142, 168, 123, 228, 66, 45, 213, 119, 93, 128, 245, 159, 169, 68,
102, 50, 19, 59, 51, 207, 45, 96, 241, 186, 81, 179
]
.to_vec()
);
}
fn test_attestation_report_from_cert() {
let tls_ra_cert = tls_ra_cert_der_v4();
let ias_root_ca_cert = ias_root_ca_cert_der();
let report = AttestationReport::from_cert(&tls_ra_cert, &ias_root_ca_cert);
assert!(report.is_ok());
let report = report.unwrap();
assert_eq!(report.sgx_quote_status, SgxQuoteStatus::GroupOutOfDate);
}
fn test_attestation_report_from_cert_api_version_not_compatible() {
let tls_ra_cert = tls_ra_cert_der_v3();
let ias_root_ca_cert = ias_root_ca_cert_der();
let report = AttestationReport::from_cert(&tls_ra_cert, &ias_root_ca_cert);
assert!(report.is_err());
}
}