This document describes the trust model, trust boundaries, and security assumptions of the Apache Teaclave™ SGX SDK. It has two audiences:
- Developers writing Intel SGX enclaves and their host applications with this SDK, who need to know where the security boundary is and what responsibilities fall on their code.
- Automated reviewers (including LLM-based audit agents), who need an explicit map of trust postures onto the repository's file structure so that findings are calibrated — flagging real boundary issues without raising false positives on code where the concern does not apply.
The model here follows Intel SGX and the
Intel SGX SDK / EDL
that this Rust SDK builds on. Nothing here weakens or replaces the SGX hardware
guarantees; the SDK provides ergonomic Rust bindings and a trusted std on top
of them.
This document describes the security model of the SDK and its boundary code. Individual samples under
samplecode/are illustrative proof-of-concept enclaves and are not hardened to this model.
An SGX system is partitioned into two domains. The processor enforces the partition in hardware:
| Untrusted host (REE) | Enclave (TEE) | |
|---|---|---|
| Runs | The host application, the OS, the hypervisor, the BIOS | The enclave: your trusted code + this SDK's trusted runtime |
| Trust posture | Untrusted | Trusted |
| Memory | Cannot read enclave memory (EPC is encrypted/access-controlled) | Can read its own memory and host memory |
| In this SDK | sgx_u* crates (e.g. sgx_urts, sgx_oc) |
sgx_t* crates (e.g. sgx_trts, sgx_tstd) |
The asymmetry matters: the enclave can read and write untrusted host memory, but the host cannot read enclave memory. That is why untrusted input is the hazard, not untrusted observation of enclave internals.
The following are trusted and assumed correct:
- The SGX-capable CPU and its microcode (the hardware root of trust).
- The Intel-provided architectural enclaves and the platform's SGX TCB.
- The enclave image itself, once measured (
MRENCLAVE) and loaded — including every Rust crate linked into it (see §5). - This SDK's trusted runtime (
sgx_trts,sgx_tstd, and the othersgx_t*crates) running inside the enclave.
The adversary is assumed to control everything outside the enclave, including privileged software. Concretely, the adversary can:
- Run the entire untrusted host: the OS kernel, the hypervisor, and the host application. They start, stop, and pause the enclave at will.
- Invoke any ECALL with arbitrary arguments — values, pointers, lengths, and buffer contents are all attacker-chosen.
- Return arbitrary values from every OCALL. Every service the enclave asks the host for (file I/O, time, network, randomness sourced outside SGX) returns attacker-controlled data.
- Read, map, unmap, and concurrently mutate any untrusted memory, including buffers an ECALL points the enclave at, during the call (Time-of-Check-to-Time-of-Use, TOCTOU / "double fetch").
- Delete, withhold, replay, or roll back anything the enclave persists outside the EPC, including sealed blobs and protected files (rollback / availability).
- Observe and manipulate the platform: schedule, interrupt, fault pages, and measure timing.
SGX — and therefore this SDK — does not defend against the following. A finding that depends only on one of these is generally not an SDK bug:
- Microarchitectural and side-channel attacks: cache timing, page-fault channels, branch prediction, and speculative-execution / transient-execution attacks (e.g. L1TF/Foreshadow, MDS). Mitigation is the platform's job.
- Availability / denial of service: the host controls scheduling and power and can refuse to run the enclave at any time.
- Rollback of sealed data when no anti-rollback mechanism (e.g. monotonic counters, an external freshness service) is used.
- Physical attacks beyond SGX's memory-encryption guarantee.
The boundary is the enclave edge, crossed by two call directions defined in EDL (Enclave Definition Language):
- ECALL — the host calls into the enclave. ECALL parameters are attacker-controlled.
- OCALL — the enclave calls out to the host. OCALL return values and output buffers are attacker-controlled.
UNTRUSTED HOST (untrusted) ││ ENCLAVE (trusted)
││
┌──────────────┐ ECALL ───────────► ││ ──────────► enclave logic
│ host app + │ (params untrusted) ││ (your code + sgx_tstd)
│ OS / VMM │ ◄─────────── OCALL ── ││ ◄────────── OCALL request
└──────────────┘ (return untrusted) ││
ENCLAVE EDGE ↑↑
ECALL params and OCALL returns are attacker-controlled,
and untrusted memory may mutate concurrently — validate
and copy-in before use; never trust an OCALL result for a
security decision.
The EDL files (see sgx_edl/edl/, e.g. sgx_edl/edl/sgx_file.edl) declare how
each pointer parameter crosses the edge. The generated edge routines act on
these annotations:
[in]— the marshaller copies the buffer into enclave memory before the call. The enclave then operates on a private copy (mitigates TOCTOU), but the contents are still attacker-chosen and must be validated.[out]— a buffer is allocated in enclave memory and copied back out on return. Do not write secrets here unless the caller is authorized to see them.[in, out]— copied in and back out.[string]/[size=...]/[count=...]— define the length the marshaller copies. A wrong or attacker-influenced size is the classic edge bug.[user_check]— no copy and no checking. The enclave receives a raw untrusted pointer and must validate it itself (see below) and must assume it can change under concurrent host access. This is the highest-risk annotation.
sgx_trts exposes the primitive range checks every enclave needs when handling
raw untrusted pointers:
is_within_enclave(p, len)andis_within_host(p, len)—sgx_trts/src/enclave/mem.rs:347. They validate that a pointer range lies fully inside the enclave, or fully outside it, with overflow-safe arithmetic.- The C ABI wrappers
sgx_is_within_enclave/sgx_is_outside_enclave—sgx_trts/src/capi.rs:163. - The
EnclaveRangetrait (is_enclave_range/is_host_range) for type-aware checks —sgx_trts/src/enclave/mem.rs.
These are obligations on trusted (sgx_t* / enclave) code, not provided
automatically:
- A
[user_check]pointer must be validated before any dereference. Confirm the entire range is outside the enclave withis_within_host(you are about to read host data) — never partially inside, which could trick the enclave into reading its own memory. - Copy untrusted input in once, then validate (avoid double-fetch / TOCTOU).
[in]does this for you; for[user_check], copy to enclave memory before validating and using. Never read the same untrusted field twice and assume it is unchanged. - Treat every byte of an ECALL buffer as adversarial input. Length, encoding, and structure must all be checked. Do not assume NUL-termination, well-formedness, or non-emptiness.
- Treat every OCALL result as adversarial. Return codes, output buffers, file contents, timestamps, and any "randomness" obtained via the host are attacker-controlled. Never base a security decision on an unauthenticated OCALL result; validate, bounds-check, and cryptographically verify where the value matters.
- Do not leak secrets through
[out]/[in, out]buffers or return values. Anything copied out becomes visible to the host. Size outputs deliberately and write only what the caller is authorized to learn. - Fail closed. On any validation failure, return an error — never proceed with partially validated input.
Use this table to decide whether a given concern (especially "untrusted input")
applies to a crate. This is the key reference for an automated reviewer. The
SDK encodes trust in the t / u naming convention and in Cargo features.
| Crate / path | Domain | Trust posture | What to scrutinize |
|---|---|---|---|
sgx_trts |
Enclave runtime | Boundary + trusted | The lowest layer of the boundary. Range checks (enclave/mem.rs, capi.rs), the entry/exit edge, exception handling. unsafe deref of untrusted pointers. |
sgx_tstd |
Enclave std |
Trusted | Anything that reaches host services via OCALL and presents results as trustworthy (fs, time, env, net). |
sgx_tseal |
Enclave | Trusted | Sealing/unsealing; AAD handling; assumptions about freshness of sealed blobs (rollback). |
sgx_tse, sgx_tdh |
Enclave | Trusted | Report generation, key derivation, local (DH) attestation. Verify reports before trusting a peer enclave. |
sgx_dcap/tvl, sgx_dcap/tkey_exchange |
Enclave | Trusted | DCAP quote verification inside the enclave and RA key exchange. Quote/collateral are untrusted input until verified. |
sgx_protected_fs/tfs |
Enclave | Trusted | Encrypted file I/O; integrity vs. rollback of the file on the host. |
sgx_rsrvmm, sgx_sync, sgx_alloc, sgx_unwind |
Enclave | Trusted | Enclave-resident runtime; unsafe memory handling. |
sgx_urts |
Host | Untrusted side | Loads/manages the enclave from the host. Runs in the adversary's domain — "missing validation" here is generally not an enclave-security finding. |
sgx_oc |
Host | Untrusted side | OCALL implementations. Whatever this returns, the enclave must re-validate; bugs here are host-side, not TCB. |
sgx_protected_fs/ufs, sgx_key_exchange/ukey_exchange, sgx_dcap/.../umsg |
Host | Untrusted side | Untrusted halves of protected-FS / RA. Not in the TCB. |
sgx_edl |
Interface definition | Boundary | The EDL is the contract. Check pointer annotations: is anything [user_check] that should be [in]? Are [size]/[count] correct? A wrong annotation is a boundary vulnerability. |
sgx_crypto, sgx_rand, sgx_serialize |
Feature-split | Depends on feature | These compile trusted (tcrypto/trand/tserialize) or untrusted (ucrypto/urand/userialize). Determine which side a given build links before judging. Inside the enclave they are TCB; sgx_rand trusted must use an in-enclave entropy source. |
sgx_types, sgx_ffi, sgx_libc |
Shared / FFI | Below the type system | ABI struct definitions and libc surface. The Rust type system does not protect callers; review unsafe and ABI correctness. |
sgx_macros, sgx_demangle, sgx_build_helper, sgx_no_tstd, sgx_tests |
Build-time / tooling | Build-time | Not in the runtime TCB. Review as ordinary tooling (but note build-time supply-chain risk, §5). |
samplecode/ |
Example enclaves + hosts | Illustrative, not hardened | Teaching material. Note copy-risk patterns (e.g. trusting an OCALL result) but don't report them as production vulnerabilities. |
- Sealing (
sgx_tseal) gives confidentiality and integrity, not freshness or availability. A sealed blob is bound toMRENCLAVE(this exact enclave) orMRSIGNER(any enclave from the same signer), but it is stored in the untrusted host, which can delete, withhold, or roll it back to an earlier version. Anti-rollback requires an external mechanism (monotonic counter or freshness service); it is not provided by sealing alone. MRENCLAVEvsMRSIGNERis a security decision.MRSIGNERsealing lets a future or different enclave from the same signer unseal the data — broader exposure thanMRENCLAVE. Choose deliberately.- Attestation establishes trust; it is not automatic. Local attestation
(
sgx_tdh) and remote attestation (sgx_key_exchange,sgx_dcap) let a peer verify an enclave's measurement before exchanging secrets. The report/quote and its collateral are untrusted input until verified by trusted verification code (e.g.sgx_dcap_tvl). - OCALL results are never a security oracle. Time, file existence, configuration, and any value crossing back from the host can be forged. Use authenticated/verified data for security decisions.
- Secrets must not cross to the host in cleartext unless the application's threat model explicitly accepts it.
- Randomness inside the enclave must come from SGX (
RDRAND/sgx_read_rand), not from a host OCALL.
This is an SGX-specific concern that differs sharply from ordinary applications: every crate linked into the enclave runs inside the enclave and is therefore part of the TCB. A vulnerability or backdoor in an enclave dependency is not "just" code execution in a host process — it is code execution inside the enclave, with access to whatever secrets and sealing/attestation keys the enclave holds. The boundary of §2 stops attacker input at the edge; it does not sandbox the enclave's own dependencies.
A headline feature of this SDK is that "most Rust crates work without modifications" and that Tokio/Tonic and similar run unmodified in the enclave. That ergonomics win cuts both ways: arbitrary third-party code is pulled straight into the TCB.
| Dependency kind | Executes | Trust domain |
|---|---|---|
Crates linked into the enclave (sgx_t* + your trusted deps) |
At runtime, inside the enclave | TCB — fully trusted, no sandbox |
Crates linked into the host app (sgx_u* + host deps) |
At runtime, in the untrusted host | Untrusted domain (not enclave-security-relevant) |
[build-dependencies], proc-macros (sgx_macros), edger8r/EDL codegen, build scripts |
At build time on the developer/CI host | Build host — build-time code execution; also affects MRENCLAVE |
- The TCB includes the full transitive dependency tree linked into the enclave. When reviewing an enclave, in-scope code is not only the enclave crate — it is every crate it links, including async runtimes, parsers, and crypto.
no_stdvs std-aware (BUILD_STD) is a TCB-size decision, not only an ergonomics one. The smaller the code linked into the enclave, the smaller the TCB and the smaller theMRENCLAVE-measured surface. Prefer the minimal dependency set that meets the requirement.- Software crypto and key handling run in enclave memory. That is expected under SGX (memory is protected), but it means key material lives in EPC and the implementation's correctness and side-channel resistance are your concern.
- Randomness must be SGX-backed. Any crate that needs entropy inside the
enclave must source it from
RDRAND/sgx_read_rand, never from a host OCALL. Verify the entropy path of crypto dependencies. - Build-time dependencies affect the measurement. Proc-macros, build
scripts, and the EDL/edge code generator execute arbitrary code on the build
host and determine the enclave binary that gets measured into
MRENCLAVE. Treat the build host and CI as part of the trust chain.
- Pin and lock enclave dependencies (commit
Cargo.lock) so the measured enclave is reproducible. - Run
cargo audit(RUSTSEC advisories) and considercargo denyto enforce licenses and ban yanked/duplicate crates. - Consider
cargo vetor vendoring for the subset of crates linked into the enclave. - Minimize the enclave dependency tree and review each new enclave dependency as a TCB addition, with the same scrutiny as first-party enclave code.
Note to the reviewing agent: When you audit an enclave, do not stop at the first-party enclave code — also review its dependencies whenever your tooling and context allow it. Everything linked into the enclave runs inside the TCB (§5). If you can access the dependency sources or lockfile, enumerate the enclave's transitive dependencies, flag any that are unpinned, yanked, or carry known RUSTSEC advisories, and pay particular attention to crates that handle crypto, randomness/entropy, or deserialization inside the enclave. If you cannot reach the dependency sources, say so explicitly and list the enclave's declared dependencies as unreviewed TCB surface rather than silently skipping them.
To keep findings high-signal:
Where real findings concentrate
- ECALL entry points and anything reachable from them that reads ECALL parameters.
- Every
[user_check]pointer: is the full range validated withis_within_hostbefore dereference? Is it copied in once (no double-fetch / TOCTOU)? - EDL annotations (
sgx_edl/): a[user_check]that should be[in], or a wrong[size]/[count], is a boundary vulnerability. - Code that trusts an OCALL return value (time, file contents, config, status) for a security decision.
unsafeblocks insgx_trtsand the shared/FFI crates (sgx_types,sgx_libc,sgx_ffi) that dereference untrusted pointers or cross the ABI.- Sealing/attestation code: unverified reports/quotes,
MRSIGNER-vs-MRENCLAVEchoices, and rollback assumptions on sealed blobs. - Output buffers / return values that might leak more than intended.
- Enclave dependencies (§5): the in-scope TCB is the enclave's full transitive crate tree — including software crypto, the entropy/RNG path, and deserialization of attacker-influenced data inside the enclave.
Expected non-findings (avoid these false positives)
- "Missing input validation in the host" — host code (
sgx_urts,sgx_oc,*ufs,u*) is in the untrusted domain; the enclave must validate regardless, so host-side validation is not a security control. - Side-channel / speculative-execution / availability issues attributed to the SDK — these are outside the SGX threat model (§1) unless the SDK does something specifically worse than the platform baseline.
- Treating
samplecode/as production code. Note copy-risk patterns, but frame them as illustrative. - Flagging
unsafein FFI/shared crates merely for existing — FFI isunsafeby necessity. The finding must be a concrete mismatch or misuse.
Before reporting, state which side of the enclave edge the code runs on and which adversary capability (§1) the issue depends on. If a finding does not trace to a concrete adversary capability crossing the boundary, it is likely a false positive.
Security issues in the SDK itself should be reported privately first, per
SECURITY.md, before any public disclosure.