Skip to content

extmod/modbluetooth: Add pairing/bonding support. #6651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from

Conversation

jimmo
Copy link
Member

@jimmo jimmo commented Nov 26, 2020

WIP, looking for feedback on the approach.

Much of this work was done by @andrewleech in #6289, #6312, and #6447. This PR changes:

  • Moved the bonding secret store into Python (via _IRQ_GET_SECRET and _IRQ_SET_SECRET events).
  • Adds characteristic flags to enable auto-pairing (e.g. READ_ENCRYPTED)
  • Adds passkey authentication.
  • gap_pair just takes conn_handle, and pairing/bonding parameters are set via config().
  • Documentation

Currently only supported on NimBLE on STM32 and Unix, as it requires synchronous (non-ringbuffer) events. Enabled via MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING macro (which is now enabled by default for NimBLE STM32/Unix). Some BlueKitchen support is implemented, but will need to wait until it's migrated to synchronous events. The secret store is designed to be compatible with BlueKitchen's TLV store API.

Tested against Android and BlueZ.

This PR adds:

  • Better error handling for oversize characteristic reads.
  • An _IRQ_CONNECTION_UPDATE event. Not specifically related to pairing and bonding, but useful information about the connection state.
  • An _IRQ_ENCRYPTION_UPDATE event to get notified about pairing/bonding state change.
  • Widens characteristic flags to 16-bit and allows for setting encryption/authentication/authorisation requirements.
  • Allows the user to fail the read request IRQ with a custom error code.
  • Configuration of pairing/bonding parameters (le_secure, bond, mitm, io)
  • gap_pair() method to initiate pairing.
  • _IRQ_GET_SECRET and IRQ_SET_SECRET events for persistent key management.
  • Simplify the handing of numeric arguments in invoke_irq_handler (used in synchronous events).
  • An _IRQ_PASSKEY_ACTION event to be notified of an authentication request.
  • gap_passkey() method to respond to an authentication request.
  • ble_bonding_peripheral.py example, showing implementation of key persistence and authentication. (WIP)

Four major outstanding pieces:

  • Write multi-test.
  • Support address resolving in central mode (perhaps via a _IRQ_RESOLVED_SCAN_RESULT event)
  • Improve handing of RPA mode. NimBLE doesn't seem to support using a non-fixed IRK (see Allow setting non-default IRK apache/mynewt-nimble#887)
  • PYBD doesn't work with RPA mode. (Works fine on Unix+Zephyr and WB55)

jimmo and others added 7 commits November 25, 2020 17:40
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This allows the application to be notified of changes to
 - Connection interval
 - Connection latency
 - Supervision timeout

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This widens the characteristic/descriptor flags to 16-bit, to allow setting
encryption/authentication requirements.

Sets the required flags for NimBLE and btstack implementations.

Deprecate the BLE.FLAG_* constants in favour of copy & paste Python constants
(like the IRQs).

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Instead of returning None/bool from the IRQ, return None/int.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
@dmazzella
Copy link
Contributor

+1 for bonding secret store into Python

@andrewleech
Copy link
Contributor

andrewleech commented Nov 26, 2020

Minor note: in top post MICROPY_BLUETOOTH_ENABLE_PAIRING_BONDING should be MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING

For anyone else looking at this though its a moot point, as this is enabled automatically for ports where its supported.

jimmo and others added 6 commits November 27, 2020 11:29
Enable for STM32/Unix NimBLE only (requires synchronous events and full bindings).

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This allows the application to be notified if any of
{encrypted,authenticated,bonded} states change as well as the encryption key
size.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Sets security and MITM-protection requirements.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
@jimmo jimmo force-pushed the modbluetooth-pairing branch from 34a8386 to f3f82e6 Compare November 27, 2020 00:30
@jimmo
Copy link
Member Author

jimmo commented Nov 27, 2020

Minor note: in top post MICROPY_BLUETOOTH_ENABLE_PAIRING_BONDING should be MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING

Fixed. (And the PASSKEY defines)

@andrewleech
Copy link
Contributor

My testing of this PR today has gone very well, though I think the IRK issue can cause some strange behaviors if there are multiple devices running.

I've been testing primarily android central connecting to micropython peripheral on both unix (zephyr hci radio) and stm32 (mynewt/nimble hci radio).

Pairing and bonding works from both explicit pairing request and/or reading a characteristic with the _FLAG_READ_ENCRYPTED attribute.

This is with both the ble_bonding_peripheral.py example which uses a pin to pair (recognized as authenticated) and my custom application which has no pin (recognized as not authenticated). The link is encrypted and bonded in both cases.

The bond is maintained reliably across power cycles of either end of the link.

If the ble is disabled on micropython: ble.active(False) then enabled again ble.active(True) the bond doesn't seem to be persisted correctly, however I expect this is a pretty uncommon use case at this stage.

This adds `_IRQ_GET_SECRET` and `_IRQ_SET_SECRET` to allow
the stack to request the Python code retrive/store/delete
secret key data.

The actual keys and values are opaque to Python and stack-specific.

Only NimBLE is implemented (pending moving btstack to sync events).

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Rather than dealing with the different int types, just pass them all as a single array of mp_int_t with n_unsigned (before addr) and n_signed (after addr).

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This provides a workaround for apache/mynewt-nimble#887.

Without this, all devices would share a fixed default IRK.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
@jimmo jimmo force-pushed the modbluetooth-pairing branch from f3f82e6 to c01effb Compare November 27, 2020 06:10
@jimmo
Copy link
Member Author

jimmo commented Nov 27, 2020

I've added a commit that adds a workaround for the fixed IRK.

@andrewleech
Copy link
Contributor

I've continued testing, still with micropython running as peripheral but a different Android (samsung A20, Android 10) phone running central (both nrf-connect and custom app).

With this phone, the pairing is not persisting over the micropython reboot when running on my custom stm32 application.
I can connect/disconnect/connect the phone while the stm32 is running just fine (pairing works) but after rebooting stm32 and re-connecting the pairing does not re-establish.

This exact same procedure with my previous Android phone (Xiaomi MI6 running Lineage OS Android 10) works correctly - pairing/bonding is maintained over stm32 reboots.

I'll continue to investigate the difference between the two.

@dpgeorge
Copy link
Member

I can connect/disconnect/connect the phone while the stm32 is running just fine (pairing works) but after rebooting stm32 and re-connecting the pairing does not re-establish.

I'm seeing similar issues with Andriod-PYBD bonding. Can you give a trace of the set/get secrets IRQ calls, the data (key and value) that each event is given? (You may want to email me / obfuscate your public BD ADDR)

@andrewleech
Copy link
Contributor

Yeah the secret it's trying to read upon connection is the same each time connect/disconnect/re-connect without rebooting the mpy, but then it's different after reboot mpy.

I suspect the peer IRK isn't working / not getting re-loaded into the radio correctly. I don't think the problem is from the latest "own IRK" commit though, I was seeing the same behavior before that too. I'd say my xiaomi is likely using a static public address so resolves consistently, whereas the samsung is probably using a randomised addr so relies on the IRK.

Tomorrow I'll trace it all through before and after the reboot to confirm the behavior and hopefully find the fix.

@dpgeorge
Copy link
Member

I'd say my xiaomi is likely using a static public address so resolves consistently, whereas the samsung is probably using a randomised addr so relies on the IRK.

Yes that could be it, sounds similar to what I'm seeing.

@andrewleech
Copy link
Contributor

Yep just confirmed; when the phones connect to ble_bonding_peripheral.py the working Xiaomi triggers a ble_store_read_our_sec() with key_sec->peer_addr.type == BLE_ADDR_PUBLIC while the samsung has the same function with key_sec->peer_addr.type == BLE_ADDR_RANDOM

Comment on lines +130 to +131
if i == index:
return value
Copy link
Contributor

@andrewleech andrewleech Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if i == index:
return value
if i == index:
return value
i += 1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimmo This fixes the "samsung not working over restart" issue I was having, turns out it's got nothing to do with the public/random address thing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I saw this as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now fixed in #6662

@dpgeorge
Copy link
Member

dpgeorge commented Dec 1, 2020

@andrewleech does your BLE controller support LL privacy?

@andrewleech
Copy link
Contributor

andrewleech commented Dec 1, 2020

@andrewleech does your BLE controller support LL privacy?

I think so, I'm using mynewt/nimble hci controller build on stm32, zephyr hci on unix. Would need to double check the build configs to be sure though.

@dpgeorge dpgeorge added the extmod Relates to extmod/ directory in source label Dec 1, 2020
@dpgeorge
Copy link
Member

dpgeorge commented Dec 1, 2020

Write multi-test.

Two basic multitests for pairing and bonding have been added in #6662.

PYBD doesn't work with RPA mode.

The BT controller on PYBD doesn't support LL privacy in the controller so RPA would need to be added on the host side.

@dpgeorge
Copy link
Member

dpgeorge commented Dec 2, 2020

I've tested this and am happy with how it works.

Merged in f2a9a0a through d79b9c6

Outstanding items can be addressed in follow-up work.

Thank you @jimmo and @andrewleech !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extmod Relates to extmod/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy