涉及 配对与绑定 相关知识。
这里准确的说法应该是叫配对码,而不是密码。输入这个这个配对码是配对过程中可选的一部分。
我们需要的输入“密码”这个功能,其实是配对过程中的一部分。而配对过程又是需要首先交换配对信息,然后协议栈会根据交换的信息才决定是否有输入密码这一过程。
无论是静态密码还是动态密码(配对码,后面统一叫密码)都是可以看做是配对过程中的一种认证方式即”我是我”,因为这样可以一定程度上避免他人连接你的设备,因为他们看不到设备上显示的配对码,而你自己去可以看到。
配对码的输入是配对过程中生成TK的一种方式即Passkey Entry。另外还有Just Works和Out of Band 两种方式。TK的生成是为了后续再生成STK用来加密链路然后分发LTK,IRK,CSRK。(如果配对信息交换是没是指明绑定也就不需要后续的这里密钥的分发了)。
动态密码设置 :
这里只是演示动态密码,不需要绑定
认证方式就是 :passkey entery。
因为是动态密码,板子上没有显示屏,所以通过串口将动态密码打印出来,然后手机正确输入才能配对成功。//设置动态密码步骤 细分详解:
//步骤一
//设置配对时要交换的信息:
//这里只是演示静态密码,不需要绑定
#define SEC_PARAM_BOND 0
//因为要输入密码,就是一种MITM攻击保护,所以这里设置MITM
#define SEC_PARAM_MITM 1
//这里设置只有现实屏(其实没有,但是我们用的是事先知道的静态密码,不需要显示)
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY
//不使用带外数据
#define SEC_PARAM_OOB 0
//链路加密密钥的长度
#define SEC_PARAM_MIN_KEY_SIZE 7
#define SEC_PARAM_MAX_KEY_SIZE 16
//定义全局变量, 设置配对启动后交换的信息.
ble_gap_sec_params_t m_sec_params;
//初始化 配对启动后交换的信息.该函数放在 int main(void) 里的 conn_params_init();后.
void sec_params_init(void)
{
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}
//步骤二
//触发配对信息发给对端设备,修改在 ble_nus.c
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_OPEN(&cccd_md.write_perm);
//步骤二
//触发配对信息发给对端设备
//将上面一行改为下面一行,这样手机使能板子的rx特征值得notify功能时,
//就会因为因为没有写权限而触发配对,手机就会发来配对请求,
//然后板子回复配对信息. 怎么回复?看步骤三.
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&cccd_md.write_perm);
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
memset(&char_md, 0, sizeof(char_md));
char_md.char_props.notify = 1;
char_md.p_char_user_desc = NULL;
char_md.p_char_pf = NULL;
char_md.p_user_desc_md = NULL;
char_md.p_cccd_md = &cccd_md;
char_md.p_sccd_md = NULL;
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC;
memset(&attr_md, 0, sizeof(attr_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
attr_md.vloc = BLE_GATTS_VLOC_STACK;
attr_md.rd_auth = 0;
attr_md.wr_auth = 0;
attr_md.vlen = 1;
memset(&attr_char_value, 0, sizeof(attr_char_value));
attr_char_value.p_uuid = &ble_uuid;
attr_char_value.p_attr_md = &attr_md;
attr_char_value.init_len = sizeof(uint8_t);
attr_char_value.init_offs = 0;
attr_char_value.max_len = BLE_NUS_MAX_RX_CHAR_LEN;
return sd_ble_gatts_characteristic_add(p_nus->service_handle,
&char_md,
&attr_char_value,
&p_nus->rx_handles);
/**@snippet [Adding proprietary characteristic to S110 SoftDevice] */
}
//步骤三
信息交换完后,设备低层的协议栈就会自动产生6为随机的密码(配对码),并将配对码通过事件BLE_GAP_EVT_PASSKEY_DISPLAY 上抛给app,然后就可以在app中将密码通过串口将密码打印出来了。
和判断配对是否成功,如果不成功断开连接,从而阻止他人任意连接。
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
//err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
sec_params_init();
err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &m_sec_params, NULL);
APP_ERROR_CHECK(err_code);
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_GAP_EVT_AUTH_STATUS:
if(p_ble_evt->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
{
printf("pair success\r\n");
}
else
{
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
}
break;
}