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):
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
fctrypartition; 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.