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 |
Reference Implementation
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
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()
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.
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.
Related
- Trusted Execution for Spatial Workloads — the parent design and its trust boundary.
- Enclave-Side Spatial Joins with Sealed Data — what runs once the channel is open.
- Side-Channel Risks for Geospatial Enclave Queries — the risks attestation does not address.
- Compliance Framework Mapping — where the attestation record becomes audit evidence.
Up one level: Trusted Execution for Spatial Workloads · Section: Secure Multi-Party Computation in Spatial Analytics.