diff --git a/common/rust/cctrusted_base/src/api.rs b/common/rust/cctrusted_base/src/api.rs index dfe84d50..1d681819 100644 --- a/common/rust/cctrusted_base/src/api.rs +++ b/common/rust/cctrusted_base/src/api.rs @@ -2,12 +2,7 @@ use crate::api_data::Algorithm; use crate::api_data::*; use crate::eventlog::TcgEventLog; use crate::tcg::TcgDigest; -use crate::tdx::quote::TdxQuote; -use crate::tpm::quote::TpmQuote; -use anyhow::*; -use core::mem; use core::result::Result; -use core::result::Result::Ok; pub trait CCTrustedApi { /*** @@ -28,9 +23,9 @@ pub trait CCTrustedApi { The cc report byte array or error information */ fn get_cc_report( - nonce: String, - data: String, - _extra_args: ExtraArgs, + nonce: Option, + data: Option, + extra_args: ExtraArgs, ) -> Result; /*** @@ -45,20 +40,31 @@ pub trait CCTrustedApi { fn dump_cc_report(report: &Vec); /*** - Get measurement register according to given selected index and algorithms + Get the count of measurement register. + Different trusted foundation may provide different count of measurement + register. For example, Intel TDX TDREPORT provides the 4 measurement + register by default. TPM provides 24 measurement (0~16 for SRTM and 17~24 + for DRTM). + Beyond the real mesurement register, some SDK may extend virtual measurement + reigster for addtional trust chain like container, namespace, cluster in + cloud native paradiagm. + Returns: + The count of measurement registers + */ + fn get_measurement_count() -> Result; + /*** + Get measurement register according to given selected index and algorithms Each trusted foundation in CC environment provides the multiple measurement registers, the count is update to ``get_measurement_count()``. And for each measurement register, it may provides multiple digest for different algorithms. - Args: index (u8): the index of measurement register, algo_id (u8): the alrogithms ID - Returns: TcgDigest struct */ - fn get_cc_measurement(_index: u8, _algo_id: u8) -> TcgDigest; + fn get_cc_measurement(index: u8, algo_id: u8) -> Result; /*** Get eventlog for given index and count. @@ -104,25 +110,5 @@ pub trait CCTrustedApi { fn parse_cc_report(report: Vec) -> Result; */ pub trait ParseCcReport { - fn parse_cc_report(_report: Vec) -> Result; -} - -// API function parses raw cc report to TdxQuote struct -impl ParseCcReport for CcReport { - fn parse_cc_report(report: Vec) -> Result { - match TdxQuote::parse_tdx_quote(report) { - Ok(tdx_quote) => unsafe { - let report: &TdxQuote = mem::transmute(&tdx_quote); - Ok(report.clone()) - }, - Err(e) => Err(anyhow!("[parse_cc_report] error parse tdx quote: {:?}", e)), - } - } -} - -// API function parses raw cc report to TpmQuote struct -impl ParseCcReport for CcReport { - fn parse_cc_report(_report: Vec) -> Result { - todo!() - } + fn parse_cc_report(report: Vec) -> Result; } diff --git a/common/rust/cctrusted_base/src/api_data.rs b/common/rust/cctrusted_base/src/api_data.rs index 89f2c06d..dafc778b 100644 --- a/common/rust/cctrusted_base/src/api_data.rs +++ b/common/rust/cctrusted_base/src/api_data.rs @@ -38,3 +38,18 @@ pub struct Algorithm { pub algo_id: u8, pub algo_id_str: String, } + +/*** + ******************************************** + * API get_measurement_count() related data * + ******************************************** + */ +// return number of measurement registers in a CVM + +/*** + ******************************************** + * API get_cc_measurement() related data * + ******************************************** + */ +// the return data structure is defined in cctrusted_base as: +// cctrusted_base::tcg::TcgDigest diff --git a/common/rust/cctrusted_base/src/cc_type.rs b/common/rust/cctrusted_base/src/cc_type.rs index 5282e4c6..5ba8f237 100644 --- a/common/rust/cctrusted_base/src/cc_type.rs +++ b/common/rust/cctrusted_base/src/cc_type.rs @@ -1,7 +1,7 @@ use hashbrown::HashMap; // supported TEE types -#[derive(Clone, Eq, Hash, PartialEq)] +#[derive(Clone, Eq, Hash, PartialEq, Debug)] pub enum TeeType { PLAIN = -1, TPM = 0, diff --git a/common/rust/cctrusted_base/src/tcg.rs b/common/rust/cctrusted_base/src/tcg.rs index 047aa765..eba1111b 100644 --- a/common/rust/cctrusted_base/src/tcg.rs +++ b/common/rust/cctrusted_base/src/tcg.rs @@ -1,4 +1,5 @@ use hashbrown::HashMap; +use log::info; pub const TPM_ALG_ERROR: u8 = 0x0; pub const TPM_ALG_RSA: u8 = 0x1; @@ -26,19 +27,46 @@ lazy_static! { // this trait retrieve tcg standard algorithm name in string pub trait TcgAlgorithmRegistry { fn get_algorithm_id(&self) -> u8; + fn get_algorithm_id_str(&self) -> String; } // digest format: (algo id, hash value) -#[allow(dead_code)] +#[derive(Clone)] pub struct TcgDigest { - algo_id: u8, - hash: Vec, + pub algo_id: u8, + pub hash: Vec, } -// this trait retrieve IMR's max index of a CVM and hash value +impl TcgDigest { + pub fn show(&self) { + info!("show data in struct TcgDigest"); + info!( + "algo = {}", + ALGO_NAME_MAP.get(&self.algo_id).unwrap().to_owned() + ); + info!("hash = {:02X?}", self.hash); + } + + pub fn get_hash(&self) -> Vec { + self.hash.clone() + } +} + +impl TcgAlgorithmRegistry for TcgDigest { + fn get_algorithm_id(&self) -> u8 { + self.algo_id + } + + fn get_algorithm_id_str(&self) -> String { + ALGO_NAME_MAP.get(&self.algo_id).unwrap().to_owned() + } +} + +// traits a Tcg IMR should have pub trait TcgIMR { - fn max_index(&self) -> u8; + fn max_index() -> u8; fn get_index(&self) -> u8; - fn get_hash(&self) -> Vec<&str>; - fn is_valid(&self) -> bool; + fn get_tcg_digest(&self, algo_id: u8) -> TcgDigest; + fn is_valid_index(index: u8) -> Result; + fn is_valid_algo(algo_id: u8) -> Result; } diff --git a/common/rust/cctrusted_base/src/tdx/common.rs b/common/rust/cctrusted_base/src/tdx/common.rs index d48f9a43..4a632317 100644 --- a/common/rust/cctrusted_base/src/tdx/common.rs +++ b/common/rust/cctrusted_base/src/tdx/common.rs @@ -35,3 +35,41 @@ lazy_static! { pub const REPORT_DATA_LEN: u32 = 64; pub const TDX_REPORT_LEN: u32 = 1024; pub const TDX_QUOTE_LEN: usize = 4 * 4096; + +#[repr(u16)] +#[derive(Clone, PartialEq, Debug)] +pub enum AttestationKeyType { + ECDSA_P256 = 2, + ECDSA_P384 = 3, +} + +#[repr(u32)] +#[derive(Clone, Debug, PartialEq)] +pub enum IntelTeeType { + TEE_SGX = 0x00000000, + TEE_TDX = 0x00000081, +} + +// QE_VENDOR_INTEL_SGX ID string "939a7233f79c4ca9940a0db3957f0607"; +pub const QE_VENDOR_INTEL_SGX: [u8; 16] = [ + 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07, +]; + +#[derive(Clone, PartialEq, Debug)] +#[repr(i16)] +pub enum QeCertDataType { + /*** QE Certification Data Type. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3.9. QE Certification Data - Version 4 + */ + PCK_ID_PLAIN = 1, + PCK_ID_RSA_2048_OAEP = 2, + PCK_ID_RSA_3072_OAEP = 3, + PCK_LEAF_CERT_PLAIN = 4, // Currently not supported + PCK_CERT_CHAIN = 5, + QE_REPORT_CERT = 6, + PLATFORM_MANIFEST = 7, // Currently not supported +} +pub const TDX_QUOTE_VERSION_4: u16 = 4; +pub const TDX_QUOTE_VERSION_5: u16 = 5; diff --git a/common/rust/cctrusted_base/src/tdx/quote.rs b/common/rust/cctrusted_base/src/tdx/quote.rs index 9f81fc3d..91ed3cb0 100644 --- a/common/rust/cctrusted_base/src/tdx/quote.rs +++ b/common/rust/cctrusted_base/src/tdx/quote.rs @@ -1,7 +1,13 @@ #![allow(non_camel_case_types)] +use anyhow::anyhow; +use core::mem; +use core::mem::transmute; use core::result::Result; use core::result::Result::Ok; +use log::*; +use crate::api::ParseCcReport; +use crate::api_data::CcReport; use crate::tdx::common::*; #[repr(C)] @@ -79,17 +85,498 @@ impl Tdx { } } +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteHeader { + /*** TD Quote Header. + Attributes: + ver: An integer version of the Quote data structure. + ak_type: A ``AttestationKeyType`` indicating the type of the Attestation + Key used by the Quoting Enclave. + tee_type: A ``TeeType`` indicating the TEE for this attestation. + reserved_1: Reserved 2 bytes. + reserved_2: Reserved 2 bytes. + qe_vendor: Bytes indicating the Unique identifier of the QE Vendor. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3.1. TD Quote Header + Size is count in bytes: + Name Size Type Description + Version 2 Integer Version of the Quote data structure. + Value: 4 + Attestation Key Type 2 Integer Type of the Attestation Key used by the + Quoting Enclave. Supported values: + 2 (ECDSA-256-with-P-256 curve) + 3 (ECDSA-384-with-P-384 curve) (Note: + currently not supported) + (Note: 0 and 1 are reserved, for when EPID is + moved to version 4 quotes.) + TEE Type 4 Integer TEE for this Attestation + 0x00000000: SGX + 0x00000081: TDX + RESERVED 2 Byte Array Zero + RESERVED 2 Byte Array Zero + QE Vendor ID 16 UUID Unique identifier of the QE Vendor. + Value: + 939A7233F79C4CA9940A0DB3957F0607 + (Intel® SGX QE Vendor) + Note: Each vendor that decides to provide a + customized Quote data structure should have + unique ID. + User Data 20 Byte Array Custom user-defined data. For the Intel® SGX + and TDX DCAP Quote Generation Libraries, the + first 16 bytes contain a Platform Identifier + that is used to link a PCK Certificate to an + Enc(PPID). This identifier is consistent for + every quote generated with this QE on this + platform + */ + pub version: u16, + pub ak_type: AttestationKeyType, + pub tee_type: IntelTeeType, + pub reserved_1: [u8; 2], + pub reserved_2: [u8; 2], + pub qe_vendor: [u8; 16], + pub user_data: [u8; 20], +} + +impl TdxQuoteHeader { + pub fn show(&self) { + info!("show the data of TdxQuoteHeader"); + info!("version = {}", self.version); + info!("ak_type = {:?}", self.ak_type); + info!("tee_type = {:?}", self.tee_type); + info!("qe_vendor = {:02X?}", self.qe_vendor); + info!("user_data = {:02X?}", self.user_data); + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteBody { + /*** TD Quote Body. + We define TdxQuoteBody as the base class of Version 4 Quote Format and Version 5 Quote Format. + Quote Format Version Architecture Class Usage Comment + 4 TDX 1.0 TdxQuoteBody + 4 TDX 1.5 TdxQuoteBody + 5 TDX 1.0 TODO: should use TdxQuoteBody + 5 TDX 1.5 TODO: should define a sub class with 2 more fields + TEE_TCB_SVN_2 + MRSERVICETD + 5 SGX TODO: should define a new independent class + Atrributes: + data: A bytearray fo the raw data. + tee_tcb_svn: describing the TCB of TDX. + mrseam: A bytearray storing the Measurement of the TDX Module. + mrsignerseam: A bytearray that should be zero for the Intel TDX Module. + seamattributes: A bytearray storing SEAMATRIBUTES. Must be zero for TDX 1.0. + tdattributes: A bytearray indicating TD Attributes. + xfam: A bytearray storing XFAM (eXtended Features Available Mask). + mrtd: A bytearray storing Measurement of the initial contents of the TD. + mrconfig: A bytearray storing software-defined ID for non-owner-defined TD config. + mrowner: A bytearray storing software-defined ID for the TD's owner. + mrownerconfig: A bytearray storing software-defined ID for owner-defined TD config. + rtmr0: A bytearray storing runtime extendable measurement register 0. + rtmr1: A bytearray storing runtime extendable measurement register 1. + rtmr2: A bytearray storing runtime extendable measurement register 2. + rtmr3: A bytearray storing runtime extendable measurement register 3. + reportdata: A bytearray storing 64 bytes custom data to a TD Report. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + For Version 4 Quote Format, the TD Quote Body definition is used for both TDX 1.0 and TDX 1.5. + A.3.2. TD Quote Body + Name Size (bytes) Type Description + TEE_TCB_SVN 16 Byte Array Describes the TCB of TDX. + MRSEAM 48 SHA384 Measurement of the TDX Module. + MRSIGNERSEAM 48 SHA384 Zero for the Intel® TDX Module. + SEAMATTRIBUTES 8 Byte Array Must be zero for TDX 1.0 + TDATTRIBUTES 8 Byte Array TD Attributes + XFAM 8 Byte Array XFAM (eXtended Features Available Mask) is + defined as a 64b bitmap, which has the same + format as XCR0 or IA32_XSS MSR. + MRTD 48 SHA384 Measurement of the initial contents of the TD. + See TDX Module definitions here: TDX Module + documentation + MRCONFIGID 48 Byte Array Software-defined ID for non-owner-defined + configuration of the TD, e.g., runtime or OS + configuration. + MROWNER 48 Byte Array Software-defined ID for the TD's owner + MROWNERCONFIG 48 Byte Array Software-defined ID for owner-defined + configuration of the TD, e.g., specific to the + workload rather than the runtime or OS. + RTMR0 48 SHA384 Runtime extendable measurement register + RTMR1 48 SHA384 Runtime extendable measurement register + RTMR2 48 SHA384 Runtime extendable measurement register + RTMR3 48 SHA384 Runtime extendable measurement register + REPORTDATA 64 Byte Array Each TD Quote is based on a TD Report. The + TD is free to provide 64 bytes of custom data + to a TD Report. For instance, this space can be + used to hold a nonce, a public key, or a hash + of a larger block of data. + Note that the signature of a TD Quote covers + the REPORTDATA field. As a result, the + integrity is protected with a key rooted in an + Intel CA. + For Version 5 Quote Format, the TD Quote Body has 3 types: + A.4.2. TD Quote Body Descriptor + TD Quote Body Type architecturally supported values: + - 1 (Future SGX support) + - 2 (TD Quote Body for TDX 1.0) + - 3 (TD Quote Body for TDX 1.5) + For Version 5 Quote Format TD Quote Body, TDX 1.5 body has 2 more fields than TDX 1.0 in the + trailling bytes: + A.4.4. TD Quote Body for TDX 1.5 + Name Size (bytes) Type Description + TEE_TCB_SVN_2 16 Byte Array Describes the current TCB of TDX. This value may + will be different than TEE_TCB_SVN by loading a + new version of the TDX Module using the TD + Preserving update capability + MRSERVICETD 48 SHA384 Measurement of the initial contents of the + Migration TD + */ + pub tee_tcb_svn: [u8; 16], // Array of TEE TCB SVNs + pub mrseam: [u8; 48], // Measurement of the SEAM module (SHA384 hash) + pub mrseam_signer: [u8; 48], // Measurement of a 3rd party SEAM module’s signer (SHA384 hash) + pub seam_attributes: [u8; 8], // ATTRIBUTES of SEAM + pub td_attributes: [u8; 8], // ATTRIBUTES of TD + pub xfam: [u8; 8], // XFAM of TD + pub mrtd: [u8; 48], // Measurement of the initial contents of the TD (SHA384 hash) + pub mrconfigid: [u8; 48], // Software defined ID for non-owner-defined configuration of the TD + pub mrowner: [u8; 48], // Software defined ID for the guest TD’s owner + pub mrownerconfig: [u8; 48], // Software defined ID for owner-defined configuration of the TD + pub rtmr0: [u8; 48], // data in RTMR0(SHA384 hash) + pub rtmr1: [u8; 48], // data in RTMR1(SHA384 hash) + pub rtmr2: [u8; 48], // data in RTMR2(SHA384 hash) + pub rtmr3: [u8; 48], // data in RTMR3(SHA384 hash) + pub report_data: [u8; 64], // Additional Report Data +} + +impl TdxQuoteBody { + pub fn show(&self) { + info!("show the data of TdxQuoteBody"); + info!("tee_tcb_svn = {:02X?}", self.tee_tcb_svn); + info!("mrseam = {:02X?}", self.mrseam); + info!("mrseam_signer = {:02X?}", self.mrseam_signer); + info!("seam_attributes = {:02X?}", self.seam_attributes); + info!("td_attributes = {:02X?}", self.td_attributes); + info!("xfam = {:02X?}", self.xfam); + info!("mrtd = {:02X?}", self.mrtd); + info!("mrconfigid = {:02X?}", self.mrconfigid); + info!("mrowner = {:02X?}", self.mrowner); + info!("mrownerconfig = {:02X?}", self.mrownerconfig); + info!("rtmr0 = {:02X?}", self.rtmr0); + info!("rtmr1 = {:02X?}", self.rtmr1); + info!("rtmr2 = {:02X?}", self.rtmr2); + info!("rtmr3 = {:02X?}", self.rtmr3); + info!("report_data = {:02X?}", self.report_data); + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxEnclaveReportBody { + pub cpu_svn: [u8; 16], + pub miscselect: [u8; 4], + pub reserved_1: [u8; 28], + pub attributes: [u8; 16], + pub mrenclave: [u8; 32], + pub reserved_2: [u8; 32], + pub mrsigner: [u8; 32], + pub reserved_3: [u8; 96], + pub isv_prodid: i16, + pub isv_svn: i16, + pub reserved_4: [u8; 60], + pub report_data: [u8; 64], +} + +impl TdxEnclaveReportBody { + pub fn show(&self) { + info!("show the data of TdxEnclaveReportBody"); + info!("cpu_svn = {:02X?}", self.cpu_svn); + info!("miscselect = {:02X?}", self.miscselect); + info!("attributes = {:02X?}", self.attributes); + info!("mrenclave = {:02X?}", self.mrenclave); + info!("mrsigner = {:02X?}", self.mrsigner); + info!("isv_prodid = {:02X?}", self.isv_prodid); + info!("isv_svn = {:02X?}", self.isv_svn); + info!("report_data = {:02X?}", self.report_data); + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteQeReportCert { + /*** TD Quote QE Report Certification Data. + Atrributes: + qe_report: A bytearray storing the SGX Report of the Quoting Enclave that + generated an Attestation Key. + qe_report_sig: A bytearray storing ECDSA signature over the QE Report + calculated using the Provisioning Certification Key (PCK). + qe_auth_cert: A bytearray storing the QE Authentication Data and QE + Certification Data. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3.11. QE Report Certification Data + */ + pub qe_report: TdxEnclaveReportBody, + pub qe_report_sig: [u8; 64], + pub qe_auth_data: Vec, + pub qe_auth_cert: Box, +} + +impl TdxQuoteQeReportCert { + pub fn new(data: Vec) -> TdxQuoteQeReportCert { + let tdx_enclave_report_body: TdxEnclaveReportBody = unsafe { + transmute::<[u8; 384], TdxEnclaveReportBody>( + data[0..384] + .try_into() + .expect("slice with incorrect length"), + ) + }; + let qe_report_sig = data[384..448].try_into().unwrap(); + let auth_data_size = unsafe { + transmute::<[u8; 2], u16>( + data[448..450] + .try_into() + .expect("slice with incorrect length"), + ) + } + .to_le(); + let auth_data_end = 450 + auth_data_size; + let mut qe_auth_data = Vec::new(); + if auth_data_size > 0 { + qe_auth_data = data[450..auth_data_end as usize].to_vec(); + } + + TdxQuoteQeReportCert { + qe_report: tdx_enclave_report_body, + qe_report_sig, + qe_auth_data, + qe_auth_cert: Box::new(TdxQuoteQeCert::new(data[auth_data_end as usize..].to_vec())), + } + } + + pub fn show(&self) { + info!("show the data of TdxQuoteQeReportCert"); + self.qe_report.show(); + info!("qe_report_sig = {:02X?}", self.qe_report_sig); + info!("qe_auth_data = {:02X?}", self.qe_auth_data); + self.qe_auth_cert.show(); + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteQeCert { + /*** TD Quote QE Certification Data. + Attributes: + cert_type: A ``QeCertDataType`` determining the type of data required to verify the + QE Report Signature in the Quote Signature Data structure. + cert_data: A ``TdxQuoteQeReportCert`` storing the data required to verify the QE + Report Signature depending on the value of the Certification Data Type. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3.9. QE Certification Data - Version 4 + */ + pub cert_type: QeCertDataType, + pub cert_data_struct: Option>, + pub cert_data_vec: Option>, +} + +impl TdxQuoteQeCert { + pub fn new(data: Vec) -> TdxQuoteQeCert { + let cert_type: QeCertDataType = unsafe { + transmute::<[u8; 2], QeCertDataType>( + data[0..2].try_into().expect("slice with incorrect length"), + ) + }; + let cert_size = unsafe { + transmute::<[u8; 4], u32>(data[2..6].try_into().expect("slice with incorrect length")) + } + .to_le(); + let cert_data_end = 6 + cert_size; + + if cert_type == QeCertDataType::QE_REPORT_CERT { + let cert_data = TdxQuoteQeReportCert::new(data[6..cert_data_end as usize].to_vec()); + TdxQuoteQeCert { + cert_type: cert_type as QeCertDataType, + cert_data_struct: Some(Box::new(cert_data)), + cert_data_vec: None, + } + } else { + let cert_data = data[6..cert_data_end as usize].to_vec(); + TdxQuoteQeCert { + cert_type: cert_type as QeCertDataType, + cert_data_struct: None, + cert_data_vec: Some(cert_data), + } + } + } + + pub fn show(&self) { + info!("show the data of TdxQuoteQeCert"); + info!("cert_type = {:?}", self.cert_type); + match &self.cert_data_struct { + None => match &self.cert_data_vec { + None => (), + Some(cert_data_vec) => info!("cert_data_vec = {:2X?}", cert_data_vec), + }, + Some(cert_data_struct) => cert_data_struct.show(), + } + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteEcdsa256Sigature { + /*** TD Quote ECDSA 256-bit Quote Signature. + Atrributes: + sig: A bytearray storing ECDSA signature over the Header and the TD + Quote Body calculated using the private part of the + Attestation Key generated by the Quoting Enclave. + ak: A bytearray storing Public part of the Attestation Key generated + by the Quoting Enclave. + qe_cert: A ``TdxQuoteQeCert`` storing the data required to verify + the signature over QE Report and the Attestation Key. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3.8 + */ + pub sig: [u8; 64], + pub ak: [u8; 64], + pub qe_cert: TdxQuoteQeCert, +} + +impl TdxQuoteEcdsa256Sigature { + pub fn new(data: Vec) -> TdxQuoteEcdsa256Sigature { + let sig = data[0..64].try_into().unwrap(); + let ak = data[64..128].try_into().unwrap(); + let qe_cert = TdxQuoteQeCert::new(data[128..data.len()].to_vec()); + + TdxQuoteEcdsa256Sigature { sig, ak, qe_cert } + } + + pub fn show(&self) { + info!("show the data of TdxQuoteEcdsa256Sigature"); + info!("sig = {:02X?}", self.sig); + info!("ak = {:02X?}", self.ak); + self.qe_cert.show(); + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TdxQuoteSignature { + pub data: Vec, +} + #[derive(Clone)] pub struct TdxQuote { - pub dummy_var1: u8, - pub dummy_var2: u8, + /*** TDX Quote. + Atrributes: + header: A ``TdxQuoteHeader`` storing the data of Quote Header. + body: A ``TdxQuoteBody`` storing the data of TD Quote body. + sig: Quote Signature. Currently only support ``TdxQuoteEcdsa256Sigature``. + Definition reference: + https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf + A.3. Version 4 Quote Format (TDX-ECDSA, SGX-ECDSA, and SGX-EPID) + Endianess: Little Endian (applies to all integer fields). Size in bytes: + Name Size Type Description + Quote Header 48 TD Quote Header Header of Quote data structure. + This field is transparent, i.e., the user knows its + internal structure. + Rest of the Quote data structure can be treated as + opaque, i.e., hidden from the user. + TD Quote Body 584 TD Quote Body Report of the attested TD. + The REPORTDATA contained in this field is defined + by the TD developer. See the description of the + field for example usages. + Quote Signature 4 Integer Size of the Quote Signature Data structure + Data Len + Quote Signature Variable Signature Variable-length data containing the signature and + Data Dependent supporting data. For instance, an ECDSA P-256 + Signature + For Version 5 + TODO: implement version 5 according to A.4. Version 5 Quote Format. + */ + pub header: TdxQuoteHeader, + pub body: TdxQuoteBody, + pub tdx_quote_ecdsa256_sigature: Option, // for AttestationKeyType.ECDSA_P256 + pub tdx_quote_signature: Option, // for AttestationKeyType.ECDSA_P384 } impl TdxQuote { - pub fn parse_tdx_quote(_quote: Vec) -> Result { - Ok(TdxQuote { - dummy_var1: 1, - dummy_var2: 2, - }) + pub fn parse_tdx_quote(quote: Vec) -> Result { + let tdx_quote_header: TdxQuoteHeader = unsafe { + transmute::<[u8; 48], TdxQuoteHeader>( + quote[0..48] + .try_into() + .expect("slice with incorrect length"), + ) + }; + if tdx_quote_header.version == TDX_QUOTE_VERSION_4 { + let tdx_quote_body: TdxQuoteBody = unsafe { + transmute::<[u8; 584], TdxQuoteBody>( + quote[48..632] + .try_into() + .expect("slice with incorrect length"), + ) + }; + let sig_len = unsafe { + transmute::<[u8; 4], i32>( + quote[632..636] + .try_into() + .expect("slice with incorrect length"), + ) + } + .to_le(); + let sig_idx_end = 636 + sig_len; + + if tdx_quote_header.ak_type == AttestationKeyType::ECDSA_P256 { + let tdx_quote_ecdsa256_sigature = + TdxQuoteEcdsa256Sigature::new(quote[636..sig_idx_end as usize].to_vec()); + + Ok(TdxQuote { + header: tdx_quote_header, + body: tdx_quote_body, + tdx_quote_signature: None, + tdx_quote_ecdsa256_sigature: Some(tdx_quote_ecdsa256_sigature), + }) + } else if tdx_quote_header.ak_type == AttestationKeyType::ECDSA_P384 { + let tdx_quote_signature = TdxQuoteSignature { + data: quote[636..sig_idx_end as usize].to_vec(), + }; + + Ok(TdxQuote { + header: tdx_quote_header, + body: tdx_quote_body, + tdx_quote_signature: Some(tdx_quote_signature), + tdx_quote_ecdsa256_sigature: None, + }) + } else { + return Err(anyhow!("[parse_tdx_quote] unknown ak_type!")); + } + } else if tdx_quote_header.version == TDX_QUOTE_VERSION_5 { + // TODO: implement version 5 + todo!() + } else { + return Err(anyhow!( + "[parse_tdx_quote] unknown quote header version: {:}", + tdx_quote_header.version + )); + } + } +} + +// API function parses raw cc report to TdxQuote struct +impl ParseCcReport for CcReport { + fn parse_cc_report(report: Vec) -> Result { + match TdxQuote::parse_tdx_quote(report) { + Ok(tdx_quote) => unsafe { + let report: &TdxQuote = mem::transmute(&tdx_quote); + Ok(report.clone()) + }, + Err(e) => Err(anyhow!("[parse_cc_report] error parse tdx quote: {:?}", e)), + } } } diff --git a/common/rust/cctrusted_base/src/tdx/report.rs b/common/rust/cctrusted_base/src/tdx/report.rs index 902c24f6..283589d2 100644 --- a/common/rust/cctrusted_base/src/tdx/report.rs +++ b/common/rust/cctrusted_base/src/tdx/report.rs @@ -1,6 +1,7 @@ #![allow(non_camel_case_types)] use crate::tdx::common::*; use anyhow::*; +use core::mem::transmute; use core::result::Result; use core::result::Result::Ok; use sha2::{Digest, Sha512}; @@ -20,9 +21,184 @@ pub struct tdx_1_5_report_req { pub tdreport: [u8; TDX_REPORT_LEN as usize], // User buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT] } +/*** + Struct REPORTMACSTRUCT's layout: + offset, len + 0x0, 0x8 report_type + 0x8, 0x8 reserverd1 + 0x10, 0x10 cpusvn + 0x20, 0x30 tee_tcb_info_hash + 0x50, 0x30 tee_info_hash + 0x80, 0x40 report_data + 0xc0, 0x20 reserverd2 + 0xe0, 0x20 mac +*/ +#[repr(C)] +#[derive(Clone)] +pub struct ReportMacStruct { + pub report_type: [u8; 8], + pub reserverd1: [u8; 8], + pub cpusvn: [u8; 16], + pub tee_tcb_info_hash: [u8; 48], + pub tee_info_hash: [u8; 48], + pub report_data: [u8; 64], + pub reserverd2: [u8; 32], + pub mac: [u8; 32], +} + +/*** + Struct TEE_TCB_INFO's layout: + offset, len + 0x0, 0x08 valid + 0x8, 0x10 tee_tcb_svn + 0x18, 0x30 mrseam + 0x48, 0x30 mrsignerseam + 0x78, 0x08 attributes + # fileds in tdx v1.0 + 0x80, 0x6f reserved + # fileds in tdx v1.5 + 0x80, 0x10 tee_tcb_svn2 + 0x90, 0x5f reserved +*/ +#[repr(C)] +#[derive(Clone)] +pub struct TeeTcbInfo { + pub valid: [u8; 8], + pub tee_tcb_svn: [u8; 16], + pub mrseam: [u8; 48], + pub mrsignerseam: [u8; 48], + pub attributes: [u8; 8], + pub tee_tcb_svn2: Option<[u8; 16]>, + pub reserved: Vec, +} + +impl TeeTcbInfo { + pub fn new(data: Vec, tdx_version: TdxVersion) -> TeeTcbInfo { + let valid = data[0..8].try_into().unwrap(); + let tee_tcb_svn = data[8..24].try_into().unwrap(); + let mrseam = data[24..72].try_into().unwrap(); + let mrsignerseam = data[72..120].try_into().unwrap(); + let attributes = data[120..128].try_into().unwrap(); + + if tdx_version == TdxVersion::TDX_1_0 { + let reserved = data[128..].try_into().unwrap(); + TeeTcbInfo { + valid, + tee_tcb_svn, + mrseam, + mrsignerseam, + attributes, + tee_tcb_svn2: None, + reserved, + } + } else { + // TDX 1.5 + let reserved = data[144..].try_into().unwrap(); + TeeTcbInfo { + valid, + tee_tcb_svn, + mrseam, + mrsignerseam, + attributes, + tee_tcb_svn2: Some(data[128..144].try_into().unwrap()), + reserved, + } + } + } +} + +/*** + Struct TDINFO_STRUCT's layout: + offset, len + 0x0, 0x8 attributes + 0x8, 0x8 xfam + 0x10, 0x30 mrtd + 0x40, 0x30 mrconfigid + 0x70, 0x30 mrowner + 0xa0, 0x30 mrownerconfig + 0xd0, 0x30 rtmr_0 + 0x100, 0x30 rtmr_1 + 0x130, 0x30 rtmr_2 + 0x160, 0x30 rtmr_3 + # fields in tdx v1.0 + 0x190, 0x70 reserved + # fields in tdx v1.5 + 0x190, 0x30 servtd_hash + 0x1c0, 0x40 reserved + ref: + Page 40 of Intel® TDX Module v1.5 ABI Specification + from https://www.intel.com/content/www/us/en/developer/articles/technical/ + intel-trust-domain-extensions.html +*/ +#[repr(C)] +#[derive(Clone)] +pub struct TdInfo { + pub attributes: [u8; 8], + pub xfam: [u8; 8], + pub mrtd: [u8; 48], + pub mrconfigid: [u8; 48], + pub mrowner: [u8; 48], + pub mrownerconfig: [u8; 48], + pub rtmrs: Vec<[u8; 48]>, + pub servtd_hash: Option<[u8; 48]>, + pub reserved: Vec, +} + +impl TdInfo { + pub fn new(data: Vec, tdx_version: TdxVersion) -> TdInfo { + let attributes = data[0..8].try_into().unwrap(); + let xfam = data[8..16].try_into().unwrap(); + let mrtd = data[16..64].try_into().unwrap(); + let mrconfigid = data[64..112].try_into().unwrap(); + let mrowner = data[112..160].try_into().unwrap(); + let mrownerconfig = data[160..208].try_into().unwrap(); + let mut rtmrs = Vec::new(); + rtmrs.push(data[208..256].try_into().unwrap()); + rtmrs.push(data[256..304].try_into().unwrap()); + rtmrs.push(data[304..352].try_into().unwrap()); + rtmrs.push(data[352..400].try_into().unwrap()); + + if tdx_version == TdxVersion::TDX_1_0 { + TdInfo { + attributes, + xfam, + mrtd, + mrconfigid, + mrowner, + mrownerconfig, + rtmrs, + servtd_hash: None, + reserved: data[400..].try_into().unwrap(), + } + } else { + // TDX 1.5 + TdInfo { + attributes, + xfam, + mrtd, + mrconfigid, + mrowner, + mrownerconfig, + rtmrs, + servtd_hash: Some(data[400..448].try_into().unwrap()), + reserved: data[448..].try_into().unwrap(), + } + } + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct TDReport { + pub report_mac_struct: ReportMacStruct, + pub tee_tcb_info: TeeTcbInfo, + pub reserved: [u8; 17], + pub td_info: TdInfo, +} + impl Tdx { /*** - generate tdx report data with nonce and data + generate tdx data with nonce and data Args: nonce (String): against replay attacks @@ -32,21 +208,32 @@ impl Tdx { The tdreport byte array */ pub fn generate_tdx_report_data( - nonce: String, + nonce: Option, data: Option, ) -> Result { - let nonce_decoded = match base64::decode(nonce) { - Ok(v) => v, - Err(e) => { - return Err(anyhow!( - "[generate_tdx_report_data] nonce is not base64 encoded: {:?}", - e - )) + let mut hasher = Sha512::new(); + + match nonce { + Some(_encoded_nonce) => { + if _encoded_nonce.is_empty() { + hasher.update("") + } else { + let decoded_nonce = match base64::decode(_encoded_nonce) { + Ok(v) => v, + Err(e) => { + return Err(anyhow!( + "[generate_tdx_report_data] nonce is not base64 encoded: {:?}", + e + )) + } + }; + hasher.update(decoded_nonce) + } } + None => hasher.update(""), }; - let mut hasher = Sha512::new(); - hasher.update(nonce_decoded); - let _ret = match data { + + match data { Some(_encoded_data) => { if _encoded_data.is_empty() { hasher.update("") @@ -69,7 +256,152 @@ impl Tdx { .finalize() .as_slice() .try_into() - .expect("[generate_tdx_report_data] Wrong length of report data"); + .expect("[generate_tdx_report_data] Wrong length of data"); Ok(base64::encode(hash_array)) } + + pub fn parse_td_report( + report: &Vec, + tdx_version: TdxVersion, + ) -> Result { + let report_mac_struct = unsafe { + transmute::<[u8; 256], ReportMacStruct>( + report[0..256] + .try_into() + .expect("slice with incorrect length"), + ) + }; + let tee_tcb_info = TeeTcbInfo::new(report[256..495].to_vec(), tdx_version.clone()); + let reserved = report[495..512].try_into().unwrap(); + let td_info = TdInfo::new(report[512..1024].to_vec(), tdx_version.clone()); + Ok(TDReport { + report_mac_struct, + tee_tcb_info, + reserved, + td_info, + }) + } +} + +#[cfg(test)] +mod test_generate_tdx_report_data { + use super::*; + use crate::tdx::common::Tdx; + + #[test] + //generate_tdx_report allow optional nonce + fn test_generate_tdx_report_data_no_nonce() { + let result = Tdx::generate_tdx_report_data(None, Some("YWJjZGVmZw==".to_string())); + assert!(result.is_ok()); + } + + #[test] + //generate_tdx_report allow optional data + fn tdx_get_quote_report_data_no_data() { + let result = Tdx::generate_tdx_report_data(Some("IXUKoBO1XEFBPwopN4sY".to_string()), None); + assert!(result.is_ok()); + } + + #[test] + //generate_tdx_report allow empty data + fn test_generate_tdx_report_data_data_size_0() { + let result = Tdx::generate_tdx_report_data( + Some("IXUKoBO1XEFBPwopN4sY".to_string()), + Some("".to_string()), + ); + assert!(result.is_ok()); + } + + #[test] + //generate_tdx_report require data string is base64 encoded + fn test_generate_tdx_report_data_data_not_base64_encoded() { + //coming in data should always be base64 encoded + let result = Tdx::generate_tdx_report_data( + Some("IXUKoBO1XEFBPwopN4sY".to_string()), + Some("XD^%*!x".to_string()), + ); + assert!(result.is_err()); + } + + #[test] + //generate_tdx_report require nonce string is base64 encoded + fn test_generate_tdx_report_data_nonce_not_base64_encoded() { + //coming in nonce should always be base64 encoded + let result = Tdx::generate_tdx_report_data( + Some("XD^%*!x".to_string()), + Some("IXUKoBO1XEFBPwopN4sY".to_string()), + ); + assert!(result.is_err()); + } + + #[test] + //generate_tdx_report check result as expected + //orginal nonce = "12345678", original data = "abcdefgh" + fn test_generate_tdx_report_data_report_data_nonce_base64_encoded_as_expected() { + let result = Tdx::generate_tdx_report_data( + Some("MTIzNDU2Nzg=".to_string()), + Some("YWJjZGVmZw==".to_string()), + ) + .unwrap(); + let expected_hash = [ + 93, 71, 28, 83, 115, 189, 166, 130, 87, 137, 126, 119, 140, 209, 163, 215, 13, 175, + 225, 101, 64, 195, 196, 202, 15, 37, 166, 241, 141, 49, 128, 157, 164, 132, 67, 50, 9, + 32, 162, 89, 243, 191, 177, 131, 4, 159, 156, 104, 11, 193, 18, 217, 92, 215, 194, 98, + 145, 191, 211, 85, 187, 118, 39, 80, + ]; + let generated_hash = base64::decode(result).unwrap(); + assert_eq!(generated_hash, expected_hash); + } + + #[test] + //generate_tdx_report allow long data string + fn test_generate_tdx_report_data_long_tdx_data() { + let result = Tdx::generate_tdx_report_data( + Some("IXUKoBO1XEFBPwopN4sY".to_string()), + Some( + "MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2Nzgx\ + MjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEy\ + MzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIz\ + NDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0\ + NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1\ + Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2\ + NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4Cg==" + .to_string(), + ), + ); + assert!(result.is_ok()); + } + + #[test] + //generate_tdx_report allow long nonce string + fn test_generate_tdx_report_data_long_nonce() { + let result = Tdx::generate_tdx_report_data( + Some( + "MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2Nzgx\ + MjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEy\ + MzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIz\ + NDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0\ + NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1\ + Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2\ + NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4Cg==" + .to_string(), + ), + Some("MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4".to_string()), + ); + assert!(result.is_ok()); + } + + #[test] + //generate_tdx_report_data generated data is 64 bytes + fn test_generate_tdx_report_data_report_data_is_64_bytes() { + let report_data_hashed = match Tdx::generate_tdx_report_data( + Some("MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4".to_string()), + Some("IXUKoBO1XEFBPwopN4sY".to_string()), + ) { + Ok(r) => r, + Err(_) => todo!(), + }; + let generated_hash_len = base64::decode(report_data_hashed).unwrap().len(); + assert_eq!(generated_hash_len, 64); + } } diff --git a/common/rust/cctrusted_base/src/tdx/rtmr.rs b/common/rust/cctrusted_base/src/tdx/rtmr.rs index ffb5d918..fac82c58 100644 --- a/common/rust/cctrusted_base/src/tdx/rtmr.rs +++ b/common/rust/cctrusted_base/src/tdx/rtmr.rs @@ -1,26 +1,61 @@ use crate::tcg::*; -use hashbrown::HashMap; +use anyhow::anyhow; -#[allow(dead_code)] pub struct TdxRTMR { index: u8, - digests: HashMap, + digest: (u8, TcgDigest), +} + +impl TdxRTMR { + pub fn new(index: u8, algo_id: u8, digest: [u8; 48]) -> Result { + match TdxRTMR::is_valid_index(index) { + Ok(_) => (), + Err(e) => return Err(anyhow!("error creating TdxRTMR {:?}", e)), + }; + + match TdxRTMR::is_valid_algo(algo_id) { + Ok(_) => (), + Err(e) => return Err(anyhow!("error creating TdxRTMR {:?}", e)), + }; + + let tcg_digest = TcgDigest { + algo_id, + hash: digest.to_vec(), + }; + + Ok(TdxRTMR { + index, + digest: (algo_id, tcg_digest), + }) + } } impl TcgIMR for TdxRTMR { - fn max_index(&self) -> u8 { + fn max_index() -> u8 { 3 } fn get_index(&self) -> u8 { - todo!() + self.index } - fn get_hash(&self) -> Vec<&str> { - todo!() + fn get_tcg_digest(&self, _algo_id: u8) -> TcgDigest { + self.digest.1.clone() } - fn is_valid(&self) -> bool { - todo!() + fn is_valid_index(index: u8) -> Result { + if index > TdxRTMR::max_index() { + return Err(anyhow!("[is_valid_index] invalid RTMR index: {}", index)); + } + + Ok(true) + } + + fn is_valid_algo(algo_id: u8) -> Result { + if algo_id != TPM_ALG_SHA384 { + return Err(anyhow!("[is_valid_algo] invalid algo id: {}", algo_id)); + } + + Ok(true) } } diff --git a/common/rust/cctrusted_base/src/tpm/quote.rs b/common/rust/cctrusted_base/src/tpm/quote.rs index 1ef6edf1..fa720625 100644 --- a/common/rust/cctrusted_base/src/tpm/quote.rs +++ b/common/rust/cctrusted_base/src/tpm/quote.rs @@ -1,3 +1,6 @@ +use crate::api::ParseCcReport; +use crate::api_data::CcReport; + // return of API parse_cc_report() pub struct TpmQuote {} @@ -6,3 +9,10 @@ impl TpmQuote { todo!() } } + +// API function parses raw cc report to TpmQuote struct +impl ParseCcReport for CcReport { + fn parse_cc_report(_report: Vec) -> Result { + todo!() + } +} diff --git a/vmsdk/rust/cctrusted_vm/Cargo.toml b/vmsdk/rust/cctrusted_vm/Cargo.toml index 35a0aa7a..2106cfe1 100644 --- a/vmsdk/rust/cctrusted_vm/Cargo.toml +++ b/vmsdk/rust/cctrusted_vm/Cargo.toml @@ -14,3 +14,4 @@ anyhow = "1.0" log = "0.4.20" nix = "0.26.2" base64 = "0.13.0" +rand = "0.8.5" diff --git a/vmsdk/rust/cctrusted_vm/src/cvm.rs b/vmsdk/rust/cctrusted_vm/src/cvm.rs index 120f6500..da0f6b58 100644 --- a/vmsdk/rust/cctrusted_vm/src/cvm.rs +++ b/vmsdk/rust/cctrusted_vm/src/cvm.rs @@ -16,7 +16,22 @@ pub trait CVM { Returns: the cc report byte array or error information */ - fn process_cc_report(&mut self, nonce: String, data: String) -> Result, anyhow::Error>; + fn process_cc_report( + &mut self, + nonce: Option, + data: Option, + ) -> Result, anyhow::Error>; + + /*** + retrive CVM max number of measurement registers + + Args: + None + + Returns: + max index of register of CVM + */ + fn get_max_index(&self) -> u8; /*** retrive CVM measurement registers, e.g.: RTMRs, vTPM PCRs, etc. @@ -28,7 +43,7 @@ pub trait CVM { Returns: TcgDigest struct */ - fn process_cc_measurement(&self, _index: u8, _algo_id: u8) -> TcgDigest; + fn process_cc_measurement(&self, index: u8, algo_id: u8) -> Result; /*** retrive CVM eventlogs diff --git a/vmsdk/rust/cctrusted_vm/src/sdk.rs b/vmsdk/rust/cctrusted_vm/src/sdk.rs index 7a0692a5..fc8cf3b4 100644 --- a/vmsdk/rust/cctrusted_vm/src/sdk.rs +++ b/vmsdk/rust/cctrusted_vm/src/sdk.rs @@ -15,8 +15,8 @@ pub struct API {} impl CCTrustedApi for API { // CCTrustedApi trait function: get report of a CVM fn get_cc_report( - nonce: String, - data: String, + nonce: Option, + data: Option, _extra_args: ExtraArgs, ) -> Result { match build_cvm() { @@ -42,9 +42,20 @@ impl CCTrustedApi for API { dump_data(report) } + // CCTrustedApi trait function: get max number of CVM IMRs + fn get_measurement_count() -> Result { + match build_cvm() { + Ok(cvm) => Ok(cvm.get_max_index() + 1), + Err(e) => Err(anyhow!("[get_measurement_count] error create cvm: {:?}", e)), + } + } + // CCTrustedApi trait function: get measurements of a CVM - fn get_cc_measurement(_index: u8, _algo_id: u8) -> TcgDigest { - todo!() + fn get_cc_measurement(index: u8, algo_id: u8) -> Result { + match build_cvm() { + Ok(cvm) => cvm.process_cc_measurement(index, algo_id), + Err(e) => Err(anyhow!("[get_cc_measurement] error create cvm: {:?}", e)), + } } // CCTrustedApi trait function: get eventlogs of a CVM @@ -70,3 +81,293 @@ impl CCTrustedApi for API { } } } + +#[cfg(test)] +mod sdk_api_tests { + use super::*; + use crate::cvm::get_cvm_type; + use cctrusted_base::cc_type::TeeType; + use cctrusted_base::tcg::{TPM_ALG_SHA256, TPM_ALG_SHA384}; + use cctrusted_base::tdx::common::{ + AttestationKeyType, IntelTeeType, QeCertDataType, Tdx, QE_VENDOR_INTEL_SGX, + }; + use cctrusted_base::tdx::quote::TdxQuote; + use rand::Rng; + + // test on cc trusted API [get_cc_report] + #[test] + fn test_get_cc_report() { + let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + let data = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + + match Tdx::generate_tdx_report_data(Some(nonce.clone()), Some(data.clone())) { + Ok(r) => r, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + let report = match API::get_cc_report(Some(nonce.clone()), Some(data.clone()), ExtraArgs {}) + { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + assert_ne!(report.cc_report.len(), 0); + + let expected_cvm_type = get_cvm_type().tee_type; + assert_eq!(report.cc_type, expected_cvm_type); + } + + #[test] + fn test_get_cc_report_without_data() { + let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + + let expected_report_data = match Tdx::generate_tdx_report_data(Some(nonce.clone()), None) { + Ok(r) => r, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + let report = match API::get_cc_report(Some(nonce.clone()), None, ExtraArgs {}) { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if report.cc_type == TeeType::TDX { + let tdx_quote: TdxQuote = match CcReport::parse_cc_report(report.cc_report) { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + assert_eq!( + base64::encode(&tdx_quote.body.report_data), + expected_report_data + ); + } + } + + #[test] + fn test_get_cc_report_without_nonce_and_data() { + let expected_report_data = match Tdx::generate_tdx_report_data(None, None) { + Ok(r) => r, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + let report = match API::get_cc_report(None, None, ExtraArgs {}) { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if report.cc_type == TeeType::TDX { + let tdx_quote: TdxQuote = match CcReport::parse_cc_report(report.cc_report) { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + assert_eq!( + base64::encode(&tdx_quote.body.report_data), + expected_report_data + ); + } + } + + #[test] + fn test_get_cc_report_nonce_not_base64_encoded() { + let nonce = "XD^%*!x".to_string(); + match API::get_cc_report(Some(nonce), None, ExtraArgs {}) { + Ok(q) => q, + Err(e) => { + assert_eq!( + true, + format!("{:?}", e).contains("nonce is not base64 encoded") + ); + return; + } + }; + } + + #[test] + fn test_get_cc_report_data_not_base64_encoded() { + let data = "XD^%*!x".to_string(); + match API::get_cc_report(None, Some(data), ExtraArgs {}) { + Ok(q) => q, + Err(e) => { + assert_eq!( + true, + format!("{:?}", e).contains("data is not base64 encoded") + ); + return; + } + }; + } + + // test on cc trusted API [get_default_algorithm] + #[test] + fn test_get_default_algorithm() { + let defalt_algo = match API::get_default_algorithm() { + Ok(algorithm) => algorithm, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if get_cvm_type().tee_type == TeeType::TDX { + assert_eq!(defalt_algo.algo_id, TPM_ALG_SHA384); + } + } + + // test on cc trusted API [get_measurement_count] + #[test] + fn test_get_measurement_count() { + let count = match API::get_measurement_count() { + Ok(count) => count, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if get_cvm_type().tee_type == TeeType::TDX { + assert_eq!(count, 4); + } + } + + // test on cc trusted API [get_cc_measurement] + #[test] + fn test_get_cc_measurement() { + let count = match API::get_measurement_count() { + Ok(count) => count, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if get_cvm_type().tee_type == TeeType::TDX { + for index in 0..count { + let tcg_digest = match API::get_cc_measurement(index, TPM_ALG_SHA384) { + Ok(tcg_digest) => tcg_digest, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + assert_eq!(tcg_digest.algo_id, TPM_ALG_SHA384); + assert_eq!(tcg_digest.hash.len(), 48); + } + } + } + + #[test] + fn test_get_cc_measurement_with_wrong_algo_id() { + let count = match API::get_measurement_count() { + Ok(count) => count, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if get_cvm_type().tee_type == TeeType::TDX { + for index in 0..count { + match API::get_cc_measurement(index, TPM_ALG_SHA256) { + Ok(tcg_digest) => tcg_digest, + Err(e) => { + assert_eq!(true, format!("{:?}", e).contains("invalid algo id")); + return; + } + }; + } + } + } + + // test on cc trusted API [parse_cc_report] + #[test] + fn test_parse_cc_report() { + let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + let data = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + + let expected_report_data = + match Tdx::generate_tdx_report_data(Some(nonce.clone()), Some(data.clone())) { + Ok(r) => r, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + let report = match API::get_cc_report(Some(nonce.clone()), Some(data.clone()), ExtraArgs {}) + { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + if report.cc_type == TeeType::TDX { + let tdx_quote: TdxQuote = match CcReport::parse_cc_report(report.cc_report) { + Ok(q) => q, + Err(e) => { + assert_eq!(true, format!("{:?}", e).is_empty()); + return; + } + }; + + assert_eq!(tdx_quote.header.version, 4); + assert_eq!(tdx_quote.header.tee_type, IntelTeeType::TEE_TDX); + assert_eq!(tdx_quote.header.qe_vendor, QE_VENDOR_INTEL_SGX); + assert_eq!( + base64::encode(&tdx_quote.body.report_data), + expected_report_data + ); + + if tdx_quote.header.ak_type == AttestationKeyType::ECDSA_P256 { + match tdx_quote.tdx_quote_ecdsa256_sigature { + Some(tdx_quote_ecdsa256_sigature) => { + if tdx_quote_ecdsa256_sigature.qe_cert.cert_type + == QeCertDataType::QE_REPORT_CERT + { + match tdx_quote_ecdsa256_sigature.qe_cert.cert_data_struct { + Some(_) => (), + None => assert!(false, "cert_data_struct is None"), + } + } + } + None => assert!(false, "tdx_quote_ecdsa256_sigature is None"), + } + } else if tdx_quote.header.ak_type == AttestationKeyType::ECDSA_P384 { + match tdx_quote.tdx_quote_signature { + Some(_) => (), + None => assert!(false, "tdx_quote_signature is None"), + } + } else { + assert!(false, "unknown ak type"); + } + } + } +} diff --git a/vmsdk/rust/cctrusted_vm/src/tdvm.rs b/vmsdk/rust/cctrusted_vm/src/tdvm.rs index 3cdfef42..4aac3566 100644 --- a/vmsdk/rust/cctrusted_vm/src/tdvm.rs +++ b/vmsdk/rust/cctrusted_vm/src/tdvm.rs @@ -3,10 +3,11 @@ use crate::cvm::*; use anyhow::*; use cctrusted_base::cc_type::*; -use cctrusted_base::tcg::{TcgAlgorithmRegistry, TcgDigest}; +use cctrusted_base::tcg::*; use cctrusted_base::tdx::common::*; use cctrusted_base::tdx::quote::*; use cctrusted_base::tdx::report::*; +use cctrusted_base::tdx::rtmr::TdxRTMR; use core::convert::TryInto; use core::mem; use core::ptr; @@ -63,8 +64,12 @@ impl TdxVM { } // TdxVM struct method: get tdreport - pub fn get_td_report(&self, nonce: String, data: String) -> Result, anyhow::Error> { - let report_data = match Tdx::generate_tdx_report_data(nonce, Some(data)) { + fn get_td_report( + &self, + nonce: Option, + data: Option, + ) -> Result, anyhow::Error> { + let report_data = match Tdx::generate_tdx_report_data(nonce, data) { Ok(r) => r, Err(e) => { return Err(anyhow!( @@ -188,7 +193,11 @@ impl TdxVM { // TdxVM implements the interfaces defined in CVM trait impl CVM for TdxVM { // CVM trait function: get tdx quote - fn process_cc_report(&mut self, nonce: String, data: String) -> Result, anyhow::Error> { + fn process_cc_report( + &mut self, + nonce: Option, + data: Option, + ) -> Result, anyhow::Error> { let tdreport = match self.get_td_report(nonce, data) { Ok(r) => r, Err(e) => { @@ -315,9 +324,47 @@ impl CVM for TdxVM { Ok(qgs_msg_resp.id_quote[0..(qgs_msg_resp.quote_size as usize)].to_vec()) } + // CVM trait function: get tdx rtmr max index + fn get_max_index(&self) -> u8 { + TdxRTMR::max_index() + } + // CVM trait function: retrieve TDX RTMR - fn process_cc_measurement(&self, _index: u8, _algo_id: u8) -> TcgDigest { - todo!() + fn process_cc_measurement(&self, index: u8, algo_id: u8) -> Result { + match TdxRTMR::is_valid_index(index) { + Ok(_) => (), + Err(e) => return Err(anyhow!("[process_cc_measurement] {:?}", e)), + }; + + match TdxRTMR::is_valid_algo(algo_id) { + Ok(_) => (), + Err(e) => return Err(anyhow!("[process_cc_measurement] {:?}", e)), + }; + + let tdreport_raw = match self.get_td_report(None, None) { + Ok(r) => r, + Err(e) => { + return Err(anyhow!( + "[process_cc_measurement] error getting TD report: {:?}", + e + )) + } + }; + + let tdreport = match Tdx::parse_td_report(&tdreport_raw, self.version.clone()) { + Ok(r) => r, + Err(e) => { + return Err(anyhow!( + "[process_cc_measurement] error parsing TD report: {:?}", + e + )) + } + }; + + match TdxRTMR::new(index, algo_id, tdreport.td_info.rtmrs[index as usize]) { + Ok(rtmr) => Ok(rtmr.get_tcg_digest(algo_id)), + Err(e) => Err(anyhow!("error creating TdxRTMR {:?}", e)), + } } // CVM trait function: retrieve TDX CCEL and IMA eventlog @@ -347,6 +394,10 @@ impl TcgAlgorithmRegistry for TdxVM { fn get_algorithm_id(&self) -> u8 { self.algo_id } + + fn get_algorithm_id_str(&self) -> String { + ALGO_NAME_MAP.get(&self.algo_id).unwrap().to_owned() + } } impl BuildCVM for TdxVM {} diff --git a/vmsdk/rust/sample/Cargo.toml b/vmsdk/rust/sample/Cargo.toml index 6afefc97..cc625ef5 100644 --- a/vmsdk/rust/sample/Cargo.toml +++ b/vmsdk/rust/sample/Cargo.toml @@ -5,8 +5,12 @@ edition = "2021" license = "Apache-2.0" [[bin]] -name = "cc-sample" -path = "src/cc-sample.rs" +name = "cc-sample-quote" +path = "src/cc-sample-quote.rs" + +[[bin]] +name = "cc-sample-measurement" +path = "src/cc-sample-measurement.rs" [dependencies] cctrusted_vm = { path = "../cctrusted_vm" } diff --git a/vmsdk/rust/sample/src/cc-sample-measurement.rs b/vmsdk/rust/sample/src/cc-sample-measurement.rs new file mode 100644 index 00000000..55076838 --- /dev/null +++ b/vmsdk/rust/sample/src/cc-sample-measurement.rs @@ -0,0 +1,54 @@ +use cctrusted_base::api::*; +use cctrusted_base::tcg::TcgAlgorithmRegistry; +use cctrusted_vm::sdk::API; + +use log::*; + +fn main() { + // set log level + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + // get CVM default algorithm with API "get_default_algorithm" + info!("call cc trusted API [get_default_algorithm] to get CVM supported algorithm!"); + let defalt_algo = match API::get_default_algorithm() { + Ok(algorithm) => { + info!("supported algorithm: {}", algorithm.algo_id_str); + algorithm + } + Err(e) => { + error!("error get algorithm: {:?}", e); + return; + } + }; + + // get number of measurement registers in CVM + info!("call cc trusted API [get_measurement_count] to get number of measurement registers in CVM!"); + let count = match API::get_measurement_count() { + Ok(count) => { + info!("measurement registers count: {}", count); + count + } + Err(e) => { + error!("error get measurement count: {:?}", e); + return; + } + }; + + // retrive and show measurement registers in CVM + info!("call cc trusted API [get_cc_measurement] to get measurement register content in CVM!"); + for index in 0..count { + let tcg_digest = match API::get_cc_measurement(index, defalt_algo.algo_id) { + Ok(tcg_digest) => tcg_digest, + Err(e) => { + error!("error get measurement: {:?}", e); + return; + } + }; + info!( + "show index = {}, algo = {:?}, hash = {:02X?}", + index, + tcg_digest.get_algorithm_id_str(), + tcg_digest.get_hash() + ); + } +} diff --git a/vmsdk/rust/sample/src/cc-sample-quote.rs b/vmsdk/rust/sample/src/cc-sample-quote.rs new file mode 100644 index 00000000..3ebb0c5c --- /dev/null +++ b/vmsdk/rust/sample/src/cc-sample-quote.rs @@ -0,0 +1,55 @@ +use cctrusted_base::api::*; +use cctrusted_base::api_data::*; +use cctrusted_base::cc_type::TeeType; +use cctrusted_base::tdx::quote::TdxQuote; +use cctrusted_vm::sdk::API; + +use log::*; +use rand::Rng; + +fn main() { + // set log level + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + + /*** + * Note: in real user case, the nonce should come from attestation server + * side to prevent replay attack and the data should be generate by API caller + * according to user define spec + */ + let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + let data = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); + + // retrieve cc report with API "get_cc_report" + info!("call cc trusted API [get_cc_report] to retrieve cc report!"); + let report = match API::get_cc_report(Some(nonce), Some(data), ExtraArgs {}) { + Ok(q) => q, + Err(e) => { + error!("error getting TDX report: {:?}", e); + return; + } + }; + + // dump the cc report with API "dump_cc_report" + //info!("call cc trusted API [dump_cc_report] to dump cc report!"); + //API::dump_cc_report(&report.cc_report); + + // parse the cc report with API "parse_cc_report" + if report.cc_type == TeeType::TDX { + let tdx_quote: TdxQuote = match CcReport::parse_cc_report(report.cc_report) { + Ok(q) => q, + Err(e) => { + error!("error parse tdx quote: {:?}", e); + return; + } + }; + info!( + "version = {}, report_data = {}", + tdx_quote.header.version, + base64::encode(tdx_quote.body.report_data) + ); + + // show data of the struct TdxQuoteHeader + info!("call struct show function to show data of the struct TdxQuoteHeader!"); + tdx_quote.header.show(); + } +} diff --git a/vmsdk/rust/sample/src/cc-sample.rs b/vmsdk/rust/sample/src/cc-sample.rs deleted file mode 100644 index a1812153..00000000 --- a/vmsdk/rust/sample/src/cc-sample.rs +++ /dev/null @@ -1,32 +0,0 @@ -use cctrusted_base::api::*; -use cctrusted_base::api_data::*; -use cctrusted_vm::sdk::API; -use log::*; -use rand::Rng; - -fn main() { - // set log level - env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); - - /*** - * Note: in real user case, the nonce should come from attestation server - * side to prevent replay attack and the data should be generate by API caller - * according to user define spec - */ - let nonce = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); - let data = base64::encode(rand::thread_rng().gen::<[u8; 32]>()); - - // retrieve cc report with API "get_cc_report" - info!("call cc trusted API [get_cc_report] to retrieve cc report!"); - let report = match API::get_cc_report(nonce, data, ExtraArgs {}) { - Ok(q) => q, - Err(e) => { - error!("error getting TDX report: {:?}", e); - return; - } - }; - - // dump the cc report with API "dump_cc_report" - info!("call cc trusted API [dump_cc_report] to dump cc report!"); - API::dump_cc_report(&report.cc_report); -}