Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update az snp / tdx vtpm dependency to 0.5 #293

Merged
merged 4 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
473 changes: 182 additions & 291 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions attestation-service/verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ cca-verifier = [ "ear", "veraison-apiclient" ]
anyhow.workspace = true
asn1-rs = { version = "0.5.1", optional = true }
async-trait.workspace = true
az-snp-vtpm = { version = "0.4.1", default-features = false, features = ["verifier"], optional = true }
az-tdx-vtpm = { version = "0.4.1", default-features = false, features = ["verifier"], optional = true }
az-snp-vtpm = { version = "0.5.1", default-features = false, features = ["verifier"], optional = true }
az-tdx-vtpm = { version = "0.5.1", default-features = false, features = ["verifier"], optional = true }
base64 = "0.21"
bincode = "1.3.3"
byteorder = "1"
Expand Down
107 changes: 74 additions & 33 deletions attestation-service/verifier/src/az_snp_vtpm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ impl Verifier for AzSnpVtpm {
/// The following verification steps are performed:
/// 1. TPM Quote has been signed by AK included in the HCL variable data
/// 2. Attestation report_data matches TPM Quote nonce
/// 3. SNP report's report_data field matches hashed HCL variable data
/// 4. SNP Report is genuine
/// 5. SNP Report has been issued in VMPL 0
/// 3. TPM PCRs' digest matches the digest in the Quote
/// 4. SNP report's report_data field matches hashed HCL variable data
/// 5. SNP Report is genuine
/// 6. SNP Report has been issued in VMPL 0
async fn evaluate(
&self,
evidence: &[u8],
Expand All @@ -73,6 +74,8 @@ impl Verifier for AzSnpVtpm {

verify_nonce(&evidence.quote, expected_report_data)?;

verify_pcrs(&evidence.quote)?;

let var_data_hash = hcl_report.var_data_sha256();
let snp_report = hcl_report.try_into()?;
verify_report_data(&var_data_hash, &snp_report)?;
Expand Down Expand Up @@ -106,6 +109,14 @@ fn verify_signature(quote: &Quote, hcl_report: &HclReport) -> Result<()> {
Ok(())
}

fn verify_pcrs(quote: &Quote) -> Result<()> {
quote
.verify_pcrs()
.context("Digest of PCRs does not match digest in Quote")?;
debug!("PCR verification completed successfully");
Ok(())
}

fn verify_report_data(var_data_hash: &[u8; 32], snp_report: &AttestationReport) -> Result<()> {
if *var_data_hash != snp_report.report_data[..32] {
bail!("SNP report report_data mismatch");
Expand Down Expand Up @@ -133,10 +144,10 @@ fn verify_snp_report(
#[cfg(test)]
mod tests {
use super::*;
use az_snp_vtpm::vtpm::VerifyError;

const REPORT: &[u8; 2048] = include_bytes!("../../test_data/az-snp-vtpm/hcl-report.bin");
const SIGNATURE: &[u8; 256] = include_bytes!("../../test_data/az-snp-vtpm/tpm-quote.sig");
const MESSAGE: &[u8; 122] = include_bytes!("../../test_data/az-snp-vtpm/tpm-quote.msg");
const REPORT: &[u8; 2600] = include_bytes!("../../test_data/az-snp-vtpm/hcl-report.bin");
const QUOTE: &[u8; 1362] = include_bytes!("../../test_data/az-snp-vtpm/quote.bin");
const REPORT_DATA: &[u8] = "challenge".as_bytes();

#[test]
Expand All @@ -152,12 +163,17 @@ mod tests {
fn test_verify_snp_report_failure() {
let mut wrong_report = REPORT.clone();
// messing with snp report
wrong_report[0x00b0] = 0;
wrong_report[0x01a6] = 0;
let hcl_report = HclReport::new(wrong_report.to_vec()).unwrap();
let snp_report = hcl_report.try_into().unwrap();
let vcek = Vcek::from_pem(include_str!("../../test_data/az-snp-vtpm/vcek.pem")).unwrap();
let vendor_certs = load_milan_cert_chain().as_ref().unwrap();
verify_snp_report(&snp_report, &vcek, vendor_certs).unwrap_err();
assert_eq!(
verify_snp_report(&snp_report, &vcek, vendor_certs)
.unwrap_err()
.to_string(),
"SNP version mismatch",
);
}

#[test]
Expand All @@ -175,61 +191,86 @@ mod tests {
let hcl_report = HclReport::new(wrong_report.to_vec()).unwrap();
let var_data_hash = hcl_report.var_data_sha256();
let snp_report = hcl_report.try_into().unwrap();
verify_report_data(&var_data_hash, &snp_report).unwrap_err();
assert_eq!(
verify_report_data(&var_data_hash, &snp_report)
.unwrap_err()
.to_string(),
"SNP report report_data mismatch"
);
}

#[test]
fn test_verify_signature() {
let quote = Quote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
verify_signature(&quote, &hcl_report).unwrap();
}

#[test]
fn test_verify_quote_signature_failure() {
let mut wrong_message = MESSAGE.clone();
wrong_message.reverse();
let wrong_quote = Quote {
signature: SIGNATURE.to_vec(),
message: wrong_message.to_vec(),
};
let mut quote = QUOTE.clone();
quote[0x030] = 0;
let wrong_quote: Quote = bincode::deserialize(&quote).unwrap();

let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
verify_signature(&wrong_quote, &hcl_report).unwrap_err();
assert_eq!(
verify_signature(&wrong_quote, &hcl_report)
.unwrap_err()
.downcast_ref::<VerifyError>()
.unwrap()
.to_string(),
VerifyError::SignatureMismatch.to_string()
);
}

#[test]
fn test_verify_akpub_failure() {
let quote = Quote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let mut wrong_report = REPORT.clone();
// messing with AKpub in var data
wrong_report[0x0540] = 0;
let wrong_hcl_report = HclReport::new(wrong_report.to_vec()).unwrap();
verify_signature(&quote, &wrong_hcl_report).unwrap_err();
assert_eq!(
verify_signature(&quote, &wrong_hcl_report)
.unwrap_err()
.to_string(),
"Failed to get AKpub",
);
}

#[test]
fn test_verify_quote_nonce() {
let quote = Quote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
verify_nonce(&quote, &REPORT_DATA).unwrap();
}

#[test]
fn test_verify_quote_nonce_failure() {
let quote = Quote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let mut wrong_report_data = REPORT_DATA.to_vec();
wrong_report_data.reverse();
verify_nonce(&quote, &wrong_report_data).unwrap_err();
}

#[test]
fn test_verify_pcrs() {
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
verify_pcrs(&quote).unwrap();
}

#[test]
fn test_verify_pcrs_failure() {
let mut quote = QUOTE.clone();
quote[0x0169] = 0;
let wrong_quote: Quote = bincode::deserialize(&quote).unwrap();

assert_eq!(
verify_pcrs(&wrong_quote)
.unwrap_err()
.downcast_ref::<VerifyError>()
.unwrap()
.to_string(),
VerifyError::PcrMismatch.to_string()
);
}
}
85 changes: 60 additions & 25 deletions attestation-service/verifier/src/az_tdx_vtpm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ impl Verifier for AzTdxVtpm {
/// The following verification steps are performed:
/// 1. TPM Quote has been signed by AK included in the HCL variable data
/// 2. Attestation nonce matches TPM Quote nonce
/// 3. TD Quote is genuine
/// 4. TD Report's report_data field matches hashed HCL variable data
/// 3. TPM PCRs' digest matches the digest in the Quote
/// 4. TD Quote is genuine
/// 5. TD Report's report_data field matches hashed HCL variable data
async fn evaluate(
&self,
evidence: &[u8],
Expand All @@ -54,6 +55,8 @@ impl Verifier for AzTdxVtpm {

verify_tpm_nonce(&evidence.tpm_quote, expected_report_data)?;

verify_pcrs(&evidence.tpm_quote)?;

ecdsa_quote_verification(&evidence.td_quote).await?;
let td_quote = parse_tdx_quote(&evidence.td_quote)?;

Expand Down Expand Up @@ -84,6 +87,14 @@ fn verify_tpm_signature(quote: &TpmQuote, hcl_report: &HclReport) -> Result<()>
Ok(())
}

fn verify_pcrs(quote: &TpmQuote) -> Result<()> {
quote
.verify_pcrs()
.context("Digest of PCRs does not match digest in Quote")?;
debug!("PCR verification completed successfully");
Ok(())
}

fn verify_tpm_nonce(quote: &TpmQuote, report_data: &[u8]) -> Result<()> {
let nonce = quote.nonce()?;
if nonce != report_data[..] {
Expand All @@ -96,10 +107,11 @@ fn verify_tpm_nonce(quote: &TpmQuote, report_data: &[u8]) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use az_tdx_vtpm::vtpm::Quote;
use az_tdx_vtpm::vtpm::VerifyError;

const REPORT: &[u8; 2600] = include_bytes!("../../test_data/az-tdx-vtpm/hcl-report.bin");
const SIGNATURE: &[u8; 256] = include_bytes!("../../test_data/az-tdx-vtpm/tpm-quote.sig");
const MESSAGE: &[u8; 126] = include_bytes!("../../test_data/az-tdx-vtpm/tpm-quote.msg");
const QUOTE: &[u8; 1362] = include_bytes!("../../test_data/az-tdx-vtpm/quote.bin");
const TD_QUOTE: &[u8; 5006] = include_bytes!("../../test_data/az-tdx-vtpm/td-quote.bin");

#[test]
Expand All @@ -115,48 +127,71 @@ mod tests {
wrong_report[0x0880] += 1;
let hcl_report = HclReport::new(wrong_report.to_vec()).unwrap();
let td_quote = parse_tdx_quote(TD_QUOTE).unwrap();
verify_hcl_var_data(&hcl_report, &td_quote).unwrap_err();
assert_eq!(
verify_hcl_var_data(&hcl_report, &td_quote)
.unwrap_err()
.to_string(),
"TDX Quote report data mismatch"
);
}

#[test]
fn test_verify_tpm_signature() {
let quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
verify_tpm_signature(&quote, &hcl_report).unwrap();
}

#[test]
fn test_verify_tpm_signature_failure() {
let mut wrong_message = MESSAGE.clone();
wrong_message.reverse();
let wrong_quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: wrong_message.to_vec(),
};
let mut quote = QUOTE.clone();
quote[0x020] = 0;
let wrong_quote: Quote = bincode::deserialize(&quote).unwrap();

let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
verify_tpm_signature(&wrong_quote, &hcl_report).unwrap_err();
assert_eq!(
verify_tpm_signature(&wrong_quote, &hcl_report)
.unwrap_err()
.downcast_ref::<VerifyError>()
.unwrap()
.to_string(),
VerifyError::SignatureMismatch.to_string()
);
}

#[test]
fn test_verify_tpm_nonce() {
let quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let nonce = "tdx challenge".as_bytes();
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let nonce = "challenge".as_bytes();
verify_tpm_nonce(&quote, nonce).unwrap();
}

#[test]
fn test_verify_tpm_nonce_failure() {
let quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
let wrong_nonce = "wrong".as_bytes();
verify_tpm_nonce(&quote, wrong_nonce).unwrap_err();
}

#[test]
fn test_verify_pcrs() {
let quote: Quote = bincode::deserialize(QUOTE).unwrap();
verify_pcrs(&quote).unwrap();
}

#[test]
fn test_verify_pcrs_failure() {
let mut quote = QUOTE.clone();
quote[0x0169] = 0;
let wrong_quote: Quote = bincode::deserialize(&quote).unwrap();

assert_eq!(
verify_pcrs(&wrong_quote)
.unwrap_err()
.downcast_ref::<VerifyError>()
.unwrap()
.to_string(),
VerifyError::PcrMismatch.to_string()
);
}
}
8 changes: 4 additions & 4 deletions attestation-service/verifier/src/snp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,19 @@ pub(crate) fn verify_report_signature(
// tcb version
// these integer extensions are 3 bytes with the last byte as the data
if get_oid_int(&parsed_vcek, UCODE_SPL_OID)? != report.reported_tcb.microcode {
return Err(anyhow!("Microcode verion mismatch"));
return Err(anyhow!("Microcode version mismatch"));
}

if get_oid_int(&parsed_vcek, SNP_SPL_OID)? != report.reported_tcb.snp {
return Err(anyhow!("SNP verion mismatch"));
return Err(anyhow!("SNP version mismatch"));
}

if get_oid_int(&parsed_vcek, TEE_SPL_OID)? != report.reported_tcb.tee {
return Err(anyhow!("TEE verion mismatch"));
return Err(anyhow!("TEE version mismatch"));
}

if get_oid_int(&parsed_vcek, LOADER_SPL_OID)? != report.reported_tcb.bootloader {
return Err(anyhow!("Boot loader verion mismatch"));
return Err(anyhow!("Boot loader version mismatch"));
}

// verify report signature
Expand Down
Binary file modified attestation-service/verifier/test_data/az-snp-vtpm/hcl-report.bin
Binary file not shown.
Binary file not shown.
Loading
Loading