Attestation Workflow for Spatial Enclaves

Attestation is the only part of a trusted-execution deployment that a data owner can independently verify, and it is the part most often implemented as a checkbox. A quote arrives, a signature validates, someone logs “attestation OK”, and the coordinates upload — even though the check that actually carries the guarantee, comparing the code measurement against an approved build, was never performed. This page specifies the workflow properly, including every branch that must fail closed. It sits under trusted execution for spatial workloads in Secure Multi-Party Computation in Spatial Analytics.

Parameter Configuration and Calibration

  • approved_measurements — the list that carries the guarantee. One entry per reviewed, reproducibly-built enclave image. Treat additions as code changes: same review, same audit trail. A deployment with a wildcard here has attestation in name only.
  • min_tcb_version — the platform floor. Vendors publish TCB levels as vulnerabilities are patched, and a downlevel platform still emits valid quotes. Pin the floor, and raise it on a schedule tied to the vendor’s advisories rather than to your release cadence.
  • quote_max_age_s — the freshness bound. A quote is a statement about a moment. Bind it to a fresh nonce from the owner and reject anything older than a small number of seconds; a cached quote is a replay waiting to happen.
  • key_binding — the ephemeral key must be inside the quote. Not alongside it, not in the TLS certificate: inside the signed payload. This is what stops a host from terminating the channel outside the enclave.
Check Failure mode if omitted Severity
Vendor signature any host can forge a quote fatal
Measurement in approved set some other enclave gets your data fatal
TCB version floor a known-vulnerable platform is used high
Nonce freshness a replayed quote from a retired enclave high
Key inside the quote host terminates TLS, forwards plaintext fatal
The checks that carry the guarantee are the ones most often skipped A grouped bar chart of six attestation checks. Signature verification is nearly universal and fatal if omitted. The measurement check is equally fatal and the least commonly implemented, followed by the TCB floor and the key-binding check. The pattern is that the cheap checks are done and the ones carrying the actual guarantee are not. The checks that carry the guarantee are the ones most often skipped severity against how routinely each check is actually implemented (÷10) 0 5 10 severity / implementation ÷10 3.0 9.8 signature 2.0 7.1 nonce 2.0 6.4 freshness 2.0 4.2 TCB floor 3.0 5.5 key binding 3.0 3.8 measurement severity if omitted (3 = fatal) % of deployments implementing it
A valid signature proves an enclave exists. Only the measurement check proves it is your enclave — and it is the check most often left advisory.

Reference Implementation

python
from __future__ import annotations

import hashlib
import hmac
import secrets
from dataclasses import dataclass
from typing import Callable


class AttestationError(RuntimeError):
    """Any failed check. Callers must not catch this and continue."""


@dataclass(frozen=True)
class Quote:
    measurement: str
    ephemeral_pubkey: bytes
    tcb_version: int
    nonce: bytes
    issued_at: float
    signature: bytes


@dataclass(frozen=True)
class VerifierPolicy:
    approved_measurements: frozenset[str]
    min_tcb_version: int
    vendor_key: bytes
    quote_max_age_s: float = 30.0

    def _expected_signature(self, quote: Quote) -> bytes:
        payload = b"|".join([
            quote.measurement.encode(),
            quote.ephemeral_pubkey,
            str(quote.tcb_version).encode(),
            quote.nonce,
            f"{quote.issued_at:.3f}".encode(),
        ])
        return hmac.new(self.vendor_key, payload, hashlib.sha256).digest()

    def verify(self, quote: Quote, *, expected_nonce: bytes, now: float) -> None:
        """Run every check, in the order that fails fastest and loudest.

        Ordering matters for diagnostics, not for security: all checks must pass.
        The measurement check is listed last deliberately, because it is the one
        engineers are tempted to make advisory, and putting it after the cheap
        checks makes its absence obvious in a diff.
        """
        if not hmac.compare_digest(self._expected_signature(quote), quote.signature):
            raise AttestationError("vendor signature invalid")
        if not hmac.compare_digest(quote.nonce, expected_nonce):
            raise AttestationError("nonce mismatch — this quote answers a different request")
        age = now - quote.issued_at
        if age < -1.0 or age > self.quote_max_age_s:
            raise AttestationError(f"quote age {age:.1f}s outside the accepted window")
        if quote.tcb_version < self.min_tcb_version:
            raise AttestationError(
                f"TCB {quote.tcb_version} below floor {self.min_tcb_version}"
            )
        if len(quote.ephemeral_pubkey) < 32:
            raise AttestationError("ephemeral key absent or too short")
        if quote.measurement not in self.approved_measurements:
            raise AttestationError(
                f"measurement {quote.measurement[:12]}… not approved — a valid "
                "signature says an enclave exists, not that it runs your code"
            )


@dataclass
class AttestedChannel:
    """A channel that cannot transmit before a successful verification."""

    policy: VerifierPolicy
    _key: bytes | None = None

    def open(self, request_quote: Callable[[bytes], Quote], *, now: float) -> None:
        nonce = secrets.token_bytes(32)
        quote = request_quote(nonce)
        self.policy.verify(quote, expected_nonce=nonce, now=now)
        self._key = quote.ephemeral_pubkey

    def send(self, payload: bytes) -> bytes:
        if self._key is None:
            raise AttestationError("channel not attested — refusing to transmit")
        stream = hashlib.blake2b(self._key, digest_size=len(payload)).digest()
        return bytes(a ^ b for a, b in zip(payload, stream))

Validation Checkpoint

python
def _validate() -> None:
    vendor_key = b"vendor-signing-key"
    good_measurement = hashlib.sha256(b"enclave-v4.2.1").hexdigest()
    policy = VerifierPolicy(
        approved_measurements=frozenset({good_measurement}),
        min_tcb_version=7,
        vendor_key=vendor_key,
    )

    def make_quote(nonce: bytes, *, measurement=good_measurement, tcb=9,
                   issued_at=1_000.0, key=b"k" * 32) -> Quote:
        q = Quote(measurement, key, tcb, nonce, issued_at, b"")
        return Quote(measurement, key, tcb, nonce, issued_at,
                     policy._expected_signature(q))

    nonce = secrets.token_bytes(32)

    # 1. A well-formed, approved quote passes.
    policy.verify(make_quote(nonce), expected_nonce=nonce, now=1_005.0)

    # 2. A signature from the wrong key fails.
    bad = make_quote(nonce)
    forged = Quote(bad.measurement, bad.ephemeral_pubkey, bad.tcb_version,
                   bad.nonce, bad.issued_at, b"x" * 32)
    for case, quote, kwargs in [
        ("forged signature", forged, {}),
        ("unapproved build", make_quote(nonce, measurement="deadbeef" * 8), {}),
        ("downlevel TCB", make_quote(nonce, tcb=3), {}),
        ("stale quote", make_quote(nonce, issued_at=900.0), {}),
        ("short key", make_quote(nonce, key=b"short"), {}),
    ]:
        try:
            policy.verify(quote, expected_nonce=nonce, now=1_005.0, **kwargs)
        except AttestationError:
            pass
        else:
            raise AssertionError(f"{case} must be rejected")

    # 3. A replayed quote answering a different nonce fails.
    other = secrets.token_bytes(32)
    try:
        policy.verify(make_quote(other), expected_nonce=nonce, now=1_005.0)
    except AttestationError as exc:
        assert "nonce" in str(exc)
    else:
        raise AssertionError("nonce mismatch must be rejected")

    # 4. The channel refuses to transmit before attestation.
    channel = AttestedChannel(policy)
    try:
        channel.send(b"coordinates")
    except AttestationError:
        pass
    else:
        raise AssertionError("an unattested channel must refuse to send")

    # 5. After a successful open, transmission is bound to the attested key.
    channel.open(lambda n: make_quote(n), now=1_002.0)
    assert channel.send(b"coordinates") != b"coordinates"

    print("attestation workflow: all assertions passed")


_validate()
Quote freshness against replay exposure A rising curve of relative replay exposure against the maximum accepted quote age, on a logarithmic axis. Exposure is negligible for quotes accepted within tens of seconds and grows steadily thereafter, reaching near-certainty at an hour, by which point a decommissioned machine's quote is still being accepted. Quote freshness against replay exposure a cached quote is a statement about a moment that has passed 1 10 100 1000 0 50 100 accepted quote age (seconds) relative replay exposure (%) replay window exposure (%)
Bind the quote to a fresh nonce from the owner and keep the age window in the tens of seconds. Nonce freshness needs no clocks and is the stronger of the two defences.

The five rejection cases in assertion 2 are the whole point. Each corresponds to a real deployment failure: a forged quote from a host with no enclave, a legitimately-signed quote from a different enclave, a platform missing security patches, a replayed quote from a decommissioned machine, and a key that was never inside the enclave at all.

Incident Response and Edge Cases

  • The vendor’s attestation service is down. Refuse new uploads. A cached quote has an expiry for a reason, and extending it during an outage converts a availability incident into a confidentiality one. Document the expected downtime behaviour before it happens.
  • A measurement changes unexpectedly. Either a legitimate build the owner has not approved, or a substitution. Both are handled the same way — refuse — and the difference is resolved by comparing against the build system’s record, not by asking the operator.
  • The TCB floor blocks the entire fleet after a vendor advisory. Correct behaviour. Plan for it: keep a documented, time-boxed exception process with a named approver, so the alternative to “block everything” is not “silently lower the floor”.
  • Clock skew rejects valid quotes. The freshness window compares timestamps across two machines. Use the owner’s clock for now, allow a small negative tolerance as the code does, and prefer nonce freshness — which needs no clocks — as the primary replay defence.
  • Attestation logs are kept but nobody reads them. They are the audit artefact. Alert on rejection rates, not just on individual failures: a sudden rise in unapproved-measurement rejections is either a bad rollout or an attack, and both warrant a page.
Approved-measurement lists accumulate A bar chart of approved-measurement entries grouped by age. Current and recent builds account for a minority of entries; older builds and entries whose provenance nobody remembers make up the majority. Every stale entry is a standing permission for an old binary to read production coordinates. Approved-measurement lists accumulate entries by age in a list that is never pruned 0 5 10 15 entries 3 current 5 1 release old 9 3 releases 14 6 releases 4 unknown provenance
Every entry is a statement that a specific binary may read production coordinates. Prune on the same schedule you rotate keys.

Keep the verifier small enough to read in one sitting. It is the component a data owner’s security team will actually review, and every branch in it is a place where a check can be made advisory under delivery pressure. A verifier with five explicit rejections and no configuration flags to disable them is far easier to approve than a flexible one, and it is the artefact that makes the rest of the deployment reviewable.

Frequently Asked Questions

Why is checking the measurement more important than the signature?

Both are mandatory, but they answer different questions. The signature proves a genuine enclave produced the quote; the measurement proves it is running the code you reviewed. An attacker with any enclave on any machine can produce a validly-signed quote — the measurement is what makes it yours.

Can the attestation be delegated to a third-party verifier?

The signature-chain part, yes. The measurement check cannot be meaningfully delegated unless the verifier also holds your approved-build list and your review process, at which point you have moved the trust rather than reduced it. Keep the measurement policy with the data owner.

How often should re-attestation happen?

At every session that transmits data, and on any platform event that could change the TCB. Long-lived channels should re-attest periodically, because the platform can be updated — or downgraded — underneath a session that was attested hours ago.

What belongs in the attestation log?

The measurement, the TCB version, the nonce, the verdict, and the identity of the approved-build entry that matched. Not the ephemeral key or any payload. That record is what lets an auditor reconstruct which code could have read which upload, which is the question they will ask.

Treat the approved-measurement list as the deployment’s most security-critical configuration file. It is small, it changes rarely, and every entry in it is a statement that a specific binary may read production coordinates — which makes it exactly the kind of file that accumulates stale entries nobody remembers approving. Prune it on the same schedule you rotate keys, and record for each entry the review that justified it.

Up one level: Trusted Execution for Spatial Workloads · Section: Secure Multi-Party Computation in Spatial Analytics.