Skip to content

Threat Model

Teton treats the host OS, hypervisor, and cloud operator as untrusted. The trusted computing base is the enclave silicon, the measured workload, and the on-chain verifier.

Dimension GCE Confidential VM (AMD SEV-SNP) AWS Nitro Enclaves
Isolation AES memory encryption plus Reverse Map Table Nitro ASIC core and memory partition
Device Linux /dev/sev-guest (SNP_GET_REPORT) Linux /dev/nsm (NSM_REQUEST_ATTESTATION)
Report 1,184-byte binary, ECDSA P-384 / SHA-384 COSE Sign1 CBOR, ECDSA P-384
Root AMD ARK → ASK → VCEK / VLEK AWS Nitro Root CA → intermediate → leaf
Measurement 48-byte SHA-384 launch digest PCR0 (EIF), PCR1, PCR2, PCR8
User binding 64-byte REPORT_DATA user_data (32-byte pubkey) and nonce
Network Full VM, encrypted RAM No persistent disk, vsock only
On-chain path register_vcek then verify_and_register / verify_atomic_with_nonce verify_nitro_attestation (mode 0 or 1)

The verifier binds three facts into every accepted report:

  1. Code identity. Measurement (SNP digest or Nitro PCR0) is on the PolicyRegistry allowlist. An all-zero measurement fails closed (VerifierError::DisallowedMeasurement).
  2. Key identity. Mode A REPORT_DATA[0..32] (or Nitro user_data) equals the workload Ed25519 pubkey. Mode B REPORT_DATA[32..64] equals SHA-256(slot_hash ‖ action_hash ‖ consumer_program_id).
  3. Freshness. SlotHashes at nonce_slot must match, with age clock.slot - nonce_slot ≤ 150 slots. A miss is VerifierError::StaleNonce.

Replay inside that window fails with VerifierError::NonceAlreadyUsed. The tombstone is a UsedNonceAccount PDA seeded [b"nonce", signer_pubkey, nonce].

Mode B always consumes that PDA. verify_atomic returns VerifierError::NonceRequired and writes nothing (@spec TETON-SEC-005).

Gate Failure
Debug / migrate policy bits VerifierError::UnauthorizedPolicy (@spec TETON-SEC-001)
TCB below min_tcb_* VerifierError::TcbBelowMinimum
P-384 / COSE signature VerifierError::InvalidSignature
Missing /dev/nsm or ioctl error Sidecar SidecarError::IoctlError (no synthetic production document)
Missing Nitro cabundle CryptoError::InvalidAttestationPayload
HTTP bind off loopback Sidecar refuses to listen
require_nonce == false CpiError::NonceRequired
Packet above 1,232 bytes (legacy UDP) TypeScript PacketTooLargeError

The sidecar HTTP API binds only 127.0.0.1 or ::1. DRY_RUN defaults to true. The on-disk payer is a fee payer. It is never the workload signer.

  • Host memory dumps of the signing key
  • Operator phishing of the hot wallet
  • Launch-image substitution (measurement changes)
  • Side channels (Spectre, CacheWarp, INCEPTION). Mitigate with min_tcb microcode floors.
  • Host I/O delay or drop. Sign inside the enclave before the payload leaves vsock.
  • Host clock. Freshness uses Solana SlotHashes, never guest wall time.
  • Mode A TTL. An attested Ed25519 key stays valid until expires_slot (216_000 slots). There is no revoke_signer instruction. Consumers must call assert_attested_signer.