Skip to content
Open
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
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,12 @@ jobs:
- name: Check azure-verifier builds without the TPM stack
run: cargo check -p attestation --no-default-features --features azure-verifier

# `azure-attester` carries its generation dependencies per-target, so off
# x86_64 linux it has to stay enabled and contribute nothing rather than
# fail to build. macos-latest is aarch64-darwin, the target
# tss-esapi-sys has no bindings for.
- name: Check azure-attester builds off x86_64 linux
run: cargo check -p attestation --all-features

- name: Test azure-verifier
run: cargo test -p attestation --no-default-features --features azure-verifier
24 changes: 18 additions & 6 deletions crates/attestation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,23 @@ ureq = "2.12.1"
webpki = { package = "rustls-webpki", version = "0.103.8" }
x509-parser = "0.18.0"

# Used for azure vTPM attestation support. minimal cvm crate for verification
# and full tdx crate for generation (linux only because links tpm2-tss).
# Needed for verification of azure vTPM evidence. No features on purpose since az-cvm-vtpm's
# verifier feature implies its `tpm` one, which pulls tss-esapi and the native TPM libraries.
# See https://github.com/kinvolk/azure-cvm-tooling/issues/95
az-cvm-vtpm = { version = "0.7.4", default-features = false, optional = true }
openssl = { version = "0.10.79", optional = true }

[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
# Needed for generation of azure vTPM evidence, which reads the vTPM through tss-esapi and
# attests Intel TDX. An Azure TDX CVM is x86_64 linux, so the generation code is only
# meaningful there, and neither of these builds elsewhere in any case.
# Declaring them per-target rather than gating on the feature alone keeps `azure-attester`
# (and so --all-features) resolvable on every platform; off this target the feature stays
# enabled but contributes nothing, as the crate readme describes. build.rs derives the matching
# `azure_attester_x86_64_linux` cfg, and must keep the same condition as this table.
# See https://github.com/virtee/sev/issues/369
az-tdx-vtpm = { version = "0.7.4", optional = true }
tss-esapi = { version = "7.6.0", optional = true }
openssl = { version = "0.10.79", optional = true }

[dev-dependencies]
mock-tdx = { workspace = true }
Expand All @@ -58,9 +69,10 @@ serde_json = "1.0.145"
[features]
default = []

# Generation of Microsoft Azure attestation evidence on an Azure TDX CVM.
# Implies `azure-verifier`. Reads the vTPM through tss-esapi, so it builds
# only where the native tpm2-tss libraries are available (Linux).
# Opts into generating Azure evidence, and so into the native tpm2-tss stack
# that tss-esapi links. Code should be gated on the `azure_attester_x86_64_linux` cfg
# injected by build.rs rather than on this feature, which says nothing about the target.
# The crate readme covers what enabling it does off x86_64 linux.
azure-attester = ["azure-verifier", "dep:az-tdx-vtpm", "dep:tss-esapi"]

# Verification of Microsoft Azure attestation evidence. Pure computation
Expand Down
16 changes: 13 additions & 3 deletions crates/attestation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,19 @@ attestation support.
This feature requires [tpm2](https://tpm2-software.github.io) and `openssl` to
be installed. On Debian-based systems tpm2 is provided by
[`libtss2-dev`](https://packages.debian.org/trixie/libtss2-dev), and on nix
`tpm2-tss`. This dependency is currently not packaged for MacOS, meaning
currently it is not possible to compile or run with the `azure-attester`
feature on MacOS.
`tpm2-tss`.

The generation code is compiled on x86_64 Linux targets only — the platform
an Azure TDX CVM actually runs on, and the only one where the vTPM and the
native TPM stack exist. **On every other target, including MacOS and
aarch64 Linux, `azure-attester` stays enabled but contributes nothing.**
Cargo still reports the feature as on; what changes is that its generation
dependencies are target-gated out of the dependency graph and the
generation code is not compiled, leaving the same compiled surface as
`azure-verifier` on its own. So the crate builds (`--all-features` works
everywhere), but `AttestationType::detect` will not report `AzureTdx` and
generating Azure evidence fails with `AttestationTypeNotSupported`. Verifying
Azure evidence is unaffected. If you need generation, build on x86_64 Linux.

**Note:** Azure support is currently **not actively maintained** as we do not
have production CVMs deployed on Azure and so are unlikely to notice when this
Expand Down
16 changes: 16 additions & 0 deletions crates/attestation/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ const FIRMWARE_DIR: &str = "assets/ovmf";
const GENERATED_FIRMWARE: &str = "trusted-firmware.json";

fn main() {
// Gate for the Azure evidence generation code. It takes a cfg rather
// than the `azure-attester` feature alone because it only compiles
// where az-tdx-vtpm and tss-esapi resolve, so this condition has to
// stay identical to their target table in Cargo.toml. The
// CARGO_CFG_TARGET_* vars describe the target rather than the build
// host, which keeps the two in agreement when cross-compiling. The
// check-cfg goes outside the branch: the name is expected on every
// target, including those where the code it gates is switched off.
println!("cargo::rustc-check-cfg=cfg(azure_attester_x86_64_linux)");
if env::var_os("CARGO_FEATURE_AZURE_ATTESTER").is_some() &&
env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") &&
env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("x86_64")
{
println!("cargo::rustc-cfg=azure_attester_x86_64_linux");
}

println!("cargo:rerun-if-changed={FIRMWARE_DIR}");

let mut paths = fs::read_dir(FIRMWARE_DIR)
Expand Down
26 changes: 13 additions & 13 deletions crates/attestation/src/azure/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! Microsoft Azure vTPM attestation evidence generation and verification
mod ak_certificate;
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
mod attester;
mod tpm_quote;
mod tpms_attest;
mod verify;

#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
pub use attester::{create_azure_attestation, detect_azure_cvm};
use az_cvm_vtpm::hcl;
use openssl::error::ErrorStack;
Expand Down Expand Up @@ -165,39 +165,39 @@ pub enum MaaError {
DcapVerification(#[from] crate::dcap::DcapVerificationError),

// Errors that can only occur during evidence generation on an Azure CVM
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("Report: {0}")]
Report(#[from] az_tdx_vtpm::report::ReportError),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("IMDS: {0}")]
Imds(#[from] az_tdx_vtpm::imds::ImdsError),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("vTPM report: {0}")]
VtpmReport(#[from] az_tdx_vtpm::vtpm::ReportError),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("vTPM quote: {0}")]
VtpmQuote(#[from] az_tdx_vtpm::vtpm::QuoteError),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("vTPM read: {0}")]
TssEsapi(#[from] tss_esapi::Error),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("IO: {0}")]
Io(#[from] std::io::Error),
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("AIA URL is not HTTP(S): {url}")]
UnsupportedAiaUrl { url: String },
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("Failed to fetch AIA issuer certificate from {url}: {source}")]
AiaFetch { url: String, source: Box<ureq::Error> },
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error(
"Azure vTPM AK issuer chain exceeded maximum intermediate certificate count: {max_depth}"
)]
AkIssuerChainTooDeep { max_depth: usize },
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error("Azure vTPM AK issuer chain could not be built to a pinned Azure root certificate")]
AkIssuerChainIncomplete,
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
#[error(
"Azure metadata API returned a successful response with non-JSON content-type: {content_type:?}"
)]
Expand Down
8 changes: 5 additions & 3 deletions crates/attestation/src/azure/tpm_quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
//! extraction done by the `tpms_attest` parser instead of tss-esapi. It is
//! vendored because az-cvm-vtpm's verifier feature currently requires its
//! TPM device support (tss-esapi links the native tpm2-tss libraries,
//! making such builds Linux-only). If upstream decouples verification
//! from the TPM stack, this module can be retired in favour of depending
//! on az-cvm-vtpm's verifier again.
//! making such builds Linux-only). Tracked upstream as
//! <https://github.com/kinvolk/azure-cvm-tooling/issues/95>; if upstream
//! decouples verification from the TPM stack, this module and
//! [`super::tpms_attest`] can be retired in favour of depending on
//! az-cvm-vtpm's verifier again.

use openssl::{
hash::MessageDigest,
Expand Down
11 changes: 6 additions & 5 deletions crates/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

// `azure-verifier` is the base Azure feature: it gates the whole module,
// and `azure-attester` (which implies it) additionally enables the
// generation code inside.
// generation code inside, on the x86_64 linux targets where the vTPM it
// reads exists.
#[cfg(feature = "azure-verifier")]
pub mod azure;
pub mod dcap;
Expand Down Expand Up @@ -171,7 +172,7 @@ impl AttestationType {
/// Detect what platform we are on by attempting an attestation
pub fn detect() -> Result<Self, AttestationError> {
// First attempt azure, if the feature is present
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
{
if azure::detect_azure_cvm()? {
return Ok(AttestationType::AzureTdx);
Expand Down Expand Up @@ -282,7 +283,7 @@ impl AttestationGenerator {
match self.attestation_type {
AttestationType::None => Ok(AttestationExchangeMessage::without_attestation()),
AttestationType::AzureTdx => {
#[cfg(feature = "azure-attester")]
#[cfg(azure_attester_x86_64_linux)]
{
let platform = attest_measure::platform::metadata_for(
self.attestation_type.try_into()?,
Expand All @@ -294,10 +295,10 @@ impl AttestationGenerator {
}),
})
}
#[cfg(not(feature = "azure-attester"))]
#[cfg(not(azure_attester_x86_64_linux))]
{
tracing::error!(
"Attempted to generate an azure attestation but the `azure-attester` feature not enabled"
"Azure attestation generation requires the `azure-attester` feature on an x86_64 linux host"
);
Err(AttestationError::AttestationTypeNotSupported)
}
Expand Down
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ More details in the individual READMEs of the provided crates:

The included `shell.nix` file can be used with `nix-shell`, `direnv`, or `nix
develop` to add the dependencies needed by the optional `azure-attester`
feature of the `attestation` crate on Linux. The `azure-verifier` feature
needs no extra system dependencies and builds on any platform. See the
feature of the `attestation` crate on x86_64 Linux, the only target its
generation code is compiled for. The `azure-verifier` feature needs no extra
system dependencies and builds on any platform. See the
[`attestation` crate readme](./crates/attestation) for details.
Loading