Skip to content

Security Overview

Authorized Use Only

Espilon is a security research and educational tool. Use it only on devices and networks you own or are explicitly authorized in writing to test.

Espilon's security model is built around a single per-device secret and a strict no-trace-at-rest posture. This page covers the key hierarchy, the transport and module-signing crypto, and the threat model.

Key hierarchy

Each device is provisioned once with a random 32-byte master_key, stored in the immutable fctry NVS partition. At boot the firmware derives three independent keys with HKDF-SHA256 and then zeroizes master_key from RAM.

flowchart TD
    MK["master_key (32 bytes, fctry NVS)"]
    MK -->|"HKDF salt=device_id<br/>info=espilon-c2-v1"| SK["session key<br/>ChaCha20-Poly1305 transport"]
    MK -->|"HKDF info=espm-sign"| GK["signing key<br/>HMAC-SHA256 modules"]
    MK -->|"HKDF salt=device_id<br/>info=espilon-cfg-v1"| CK["config key<br/>encrypted NVS"]
    MK -.->|"zeroized after derivation"| Z["(wiped from RAM)"]

The derived keys are regenerated on every boot and never persisted. The operator's C3PO holds a copy of each device's master_key in keys.json and derives the same keys.

Transport encryption

Every C2 frame is encrypted with ChaCha20-Poly1305 (AEAD):

base64( nonce[12] || ciphertext || tag[16] ) + "\n"

The 12-byte nonce is random per message (hardware RNG), and the 16-byte tag is verified before any plaintext is processed. A frame that fails authentication is logged and dropped; the connection stays up. The plaintext is a protobuf Command or AgentMessage. See protocol and crypto for the handshake and framing.

Module signing

Modules are signed with HMAC-SHA256 under a key derived from master_key (info="espm-sign"). The 32-byte tag is prepended to the ELF, and the firmware verifies it before relocation. Because the signing key is the device key, a module is bound to one device. See ESPM security.

Data at rest

  • master_key: in the fctry partition; read once, derived, then wiped from RAM.
  • WiFi credentials and server address: encrypted in the NVS config namespace with the config_key.
  • Modules: never in flash unless explicitly persisted to encrypted NVS; a power cycle wipes RAM.
  • Staging buffers: zeroed before being freed.

Threat model

Attacker capability Can achieve Cannot achieve
Passive network sniff See handshake nonces and base64 ciphertext Decrypt payloads or identify the device without master_key
Read entire flash Read encrypted NVS; with flash encryption off, read master_key from fctry Without master_key: derive session key, decrypt transport, or forge module signatures
Write entire flash Modify firmware/NVS if the eFuse device key is not locked Inject a valid module without master_key
Reboot / UART access Reset the device, read boot logs if UART is open Bypass watchdog or panic isolation

Enable flash encryption for physical-access threats

Flash encryption (XTS-AES-256 with a device key in eFuses) prevents reading the plaintext master_key from a flash dump. Enable it when devices may be physically captured.

Isolation

Modules run in isolated FreeRTOS tasks with per-module watchdogs (30 s). On Xtensa a faulting module is killed without rebooting the device (panic isolation); on RISC-V the device reboots. See ESPM security.

See also