Encryption Architecture
The L{CORE} attestation system uses two separate encryption systems to protect data at different stages. Understanding this architecture is essential for proper deployment and troubleshooting.
Overview
| System | What It Protects | Direction |
|---|---|---|
| INPUT Encryption | Device attestation data | Attestor → On-chain → Cartesi |
| OUTPUT Encryption | Query responses | Cartesi → Attestor → Client |
These systems use different keypairs and serve different security purposes.
Why Two Encryption Systems?
The Problem
- On-chain data is public: When device attestations are submitted to the blockchain via the Cartesi InputBox, anyone can read them
- Query responses traverse networks: Responses from the Cartesi machine travel through potentially untrusted infrastructure
The Solution
- INPUT encryption ensures only the TEE can read sensitive device data, even though it's stored on a public blockchain
- OUTPUT encryption ensures only authorized parties can read query results
INPUT Encryption Flow
INPUT encryption protects device data as it travels to the Cartesi machine.
┌──────────────────────────────────────────────────────────────────────────┐
│ INPUT ENCRYPTION FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ Device │ 1. Signs data with JWS │
│ │ │ (proves device identity) │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Attestor │ 2. Encrypts signed payload │
│ │ │ using INPUT_PUBLIC_KEY │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ InputBox │ 3. Encrypted blob stored on-chain │
│ │ (Chain) │ (publicly visible but unreadable) │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Cartesi │ 4. Decrypts using INPUT_PRIVATE_KEY │
│ │ TEE │ 5. Verifies JWS signature │
│ │ │ 6. Processes and stores data │
│ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Key Points
- The Attestor holds the
INPUT_PUBLIC_KEY(encrypts data) - The Cartesi machine holds the
INPUT_PRIVATE_KEY(decrypts data) - On-chain data appears as opaque encrypted blobs
- Only the TEE can decrypt and process the device data
Why INPUT Private Key Must Be Baked In
The Cartesi Machine runs as a deterministic RISC-V emulator:
- It cannot access external environment variables at runtime
- It cannot make network requests to fetch keys
- The machine image is sealed at build time
Therefore, INPUT_PRIVATE_KEY must be hardcoded in the Dockerfile. See Encryption Key Configuration for setup instructions.
OUTPUT Encryption Flow
OUTPUT encryption protects query responses from the Cartesi machine.
┌──────────────────────────────────────────────────────────────────────────┐
│ OUTPUT ENCRYPTION FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ Client │ 1. Sends query request │
│ │ │ │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Attestor │ 2. Forwards to Cartesi /inspect endpoint │
│ │ │ │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Cartesi │ 3. Processes query │
│ │ TEE │ 4. Encrypts response with OUTPUT_PUBLIC_KEY │
│ │ │ │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Attestor │ 5. Decrypts using OUTPUT_PRIVATE_KEY │
│ │ │ 6. Returns plain response to client │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Client │ 7. Receives decrypted response │
│ │ │ │
│ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Key Points
- The Cartesi machine holds the
OUTPUT_PUBLIC_KEY(encrypts responses) - The Attestor holds the
OUTPUT_PRIVATE_KEY(decrypts responses) - Responses are protected in transit between Cartesi and Attestor
- OUTPUT encryption is optional (can be disabled for public data)
Complete Data Flow
Here's the full end-to-end flow showing both encryption systems:
Device Attestor Chain Cartesi TEE
│ │ │ │
│ 1. JWS-signed data │ │ │
│─────────────────────►│ │ │
│ │ │ │
│ │ 2. Encrypt with │ │
│ │ INPUT_PUBLIC │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 3. Submit to │ │
│ │ InputBox │ │
│ │───────────────────►│ │
│ │ │ │
│ │ │ 4. Deliver input │
│ │ │───────────────────► │
│ │ │ │
│ │ │ 5. Decrypt with │
│ │ │ INPUT_PRIVATE │
│ │ │ │
│ │ │ 6. Verify JWS │
│ │ │ │
│ │ │ 7. Store data │
│ │ │ │
│ │ │ │
│ │ 8. Query request │ │
│ │───────────────────────────────────────► │
│ │ │ │
│ │ │ 9. Process query │
│ │ │ │
│ │ │ 10. Encrypt with │
│ │ │ OUTPUT_PUBLIC │
│ │ │ │
│ │ 11. Encrypted │ │
│ │ response │ │
│ │◄─────────────────────────────────────── │
│ │ │ │
│ │ 12. Decrypt with │ │
│ │ OUTPUT_PRIVATE │ │
│ │ │ │
│ 13. Plain response │ │ │
│◄─────────────────────│ │ │
│ │ │ │
Key Summary Table
| Key | Holder | Format | Purpose | Location |
|---|---|---|---|---|
LCORE_INPUT_PUBLIC_KEY | Attestor | Base64 | Encrypt device data for chain | .env file |
LCORE_INPUT_PRIVATE_KEY | Cartesi | Base64 | Decrypt device data in TEE | Dockerfile (baked) |
LCORE_OUTPUT_PUBLIC_KEY | Cartesi | Base64 | Encrypt query responses | Dockerfile or API |
LCORE_OUTPUT_PRIVATE_KEY | Attestor | Base64 | Decrypt query responses | .env file |
Security Properties
On-Chain Privacy
- Device attestations are stored encrypted on the blockchain
- Historical data remains protected indefinitely
- Even with chain access, data cannot be read without the private key
Query Privacy
- Responses are encrypted between Cartesi and Attestor
- Prevents inspection by intermediate network infrastructure
- Only authorized Attestor instances can decrypt responses
TEE Protection
- Private keys inside the Cartesi machine are protected by hardware isolation
- Keys cannot be extracted from a running TEE instance
- Remote attestation verifies execution integrity
Encryption Algorithm
L{CORE} uses NaCl box encryption:
- Key Exchange: Curve25519
- Encryption: XSalsa20
- Authentication: Poly1305
This provides:
- Authenticated encryption (integrity + confidentiality)
- Forward secrecy when keys are rotated
- High performance suitable for IoT data volumes
Understanding Encryption Status
The Cartesi machine exposes an encryption status endpoint:
curl http://<node>/inspect/encryption_status
Response:
{
"encryption_configured": true,
"input_decryption_enabled": true,
"output_encryption_enabled": true
}
Important distinctions:
| Field | What It Means |
|---|---|
input_decryption_enabled | INPUT private key is present (can decrypt device data) |
output_encryption_enabled | OUTPUT public key is configured (will encrypt responses) |
encryption_configured | Overall encryption is set up |
If input_decryption_enabled is false but you set the key, it likely wasn't baked into the Dockerfile correctly.
Common Confusion Points
"I set the key but decryption fails"
The key is probably in a .env file instead of baked into the Dockerfile. The Cartesi machine cannot read runtime environment variables.
"encryption_configured shows false"
This typically refers to OUTPUT encryption configuration. INPUT decryption can still work—check input_decryption_enabled separately.
"Which encryption protects what?"
- INPUT = device data privacy (on-chain storage)
- OUTPUT = query response privacy (in-transit)
They are independent. You can use INPUT encryption without OUTPUT encryption, or vice versa.
Next Steps
- Encryption Key Configuration — How to generate and configure keys
- EigenCloud Deployment — Deploy to TEE infrastructure
- Claim Creation — How attestations are created