BLE provisioning: drop PoP (Security0); bump nvs partition 24 KiB -> 64 KiB

BLE provisioning used protocomm Security1 with the device serial
number as proof-of-possession, on the reasoning that pairing should
require reading something off the physical unit. In practice the
companion app has no easy way to read that serial without a manual
step, so it was deriving the PoP from the BLE advertising name
instead (DigiRadio-XXXX -> XXXX) -- but that name is broadcast openly
to any scanner, so it was never actually secret. The PoP added
app/firmware coupling (an exact-match string derived independently on
both sides) without adding real secrecy, and a mismatch there was
silently blocking provisioning entirely. Switched to Security0 (no
PoP, no encryption) -- the same trust level as the SoftAP setup path
this runs alongside, which is already an open network.

Separately, bumped the nvs partition from 24 KiB to 64 KiB. The small
original size was a suspected contributor to intermittent
NvsAudioProfileStore::saveProfile() store_failed under this project's
accumulated write traffic (wifi creds, station_list,
audio_profile_json, last_preset) -- flagged but not applied in an
earlier commit today. otadata/nvs_keys/phy_init shift forward to make
room; they still fit before ota_0's existing 64 KiB alignment
boundary, so ota_0/ota_1/dsp stay at their original offsets. Applied
via idf.py erase-flash flash (required whenever partition offsets
move) and verified: fresh boot enters SoftAP + BLE setup mode with no
partition-table warnings, and the previously-saved FM ANTCAP
calibration (stored in the 24AA025E48 EEPROM, unaffected by the NVS
partition change) still applies automatically after re-provisioning
Wi-Fi.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178rASQ6ZETPMUamvpoR2KR
This commit is contained in:
2026-08-19 17:41:44 +02:00
co-authored by Claude Sonnet 5
parent c0eee4a4ad
commit 77e7623280
3 changed files with 22 additions and 14 deletions
@@ -31,15 +31,19 @@ namespace net::ble_provisioning {
* @dname start
* @param store Secure store the received credentials are saved
* to (same store POST /api/wifi writes to).
* @param deviceIdentity Supplies the BLE advertising name (softApSsid)
* and the proof-of-possession string (serialNumber).
* @param deviceIdentity Supplies the BLE advertising name (softApSsid).
* @return Ok once provisioning is advertising, or NetError::BleProvisioningFailed.
* @pubstate Starts the onboard ESP32-S3 BLE radio (independent of the BT1035
* UART module) and a process-lifetime wifi_provisioning manager
* singleton. On a successful join, saves credentials to store and
* reboots, mirroring wifiPostHandler's POST /api/wifi behaviour.
* Runs alongside the existing SoftAP + HTTP provisioning route,
* not instead of it — either path can complete setup.
* not instead of it — either path can complete setup. Uses
* protocomm Security0 (no proof-of-possession, no encryption):
* a PoP derived from the BLE advertising name would be visible to
* anyone scanning anyway, so it added app/firmware coupling
* without adding real secrecy — same trust level as the open
* SoftAP setup path this runs alongside.
*
* @author Michele Bigi
* @date 2026-08-18
@@ -125,10 +125,9 @@ start(core::ISecureStore& store, const core::DeviceIdentity& deviceIdentity)
gStore = &store;
gPendingCreds.reset();
// Copies kept for the lifetime of provisioning: wifi_prov_mgr_start_
// provisioning only borrows these pointers, it does not take ownership.
// Copy kept for the lifetime of provisioning: wifi_prov_mgr_start_
// provisioning only borrows this pointer, it does not take ownership.
static const std::string serviceName(deviceIdentity.softApSsid());
static const std::string pop(deviceIdentity.serialNumber());
const wifi_prov_mgr_config_t config{
.scheme = wifi_prov_scheme_ble,
@@ -141,7 +140,7 @@ start(core::ISecureStore& store, const core::DeviceIdentity& deviceIdentity)
return std::unexpected(NetError::BleProvisioningFailed);
}
if (wifi_prov_mgr_start_provisioning(WIFI_PROV_SECURITY_1, pop.c_str(),
if (wifi_prov_mgr_start_provisioning(WIFI_PROV_SECURITY_0, nullptr,
serviceName.c_str(),
nullptr) != ESP_OK) {
ESP_LOGE(kTag, "wifi_prov_mgr_start_provisioning failed");
@@ -149,9 +148,8 @@ start(core::ISecureStore& store, const core::DeviceIdentity& deviceIdentity)
return std::unexpected(NetError::BleProvisioningFailed);
}
ESP_LOGI(kTag,
"BLE provisioning advertising as %s (proof-of-possession: "
"device serial number)",
ESP_LOGI(kTag, "BLE provisioning advertising as %s (no PoP, same trust "
"level as the open SoftAP)",
serviceName.c_str());
return {};
}