From a68689fdd5df583e4f097c2f565c38da8ecd0cf4 Mon Sep 17 00:00:00 2001 From: tony weston Date: Sun, 23 Aug 2026 06:39:48 +0100 Subject: [PATCH] Add sm_sec_req_auto_pair: make auto-pairing on Security Request configurable On a Security Request from a peer we have no keys for, the host sends a Pairing Request immediately from ble_sm_sec_req_rx(). Some peripherals never answer a Pairing Request sent in the same instant as their own Security Request (e.g. the Okida OT-2000 oven module: no Pairing Response, link dropped after its 30 s SMP timeout). They pair first try with Bluedroid, macOS and iOS, which leave initiation to the application. Core Spec Vol 3, Part H, 2.4.6 says the central "may" initiate pairing on a Security Request. Make it configurable: ble_hs_cfg.sm_sec_req_auto_pair (syscfg BLE_SM_SEC_REQ_AUTO_PAIR, default 1 = unchanged). When cleared the request is ignored and the application initiates pairing, e.g. via NimBLEClient::secureConnection(). Peers we have keys for are unaffected. Exposed as NimBLEDevice::setSecurityAutoPairOnSecReq(). Verified on ESP32-C3 / arduino-esp32 3.3.11: the oven pairs with the flag cleared; with stock behaviour it fails regardless of MTU exchange, connection interval, key distribution, address type or SC/legacy. --- src/NimBLEDevice.cpp | 12 ++++++++++++ src/NimBLEDevice.h | 1 + src/nimble/nimble/host/include/host/ble_hs.h | 10 ++++++++++ src/nimble/nimble/host/src/ble_hs_cfg.c | 1 + src/nimble/nimble/host/src/ble_sm.c | 11 ++++++++++- src/syscfg/syscfg.h | 4 ++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index e125aeb9d..0663fdf48 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -1221,6 +1221,18 @@ void NimBLEDevice::setSecurityAuth(uint8_t auth_req) { * * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability * * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability */ +/** + * @brief Set whether a Security Request from a peer we have no keys for + * automatically sends a Pairing Request. + * @param [in] enable true (default): pair immediately. false: ignore the + * request; the application pairs via NimBLEClient::secureConnection(). + * @details Some peripherals never answer a Pairing Request sent in the same + * instant as their own Security Request. + */ +void NimBLEDevice::setSecurityAutoPairOnSecReq(bool enable) { + ble_hs_cfg.sm_sec_req_auto_pair = enable; +} // setSecurityAutoPairOnSecReq + void NimBLEDevice::setSecurityIOCap(uint8_t iocap) { ble_hs_cfg.sm_io_cap = iocap; } // setSecurityIOCap diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index fb64c83db..1aa5836b0 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -141,6 +141,7 @@ class NimBLEDevice { static void setSecurityAuth(bool bonding, bool mitm, bool sc); static void setSecurityAuth(uint8_t auth); static void setSecurityIOCap(uint8_t iocap); + static void setSecurityAutoPairOnSecReq(bool enable); static void setSecurityInitKey(uint8_t initKey); static void setSecurityRespKey(uint8_t respKey); static void setSecurityPasskey(uint32_t passKey); diff --git a/src/nimble/nimble/host/include/host/ble_hs.h b/src/nimble/nimble/host/include/host/ble_hs.h index 6a6c2e7b4..af8930fff 100644 --- a/src/nimble/nimble/host/include/host/ble_hs.h +++ b/src/nimble/nimble/host/include/host/ble_hs.h @@ -319,6 +319,16 @@ struct ble_hs_cfg { */ unsigned sm_keypress:1; + /** @brief Security Manager - auto-pair on Security Request + * + * If set (default), a Security Request from a peer we have no keys for + * immediately triggers a Pairing Request. If clear, it is ignored and the + * application initiates pairing (ble_gap_security_initiate). Some + * peripherals never answer a Pairing Request sent in the same instant as + * their Security Request. Peers we have keys for are unaffected. + */ + unsigned sm_sec_req_auto_pair:1; + /** @brief Security Manager Local Key Distribution Mask */ uint8_t sm_our_key_dist; diff --git a/src/nimble/nimble/host/src/ble_hs_cfg.c b/src/nimble/nimble/host/src/ble_hs_cfg.c index 4b9700628..2145a061b 100644 --- a/src/nimble/nimble/host/src/ble_hs_cfg.c +++ b/src/nimble/nimble/host/src/ble_hs_cfg.c @@ -28,6 +28,7 @@ struct ble_hs_cfg ble_hs_cfg = { .sm_mitm = MYNEWT_VAL(BLE_SM_MITM), .sm_sc = MYNEWT_VAL(BLE_SM_SC), .sm_keypress = MYNEWT_VAL(BLE_SM_KEYPRESS), + .sm_sec_req_auto_pair = MYNEWT_VAL(BLE_SM_SEC_REQ_AUTO_PAIR), .sm_our_key_dist = MYNEWT_VAL(BLE_SM_OUR_KEY_DIST), .sm_their_key_dist = MYNEWT_VAL(BLE_SM_THEIR_KEY_DIST), }; diff --git a/src/nimble/nimble/host/src/ble_sm.c b/src/nimble/nimble/host/src/ble_sm.c index 198c6fc87..c1345bd71 100644 --- a/src/nimble/nimble/host/src/ble_sm.c +++ b/src/nimble/nimble/host/src/ble_sm.c @@ -2090,7 +2090,16 @@ ble_sm_sec_req_rx(uint16_t conn_handle, struct os_mbuf **om, } } } else { - /* no keys present, start pairing */ + /* no keys present: start pairing, unless the application wants to + * initiate pairing itself. In that case only a clean "no keys" + * result is ignored; store errors are propagated. + */ + if (!ble_hs_cfg.sm_sec_req_auto_pair) { + if (res->app_status == BLE_HS_ENOENT) { + res->app_status = 0; + } + return; + } start_pairing = true; } diff --git a/src/syscfg/syscfg.h b/src/syscfg/syscfg.h index e5a511fea..0bb59a625 100644 --- a/src/syscfg/syscfg.h +++ b/src/syscfg/syscfg.h @@ -835,6 +835,10 @@ #define MYNEWT_VAL_BLE_SM_SC_DEBUG_KEYS (0) #endif +#ifndef MYNEWT_VAL_BLE_SM_SEC_REQ_AUTO_PAIR +#define MYNEWT_VAL_BLE_SM_SEC_REQ_AUTO_PAIR (1) +#endif + #ifndef MYNEWT_VAL_BLE_SM_SC_ONLY #define MYNEWT_VAL_BLE_SM_SC_ONLY (0) #endif