Skip to main content

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

SystemWhat It ProtectsDirection
INPUT EncryptionDevice attestation dataAttestor → On-chain → Cartesi
OUTPUT EncryptionQuery responsesCartesi → Attestor → Client

These systems use different keypairs and serve different security purposes.

Why Two Encryption Systems?

The Problem

  1. On-chain data is public: When device attestations are submitted to the blockchain via the Cartesi InputBox, anyone can read them
  2. 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

KeyHolderFormatPurposeLocation
LCORE_INPUT_PUBLIC_KEYAttestorBase64Encrypt device data for chain.env file
LCORE_INPUT_PRIVATE_KEYCartesiBase64Decrypt device data in TEEDockerfile (baked)
LCORE_OUTPUT_PUBLIC_KEYCartesiBase64Encrypt query responsesDockerfile or API
LCORE_OUTPUT_PRIVATE_KEYAttestorBase64Decrypt 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:

FieldWhat It Means
input_decryption_enabledINPUT private key is present (can decrypt device data)
output_encryption_enabledOUTPUT public key is configured (will encrypt responses)
encryption_configuredOverall 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