Skip to content
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
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
22 changes: 16 additions & 6 deletions crates/attestation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ 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]
# Azure vTPM evidence generation: reads the vTPM through tss-esapi, attests Intel TDX.
# An Azure TDX CVM is x86_64 linux, and tss-esapi-sys has no bindings for most other
# tuples in any case. Keeping these per-target rather than behind the feature alone
# leaves `azure-attester` (and --all-features) buildable everywhere: elsewhere the
# feature stays on but pulls in neither dep, so nothing extra compiles. build.rs
# mirrors the condition as the `azure_attester_x86_64_linux` cfg; keep the two in sync.
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 +67,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
17 changes: 14 additions & 3 deletions crates/attestation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,20 @@ 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 for x86_64 Linux targets only — where an Azure
TDX CVM actually runs. **Elsewhere, MacOS and aarch64 Linux included,
`azure-attester` stays enabled but contributes nothing:** its generation
dependencies are target-gated out, so the crate behaves as if only
`azure-verifier` were enabled. `--all-features` still builds everywhere, but
`AttestationType::detect` will never report `AzureTdx` and generation will fail
with `AttestationTypeNotSupported`. Verification is unaffected. `build.rs` emits
a `cargo::warning` when this happens.

The condition is on the target, not the build host, so any machine can produce a
build with generation in it via `--target x86_64-unknown-linux-gnu`, given a
cross toolchain and tpm2-tss and openssl for that target.

**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
31 changes: 31 additions & 0 deletions crates/attestation/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,37 @@ 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: any
// host targeting x86_64 linux gets the generation code. 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() {
let os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
if os == "linux" && arch == "x86_64" {
println!("cargo::rustc-cfg=azure_attester_x86_64_linux");
} else {
// Warn rather than fail: --all-features has to stay usable
// everywhere, so the feature is a no-op off this
// target instead of an error.
println!(
"cargo::warning=`azure-attester` is enabled but the target is {arch}-{os}. \
The Azure evidence generation code is only compiled for x86_64 linux, so this \
build will behave as if only `azure-verifier` were enabled: `detect` will \
never return `AzureTdx`, and `generate_attestation` for `AzureTdx` will fail \
with `AttestationTypeNotSupported`. Verifying Azure evidence still works. If \
you need generation, target x86_64 linux — any host can, with \
`--target x86_64-unknown-linux-gnu` and a cross toolchain."
);
}
}

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