From a8f2fd1c6c58e6d17213b249323308acb913e724 Mon Sep 17 00:00:00 2001 From: Michele Bigi Date: Tue, 7 Jul 2026 07:46:59 +0200 Subject: [PATCH] Enable NVS and flash encryption at rest (fw 0.8.3). Add initEncryptedStorage, development-mode Kconfig defaults, production overlay, and security HIL docs; wire NetBootstrap through encrypted NVS bring-up. Co-authored-by: Cursor --- README.md | 13 ++-- Software/README.md | 6 +- Software/components/net/CMakeLists.txt | 2 +- Software/components/net/src/NetBootstrap.cpp | 15 ++-- .../components/net/src/SetupWebServer.cpp | 2 +- .../components/secure_store/CMakeLists.txt | 1 + .../include/secure_store/NvsPlatformInit.hpp | 48 ++++++++++++ .../include/secure_store/NvsSecureStore.hpp | 6 +- .../secure_store/src/NvsPlatformInit.cpp | 75 +++++++++++++++++++ Software/docs/TODO.md | 20 +++-- Software/docs/manual/ch-api.tex | 12 +-- Software/docs/manual/ch-classes.tex | 5 +- Software/docs/manual/ch-firmware.tex | 5 +- Software/docs/security-flash-nvs.md | 62 +++++++++++++++ Software/instructions.md | 7 +- Software/sdkconfig.defaults | 17 ++++- Software/sdkconfig.defaults.production | 11 +++ 17 files changed, 259 insertions(+), 48 deletions(-) create mode 100644 Software/components/secure_store/include/secure_store/NvsPlatformInit.hpp create mode 100644 Software/components/secure_store/src/NvsPlatformInit.cpp create mode 100644 Software/docs/security-flash-nvs.md create mode 100644 Software/sdkconfig.defaults.production diff --git a/README.md b/README.md index 3a05332..481d517 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ **Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3** ![Status](https://img.shields.io/badge/hardware-verified-brightgreen) -![Firmware](https://img.shields.io/badge/firmware-0.8.2-blue) +![Firmware](https://img.shields.io/badge/firmware-0.8.3-blue) ![CI](https://github.com/manvalan/DigiRadio/actions/workflows/ci.yml/badge.svg) ![PCB](https://img.shields.io/badge/PCB-6--layer-blue) ![Hardware License](https://img.shields.io/badge/hardware-CERN--OHL--S-lightgrey) @@ -57,7 +57,7 @@ no analogue conversions are introduced between the tuner and the wireless link. - **6-layer impedance-controlled PCB**, 50 × 90 mm, two ground planes - Three antennas (ESP32 2.4 GHz, BT1035 2.4 GHz, FM/DAB SMA) with proper keep-outs -### Firmware (ESP-IDF, C++23 — fw **0.8.2**) +### Firmware (ESP-IDF, C++23 — fw **0.8.3**) | Area | Capability | |------|------------| @@ -68,7 +68,7 @@ no analogue conversions are introduced between the tuner and the wireless link. | **Presets** | Station list CRUD, reorder, recall with audio profile re-apply, last-preset restore at boot | | **Bluetooth** | Discoverable pairing, A2DP status, disconnect | | **Network** | SoftAP setup mode, STA provisioning, tabbed gzipped SPA (`/`), typed JSON REST API | -| **Security** | NVS-backed credential and preset storage (flash encryption planned — see roadmap) | +| **Security** | NVS + flash encryption at rest (dev mode); keys never in repo | | **Quality** | **13** host unit tests, Doxygen gate, LaTeX manual sync check, GitHub Actions CI on `main` | Architecture follows a **functional core + imperative shell**: pure domain logic @@ -83,6 +83,7 @@ Vertical slices landed on `main` (newest first): | Version | Highlights | |---------|------------| +| **0.8.3** | NVS + flash encryption (development mode), `initEncryptedStorage`, security docs | | **0.8.2** | Tabbed configuration Web UI — now-playing, 6-band EQ, full API coverage | | **0.8.1** | `IntegrationService` — boot preset recall, tune orchestration (tuner + audio + NVS `last_preset`); services stub removed | | **0.8.0** | Preset reorder API/UI; broadcast metadata (RDS + DAB DLS); `readDabServiceData` driver path | @@ -91,7 +92,8 @@ Vertical slices landed on `main` (newest first): | **0.5–0.6** | ADAU1701 runtime EQ/mixer, Si4684 tuning & DAB service list, BT1035 driver | | **0.3–0.4** | Secure store, Wi-Fi provisioning, companion-chip boot, walking skeleton | -**Next up** ([`Software/docs/TODO.md`](Software/docs/TODO.md)): NVS/flash encryption (T8). +**Next up:** device HIL for encrypted NVS (when PCB arrives). Agent backlog: +[`Software/docs/TODO.md`](Software/docs/TODO.md). --- @@ -221,8 +223,9 @@ Generated C++ API reference: run `doxygen Doxyfile` → `Software/docs/api/html/ | **PCB layout** | 6-layer, DRC clean, plane continuity verified | | **BOM** | Finalised (manufacturable / sourced) | | **Prototype** | In fabrication (PCBWay) | -| **Firmware** | **0.8.2** on `main` — CI green; full Web UI, integration, RDS/DLS | +| **Firmware** | **0.8.3** on `main` — encrypted NVS, full Web UI, integration, RDS/DLS | | **Web UI** | Tabbed SPA covering every REST endpoint | +| **HIL** | Security checklist pending PCB (`Software/docs/security-flash-nvs.md`) | | **Production hardening** | Si4684 blob policy (T7), NVS encryption (T8) — open | Agent task list: [`Software/docs/TODO.md`](Software/docs/TODO.md). diff --git a/Software/README.md b/Software/README.md index e0da7f3..48760ef 100644 --- a/Software/README.md +++ b/Software/README.md @@ -2,7 +2,7 @@ Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3. -**Status:** fw **0.8.2** — tabbed Web UI (now-playing, 6-band EQ, all APIs); +**Status:** fw **0.8.3** — NVS + flash encryption (dev mode); tabbed Web UI; `IntegrationService`; **13** host tests; CI on `main`. See [`docs/TODO.md`](docs/TODO.md) for the agent task list. @@ -14,6 +14,7 @@ Open this directory (`Software/`) as the Cursor project so `AGENTS.md` and ```bash idf.py set-target esp32s3 idf.py build +idf.py erase-flash flash # once when upgrading to encrypted NVS (0.8.3+) idf.py -p flash monitor ``` @@ -41,7 +42,7 @@ Manual PDF (design + HTTP API + class reference): cd docs/manual && latexmk -lualatex manual.tex ``` -## HTTP API (fw 0.8.2) +## HTTP API (fw 0.8.3) | Method | Path | Purpose | |--------|------|---------| @@ -79,6 +80,7 @@ C++ signatures: `doxygen Doxyfile` → `docs/api/html/index.html`. | `components/services/` | Tuner, audio, Bluetooth, station, integration services | | `components/net/` | Wi-Fi, HTTP server, gzipped web UI | | `docs/manual/` | LaTeX technical manual (canonical) | +| `docs/security-flash-nvs.md` | NVS + flash encryption and HIL checklist | | `docs/TODO.md` | Agent task list (prioritised backlog) | See [`AGENTS.md`](AGENTS.md) §12 and [`instructions.md`](instructions.md) for diff --git a/Software/components/net/CMakeLists.txt b/Software/components/net/CMakeLists.txt index 660a17d..f13f141 100644 --- a/Software/components/net/CMakeLists.txt +++ b/Software/components/net/CMakeLists.txt @@ -7,7 +7,7 @@ idf_component_register( "src/NetBootstrap.cpp" INCLUDE_DIRS "include" EMBED_FILES "www/index.html.gz" - REQUIRES core esp_wifi esp_netif esp_event nvs_flash esp_http_server tuner audio bluetooth station integration bt1035 + REQUIRES core esp_wifi esp_netif esp_event nvs_flash esp_http_server secure_store tuner audio bluetooth station integration bt1035 ) target_compile_features(${COMPONENT_LIB} PUBLIC cxx_std_23) diff --git a/Software/components/net/src/NetBootstrap.cpp b/Software/components/net/src/NetBootstrap.cpp index 176b998..bf2f838 100644 --- a/Software/components/net/src/NetBootstrap.cpp +++ b/Software/components/net/src/NetBootstrap.cpp @@ -22,9 +22,9 @@ #include "esp_log.h" #include "esp_netif.h" #include "esp_wifi.h" -#include "nvs_flash.h" #include "audio/AudioService.hpp" #include "bluetooth/BluetoothService.hpp" +#include "secure_store/NvsPlatformInit.hpp" #include "station/StationService.hpp" #include "tuner/TunerService.hpp" @@ -38,21 +38,16 @@ constexpr char kTag[] = "NetBootstrap"; * * @dname initPlatform * @return Ok on success, or a NetError describing the failure. - * @pubstate initialises NVS, esp_netif, and the default event loop. + * @pubstate initialises encrypted NVS, esp_netif, and the default event loop. * * @author Michele Bigi * @date 2026-07-06 */ [[nodiscard]] std::expected initPlatform() { - esp_err_t nvsErr = nvs_flash_init(); - if (nvsErr == ESP_ERR_NVS_NO_FREE_PAGES - || nvsErr == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_ERROR_CHECK(nvs_flash_erase()); - nvsErr = nvs_flash_init(); - } - if (nvsErr != ESP_OK) { - ESP_LOGE(kTag, "nvs_flash_init failed"); + const auto nvsResult = secure_store::initEncryptedStorage(); + if (!nvsResult) { + ESP_LOGE(kTag, "encrypted NVS init failed"); return std::unexpected(NetError::NvsInitFailed); } diff --git a/Software/components/net/src/SetupWebServer.cpp b/Software/components/net/src/SetupWebServer.cpp index 43f16b7..a27ec03 100644 --- a/Software/components/net/src/SetupWebServer.cpp +++ b/Software/components/net/src/SetupWebServer.cpp @@ -52,7 +52,7 @@ namespace net { namespace { constexpr char kTag[] = "SetupWebServer"; -constexpr char kFirmwareVersion[] = "0.8.2"; +constexpr char kFirmwareVersion[] = "0.8.3"; constexpr unsigned kRebootDelaySec = 3; extern const uint8_t www_index_html_gz_start[] asm( diff --git a/Software/components/secure_store/CMakeLists.txt b/Software/components/secure_store/CMakeLists.txt index 86e5045..a9bff67 100644 --- a/Software/components/secure_store/CMakeLists.txt +++ b/Software/components/secure_store/CMakeLists.txt @@ -2,6 +2,7 @@ idf_component_register( SRCS "src/NvsSecureStore.cpp" "src/NvsAudioProfileStore.cpp" + "src/NvsPlatformInit.cpp" INCLUDE_DIRS "include" REQUIRES core nvs_flash ) diff --git a/Software/components/secure_store/include/secure_store/NvsPlatformInit.hpp b/Software/components/secure_store/include/secure_store/NvsPlatformInit.hpp new file mode 100644 index 0000000..8fda773 --- /dev/null +++ b/Software/components/secure_store/include/secure_store/NvsPlatformInit.hpp @@ -0,0 +1,48 @@ +/** + * @file NvsPlatformInit.hpp + * @brief Encrypted NVS partition bring-up for ISecureStore backends. + * + * DigiRadio firmware — https://github.com/manvalan/DigiRadio + * + * Copyright 2026 Michele Bigi + * SPDX-License-Identifier: Apache-2.0 + * + * @author Michele Bigi + * @date 2026-07-07 + */ +#pragma once + +#include + +namespace secure_store { + +/** + * @brief NvsInitError — failure mode for encrypted NVS initialisation. + * + * @dname NvsInitError + * @return n/a (type) + * @pubstate none + * + * @author Michele Bigi + * @date 2026-07-07 + */ +enum class NvsInitError { + EraseFailed, ///< nvs_flash_erase failed during recovery. + InitFailed, ///< nvs_flash_init failed after recovery attempt. +}; + +/** + * @brief initEncryptedStorage — initialise default NVS (+ nvs_keys when enabled). + * + * @dname initEncryptedStorage + * @return Ok on success, or NvsInitError describing the failure. + * @pubstate When CONFIG_NVS_ENCRYPTION is set, nvs_flash_init() uses the + * nvs_keys partition and flash-encryption key protection per + * ESP-IDF v5.5 security docs. Erases and retries on layout mismatch. + * + * @author Michele Bigi + * @date 2026-07-07 + */ +[[nodiscard]] std::expected initEncryptedStorage() noexcept; + +} // namespace secure_store diff --git a/Software/components/secure_store/include/secure_store/NvsSecureStore.hpp b/Software/components/secure_store/include/secure_store/NvsSecureStore.hpp index 2475005..40e5b78 100644 --- a/Software/components/secure_store/include/secure_store/NvsSecureStore.hpp +++ b/Software/components/secure_store/include/secure_store/NvsSecureStore.hpp @@ -12,9 +12,9 @@ * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * - * Production builds should enable NVS encryption (nvs_keys partition in - * partitions.csv) per ESP-IDF security docs; this slice uses plain NVS - * for development bring-up. + * Production: NVS encryption + flash encryption enabled in sdkconfig.defaults + * (fw 0.8.3+). Call secure_store::initEncryptedStorage() before first use; + * see docs/security-flash-nvs.md. * * @author Michele Bigi * @date 2026-07-06 diff --git a/Software/components/secure_store/src/NvsPlatformInit.cpp b/Software/components/secure_store/src/NvsPlatformInit.cpp new file mode 100644 index 0000000..de8a627 --- /dev/null +++ b/Software/components/secure_store/src/NvsPlatformInit.cpp @@ -0,0 +1,75 @@ +/** + * @file NvsPlatformInit.cpp + * @brief Encrypted NVS partition bring-up implementation. + * + * DigiRadio firmware — https://github.com/manvalan/DigiRadio + * + * Copyright 2026 Michele Bigi + * SPDX-License-Identifier: Apache-2.0 + * + * @author Michele Bigi + * @date 2026-07-07 + */ + +#include "secure_store/NvsPlatformInit.hpp" + +#include "esp_log.h" +#include "nvs_flash.h" +#include "sdkconfig.h" + +namespace secure_store { + +namespace { +constexpr char kTag[] = "NvsPlatformInit"; + +/** + * @brief logEncryptionMode — log active NVS security Kconfig (no secrets). + * + * @dname logEncryptionMode + * @pubstate none; INFO log only. + * + * @author Michele Bigi + * @date 2026-07-07 + */ +void logEncryptionMode() noexcept +{ +#if CONFIG_NVS_ENCRYPTION + ESP_LOGI(kTag, "NVS encryption enabled"); +#if CONFIG_SECURE_FLASH_ENC_ENABLED + ESP_LOGI(kTag, "Flash encryption enabled (development=%d)", + static_cast(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)); +#else + ESP_LOGW(kTag, "NVS encryption without flash encryption — check Kconfig"); +#endif +#else + ESP_LOGW(kTag, "NVS encryption disabled — not for production"); +#endif +} + +} // namespace + +std::expected initEncryptedStorage() noexcept +{ + logEncryptionMode(); + + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES + || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_LOGW(kTag, "NVS partition needs erase (err=0x%x)", static_cast(err)); + err = nvs_flash_erase(); + if (err != ESP_OK) { + ESP_LOGE(kTag, "nvs_flash_erase failed (0x%x)", static_cast(err)); + return std::unexpected(NvsInitError::EraseFailed); + } + err = nvs_flash_init(); + } + + if (err != ESP_OK) { + ESP_LOGE(kTag, "nvs_flash_init failed (0x%x)", static_cast(err)); + return std::unexpected(NvsInitError::InitFailed); + } + + return {}; +} + +} // namespace secure_store diff --git a/Software/docs/TODO.md b/Software/docs/TODO.md index 1aa1ec0..16a27ea 100644 --- a/Software/docs/TODO.md +++ b/Software/docs/TODO.md @@ -12,12 +12,12 @@ errors, no plaintext secrets. Working directory for all commands is `Software/`. -**Current firmware:** `0.8.2` — tabbed configuration Web UI (full API -coverage), integration service, RDS/DLS metadata, CI gate. +**Current firmware:** `0.8.3` — NVS + flash encryption (dev mode), tabbed Web +UI, integration service, CI gate. --- -## Completed (fw 0.7.0–0.7.2) +## Completed (fw 0.7.0–0.8.3) - **Integration service (T5)** — preset recall with audio profile re-apply, last-preset NVS, \texttt{app\_main} orchestration. @@ -74,14 +74,12 @@ embedded gzip blob. No debug routes in `SetupWebServer`. Procurement documented in `Si4684-Firmware/README.md`; CI job `si4684-blobs` runs `tools/check_si4684_blobs.py`. -### T8. Flash/NVS encryption enablement -**Why:** secure storage holds Wi-Fi and user credentials; encryption at -rest was deferred. -**What:** enable NVS encryption on an encrypted partition (with flash -encryption), per current ESP-IDF security docs. Verify the mechanism -before enabling; keep keys out of the repo. -**Done when:** credentials are encrypted at rest and the boot path still -loads them. +### T8. Flash/NVS encryption enablement — **DONE (fw 0.8.3)** +`CONFIG_NVS_ENCRYPTION` + flash encryption (development mode) in +`sdkconfig.defaults`; `secure_store::initEncryptedStorage()`; production +overlay `sdkconfig.defaults.production`; HIL checklist in +`docs/security-flash-nvs.md`. **Pending:** device verification when PCB +arrives. --- diff --git a/Software/docs/manual/ch-api.tex b/Software/docs/manual/ch-api.tex index b0a0e23..66e2356 100644 --- a/Software/docs/manual/ch-api.tex +++ b/Software/docs/manual/ch-api.tex @@ -36,7 +36,7 @@ Returns a health-check DTO serialised by \begin{drnote}[Response schema] \begin{drcode}[JSON] -{"status":"ok","fw":"0.8.2", +{"status":"ok","fw":"0.8.3", "chips":{"si4684":true,"adau1701":true,"bt1035":true}} \end{drcode} \begin{itemize} @@ -354,8 +354,10 @@ as \texttt{last\_preset} (u8). Passwords are wrapped in \texttt{core::Secret} in RAM and are never logged or returned by the API. \begin{drcaution}[Encryption at rest] -Development builds use plain NVS. Production should enable NVS encryption -using the reserved \texttt{nvs\_keys} partition (see -\texttt{partitions.csv} and \texttt{sdkconfig.defaults} comments) per -current ESP-IDF security guidance. +Firmware~0.8.3+ enables NVS encryption (\texttt{CONFIG\_NVS\_ENCRYPTION}) and +flash encryption in development mode (\texttt{sdkconfig.defaults}). Keys live in +the \texttt{nvs\_keys} partition; Wi-Fi passwords remain wrapped in +\texttt{core::Secret} in RAM and are never logged. First upgrade from plain NVS +requires \texttt{idf.py erase-flash}. HIL checklist: +\texttt{Software/docs/security-flash-nvs.md}. \end{drcaution} diff --git a/Software/docs/manual/ch-classes.tex b/Software/docs/manual/ch-classes.tex index 3624478..507b152 100644 --- a/Software/docs/manual/ch-classes.tex +++ b/Software/docs/manual/ch-classes.tex @@ -99,8 +99,9 @@ RAII STA join helper with an explicit connect timeout. Assumes \section{NvsSecureStore}\label{cls:NvsSecureStore} \texttt{ISecureStore} implementation backed by an NVS namespace. Passwords -are stored as NVS strings and never logged. Production should enable NVS -encryption using the reserved \texttt{nvs\_keys} partition. +are stored as NVS strings and never logged. NVS encryption and flash +encryption are enabled in \texttt{sdkconfig.defaults}; initialisation runs in +\texttt{secure\_store::initEncryptedStorage()} before network bring-up. % ------------------------------------------------------------------ % Hardware drivers (Slice 3) diff --git a/Software/docs/manual/ch-firmware.tex b/Software/docs/manual/ch-firmware.tex index e398b4d..ba1cf15 100644 --- a/Software/docs/manual/ch-firmware.tex +++ b/Software/docs/manual/ch-firmware.tex @@ -180,8 +180,9 @@ Chapter~\ref{ch:api}. Sensitive data --- Wi-Fi credentials today; user credentials and the station/frequency list in later slices --- uses \texttt{core::Secret} so values cannot be logged or implicitly converted to a string; buffers are -cleared on destruction. Production builds should enable NVS encryption -at rest (see Chapter~\ref{sec:api-storage}). +cleared on destruction. Firmware~0.8.3+ enables NVS and flash encryption at +rest via \texttt{secure\_store::initEncryptedStorage()} (see +\texttt{docs/security-flash-nvs.md} and Chapter~\ref{sec:api-storage}). \section{Companion-chip boot at power-up} \label{sec:fw-chip-boot} diff --git a/Software/docs/security-flash-nvs.md b/Software/docs/security-flash-nvs.md new file mode 100644 index 0000000..76948ce --- /dev/null +++ b/Software/docs/security-flash-nvs.md @@ -0,0 +1,62 @@ +# Flash and NVS encryption (T8) + +DigiRadio stores Wi-Fi credentials, preset lists, audio profiles, and the last +preset index in the `digiradio` NVS namespace. Firmware **0.8.3+** enables: + +| Layer | Kconfig | Effect | +|-------|---------|--------| +| Flash encryption | `CONFIG_SECURE_FLASH_ENC_ENABLED` | On-chip transparent flash ciphertext | +| Mode (default) | `CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT` | Plaintext download still allowed for bring-up | +| NVS encryption | `CONFIG_NVS_ENCRYPTION` | XTS-AES over NVS entries; keys in `nvs_keys` partition | +| Partition table | `partitions.csv` | `nvs` @ 0x9000, `nvs_keys` @ 0xf000 | + +Implementation: `secure_store::initEncryptedStorage()` (called from +`NetBootstrap` before any `NvsSecureStore` access). With `CONFIG_NVS_ENCRYPTION`, +ESP-IDF `nvs_flash_init()` loads or generates keys in the first `nvs_keys` +partition automatically (ESP-IDF v5.5 NVS Encryption guide). + +**No encryption keys are stored in this repository.** + +## First flash (virgin ESP32-S3) + +```bash +cd Software +idf.py set-target esp32s3 +idf.py erase-flash flash monitor +``` + +Provision Wi-Fi via SoftAP UI, save a preset, reboot — credentials should +survive `STA` reconnect. + +## Migrating from plain NVS (pre-0.8.3 dev boards) + +Encryption changes the on-flash layout. **Erase once** before using encrypted +firmware: + +```bash +idf.py erase-flash flash +``` + +Users must re-provision Wi-Fi and presets after erase. + +## Production release mode + +After HIL sign-off, build with the production overlay +(`sdkconfig.defaults.production`) so flash encryption uses **RELEASE** mode. +This limits future plaintext downloads — follow Espressif’s flash encryption +checklist for ESP32-S3 before shipping units. + +## HIL checklist (requires hardware — pending) + +- [ ] Boot log shows `NvsPlatformInit: NVS encryption enabled` and flash encryption enabled +- [ ] `POST /api/wifi` → reboot → STA connects without re-provisioning +- [ ] Preset save/recall and `last_preset` survive power cycle +- [ ] Audio profile round-trip after reboot +- [ ] `espefuse.py summary` shows expected `SPI_BOOT_CRYPT_CNT` after first encrypted boot +- [ ] Optional: UART hex dump of NVS region shows non-plaintext SSID (do not log secrets in CI) + +## References + +- ESP-IDF v5.5 — [NVS Encryption (ESP32-S3)](https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32s3/api-reference/storage/nvs_encryption.html) +- ESP-IDF v5.5 — [Flash Encryption (ESP32-S3)](https://docs.espressif.com/projects/esp-idf/en/v5.5.3/esp32s3/security/flash-encryption.html) +- `Software/partitions.csv`, `Software/sdkconfig.defaults` diff --git a/Software/instructions.md b/Software/instructions.md index 92de8ce..feb3ae2 100644 --- a/Software/instructions.md +++ b/Software/instructions.md @@ -62,8 +62,8 @@ so every later slice drops into a working frame. Build: - Top-level ESP-IDF project targeting `esp32s3`. -- `sdkconfig.defaults` sets C++23, exceptions off, and documents flash/NVS - encryption options (not hard-enabled until production). +- `sdkconfig.defaults` sets C++23, exceptions off, NVS + flash encryption + (development mode). Production overlay: `sdkconfig.defaults.production`. - The `components/core` component compiles both under ESP-IDF and standalone on the host. @@ -97,7 +97,8 @@ Acceptance criteria: - [x] Doxygen green; manual sync green; `ch-api.tex` documents endpoints. - [x] No ESP-IDF headers in `components/core`. -Out of scope: station list, user credentials, NVS encryption enablement +Out of scope for Slice 2: station list (later slices). NVS encryption landed +in fw 0.8.3 — see `docs/security-flash-nvs.md`. (production), chip drivers. ## Slice 3 — Companion-chip boot (complete) diff --git a/Software/sdkconfig.defaults b/Software/sdkconfig.defaults index 691716c..1964aaf 100644 --- a/Software/sdkconfig.defaults +++ b/Software/sdkconfig.defaults @@ -1,9 +1,20 @@ -# DigiRadio — default Kconfig -# NVS encryption: enable CONFIG_NVS_ENCRYPTION and flash encryption for -# production; nvs_keys partition is reserved in partitions.csv. +# DigiRadio — default Kconfig (development / first-board bring-up) +# Security: NVS + flash encryption — see docs/security-flash-nvs.md +# Production release mode: sdkconfig.defaults.production (overlay at build time) CONFIG_IDF_TARGET="esp32s3" +# Custom partition table (nvs + nvs_keys for encrypted storage) +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +# Flash encryption — DEVELOPMENT mode (re-flash plaintext until eFuse policy set) +CONFIG_SECURE_FLASH_ENC_ENABLED=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y + +# NVS encryption at rest (XTS-AES; keys in nvs_keys, protected by flash encryption) +CONFIG_NVS_ENCRYPTION=y + # C++23, exceptions and RTTI off (AGENTS.md §2) CONFIG_COMPILER_CXX_EXCEPTIONS=n CONFIG_COMPILER_CXX_RTTI=n diff --git a/Software/sdkconfig.defaults.production b/Software/sdkconfig.defaults.production new file mode 100644 index 0000000..57dba7c --- /dev/null +++ b/Software/sdkconfig.defaults.production @@ -0,0 +1,11 @@ +# DigiRadio — production security overlay (apply on top of sdkconfig.defaults) +# +# Build example: +# cp sdkconfig.defaults.production sdkconfig.defaults.release +# idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.release" build +# +# WARNING: RELEASE flash encryption is irreversible on the chip. Test on a +# dedicated unit after HIL validation in development mode (T8 checklist). + +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y +CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=n