Skip to content
Draft
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
232 changes: 228 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

22 changes: 21 additions & 1 deletion crates/attestation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ tokio = { workspace = true, features = ["fs"] }
tokio-rustls = { workspace = true, default-features = false }

anyhow = "1.0.100"
coset = { version = "0.4.2", optional = true }
nsm-nitro-enclave-utils = { version = "0.1.3", default-features = false, optional = true }
nsm-nitro-enclave-utils-keygen = { version = "=0.1.3", optional = true }
pem-rfc7468 = { version = "0.7.0", features = ["std"] }
tdx-attest = { git = "https://github.com/Dstack-TEE/dstack.git", rev = "4f602dddc0542cd34da031c90ac0b3a560f316ed" }
base64 = "0.22.1"
Expand Down Expand Up @@ -47,13 +50,30 @@ tokio-rustls = { workspace = true, default-features = true }
serde-saphyr = "0.0.22"

[features]
default = []
default = ["nitro"]

# Adds support for Microsoft Azure attestation generation and verification
azure = ["tss-esapi", "az-tdx-vtpm", "openssl"]

# Adds support for AWS Nitro Enclaves attestation generation and verification
nitro = [
"dep:coset",
"dep:nsm-nitro-enclave-utils",
"nsm-nitro-enclave-utils/nitro",
"nsm-nitro-enclave-utils/verify",
]

# Allows mock quotes used in tests and exposes related functions for testing
mock = ["dep:mock-tdx"]

# Adds mock AWS Nitro attestations for tests and local development
mock-nitro = [
"mock",
"nitro",
"dep:nsm-nitro-enclave-utils-keygen",
"nsm-nitro-enclave-utils/pki",
"nsm-nitro-enclave-utils/seed",
]

[lints]
workspace = true
79 changes: 70 additions & 9 deletions crates/attestation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ policy handling.

This crate provides:

- Attestation type detection (`none`, `dcap-tdx`, `gcp-tdx`, and `azure-tdx`
when enabled)
- Attestation generation and verification for DCAP and (optionally) Azure
- Attestation type detection (`none`, `dcap-tdx`, `gcp-tdx`, `qemu-tdx`,
`aws-nitro`, and `azure-tdx` when enabled)
- Attestation generation and verification for DCAP, AWS Nitro Enclaves, and
(optionally) Azure
- Parsing and evaluation of measurement policies

## Runtime Requirements
Expand All @@ -23,6 +24,14 @@ Tokio-backed background tasks such as PCCS pre-warm and cache refresh.

## Feature flags

### `nitro`

Enables AWS Nitro Enclaves attestation support (generation, verification, and
local platform detection), through the Nitro Secure Module at `/dev/nsm`.

This feature is enabled by default. Disable default features to build without
Nitro support or Nitro-specific dependencies.

### `azure`

Enables Microsoft Azure vTPM attestation support (generation and verification),
Expand Down Expand Up @@ -53,6 +62,14 @@ development on non-TDX hardware.

Do not use in production. Disabled by default.

### `mock-nitro`

Enables locally signed mock Nitro attestation documents for tests and
development on non-Nitro hardware. This feature also enables `mock` and
`nitro`.

Do not use in production. Disabled by default.

## Attestation Types

These are the attestation type names used in the measurements file.
Expand All @@ -62,10 +79,12 @@ These are the attestation type names used in the measurements file.
- `azure-tdx` - TDX on Azure, with vTPM attestation
- `qemu-tdx` - TDX on Qemu (no cloud platform)
- `dcap-tdx` - DCAP TDX (platform not specified)
- `aws-nitro` - AWS Nitro Enclaves attestation

Local attestation types can be automatically detected. This works by initially
attempting an Azure attestation, and if it fails attempting a DCAP attestation,
and if that fails assume no CVM attestation. On detecting DCAP, a call to the
attempting an Azure attestation when the `azure` feature is enabled, then
checking for an AWS Nitro Secure Module, then attempting a DCAP attestation, and
if that fails assuming no CVM attestation. On detecting DCAP, a call to the
Google Cloud metadata API is used to detect whether we are on Google Cloud.

In the case of attestation types `dcap-tdx`, `gcp-tdx`, and `qemu-tdx`, a
Expand All @@ -75,6 +94,14 @@ interface. This means that the binary must be run with access to
configfs-tsm is unavailable, quote generation via vSOCK to the QGS will be
attempted.

In the case of attestation type `aws-nitro`, an AWS Nitro Enclaves attestation
document is generated using the Nitro Secure Module exposed at `/dev/nsm`. The
certificate hash used by this crate is placed in the attestation document
`nonce` field and checked during verification. Nitro verification validates the
document signature and certificate chain against the AWS Nitro Enclaves root of
trust bundled with this crate, checks certificate validity using the local
system time, and extracts SHA384 PCR measurements from the signed document.

Alternatively, an external 'attestation provider service' URL can be provided
which outsources the attestation generation to another process.

Expand All @@ -99,8 +126,8 @@ These objects have the following fields:
- `attestation_type` - a string containing one of the attestation types
(confidential computing platforms) described below.
- `measurements` - an object with fields referring to the five measurement
registers. Field names are the same as for the measurement headers (see
below).
registers or PCRs for the selected attestation type. Field names are the same
as for the measurement headers (see below).

Each measurement register entry supports two mutually exclusive fields:

Expand Down Expand Up @@ -218,8 +245,42 @@ Legacy numeric field names are still supported for backwards compatibility:
- `"11"` - PCR 11
- and so on for valid PCR indices `0` through `23`

All other attestation types are DCAP based. In measurement-policy JSON, the
preferred field names are the register names and they are matched
For AWS Nitro Enclaves attestations, field names are bare numeric PCR indexes.
PCR values are SHA384 digests and must be 48 bytes, encoded as 96 hex
characters. The Nitro attestation document format allows PCR indices `0`
through `31`, and this crate accepts that range. AWS Nitro Enclaves currently
documents six enclave measurements that are typically useful for policy:
PCR0, PCR1, PCR2, PCR3, PCR4, and PCR8.

```JSON
[
{
"measurement_id": "aws-nitro-enclave-example",
"attestation_type": "aws-nitro",
"measurements": {
"0": {
"expected_any": [
"5fd25293fa7f5682ab2290f0850da91ff42e7e37f79498a7f133dac86a66e678e3c399891a119d82ab35b2fca0d647fe"
]
},
"1": {
"expected_any": [
"0343b056cd8485ca7890ddd833476d78460aed2aa161548e4e26bedf321726696257d623e8805f3f605946b3d8b0c6aa"
]
},
"2": {
"expected_any": [
"c48f4b4ddb0711cac8c94de79f3e96e387eb52693cc3b1fb664ef90c7f9c5df602a16e7dabe6cad52e8791223ddf602b"
]
}
}
}
]
```

DCAP-based attestation types use TDX measurement registers. In
measurement-policy JSON, the preferred field names are the register names and
they are matched
case-insensitively:

- `mrtd` - MRTD
Expand Down
Binary file not shown.
67 changes: 64 additions & 3 deletions crates/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
pub mod azure;
pub mod dcap;
pub mod measurements;
#[cfg(feature = "nitro")]
pub mod nitro;

use std::{
fmt::{self, Display, Formatter},
Expand Down Expand Up @@ -60,6 +62,16 @@ impl AttestationExchangeMessage {
.map_err(DcapVerificationError::from)?;
Ok(Some(MultiMeasurements::from_dcap_qvl_quote(&quote)?))
}
AttestationType::AwsNitro => {
#[cfg(feature = "nitro")]
{
Ok(Some(nitro::get_measurements(&self.attestation)?))
}
#[cfg(not(feature = "nitro"))]
{
Err(AttestationError::AttestationTypeNotSupported)
}
}
}
}
}
Expand All @@ -79,6 +91,8 @@ pub enum AttestationType {
QemuTdx,
/// DCAP TDX
DcapTdx,
/// AWS Nitro Enclaves
AwsNitro,
}

impl AttestationType {
Expand All @@ -90,6 +104,7 @@ impl AttestationType {
AttestationType::QemuTdx => "qemu-tdx",
AttestationType::GcpTdx => "gcp-tdx",
AttestationType::DcapTdx => "dcap-tdx",
AttestationType::AwsNitro => "aws-nitro",
}
}

Expand All @@ -102,6 +117,12 @@ impl AttestationType {
return Ok(AttestationType::AzureTdx);
}
}
#[cfg(feature = "nitro")]
{
if nitro::running_on_nitro() {
return Ok(AttestationType::AwsNitro);
}
}
// Otherwise try DCAP quote - this internally checks that the quote provider
// is `tdx_guest`
if tdx_attest::get_quote(&[0; 64]).is_ok() {
Expand Down Expand Up @@ -235,6 +256,19 @@ impl AttestationGenerator {
AttestationType::DcapTdx | AttestationType::GcpTdx | AttestationType::QemuTdx => {
dcap::create_dcap_attestation(input_data)
}
AttestationType::AwsNitro => {
#[cfg(feature = "nitro")]
{
Ok(nitro::create_nitro_attestation(input_data)?)
}
#[cfg(not(feature = "nitro"))]
{
tracing::error!(
"Attempted to generate an AWS Nitro attestation but the `nitro` feature is not enabled"
);
Err(AttestationError::AttestationTypeNotSupported)
}
}
}
}

Expand Down Expand Up @@ -272,9 +306,7 @@ impl AttestationGenerator {
pub struct AttestationVerifier {
/// The measurement policy with accepted values and attestation types
pub measurement_policy: MeasurementPolicy,
/// If this is empty, anything will be accepted - but measurements are
/// always injected into HTTP headers, so that they can be verified
/// upstream A PCCS service to use - defaults to Intel PCS
/// A PCCS service to use - defaults to Intel PCS
pub pccs_url: Option<String>,
/// Whether to write quotes to files on disk
pub dump_dcap_quotes: bool,
Expand Down Expand Up @@ -406,6 +438,19 @@ impl AttestationVerifier {
)
.await?
}
AttestationType::AwsNitro => {
#[cfg(feature = "nitro")]
{
nitro::verify_nitro_attestation(
attestation_exchange_message.attestation,
expected_input_data,
)?
}
#[cfg(not(feature = "nitro"))]
{
return Err(AttestationError::AttestationTypeNotSupported);
}
}
};

// Do a measurement / attestation type policy check
Expand Down Expand Up @@ -467,6 +512,19 @@ impl AttestationVerifier {
pccs,
)?
}
AttestationType::AwsNitro => {
#[cfg(feature = "nitro")]
{
nitro::verify_nitro_attestation(
attestation_exchange_message.attestation,
expected_input_data,
)?
}
#[cfg(not(feature = "nitro"))]
{
return Err(AttestationError::AttestationTypeNotSupported);
}
}
};

// Do a measurement / attestation type policy check
Expand Down Expand Up @@ -586,6 +644,9 @@ pub enum AttestationError {
QuoteGeneration(#[from] tdx_attest::TdxAttestError),
#[error("DCAP verification: {0}")]
DcapVerification(#[from] DcapVerificationError),
#[cfg(feature = "nitro")]
#[error("Nitro attestation: {0}")]
Nitro(#[from] nitro::NitroError),
#[error("Attestation type not supported")]
AttestationTypeNotSupported,
#[error("Attestation type not accepted")]
Expand Down
Loading
Loading