涉及 配对与绑定 相关知识。
这里准确的说法应该是叫配对码,而不是密码。输入这个这个配对码是配对过程中可选的一部分。
我们需要的输入“密码”这个功能,其实是配对过程中的一部分。而配对过程又是需要首先交换配对信息,然后协议栈会根据交换的信息才决定是否有输入密码这一过程。
无论是静态密码还是动态密码(配对码,后面统一叫密码)都是可以看做是配对过程中的一种认证方式即”我是我”,因为这样可以一定程度上避免他人连接你的设备,因为他们看不到设备上显示的配对码,而你自己去可以看到。
配对码的输入是配对过程中生成TK的一种方式即Passkey Entry。另外还有Just Works和Out of Band 两种方式。TK的生成是为了后续再生成STK用来加密链路然后分发LTK,IRK,CSRK。(如果配对信息交换是没是指明绑定也就不需要后续的这里密钥的分发了)。
静态密码设置 :
这里只是演示静态密码,不需要绑定
认证方式就是 :passkey entery。
那么我们要做的有如下几步:
1: 首先设置要输入的静态密码
2: 设置配对时会交换的信息:根据《配对与绑定》的介绍如果我们需要手机输入密码,那么配对时就要设置只具有显示器(这样就会是一端显示,一端输入,虽然我们真的没显示器,但是设置的是静态密码所以也是可以的),设置需要MITM攻击保护。
3:触发配对。使用的是配对触发方式:3 。所以我们将具有notify性质的RX特征值的cccd(客户端配置描述符)设置为需要认证和加密的安全链路。因为手机端使能notify是需要cccd的,那么当手机连接板子后 点击rx特征值的notify按钮后主机会发一个写命令 写板子上rx特征值的cccd,因为初试链路是不完全的,这时手机就会返回写出错,然后启动配对过程。
//设置静态密码步骤 细分详解:
//步骤一
//定义静态密码,配对密码只能是 6-digit ASII string
//步骤二
//是设置静态密码
//步骤三
//设置配对时要交换的信息
//步骤四
//触发配对信息发给对端设备
//步骤五
//板子回复配对信息
//步骤一
//定义静态密码,配对密码只能是 6-digit ASII string
#define STATIC_PASSKEY "123456" /**< Static pin. */
//改结构体 可以设置静态密码
static ble_opt_t m_static_pin_option;
//定义这两个参数后,我们需要设置以下静态密码,在gap_params_init()函数的最后。
//步骤二 ://以下是设置静态密码操作
void gap_params_init(void)
{
uint32_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *) DEVICE_NAME,
strlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);
//设置外围设备连接上的首选参数。
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;//最小连接间隔
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;//最大连接间隔
gap_conn_params.slave_latency = SLAVE_LATENCY;//从机潜伏
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;//连接超时
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
//以下是设置静态密码操作
uint8_t passkey[] = STATIC_PASSKEY;
m_static_pin_option.gap_opt.passkey.p_passkey = passkey;
//改系统调用执行密码的设置操作
err_code=sd_ble_opt_set(BLE_GAP_OPT_PASSKEY,&m_static_pin_option);
APP_ERROR_CHECK(err_code);
//到这里设置静态密码的操作就完成了,
//然后是设置配对时要交换的信息。
}
//步骤三 ://设置配对时要交换的信息:
//这里只是演示静态密码,不需要绑定
#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;
//初始化 配对启动后交换的信息.
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] */
}
//步骤五 ://板子回复配对信息.
//当手机发来配对请求时,这对板子来说是一个事件,即配对事件。
//最终由dispatch派发函数交给各个服务或模块的事件处理函数。
//那么收到这个配对请求后回复步骤二中设置的配对信息就可以了。
//和判断配对是否成功,如果不成功断开连接,从而阻止他人任意连接。
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_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;
}
程序运行,打开lightblue连接板子,点击listen for notification 就会弹出蓝牙配对请求的对话框。正确输入静态密码即可连上。错误或超时输入需要退出重新操作配对。