STEVE: End-to-end encryption for enclaves
TL;DR
TLS alone is insufficient for enclave security. When TLS terminates outside the enclave, the host system can read all traffic in plaintext, negating the security guarantees of the enclave.
STEVE (Secure Transport Encryption Via Enclave) solves this by providing a second layer of encryption that terminates exclusively inside the enclave. Using X25519 key exchange, Ed25519 signatures, and AES-256-GCM encryption, STEVE ensures that only verified enclave code can decrypt application traffic. A browser service worker handles encryption transparently, requiring no changes to existing applications.
Table of contents
- Introduction
- The TLS termination problem
- STEVE: A second encryption layer
- Browser integration via service workers
- Server-side architecture
- Threat model
- Conclusion
- Try STEVE with Caution
Introduction
Secure enclaves, amongst other things, promise confidentiality: the ability to run sensitive computations on untrusted infrastructure while keeping data private. AWS Nitro Enclaves, Intel TDX, and AMD SEV-SNP all provide hardware-enforced isolation that prevents the host system from accessing enclave memory. The latter two even provide CPU enforced memory encryption.
But there’s a gap between what enclaves provide and what applications actually need.
Consider a typical enclave deployment: an application runs inside the enclave, serving HTTPS traffic. TLS certificates are managed by a reverse proxy or load balancer in a network-attached environment for operational simplicity. The encryption terminates at the host, and plaintext traffic flows into the enclave.
This architecture has a fundamental problem: the host sees everything.
A compromised host, a malicious cloud operator, or a sophisticated attacker with infrastructure access can intercept all traffic before it reaches the enclave. The hardware isolation becomes meaningless because the sensitive data is exposed before it ever enters the protected environment.
The TLS termination problem
TLS was designed to protect data in transit between a client and a server. It assumes the server endpoint is trusted. In the enclave context, this assumption breaks down.
| Architecture | Who Terminates TLS | Who Sees Plaintext |
|---|---|---|
| Traditional | Application server | Application server |
| CDN/Load balancer | Edge infrastructure | CDN + Application |
| Enclave (typical) | Host/proxy | Host + Enclave |
| Enclave (with STEVE) | Host/proxy (outer) + Enclave (inner) | Enclave only |
When TLS terminates outside the enclave, the security model reduces to trusting the cloud provider and every piece of infrastructure between the client and the enclave. For applications handling cryptographic keys, financial transactions, or sensitive personal data, this is unacceptable.
Some deployments attempt to solve this by terminating TLS inside the enclave. This introduces operational complexity: certificate management, key rotation, and ACME challenges all become significantly harder when the enclave has no direct network access (as it shouldn’t). Alternatively, having access to the public internet adds additional surface for attacks and exploitations.
STEVE: A second encryption layer
STEVE takes a different approach. Instead of replacing TLS, it adds a second encryption layer that terminates exclusively inside the enclave. TLS continues to provide transport security and certificate-based authentication. STEVE adds enclave-specific confidentiality.
The architecture is straightforward:
Browser Host Enclave
| | |
|------ TLS (outer) ---->| |
| |------ plaintext -------->|
| | |
|====== E2E (inner) ===============================>|
From the host’s perspective, all traffic is opaque binary data. It can route packets, but it cannot read their contents. The enclave is the only entity with the keys to decrypt application traffic.
Cryptographic design
STEVE uses modern, well-audited cryptographic primitives:
| Component | Algorithm | Purpose |
|---|---|---|
| Key exchange | X25519 ECDH | Establish shared secret |
| Authentication | Ed25519 | Sign key exchange responses |
| Key derivation | HKDF-SHA256 | Derive session key from shared secret |
| Encryption | AES-256-GCM | Encrypt request/response payloads |
| Serialization | CBOR | Encode encrypted HTTP messages |
The key exchange is bound to the enclave’s attestation. The enclave generates an Ed25519 signing key at startup and includes the public verifying key in its attestation document. When clients perform key exchange, the enclave signs its ephemeral X25519 public key with this attestation-bound key. Clients verify the signature against the key from attestation, ensuring they’re establishing a channel with the attested enclave and not an impersonator.
Session establishment
The protocol follows these steps:
-
Client requests attestation: The browser fetches an attestation document from the enclave, including a client-generated nonce for freshness.
-
Client verifies attestation: Using the hardware root of trust (e.g., AWS Nitro’s certificate chain), the client verifies the attestation document is genuine and the PCRs match expected values.
-
Client extracts verifying key: The attestation document’s user data contains the enclave’s Ed25519 public key.
-
Key exchange: The client generates an ephemeral X25519 keypair, sends its public key to the enclave, and receives the enclave’s ephemeral public key plus a signature.
-
Client verifies signature: The client checks that the enclave’s key exchange response is signed by the attested key.
-
Shared secret derivation: Both parties compute the X25519 shared secret and derive an AES-256-GCM session key via HKDF.
-
Encrypted communication: All subsequent requests are encrypted with the session key before being sent over the TLS connection.
The enclave also encrypts the derived shared key with a persistent internal key and returns this “enclave-encrypted shared key” to the client. On each request, the client sends this encrypted blob in a header, allowing the enclave to recover the session key without maintaining state.
Browser integration via service workers
The client-side implementation uses a service worker to provide transparent encryption. Once registered, the service worker intercepts all fetch requests to the enclave and handles encryption/decryption automatically. Application code doesn’t need to change.
Behind the scenes, the service worker:
- Serializes the request (method, path, headers, body) as CBOR
- Encrypts the CBOR payload with AES-256-GCM
- Sends the encrypted blob with a custom header
- Receives an encrypted response
- Decrypts and reconstructs the original HTTP response
The service worker also handles session management, including automatic key rotation at configurable intervals (default: 30 minutes).
Currently a JavaScript SDK is available, with a Rust SDK in development.
While not yet implemented, we intend on expanding the scope of the service worker in the future to also prevent it from being updated except by signatures by m-of-n administrators, eliminating the chance of a compromised server or system administrator having the power to push a compromised service worker to clients.
We will cover this extension in a future blog post, at which time the service worker portion of this approach will be more suitable for production use without single points of failure.
Passthrough for bootstrap resources
Certain paths must remain unencrypted for the system to bootstrap:
/- The initial HTML page that loads the service worker/enclave-sw.js- The service worker itself/register.js- Service worker registration code/attestation- The attestation endpoint/e2p/*- Key exchange endpoints
These paths are served in plaintext over TLS. Once the service worker is active and has established an encrypted session, all other requests are encrypted.
This creates a trust-on-first-use model: the initial page load must be trusted. For applications requiring stronger guarantees, the service worker code can be pinned or verified out-of-band.
Graceful degradation
STEVE supports applications that may receive both encrypted and unencrypted traffic. If a request arrives without the custom header, STEVE forwards it to the upstream application unmodified.
Server-side architecture
On the enclave side, STEVE runs as a reverse proxy in front of the application:
Enclave
┌─────────────────────────────────────┐
TLS-encrypted │ │
─────────────> │ STEVE ──> Application │
traffic │ (8080) (port 8083) │
└─────────────────────────────────────┘
STEVE listens on port 8080, decrypts incoming requests, and forwards them to the application on port 8083. Responses flow back through STEVE, which encrypts them before sending to the client.
The application remains unaware of the encryption layer. It receives standard HTTP requests and returns standard HTTP responses. This separation of concerns means existing applications can be deployed in enclaves with E2E encryption without code changes.
Integration with EnclaveOS
In Caution deployments, STEVE is built into the enclave image automatically. The init system starts STEVE before the user application, and vsock proxies route external traffic through STEVE. Developers don’t need to configure or manage the encryption layer.
The attestation service runs alongside STEVE, providing the /attestation endpoint that clients use to verify the enclave and obtain the verifying key.
Threat model
What STEVE protects against
| Threat | Mitigation |
|---|---|
| Compromised host reading traffic | All application data encrypted with keys only the enclave holds |
| Man-in-the-middle on key exchange | Signatures verified against attestation-bound key |
| Replay attacks | Nonces in attestation and key exchange |
| Session hijacking | Keys rotated periodically; enclave-encrypted shared key cannot be forged |
| Cloud provider surveillance | Operator sees only encrypted blobs |
What STEVE does not protect against
| Threat | Limitation |
|---|---|
| Compromised enclave code | If the application has vulnerabilities, encryption won’t help |
| Side-channel attacks on enclave | STEVE is software; it cannot mitigate hardware-level attacks |
| Denial of service | The host can always drop traffic |
| Trust-on-first-use attacks | First page load installs service worker to protect future page loads |
| Browser compromise | A compromised browser can access plaintext |
| Server compromise | Server can update the service worker. This will be addressed in future work |
STEVE is one layer in a defense-in-depth strategy. It eliminates the host as a trusted party for confidentiality, but it doesn’t replace secure coding practices or hardware-level protections. However, assuming those issues are resolved, STEVE becomes a component in a highly secure architecture.
Conclusion
TLS is necessary but not sufficient for enclave security. When TLS terminates outside the enclave, the host becomes a trusted party with access to all plaintext traffic. This undermines the confidentiality guarantees that enclaves are meant to provide.
STEVE adds a second encryption layer that terminates inside the enclave, bound to hardware attestation. Clients verify they’re communicating with attested enclave code before establishing an encrypted channel. The host sees only opaque ciphertext.
The implementation is transparent to applications: a service worker handles browser-side encryption, and STEVE runs as a reverse proxy inside the enclave. Existing applications can be deployed with E2E encryption without code changes.
For applications where data confidentiality matters, trusting a network-attached host is not acceptable. STEVE removes that trust requirement.
Try STEVE with Caution
STEVE is integrated into Caution, the verifiable compute platform built by members of the Distrust team. Deploy an application and STEVE handles E2E encryption automatically.
Source code: git.distrust.co/public/steve
About Distrust
Distrust is a security consulting and R&D firm focused on practical engineering, a no-compromise approach to security, and open source software. Our team has built and secured some of the highest-risk systems in the world. This includes vaulting infrastructure at BitGo, Unit410, Ledn, and Turnkey, as well as security engineering for electrical grid operators, industrial control systems, and other mission-critical environments.
We have conducted deep security due diligence across most major custodians. Working with organizations that operate under constant threat, where every class of attack is viable, led us to develop a practical methodology and build a suite of open source tools designed to defend against even the most sophisticated adversaries.
Our goal is to share our hard-earned lessons with the broader community and help strengthen overall security posture by making our knowledge and tools available to everyone.
Looking for help analyzing and mitigating security risks in your own organization? Talk to us.