Pairing/Bonding errors with a nRF MCU to a Windows laptop

I've written a GUI in python on a windows laptop which communicates fine with an nRF MCU when using open broadcast and no security. I'm using python's bleak library to connect and read/write gatt characteristics. I now want to implement security. I have copied much of the code from lesson 2 in the sdk fundamentals. I've modified it to use a static passkey for security level 4 connections. It still uses the filter accept list and pairing button. 

It works fine on the nRF connect app. I connect, then write to one of the characteristics, which then prompts an input for the static passkey. However, on windows, this is not the case. I'm doing pairing outside of the python program as I don't believe bleak supports that functionality. I intend to use the GUI after pairing. When connecting, it says "connecting..." on windows, the 'on_connected' callback executes on the MCU, then it errors out on both the laptop and mcu after about 15 seconds. No bonding occurs and no messages are sent/received.

Errors seen:

[00:09:16.687,164] <inf> BLE_Main: Security failed: 58:A0:23:A6:35:0A (public) level 1 err 9

[00:09:16.687,377] <inf> BLE_Main: Disconnected (reason 8)

My Bluetooth drivers:

Error 9 on security fail is BT_SECURITY_ERR_UNSPECIFIED. Is the issue that the windows laptop has no means of entering the static passkey? Or could it be that LE Secure Connections is not natively supported?

Help is much appreciated.

Parents
  • Hi Austin, 
    As you already found error 9 doesn't give much info about the error. I would suggest to use a sniffer to capture the bonding process. 
    You can find the sniffer guide in the same Bluetooth course. Please send us the sniffer trace for further investigation. 
    Could you describe how you do static passkey. As far as I understand it's not recommended to use static passkey with LE Secure Connection. It only takes 20 trials to crack it. 

  • Hi Hung Bui,

    I made a post on stack overflow and was assisted by two individuals Emil and Risto, who pointed out another post https://devzone.nordicsemi.com/f/nordic-q-a/104258/security-failed-level-1-err-9-connecting-error-with-bonded-device.

    Upon placing either CONFIG_BT_PHY_UPDATE=n or CONFIG_BT_AUTO_PHY_UPDATE=n the pairing issue is resolved. I would still like to know exactly why this occurs. Why does this only occur on PC but not my mobile phone? Where can I read more about PHY update details? 

    Also, could you help answer Emil's question?

  • Hi Austin, 

    I'm glad that you found the workaround. I agree with Emil that it could be something wrong on the BLE controller on the PC's side that couldn't handle the LL_PHY_REQ. My suggestion is to try request LL_PHY_REG at later stage after the bonding has been completed. There is a chance that the controller prohibit to change PHY when the pairing is on going. Even though as far as I know it's not prohibited by the spec. 

    Regarding static passkey, I don't agree with Emil that Passkey (not static passkey) doesn't offer any MITM protection. It does provide MITM protection if it's used correctly, in this case randomly generated on each pairing. 

    You should avoid using static passkey though. It's intended for testing only, not production. You can find the discussion here: https://github.com/zephyrproject-rtos/zephyr/issues/36005

    Regarding this

    Emil Lenngren said:
    One question though to the Nordic team is why the peripheral sends an additional feature request to the central when it moments ago already received the feature set from the central.

    I assume Emil was asking about the LL_PERIPHERAL_FEATURE_REQ. Be aware that the first LL_FEATURE_REQ is from the central the second one is from the peripheral. So they are on the opposite direction to get the feature set of each peers. 

Reply
  • Hi Austin, 

    I'm glad that you found the workaround. I agree with Emil that it could be something wrong on the BLE controller on the PC's side that couldn't handle the LL_PHY_REQ. My suggestion is to try request LL_PHY_REG at later stage after the bonding has been completed. There is a chance that the controller prohibit to change PHY when the pairing is on going. Even though as far as I know it's not prohibited by the spec. 

    Regarding static passkey, I don't agree with Emil that Passkey (not static passkey) doesn't offer any MITM protection. It does provide MITM protection if it's used correctly, in this case randomly generated on each pairing. 

    You should avoid using static passkey though. It's intended for testing only, not production. You can find the discussion here: https://github.com/zephyrproject-rtos/zephyr/issues/36005

    Regarding this

    Emil Lenngren said:
    One question though to the Nordic team is why the peripheral sends an additional feature request to the central when it moments ago already received the feature set from the central.

    I assume Emil was asking about the LL_PERIPHERAL_FEATURE_REQ. Be aware that the first LL_FEATURE_REQ is from the central the second one is from the peripheral. So they are on the opposite direction to get the feature set of each peers. 

Children
  • Some clarifications:

    1. Passkeys offer MITM protection if used correctly. What I wrote was "assuming no input/output capabilities or OOB", you get no MITM protection, because then you have to use JustWorks. If you have input/output capabilities, then you per the standard generate a passkey that is shown on the display and is inputed on the other device.

    2. In the wireshark log, you can see the central sending LL_FEATURE_REQ. This contains the feature set from the central. There is no point after receiving this packet to send LL_PERIPHERAL_FEATURE_REQ because the response from the central will be the same content as in the previous LL_FEATURE_REQ.

  • Hi Emil, 
    1. Thank for the clarification. You are right. I was misreading the part when you mentioned "assuming no input/output capabilities or OOB" sorry about that. 

    2. I assume our controller would send LL_PERIPHERAL_FEATURE_REQ automatically. I will need to check internally. But from my understanding the spec doesn't prohibit that. And by sending that you can confirm that the central has updated the feature set according to the LL_FEATURE_RSP from the peripheral send earlier.  

  • Correct. The spec allows performing the feature negotiation several times per connection, but there is no point doing so, as long as you can set aside 8 bytes of data in the connection structure to keep this information. It explicitly recommends doing this only once: "The FeatureSet information may be cached either during a connection or between connections."

    I would say "confirming that the central has updated the feature set" doesn't make much sense, since the LL_FEATURE_RSP is defined exactly what it should contain, namely that the first octet is a bitwise-and of the first octet of the local feature set and the first octet received in the corresponding request and the rest of the octets are octet 2-8 of the local feature set unmodified.

    Note that the version negotiation (LL_VERSION_IND) is however only permitted to perform once per connection.

Related