From 490259db460790b001c6e4e3cc90833f97892f64 Mon Sep 17 00:00:00 2001 From: hairongchen <105473940+hairongchen@users.noreply.github.com> Date: Thu, 11 Jan 2024 21:17:48 +0800 Subject: [PATCH] Rust SDK framework for CC API VM SDK [Ready to Merge] (#31) * initial commit for Rust SDK framework for CC API VM SDK * update according to comments: move type TPM to 0 * clear code and format * This commit fix follows: 1. update crate common/cctrusetd_base does not depend on std crates 2. change crate name from cctrusted to cctrusted_vm for VM SDK to in-sync with Python latest update 3. define trait CCTrustedApi in crate common/cctrusetd_base to define general cc trusted APIs 4. change api to sdk concept in cctrusted_vm crate 5. move CVM related functions to cctrusted_vm crate 6. update TCG algo list to reflact review comments by Jiewen * updates according to review comments: - add github action for rust check - add license and lincense check - update code format according to rust code check feedback * rust github action: update rust toolchain * refine rust check github action * refine the action * update runner * update code format according to rust code scan report * update cargo base work dir * add check for VM SDK --- .github/workflows/pr-check-rust.yaml | 55 +++ common/rust/cctrusted_base/Cargo.toml | 17 + common/rust/cctrusted_base/deny.toml | 35 ++ common/rust/cctrusted_base/src/api.rs | 128 +++++++ common/rust/cctrusted_base/src/api_data.rs | 40 ++ common/rust/cctrusted_base/src/binary_blob.rs | 59 +++ common/rust/cctrusted_base/src/cc_type.rs | 38 ++ common/rust/cctrusted_base/src/eventlog.rs | 2 + common/rust/cctrusted_base/src/lib.rs | 11 + common/rust/cctrusted_base/src/tcg.rs | 44 +++ common/rust/cctrusted_base/src/tdx/common.rs | 37 ++ common/rust/cctrusted_base/src/tdx/mod.rs | 4 + common/rust/cctrusted_base/src/tdx/quote.rs | 95 +++++ common/rust/cctrusted_base/src/tdx/report.rs | 75 ++++ common/rust/cctrusted_base/src/tdx/rtmr.rs | 26 ++ common/rust/cctrusted_base/src/tpm/mod.rs | 1 + common/rust/cctrusted_base/src/tpm/quote.rs | 8 + vmsdk/rust/cctrusted_vm/Cargo.toml | 16 + vmsdk/rust/cctrusted_vm/deny.toml | 35 ++ vmsdk/rust/cctrusted_vm/src/cvm.rs | 100 +++++ vmsdk/rust/cctrusted_vm/src/lib.rs | 3 + vmsdk/rust/cctrusted_vm/src/sdk.rs | 72 ++++ vmsdk/rust/cctrusted_vm/src/tdvm.rs | 352 ++++++++++++++++++ vmsdk/rust/sample/Cargo.toml | 18 + vmsdk/rust/sample/src/cc-sample.rs | 32 ++ 25 files changed, 1303 insertions(+) create mode 100644 .github/workflows/pr-check-rust.yaml create mode 100644 common/rust/cctrusted_base/Cargo.toml create mode 100644 common/rust/cctrusted_base/deny.toml create mode 100644 common/rust/cctrusted_base/src/api.rs create mode 100644 common/rust/cctrusted_base/src/api_data.rs create mode 100644 common/rust/cctrusted_base/src/binary_blob.rs create mode 100644 common/rust/cctrusted_base/src/cc_type.rs create mode 100644 common/rust/cctrusted_base/src/eventlog.rs create mode 100644 common/rust/cctrusted_base/src/lib.rs create mode 100644 common/rust/cctrusted_base/src/tcg.rs create mode 100644 common/rust/cctrusted_base/src/tdx/common.rs create mode 100644 common/rust/cctrusted_base/src/tdx/mod.rs create mode 100644 common/rust/cctrusted_base/src/tdx/quote.rs create mode 100644 common/rust/cctrusted_base/src/tdx/report.rs create mode 100644 common/rust/cctrusted_base/src/tdx/rtmr.rs create mode 100644 common/rust/cctrusted_base/src/tpm/mod.rs create mode 100644 common/rust/cctrusted_base/src/tpm/quote.rs create mode 100644 vmsdk/rust/cctrusted_vm/Cargo.toml create mode 100644 vmsdk/rust/cctrusted_vm/deny.toml create mode 100644 vmsdk/rust/cctrusted_vm/src/cvm.rs create mode 100644 vmsdk/rust/cctrusted_vm/src/lib.rs create mode 100644 vmsdk/rust/cctrusted_vm/src/sdk.rs create mode 100644 vmsdk/rust/cctrusted_vm/src/tdvm.rs create mode 100644 vmsdk/rust/sample/Cargo.toml create mode 100644 vmsdk/rust/sample/src/cc-sample.rs diff --git a/.github/workflows/pr-check-rust.yaml b/.github/workflows/pr-check-rust.yaml new file mode 100644 index 00000000..76107628 --- /dev/null +++ b/.github/workflows/pr-check-rust.yaml @@ -0,0 +1,55 @@ +name: Rust Code Scan + +on: + push: + branches: + - main + paths: + - 'common/rust/cctrusted_base/src/**.rs' + - 'common/rust/cctrusted_base/src/tdx/**.rs' + - 'common/rust/cctrusted_base/src/tpm/**.rs' + - 'vmsdk/rust/cctrusted_vm/src/**.rs' + - 'vmsdk/rust/sample/src/**.rs' + - '.github/workflows/pr-check-rust.yaml' + pull_request: + paths: + - 'common/rust/cctrusted_base/src/**.rs' + - 'common/rust/cctrusted_base/src/tdx/**.rs' + - 'common/rust/cctrusted_base/src/tpm/**.rs' + - 'vmsdk/rust/cctrusted_vm/src/**.rs' + - 'vmsdk/rust/sample/src/**.rs' + - '.github/workflows/pr-check-rust.yaml' + workflow_dispatch: + +jobs: + codescan: + runs-on: ubuntu-latest + steps: + - name: Checkout PR + uses: actions/checkout@v3 + + - name: Set up Rust action + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.70.0 + + - name: Install dependencies + run: | + sudo apt update && yes | DEBIAN_FRONTEND=noninteractive sudo apt install -y libcryptsetup-dev clang protobuf-compiler protobuf-c-compiler libprotobuf-c-dev libprotobuf-c1 build-essential pkg-config libssl-dev + + - name: Run cargo check for cctrusted_base + run: | + cd common/rust/cctrusted_base/ + cargo check + cargo fmt -- --check + cargo clippy + cargo install --locked cargo-deny + cargo deny check + + - name: Run cargo check for VM SDK + run: | + cd vmsdk/rust/cctrusted_vm/ + cargo check + cargo fmt -- --check + cargo clippy + cargo deny check \ No newline at end of file diff --git a/common/rust/cctrusted_base/Cargo.toml b/common/rust/cctrusted_base/Cargo.toml new file mode 100644 index 00000000..21509f3c --- /dev/null +++ b/common/rust/cctrusted_base/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cctrusted_base" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +name = "cctrusted_base" +path = "src/lib.rs" + +[dependencies] +anyhow = "1.0" +base64 = "0.13.0" +log = "0.4.20" +sha2 = "0.10" +lazy_static = "1.4.0" +hashbrown = "0.14" diff --git a/common/rust/cctrusted_base/deny.toml b/common/rust/cctrusted_base/deny.toml new file mode 100644 index 00000000..6e656f7e --- /dev/null +++ b/common/rust/cctrusted_base/deny.toml @@ -0,0 +1,35 @@ +[advisories] +vulnerability = "deny" +unmaintained = "warn" +yanked = "warn" +notice = "warn" + +[licenses] +unlicensed = "warn" +allow = [ + "MIT", + "Apache-2.0", + "ISC", + "BSD-3-Clause", + "Unicode-DFS-2016", +] + +copyleft = "warn" +allow-osi-fsf-free = "neither" +default = "deny" +confidence-threshold = 0.8 + +[[licenses.clarify]] +name = "ring" +expression = "MIT AND ISC AND OpenSSL" +license-files = [ + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[bans] +multiple-versions = "warn" +wildcards = "allow" + +[sources] +unknown-registry = "warn" +unknown-git = "warn" \ No newline at end of file diff --git a/common/rust/cctrusted_base/src/api.rs b/common/rust/cctrusted_base/src/api.rs new file mode 100644 index 00000000..dfe84d50 --- /dev/null +++ b/common/rust/cctrusted_base/src/api.rs @@ -0,0 +1,128 @@ +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 { + /*** + Get the cc report for given nonce and data. + + The cc report is signing of attestation data (IMR values or hashes of IMR + values), made by a trusted foundation (TPM) using a key trusted by the + verifier. + + Different trusted foundation may use different cc report format. + + Args: + nonce (String): against replay attacks + data (String): user data + extraArgs: for TPM, it will be given list of IMR/PCRs + + Returns: + The cc report byte array or error information + */ + fn get_cc_report( + nonce: String, + data: String, + _extra_args: ExtraArgs, + ) -> Result; + + /*** + Dump the given cc report in hex and char format + + Args: + report (Vec): cc report to be printed + + Returns: + None + */ + fn dump_cc_report(report: &Vec); + + /*** + 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; + + /*** + Get eventlog for given index and count. + + TCG log in Eventlog. Verify to spoof events in the TCG log, hence defeating + remotely-attested measured-boot. + + To measure the full CC runtime environment, the eventlog may include addtional + OS type and cloud native type event beyond the measured-boot. + + Returns: + TcgEventLog struct + */ + fn get_cc_eventlog(_start: u16, _count: u16) -> TcgEventLog; + + /*** + Get the default Digest algorithms supported by trusted foundation. + + Different trusted foundation may support different algorithms, for example + the Intel TDX use SHA384, TPM uses SHA256. + + Beyond the default digest algorithm, some trusted foundation like TPM + may support multiple algorithms. + + Returns: + The Algorithm struct + + */ + fn get_default_algorithm() -> Result; +} + +/*** + trait to be implemented for cc report parsing. + + the cooresponding implementation of parse_cc_report will be called according to + intented return format and the return of the trait function depends on + the type of cc report, e.g.: TdxQuote, TpmQuote and etc. + + TDX quote parsing Example: + if following is provided: + let tdx_quote: TdxQuote = parse_cc_report(cc_report_str); + then this implementation in api.rs will be called: + 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!() + } +} diff --git a/common/rust/cctrusted_base/src/api_data.rs b/common/rust/cctrusted_base/src/api_data.rs new file mode 100644 index 00000000..89f2c06d --- /dev/null +++ b/common/rust/cctrusted_base/src/api_data.rs @@ -0,0 +1,40 @@ +use crate::cc_type::TeeType; + +/*** + ************************************ + * API get_cc_report() related data * + ************************************ + */ +// input of API get_cc_report() +// this struct is used in vTPM and other CVM scenarios +// e.g.: vTPM may need report based on selective PCRs +pub struct ExtraArgs {} + +// return of API get_cc_report() +pub struct CcReport { + pub cc_report: Vec, + pub cc_type: TeeType, +} + +/*** + ************************************** + * API parse_cc_report() related data * + ************************************** +*/ +/*** + the return data structure is defined in cctrusted_base + e.g.: + - cctrusted_base::tdx::quote::TdxQuote; + - cctrusted_base::tpm::quote::TpmQuote; +*/ + +/*** + ******************************************** + * API get_default_algorithm() related data * + ******************************************** + */ +// return structure for get_default_algorithm +pub struct Algorithm { + pub algo_id: u8, + pub algo_id_str: String, +} diff --git a/common/rust/cctrusted_base/src/binary_blob.rs b/common/rust/cctrusted_base/src/binary_blob.rs new file mode 100644 index 00000000..0362a72e --- /dev/null +++ b/common/rust/cctrusted_base/src/binary_blob.rs @@ -0,0 +1,59 @@ +use log::info; + +/* dumnp raw cc report in following format: + 00000000 04 00 02 00 81 00 00 00 00 00 00 00 93 9A 72 33 ..............r3 + 00000010 F7 9C 4C A9 94 0A 0D B3 95 7F 06 07 D5 68 59 C7 ..L..........hY. + 00000020 35 FB B4 91 29 27 55 B2 E8 E8 23 B6 00 00 00 00 5...)'U...#..... +... +*/ + +pub fn dump_data(data: &Vec) { + let mut index: usize = 0; + let mut linestr = "".to_string(); + let mut printstr = "".to_string(); + + let printable = vec![ + ' ', '\t', '\n', '\r', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', + 'f', 'A', 'B', 'C', 'D', 'E', 'F', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', + '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', + '~', '"', '!', + ]; + + while index < data.len() { + if index % 16 == 0 { + if !printstr.is_empty() { + info!("{} {}", linestr, printstr); + printstr = "".to_string(); + } + linestr = format!("{:08X} ", ((index / 16) as u16) * 16); + } + + let v = data[index]; + linestr.push_str(format!("{:02X} ", v).as_str()); + match printable.iter().position(|&c| c == (v as char)) { + Some(_) => { + if !(0x9..=0xD).contains(&v) { + printstr.push_str(core::str::from_utf8(&[v]).unwrap()); + } else { + printstr.push('.'); + } + } + None => printstr.push('.'), + } + + index += 1; + } + + if index % 16 != 0 { + let mut blank = "".to_string(); + for _ in 1..=(16 - index % 16) { + blank.push_str(" "); + } + info!("{}{} {}", linestr, blank, printstr); + } else if index == data.len() { + info!("{} {}", linestr, printstr); + } +} diff --git a/common/rust/cctrusted_base/src/cc_type.rs b/common/rust/cctrusted_base/src/cc_type.rs new file mode 100644 index 00000000..5282e4c6 --- /dev/null +++ b/common/rust/cctrusted_base/src/cc_type.rs @@ -0,0 +1,38 @@ +use hashbrown::HashMap; + +// supported TEE types +#[derive(Clone, Eq, Hash, PartialEq)] +pub enum TeeType { + PLAIN = -1, + TPM = 0, + TDX = 1, + SEV = 2, + CCA = 3, +} + +// TEE type to type name string mapping +lazy_static! { + pub static ref TEE_NAME_MAP: HashMap = { + let mut map: HashMap = HashMap::new(); + map.insert(TeeType::PLAIN, "PLAIN".to_string()); + map.insert(TeeType::TDX, "TDX".to_string()); + map.insert(TeeType::SEV, "SEV".to_string()); + map.insert(TeeType::CCA, "CCA".to_string()); + map.insert(TeeType::TPM, "TPM".to_string()); + map + }; +} + +// public known device node path +pub const TEE_TPM_PATH: &str = "/dev/tpm0"; +pub const TEE_TDX_1_0_PATH: &str = "/dev/tdx-guest"; +pub const TEE_TDX_1_5_PATH: &str = "/dev/tdx_guest"; +pub const TEE_SEV_PATH: &str = "/dev/sev-guest"; +pub const TEE_CCA_PATH: &str = ""; + +// holds the TEE type info +#[derive(Clone)] +pub struct CcType { + pub tee_type: TeeType, + pub tee_type_str: String, +} diff --git a/common/rust/cctrusted_base/src/eventlog.rs b/common/rust/cctrusted_base/src/eventlog.rs new file mode 100644 index 00000000..d63f1a7b --- /dev/null +++ b/common/rust/cctrusted_base/src/eventlog.rs @@ -0,0 +1,2 @@ +// struct for standard TCG eventlog +pub struct TcgEventLog {} diff --git a/common/rust/cctrusted_base/src/lib.rs b/common/rust/cctrusted_base/src/lib.rs new file mode 100644 index 00000000..c4255190 --- /dev/null +++ b/common/rust/cctrusted_base/src/lib.rs @@ -0,0 +1,11 @@ +#[macro_use] +extern crate lazy_static; + +pub mod api; +pub mod api_data; +pub mod binary_blob; +pub mod cc_type; +pub mod eventlog; +pub mod tcg; +pub mod tdx; +pub mod tpm; diff --git a/common/rust/cctrusted_base/src/tcg.rs b/common/rust/cctrusted_base/src/tcg.rs new file mode 100644 index 00000000..047aa765 --- /dev/null +++ b/common/rust/cctrusted_base/src/tcg.rs @@ -0,0 +1,44 @@ +use hashbrown::HashMap; + +pub const TPM_ALG_ERROR: u8 = 0x0; +pub const TPM_ALG_RSA: u8 = 0x1; +pub const TPM_ALG_SHA1: u8 = 0x4; +pub const TPM_ALG_SHA256: u8 = 0xB; +pub const TPM_ALG_SHA384: u8 = 0xC; +pub const TPM_ALG_SHA512: u8 = 0xD; +pub const TPM_ALG_ECDSA: u8 = 0x18; + +// hash algorithm ID to algorithm name string map +lazy_static! { + pub static ref ALGO_NAME_MAP: HashMap = { + let mut map: HashMap = HashMap::new(); + map.insert(TPM_ALG_ERROR, "TPM_ALG_ERROR".to_string()); + map.insert(TPM_ALG_RSA, "TPM_ALG_RSA".to_string()); + map.insert(TPM_ALG_SHA1, "TPM_ALG_SHA1".to_string()); + map.insert(TPM_ALG_SHA256, "TPM_ALG_SHA256".to_string()); + map.insert(TPM_ALG_SHA384, "TPM_ALG_SHA384".to_string()); + map.insert(TPM_ALG_SHA512, "TPM_ALG_SHA512".to_string()); + map.insert(TPM_ALG_ECDSA, "TPM_ALG_ECDSA".to_string()); + map + }; +} + +// this trait retrieve tcg standard algorithm name in string +pub trait TcgAlgorithmRegistry { + fn get_algorithm_id(&self) -> u8; +} + +// digest format: (algo id, hash value) +#[allow(dead_code)] +pub struct TcgDigest { + algo_id: u8, + hash: Vec, +} + +// this trait retrieve IMR's max index of a CVM and hash value +pub trait TcgIMR { + fn max_index(&self) -> u8; + fn get_index(&self) -> u8; + fn get_hash(&self) -> Vec<&str>; + fn is_valid(&self) -> bool; +} diff --git a/common/rust/cctrusted_base/src/tdx/common.rs b/common/rust/cctrusted_base/src/tdx/common.rs new file mode 100644 index 00000000..d48f9a43 --- /dev/null +++ b/common/rust/cctrusted_base/src/tdx/common.rs @@ -0,0 +1,37 @@ +#![allow(non_camel_case_types)] +use crate::cc_type::*; +use hashbrown::HashMap; + +pub struct Tdx {} + +// TDX version ID +#[derive(Clone, Eq, Hash, PartialEq)] +pub enum TdxVersion { + TDX_1_0, + TDX_1_5, +} + +// TDX version ID to version string map +lazy_static! { + pub static ref TDX_VERSION_MAP: HashMap = { + let mut map: HashMap = HashMap::new(); + map.insert(TdxVersion::TDX_1_0, "1.0".to_string()); + map.insert(TdxVersion::TDX_1_5, "1.5".to_string()); + map + }; +} + +// TDX version ID to device path string map +lazy_static! { + pub static ref TDX_DEVICE_NODE_MAP: HashMap = { + let mut map: HashMap = HashMap::new(); + map.insert(TdxVersion::TDX_1_0, TEE_TDX_1_0_PATH.to_string()); + map.insert(TdxVersion::TDX_1_5, TEE_TDX_1_5_PATH.to_string()); + map + }; +} + +// quote and tdreport length +pub const REPORT_DATA_LEN: u32 = 64; +pub const TDX_REPORT_LEN: u32 = 1024; +pub const TDX_QUOTE_LEN: usize = 4 * 4096; diff --git a/common/rust/cctrusted_base/src/tdx/mod.rs b/common/rust/cctrusted_base/src/tdx/mod.rs new file mode 100644 index 00000000..8adee353 --- /dev/null +++ b/common/rust/cctrusted_base/src/tdx/mod.rs @@ -0,0 +1,4 @@ +pub mod common; +pub mod quote; +pub mod report; +pub mod rtmr; diff --git a/common/rust/cctrusted_base/src/tdx/quote.rs b/common/rust/cctrusted_base/src/tdx/quote.rs new file mode 100644 index 00000000..9f81fc3d --- /dev/null +++ b/common/rust/cctrusted_base/src/tdx/quote.rs @@ -0,0 +1,95 @@ +#![allow(non_camel_case_types)] +use core::result::Result; +use core::result::Result::Ok; + +use crate::tdx::common::*; + +#[repr(C)] +pub struct qgs_msg_header { + pub major_version: u16, // TDX major version + pub minor_version: u16, // TDX minor version + pub msg_type: u32, // GET_QUOTE_REQ or GET_QUOTE_RESP + pub size: u32, // size of the whole message, include this header, in byte + pub error_code: u32, // used in response only +} + +#[repr(C)] +pub struct qgs_msg_get_quote_req { + pub header: qgs_msg_header, // header.type = GET_QUOTE_REQ + pub report_size: u32, // cannot be 0 + pub id_list_size: u32, // length of id_list, in byte, can be 0 + pub report_id_list: [u8; TDX_REPORT_LEN as usize], // report followed by id list +} + +#[repr(C)] +pub struct tdx_quote_hdr { + pub version: u64, // Quote version, filled by TD + pub status: u64, // Status code of Quote request, filled by VMM + pub in_len: u32, // Length of TDREPORT, filled by TD + pub out_len: u32, // Length of Quote, filled by VMM + pub data_len_be_bytes: [u8; 4], // big-endian 4 bytes indicate the size of data following + pub data: [u8; TDX_QUOTE_LEN as usize], // Actual Quote data or TDREPORT on input +} + +#[repr(C)] +pub struct tdx_quote_req { + pub buf: u64, // Pass user data that includes TDREPORT as input. Upon successful completion of IOCTL, output is copied back to the same buffer + pub len: u64, // Length of the Quote buffer +} + +#[repr(C)] +pub struct qgs_msg_get_quote_resp { + pub header: qgs_msg_header, // header.type = GET_QUOTE_RESP + pub selected_id_size: u32, // can be 0 in case only one id is sent in request + pub quote_size: u32, // length of quote_data, in byte + pub id_quote: [u8; TDX_QUOTE_LEN], // selected id followed by quote +} + +impl Tdx { + /*** + generate qgs message for TDX quote generation + + Args: + report (Vec): tdreport + + Returns: + qgs_msg_get_quote_req struct instance + */ + pub fn generate_qgs_quote_msg(report: [u8; TDX_REPORT_LEN as usize]) -> qgs_msg_get_quote_req { + //build quote service message header to be used by QGS + let qgs_header = qgs_msg_header { + major_version: 1, + minor_version: 0, + msg_type: 0, + size: 16 + 8 + TDX_REPORT_LEN, // header + report_size and id_list_size + TDX_REPORT_LEN + error_code: 0, + }; + + //build quote service message body to be used by QGS + let mut qgs_request = qgs_msg_get_quote_req { + header: qgs_header, + report_size: TDX_REPORT_LEN, + id_list_size: 0, + report_id_list: [0; TDX_REPORT_LEN as usize], + }; + + qgs_request.report_id_list.copy_from_slice(&report[0..]); + + qgs_request + } +} + +#[derive(Clone)] +pub struct TdxQuote { + pub dummy_var1: u8, + pub dummy_var2: u8, +} + +impl TdxQuote { + pub fn parse_tdx_quote(_quote: Vec) -> Result { + Ok(TdxQuote { + dummy_var1: 1, + dummy_var2: 2, + }) + } +} diff --git a/common/rust/cctrusted_base/src/tdx/report.rs b/common/rust/cctrusted_base/src/tdx/report.rs new file mode 100644 index 00000000..902c24f6 --- /dev/null +++ b/common/rust/cctrusted_base/src/tdx/report.rs @@ -0,0 +1,75 @@ +#![allow(non_camel_case_types)] +use crate::tdx::common::*; +use anyhow::*; +use core::result::Result; +use core::result::Result::Ok; +use sha2::{Digest, Sha512}; + +#[repr(C)] +pub struct tdx_1_0_report_req { + pub subtype: u8, // Subtype of TDREPORT: fixed as 0 by TDX Module specification + pub reportdata: u64, // User-defined REPORTDATA to be included into TDREPORT + pub rpd_len: u32, // Length of the REPORTDATA: fixed as 64 bytes by the TDX Module specification + pub tdreport: u64, // TDREPORT output from TDCALL[TDG.MR.REPORT] + pub tdr_len: u32, // Length of the TDREPORT: fixed as 1024 bytes by the TDX Module specification +} + +#[repr(C)] +pub struct tdx_1_5_report_req { + pub reportdata: [u8; REPORT_DATA_LEN as usize], // User buffer with REPORTDATA to be included into TDREPORT + pub tdreport: [u8; TDX_REPORT_LEN as usize], // User buffer to store TDREPORT output from TDCALL[TDG.MR.REPORT] +} + +impl Tdx { + /*** + generate tdx report data with nonce and data + + Args: + nonce (String): against replay attacks + data (String): user data + + Returns: + The tdreport byte array + */ + pub fn generate_tdx_report_data( + nonce: String, + 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(); + hasher.update(nonce_decoded); + let _ret = match data { + Some(_encoded_data) => { + if _encoded_data.is_empty() { + hasher.update("") + } else { + let decoded_data = match base64::decode(_encoded_data) { + Ok(v) => v, + Err(e) => { + return Err(anyhow!( + "[generate_tdx_report_data] user data is not base64 encoded: {:?}", + e + )) + } + }; + hasher.update(decoded_data) + } + } + None => hasher.update(""), + }; + let hash_array: [u8; 64] = hasher + .finalize() + .as_slice() + .try_into() + .expect("[generate_tdx_report_data] Wrong length of report data"); + Ok(base64::encode(hash_array)) + } +} diff --git a/common/rust/cctrusted_base/src/tdx/rtmr.rs b/common/rust/cctrusted_base/src/tdx/rtmr.rs new file mode 100644 index 00000000..ffb5d918 --- /dev/null +++ b/common/rust/cctrusted_base/src/tdx/rtmr.rs @@ -0,0 +1,26 @@ +use crate::tcg::*; +use hashbrown::HashMap; + +#[allow(dead_code)] +pub struct TdxRTMR { + index: u8, + digests: HashMap, +} + +impl TcgIMR for TdxRTMR { + fn max_index(&self) -> u8 { + 3 + } + + fn get_index(&self) -> u8 { + todo!() + } + + fn get_hash(&self) -> Vec<&str> { + todo!() + } + + fn is_valid(&self) -> bool { + todo!() + } +} diff --git a/common/rust/cctrusted_base/src/tpm/mod.rs b/common/rust/cctrusted_base/src/tpm/mod.rs new file mode 100644 index 00000000..9e1a6d43 --- /dev/null +++ b/common/rust/cctrusted_base/src/tpm/mod.rs @@ -0,0 +1 @@ +pub mod quote; diff --git a/common/rust/cctrusted_base/src/tpm/quote.rs b/common/rust/cctrusted_base/src/tpm/quote.rs new file mode 100644 index 00000000..1ef6edf1 --- /dev/null +++ b/common/rust/cctrusted_base/src/tpm/quote.rs @@ -0,0 +1,8 @@ +// return of API parse_cc_report() +pub struct TpmQuote {} + +impl TpmQuote { + pub fn parse_tpm_quote(_quote: Vec) -> Result { + todo!() + } +} diff --git a/vmsdk/rust/cctrusted_vm/Cargo.toml b/vmsdk/rust/cctrusted_vm/Cargo.toml new file mode 100644 index 00000000..35a0aa7a --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "cctrusted_vm" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +name = "cctrusted_vm" +path = "src/lib.rs" + +[dependencies] +cctrusted_base = { path = "../../../common/rust/cctrusted_base" } +anyhow = "1.0" +log = "0.4.20" +nix = "0.26.2" +base64 = "0.13.0" diff --git a/vmsdk/rust/cctrusted_vm/deny.toml b/vmsdk/rust/cctrusted_vm/deny.toml new file mode 100644 index 00000000..6e656f7e --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/deny.toml @@ -0,0 +1,35 @@ +[advisories] +vulnerability = "deny" +unmaintained = "warn" +yanked = "warn" +notice = "warn" + +[licenses] +unlicensed = "warn" +allow = [ + "MIT", + "Apache-2.0", + "ISC", + "BSD-3-Clause", + "Unicode-DFS-2016", +] + +copyleft = "warn" +allow-osi-fsf-free = "neither" +default = "deny" +confidence-threshold = 0.8 + +[[licenses.clarify]] +name = "ring" +expression = "MIT AND ISC AND OpenSSL" +license-files = [ + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[bans] +multiple-versions = "warn" +wildcards = "allow" + +[sources] +unknown-registry = "warn" +unknown-git = "warn" \ No newline at end of file diff --git a/vmsdk/rust/cctrusted_vm/src/cvm.rs b/vmsdk/rust/cctrusted_vm/src/cvm.rs new file mode 100644 index 00000000..120f6500 --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/src/cvm.rs @@ -0,0 +1,100 @@ +use crate::tdvm::TdxVM; +use anyhow::*; +use cctrusted_base::cc_type::*; +use cctrusted_base::tcg::{TcgAlgorithmRegistry, TcgDigest}; +use std::path::Path; + +// the interfaces a CVM should implement +pub trait CVM { + /*** + retrive CVM signed report + + Args: + nonce (String): against replay attacks + data (String): user data + + Returns: + the cc report byte array or error information + */ + fn process_cc_report(&mut self, nonce: String, data: String) -> Result, anyhow::Error>; + + /*** + retrive CVM measurement registers, e.g.: RTMRs, vTPM PCRs, etc. + + Args: + index (u8): the index of measurement register, + algo_id (u8): the alrogithms ID + + Returns: + TcgDigest struct + */ + fn process_cc_measurement(&self, _index: u8, _algo_id: u8) -> TcgDigest; + + /*** + retrive CVM eventlogs + + Args: + start and count of eventlogs + + Returns: + array of eventlogs + */ + fn process_cc_eventlog(&self); + + /*** + retrive CVM type + + Args: + None + + Returns: + CcType of CVM + */ + fn get_cc_type(&self) -> CcType; + + //Dump confidential CVM information + fn dump(&self); +} + +// used for return of Boxed trait object in build_cvm() +// this composed trait includes functions in both trait CVM and trait TcgAlgorithmRegistry +pub trait BuildCVM: CVM + TcgAlgorithmRegistry {} + +// holds the device node info +pub struct DeviceNode { + pub device_path: String, +} + +/*** + instance a specific object containers specific CVM methods + and desired trait functions specified by "dyn BuildCVM" +*/ +pub fn build_cvm() -> Result, anyhow::Error> { + // instance a CVM according to detected TEE type + match get_cvm_type().tee_type { + TeeType::TDX => Ok(Box::new(TdxVM::new())), + TeeType::SEV => todo!(), + TeeType::CCA => todo!(), + TeeType::TPM => todo!(), + TeeType::PLAIN => Err(anyhow!("[build_cvm] Error: not in any TEE!")), + } +} + +// detect CVM type +pub fn get_cvm_type() -> CcType { + let mut tee_type = TeeType::PLAIN; + if Path::new(TEE_TPM_PATH).exists() { + tee_type = TeeType::TPM; + } else if Path::new(TEE_TDX_1_0_PATH).exists() || Path::new(TEE_TDX_1_5_PATH).exists() { + tee_type = TeeType::TDX; + } else if Path::new(TEE_SEV_PATH).exists() { + tee_type = TeeType::SEV; + } else { + // TODO add support for CCA and etc. + } + + CcType { + tee_type: tee_type.clone(), + tee_type_str: TEE_NAME_MAP.get(&tee_type).unwrap().to_owned(), + } +} diff --git a/vmsdk/rust/cctrusted_vm/src/lib.rs b/vmsdk/rust/cctrusted_vm/src/lib.rs new file mode 100644 index 00000000..5fcc9b0f --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/src/lib.rs @@ -0,0 +1,3 @@ +pub mod cvm; +pub mod sdk; +pub mod tdvm; diff --git a/vmsdk/rust/cctrusted_vm/src/sdk.rs b/vmsdk/rust/cctrusted_vm/src/sdk.rs new file mode 100644 index 00000000..7a0692a5 --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/src/sdk.rs @@ -0,0 +1,72 @@ +use anyhow::*; +use core::result::Result; +use core::result::Result::Ok; + +use cctrusted_base::binary_blob::dump_data; +use cctrusted_base::eventlog::TcgEventLog; +use cctrusted_base::tcg::{TcgDigest, ALGO_NAME_MAP}; + +use crate::cvm::build_cvm; +use cctrusted_base::api::*; +use cctrusted_base::api_data::*; + +pub struct API {} + +impl CCTrustedApi for API { + // CCTrustedApi trait function: get report of a CVM + fn get_cc_report( + nonce: String, + data: String, + _extra_args: ExtraArgs, + ) -> Result { + match build_cvm() { + Ok(mut cvm) => { + // call CVM trait defined methods + cvm.dump(); + Ok(CcReport { + cc_report: match cvm.process_cc_report(nonce, data) { + Ok(r) => r, + Err(e) => { + return Err(anyhow!("[get_cc_report] error get cc report: {:?}", e)); + } + }, + cc_type: cvm.get_cc_type().tee_type, + }) + } + Err(e) => Err(anyhow!("[get_cc_report] error create cvm: {:?}", e)), + } + } + + // CCTrustedApi trait function: dump report of a CVM in hex and char format + fn dump_cc_report(report: &Vec) { + dump_data(report) + } + + // CCTrustedApi trait function: get measurements of a CVM + fn get_cc_measurement(_index: u8, _algo_id: u8) -> TcgDigest { + todo!() + } + + // CCTrustedApi trait function: get eventlogs of a CVM + fn get_cc_eventlog(_start: u16, _count: u16) -> TcgEventLog { + todo!() + } + + // CCTrustedApi trait function: get default algorithm of a CVM + fn get_default_algorithm() -> Result { + match build_cvm() { + Ok(cvm) => { + // call CVM trait defined methods + let algo_id = cvm.get_algorithm_id(); + Ok(Algorithm { + algo_id, + algo_id_str: ALGO_NAME_MAP.get(&algo_id).unwrap().to_owned(), + }) + } + Err(e) => Err(anyhow!( + "[get_default_algorithm] error get algorithm: {:?}", + e + )), + } + } +} diff --git a/vmsdk/rust/cctrusted_vm/src/tdvm.rs b/vmsdk/rust/cctrusted_vm/src/tdvm.rs new file mode 100644 index 00000000..3cdfef42 --- /dev/null +++ b/vmsdk/rust/cctrusted_vm/src/tdvm.rs @@ -0,0 +1,352 @@ +#![allow(non_camel_case_types)] + +use crate::cvm::*; +use anyhow::*; +use cctrusted_base::cc_type::*; +use cctrusted_base::tcg::{TcgAlgorithmRegistry, TcgDigest}; +use cctrusted_base::tdx::common::*; +use cctrusted_base::tdx::quote::*; +use cctrusted_base::tdx::report::*; +use core::convert::TryInto; +use core::mem; +use core::ptr; +use core::result::Result; +use core::result::Result::Ok; +use log::info; +use nix::*; +use std::fs::File; +use std::os::fd::AsRawFd; +use std::path::Path; + +// TDX ioctl operation code to be used for get TDX quote and TD Report +pub enum TdxOperation { + TDX_GET_TD_REPORT = 1, + TDX_1_0_GET_QUOTE = 2, + TDX_1_5_GET_QUOTE = 4, +} + +/* + TdxVM is an abstraction of TDX running environment, it contains: + cc_type: should always be CcType built with TeeType::TDX + version: TdxVersion::TDX_1_0 or TdxVersion::TDX_1_5 + device_node: /dev/tdx-guest or /dev/tdx_guest + algo_id: should be TPM_ALG_SHA384 +*/ +pub struct TdxVM { + pub cc_type: CcType, + pub version: TdxVersion, + pub device_node: DeviceNode, + pub algo_id: u8, +} + +// implement the structure method and associated function +impl TdxVM { + // TdxVM struct associated function: to build a TdxVM sturcture instance + pub fn new() -> TdxVM { + let cc_type = CcType { + tee_type: TeeType::TDX, + tee_type_str: TEE_NAME_MAP.get(&TeeType::TDX).unwrap().to_owned(), + }; + + let version = Self::get_tdx_version(); + let device_node = DeviceNode { + device_path: TDX_DEVICE_NODE_MAP.get(&version).unwrap().to_owned(), + }; + let algo_id = cctrusted_base::tcg::TPM_ALG_SHA384; + + TdxVM { + cc_type, + version, + device_node, + algo_id, + } + } + + // 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)) { + Ok(r) => r, + Err(e) => { + return Err(anyhow!( + "[get_td_report] error generating TDX report data: {:?}", + e + )) + } + }; + + let device_node = match File::options() + .read(true) + .write(true) + .open(self.device_node.device_path.clone()) + { + Err(e) => { + return Err(anyhow!( + "[get_td_report] Fail to open {}: {:?}", + self.device_node.device_path, + e + )) + } + Ok(fd) => fd, + }; + + match self.version { + TdxVersion::TDX_1_0 => { + let report_data_bytes = match base64::decode(report_data) { + Ok(v) => v, + Err(e) => return Err(anyhow!("report data is not base64 encoded: {:?}", e)), + }; + + //prepare get TDX report request data + let mut report_data_array: [u8; REPORT_DATA_LEN as usize] = + [0; REPORT_DATA_LEN as usize]; + report_data_array.copy_from_slice(&report_data_bytes[0..]); + let td_report: [u8; TDX_REPORT_LEN as usize] = [0; TDX_REPORT_LEN as usize]; + + //build the request + let request = tdx_1_0_report_req { + subtype: 0_u8, + reportdata: ptr::addr_of!(report_data_array) as u64, + rpd_len: REPORT_DATA_LEN, + tdreport: ptr::addr_of!(td_report) as u64, + tdr_len: TDX_REPORT_LEN, + }; + + //build the operator code + ioctl_readwrite!( + get_report_1_0_ioctl, + b'T', + TdxOperation::TDX_GET_TD_REPORT, + u64 + ); + + //apply the ioctl command + match unsafe { + get_report_1_0_ioctl( + device_node.as_raw_fd(), + ptr::addr_of!(request) as *mut u64, + ) + } { + Err(e) => { + return Err(anyhow!("[get_td_report] Fail to get TDX report: {:?}", e)) + } + Ok(_) => (), + }; + + Ok(td_report.to_vec()) + } + TdxVersion::TDX_1_5 => { + let report_data_bytes = match base64::decode(report_data) { + Ok(v) => v, + Err(e) => return Err(anyhow!("report data is not base64 encoded: {:?}", e)), + }; + + //prepare get TDX report request data + let mut request = tdx_1_5_report_req { + reportdata: [0; REPORT_DATA_LEN as usize], + tdreport: [0; TDX_REPORT_LEN as usize], + }; + request.reportdata.copy_from_slice(&report_data_bytes[0..]); + + //build the operator code + ioctl_readwrite!( + get_report_1_5_ioctl, + b'T', + TdxOperation::TDX_GET_TD_REPORT, + tdx_1_5_report_req + ); + + //apply the ioctl command + match unsafe { + get_report_1_5_ioctl( + device_node.as_raw_fd(), + ptr::addr_of!(request) as *mut tdx_1_5_report_req, + ) + } { + Err(e) => { + return Err(anyhow!("[get_td_report] Fail to get TDX report: {:?}", e)) + } + Ok(_) => (), + }; + + Ok(request.tdreport.to_vec()) + } + } + } + + // TdxVM struct associated function: detect the TDX version + fn get_tdx_version() -> TdxVersion { + if Path::new(TEE_TDX_1_0_PATH).exists() { + TdxVersion::TDX_1_0 + } else if Path::new(TEE_TDX_1_5_PATH).exists() { + TdxVersion::TDX_1_5 + } else { + TdxVersion::TDX_1_0 + } + } +} + +// 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> { + let tdreport = match self.get_td_report(nonce, data) { + Ok(r) => r, + Err(e) => { + return Err(anyhow!( + "[process_cc_report] error getting TD report: {:?}", + e + )) + } + }; + + let report_data_array: [u8; TDX_REPORT_LEN as usize] = match tdreport.try_into() { + Ok(r) => r, + Err(e) => return Err(anyhow!("[get_tdx_quote] Wrong TDX report format: {:?}", e)), + }; + + //build QGS request message + let qgs_msg = Tdx::generate_qgs_quote_msg(report_data_array); + + //build quote generation request header + let mut quote_header = tdx_quote_hdr { + version: 1, + status: 0, + in_len: (mem::size_of_val(&qgs_msg) + 4) as u32, + out_len: 0, + data_len_be_bytes: (1048_u32).to_be_bytes(), + data: [0; TDX_QUOTE_LEN], + }; + + let qgs_msg_bytes = unsafe { + let ptr = &qgs_msg as *const qgs_msg_get_quote_req as *const u8; + core::slice::from_raw_parts(ptr, mem::size_of::()) + }; + quote_header.data[0..(16 + 8 + TDX_REPORT_LEN) as usize] + .copy_from_slice(&qgs_msg_bytes[0..((16 + 8 + TDX_REPORT_LEN) as usize)]); + + let tdx_quote_request = tdx_quote_req { + buf: ptr::addr_of!(quote_header) as u64, + len: TDX_QUOTE_LEN as u64, + }; + + let device_node = match File::options() + .read(true) + .write(true) + .open(self.device_node.device_path.clone()) + { + Err(e) => { + return Err(anyhow!( + "[get_td_report] Fail to open {}: {:?}", + self.device_node.device_path, + e + )) + } + Ok(fd) => fd, + }; + + //build the operator code and apply the ioctl command + match self.version { + TdxVersion::TDX_1_0 => { + ioctl_read!( + get_quote_1_0_ioctl, + b'T', + TdxOperation::TDX_1_0_GET_QUOTE, + u64 + ); + match unsafe { + get_quote_1_0_ioctl( + device_node.as_raw_fd(), + ptr::addr_of!(tdx_quote_request) as *mut u64, + ) + } { + Err(e) => { + return Err(anyhow!("[get_tdx_quote] Fail to get TDX quote: {:?}", e)) + } + Ok(_r) => _r, + }; + } + TdxVersion::TDX_1_5 => { + ioctl_read!( + get_quote_1_5_ioctl, + b'T', + TdxOperation::TDX_1_5_GET_QUOTE, + tdx_quote_req + ); + match unsafe { + get_quote_1_5_ioctl( + device_node.as_raw_fd(), + ptr::addr_of!(tdx_quote_request) as *mut tdx_quote_req, + ) + } { + Err(e) => { + return Err(anyhow!("[get_tdx_quote] Fail to get TDX quote: {:?}", e)) + } + Ok(_r) => _r, + }; + } + }; + + //inspect the response and retrive quote data + let out_len = quote_header.out_len; + let qgs_msg_resp_size = + unsafe { core::mem::transmute::<[u8; 4], u32>(quote_header.data_len_be_bytes) }.to_be(); + + let qgs_msg_resp = unsafe { + let raw_ptr = ptr::addr_of!(quote_header.data) as *mut qgs_msg_get_quote_resp; + raw_ptr.as_mut().unwrap() as &mut qgs_msg_get_quote_resp + }; + + if out_len - qgs_msg_resp_size != 4 { + return Err(anyhow!( + "[get_tdx_quote] Fail to get TDX quote: wrong TDX quote size!" + )); + } + + if qgs_msg_resp.header.major_version != 1 + || qgs_msg_resp.header.minor_version != 0 + || qgs_msg_resp.header.msg_type != 1 + || qgs_msg_resp.header.error_code != 0 + { + return Err(anyhow!( + "[get_tdx_quote] Fail to get TDX quote: QGS response error!" + )); + } + + Ok(qgs_msg_resp.id_quote[0..(qgs_msg_resp.quote_size as usize)].to_vec()) + } + + // CVM trait function: retrieve TDX RTMR + fn process_cc_measurement(&self, _index: u8, _algo_id: u8) -> TcgDigest { + todo!() + } + + // CVM trait function: retrieve TDX CCEL and IMA eventlog + fn process_cc_eventlog(&self) { + todo!() + } + + // CVM trait function: retrive CVM type + fn get_cc_type(&self) -> CcType { + self.cc_type.clone() + } + + // CVM trait function: dump CVM basic information + fn dump(&self) { + info!("======================================"); + info!("CVM type = {}", self.cc_type.tee_type_str); + info!( + "CVM version = {}", + TDX_VERSION_MAP.get(&self.version).unwrap().to_owned() + ); + info!("======================================"); + } +} + +impl TcgAlgorithmRegistry for TdxVM { + // TcgAlgorithmRegistry trait function: return CVM default algorithm ID + fn get_algorithm_id(&self) -> u8 { + self.algo_id + } +} + +impl BuildCVM for TdxVM {} diff --git a/vmsdk/rust/sample/Cargo.toml b/vmsdk/rust/sample/Cargo.toml new file mode 100644 index 00000000..6afefc97 --- /dev/null +++ b/vmsdk/rust/sample/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cctrusted-sample" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[[bin]] +name = "cc-sample" +path = "src/cc-sample.rs" + +[dependencies] +cctrusted_vm = { path = "../cctrusted_vm" } +cctrusted_base = { path = "../../../common/rust/cctrusted_base" } +anyhow = "1.0" +log = "0.4.20" +env_logger = "0.10.1" +base64 = "0.13.0" +rand = "0.8.5" \ No newline at end of file diff --git a/vmsdk/rust/sample/src/cc-sample.rs b/vmsdk/rust/sample/src/cc-sample.rs new file mode 100644 index 00000000..a1812153 --- /dev/null +++ b/vmsdk/rust/sample/src/cc-sample.rs @@ -0,0 +1,32 @@ +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); +}