Hi,
I'm using an NRF51 Dongle as a central to connect to a BLE peripheral using LE Secure passkey pairing. I've got to scanning / connecting successfully, I'm just stuck on the pairing.
It gets to the point where it sends the SMP Pairing Public Key but it's all 0's, so the peripheral rejects it with a DHKey Check Failed:
I've used the heart rate collector and a few examples around to get to this point, here's my pairing function:
if self.conn_handle is None: return False kdist_own = BLEGapSecKDist(enc = 1, id = 1, sign = 0, link = 0) kdist_peer = BLEGapSecKDist(enc = 1, id = 1, sign = 0, link = 0) sec_params = BLEGapSecParams(bond = True, mitm = True, lesc = True, oob = False, keypress = False, io_caps = BLEGapIOCaps.keyboard_display, min_key_size = 7, max_key_size = 16, kdist_own = kdist_own, kdist_peer = kdist_peer) self.adapter.driver.ble_gap_authenticate(self.conn_handle, sec_params) self.adapter.evt_sync[self.conn_handle].wait(BLEEvtID.gap_evt_sec_params_request) self.adapter.driver.ble_gap_sec_params_reply(self.conn_handle, BLEGapSecStatus.success, sec_params = None) result = self.adapter.evt_sync[self.conn_handle].wait(BLEEvtID.gap_evt_auth_status) if result['auth_status'] == BLEGapSecStatus.success: self.keyset = adapter.db_conns[self.conn_handle]._keyset return True return False
I think the issue is around the keyset that's being used in the call to ble_gap_sec_params_reply but I can't figure out how to get the keys set to something non-zero.
I get an error in the debug logging after the gap_evt_auth_status comes back:
evt> auth_status conn(0) error_src(1) bonded(0) sm1_levels(<pc_ble_driver_py.lib.nrf_ble_driver_sd_api_v2.ble_gap_sec_levels_t; proxy of <Swig Object of type 'ble_gap_sec_levels_t *' at 0x000001AC59C56780> >) sm2_levels(<pc_ble_driver_py.lib.nrf_ble_driver_sd_api_v2.ble_gap_sec_levels_t; proxy of <Swig Object of type 'ble_gap_sec_levels_t *' at 0x000001AC59C564B0> >) kdist_own(enc(0) id(1) sign(0) link(0)) kdist_peer(enc(0) id(0) sign(0) link(0)) auth_status(BLEGapSecStatus.dhkey_failure)
Full code:
import sys import time import logging from pc_ble_driver_py import config config.__conn_ic_id__ = "NRF51" from queue import Queue, Empty from pc_ble_driver_py.observers import * import pc_ble_driver_py.lib.nrf_ble_driver_sd_api_v2 as driver import pc_ble_driver_py.ble_driver_types as util TARGET_DEV_NAME = "BLE_DEVICE" CFG_TAG = 1 class HRCollector(BLEDriverObserver, BLEAdapterObserver): def __init__(self, adapter): super(HRCollector, self).__init__() self.adapter = adapter self.conn_q = Queue() self.adapter.observer_register(self) self.adapter.driver.observer_register(self) self.adapter.default_mtu = 250 self.keyset = None self.conn_handle = None def open(self): self.adapter.driver.open() self.adapter.driver.ble_enable(BLEEnableParams( vs_uuid_count=5, service_changed=0, periph_conn_count=0, central_conn_count=1, central_sec_count=1)) def close(self): self.adapter.driver.close() def connect_and_discover(self): scan_duration = 60 params = BLEGapScanParams(interval_ms=200, window_ms=150, timeout_s=scan_duration) self.adapter.driver.ble_gap_scan_start(scan_params=params) try: new_conn = self.conn_q.get(timeout=scan_duration) self.adapter.service_discovery(new_conn) return new_conn except Empty: print(f"No device advertising with name {TARGET_DEV_NAME} found.") return None def on_gap_evt_connected(self, ble_driver, conn_handle, peer_addr, role, conn_params): print("New connection: {}".format(conn_handle)) self.conn_q.put(conn_handle) self.conn_handle = conn_handle def on_gap_evt_auth_key_request(self, ble_driver, conn_handle, **kwargs): passkey = util.list_to_uint8_array([1, 1, 1, 1, 1, 1]) print("passkey requested") driver.sd_ble_gap_auth_key_reply( ble_driver.rpc_adapter, conn_handle, kwargs['key_type'], passkey.cast(), ) def on_gap_evt_disconnected(self, ble_driver, conn_handle, reason): print("Disconnected: {} {}".format(conn_handle, reason)) self.conn_handle = None def on_gap_evt_adv_report(self, ble_driver, conn_handle, peer_addr, rssi, adv_type, adv_data): if BLEAdvData.Types.short_local_name in adv_data.records: dev_name_list = adv_data.records[BLEAdvData.Types.short_local_name] dev_name = "".join(chr(e) for e in dev_name_list) address_string = "".join("{0:02X}".format(b) for b in peer_addr.addr) print("Received advertisment report, address: 0x{}, device_name: {}".format(address_string, dev_name)) if dev_name == TARGET_DEV_NAME: self.adapter.connect(peer_addr, tag=CFG_TAG) def authenticate(self): if self.conn_handle is None: return False kdist_own = BLEGapSecKDist(enc = 1, id = 1, sign = 0, link = 0) kdist_peer = BLEGapSecKDist(enc = 1, id = 1, sign = 0, link = 0) sec_params = BLEGapSecParams(bond = True, mitm = True, lesc = True, oob = False, keypress = False, io_caps = BLEGapIOCaps.keyboard_display, min_key_size = 7, max_key_size = 16, kdist_own = kdist_own, kdist_peer = kdist_peer) self.adapter.driver.ble_gap_authenticate(self.conn_handle, sec_params) self.adapter.evt_sync[self.conn_handle].wait(BLEEvtID.gap_evt_sec_params_request) self.adapter.driver.ble_gap_sec_params_reply(self.conn_handle, BLEGapSecStatus.success, sec_params = None) result = self.adapter.evt_sync[self.conn_handle].wait(BLEEvtID.gap_evt_auth_status) if result['auth_status'] == BLEGapSecStatus.success: self.keyset = adapter.db_conns[self.conn_handle]._keyset return True return False def init(conn_ic_id): global config, \ BLEDriver, \ BLEAdvData, \ BLEEvtID, \ BLEAdapter, \ BLEEnableParams, \ BLEGapTimeoutSrc, \ BLEGapIOCaps, \ BLEUUID, \ BLEUUIDBase, \ BLEConfigCommon, \ BLEConfig, \ BLEConfigConnGatt, \ BLEGapScanParams, \ BLEGapSecKDist, \ BLEGapSecParams, \ BLEGapSecStatus, \ BLEGapSecKeyset from pc_ble_driver_py.ble_driver import ( BLEDriver, BLEAdvData, BLEEvtID, BLEEnableParams, BLEGapTimeoutSrc, BLEGapIOCaps, BLEUUID, BLEUUIDBase, BLEConfigCommon, BLEConfig, BLEConfigConnGatt, BLEGapScanParams, BLEGapSecKDist, BLEGapSecParams, BLEGapSecStatus, BLEGapSecKeyset, util, driver ) from pc_ble_driver_py.ble_adapter import BLEAdapter global nrf_sd_ble_api_ver nrf_sd_ble_api_ver = config.sd_api_ver_get() def main(selected_serial_port): print("Serial port used: {}".format(selected_serial_port)) driver = BLEDriver(serial_port=selected_serial_port, auto_flash=False, baud_rate=1000000, log_severity_level="debug") adapter = BLEAdapter(driver) collector = HRCollector(adapter) collector.open() conn = collector.connect_and_discover() collector.authenticate() if conn is not None: time.sleep(10) collector.close() def item_choose(item_list): for i, it in enumerate(item_list): print("\t{} : {}".format(i, it)) print(" ") while True: try: choice = int(input("Enter your choice: ")) if (choice >= 0) and (choice < len(item_list)): break except Exception: pass print("\tTry again...") return choice if __name__ == "__main__": logging.basicConfig(level="DEBUG", format="%(asctime)s [%(thread)d/%(threadName)s] %(message)s") serial_port = None init("NRF51") if len(sys.argv) == 3: serial_port = sys.argv[2] else: descs = BLEDriver.enum_serial_ports() choices = ["{}: {}".format(d.port, d.serial_number) for d in descs] choice = item_choose(choices) serial_port = descs[choice].port main(serial_port) quit()
Many thanks