Confidential VM images
Run tenzro-node in an Intel TDX or AMD SEV-SNP confidential VM, or an AWS Nitro enclave, from a measured release image, and check that a node and the proofs it makes come from the published release.
Testnet v1 publishes measured images of the node, so a node running in a confidential VM can prove which release it runs:
| Image | Platform | What it measures |
|---|---|---|
tenzro-node-cvm-x86_64 | Intel TDX and AMD SEV-SNP guests (one UEFI disk) | the node and its operating system |
tenzro-node-cvm-x86_64-prover | the same | the node plus the finality prover and its Groth16 prover container |
tenzro-node-nitro-x86_64.eif | AWS Nitro Enclaves | the node and the libraries it links |
The images carry the released tenzro-node-linux-x86_64-cpu binaries
unchanged. They are built by scripts/release/build-cvm-image.sh and
scripts/release/build-nitro-eif.sh from that archive; two builds of the same
inputs give the same bytes, so anyone can rebuild an image and compare its
digests with the published ones.
What is measured
The confidential VM disk has three partitions: an EFI system partition holding one file, a read-only root file system, and its dm-verity hash tree. The EFI file is a unified kernel image: the kernel, the initrd and a kernel command line that carries the verity root hash. Changing any byte of the root file system changes the root hash, which changes the unified kernel image, which changes what the firmware measures when it starts it.
- Intel TDX. The firmware records the unified kernel image's Authenticode
digest in RTMR1, with the disk's partition table, and the image's own stub
records its sections in RTMR2. MRTD and RTMR0 describe the platform's
firmware and virtual hardware.
scripts/release/cvm-measure.py verifyreplays the firmware event log against the quote, checks that the image and the kernel it carries are the only programs the firmware started, and compares RTMR1 and RTMR2 with the published values. - AMD SEV-SNP. The launch measurement covers what the hypervisor loads
before the guest runs. When the firmware boots the disk, as on cloud
platforms that supply their own firmware, it measures that firmware only:
compare it with the firmware measurement the platform publishes, and bind
the image through the guest's TPM event log. On your own host, boot the
unified kernel image directly (QEMU
-kernel tenzro-node-cvm-x86_64.efiwith an SEV-SNP OVMF build and kernel hashes on); the launch measurement then covers the image, andsev-snp-measure --mode snp --ovmf <OVMF.fd> --vcpus <n> --vcpu-type <type> --kernel tenzro-node-cvm-x86_64.efigives the expected value for that host. - AWS Nitro. PCR0 measures the whole enclave image, PCR1 the kernel, command line and bootstrap ramdisk, PCR2 the node's ramdisk.
The published values for this release are in the table at the end of this
page and in each image's .measurements.json / .pcrs.json.
Booting the image
The disk is a plain UEFI disk (.raw; .gce.tar.gz holds it as disk.raw
for cloud image import). It needs:
- a confidential VM with Intel TDX or AMD SEV-SNP and a virtual TPM 2.0 (the node's keys live in the TPM; a guest without one refuses to start the node);
- a disk of at least 8 GB: on first boot the free space becomes the state
partition (
/var), which holds the chain data, the TPM-sealed authorization of the node's keys and nothing else; - outbound network access (DHCP). The node's RPC listens on loopback only.
On first boot the image seals a fresh authorization for the node's TPM keys
to the guest's TPM, provisions the node's keys (tenzro-node tpm provision)
and starts the node. Node flags go in /var/lib/tenzro-config/node.env as
TENZRO_NODE_ARGS=...; on Google Cloud the instance metadata attribute
tenzro-node-args sets them. Flags are configuration, not code, and are not
part of the measurement.
When the node answers, the image prints to the serial console the detected
TEE, the measurement registers of a fresh report, and the report and the
firmware event logs (gzip, base64), in lines tagged TENZRO-DETECT,
TENZRO-MEASUREMENTS, TENZRO-REPORT and TENZRO-EVENTLOG-*.
Checking a node
Ask the node for a report and its registers:
tenzro tee attest --out report.jsontenzro_getTeeAttestation returns the registers (measurements: MRTD and
RTMR0-3 on TDX, MEASUREMENT on SEV-SNP) and the report itself (report_hex).
tenzro_verifyTeeAttestation verifies a report against a nonce from
tenzro_teeAttestationChallenge and returns the verified registers.
For a TDX guest, check the image too:
python3 scripts/release/cvm-measure.py verify \
--measurements tenzro-node-cvm-x86_64.measurements.json \
--report report.json --eventlog CCEL.bin --platform gcp-tdxCCEL.bin is the guest's /sys/firmware/acpi/tables/data/CCEL (also printed
on the console).
Proofs made in the VM
The prover image runs both stages of the finality prover on the machine: the
STARK stage (tenzro-finality-prover stark, on the CPU here; on an NVIDIA GPU
in a GPU confidential VM) and the Groth16 wrap
(tenzro-finality-prover wrap, on the CPU, in the Groth16 prover container the
image carries by digest). A proof made there is bound to the machine's
attestation:
tenzro-finality-prover wrap --receipt receipt.bin --out proof.json
tenzro zk attest-finality-proof --proof proof.json --out bound.json
tenzro zk verify-tee-finality-proof --bundle bound.jsontenzro_attestFinalityProof verifies the seal, then asks the TEE for a report
whose report data commits to SHA-256("tenzro/zk/tee-bound-finality-proof" || image_id || SHA-256(journal) || SHA-256(seal) || prover_digest), where
prover_digest is the digest of the Groth16 prover container. Any node
verifies the bundle with tenzro_verifyTeeFinalityProof: the seal, the
prover, the binding and the vendor evidence, returning the verified registers.
Comparing those registers with the published measurements of the prover image
shows the proof came from the published prover in a measured VM.
Receipts placed in /var/lib/tenzro-prover/inbox/*.receipt are wrapped,
bound and verified automatically, with the results in
/var/lib/tenzro-prover/outbox/. On Google Cloud the instance metadata
attribute tenzro-prover-receipt (a gs:// object the instance's service
account can read) places one receipt in the inbox.
The GPU STARK stage needs an NVIDIA GPU in confidential-computing mode inside
a TDX or SEV-SNP guest, and a prover built with the cuda feature; the
evidence then also carries the GPU's attestation. The prover image as
published runs both stages on the CPU.
Published measurements (testnet v1)
These values are for the release candidate (node built from commit
dc36c9fe, images built with SOURCE_DATE_EPOCH=1791298041). The release
build of testnet v1 rebuilds the node, so the images and these values are
refreshed then; the release's .measurements.json and .pcrs.json files are
authoritative.
tenzro-node-cvm-x86_64
| Value | SHA-384 / SHA-256 |
|---|---|
disk (.raw, SHA-256) | cbf8a848fcbf30c3bef02b5738d860e6e6b479aa0f70d52312832696de1248d9 |
unified kernel image (.efi, SHA-256) | 91d77b458aced8c92864db69f3142a7b817792bef1e9e9da83e9c2b8d279dea3 |
| dm-verity root hash | e80e17ec3a3fd67d95bbaaecfb71df43f832fe595af238dc2f3ee81362c47e77 |
| unified kernel image, Authenticode | 7df00391382ee1732abdd99aaccf81209de8a2b523613ad6580da5818914f8abd5a62537f551308aef4d6a0838774ae5 |
| kernel it starts, Authenticode | 8fbeb1462072cc699e357771ed8276a46a2bc2cc8db5df2bdc43e0099921b6e6289cab24efba9f8612067443ac641d2e |
| TDX RTMR1 | c00baf2814f20c4fbc9e9c6d5d16bf31ed8283e3a799b6a91f0faccc8e9f5c31fd3ce711f270b439b2c1f1f144d052b4 |
| TDX RTMR2 | 6f6d2f3d3fc3b0e0becfa89238397f4be702f6ef7a35c235c45a5a87ebe927880c71dcde9e24a5149d6615ce99e8ce42 |
tenzro-node-cvm-x86_64-prover
| Value | SHA-384 / SHA-256 |
|---|---|
disk (.raw, SHA-256) | 1708949c7091006a6f3a63fe51c7a08e7b0a17e50672132d1701dad7b22432d3 |
unified kernel image (.efi, SHA-256) | 0c65f28013507b2297a89ccd0fb270c1296c5e2b59f009738584ef6fd20f2371 |
| dm-verity root hash | bc935795b9fa73c11b5e40a2e5340161ef6afa174380a1dd8906ac7d131117b7 |
| unified kernel image, Authenticode | 22a66bc1115783b335b99c082de1b2e138cf341061ff555f8d0b97facafa98b20afcca22fec63602feda5084445cb481 |
| TDX RTMR1 | bd17e08b18818b0b52bf0d26d951351b3e8bb94aafda8ff15725acd24c8b28429495f5f65e9cad22c711fafbbc279d8c |
| TDX RTMR2 | b208e93aca7db283218667617787f46fc243787106ffbd6c81f17cfb3e30f87441834a76cb1e0cd07d49555fa7b31b7f |
| Groth16 prover container | risczero/risc0-groth16-prover@sha256:7f173963196570b7a71816ed70565a4579264c5d2e3e0ecb028102538ad0e331 |
tenzro-node-nitro-x86_64.eif
| Register | SHA-384 |
|---|---|
| PCR0 | 955ceb0ec943e9d337a2bc75da6d87b03ca16f70c5ea8bc1545a3fd84913688bef41bdab6f3812f1fef80cbe246a9ab5 |
| PCR1 | 8e229f58d29ff87baf647b3771a37b966d29a7cdb499e95b4504ba5a9087d095a34c53d5028d7ebf69a1df2f789b7a80 |
| PCR2 | 7f46aa3c49f728a26c07ed3995107b2d74fb94d6ec010452170ca2fdac766b88e0293a39e79e2bff03bf73a7ec21f2f0 |
Platform registers on Google Cloud. MRTD, RTMR0 and the SEV-SNP launch
measurement belong to the platform's firmware, not the image; each is signed
for by the platform's firmware endorsement
(gs://gce_tcb_integrity/ovmf_x64_csm/<tdx|sevsnp>/<value>.binarypb).
Observed values:
| Machine | Register | SHA-384 |
|---|---|---|
| c3-standard-4, Intel TDX | MRTD | c1ee9c16e3afc506cfe042c5b846a368528f3b37618eafb27469bc114cf914e9222c91618470e7f2b28ac360968270a5 |
| c3-standard-4, Intel TDX | RTMR0 | c49d22aff6edb37cb6178defb05e0e2b512c26960e6ee73b1ea303365a31def807ab2ad71e5874236feca2ca552c6307 |
| n2d-standard-2, AMD SEV-SNP | MEASUREMENT | 0e017d2fba7c4964575fdd8c770061abf07f0753148d030f94e969a2d1fa5a5747ba0ced9174f2d44b5f6663fa2de00f |