mirror of
https://github.com/arendst/Tasmota.git
synced 2026-09-11 09:13:17 +00:00
Store BLE security bonds in UfsJsonSettings (#25015)
* Store BLE security bonds in UfsJsonSettings Store BLE security bonds in `UfsJsonSettings`, as promised in PR #25008 * Optimize LTK storage
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
#ifdef ESP32 // ESP32 family only. Use define USE_HM10 for ESP8266 support
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#ifdef USE_BLE_ESP32
|
||||
#ifdef USE_UFILESYS
|
||||
|
||||
/*
|
||||
xdrv_79:
|
||||
@@ -151,6 +152,9 @@ i.e. the Bluetooth of the ESP can be shared without conflict.
|
||||
|
||||
#define XDRV_79 79
|
||||
|
||||
#define XDRV_79_KEY "drvset79" // Unique driver key for central filesystem
|
||||
#define MAX_BOND_DEVICES 16
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string.h>
|
||||
@@ -230,8 +234,6 @@ namespace BLE_ESP32 {
|
||||
#define BLE_ESP32_MAXNAMELEN 32
|
||||
#define BLE_ESP32_MAXALIASLEN 32
|
||||
|
||||
#define BLE_STORE_DIR "/.blestore"
|
||||
|
||||
#define MAX_BLE_DATA_LEN 100
|
||||
struct generic_sensor_t {
|
||||
int16_t state;
|
||||
@@ -652,64 +654,6 @@ const char *BLE_RESTART_BLE_REASON_CONN_LIMIT = PSTR("connect failed with connec
|
||||
const char *BLE_RESTART_BLE_REASON_CONN_EXISTS = PSTR("connect failed with connection exists");
|
||||
const char *BLERestartBLEReason = nullptr;
|
||||
|
||||
// Global native hook: Filepath
|
||||
static void ble_get_bond_filepath(const uint8_t* val, int type, char* out_buf, size_t buf_len) {
|
||||
// Format: BLE_STORE_DIR/AABBCCDDEEFF.TYPE
|
||||
snprintf(out_buf, buf_len, BLE_STORE_DIR "/%02X%02X%02X%02X%02X%02X.%03d",
|
||||
val[5], val[4], val[3], val[2], val[1], val[0], type);
|
||||
}
|
||||
|
||||
// Global native hook: Read stored BLE data
|
||||
int ble_local_store_read(int type, const union ble_store_key* key, union ble_store_value* value) {
|
||||
if (type != BLE_STORE_OBJ_TYPE_PEER_SEC) return BLE_HS_ENOENT; // Only that is needed at the moment
|
||||
char filepath[sizeof(BLE_STORE_DIR) + 20];
|
||||
|
||||
// Pass the raw 6-byte MAC array pointer from the internal union struct
|
||||
ble_get_bond_filepath(key->sec.peer_addr.val, type, filepath, sizeof(filepath));
|
||||
|
||||
FILE* file = fopen(filepath, "r");
|
||||
if (file) {
|
||||
if (fread(&value->sec, 1, sizeof(ble_store_value_sec), file) == sizeof(ble_store_value_sec)) {
|
||||
fclose(file);
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: BLE data loaded from %s", filepath);
|
||||
return 0; // 0 = Success for the NimBLE controller
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
return BLE_HS_ENOENT; // Key not found, falls back to normal pairing workflow
|
||||
}
|
||||
|
||||
// Global native hook: Write BLE data
|
||||
int ble_local_store_write(int type, const union ble_store_value* value) {
|
||||
if (type != BLE_STORE_OBJ_TYPE_PEER_SEC) return BLE_HS_ENOMEM; // Only that is needed at the moment
|
||||
char filepath[sizeof(BLE_STORE_DIR) + 20];
|
||||
|
||||
// Pass the raw 6-byte MAC array pointer from the internal union struct
|
||||
ble_get_bond_filepath(value->sec.peer_addr.val, type, filepath, sizeof(filepath));
|
||||
|
||||
FILE* file = fopen(filepath, "w");
|
||||
if (file) {
|
||||
size_t written = fwrite(&value->sec, 1, sizeof(ble_store_value_sec), file);
|
||||
fclose(file);
|
||||
if (written == sizeof(ble_store_value_sec)) {
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: BLE data saved in %s", filepath);
|
||||
return 0; // 0 = Success
|
||||
}
|
||||
}
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: Save BLE data failed");
|
||||
return BLE_HS_ENOMEM; // Write failed
|
||||
}
|
||||
|
||||
// Global API: Delete dynamic storage file
|
||||
void ble_local_store_delete(const uint8_t* mac_addr) {
|
||||
if (!mac_addr) return;
|
||||
char filepath[sizeof(BLE_STORE_DIR) + 20];
|
||||
|
||||
// Only delete files of BLE_STORE_OBJ_TYPE_PEER_SEC as no others are needed at the moment
|
||||
ble_get_bond_filepath(mac_addr, BLE_STORE_OBJ_TYPE_PEER_SEC, filepath, sizeof(filepath));
|
||||
unlink(filepath);
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: Deleted BLE data file %s", filepath);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* log of all devices present
|
||||
@@ -1249,6 +1193,132 @@ bool isDeviceInFilter(const String& deviceName) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/*********************************************************************************************\
|
||||
* BLE storage functions
|
||||
\*********************************************************************************************/
|
||||
|
||||
int BLEStoreModify(const uint8_t* peerMAC, const char* peerKeyStr) {
|
||||
char xdrv_key[] = XDRV_79_KEY "ltk";
|
||||
bool first = true;
|
||||
uint8_t peerAddr[6];
|
||||
memcpy(peerAddr, peerMAC, sizeof(peerAddr));
|
||||
ReverseMAC(peerAddr);
|
||||
char peerAddrStr[13];
|
||||
dump(peerAddrStr, sizeof(peerAddrStr), peerAddr, sizeof(peerAddr));
|
||||
|
||||
// Read and write entries from/to settings file
|
||||
String json = UfsJsonSettingsRead(xdrv_key);
|
||||
String jsonOutput = "{\"";
|
||||
jsonOutput += xdrv_key;
|
||||
jsonOutput += "\":{\"ltkBondKeys\":[";
|
||||
if (json.length()) {
|
||||
JsonParser parser((char*)json.c_str());
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (root) {
|
||||
JsonParserArray ltkBondKeys = root["ltkBondKeys"];
|
||||
if (ltkBondKeys) {
|
||||
int8_t i = -1;
|
||||
while(ltkBondKeys[++i]) {
|
||||
if (strncmp(ltkBondKeys[i].getStr(), peerAddrStr, 12)) { // not ours, just copy
|
||||
if (!first) jsonOutput += ',';
|
||||
jsonOutput += '"';
|
||||
jsonOutput += ltkBondKeys[i].getStr();
|
||||
jsonOutput += '"';
|
||||
first = false;
|
||||
} else {
|
||||
continue; // Skip when found; means remove the entry at this point
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add entry
|
||||
if (peerKeyStr) {
|
||||
if (!first) jsonOutput += ',';
|
||||
jsonOutput += '"';
|
||||
jsonOutput += peerAddrStr;
|
||||
jsonOutput += ':';
|
||||
jsonOutput += peerKeyStr;
|
||||
jsonOutput += '"';
|
||||
}
|
||||
|
||||
jsonOutput += "]}}";
|
||||
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: ltk-setting: %s", jsonOutput.c_str());
|
||||
|
||||
if (jsonOutput.indexOf("[]") != -1) { // No pair present, so no setting is needed and deleted
|
||||
UfsJsonSettingsDelete(xdrv_key);
|
||||
return 0; // 0 = Success for the NimBLE controller
|
||||
}
|
||||
|
||||
if (UfsJsonSettingsWrite(jsonOutput.c_str())) return 0; // 0 = Success for the NimBLE controller
|
||||
AddLog(LOG_LEVEL_ERROR, "BLE: Saving bond keys failed");
|
||||
return BLE_HS_ENOMEM;
|
||||
}
|
||||
|
||||
// Global Hook: Read the 16-byte raw LTK using clean native C String slicing
|
||||
int ble_local_store_read(int type, const union ble_store_key* key, union ble_store_value* value) {
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: ble_local_store_read called");
|
||||
if (type != BLE_STORE_OBJ_TYPE_PEER_SEC) return BLE_HS_ENOENT;
|
||||
if (!key->sec.peer_addr.val) return BLE_HS_ENOENT;
|
||||
|
||||
char xdrv_key[] = XDRV_79_KEY "ltk";
|
||||
uint8_t peerAddr[6];
|
||||
memcpy(peerAddr, key->sec.peer_addr.val, sizeof(peerAddr));
|
||||
ReverseMAC(peerAddr);
|
||||
char peerAddrStr[13];
|
||||
dump(peerAddrStr, sizeof(peerAddrStr), peerAddr, sizeof(peerAddr));
|
||||
|
||||
// Read entries from settings file
|
||||
String json = UfsJsonSettingsRead(xdrv_key);
|
||||
if (json.length()) {
|
||||
JsonParser parser((char*)json.c_str());
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (root) {
|
||||
JsonParserArray ltkBondKeys = root["ltkBondKeys"];
|
||||
if (ltkBondKeys) {
|
||||
int8_t i = -1;
|
||||
while(ltkBondKeys[++i]) {
|
||||
if (!strncmp(ltkBondKeys[i].getStr(), peerAddrStr, 12)) { // Entry found
|
||||
const char* peerKeyStr = ltkBondKeys[i].getStr() + 13;
|
||||
if (strlen(peerKeyStr) != 32 ) return BLE_HS_ENOENT;
|
||||
HexToBytes(peerKeyStr, value->sec.ltk, 16);
|
||||
value->sec.peer_addr.type = key->sec.peer_addr.type;
|
||||
memcpy(value->sec.peer_addr.val, key->sec.peer_addr.val, 6);
|
||||
value->sec.authenticated = 1;
|
||||
value->sec.ltk_present = 1;
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: Loaded Bond Key Idx: %d, MAC: %s, Key: %s", i, peerAddrStr, peerKeyStr);
|
||||
return 0; // 0 = Success for the NimBLE controller
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return BLE_HS_ENOENT;
|
||||
}
|
||||
|
||||
// Global Hook: Write LTK
|
||||
int ble_local_store_write(int type, const union ble_store_value* value) {
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: ble_local_store_write called");
|
||||
if (type != BLE_STORE_OBJ_TYPE_PEER_SEC) return BLE_HS_ENOMEM; // Only that is needed at the moment
|
||||
if (!value->sec.peer_addr.val) return BLE_HS_ENOMEM;
|
||||
|
||||
char peerKeyStr[33];
|
||||
dump(peerKeyStr, 33, value->sec.ltk, 16);
|
||||
|
||||
return BLEStoreModify(value->sec.peer_addr.val, peerKeyStr);
|
||||
}
|
||||
|
||||
// Global Hook: Delete LTK
|
||||
int ble_local_store_delete(int type, const union ble_store_key* key) {
|
||||
AddLog(BLELogLevel[LOG_LEVEL_DEBUG], "BLE: ble_local_store_delete called");
|
||||
if (type != BLE_STORE_OBJ_TYPE_PEER_SEC) return BLE_HS_ENOMEM; // Only that is needed at the moment
|
||||
if (!key->sec.peer_addr.val) return BLE_HS_ENOMEM;
|
||||
|
||||
return BLEStoreModify(key->sec.peer_addr.val, nullptr);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Advertisment details
|
||||
\*********************************************************************************************/
|
||||
@@ -1399,7 +1469,6 @@ void postAdvertismentDetails(){
|
||||
* Classes
|
||||
\*********************************************************************************************/
|
||||
|
||||
// does not really take any action
|
||||
class BLESensorCallback : public NimBLEClientCallbacks {
|
||||
void onConnect(NimBLEClient* pClient) {
|
||||
#ifdef BLE_ESP32_DEBUG
|
||||
@@ -1842,11 +1911,10 @@ static void BLETaskStopStartNimBLE(NimBLEClient **ppClient, bool start = true){
|
||||
NimBLEDevice::init("BLE_ESP32");
|
||||
|
||||
// --- NEUTRAL ADVANCED STORAGE ROUTING LAYER ---
|
||||
// Create the physical folder BLE_STORE_DIR in the flash memory
|
||||
mkdir(BLE_STORE_DIR, 0777);
|
||||
// Hook the dynamic directory filesystem into the native Apache MyNewT config
|
||||
ble_hs_cfg.store_read_cb = ble_local_store_read;
|
||||
ble_hs_cfg.store_write_cb = ble_local_store_write;
|
||||
ble_hs_cfg.store_delete_cb = ble_local_store_delete;
|
||||
|
||||
// Set default global security capabilities for the Bluetooth stack
|
||||
NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC);
|
||||
@@ -1926,8 +1994,8 @@ static void BLEDoPairing(NimBLEClient **ppClient) {
|
||||
|
||||
BLERunningScan = 0;
|
||||
// Delete old bonding
|
||||
ble_local_store_delete(pairingAddress.getBase()->val);
|
||||
NimBLEDevice::deleteBond(pairingAddress); // Must be executed after ble_local_store_delete
|
||||
BLEStoreModify(pairingAddress.getBase()->val, nullptr);
|
||||
NimBLEDevice::deleteBond(pairingAddress); // Must be executed after BLEStoreModify
|
||||
|
||||
if (pClient->connect(pairingAddress, false, false, false)) {
|
||||
AddLog(LOG_LEVEL_DEBUG, "BLE: Connected for pairing. Starting crypto handshake...");
|
||||
@@ -1939,7 +2007,7 @@ static void BLEDoPairing(NimBLEClient **ppClient) {
|
||||
pairingState = PAIRING_NONE;
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_ERROR, "BLE: Connect for pairing failed.");
|
||||
AddLog(LOG_LEVEL_ERROR, "BLE: Connect for pairing failed. Please try again.");
|
||||
pairingState = PAIRING_NONE;
|
||||
}
|
||||
}
|
||||
@@ -3990,7 +4058,7 @@ void sendExample(){
|
||||
// end #ifdef BLE_ESP32_EXAMPLES
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
#endif // USE_UFILESYS
|
||||
#endif // USE_BLE_ESP32
|
||||
#endif // CONFIG_IDF_TARGET_ESP32 or CONFIG_IDF_TARGET_ESP32C3
|
||||
#endif // ESP32
|
||||
|
||||
Reference in New Issue
Block a user