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:
+9
-11
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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`
|
||||
Reference in New Issue
Block a user