Verifying a deployment

This page describes how a user of a deployment in the Attested CPU profile (Confidential VM deployment) checks, before sending data, that TLS terminates inside an attested confidential VM. It covers the report data that binds the TLS key to the evidence, the /attestation endpoint, the open-source binding check and the verification of the quote.

The user checks two things:

  1. The binding. The attestation evidence names the TLS public key of the connection the user is talking to. Open-source kLLM performs this check with python -m kllm.keyed.attest.

  2. The quote. The evidence is a genuine quote from a confidential VM: its signature chain verifies against the hardware vendor’s roots, its TCB status is acceptable, and its measurements match the reference values that the deployer publishes for the guest image. kLLM delegates this check to existing verifiers: the Trustee attestation service, Intel DCAP QVL or AMD snpguest.

Both checks are required. The binding alone proves nothing about the server: an active attacker in the network path can present its own TLS key together with fabricated evidence that names that key. Only a verified quote shows that the key belongs to the attested VM.

Important

The gateway that serves /attestation is part of kLLM Enterprise. The binding check described on this page is open source. Neither has yet been validated on TDX, SEV-SNP or Hygon CSV hardware.

Report data

The gateway generates its TLS key inside the VM and asks the platform for a quote whose report data commits to that key and to a nonce chosen by the user:

report_data = hex(SHA-256("kllm-tls-v1" || SPKI || nonce))

Element

Definition

"kllm-tls-v1"

A fixed domain-separation string, encoded as ASCII bytes.

SPKI

The DER encoding of the SubjectPublicKeyInfo of the gateway’s TLS certificate.

nonce

The raw bytes of the nonce sent by the user. python -m kllm.keyed.attest uses 16 random bytes per check.

hex(...)

The lowercase hexadecimal digest as 64 ASCII characters, which fills the 64-byte report data field of TDX and SEV-SNP quotes.

A quote that verifies and carries this report data proves that the TLS connection terminates inside the measured VM: with --self-signed, the private key of that SPKI is generated in VM memory and never leaves it.

The /attestation endpoint

The user requests GET /attestation?nonce=<hex> over TLS from the gateway. The response is a JSON object with the following fields:

Field

Content

nonce

The nonce, in hexadecimal, that the report data was computed with.

report_data

The report data string defined above.

source

Where the evidence came from: aa (the Attestation Agent), tsm/... (the kernel’s configfs-tsm interface) or none (no confidential VM).

evidence

The evidence, base64-encoded: a raw quote, or the Attestation Agent’s JSON evidence that contains the quote.

Running the binding check

The check needs the keyed extra, which provides cryptography:

$ python -m kllm.keyed.attest https://api.example.com:8443
Options

Option

Meaning

URL

https://host:port of the gateway. The port defaults to 443.

--cafile FILE

Verify the gateway’s certificate and host name against the CA certificates in FILE. Without it, certificate verification is off, which is the expected mode for a gateway started with --self-signed: the quote, not a certificate authority, vouches for the key.

The command opens a TLS connection, records the server’s certificate, requests /attestation with a fresh nonce on the same connection, and prints:

  • the response without the evidence field, as indented JSON;

  • the length of the evidence in base64 characters and its source;

  • a line stating that no TEE is present, when source is none;

  • whether the evidence is bound to this TLS key, followed by a reminder to verify the quote itself.

The exit status is 0 when the binding holds and 1 when it does not.

How the binding is checked

The helper recomputes the report data from the certificate’s SPKI and the nonce field of the response, and requires that it equals the report_data field. Unless source is none, it then decodes evidence and requires that the recomputed report data occur in it, either in the raw bytes or in a base64-encoded string longer than 512 characters inside JSON evidence.

Warning

Interpret the result with the following limits:

  • The helper does not verify the quote’s signature chain, TCB status or measurements.

  • A response with "source": "none" passes the binding check and the command exits with status 0, after printing that the server runs outside a confidential VM. Treat such a response as a failed verification for any deployment that claims the Attested CPU profile.

  • The helper does not compare the nonce field of the response with the nonce it sent. Check freshness yourself when you verify the quote.

  • The helper closes its connection after the check. A client that sends requests later opens a new connection; accept on it only the TLS public key that was verified.

Verifying the quote

Fetch the evidence for a nonce of your choice and pass the quote to the verifier for the platform, together with the deployer’s reference values:

$ curl -sk "https://api.example.com:8443/attestation?nonce=$(openssl rand -hex 16)" \
    -o attestation.json

Platform

Verifier

Intel TDX

Intel DCAP Quote Verification Library (QVL), or the Trustee attestation service

AMD SEV-SNP

AMD snpguest, or the Trustee attestation service

Hygon CSV

This documentation names no verifier; use one that supports Hygon CSV evidence

The verifier must confirm the signature chain and the TCB status, and the user must compare the measurements in the quote with the reference values that the deployer publishes for the reproducible guest image. Confirm also that the report data in the verified quote equals the value recomputed from the gateway’s SPKI and your nonce. kLLM does not wrap these verifiers.

Using the helper from Python

The module exposes the same steps as functions:

from kllm.keyed.attest import fetch, report_data

doc, bound = fetch("https://api.example.com:8443")   # (response dict, binding result)
if not bound or doc["source"] == "none":
    raise SystemExit("deployment is not attested")
# Pass doc["evidence"] to the platform's quote verifier before sending data.

fetch(url, nonce=None, cafile=None) returns the response and the binding result. check_binding(doc, cert_der) checks a response against a DER certificate, and report_data(spki_der, nonce) computes the expected report data.

Note

Part of the upcoming 0.2 line. kllm check https://host:port runs this binding check, and kllm check REF checks a local model against its store record and catalog manifest (kllm check). Neither verifies the quote’s signature chain, TCB status or measurements. The core command kllm verify is unrelated to attestation: it checks a sealed model against its public base (kllm verify).

See also