Intel SGX Enclave Development with the Open Enclave SDK
How to design SGX enclaves that don't leak secrets at the boundary.

SGX works by partitioning a region of physical memory called the Enclave Page Cache, or EPC. This region is encrypted by dedicated hardware and is inaccessible to any memory accessor outside the enclave, including the OS kernel, the hypervisor, and other enclaves. The encryption key lives inside the CPU package and changes every power cycle; tapping physical DRAM yields only ciphertext.
The practical constraints of this architecture are more binding than they first appear. Total EPC size is 128 MB, of which roughly 93 MB is usable for user code and data; the remainder is reserved for enclave metadata. Memory is managed in 4 KB pages. On Linux, EPC can be over-committed via a kernel paging mechanism that swaps pages between trusted EPC and untrusted DRAM, but integrity verification on each swap introduces overhead that degrades performance by an average of roughly 5x. For memory-hungry workloads, this is a hard design constraint, not a footnote.
The isolation guarantee itself is strong: enclave memory cannot be read or written from outside the enclave regardless of privilege level, ring 3, ring 0, System Management Mode, or the virtual machine monitor. This is enforced in hardware, not by software policy.
SGX also requires an explicit choice at build time between debug mode and production mode. A debug enclave permits an SGX-aware debugger to inspect its contents; a production enclave cannot be inspected by software or hardware. This is not merely a build flag. It affects attestation, and remote parties will reject a debug enclave as a production artifact.
Two limitations require candor. First, SGX does not protect against side-channel attacks. Cache-timing attacks, branch-predictor side channels, and speculative execution leakage are the developer's problem. The hardware makes no claim otherwise, and treating the TEE as a general-purpose isolation solution without addressing these vectors produces false confidence. Second, SGX hardware mode requires processors with Flexible Launch Control support; specific Intel Xeon Scalable, Xeon D, Xeon E, Core, and Atom families qualify, but availability cannot be assumed without verification. That raises an important question: if the hardware guarantees are this narrowly scoped, what does the developer actually own, and where does the threat model begin to rely on discipline rather than silicon?
The Trusted/Untrusted Split and Why Getting the Boundary Wrong Undermines Everything
Every SGX application divides into two components. The trusted component, called the enclave, is where secrets live and where protected computation happens. The untrusted component handles everything else: OS interaction, file I/O, networking, logging. In Open Enclave SDK terminology, the untrusted component is called the host and the trusted component is simply the enclave. Fix this vocabulary early, because the entire development model follows from it.
The enclave's text and data segments are protected from access by the host, by privileged users, and by any hardware layer outside the CPU package. What the hardware cannot protect is the interface between the two components. The interface is defined by the developer, and the interface is where most design errors accumulate.
A large enclave with a complex interface is a large attack surface. Every function pulled into the trusted component must be audited as security-critical; any vulnerability in that code operates at the highest possible sensitivity level. The practical heuristic: move only the code that directly handles secret data into the enclave. Networking, logging, configuration parsing, and user interface logic belong in the host.
The boundary is crossed through two mechanisms. ECALLs are entry points the host calls into the enclave. OCALLs are functions the enclave calls out to the host when it needs capabilities it cannot access directly, such as system calls, file I/O, or network operations. The receiving side in each direction must treat inputs as untrusted; this is an architectural requirement, not a convention.
Two design mistakes recur. The first is pulling too much logic into the enclave under the assumption that more protection is better. It is not. A larger trusted codebase means more auditable surface and more potential for logic errors that compromise secrets. The second is passing large composite structures across the boundary without carefully considering what gets copied, what gets verified, and whether the data can be modified between check and use. Both mistakes tend to be made by engineers who understand SGX conceptually but have not yet felt how much discipline the boundary demands when something goes wrong at 2 a.m. and the audit log is the only thing standing between you and a disclosure conversation. One might argue that the boundary itself is the most important security artifact in the entire design — more so than the cryptographic primitives inside the enclave — and that argument is difficult to refute once you have watched a well-intentioned ECALL become an entry point for confused-deputy abuse.
How the Enclave Definition Language Formalizes and Enforces the Boundary in Code
The Enclave Definition Language, EDL, is the file format in which all ECALLs and OCALLs are formally declared. It is both a design document and a build artifact; it exists before any implementation and drives the code generation that produces the boundary-crossing stubs.
An EDL file has two sections. The trusted section declares ECALLs, the functions the host can call into the enclave. The untrusted section declares OCALLs, the functions the enclave can call out to the host. From this file, a code generator, called Edger8r in the Intel SGX SDK and an equivalent tool in the OE SDK, parses the definitions and generates proxy function pairs for each declared crossing.
The generated files come in two halves. The trusted half is a stub inside the enclave that receives the call and marshals the data. The untrusted half is a stub in the host that initiates the call and handles return values. In the Intel SDK convention these files follow the naming pattern EnclaveProjectt.h and.c for the trusted side, and EnclaveProjectu.h and.c for the untrusted side. Treat these as generated artifacts. Editing them by hand breaks the contract between the EDL and the implementation, and that break tends to surface at the worst possible moment.
Pointer direction attributes on ECALL parameters are security-relevant declarations, not stylistic annotations. The [in] attribute means data flows from the host into the enclave; [out] means data is returned from the enclave to the host. Misattributing direction is a security defect, not a type error, because it determines what memory gets copied and what gets verified. Buffer sizes must also be specified; the code generator uses these to produce correct marshaling and validation code that prevents the enclave's memory from being overwritten. Annotations treated as an afterthought rather than a primary design concern produce defects that are often invisible until the system is under adversarial pressure.
The generated bridge code does real security work. It verifies ECALL input parameters, guards against TOCTOU vulnerabilities by copying data into trusted memory before checking it, and enforces the boundary in ways that hand-written code frequently gets wrong. But how does this affect our original promise? If the generated code absorbs that complexity correctly, the developer's obligation shifts entirely to the EDL declarations themselves — which means an imprecise annotation is no longer just a documentation problem; it is the actual defect. The OE SDK also governs system OCALLs through the same EDL mechanism; developers can explicitly refuse to import a function, which causes a linker error. This forces a deliberate review of every boundary crossing the enclave permits, which is exactly the kind of friction that catches oversights before they become incidents.
What the Open Enclave SDK Provides and How It Abstracts the SGX Hardware Layer
The Open Enclave SDK is an open-source, MIT-licensed SDK maintained primarily by Microsoft with broad community contribution; the repository is at github.com/openenclave/openenclave. Its core design goal is a unified API surface for enclave development that is not tied to a single hardware vendor. Currently it supports Intel SGX at production quality and ARM TrustZone via OP-TEE in preview status.
What the SDK delivers is a set of well-defined abstractions over the raw SGX instruction set. The runtime header for use inside the enclave is enclave.h; the runtime header for the untrusted host application is host.h. Because the enclave cannot call the system libc directly, the SDK provides its own implementations of libc and libcxx for use within the trusted boundary. mbedTLS is also available inside the enclave for cryptographic operations, covering key derivation, encryption, and signature verification without requiring a link against an external library that may carry unsafe assumptions about its execution context.
The SDK supports three operating modes. SGX1 covers generic SGX features on qualified hardware. SGX1 with Flexible Launch Control adds the capability needed for DCAP-based attestation. Simulation mode runs on machines without SGX hardware entirely in software, enforcing no real isolation guarantees but providing a functional development environment for early-stage work and CI pipelines. One constraint that is easy to miss: OE SDK does not support 32-bit applications.
The SDK incorporates attestation and secure storage as first-class features. Attestation, the mechanism by which a remote party verifies that code is running inside a valid SGX enclave on real hardware, is complex enough that building it from raw SGX instructions is a significant engineering investment on its own. The SDK absorbs that complexity.
It is also worth considering how OE SDK compares against the Intel SGX SDK before committing to either. The Intel SGX SDK is developed and maintained directly by Intel's SGX team, is C/C++-focused, and is tightly coupled to Intel's ecosystem and toolchain. It offers more SGX-specific depth and direct access to platform capabilities. OE SDK trades some of that depth for cross-platform portability and a vendor-neutral development model. If portability across TEE architectures is a priority, or if the project may eventually run on non-Intel hardware, OE SDK's abstraction layer is a reasonable choice. If the requirement is maximum access to Intel-specific SGX capabilities with no expectation of cross-platform deployment, the Intel SDK warrants evaluation alongside it. The choice depends on what the workload looks like in three years, and that is worth thinking through before the first line of code is written.
Setting Up a Development Environment and Choosing the Right Mode Before Writing Any Enclave Code
The first decision, made before installing anything, is whether to work in hardware mode or simulation mode. Simulation mode allows development and functional testing on any machine without SGX-capable hardware. It is the practical starting point for most developers, particularly those on laptops or CI machines without verified SGX support. Hardware mode requires a processor with Flexible Launch Control and carries the additional complexity of driver management and hardware verification; it is essential before any production deployment or attestation testing.
For hardware mode development without dedicated on-premises machines, Azure Confidential Computing virtual machines expose SGX hardware directly to the tenant. This is the path of least resistance for hardware-mode work at any scale.
The Linux setup path for OE SDK follows a clear sequence. Install the Intel SGX driver, which provides kernel access to SGX instructions and EPC management. Install the OE SDK package from the openenclave release artifacts. Then verify the installation by running the SDK's built-in sample enclaves. This step confirms that the driver, the SDK, and the hardware are correctly wired together before any application-specific code enters the picture. Skipping it tends to produce debugging sessions that would have been unnecessary. OE SDK also supports Windows development with Visual Studio; the same core concepts apply, though toolchain integration differs.
The SDK uses CMake as its build system. Understanding the CMake targets for the enclave and host components separately is necessary before customizing the build; the enclave and host compile with different flags, different include paths, and different linking constraints.
Signing deserves attention before the first line of enclave code is written. Every enclave must be signed before it can be loaded. The enclave's identity, encoded as MRENCLAVE and MRSIGNER values, is derived from its binary content and signing key and is used during attestation to prove to remote parties exactly what code is running. Treating signing as an afterthought creates attestation problems that are difficult to unwind. Setting up signing keys correctly at this stage, and understanding what measurements attestation verifiers will expect, is architectural work. Signing is not a final step; it is a foundational one, and the setup sequence only holds if it is treated that way from the beginning.
Debug versus release build mode should also be decided consciously here. Debug enclaves carry the debug attribute in their metadata, permit SGX-aware debugger inspection, and are rejected by remote attestation verifiers as production artifacts. The workflow for each is different enough that switching late in development carries real cost.
Writing and Structuring the Enclave: From EDL Definition to the First Working Trusted Function
Start with the EDL file. Before it is a build artifact it is a design document, the formal statement of what the trusted boundary looks like. Define the minimum set of ECALLs the host actually needs; every function added here is a function that must be secured. Declare all OCALLs the enclave will need to operate: print output, file access, network calls, any interaction with the outside world requires an explicit OCALL declaration. Annotate every pointer parameter with [in], [out], or [in, out] and specify buffer sizes. The code generator depends on these annotations to produce correct marshaling and to prevent the categories of memory safety bugs that have historically plagued trusted code.
Run the OE SDK EDL code generator to produce the trusted and untrusted proxy stubs. Do not edit these files by hand; they are generated artifacts, and modifications will be overwritten or will diverge from the EDL in ways that are not immediately obvious.
On the enclave side, implementing the ECALL bodies means including openenclave/enclave.h as the entry point to the OE SDK runtime. The enclave cannot call the system libc; use the SDK-provided libc and libcxx headers, which are re-implementations safe for execution inside the trusted boundary. mbedTLS is available for cryptographic operations. One behavioral detail worth understanding early: enclave global state persists across ECALLs within a session. Be deliberate about what is stored in global scope and for how long, because this state is shared across all ECALL invocations into the same enclave instance. The enclave instance is a persistent execution environment, not a request handler that initializes fresh on each invocation, and that distinction changes how shared state must be reasoned about. Engineers who miss it tend to discover it under load, which is not the ideal classroom.
On the host side, the application includes openenclave/host.h and loads the enclave with oecreateenclave(), specifying the signed enclave binary, the operating mode (SGX hardware or simulation), and the relevant flags. ECALLs are invoked through the generated proxy functions; from the host's perspective these look like ordinary function calls, but the generated stub handles the context switch and data marshaling. Any OCALLs declared in the EDL must be implemented as regular host-side functions. When the enclave is no longer needed, oeterminateenclave() tears it down and releases EPC resources.
The 93 MB usable EPC limit becomes a real constraint during implementation. Large data sets, heavyweight libraries, and deep call stacks can exhaust EPC; when EPC is over-committed and paging begins, the performance penalty is substantial. The design principle that follows: bulk data stays in untrusted memory, and only secrets or active computations on them enter the enclave.
A common implementation pitfall is calling functions inside the enclave that depend on system resources without first declaring the corresponding OCALLs. But what if the missing OCALL is not a simple oversight but a symptom of a deeper misunderstanding — that the developer has not fully internalized just how constrained the enclave's execution environment is relative to a normal process? Missing OCALLs produce linker errors or runtime failures that are genuinely confusing if the constraint is understood only after the fact. The EDL is the complete contract for what the enclave can and cannot do. Building with that contract as the primary artifact, rather than retrofitting it to match existing code, is what separates enclave development that stays tractable from enclave development that requires an archaeologist. Products that stake their value on keeping user data private, such as Confidant AI, a privacy-preserving AI assistant built so that user data is never collected or monetized, depend on exactly this kind of ground-up architectural discipline to make that guarantee credible.


