Attestation generation and verification for confidential VMs, plus measurement policy handling.
This crate provides:
- Attestation type detection (
none,dcap-tdx,gcp-tdx, andazure-tdxwhen enabled) - Attestation generation and verification for DCAP and (optionally) Azure
- Parsing and evaluation of measurement policies
Verification uses the pccs crate for collateral caching and
background refresh. As a result, constructing an AttestationVerifier with
PCCS enabled and calling verification APIs is expected to happen from within a
Tokio runtime and might panic if called outside of one.
Note that although some of the verification API methods are synchronous (for
example verify_attestation_sync), still their functionality depends on
Tokio-backed background tasks such as PCCS pre-warm and cache refresh.
Enables Microsoft Azure vTPM attestation support (generation and verification),
through tss-esapi.
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 implementation of Azure attestation generation or verification ceases to work. Use at your own risk.
This feature requires tpm2 and openssl to
be installed. On Debian-based systems tpm2 is provided by
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 feature on
MacOS.
This feature is disabled by default. Note that without this feature, verification of azure attestations is not possible and azure attestations will be rejected with an error.
*** Note ***
Azure is known to use an outdated FMSPC 90C06F000000 which will cause
verifications to fail. A workaround is provided which will allow this, but it
must be explicitly enabled via the override_azure_outdated_tcb flag on
AttestationVerifier.
Enables mock quote support via the local mock-tdx crate for tests and
development on non-TDX hardware.
Do not use in production. Disabled by default.
These are the attestation type names used in the measurements file.
none- No attestation providedgcp-tdx- DCAP TDX on Google Cloud Platformazure-tdx- TDX on Azure, with vTPM attestationdcap-tdx- DCAP TDX (platform not specified)
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 Google Cloud metadata API is used to detect whether we are on Google Cloud.
In the case of attestation types dcap-tdx and gcp-tdx, a
standard DCAP attestation is generated using the configfs-tsm linux filesystem
interface. This means that the binary must be run with access to
/sys/kernel/config/tsm/report which on many systems requires sudo. If
configfs-tsm is unavailable, quote generation via vSOCK to the QGS will be
attempted.
Alternatively, an external 'attestation provider service' URL can be provided which outsources the attestation generation to another process.
When verifying DCAP attestations, the Intel PCS is used to retrieve collateral unless a PCCS URL is provided via a command line argument. If outdated TCB is used, the quote will fail to verify. For special cases where outdated TCB should be allowed, a custom override function can be passed when verifying which may modify collateral before it is validated against the TCB.
Accepted measurements for the remote party can be specified in a JSON file containing an array of objects, each of which specifies an accepted attestation type and set of measurements.
This aims to match the formatting used by cvm-reverse-proxy.
These objects have the following fields:
measurement_id- a name used to describe the entry. For example the name and version of the CVM OS image that these measurements correspond to.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).dcap_image_hashes- an alternative tomeasurementsthat pins the hashes of the boot components (UKI, kernel, initrd, cmdline, GPT disk GUID) rather than raw register values. The verifier reconstructs the expected RTMRs at check time from these hashes and platform-fetched firmware. See Portable measurement policies below.
A record may set either measurements or dcap_image_hashes, not both.
Each measurement register entry supports two mutually exclusive fields:
expected_any- (recommended) an array of hex-encoded measurement values. The attestation is accepted if the actual measurement matches any value in the list (OR semantics).expected- (deprecated) a single hex-encoded measurement value. Retained for backwards compatibility butexpected_anyshould be preferred.
Example using expected_any (recommended):
[
{
"measurement_id": "dcap-tdx-example",
"attestation_type": "dcap-tdx",
"measurements": {
"mrtd": {
"expected_any": [
"47a1cc074b914df8596bad0ed13d50d561ad1effc7f7cc530ab86da7ea49ffc03e57e7da829f8cba9c629c3970505323"
]
},
"rtmr0": {
"expected_any": [
"da6e07866635cb34a9ffcdc26ec6622f289e625c42c39b320f29cdf1dc84390b4f89dd0b073be52ac38ca7b0a0f375bb"
]
},
"rtmr1": {
"expected_any": [
"a7157e7c5f932e9babac9209d4527ec9ed837b8e335a931517677fa746db51ee56062e3324e266e3f39ec26a516f4f71"
]
},
"rtmr2": {
"expected_any": [
"e63560e50830e22fbc9b06cdce8afe784bf111e4251256cf104050f1347cd4ad9f30da408475066575145da0b098a124"
]
},
"rtmr3": {
"expected_any": [
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
]
}
}
}
]The expected_any field is useful when multiple measurement values should be
accepted for a register (e.g., for different versions of the firmware):
{
"mrtd": {
"expected_any": [
"47a1cc074b914df8596bad0ed13d50d561ad1effc7f7cc530ab86da7ea49ffc03e57e7da829f8cba9c629c3970505323",
"abc123def456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
]
}
}Legacy format using deprecated expected
field
The expected field is deprecated but still supported for backwards
compatibility:
[
{
"measurement_id": "dcap-tdx-example",
"attestation_type": "dcap-tdx",
"measurements": {
"mrtd": {
"expected": "47a1cc074b914df8596bad0ed13d50d561ad1effc7f7cc530ab86da7ea49ffc03e57e7da829f8cba9c629c3970505323"
}
}
}
]The only mandatory field is attestation_type. If an attestation type is
specified with neither measurements nor dcap_image_hashes, any
measurements will be accepted for this attestation type. The measurements can
still be checked up-stream by the source client or target service using header
injection for example. But it is then up to external programs to reject
unacceptable measurements.
For Azure vTMP attestations, the preferred field names are PCR register
indexes prefixed with pcr or PCR. For example the following specifies PCRs
4 and 9:
{
"measurement_id": "cvm-image-azure-tdx.rootfs-20241107200854.wic.vhd",
"attestation_type": "azure-tdx",
"measurements": {
"pcr4": {
"expected_any": ["1b8cd655f5ebdf50bedabfb5db6b896a0a7c56de54f318103a2de1e7cea57b6b"]
},
"pcr9": {
"expected_any": ["992465f922102234c196f596fdaba86ea16eaa4c264dc425ec26bc2d1c364472"]
}
}
}Legacy numeric field names are still supported for backwards compatibility:
"4"- PCR 4"9"- PCR 9"11"- PCR 11- and so on for valid PCR indices
0through23
All other attestation types are DCAP based. In measurement-policy JSON, the preferred field names are the register names and they are matched case-insensitively:
mrtd- MRTDrtmr0- RTMR0rtmr1- RTMR1rtmr2- RTMR2rtmr3- RTMR3
Legacy numeric field names are still supported for backwards compatibility:
- "0" - MRTD
- "1" - RTMR0
- "2" - RTMR1
- "3" - RTMR2
- "4" - RTMR3
The measurements format above specifies register values, so any change
to platform-injected values (firmware, RAM size, disk count, ACPI tables)
changes the expected register values even when the OS image is unchanged.
The dcap_image_hashes alternative allows you to specify the OS image's
boot-component hashes instead, and the verifier reconstructs the expected
register values from those hashes plus platform metadata fetched attest
verification time. The same policy record then matches the same OS images
across platform variants.
This can be done with the attest measure CLI from
Easy-TEE/attest which outputs five
hex-encoded SHA-384 values:
uki_authenticode- authenticode hash of the UKI (unified kernel image)kernel_authenticode- authenticode hash of the kernel binarycmdline_hash- hash of the kernel command lineinitrd_hash- hash of the initramfsgpt_disk_guid_hash- hash derived from GPT partition GUIDs
Example:
[
{
"measurement_id": "flashbox-l1-v1.0.0",
"attestation_type": "gcp-tdx",
"dcap_image_hashes": {
"uki_authenticode": "fcaceb6d87694746ba2d93a87ef4209f2a7629b7f400097b93241e80b9ec3e1e80f9a4cd8028e6a83f297ea5de8d9abc",
"kernel_authenticode": "b6c5133268aa8b440509f3d53ee855a5cd3aeb6441eb109a9f27f14c43bce3e2383856df4af876501ceeb4c9a3b15f0c",
"cmdline_hash": "e03b89abf354a38976537b7a9138fd312e4cbf73b61eebc44086491701b1d167b9f6cb97a922325866c93e0834723d87",
"initrd_hash": "a5b3d4742045e7d08aa19953c35098e784826b01a84f60568fa69f1a848dafd96ec98b8df616d6142779c9b97318166b",
"gpt_disk_guid_hash": "180bac1af9c35cc15e909623c005289539b4da2840d9c9b658fd4968ea4f03e0159402d03da1afc9035e0db30804e282"
}
}
]Portable policies currently only work with the "gcp-tdx" attestation type.
For GCP, the verifier fetches the platform firmware blob from Google's metadata
service (keyed by MRTD) and combines it with the image hashes to reconstruct the
expected registers. A dcap_image_hashes record with any other attestation type
is rejected when parsing from JSON.
The JSON object emitted directly by attest measure portable is also accepted
as a measurement policy. Its optional azure PCR values and its dcap image
hashes are converted into Azure TDX and GCP TDX policy records respectively.
It can be supplied on its own or as an element of a policy array, including an
array mixed with records in the policy format described above:
{
"kind": "portable",
"azure": {
"pcr4": "<64 hex characters>",
"pcr9": "<64 hex characters>",
"pcr11": "<64 hex characters>"
},
"dcap": {
"uki_authenticode": "<96 hex characters>",
"kernel_authenticode": "<96 hex characters>",
"cmdline_hash": "<96 hex characters>",
"initrd_hash": "<96 hex characters>",
"gpt_disk_guid_hash": "<96 hex characters>"
}
}The non-portable "kind": "azure" output from attest measure is accepted in
the same places and pins PCRs 4, 9, and 11. The "kind": "dcap" output is
rejected because it only pins RTMR1 and RTMR2, leaving the other DCAP registers
unconstrained. Use the portable output or an explicit measurement policy
instead.