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​