Mode A — Stateful Session Caching
Mode A registers a hardware-attested Ed25519 key once. Later consumer transactions treat that key as a session signer.
Use Mode A for high-frequency work: quotes, heartbeats, streaming oracles.
- The enclave generates an ephemeral Ed25519 key in encrypted RAM.
- Hardware signs a report with
REPORT_DATA[0..32] = pubkeyandREPORT_DATA[32..64] = slot_hash. - The client submits
verify_and_register. - The verifier checks VCEK (or Nitro COSE), measurement, TCB, SlotHashes age ≤ 150, and burns a
UsedNonceAccount. - The verifier writes
AttestedSignerAccountwithexpires_slot = clock.slot + 216_000. - The consumer calls
assert_attested_signer(orAttestedSignerAccount::validate_session_signer). Subsequent transactions use native Ed25519 and skip P-384.
Stage 2 stays under 550,000 CUs (@spec TETON-PERF-001). Mock-crypto SBF harness: verify_and_register ≈ 62,734 CUs. The consumer PDA check is bounded at 1,500 CUs (@spec TETON-CONSUMER-001).
ATTESTATION_TTL_SLOTS = 216_000 (~24 hours at 400 ms slots). The verifier writes expiry from Clock. Clients pass expiresSlot only as bookkeeping.
Re-attestation of a live PDA is allowed when current_slot > expires_slot, or when the stored pubkey matches and a fresh unused nonce verifies (@spec TETON-VERIFY-004).
Wire layout (verify_and_register)
Section titled “Wire layout (verify_and_register)”Discriminator sha256("global:verify_and_register")[..8]:
[246, 249, 100, 14, 102, 116, 92, 145]
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | Discriminator |
| 8 | 8 | nonce_slot (u64 LE) |
| 16 | 1184 | SNP report (zero-copy borrow, @spec TETON-ARCH-002) |
Total instruction data: 1,200 bytes.
Accounts: payer, policy_registry, vcek, attested_signer, used_nonce, SlotHashes, Clock, system_program.
Nitro Mode A uses verify_nitro_attestation with mode = 0. See the instruction matrix.
AttestedSignerAccount
Section titled “AttestedSignerAccount”PDA seeds: [b"attested_signer", signer_pubkey]. Size 146 bytes. Discriminator [97, 156, 233, 39, 50, 169, 179, 13].
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | Discriminator |
| 8 | 32 | signer_pubkey |
| 40 | 1 | enclave_type |
| 41 | 48 | measurement |
| 89 | 32 | chip_id_hash |
| 121 | 8 | tcb_version LE |
| 129 | 8 | registered_slot LE |
| 137 | 8 | expires_slot LE |
| 145 | 1 | bump |
tcb_version and slot fields sit at unaligned offsets, so the CPI type stores them as [u8; 8] (bytemuck::Pod, align 1).
Consumer check
Section titled “Consumer check”let attested = teton_cpi::AttestedSignerAccount::try_from_account_info(attested_info)?;attested.validate_session_signer(workload_signer, &EXPECTED_MEASUREMENT)?;validate_session_signer requires workload_signer.is_signer, pubkey equality, measurement match, and Clock::get()?.slot <= expires_slot (@spec TETON-SEC-004). Owner plus discriminator authenticate the record. PDA re-derivation is optional defense-in-depth and costs 1,500 CUs.
The helper teton_cpi::assert_attested_signer also re-derives the PDA.
PDA lifecycle (@spec TETON-ARCH-001)
Section titled “PDA lifecycle (@spec TETON-ARCH-001)”New PDAs allocate through System Program create_account, funded by payer. The program asserts owner == program_id and is_writable on writable accounts. Stage 2 prefers client-supplied bumps and create_program_address over find_program_address.