Split Trusted and Untrusted Code
What you will learn
This lesson covers:
- Application partitioning
- Trusted and untrusted code
- ECALL and OCALL-style boundaries
- Input validation
- Iago attacks
- Interface minimization
- Error handling
- Data ownership
The application boundary is an API
A TEE boundary is not only a memory boundary. It is also an application interface.
The trusted component receives requests from untrusted code and may request services from the untrusted host. Every crossing is an attack surface.
A safe TEE application should expose operations, not raw internal memory.
A good interface might look like:
`` sign_approved_transaction(transaction) ``
A dangerous interface might look like:
`` read_enclave_memory(address, length) ``
Trusted and untrusted components
A typical process-enclave design contains:
Untrusted host
Responsible for:
- Starting the enclave
- Receiving network traffic
- File and network I/O
- Scheduling
- Storage
- User interface
- Resource management
Trusted application
Responsible for:
- Secret processing
- Cryptographic operations
- Policy enforcement
- Input validation
- State validation
- Sensitive output generation
The objective is not to move the entire application inside the enclave. The objective is to move the security-critical logic inside while keeping the interface small.
ECALL and OCALL
Intel SGX uses the terms:
- ECALL: Entry from the untrusted application into the enclave
- OCALL: Call from enclave code to an untrusted host function
Intel's Enclave Definition Language describes trusted and untrusted function declarations and generates code that marshals data across the boundary.
Other platforms use different names, but the same idea applies:
- Trusted code receives an external request.
- Trusted code may request an external service.
- Parameters cross between trusted and untrusted memory.
Minimize entry points
Every entry point should have:
- Clear purpose
- Defined input format
- Maximum input size
- Authentication requirement
- Authorization rule
- Error behaviour
- Rate limit
- State transition
- Output classification
Instead of exposing many small internal operations, expose a few high-level operations.
``` Bad: get_key() decrypt_block() read_plaintext() sign_hash()
Better: decrypt_and_validate_document(encrypted_document) sign_transaction_if_policy_allows(transaction) ```
The second design keeps key material and intermediate plaintext inside the trusted boundary.
Validate every input
The trusted application should assume that the host can provide:
- Incorrect lengths
- Invalid pointers
- Duplicate requests
- Reordered requests
- Malformed encodings
- Oversized values
- Invalid signatures
- Stale timestamps
- Unexpected state
- Incorrect file contents
- Fake network responses
Validation should happen inside the trusted boundary.
``` function process_request(request_bytes): if length(request_bytes) > MAX_REQUEST_SIZE: reject
request = strict_decode(request_bytes)
if request.version != SUPPORTED_VERSION: reject
if not verify_signature(request): reject
if request.nonce already used: reject
if not policy_allows(request.operation): reject
return execute(request) ```
Copy, validate, then use
Data stored in untrusted memory may change while the trusted application is reading it.
A safer pattern is:
1. Check the declared size. 2. Copy the input into trusted memory. 3. Validate the copied value. 4. Use only the trusted copy. 5. Clear sensitive temporary buffers.
This reduces time-of-check to time-of-use problems across the boundary.

Do not trust host pointers
Raw pointers from the host should not be treated as stable trusted references.
The host may:
- Change the referenced memory
- Reuse the buffer
- Provide an overlapping range
- Provide a range outside expected memory
- Trigger integer overflow in length calculations
Use bounded copying and checked arithmetic.
``` if length > MAX: reject
if pointer + length overflows: reject
copy_from_untrusted(pointer, length, trusted_buffer) ```
Iago attacks
An Iago attack occurs when an untrusted operating system returns malicious values from a system call or service interface.
The trusted application may assume that the operating system follows normal rules. The malicious host breaks those assumptions.
Examples include:
- Returning an overlapping memory address
- Returning a fake file offset
- Returning a manipulated time value
- Replaying network responses
- Returning inconsistent file metadata
- Claiming a write succeeded when it did not
- Providing a malicious random value

Trusted time is difficult
The host clock may be untrusted. A malicious host may:
- Move time backward
- Move time forward
- Freeze time
- Return inconsistent timestamps
Do not use host time as the only protection for:
- Certificate validity
- Auction deadlines
- Key expiration
- Replay prevention
- Rate limits
- State versions
Possible alternatives include:
- Trusted external time service
- Monotonic counter
- Consensus timestamp
- Signed time response
- Multiple independent time sources
Each option introduces new trust.
Trusted randomness
Randomness used for keys, nonces, and challenges must come from a suitable source.
Do not allow an untrusted host to select a cryptographic nonce without validation.
The TEE may use:
- Hardware random-number generator
- Protected operating-system randomness in a confidential VM
- Multiple entropy sources
- Deterministic random generator seeded inside the TEE
The exact source depends on the platform.
Avoid secret-dependent error messages
The application may accidentally reveal information through errors.
`` Error: User exists but password is incorrect Error: Decryption key was correct but document signature failed Error: First 12 bytes of token matched ``
Use uniform external errors when detailed information would leak secrets. Detailed diagnostics may be stored inside a protected debugging environment, but production logs should remain privacy-aware.
Example: TEE signing service
A signing service should not expose:
`` get_private_key() ``
It should expose:
`` sign_if_allowed(message, authorization) ``
Inside the TEE:
1. Parse the message. 2. Verify the authorization. 3. Check policy. 4. Check nonce and state. 5. Generate signature. 6. Record the action. 7. Return the signature.
The host receives the result but never receives the key.
Developer exercise
Design the interface for a private-auction enclave.
The enclave must:
- Accept encrypted bids
- Enforce a deadline
- Prevent duplicate bids
- Select a winner
- Reveal only the result
Write:
1. Three trusted functions 2. Three host functions 3. Input rules 4. State rules 5. Output rules 6. Two malicious host behaviours
Common mistakes
Key takeaways
- Treat the TEE boundary as a hostile API boundary.
- Keep trusted code small.
- Expose high-level operations.
- Copy and validate all untrusted input.
- Treat system calls and host responses as attacker-controlled.
- Keep secret material and intermediate plaintext inside the trusted boundary.
Check your understanding
1. Why should the TEE expose operations rather than raw keys? 2. What is an OCALL? 3. Why should data be copied into trusted memory before validation? 4. What is an Iago attack? 5. Why can host-provided time be dangerous?
Answer the quiz correctly to continue →
What is an Iago attack, and what does it target in a TEE system?