espilon-source/espilon_bot/components/command/command_async.c
Eun0us 8b6c1cd53d ε - ChaCha20-Poly1305 AEAD + HKDF crypto upgrade + C3PO rewrite + docs
Crypto:
- Replace broken ChaCha20 (static nonce) with ChaCha20-Poly1305 AEAD
- HKDF-SHA256 key derivation from per-device factory NVS master keys
- Random 12-byte nonce per message (ESP32 hardware RNG)
- crypto_init/encrypt/decrypt API with mbedtls legacy (ESP-IDF v5.3.2)
- Custom partition table with factory NVS (fctry at 0x10000)

Firmware:
- crypto.c full rewrite, messages.c device_id prefix + AEAD encrypt
- crypto_init() at boot with esp_restart() on failure
- Fix command_t initializations across all modules (sub/help fields)
- Clean CMakeLists dependencies for ESP-IDF v5.3.2

C3PO (C2):
- Rename tools/c2 + tools/c3po -> tools/C3PO
- Per-device CryptoContext with HKDF key derivation
- KeyStore (keys.json) for master key management
- Transport parses device_id:base64(...) wire format

Tools:
- New tools/provisioning/provision.py for factory NVS key generation
- Updated flasher with mbedtls config for v5.3.2

Docs:
- Update all READMEs for new crypto, C3PO paths, provisioning
- Update roadmap, architecture diagrams, security sections
- Update CONTRIBUTING.md project structure
2026-02-10 21:28:45 +01:00

109 lines
2.9 KiB
C

#include "command.h"
#include "utils.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include <string.h>
static const char *TAG = "CMD_ASYNC";
/* =========================================================
* Async job structure
* ========================================================= */
typedef struct {
const command_t *cmd;
int argc;
char argv[MAX_ASYNC_ARGS][MAX_ASYNC_ARG_LEN];
char *argv_ptrs[MAX_ASYNC_ARGS];
char request_id[64];
} async_job_t;
static QueueHandle_t async_queue;
/* =========================================================
* Worker task
* ========================================================= */
static void async_worker(void *arg)
{
async_job_t job;
while (1) {
if (xQueueReceive(async_queue, &job, portMAX_DELAY)) {
/* Recompute argv_ptrs to point into THIS copy's argv buffers.
* xQueueReceive copies the struct by value, so the old
* pointers (set at enqueue time) are now dangling. */
for (int i = 0; i < job.argc; i++) {
job.argv_ptrs[i] = job.argv[i];
}
ESP_LOGI(TAG, "Async exec: %s", job.cmd->name);
job.cmd->handler(
job.argc,
job.argv_ptrs,
job.request_id[0] ? job.request_id : NULL,
job.cmd->ctx
);
}
}
}
/* =========================================================
* Init async system
* ========================================================= */
void command_async_init(void)
{
async_queue = xQueueCreate(8, sizeof(async_job_t));
if (!async_queue) {
ESP_LOGE(TAG, "Failed to create async queue");
return;
}
xTaskCreate(
async_worker,
"cmd_async",
4096,
NULL,
5,
NULL
);
ESPILON_LOGI_PURPLE(TAG, "Async command system ready");
}
/* =========================================================
* Enqueue async command
* ========================================================= */
void command_async_enqueue(const command_t *cmd,
const c2_Command *pb_cmd)
{
if (!cmd || !pb_cmd) return;
async_job_t job = {0};
job.cmd = cmd;
job.argc = pb_cmd->argv_count;
if (job.argc > MAX_ASYNC_ARGS)
job.argc = MAX_ASYNC_ARGS;
for (int i = 0; i < job.argc; i++) {
strncpy(job.argv[i],
pb_cmd->argv[i],
MAX_ASYNC_ARG_LEN - 1);
job.argv_ptrs[i] = job.argv[i];
}
if (pb_cmd->request_id[0]) {
strncpy(job.request_id,
pb_cmd->request_id,
sizeof(job.request_id) - 1);
}
if (xQueueSend(async_queue, &job, 0) != pdTRUE) {
ESP_LOGE(TAG, "Async queue full");
msg_error("cmd", "Async queue full",
pb_cmd->request_id);
}
}