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

Add support az-tdx-vtpm tee #169

Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ chrono = "0.4.19"
clap = { version = "4", features = ["derive"] }
env_logger = "0.10.0"
hex = "0.4.3"
kbs-types = "0.5"
kbs-types = "0.5.3"
log = "0.4.17"
prost = "0.11.0"
rstest = "0.18.1"
Expand Down
2 changes: 1 addition & 1 deletion attestation-service/attestation-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ all-verifier = [ "verifier/all-verifier" ]
tdx-verifier = [ "verifier/tdx-verifier" ]
sgx-verifier = [ "verifier/sgx-verifier" ]
az-snp-vtpm-verifier = [ "verifier/az-snp-vtpm-verifier" ]
az-tdx-vtpm-verifier = [ "verifier/az-tdx-vtpm-verifier" ]
snp-verifier = [ "verifier/snp-verifier" ]
csv-verifier = [ "verifier/csv-verifier" ]
cca-verifier = [ "verifier/cca-verifier" ]
Expand Down Expand Up @@ -42,7 +43,6 @@ clap = { workspace = true, optional = true }
env_logger = { workspace = true, optional = true }
futures = "0.3.17"
hex.workspace = true
# TODO: change it to "0.5", once released.
kbs-types.workspace = true
lazy_static = "1.4.0"
log.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion attestation-service/verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ edition = "2021"

[features]
default = [ "all-verifier" ]
all-verifier = [ "tdx-verifier", "sgx-verifier", "snp-verifier", "az-snp-vtpm-verifier", "csv-verifier", "cca-verifier" ]
all-verifier = [ "tdx-verifier", "sgx-verifier", "snp-verifier", "az-snp-vtpm-verifier", "az-tdx-vtpm-verifier", "csv-verifier", "cca-verifier" ]
tdx-verifier = [ "eventlog-rs", "scroll", "sgx-dcap-quoteverify-rs" ]
sgx-verifier = [ "scroll", "sgx-dcap-quoteverify-rs" ]
az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ]
az-tdx-vtpm-verifier = [ "az-tdx-vtpm", "openssl", "tdx-verifier" ]
snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ]
csv-verifier = [ "openssl", "csv-rs", "codicon" ]
cca-verifier = [ "ear", "veraison-apiclient" ]
Expand All @@ -18,6 +19,7 @@ anyhow.workspace = true
asn1-rs = { version = "0.5.1", optional = true }
async-trait.workspace = true
az-snp-vtpm = { version = "0.4", default-features = false, features = ["verifier"], optional = true }
az-tdx-vtpm = { version = "0.4.1", default-features = false, features = ["verifier"], optional = true }
base64 = "0.21"
bincode = "1.3.3"
byteorder = "1"
Expand Down
12 changes: 6 additions & 6 deletions attestation-service/verifier/src/az_snp_vtpm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ fn verify_snp_report(
mod tests {
use super::*;

const REPORT: &[u8; 2048] = include_bytes!("../../test_data/az-hcl-data.bin");
const SIGNATURE: &[u8; 256] = include_bytes!("../../test_data/az-vtpm-quote-sig.bin");
const MESSAGE: &[u8; 122] = include_bytes!("../../test_data/az-vtpm-quote-msg.bin");
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_DATA: &[u8] = "challenge".as_bytes();

#[test]
fn test_verify_snp_report() {
let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
let snp_report = hcl_report.try_into().unwrap();
let vcek = Vcek::from_pem(include_str!("../../test_data/az-vcek.pem")).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();
}
Expand All @@ -146,7 +146,7 @@ mod tests {
wrong_report[0x00b0] = 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-vcek.pem")).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();
}
Expand Down Expand Up @@ -197,7 +197,7 @@ mod tests {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let report = include_bytes!("../../test_data/az-hcl-data.bin");
let report = include_bytes!("../../test_data/az-snp-vtpm/hcl-report.bin");
let hcl_report = HclReport::new(report.to_vec()).unwrap();
let mut report_data = REPORT_DATA.to_vec();
report_data.reverse();
Expand Down
162 changes: 162 additions & 0 deletions attestation-service/verifier/src/az_tdx_vtpm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) Microsoft Corporation.
//
// SPDX-License-Identifier: Apache-2.0
//

use super::tdx::claims::generate_parsed_claim;
use super::tdx::quote::{ecdsa_quote_verification, parse_tdx_quote, Quote as TdQuote};
use super::{TeeEvidenceParsedClaim, Verifier};
use crate::{InitDataHash, ReportData};
use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use az_tdx_vtpm::hcl::HclReport;
use az_tdx_vtpm::vtpm::Quote as TpmQuote;
use log::{debug, warn};
use openssl::pkey::PKey;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Evidence {
tpm_quote: TpmQuote,
hcl_report: Vec<u8>,
td_quote: Vec<u8>,
}

#[derive(Default)]
pub struct AzTdxVtpm;

#[async_trait]
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
async fn evaluate(
&self,
evidence: &[u8],
expected_report_data: &ReportData,
expected_init_data_hash: &InitDataHash,
) -> Result<TeeEvidenceParsedClaim> {
let ReportData::Value(expected_report_data) = expected_report_data else {
mkulke marked this conversation as resolved.
Show resolved Hide resolved
bail!("unexpected empty report data");
};

if let InitDataHash::Value(_) = expected_init_data_hash {
warn!("Azure TDX vTPM verifier does not support verify init data hash, will ignore the input `init_data_hash`");
}

let evidence = serde_json::from_slice::<Evidence>(evidence)
.context("Failed to deserialize Azure vTPM TDX evidence")?;

let hcl_report = HclReport::new(evidence.hcl_report)?;
verify_tpm_signature(&evidence.tpm_quote, &hcl_report)?;

verify_tpm_nonce(&evidence.tpm_quote, expected_report_data)?;

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

verify_hcl_var_data(&hcl_report, &td_quote)?;

let claim = generate_parsed_claim(td_quote, None)?;
mkulke marked this conversation as resolved.
Show resolved Hide resolved
Ok(claim)
}
}

fn verify_hcl_var_data(hcl_report: &HclReport, td_quote: &TdQuote) -> Result<()> {
let var_data_hash = hcl_report.var_data_sha256();
if var_data_hash != td_quote.report_body.report_data[..32] {
bail!("TDX Quote report data mismatch");
}
debug!("Report data verification completed successfully.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the debug check point here

Ok(())
}

fn verify_tpm_signature(quote: &TpmQuote, hcl_report: &HclReport) -> Result<()> {
let ak_pub = hcl_report.ak_pub().context("Failed to get AKpub")?;
let der = ak_pub.key.try_to_der()?;
let ak_pub = PKey::public_key_from_der(&der).context("Failed to parse AKpub")?;

quote
.verify_signature(&ak_pub)
.context("Failed to verify vTPM quote")?;
Ok(())
}

fn verify_tpm_nonce(quote: &TpmQuote, report_data: &[u8]) -> Result<()> {
let nonce = quote.nonce()?;
if nonce != report_data[..] {
bail!("TPM quote nonce doesn't match expected report_data");
}
debug!("TPM report_data verification completed successfully");
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

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 TD_QUOTE: &[u8; 5006] = include_bytes!("../../test_data/az-tdx-vtpm/td-quote.bin");

#[test]
fn test_verify_hcl_var_data() {
let hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
let td_quote = parse_tdx_quote(TD_QUOTE).unwrap();
verify_hcl_var_data(&hcl_report, &td_quote).unwrap();
}

#[test]
fn test_verify_hcl_var_data_failure() {
let mut wrong_report = REPORT.clone();
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();
}

#[test]
fn test_verify_tpm_signature() {
let quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
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 hcl_report = HclReport::new(REPORT.to_vec()).unwrap();
verify_tpm_signature(&wrong_quote, &hcl_report).unwrap_err();
}

#[test]
fn test_verify_tpm_nonce() {
let quote = TpmQuote {
signature: SIGNATURE.to_vec(),
message: MESSAGE.to_vec(),
};
let nonce = "tdx 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 wrong_nonce = "wrong".as_bytes();
verify_tpm_nonce(&quote, wrong_nonce).unwrap_err();
}
}
13 changes: 12 additions & 1 deletion attestation-service/verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub mod sample;
#[cfg(feature = "az-snp-vtpm-verifier")]
pub mod az_snp_vtpm;

#[cfg(feature = "az-tdx-vtpm-verifier")]
pub mod az_tdx_vtpm;

#[cfg(feature = "snp-verifier")]
pub mod snp;

Expand Down Expand Up @@ -38,6 +41,15 @@ pub fn to_verifier(tee: &Tee) -> Result<Box<dyn Verifier + Send + Sync>> {
}
}
}
Tee::AzTdxVtpm => {
cfg_if::cfg_if! {
if #[cfg(feature = "az-tdx-vtpm-verifier")] {
Ok(Box::<az_tdx_vtpm::AzTdxVtpm>::default() as Box<dyn Verifier + Send + Sync>)
} else {
bail!("feature `az-tdx-vtpm-verifier` is not enabled for `verifier` crate.");
}
}
}
Tee::Tdx => {
cfg_if::cfg_if! {
if #[cfg(feature = "tdx-verifier")] {
Expand Down Expand Up @@ -87,7 +99,6 @@ pub fn to_verifier(tee: &Tee) -> Result<Box<dyn Verifier + Send + Sync>> {
}
}
}
Tee::AzTdxVtpm => todo!(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions attestation-service/verifier/src/tdx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use eventlog::{CcEventLog, Rtmr};
use quote::{ecdsa_quote_verification, parse_tdx_quote};
use serde::{Deserialize, Serialize};

mod claims;
pub(crate) mod claims;
mkulke marked this conversation as resolved.
Show resolved Hide resolved
mod eventlog;
mod quote;
pub(crate) mod quote;

#[derive(Serialize, Deserialize, Debug)]
struct TdxEvidence {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion kbs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ This project relies on the [Attestation-Service (AS)](https://github.com/confide
The following TEE platforms are currently supported:

- AMD SEV-SNP
- Azure SNP vTPM
- Azure SEV-SNP vTPM
- Azure TDX vTPM
- Intel SGX
- Intel TDX

Expand Down
2 changes: 1 addition & 1 deletion kbs/src/api/src/attestation/coco/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ pub const COCO_AS_HASH_ALGORITHM: &str = "sha384";
fn to_grpc_tee(tee: Tee) -> GrpcTee {
match tee {
Tee::AzSnpVtpm => GrpcTee::AzSnpVtpm,
Tee::AzTdxVtpm => GrpcTee::AzTdxVtpm,
Tee::Cca => GrpcTee::Cca,
Tee::Csv => GrpcTee::Csv,
Tee::Sample => GrpcTee::Sample,
Tee::Sev => GrpcTee::Sev,
Tee::Sgx => GrpcTee::Sgx,
Tee::Snp => GrpcTee::Snp,
Tee::Tdx => GrpcTee::Tdx,
Tee::AzTdxVtpm => GrpcTee::AzTdxVtpm,
}
}

Expand Down
1 change: 1 addition & 0 deletions kbs/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ install-dependencies:
sudo apt-get install -y \
build-essential \
clang \
libsgx-dcap-default-qpl \
libsgx-dcap-quote-verify-dev \
libtdx-attest-dev \
libtss2-dev \
Expand Down
Loading