1use az_tdx_vtpm::{hcl, imds, vtpm};
5use base64::{Engine as _, engine::general_purpose::URL_SAFE as BASE64_URL_SAFE};
6use serde::Serialize;
7use thiserror::Error;
8use tss_esapi::{
9 Context,
10 handles::NvIndexTpmHandle,
11 interface_types::{resource_handles::NvAuth, session_handles::AuthSession},
12 tcti_ldr::{DeviceConfig, TctiNameConf},
13};
14
15const TPM_AK_CERT_IDX: u32 = 0x1C101D0;
17
18#[derive(Serialize)]
19struct AttestationDocument {
20 tdx_quote_base64: String,
21 hcl_report_base64: String,
22 tpm_attestation: TpmAttest,
23}
24
25#[derive(Serialize)]
26struct TpmAttest {
27 ak_certificate_pem: String,
28 quote: vtpm::Quote,
29 event_log: Vec<u8>,
30 instance_info: Option<Vec<u8>>,
31}
32
33pub fn create_quote(input_data: [u8; 64]) -> Result<Vec<u8>, AzureError> {
34 let hcl_report_bytes = vtpm::get_report_with_report_data(&input_data)?;
35 let hcl_report = hcl::HclReport::new(hcl_report_bytes.clone())?;
36 let td_report = hcl_report.try_into()?;
37 let td_quote_bytes = imds::get_td_quote(&td_report)?;
38 let ak_certificate_der = read_ak_certificate_from_tpm()?;
39
40 let document = AttestationDocument {
41 tdx_quote_base64: BASE64_URL_SAFE.encode(&td_quote_bytes),
42 hcl_report_base64: BASE64_URL_SAFE.encode(&hcl_report_bytes),
43 tpm_attestation: TpmAttest {
44 ak_certificate_pem: pem_rfc7468::encode_string(
45 "CERTIFICATE",
46 pem_rfc7468::LineEnding::default(),
47 &ak_certificate_der,
48 )?,
49 quote: vtpm::get_quote(&input_data[..32])?,
50 event_log: Vec::new(),
51 instance_info: None,
52 },
53 };
54 Ok(serde_json::to_vec(&document)?)
55}
56
57fn read_ak_certificate_from_tpm() -> Result<Vec<u8>, tss_esapi::Error> {
58 let mut context = Context::new(TctiNameConf::Device(DeviceConfig::default()))?;
59 context.set_sessions((Some(AuthSession::Password), None, None));
60 let nv_handle = NvIndexTpmHandle::new(TPM_AK_CERT_IDX)?;
61 let buf = tss_esapi::abstraction::nv::read_full(&mut context, NvAuth::Owner, nv_handle)?;
62 Ok(buf.to_vec())
63}
64
65#[derive(Error, Debug)]
66pub enum AzureError {
67 #[error("HCL report: {0}")]
68 Report(#[from] az_tdx_vtpm::report::ReportError),
69 #[error("IMDS: {0}")]
70 Imds(#[from] imds::ImdsError),
71 #[error("vTPM report: {0}")]
72 VtpmReport(#[from] az_tdx_vtpm::vtpm::ReportError),
73 #[error("HCL: {0}")]
74 Hcl(#[from] hcl::HclError),
75 #[error("vTPM quote: {0}")]
76 VtpmQuote(#[from] vtpm::QuoteError),
77 #[error("vTPM read: {0}")]
78 TssEsapi(#[from] tss_esapi::Error),
79 #[error("PEM encode: {0}")]
80 Pem(#[from] pem_rfc7468::Error),
81 #[error("JSON: {0}")]
82 Json(#[from] serde_json::Error),
83}