@@ -46,6 +46,7 @@ bool BLESecurity::m_securityStarted = false;
4646bool BLESecurity::m_passkeySet = false ;
4747bool BLESecurity::m_staticPasskey = true ;
4848bool BLESecurity::m_regenOnConnect = false ;
49+ bool BLESecurity::m_authenticationComplete = false ;
4950uint8_t BLESecurity::m_iocap = ESP_IO_CAP_NONE;
5051uint8_t BLESecurity::m_authReq = 0 ;
5152uint8_t BLESecurity::m_initKey = 0 ;
@@ -59,6 +60,7 @@ uint32_t BLESecurity::m_passkey = BLE_SM_DEFAULT_PASSKEY;
5960#if defined(CONFIG_BLUEDROID_ENABLED)
6061uint8_t BLESecurity::m_keySize = 16 ;
6162esp_ble_sec_act_t BLESecurity::m_securityLevel;
63+ FreeRTOS::Semaphore *BLESecurity::m_authCompleteSemaphore = nullptr ;
6264#endif
6365
6466/* **************************************************************************
@@ -191,6 +193,7 @@ void BLESecurity::regenPassKeyOnConnect(bool enable) {
191193void BLESecurity::resetSecurity () {
192194 log_d (" resetSecurity: Resetting security state" );
193195 m_securityStarted = false ;
196+ m_authenticationComplete = false ;
194197}
195198
196199// This function sets the authentication mode with bonding, MITM, and secure connection options.
@@ -263,6 +266,48 @@ bool BLESecurityCallbacks::onAuthorizationRequest(uint16_t connHandle, uint16_t
263266 return true ;
264267}
265268
269+ // This function waits for authentication to complete when bonding is enabled
270+ // It prevents GATT operations from proceeding before pairing completes
271+ void BLESecurity::waitForAuthenticationComplete (uint32_t timeoutMs) {
272+ #if defined(CONFIG_BLUEDROID_ENABLED)
273+ // Only wait if bonding is enabled
274+ if ((m_authReq & ESP_LE_AUTH_BOND) == 0 ) {
275+ return ;
276+ }
277+
278+ // If already authenticated, no need to wait
279+ if (m_authenticationComplete) {
280+ return ;
281+ }
282+
283+ // Semaphore should have been created in startSecurity()
284+ if (m_authCompleteSemaphore == nullptr ) {
285+ log_e (" Authentication semaphore not initialized" );
286+ return ;
287+ }
288+
289+ // Wait for authentication with timeout
290+ bool success = m_authCompleteSemaphore->timedWait (" waitForAuthenticationComplete" , timeoutMs / portTICK_PERIOD_MS);
291+
292+ if (!success) {
293+ log_w (" Timeout waiting for authentication to complete" );
294+ }
295+ #endif
296+ }
297+
298+ // This function signals that authentication has completed
299+ // Called from ESP_GAP_BLE_AUTH_CMPL_EVT handler
300+ void BLESecurity::signalAuthenticationComplete () {
301+ #if defined(CONFIG_BLUEDROID_ENABLED)
302+ m_authenticationComplete = true ;
303+
304+ // Signal waiting threads if semaphore exists
305+ if (m_authCompleteSemaphore != nullptr ) {
306+ m_authCompleteSemaphore->give ();
307+ }
308+ #endif
309+ }
310+
266311/* **************************************************************************
267312 * Bluedroid functions *
268313 ***************************************************************************/
@@ -285,6 +330,18 @@ bool BLESecurity::startSecurity(esp_bd_addr_t bd_addr, int *rcPtr) {
285330 }
286331
287332 if (m_securityEnabled) {
333+ // Initialize semaphore before starting security to avoid race condition
334+ if (m_authCompleteSemaphore == nullptr ) {
335+ m_authCompleteSemaphore = new FreeRTOS::Semaphore (" AuthComplete" );
336+ }
337+
338+ // Reset authentication complete flag when starting new security negotiation
339+ m_authenticationComplete = false ;
340+
341+ // Consume any pending semaphore signals from previous operations
342+ // This ensures the next wait will block until the new auth completes
343+ m_authCompleteSemaphore->take (" startSecurity-reset" );
344+
288345 int rc = esp_ble_set_encryption (bd_addr, m_securityLevel);
289346 if (rc != ESP_OK) {
290347 log_e (" esp_ble_set_encryption: rc=%d %s" , rc, GeneralUtils::errorToString (rc));
0 commit comments