esp-nimble-cpp
esp-nimble-cpp copied to clipboard
Bonded flag not set onConnect for previously bonded device
Hey there, thanks for creating an awesome wrapper for NimBLE on the ESP32!
I've been having an issue trying to get the setScanFilter
function working. If I whitelist scanning or connections, I cannot get the device to show up (or connect) when scanning from an Android phone, even after bonding.
I set the following security setup before starting the server:
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityCallbacks(ble);
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
NimBLEDevice::init("");
NimBLEDevice::setMTU(512);
I'm assuming the bonded device needs to be added to the whitelist. Does this happen out of the box or is it additional functionality I need to add in the server callbacks?
Thanks for any help!
Hi @outlandnish, You're welcome!
If you setup the security settings after the call to ::init()
I think you will see a better result. The ::init()
call will initialize the security settings to defaults, overwriting what you've set there.
When bonding with a phone and using a whitelist it will not work on the esp32 due to the controller not supporting resolvable private addresses, instead they have implemented that in the host. Unfortunately this is a limitation in bluedroid as well.
Thanks for the quick response - I'll give that a shot with the security settings!
And that's unfortunate re: the whitelist. I remember that being an issue with Bluedroid too. So if I wanted my ESP32 to only allow bonding at certain times (i.e. when entering a pairing mode), there's no way for me to do that, is there?
You can turn bonding on or off as needed there is just not a way to recognize a peer device with a random address via whitelist.
What you can do if you want is to filter out connections from non bonded peers. For instance when you have bonded with your phone, the next time it connects it will be recognized as bonded and you can check the flag. If a non-bonded peer connects you could just disconnect them in the callback. Not a great solution but it should work.
Conceptually, that solution works well, thanks!
However, when I tested it, I found that the bonded flag would only get set after onAuthenticationComplete
, not onConnect
. The odd thing was that I couldn't stop other devices from bonding either. I noticed the code for onSecurityRequest
doesn't fire - is that because there's no longer a corresponding event from NimBLE that triggers it?
Yes onAuthenticationComplete
will tell you when you've bonded, may want to set a flag here or something, that you can test in onConnect()
.
You can check for the bonded flag in the connection description parameter from the overloaded onConnect()
callback, seen here.
NimBLE does not provide a callback for the onSecurityRequest
, only when using passkey or numeric compare will it ask the app to check permission. So the best way to deal with that is to use one of those pairing methods.
Yeah, so I use the overloaded onConnect callback and the bonded flag isn't set, even for devices that have previously been bonded.
Here's the relevant logs from my device:
Thanks for the heads up on using the passkey / numeric compare though. I'll give that a shot!
I'll play with this tonight and see if I can find a solution. Very strange that the bonded flag isn't set when reconnecting, might be a bug in NimBLE
Does the bonding process happen automatically within NimBLE or is it initiated by ble_gap_security_initiate
? If it's the latter, I could potentially call that in onConnect conditionally
It can be triggered multiple ways but the whole process is handled in the stack.
You certainly can call that in the callback although it may cause another issue if already bonded to the peer.
Are you using the NimBLE stack included in IDF or my component repo?
I'm using the NimBLE stack in included in IDF 4.1. Are there any differences in your component? I didn't see any, but I could've missed something.
I'll have a look, there has been a lot of work on this area of the NimBLE stack in recent weeks to address such things. If you feel like giving it a go you could try using the master branch of esp-nimble in 4.1, It should cause no issue.
I just got home so I will be testing for a solution to this shortly.
Sounds good, I'll see what I can find. And thanks for taking a look into it!
Just did a quick test and I can confirm that NimBLE is not reporting the bonded flag correctly in the onConnect
callback.
Unfortunately the proper fix for this will likely require an upstream PR. I'll see what I can come up with in the mean time.
Ahh that's unfortunate :/
I guess a temporary workaround would be to store the peer_id_addr
in NVS on successful bonding and then check against it onConnect. I imagine that's all NimBLE is doing behind the scenes anyway
Also, I found this interesting PR to enable accepting / rejecting JUST_WORKS
pairing requests: https://github.com/apache/mynewt-nimble/pull/540
Looks like it needs a bit of work to conform with the way the NimBLE peeps want it but it sounds like a good starting point.
Yes that PR is being worked on by espressif and will hopefully get merged.
In the mean time, for your purproses I believe there is a possible work around, it's not pretty but it might work for you.
In the onConnect()
callback you would need to do something like this:
ble_addr_t peer_id_addrs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)];
int num_peers;
int i;
ble_store_util_bonded_peers(&peer_id_addrs[0], &num_peers, MYNEWT_VAL(BLE_STORE_MAX_BONDS));
for (i = 0; i < num_peers; i++) {
if (ble_addr_cmp(desc->peer_id_addr, &peer_id_addrs[i]) != 0) {
break;
}
}
if (i >= num_peers) {
pServer->disconnect(desc->conn_handle);
}
Not tested code but it should be close to what you need, I'll spend some more time on this and try to come up with a better solution on the weekend.
That solution worked perfectly, thank you!
Great, I'll see what can be done to simplify this going forward.
Hi, I have the same issue as outlandnish, I guess the onConnect() solution you proposed wont work with RPA address? Is there a way to use the IRK to know if the device is bonded?
Hi @fhelie, it should work with an RPA as the address gets resolved before the callback is called.
If that is not the case on your tests let me know.
Thank you, I will test it soon and give back the result!
Hi @h2zero , I had the same issue and tired the proposed solution:
void BluetoothController::setupSecurity(const uint32_t pin)
{
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityPasskey(pin);
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
NimBLEDevice::setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC);
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC);
NimBLEDevice::setMTU(512);
}
The device bonds. After reconnecting however the bonding check failes because the addrress->type is not matching:
ServerCallbacks::isDeviceBonded() - num_peers: 1
ServerCallbacks::isDeviceBonded() - ble_addr_cmp(21921685547864, 21921685547864): type of device address: 0 type of nvm address: 1073555579
ServerCallbacks::isDeviceBonded() - (i < num_peers): false
ServerCallbacks::onConnect() - Connection rejected because device is not bonded.
Do you think I can ignore the address type? Do you have any idea why the saved bonded device would store such a wierd number a address type?
Best Regards
Yes you can ignore the address type. This is due to how espressif deals with bonded peers using host based instead of controller based privacy.
Thanks, I have also found out that NimbleDevice::isBonded(...) also works. So that above solution is not needed. I worked around the rest of the issues as described in this thread. It is not optimal, but it works just fine. Thanks.