Hi all,
I use nRF52840 custom device in peripheral role and I am testing the pairing and bonding process with passkey, first with a tablet and then with PC in client role.
when I connect from the client/central device, a dynamic passkey is sent to the peripheral, I read it in the debugger window and finally I type it in the tablet or PC.
When I pair and bond my tablet through nRF Connect app to the peripheral the procedure works fine and the central (tablet) is paired and bonded without issues.
When I try the same thing from my PC through Bluetooth Devices > Add a bluetooth device, I get error BLE_GATTS_EVT_SYS_ATTR_MISSING after it has been paired and bonded to the peripheral. I have tested this with two PCs and it happens the same thing.
In my peer_manager_init() I set MITM with BLE_GAP_IO_CAPS_DISPLAY_ONLY
Any ideas?
Thank you for your time
#define LESC_DEBUG_MODE 0 /**< Set to 1 to use LESC debug keys, allows you to use a sniffer to inspect traffic. */ #define SEC_PARAM_BOND 1 #define SEC_PARAM_MITM 1 #define SEC_PARAM_LESC 0 #define SEC_PARAM_KEYPRESS 0 #define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY //BLE_GAP_IO_CAPS_NONE #define SEC_PARAM_OOB 0 #define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */ #define SEC_PARAM_MAX_KEY_SIZE 16
this is the peer_manager_init() definition
static void peer_manager_init(void) // Initializes the Peer Manager
{
ret_code_t err_code;
// Initialize the Peer Manager
err_code = pm_init();
APP_ERROR_CHECK(err_code);
// Register the event handler
err_code = pm_register(pm_evt_handler);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("Clearing all data from peers");
// Delete all existing bonds
err_code = pm_peers_delete();
APP_ERROR_CHECK(err_code);
// Initialize security parameters
memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
// Set security parameters to be used for all security procedures
sec_param.bond = SEC_PARAM_BOND;
sec_param.mitm = SEC_PARAM_MITM;
sec_param.lesc = SEC_PARAM_LESC;
sec_param.keypress = SEC_PARAM_KEYPRESS;
sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES;
sec_param.oob = SEC_PARAM_OOB;
sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
sec_param.kdist_own.enc = 1;
sec_param.kdist_own.id = 1;
sec_param.kdist_peer.enc = 1;
sec_param.kdist_peer.id = 1;
// Set the security parameters
err_code = pm_sec_params_set(&sec_param);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("Security parameters set.");
NRF_LOG_INFO("sec_param: bond=%d, mitm=%d, lesc=%d, io_caps=%d",
sec_param.bond, sec_param.mitm, sec_param.lesc, sec_param.io_caps);
}
This is the pm_handler()
static void pm_evt_handler(pm_evt_t const * p_evt) //TODO THIS IS FOR SECURITY
{
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
NRF_LOG_INFO("PM_EVENT_HANDLER: %d", p_evt->evt_id);
switch (p_evt->evt_id)
{
case PM_EVT_PEERS_DELETE_SUCCEEDED:
NRF_LOG_INFO("PM_EVT_PEERS_DELETE_SUCCEEDED");
//advertising_start(false);
break;
case PM_EVT_PEERS_DELETE_FAILED:
NRF_LOG_INFO("PM_EVT_PEERS_DELETE_FAILED");
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
// Provide security configuration
NRF_LOG_INFO("PM_EVT_CONN_SEC_CONFIG_REQ");
pm_conn_sec_config_t conn_sec_config = { .allow_repairing = true };
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
}
break;
case PM_EVT_CONN_SEC_PARAMS_REQ:
{
NRF_LOG_INFO("PM_EVT_CONN_SEC_PARAMS_REQ");
NRF_LOG_INFO("Security parameters request received.");
NRF_LOG_INFO("sec_param: bond=%d, mitm=%d, lesc=%d, io_caps=%d",
sec_param.bond, sec_param.mitm, sec_param.lesc, sec_param.io_caps);
// Respond to the security parameters request
err_code = pm_conn_sec_params_reply(p_evt->conn_handle, &sec_param, p_evt->params.conn_sec_params_req.p_context);
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("REPLIED WITH SEC_PARAMS %d", p_evt->params.conn_sec_params_req.p_context);
}
break;
case PM_EVT_LOCAL_DB_CACHE_APPLIED:
NRF_LOG_INFO("PM_EVT_LOCAL_DB_CACHE_APPLIED event received.");
break;
default:
break;
}
}
And this is the corresponding security code parts ble_evt_handler()
case BLE_GAP_EVT_CONNECTED:
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, p_ble_evt->evt.gap_evt.conn_handle);
APP_ERROR_CHECK(err_code);
// Start the security procedure for the new connection
err_code = pm_conn_secure(p_ble_evt->evt.gap_evt.conn_handle, false);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("pm_conn_secure failed with error: %d", err_code);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_AUTH_KEY_REQUEST: // THIS IS USED ONLY when: MITM = 1 and I/O Capabilities = Display
NRF_LOG_DEBUG("BLE_GAP_EVT_AUTH_KEY_REQUEST");
err_code = sd_ble_gap_auth_key_reply(p_ble_evt->evt.gap_evt.conn_handle, BLE_GAP_AUTH_KEY_TYPE_NONE, passkey);
if (err_code != NRF_SUCCESS) {NRF_LOG_ERROR("Failed to reply with passkey. Error code: 0x%04x", err_code);}
else {NRF_LOG_INFO("Passkey reply sent successfully.");}
break;
case BLE_GAP_EVT_PASSKEY_DISPLAY: // THIS IS USED ONLY when: MITM = 1 and I/O Capabilities = Display
memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, 6);
NRF_LOG_INFO("PASSKEY DISPLAY: %s", passkey);
// Display the passkey to the user (e.g., on a screen or log)
break;
case BLE_GAP_EVT_AUTH_STATUS:
NRF_LOG_INFO("Authentication status: %d", p_ble_evt->evt.gap_evt.params.auth_status.auth_status);
if (p_ble_evt->evt.gap_evt.params.auth_status.auth_status != BLE_GAP_SEC_STATUS_SUCCESS)
{
NRF_LOG_ERROR("Bonding failed with status: 0x%x", p_ble_evt->evt.gap_evt.params.auth_status.auth_status);
}
else
{
NRF_LOG_INFO("Bonding succeeded.");
}
break;
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
// No system attributes have been stored.
err_code = sd_ble_gatts_sys_attr_set(*m_conn_handle_gen, NULL, 0, 0);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("Failed to set system attributes, error code: %d", err_code);
}
else
{
NRF_LOG_INFO("System attributes set successfully.");
}
APP_ERROR_CHECK(err_code);
break;

