diff --git a/README.md b/README.md index 324ebbd..f609861 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ snpguest verify **Usage** ```bash - snpguest verify attestation $CERTS_DIR $ATT_REPORT_PATH [-t, --tcb] [-s, --signature] + snpguest verify attestation $CERTS_DIR $ATT_REPORT_PATH [-t, --tcb] [-s, --signature] [-m, --measurement] [-d, --host-data] [-r, --report-data] ``` **Arguments** @@ -318,6 +318,9 @@ snpguest verify - `-t, --tcb`: Verify the Reported TCB section of the report only. - `-s, --signature`: Verify the signature of the report only. + - `-m, --measurement`: Verify the measurement from the attestation report. + - `-d, --host-data`: Verify the host-data from the attestation report. + - `-r, --report-data`: Verify the report-data from the attestation report. **Example** ```bash @@ -327,6 +330,12 @@ snpguest verify snpguest verify attestation ./certs attestation-report.bin --tcb # Verify Attestation Signature only snpguest verify attestation ./certs attestation-report.bin --signature + # Verify Attestation Measurement only + snpguest verify attestation --measurement 0xf28aac58964258d8ae0b2e88a706fc7afd0bb524f6a291ac3eedeccb73f89d7cfcf2e4fb6045e7d5201e41d1726afa02 /home/amd/certs /home/amd/report.bin + # Verify Attestation host-data only + snpguest verify attestation --host-data 0x7e4a3f9c1b82a056d39f0d44e5c8a7b1f02394de6b58ac0d7e3c11af0042bd59 /home/amd/certs /home/amd/report.bin + # Verify Attestation report-data only + snpguest verify attestation --report-data 0x5482c1ffe29145d47cf678f7681e3b64a89909d6cf8ec0104cfacb0b0418f005f564ad14f5c1381c99b74903a780ea340e887c9b445e9c760bf0b74115b26d45 /home/amd/certs /home/amd/report.bin ``` ### Global Options diff --git a/docs/snpguest.1.adoc b/docs/snpguest.1.adoc index 5a68c46..7ffcc34 100644 --- a/docs/snpguest.1.adoc +++ b/docs/snpguest.1.adoc @@ -120,12 +120,25 @@ COMMANDS An error will be raised if the attestation verification fails at any point. The user can use the [-t, --tcb] flag to only validate the tcb contents of the report. The user can use the [-s, --signature] flag to only validate the report signature. + The user can use the [-m, --measurement] flag to verify that the measurement in the attestation + report matches the expected measurement value (prefix with 0x for hex, without prefix it assumes + decimal values). + The user can use the [-d, --host-data] flag to verify that the host-data in the attestation + report matches the expected host-data value (prefix with 0x for hex, without prefix it assumes + decimal values). + The user can use the [-r, --report-data] flag to verify that the report-data in the attestation + report matches the expected report-data value (prefix with 0x for hex, without prefix it assumes + decimal values). + If the optional flags are not passed, just the signature will be verified. options: -h, --help show a help message -p, --processor_model Specify the processor model to use for verification -t, --tcb verify the tcb section of the report only -s, --signature verify the signature of the report only + -m, --measurement provide an expected measurement to verify the measurement field in the attestation report + -d, --host-data provide the expected host-data to verify the host-data field in the attestation report + -r, --report-data provide the expected report-data to verify the report-data in the attestation report *snpguest key*:: usage: snpguest key $KEY_PATH $ROOT_KEY_SELECT [-g, --guest_field_select] [-s, --guest_svn] [-t, --tcb_version] [-v, --vmpl] diff --git a/src/verify.rs b/src/verify.rs index bd972e6..0664105 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -16,6 +16,8 @@ use openssl::{ecdsa::EcdsaSig, sha::Sha384}; use sev::certs::snp::Chain; use sev::parser::ByteParser; +use hex::FromHex; + #[derive(Subcommand)] pub enum VerifyCmd { /// Verify the certificate chain. @@ -201,6 +203,18 @@ mod attestation { /// Run the Signature Verification Exclusively. #[arg(short, long, conflicts_with = "tcb")] pub signature: bool, + + /// Optional measurement string (hex, 48 bytes / 96 chars) + #[arg(short, long, value_name = "measurement")] + pub measurement: Option, + + /// Optional host_data string (hex, 32 bytes / 64 chars) + #[arg(short = 'd', long, value_name = "host_data")] + pub host_data: Option, + + /// Optional report_data string (hex, 64 bytes / 128 chars) + #[arg(short, long, value_name = "report_data")] + pub report_data: Option, } fn verify_attestation_signature( @@ -418,7 +432,7 @@ mod attestation { .context("Could not open attestation report")? }; - let proc_model = if let Some(proc_model) = args.processor_model { + let proc_model = if let Some(proc_model) = args.processor_model.clone() { proc_model } else { let att_report = report::read_report(args.att_report_path.clone()) @@ -447,6 +461,89 @@ mod attestation { verify_attestation_signature(vek, att_report, quiet)?; } + // Verify optional fields if provided + if args.measurement.is_some() || args.host_data.is_some() || args.report_data.is_some() { + verify_report_fields(&args, &att_report, quiet)?; + } + + Ok(()) + } + + fn decode_hex_or_decimal(input: &str) -> Result> { + // Look for "0x" at beginning. If it exists, treat as a hex. + if let Some(hex_str) = input.strip_prefix("0x") { + Ok(Vec::from_hex(hex_str)?) + } else { + Ok(input.as_bytes().to_vec()) + } + } + + fn verify_field( + field_name: &str, + expected: &[u8], + provided: &str, + expected_len: usize, + quiet: bool, + ) -> Result<()> { + let actual = decode_hex_or_decimal(provided)?; + + if actual.len() != expected_len { + return Err(anyhow::anyhow!( + "Expected {} characters for {}, got {}", + expected_len, + field_name, + actual.len() + )); + } + + if expected != actual.as_slice() { + return Err(anyhow::anyhow!( + "{} did not match:\n expected {}\n got {}", + field_name, + hex::encode(expected), + hex::encode(actual) + )); + } + if !quiet { + println!("{} verified successfully.", field_name); + } + + Ok(()) + } + + fn verify_report_fields( + args: &Args, + att_report: &AttestationReport, + quiet: bool, + ) -> Result<()> { + if let Some(measure) = &args.measurement { + verify_field( + "Measurement", + att_report.measurement.as_slice(), + measure, + 48, + quiet, + )?; + } + if let Some(host) = &args.host_data { + verify_field( + "Host Data", + att_report.host_data.as_slice(), + host, + 32, + quiet, + )?; + } + if let Some(report) = &args.report_data { + verify_field( + "Report Data", + att_report.report_data.as_slice(), + report, + 64, + quiet, + )?; + } + Ok(()) }