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