Skip to content

Commit b5ff02f

Browse files
authored
Merge pull request #138 from flashbots/peg/fix-azure-attestation-verification
Fix input data checks for azure attestation
2 parents 16b4269 + 1965fb9 commit b5ff02f

1 file changed

Lines changed: 62 additions & 4 deletions

File tree

  • attested-tls/src/attestation/azure

attested-tls/src/attestation/azure/mod.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ async fn verify_azure_attestation_with_given_timestamp(
137137
let hcl_ak_pub = hcl_report.ak_pub()?;
138138

139139
// Get attestation key from runtime claims
140-
let ak_from_claims = {
140+
let (ak_from_claims, user_data_input) = {
141141
let runtime_data_raw = hcl_report.var_data();
142142
let claims: HclRuntimeClaims = serde_json::from_slice(runtime_data_raw)?;
143143

@@ -147,14 +147,26 @@ async fn verify_azure_attestation_with_given_timestamp(
147147
.find(|k| k.kid == "HCLAkPub")
148148
.ok_or(MaaError::ClaimsMissingHCLAkPub)?;
149149

150-
RsaPubKey::from_jwk(ak_jwk)?
150+
let user_data = claims
151+
.user_data
152+
.as_deref()
153+
.ok_or(MaaError::ClaimsMissingUserData)?;
154+
let user_data_bytes = hex::decode(user_data)?;
155+
let user_data_input: [u8; 64] = user_data_bytes
156+
.try_into()
157+
.map_err(|_| MaaError::ClaimsUserDataBadLength)?;
158+
159+
(RsaPubKey::from_jwk(ak_jwk)?, user_data_input)
151160
};
152161

153162
// Check that the TD report input data matches the HCL var data hash
154163
let td_report: az_tdx_vtpm::tdx::TdReport = hcl_report.try_into()?;
155164
if var_data_hash != td_report.report_mac.reportdata[..32] {
156165
return Err(MaaError::TdReportInputMismatch);
157166
}
167+
if user_data_input != expected_input_data {
168+
return Err(MaaError::ClaimsUserDataInputMismatch);
169+
}
158170

159171
// Verify the vTPM quote
160172
let vtpm_quote = attestation_document.tpm_attestation.quote;
@@ -217,9 +229,8 @@ struct HclRuntimeClaims {
217229
#[allow(unused)]
218230
#[serde(rename = "vm-configuration")]
219231
vm_config: Option<serde_json::Value>,
220-
#[allow(unused)]
221232
#[serde(rename = "user-data")]
222-
user_data: Option<serde_json::Value>,
233+
user_data: Option<String>,
223234
}
224235

225236
/// This is only used as a common type to compare public keys with different formats
@@ -295,6 +306,8 @@ pub enum MaaError {
295306
TdReportInputMismatch,
296307
#[error("Base64 decode: {0}")]
297308
Base64(#[from] base64::DecodeError),
309+
#[error("Hex decode: {0}")]
310+
Hex(#[from] hex::FromHexError),
298311
#[error("Attestation Key from HCL runtime claims does not match that from HCL report")]
299312
AkFromClaimsNotEqualAkFromHcl,
300313
#[error("Attestation Key from HCL runtime claims does not match that from attestation key certificate")]
@@ -317,6 +330,12 @@ pub enum MaaError {
317330
JwkParse,
318331
#[error("HCL runtime claims is missing HCLAkPub field")]
319332
ClaimsMissingHCLAkPub,
333+
#[error("HCL runtime claims is missing user-data field")]
334+
ClaimsMissingUserData,
335+
#[error("HCL runtime claims user-data must decode to exactly 64 bytes")]
336+
ClaimsUserDataBadLength,
337+
#[error("HCL runtime claims user-data does not match expected report input data")]
338+
ClaimsUserDataInputMismatch,
320339
#[error("DCAP verification: {0}")]
321340
DcapVerification(#[from] crate::attestation::dcap::DcapVerificationError),
322341
}
@@ -327,6 +346,18 @@ mod tests {
327346

328347
use super::*;
329348

349+
fn input_data_from_attestation(attestation_bytes: &[u8]) -> [u8; 64] {
350+
let attestation_document: AttestationDocument =
351+
serde_json::from_slice(attestation_bytes).unwrap();
352+
let hcl_report_bytes = BASE64_URL_SAFE
353+
.decode(attestation_document.hcl_report_base64)
354+
.unwrap();
355+
let hcl_report = hcl::HclReport::new(hcl_report_bytes).unwrap();
356+
let claims: HclRuntimeClaims = serde_json::from_slice(hcl_report.var_data()).unwrap();
357+
let user_data_hex = claims.user_data.unwrap();
358+
hex::decode(user_data_hex).unwrap().try_into().unwrap()
359+
}
360+
330361
#[tokio::test]
331362
async fn test_decode_hcl() {
332363
// From cvm-reverse-proxy/internal/attestation/azure/tdx/testdata/hclreport.bin
@@ -395,4 +426,31 @@ mod tests {
395426

396427
measurement_policy.check_measurement(&measurements).unwrap();
397428
}
429+
430+
#[tokio::test]
431+
async fn test_verify_fails_on_input_mismatch() {
432+
let attestation_bytes: &'static [u8] =
433+
include_bytes!("../../../test-assets/azure-tdx-1764662251380464271");
434+
let now = 1771423480;
435+
436+
let mut expected_input_data = input_data_from_attestation(attestation_bytes);
437+
expected_input_data[63] ^= 0x01;
438+
439+
let collateral_bytes: &'static [u8] =
440+
include_bytes!("../../../test-assets/azure-collateral02.json");
441+
let collateral = serde_json::from_slice(collateral_bytes).unwrap();
442+
443+
let err = verify_azure_attestation_with_given_timestamp(
444+
attestation_bytes.to_vec(),
445+
expected_input_data,
446+
None,
447+
Some(collateral),
448+
now,
449+
false,
450+
)
451+
.await
452+
.unwrap_err();
453+
454+
assert!(matches!(err, MaaError::ClaimsUserDataInputMismatch));
455+
}
398456
}

0 commit comments

Comments
 (0)