Skip to content

Commit 739507f

Browse files
committed
WIP - get JWT from azure api
1 parent e932957 commit 739507f

File tree

3 files changed

+88
-10
lines changed

3 files changed

+88
-10
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ bytes = "1.10.1"
2727
http = "1.3.1"
2828
serde_json = "1.0.145"
2929
az-tdx-vtpm = "0.7.4"
30+
serde = "1.0.228"
31+
base64 = "0.22.1"
32+
reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] }
3033

3134
[dev-dependencies]
3235
rcgen = "0.14.5"
3336
axum = "0.8.6"
34-
reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] }

src/attestation/azure.rs

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
use az_tdx_vtpm::{hcl, imds, report, tdx, vtpm};
22
use tokio_rustls::rustls::pki_types::CertificateDer;
33
// use openssl::pkey::{PKey, Public};
4+
use base64::prelude::*;
5+
use reqwest::Client;
6+
use serde::Serialize;
47

58
use crate::attestation::{compute_report_input, AttestationError, AttestationType, QuoteGenerator};
69

710
#[derive(Clone)]
8-
pub struct MaaQuoteGenerator {}
11+
pub struct MaaQuoteGenerator {
12+
maa_endpoint: String,
13+
aad_access_token: String,
14+
}
915

1016
impl QuoteGenerator for MaaQuoteGenerator {
1117
/// Type of attestation used
@@ -18,7 +24,7 @@ impl QuoteGenerator for MaaQuoteGenerator {
1824
cert_chain: &[CertificateDer<'_>],
1925
exporter: [u8; 32],
2026
) -> Result<Vec<u8>, AttestationError> {
21-
let _quote_input = compute_report_input(cert_chain, exporter)?;
27+
let quote_input = compute_report_input(cert_chain, exporter)?;
2228

2329
let td_report = report::get_report().unwrap();
2430

@@ -29,22 +35,90 @@ impl QuoteGenerator for MaaQuoteGenerator {
2935
// let rtmr3 = td_report.tdinfo.rtrm[3];
3036

3137
// This makes a request to Azure Instance metadata service and gives us a binary response
32-
let _td_quote_bytes = imds::get_td_quote(&td_report).unwrap();
38+
let td_quote_bytes = imds::get_td_quote(&td_report).unwrap();
3339

34-
let bytes = vtpm::get_report().unwrap();
35-
let hcl_report = hcl::HclReport::new(bytes).unwrap();
36-
let var_data_hash = hcl_report.var_data_sha256();
37-
let _ak_pub = hcl_report.ak_pub().unwrap();
40+
let hcl_report_bytes = vtpm::get_report_with_report_data(&quote_input).unwrap();
41+
let hcl_report = hcl::HclReport::new(hcl_report_bytes).unwrap();
42+
let hcl_var_data = hcl_report.var_data();
3843

39-
let td_report: tdx::TdReport = hcl_report.try_into().unwrap();
40-
assert!(var_data_hash == td_report.report_mac.reportdata[..32]);
44+
// let bytes = vtpm::get_report().unwrap();
45+
// let hcl_report = hcl::HclReport::new(bytes).unwrap();
46+
// let var_data_hash = hcl_report.var_data_sha256();
47+
// let _ak_pub = hcl_report.ak_pub().unwrap();
48+
//
49+
// let td_report: tdx::TdReport = hcl_report.try_into().unwrap();
50+
// assert!(var_data_hash == td_report.report_mac.reportdata[..32]);
4151

4252
// let nonce = "a nonce".as_bytes();
4353
//
4454
// let tpm_quote = vtpm::get_quote(nonce).unwrap();
4555
// let der = ak_pub.key.try_to_der().unwrap();
4656
// let pub_key = PKey::public_key_from_der(&der).unwrap();
4757
// tpm_quote.verify(&pub_key, nonce).unwrap();
58+
59+
let quote_b64 = BASE64_STANDARD.encode(&td_quote_bytes);
60+
let runtime_b64 = BASE64_STANDARD.encode(hcl_var_data);
61+
62+
let body = TdxVmRequest {
63+
quote: quote_b64,
64+
runtimeData: Some(RuntimeData {
65+
data: runtime_b64,
66+
data_type: "Binary",
67+
}),
68+
nonce: Some("my-app-nonce-or-session-id".to_string()),
69+
};
70+
let body_bytes = serde_json::to_vec(&body).unwrap();
71+
let jwt_token = self.call_tdxvm_attestation(body_bytes).await;
4872
todo!()
4973
}
5074
}
75+
76+
impl MaaQuoteGenerator {
77+
/// Get a signed JWT from the azure API
78+
async fn call_tdxvm_attestation(
79+
&self,
80+
body_bytes: Vec<u8>,
81+
) -> Result<String, Box<dyn std::error::Error>> {
82+
let url = format!("{}/attest/TdxVm?api-version=2025-06-01", self.maa_endpoint);
83+
84+
let client = Client::new();
85+
let res = client
86+
.post(&url)
87+
.bearer_auth(&self.aad_access_token)
88+
.header("Content-Type", "application/json")
89+
.body(body_bytes)
90+
.send()
91+
.await?;
92+
93+
let status = res.status();
94+
let text = res.text().await?;
95+
96+
if !status.is_success() {
97+
return Err(format!("MAA attestation failed: {status} {text}").into());
98+
}
99+
100+
#[derive(serde::Deserialize)]
101+
struct AttestationResponse {
102+
token: String,
103+
}
104+
105+
let parsed: AttestationResponse = serde_json::from_str(&text)?;
106+
Ok(parsed.token) // Microsoft-signed JWT
107+
}
108+
}
109+
110+
#[derive(Serialize)]
111+
struct RuntimeData<'a> {
112+
data: String, // base64url of VarData bytes
113+
#[serde(rename = "dataType")]
114+
data_type: &'a str, // "Binary" in our case
115+
}
116+
117+
#[derive(Serialize)]
118+
struct TdxVmRequest<'a> {
119+
quote: String, // base64url(TDX quote)
120+
#[serde(skip_serializing_if = "Option::is_none")]
121+
runtimeData: Option<RuntimeData<'a>>,
122+
#[serde(skip_serializing_if = "Option::is_none")]
123+
nonce: Option<String>,
124+
}

0 commit comments

Comments
 (0)