From 15392bf58104a8c3359004783a45a56fe2e5515b Mon Sep 17 00:00:00 2001 From: thekurtovic <40248206+thekurtovic@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:02:18 -0500 Subject: [PATCH] Add asynchronous client connection secure * Adds parameter `rcPtr` to `NimBLEDevice::startSecurity`, default value works as the original method. * * `rcPtr`: if not nullptr, will allow caller to obtain the internal return code. * Adds parameter `async` to `NimBLEClient::secureConnection`, default value works as the original method. * * `async`; if true, will send the secure command and return immediately with a true value for successfully sending the command, else false. --- src/NimBLEClient.cpp | 26 ++++++++++++++++++++++++-- src/NimBLEClient.h | 3 ++- src/NimBLEDevice.cpp | 9 ++++++--- src/NimBLEDevice.h | 2 +- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index b2eb70e..3d7cc10 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -63,6 +63,7 @@ NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress) m_pClientCallbacks{&defaultCallbacks}, m_connHandle{BLE_HS_CONN_HANDLE_NONE}, m_terminateFailCount{0}, + m_asyncSecureAttempt{0}, m_config{}, # if CONFIG_BT_NIMBLE_EXT_ADV m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK}, @@ -293,12 +294,26 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes, /** * @brief Initiate a secure connection (pair/bond) with the server.\n * Called automatically when a characteristic or descriptor requires encryption or authentication to access it. + * @param [in] async If true, the connection will be secured asynchronously and this function will return immediately.\n + * If false, this function will block until the connection is secured or the client disconnects. * @return True on success. - * @details This is a blocking function and should not be used in a callback. + * @details If async=false, this function will block and should not be used in a callback. */ -bool NimBLEClient::secureConnection() const { +bool NimBLEClient::secureConnection(bool async) const { NIMBLE_LOGD(LOG_TAG, ">> secureConnection()"); + int rc = 0; + if (async && !NimBLEDevice::startSecurity(m_connHandle, &rc)) { + m_lastErr = rc; + m_asyncSecureAttempt = 0; + return false; + } + + if (async) { + m_asyncSecureAttempt++; + return true; + } + NimBLETaskData taskData(const_cast(this), BLE_HS_ENOTCONN); m_pTaskData = &taskData; int retryCount = 1; @@ -988,6 +1003,8 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { NIMBLE_LOGD(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); pClient->m_terminateFailCount = 0; + pClient->m_asyncSecureAttempt = 0; + // Don't call the disconnect callback if we are waiting for a connection to complete and it fails if (rc != (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) || pClient->m_config.asyncConnect) { pClient->m_pClientCallbacks->onDisconnect(pClient, rc); @@ -1150,7 +1167,12 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) { if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) { // Key is missing, try deleting. ble_store_util_delete_peer(&peerInfo.m_desc.peer_id_addr); + // Attempt a retry if async secure failed. + if (pClient->m_asyncSecureAttempt == 1) { + pClient->secureConnection(true); + } } else { + pClient->m_asyncSecureAttempt = 0; pClient->m_pClientCallbacks->onAuthenticationComplete(peerInfo); } } diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 3d725f1..b558b33 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -65,7 +65,7 @@ class NimBLEClient { bool setConnection(uint16_t connHandle); uint16_t getMTU() const; bool exchangeMTU(); - bool secureConnection() const; + bool secureConnection(bool async = false) const; void setConnectTimeout(uint32_t timeout); bool setDataLen(uint16_t txOctets); bool discoverAttributes(); @@ -131,6 +131,7 @@ class NimBLEClient { NimBLEClientCallbacks* m_pClientCallbacks; uint16_t m_connHandle; uint8_t m_terminateFailCount; + mutable uint8_t m_asyncSecureAttempt; Config m_config; # if CONFIG_BT_NIMBLE_EXT_ADV diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index c109cfc..e88a4fb 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -1136,14 +1136,17 @@ uint32_t NimBLEDevice::getSecurityPasskey() { /** * @brief Start the connection securing and authorization for this connection. * @param connHandle The connection handle of the peer device. - * @returns NimBLE stack return code, 0 = success. + * @param rcPtr Optional pointer to pass the return code to the caller. + * @returns True if successfully started, success = 0 or BLE_HS_EALREADY. */ -bool NimBLEDevice::startSecurity(uint16_t connHandle) { +bool NimBLEDevice::startSecurity(uint16_t connHandle, int* rcPtr) { int rc = ble_gap_security_initiate(connHandle); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); } - + if (rcPtr) { + *rcPtr = rc; + } return rc == 0 || rc == BLE_HS_EALREADY; } // startSecurity diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 96b2c67..7be94f1 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -129,7 +129,7 @@ class NimBLEDevice { static void setSecurityRespKey(uint8_t respKey); static void setSecurityPasskey(uint32_t passKey); static uint32_t getSecurityPasskey(); - static bool startSecurity(uint16_t connHandle); + static bool startSecurity(uint16_t connHandle, int* rcPtr = nullptr); static bool setMTU(uint16_t mtu); static uint16_t getMTU(); static void onReset(int reason);