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 <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 07:46:59 +02:00
co-authored by Cursor
parent 9b46fe9465
commit a8f2fd1c6c
17 changed files with 259 additions and 48 deletions
+8 -5
View File
@@ -7,7 +7,7 @@
**Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3** **Open-source digital radio — Si4684 tuner · ADAU1701 SigmaDSP · Bluetooth aptX Adaptive · ESP32-S3**
![Status](https://img.shields.io/badge/hardware-verified-brightgreen) ![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) ![CI](https://github.com/manvalan/DigiRadio/actions/workflows/ci.yml/badge.svg)
![PCB](https://img.shields.io/badge/PCB-6--layer-blue) ![PCB](https://img.shields.io/badge/PCB-6--layer-blue)
![Hardware License](https://img.shields.io/badge/hardware-CERN--OHL--S-lightgrey) ![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 - **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 - 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 | | 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 | | **Presets** | Station list CRUD, reorder, recall with audio profile re-apply, last-preset restore at boot |
| **Bluetooth** | Discoverable pairing, A2DP status, disconnect | | **Bluetooth** | Discoverable pairing, A2DP status, disconnect |
| **Network** | SoftAP setup mode, STA provisioning, tabbed gzipped SPA (`/`), typed JSON REST API | | **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` | | **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 Architecture follows a **functional core + imperative shell**: pure domain logic
@@ -83,6 +83,7 @@ Vertical slices landed on `main` (newest first):
| Version | Highlights | | 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.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.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 | | **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.50.6** | ADAU1701 runtime EQ/mixer, Si4684 tuning & DAB service list, BT1035 driver | | **0.50.6** | ADAU1701 runtime EQ/mixer, Si4684 tuning & DAB service list, BT1035 driver |
| **0.30.4** | Secure store, Wi-Fi provisioning, companion-chip boot, walking skeleton | | **0.30.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 | | **PCB layout** | 6-layer, DRC clean, plane continuity verified |
| **BOM** | Finalised (manufacturable / sourced) | | **BOM** | Finalised (manufacturable / sourced) |
| **Prototype** | In fabrication (PCBWay) | | **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 | | **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 | | **Production hardening** | Si4684 blob policy (T7), NVS encryption (T8) — open |
Agent task list: [`Software/docs/TODO.md`](Software/docs/TODO.md). Agent task list: [`Software/docs/TODO.md`](Software/docs/TODO.md).
+4 -2
View File
@@ -2,7 +2,7 @@
Open-source Hi-Fi DAB+/FM receiver firmware for the ESP32-S3. 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`. `IntegrationService`; **13** host tests; CI on `main`.
See [`docs/TODO.md`](docs/TODO.md) for the agent task list. 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 ```bash
idf.py set-target esp32s3 idf.py set-target esp32s3
idf.py build idf.py build
idf.py erase-flash flash # once when upgrading to encrypted NVS (0.8.3+)
idf.py -p <port> flash monitor idf.py -p <port> flash monitor
``` ```
@@ -41,7 +42,7 @@ Manual PDF (design + HTTP API + class reference):
cd docs/manual && latexmk -lualatex manual.tex cd docs/manual && latexmk -lualatex manual.tex
``` ```
## HTTP API (fw 0.8.2) ## HTTP API (fw 0.8.3)
| Method | Path | Purpose | | 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/services/` | Tuner, audio, Bluetooth, station, integration services |
| `components/net/` | Wi-Fi, HTTP server, gzipped web UI | | `components/net/` | Wi-Fi, HTTP server, gzipped web UI |
| `docs/manual/` | LaTeX technical manual (canonical) | | `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) | | `docs/TODO.md` | Agent task list (prioritised backlog) |
See [`AGENTS.md`](AGENTS.md) §12 and [`instructions.md`](instructions.md) for See [`AGENTS.md`](AGENTS.md) §12 and [`instructions.md`](instructions.md) for
+1 -1
View File
@@ -7,7 +7,7 @@ idf_component_register(
"src/NetBootstrap.cpp" "src/NetBootstrap.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
EMBED_FILES "www/index.html.gz" 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) target_compile_features(${COMPONENT_LIB} PUBLIC cxx_std_23)
+5 -10
View File
@@ -22,9 +22,9 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "esp_wifi.h" #include "esp_wifi.h"
#include "nvs_flash.h"
#include "audio/AudioService.hpp" #include "audio/AudioService.hpp"
#include "bluetooth/BluetoothService.hpp" #include "bluetooth/BluetoothService.hpp"
#include "secure_store/NvsPlatformInit.hpp"
#include "station/StationService.hpp" #include "station/StationService.hpp"
#include "tuner/TunerService.hpp" #include "tuner/TunerService.hpp"
@@ -38,21 +38,16 @@ constexpr char kTag[] = "NetBootstrap";
* *
* @dname initPlatform * @dname initPlatform
* @return Ok on success, or a NetError describing the failure. * @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 * @author Michele Bigi
* @date 2026-07-06 * @date 2026-07-06
*/ */
[[nodiscard]] std::expected<void, NetError> initPlatform() [[nodiscard]] std::expected<void, NetError> initPlatform()
{ {
esp_err_t nvsErr = nvs_flash_init(); const auto nvsResult = secure_store::initEncryptedStorage();
if (nvsErr == ESP_ERR_NVS_NO_FREE_PAGES if (!nvsResult) {
|| nvsErr == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_LOGE(kTag, "encrypted NVS init failed");
ESP_ERROR_CHECK(nvs_flash_erase());
nvsErr = nvs_flash_init();
}
if (nvsErr != ESP_OK) {
ESP_LOGE(kTag, "nvs_flash_init failed");
return std::unexpected(NetError::NvsInitFailed); return std::unexpected(NetError::NvsInitFailed);
} }
@@ -52,7 +52,7 @@ namespace net {
namespace { namespace {
constexpr char kTag[] = "SetupWebServer"; constexpr char kTag[] = "SetupWebServer";
constexpr char kFirmwareVersion[] = "0.8.2"; constexpr char kFirmwareVersion[] = "0.8.3";
constexpr unsigned kRebootDelaySec = 3; constexpr unsigned kRebootDelaySec = 3;
extern const uint8_t www_index_html_gz_start[] asm( extern const uint8_t www_index_html_gz_start[] asm(
@@ -2,6 +2,7 @@ idf_component_register(
SRCS SRCS
"src/NvsSecureStore.cpp" "src/NvsSecureStore.cpp"
"src/NvsAudioProfileStore.cpp" "src/NvsAudioProfileStore.cpp"
"src/NvsPlatformInit.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES core nvs_flash REQUIRES core nvs_flash
) )
@@ -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 <expected>
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<void, NvsInitError> initEncryptedStorage() noexcept;
} // namespace secure_store
@@ -12,9 +12,9 @@
* You may obtain a copy of the License at * You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Production builds should enable NVS encryption (nvs_keys partition in * Production: NVS encryption + flash encryption enabled in sdkconfig.defaults
* partitions.csv) per ESP-IDF security docs; this slice uses plain NVS * (fw 0.8.3+). Call secure_store::initEncryptedStorage() before first use;
* for development bring-up. * see docs/security-flash-nvs.md.
* *
* @author Michele Bigi * @author Michele Bigi
* @date 2026-07-06 * @date 2026-07-06
@@ -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<int>(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<void, NvsInitError> 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<unsigned>(err));
err = nvs_flash_erase();
if (err != ESP_OK) {
ESP_LOGE(kTag, "nvs_flash_erase failed (0x%x)", static_cast<unsigned>(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<unsigned>(err));
return std::unexpected(NvsInitError::InitFailed);
}
return {};
}
} // namespace secure_store
+9 -11
View File
@@ -12,12 +12,12 @@ errors, no plaintext secrets.
Working directory for all commands is `Software/`. Working directory for all commands is `Software/`.
**Current firmware:** `0.8.2`tabbed configuration Web UI (full API **Current firmware:** `0.8.3`NVS + flash encryption (dev mode), tabbed Web
coverage), integration service, RDS/DLS metadata, CI gate. UI, integration service, CI gate.
--- ---
## Completed (fw 0.7.00.7.2) ## Completed (fw 0.7.00.8.3)
- **Integration service (T5)** — preset recall with audio profile re-apply, - **Integration service (T5)** — preset recall with audio profile re-apply,
last-preset NVS, \texttt{app\_main} orchestration. 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 Procurement documented in `Si4684-Firmware/README.md`; CI job
`si4684-blobs` runs `tools/check_si4684_blobs.py`. `si4684-blobs` runs `tools/check_si4684_blobs.py`.
### T8. Flash/NVS encryption enablement ### T8. Flash/NVS encryption enablement — **DONE (fw 0.8.3)**
**Why:** secure storage holds Wi-Fi and user credentials; encryption at `CONFIG_NVS_ENCRYPTION` + flash encryption (development mode) in
rest was deferred. `sdkconfig.defaults`; `secure_store::initEncryptedStorage()`; production
**What:** enable NVS encryption on an encrypted partition (with flash overlay `sdkconfig.defaults.production`; HIL checklist in
encryption), per current ESP-IDF security docs. Verify the mechanism `docs/security-flash-nvs.md`. **Pending:** device verification when PCB
before enabling; keep keys out of the repo. arrives.
**Done when:** credentials are encrypted at rest and the boot path still
loads them.
--- ---
+7 -5
View File
@@ -36,7 +36,7 @@ Returns a health-check DTO serialised by
\begin{drnote}[Response schema] \begin{drnote}[Response schema]
\begin{drcode}[JSON] \begin{drcode}[JSON]
{"status":"ok","fw":"0.8.2", {"status":"ok","fw":"0.8.3",
"chips":{"si4684":true,"adau1701":true,"bt1035":true}} "chips":{"si4684":true,"adau1701":true,"bt1035":true}}
\end{drcode} \end{drcode}
\begin{itemize} \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. \texttt{core::Secret} in RAM and are never logged or returned by the API.
\begin{drcaution}[Encryption at rest] \begin{drcaution}[Encryption at rest]
Development builds use plain NVS. Production should enable NVS encryption Firmware~0.8.3+ enables NVS encryption (\texttt{CONFIG\_NVS\_ENCRYPTION}) and
using the reserved \texttt{nvs\_keys} partition (see flash encryption in development mode (\texttt{sdkconfig.defaults}). Keys live in
\texttt{partitions.csv} and \texttt{sdkconfig.defaults} comments) per the \texttt{nvs\_keys} partition; Wi-Fi passwords remain wrapped in
current ESP-IDF security guidance. \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} \end{drcaution}
+3 -2
View File
@@ -99,8 +99,9 @@ RAII STA join helper with an explicit connect timeout. Assumes
\section{NvsSecureStore}\label{cls:NvsSecureStore} \section{NvsSecureStore}\label{cls:NvsSecureStore}
\texttt{ISecureStore} implementation backed by an NVS namespace. Passwords \texttt{ISecureStore} implementation backed by an NVS namespace. Passwords
are stored as NVS strings and never logged. Production should enable NVS are stored as NVS strings and never logged. NVS encryption and flash
encryption using the reserved \texttt{nvs\_keys} partition. encryption are enabled in \texttt{sdkconfig.defaults}; initialisation runs in
\texttt{secure\_store::initEncryptedStorage()} before network bring-up.
% ------------------------------------------------------------------ % ------------------------------------------------------------------
% Hardware drivers (Slice 3) % Hardware drivers (Slice 3)
+3 -2
View File
@@ -180,8 +180,9 @@ Chapter~\ref{ch:api}.
Sensitive data --- Wi-Fi credentials today; user credentials and the Sensitive data --- Wi-Fi credentials today; user credentials and the
station/frequency list in later slices --- uses \texttt{core::Secret} so station/frequency list in later slices --- uses \texttt{core::Secret} so
values cannot be logged or implicitly converted to a string; buffers are values cannot be logged or implicitly converted to a string; buffers are
cleared on destruction. Production builds should enable NVS encryption cleared on destruction. Firmware~0.8.3+ enables NVS and flash encryption at
at rest (see Chapter~\ref{sec:api-storage}). 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} \section{Companion-chip boot at power-up}
\label{sec:fw-chip-boot} \label{sec:fw-chip-boot}
+62
View File
@@ -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 Espressifs 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`
+4 -3
View File
@@ -62,8 +62,8 @@ so every later slice drops into a working frame.
Build: Build:
- Top-level ESP-IDF project targeting `esp32s3`. - Top-level ESP-IDF project targeting `esp32s3`.
- `sdkconfig.defaults` sets C++23, exceptions off, and documents flash/NVS - `sdkconfig.defaults` sets C++23, exceptions off, NVS + flash encryption
encryption options (not hard-enabled until production). (development mode). Production overlay: `sdkconfig.defaults.production`.
- The `components/core` component compiles both under ESP-IDF and - The `components/core` component compiles both under ESP-IDF and
standalone on the host. standalone on the host.
@@ -97,7 +97,8 @@ Acceptance criteria:
- [x] Doxygen green; manual sync green; `ch-api.tex` documents endpoints. - [x] Doxygen green; manual sync green; `ch-api.tex` documents endpoints.
- [x] No ESP-IDF headers in `components/core`. - [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. (production), chip drivers.
## Slice 3 — Companion-chip boot (complete) ## Slice 3 — Companion-chip boot (complete)
+14 -3
View File
@@ -1,9 +1,20 @@
# DigiRadio — default Kconfig # DigiRadio — default Kconfig (development / first-board bring-up)
# NVS encryption: enable CONFIG_NVS_ENCRYPTION and flash encryption for # Security: NVS + flash encryption — see docs/security-flash-nvs.md
# production; nvs_keys partition is reserved in partitions.csv. # Production release mode: sdkconfig.defaults.production (overlay at build time)
CONFIG_IDF_TARGET="esp32s3" 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) # C++23, exceptions and RTTI off (AGENTS.md §2)
CONFIG_COMPILER_CXX_EXCEPTIONS=n CONFIG_COMPILER_CXX_EXCEPTIONS=n
CONFIG_COMPILER_CXX_RTTI=n CONFIG_COMPILER_CXX_RTTI=n
+11
View File
@@ -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