本讲教程也是在 sdk中的的uart例子 的基础上修改实现动态密码(其实是配对码)的功能。Uart例子在以下目录中
XXX\Keil_v5\ARM\Pack\NordicSemiconductor\nRF_Examples\9.0.0\ble_peripheral\ble_app_uart
关于配对的一些理论知识在静态密码教程中有介绍。这里就不再赘述。
因为是动态密码,板子上没有显示屏,所以通过串口将动态密码打印出来,然后手机正确输入才能配对成功。
无论是静态密码还是动态密码(配对码,后面统一叫密码)都是可以看做是配对过程中的一种认证方式即”我是我”,因为这样可以一定程度上避免他人连接你的设备,因为他们看不到设备上显示的配对码,而你自己去可以看到。
配对码的输入是配对过程中生成TK的一种方式即Passkey Entry。另外还有Just Works和Out of Band 两种方式。TK的生成是为了后续再生成STK用来加密链路然后分发LTK,IRK,CSRK。(如果配对信息交换是没是指明绑定也就不需要后续的这里密钥的分发了)
既然有三种方式可选,那么协议是如何决定选择哪种的呢。这个就和配对时交换的配对信息有关了。我们使用的是Passkey Entry 即输入配对码的方式,那么只要将 配对信息中的 OOB设置为0 ,MITM设置为 1, bond设置为0(教程只是演示配对中的配对码的输入不需要后面的密钥分发过程),然后将自己的 I/O能力设置为 DisplayOnly 就行了。
:至于各种可能的组合会导致选择什么样的方式在规范的安全管理部分有详细的对照表可参考。
根据上面的描述,我们首先就是要配置配对时要交换的配对信息
#define IO_CAPS BLE_GAP_IO_CAPS_DISPLAY_ONLY //只有显示装置
#define BOND 0 //不绑定
#define OOB 0
#define MITM 1
然后就是在程序中回复配对信息。
实现如下函数来回复配对请求信息并将自己的配对信息传给对方。
void resp_pair_request(){
ble_gap_sec_params_t sec_params;
uint32_t err_code;
memset(&sec_params,0,sizeof(ble_gap_sec_params_t));
sec_params.bond = BOND;
sec_params.io_caps = IO_CAPS;
sec_params.max_key_size = 16;
sec_params.min_key_size = 7;
sec_params.oob = BOND;
sec_params.mitm = MITM;
err_code=sd_ble_gap_sec_params_reply(m_conn_handle,BLE_GAP_SEC_STATUS_SUCCESS,&sec_params,NULL);
APP_ERROR_CHECK(err_code);
}
将该函数添加到BLE_GAP_EVT_SEC_PARAMS_REQUEST事件处理部分中去。这样在主机请求配对时,从机就可以将自己的配对信息发送给主机了。
信息交换完后,设备低层的协议栈就会自动产生6为随机的密码(配对码),并将配对码通过事件BLE_GAP_EVT_PASSKEY_DISPLAY 上抛给app,然后就可以在app中将密码通过串口将密码打印出来了。
上面说的两个事件的处理我们都在 on_ble_evt 事件处理函数中处理。红色代码部分为处理函数。
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
uint32_t err_code;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
APP_ERROR_CHECK(err_code);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
break;
case BLE_GAP_EVT_DISCONNECTED:
err_code = bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
printf("receive pair request\n");
resp_pair_request();
break;
case BLE_GAP_EVT_PASSKEY_DISPLAY:
printf("show passkey: ")
for ( int i = 0; i < 6; i++){ printf("%c",p_ble_evt->evt.gap_evt.params. \
passkey_display.passkey[i]);
}
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, NULL, 0, 0);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
然后添加触发配对代码。 这里实现的很简单。就是将Rx特征值的cccd的写设置成需要链路加密和MITM。这样在未配对的情况下手机使能Notify时设备就会回复权限不足,然后手机就会发一个配对请求过来从而实现配对和链路的加密。
修改ble_nus.c文件中的rx_char_add函数。
static uint32_t rx_char_add(ble_nus_t * p_nus,
const ble_nus_init_t * p_nus_init)
{
/**@snippet [Adding proprietary characteristic to S110 SoftDevice] */
ble_gatts_char_md_t char_md;
ble_gatts_attr_md_t cccd_md;
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_t attr_md;
memset(&cccd_md, 0, sizeof(cccd_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
//设置权限
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&cccd_md.write_perm);
// BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
…………
…………
}
之后烧录程序后,在手机执行使能Notify时就会触发配对,串口会打印出随机密码,手机正确输入后便是成功配对。