Attestation Flow
An attestation is a TDX quote: a hardware-signed measurement of a confidential VM. Before any key is released or any peer is trusted, a component has to produce one and bind it to the request it is answering. This page covers that production step — the report-data layout, the nonce binding, and the endpoints that hand a quote back. What a verifier then does with the quote lives in Verification; the KMS key gate lives in Key Release.
For the Calimero Core view of the same primitive, see the core TEE Attestation page.
Where quotes come from
Section titled “Where quotes come from”Every quote in Mero TEE comes from the TD’s own hardware through the kernel’s
configfs-tsm interface (/sys/kernel/config/tsm/report): the service writes a
64-byte report_data value and reads back the raw quote. The service never
fabricates measurements — MRTD and RTMR0–3 come from the hardware, firmware and
boot chain, and the caller only controls report_data.
The 64-byte report_data
Section titled “The 64-byte report_data”report_data is the only field the caller influences, and it is what binds a quote
to a specific, fresh request. It is a plain concatenation of two 32-byte halves — not
a hash of them:
report_data[0..32] = nonce (32 bytes, caller-supplied freshness value)report_data[32..64] = binding (32 bytes, identity / channel binding)The first half is the freshness nonce. The second half binds the quote to an identity so a captured quote cannot be replayed for a different subject. What the binding contains depends on which surface generated the quote:
| Surface | report_data[0..32] |
report_data[32..64] |
|---|---|---|
KMS /attest |
caller nonce (nonceB64) |
caller bindingB64, or the default SHA-256("mero-kms-attest-v1"); with transportKey: true, a binding that also commits to the KMS’s transport key |
| KMS key-release quote (from a node) | challenge nonce issued by the KMS | SHA-256(peer_id) |
KMS self-attestation — POST /attest
Section titled “KMS self-attestation — POST /attest”The KMS exposes POST /attest so any caller (for example a merod node before it
trusts the KMS, or the public verifier) can pull a fresh quote and confirm the KMS
runs the expected measurement.
-
Caller sends a nonce. The request body is JSON:
{"nonceB64": "<base64 of 32 random bytes>","bindingB64": "<optional base64 of 32 bytes>","transportKey": true}nonceB64must decode to exactly 32 bytes;bindingB64is optional and must also be 32 bytes when present.transportKey(optional) asks for the KMS’s X25519 transport key, which merod uses for sealed key release. -
KMS builds report_data. It sets
report_data = nonce || binding, wherebindingis the supplied value or the default domain-separated constant — and, when the transport key was asked for, a binding that commits to it too. -
KMS takes a quote over that
report_datafrom configfs-tsm. -
KMS returns the quote:
{"quoteB64": "<base64 raw TDX quote>","reportDataHex": "<hex of the 64-byte report_data>","transportPublicKeyB64": "<present when transportKey was asked for>"}The transport key is derived from the cluster root, so every replica of a cluster reports the same one.
The caller confirms freshness by checking that the first 32 bytes of the quote’s own
report_data equal the nonce it sent. reportDataHex is a convenience copy; a verifier
reads the value from the quote bytes it has checked the signature of.
Node self-attestation — POST /admin-api/tee/attest
Section titled “Node self-attestation — POST /admin-api/tee/attest”A merod node exposes its own attestation endpoint (documented in Calimero Core) at
POST /admin-api/tee/attest, taking a hex nonce and returning a quote for the node
itself. The public verifier uses this path when pointed at a node URL rather than a
KMS URL. Treat the exact response envelope as node-owned; the verifier extracts the
quote (data.quoteB64) and the report-data from whichever field the node populates.
End to end: getting attested
Section titled “End to end: getting attested”Putting the production step in context, a component becomes “attested” only once a verifier has checked the quote it produced:
1. Verifier generates a fresh 32-byte nonce2. Verifier -> component: POST /attest { nonceB64 } (KMS) POST /admin-api/tee/attest { nonce } (node)3. Component -> TDX: quote over (nonce || binding) (configfs-tsm)4. TDX -> component: quote5. Component -> verifier: { quoteB64, ... }6. Verifier: quote report_data[0..32] == nonce ? (freshness)7. Verifier: quote signature chain valid ? (see Verification)8. Verifier: measurements match policy ? (see Policy Management)Steps 6–8 are the verifier’s job; this page ends at step 5, where a fresh, identity-bound quote exists. Continue to Verification for what makes a quote trustworthy, and to Key Release for the node → KMS flow that turns a passing attestation into a released storage key.