nrf52832 学习笔记配对和绑定
Posted 不咸不要钱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nrf52832 学习笔记配对和绑定相关的知识,希望对你有一定的参考价值。
nrf52832 学习笔记(六)配对和绑定
配对绑定推荐博客低功耗蓝牙配对绑定解读和实践
蓝牙在配对之前都是明文通信的,也就是说主从机之间传输的数据包可以被第三方抓取分析逆向,而且如果没有配对,谁都可以无密码连接,显然是不安全的。
常用术语
Bonding(绑定)
配对过程中会生成一个长期密钥(LTK,long-term Key),如果配对双方把这个LTK存储起来放在Flash中,那么这两个设备再次重连的时候,就可以跳过配对流程,而直接使用LTK对蓝牙连接进行加密,设备的这种状态称为bonding。如果paring过程中不存储LTK(不分发LTK)也是可以的,paring完成后连接也是加密的,但是如果两个设备再次重连,那么就需要重走一次paring流程,否则两者还是明文通信。在不引起误解的情况下,我们经常把paring当成paring和bonding两者的组合,因为只paring不bonding的应用情况非常少见。在不引起混淆的情况下,下文就不区分paring和bonding的区别,换句话说,我们会把paring和bonding两个概念等同起来进行混用。
SM(security manager)
蓝牙协议栈的安全管理层,规定了跟蓝牙安全通信有关的所有要素,包括paring,bonding,以及下文提到的SMP。
SMP(security manager protocol)
安全管理协议,SMP着重两个设备之间的蓝牙交互命令序列,对paring的空中包进行了严格时序规定。
OOB(out of band,带外)
OOB就是不通过蓝牙射频本身来交互,而是通过比如人眼,NFC,UART等带外方式来交互配对信息,在这里人眼,NFC,UART通信方式就被称为OOB通信方式。
对于legacy paring,如果A和B都支持OOB,那么两者就会采用OOB方式进行配对,否则根据IO能力选择配对方式。对于SC paring,如果A或者B有一方支持OOB,那么两者就会采用OOB方式进行配对,否则根据IO能力选择配对方式。
Passkey
Passkey又称pin码,是指用户在键盘中输入的一串数字,以达到认证设备的目的。低功耗蓝牙的passkey必须为6位。
Numeric comparison(数字比较)
numeric comparison其实跟passkey一样,也是用来认证设备的,只不过passkey是通过键盘输入的,而numeric comparison是显示在显示器上的,numeric comparison也必须是6位的数字。
MITM(man in the middle)
MITM是指A和B通信过程中,C会插入进来以模拟A或者B,并且具备截获和篡改A和B之间所有通信报文的能力,从而达到让A或者B信任它,以至于错把C当成B或者A来通信。如果对安全要求比较高,需要具备MITM保护能力,在SM中这个是通过认证(authentication)来实现的,SM中实现认证的方式有三种:OOB认证信息,passkey以及numeric comparison,大家根据自己的实际情况,选择其中一种即可。
LESC(LE secure connections)
又称SC,蓝牙4.2引入的一种新的密钥生成方式和验证方式,SC通过基于椭圆曲线的Diffie-Hellman密钥交换算法来生成设备A和B的共享密钥,此密钥生成过程中需要用到公私钥对,以及其他的密码算法库。LESC同时还规定了相应的通信协议以生成该密钥,并验证该密钥。需要注意的是LESC对paring的其他方面也会产生一定的影响,所以我们经常会把LESC看成是一种新的配对方式。
Legacy paring
在LESC引入之前的密钥生成方式,称为legacy paring,换句话说,legacy paring是相对LESC来说的,不支持LESC的配对即为legacy paring(legacy配对)。
TK(Temporary Key,临时密钥)
legacy paring里面的概念,如果采用just work配对方式,TK就是为全0;如果采用passkey配对方式,TK就是passkey;如果采用OOB配对方式,TK就是OOB里面的信息。
STK(short term key,短期密钥)
legacy配对里面的概念,STK是通过TK推导出来的,通过TK对设备A和B的随机数进行加密,即得到STK。
LTK(long term key,长期密钥)
legacy配对和LESC配对都会用到LTK,如前所述,LTK是用来对未来的连接进行加密和解密用的。Legacy paring中的LTK由从设备根据相应的算法自己生成的(LTK生成过程中会用到EDIV(分散因子)和Rand(随机数)),然后通过蓝牙空中包传给主机。LESC配对过程中,先通过Diffie-Hellman生成一个共享密钥,然后这个共享密钥再对设备A和B的蓝牙地址和随机数进行加密,从而得到LTK,LTK由设备A和B各自同时生成,因此LTK不会出现在LESC蓝牙空中包中,大大提高了蓝牙通信的安全性。
IRK(Identity Resolving Key,蓝牙设备地址解析密钥)
有些蓝牙设备的地址为可解析的随机地址,比如iPhone手机,由于他们的地址随着时间会变化,那如何确定这些变化的地址都来自同一个设备呢?答案就是IRK,IRK通过解析变化的地址的规律,从而确定这些地址是否来自同一个设备,换句话说,IRK可以用来识别蓝牙设备身份,因此其也称为Identity information。IRK一般由设备出厂的时候按照一定要求自动生成。
Identity Address(设备唯一地址)
蓝牙设备地址包括public,random static, private resolvable,random unresolved共四类。如果设备不支持privacy,那么identity address就等于public或者random static设备地址。如果设备支持privacy,即使用private resolvable蓝牙设备地址,在这种情况下,虽然其地址每隔一段时间会变化一次,但是identity address仍然保持不变,其取值还是等于内在的public或者random static设备地址。Identity Address和IRK都可以用来唯一标识一个蓝牙设备。
IO capabilities(输入输出能力)
是指蓝牙设备的输入输出能力,比如是否有键盘,是否有显示器,是否可以输入Yes/No两个确认值。
根据IO输入输出能力以及是否存在OOB,存在如下几种配对方式(或者说认证方式)
- Just works(不带MITM保护,相当于Passkey方式 Passkey为000000)
- Numeric comparison(LESC才有,带MITM)
- Passkey(带MITM)
- OOB(OOB方式有没有MITM保护,取决于OOB通信的安全性,如果OOB通信具备MITM保护,那么蓝牙也具备MITM保护,否则就不具备。)
Key size(密钥长度)
一般来说,密钥默认长度为16字节,为了适应一些低端的蓝牙设备处理能力,你也可以把密钥长度调低,比如变为10个字节。
配对
配对的目的有三个:加密连接,认证设备,以及生成密钥
nrf52832 支持主从机配对和绑定操作的,但是做主机时配对和绑定没用过(官方论坛找到一个demo),下面的都是针对nrf52832做从机时的配对操作。
静态密码(Passkey模式)
日常使用蓝牙对从机进行连接时,会提示输入密码,这个其实就是一种Passkey配对过程.
- 蓝牙从机中存在一个静态密码(TK).
- 主机连接到从机后,从机启动安全连接认证
- 主从机交换配对信息(OOB、BAND、IO等信息)
- 主机产生一个随机数,根据随机数和主机的静态密码计算出一个确认值
- 从机同样产生一个随机数,根据随机数和从机的静态密码计算出一个确认值,主从机交换随机数和确认值.
- 主从机通过自己的静态密码和交换来的随机数,计算确认值和交换来的确认值对比,如果一致则配对成功,不一致则断开连接
- 配对成功后,主从机根据两个随机数和静态密码 生成一个临时密匙SKT,后面蓝牙通信都会经过该密匙加密处理
- 一旦连接加密了,主机和从机之间就可以分发一些秘密信息。如果是legacy paring,如下秘密信息必须分发:LTK、EDIV、Rand,同时根据情况,legacy paring还需分发如下信息:IRK、Identity address
nrf52832 sdk中添加配对处理,首先在gap初始化中添加静态密码设置
#define STATIC_PASSKEY "123456"
static ble_opt_t m_static_pin_option;
/**@brief Function for the GAP initialization.
*
* @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
* device including the device name, appearance, and the preferred connection parameters.
*/
static void gap_params_init(void)
ret_code_t 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 IO_CAPS BLE_GAP_IO_CAPS_DISPLAY_ONLY //只有显示装置
#define BOND 0 //不绑定
#define OOB 0 //没有外带认证数据 例如使用NFC或者二维码交换一些信息
#define MITM 1 //中间人保护
#define MIN_KEY_SIZE 7 //密匙最小长度
#define MAX_KEY_SIZE 16 //密匙最大长度
//配对请求函数,交换配对信息
void pairng_request(void)
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 = MAX_KEY_SIZE;
sec_params.min_key_size = MIN_KEY_SIZE;
sec_params.oob = OOB ;
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);
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
ret_code_t err_code;
switch (p_ble_evt->header.evt_id)
case BLE_GAP_EVT_CONNECTED: //主从机连接成功
NRF_LOG_INFO("Connected");
bsp_board_led_on(CONNECTED_LED);
bsp_board_led_off(ADVERTISING_LED);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
// err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
// APP_ERROR_CHECK(err_code);
err_code = app_button_enable();
APP_ERROR_CHECK(err_code);
ble_gap_phys_t const phys =
.rx_phys = BLE_GAP_PHY_2MBPS,
.tx_phys = BLE_GAP_PHY_2MBPS,
;
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
//启动连接后,连接成功,启动本次连接的安全认证
ble_gap_sec_params_t params;
params.bond = 0;
params.mitm = 1;
sd_ble_gap_authenticate(m_conn_handle,¶ms);
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST://PHY更新请求
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
;
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_PHY_UPDATE: //PHY更新完成
NRF_LOG_INFO("tx: %d, rx: %d",
p_ble_evt->evt.gap_evt.params.phy_update.tx_phy,
p_ble_evt->evt.gap_evt.params.phy_update.rx_phy
);
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected");
bsp_board_led_off(CONNECTED_LED);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
advertising_start();
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: //安全参数交换请求
pairng_request();
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;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_DEBUG("GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_DEBUG("GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
// 连接参数更新
case BLE_GAP_EVT_CONN_PARAM_UPDATE:
NRF_LOG_INFO("conn_Param Update: %d,%d,%d,%d",
p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.min_conn_interval,
p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.max_conn_interval,
p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.slave_latency,
p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.conn_sup_timeout
);
break;
case BLE_GAP_EVT_AUTH_STATUS:
//认证,如果认证失败,则断开连接
if(p_ble_evt->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
else
sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
default:
// No implementation needed.
break;
动态密码
动态密码和静态密码基本类似,只是不用在gap初始化时设置静态密码
//#define STATIC_PASSKEY "123456"
//static ble_opt_t m_static_pin_option;
/**@brief Function for the GAP initialization.
*
* @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
* device including the device name, appearance, and the preferred connection parameters.
*/
static void gap_params_init(void)
ret_code_t 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);
在协议栈回调者函数中添加一个事件处理将随机密码输出.注意IO要有显示器
case BLE_GAP_EVT_PASSKEY_DISPLAY:
NRF_LOG_INFO("passkey: %c,%c,%c,%c,%c,%c",
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[0],
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[1],
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[2],
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[3],
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[4],
p_ble_evt->evt.gap_evt.params.passkey_display.passkey[5]
);
break;
绑定
绑定是将第一次配对后产生的长期密匙信息和对应MAC存储在flash中,下次再遇到对应MAC地址就不需要进行配对输入密码操作,直接交换密匙信息即可。配对和绑定整个流程十分复杂,nordic 的SDK中也给了对应的驱动库,和上面的配对不同,下面使用sdk中的pm库文件进行配对绑定操作。
添加库文件
-
开启SDK配置项
-
添加库文件
-
添加文件路径
从机
如果使用静态密码,仍然在gap初始化时设置静态密码
#define STATIC_PASSKEY "123456"
static ble_opt_t m_static_pin_option;
/**@brief Function for the GAP initialization.
*
* @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
* device including the device name, appearance, and the preferred connection parameters.
*/
static void gap_params_init(void)
ret_code_t 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);
然后在主函数中进行pm管理器初始化
#include "peer_manager.h"
#include "peer_manager_handler.h"
#define SEC_PARAM_BOND 1 /**< Perform bonding. */
#define SEC_PARAM_MITM 1 /**< Man In The Middle protection is enabled. */
#define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY /**< Display Only. */
#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */
/**@brief Function for handling Peer Manager events.
*
* @param[in] p_evt Peer Manager event.
*/
static void pm_evt_handler(pm_evt_t const * p_evt)
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
switch (p_evt->evt_id)
case PM_EVT_PEERS_DELETE_SUCCEEDED:
advertising_start();
break;
default:
break;
///Added for bonding
/**@brief Function for the Peer Manager initialization.
*/
static void peer_manager_init(void)
ret_code_t err_code;
err_code = pm_init();
APP_ERROR_CHECK(err_code);
err_code = pm_register(pm_evt_handler);
APP_ERROR_CHECK(err_code);
ble_gap_sec_params_t sec_param;
memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
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;
err_code = pm_sec_params_set(&sec_param);
APP_ERROR_CHECK(err_code);
最后在连接后启动配对绑定处理函数,也可以由主机启动配对绑定处理函数
/**@brief Function for handling BLE events.
*
* @param[in] p_ble_evt Bluetooth stack event.
* @param[in] p_context Unused.
*/
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
ret_code_t err_code;
switch (p_ble_evt->header.evt_id)
case BLE_GAP_EVT_CONNECTED: //主从机连接成功
NRF_LOG_INFO("Connected");
bsp_board_led_on(CONNECTED_LED);
bsp_board_led_off(ADVERTISING_LED);
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
// err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
// APP_ERROR_CHECK(err_code);
err_code = app_button_enable();
APP_ERROR_CHECK(err_code);
ble_gap_phys_t const phys =
.rx_phys = BLE_GAP_PHY_2MBPS,
.tx_phys = BLE_GAP_PHY_2MBPS,
;
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
/启动配对绑定处理函数
err_code = pm_conn_secure(p_ble_evt->evt.gap_evt.conn_handle, false);
if (err_code != NRF_ERROR_BUSY)
APP_ERROR_CHECK(err_code);
/Added for bonding
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST://PHY更新请求
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
;
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_PHY_UPDATE: //PHY更新完成
NRF_LOG_INFO("tx: %d, rx: %d",
p_ble_evt->evt.gap_evt.params.phy_update.tx_phy,
p_ble_evt->evt.gap_evt.params.phy_update.rx_phy
);
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("Disconnected");
bsp_board_led_off(CONNECTED_LED);
m_conn_handle = BLE_CONN_HANDLE_INVALID;
err_code = app_button_disable();
APP_ERROR_CHECK(err_code);
advertising_start();
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: //安全参数交换请求
//pairng_request();
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;以上是关于nrf52832 学习笔记配对和绑定的主要内容,如果未能解决你的问题,请参考以下文章