关于配对绑定的一些原理内容这里不再重复介绍,看之前的几篇文档,静态密码,动态密码,连接时触发配对就可以了。

配对绑定的内容可能比较难懂,升入的学习需要去看规范,将前面的几篇相关文档看一遍实验一边再去看规范能更好理解相关理论。

配对绑定是一个完整的过程,只是绑定是可选的,绑定简单来说就是存储一个长期秘钥LTK,以方便以后加密。当然还分配了其他秘钥,这里不涉及。

绑定是在配对之后,要明确 所谓配对 目的就是加密链路,以让数据能加密传输,所以绑定肯定是在配对之后,因为绑定就是分发各种秘钥,所以肯定要加密传输不然被别人窃听到了,以后用 分发的秘钥 再加密链路就不安全了。

总之 配对的目的 就是单纯的加密链路,但是配对过程比较耗时(包括配对信息交换,用户输入配对码或带外传输配对码,协议层的配对确认交换和随机数交换以及确认验证,都没问题后才会生成链路加密秘钥来加密链路),如果为了数据始终都是加密传输而每次连接都去配对的话就比较麻烦,所以又定义了一个绑定过程,绑定过程是在 配对后链路加密的情况下 分发一个 LTK(其他秘钥这里不涉及),这个LTK就可以供以后直接加密链路,而不用进过繁琐的配对过程。

PS:其实LTK分配之后,每次重新连接时的加密并不是用LTK直接加密链路,而是双方交换一些信息(称为会话秘钥分散器),然后利用这些信息和LTK最终生成一个会话秘钥,真正的加密是用这个会话秘钥。

这里我实现一个 从机显示配对码,主机输入配对码的配对方式,配对码为随机的,从机的配对码从串口打印出来。

主机输入配对码这个配对方式由配对信息交换时是否存在MITM标志以及从机是否有显示装置决定,所以我们配对信息中需要设置MITM标志,以及将I/O能力设置为有显示。

绑定过程是否存在 取决于配对信息交换中是否设置了Bond标志,这里我们也要设置。

我们这里测试绑定时在绑定阶段只分发LTK,其他秘钥这里不涉及

配对绑定大致分为3个阶段:

1:配对信息的交换

2:生成STK(短期秘钥)加密链路

3:链路加密后就可以安全分发各种秘钥了。

大致的过程图如下所示

大部分的工作协议栈都做好了,上层要处理的就是设置一些 参数以及处理几个事件。

首先要处理 BLE_GAP_EVT_SEC_PARAMS_REQUEST 事件,当手机发来配对请求时就会收到这个事件。 需要调用 回复api回复配对参数。

简要代码如下。

case BLE_GAP_EVT_SEC_PARAMS_REQUEST:

    printf("\r\n\r\n step : %d\r\n",++step_count);
    printf("receive pair req\r\n");

    init_sec();
    err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &g_pair_params, &keyset);

    printf("err_code :%d\r\n",err_code);

    break; 

sd_ble_gap_sec_params_repl 的第三个参数g_pair_params参数就是要回复的配对绑定参数设置

具体设置如下

ble_gap_sec_params_t  g_pair_params;

void init_sec(void){
    g_pair_params.bond = ;
    g_pair_params.io_caps = ;
    g_pair_params.oob = ;
    g_pair_params.mitm = ;
    g_pair_params.min_key_size = ;
    g_pair_params.max_key_size = ;

    g_pair_params.kdist_central.enc = ;
    g_pair_params.kdist_central.id = ;
    g_pair_params.kdist_central.sign = ;
    g_pair_params.kdist_periph.enc = ;
    g_pair_params.kdist_periph.id = ;
    g_pair_params.kdist_periph.sign = ;
}

IO能力及设备的输入输出能力,有以下几个值

#define        BLE_GAP_IO_CAPS_DISPLAY_ONLY        0x00
#define        BLE_GAP_IO_CAPS_DISPLAY_YESNO       0x01
#define        BLE_GAP_IO_CAPS_KEYBOARD_ONLY       0x02
#define        BLE_GAP_IO_CAPS_NONE                0x03
#define        BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY    0x04

设置了MITM为1和io能力设置为0,这个组合表示配对过程中 设备会显示配对码,主机需要输入对应配对码。

Bond 设置为1表示需要绑定,则配对会存在绑定过程即LTK等秘钥的分发。

具体分发哪些秘钥也是可以控制,这里只说LTK,所以上面的设置中只设置了相互分发长期秘钥LTK,其他不需要设置

PS:之所以相互分发LTK是因为,如果手机本次作为主机连接了设备,设备作为从机,配对绑定后断开连接,当再次连接时如果手机依然是作为主机去连设备,那么加密时就需要  从机分发给手机的LTK。但是有的应用可能主从角色并不是固定的,下次可能 是设备作为主机去连手机那么 加密时 就需要上次绑定时 手机发给设备的LTK。 上面的情况我们设置了相互分发LTK,其实一般都是手机一直作为主机去连设备,这种情况我们只需要g_pair_params.kdist_periph.enc = 1;就可以了

既然是配对会存在分发秘钥过程,那么协议栈交换的秘钥存储在哪里以在绑定完成后返回给上层呢。

返回的秘钥就是存在 sd_ble_gap_sec_params_repl 的第四个参数keyset

设置如下,即我们需要自己创建变量来存储分发的秘钥,因为这只用了LTK,所以其他两个指针设置为NULL就行了。

ble_gap_enc_key_t my_enc_key;
ble_gap_enc_key_t my_enc_key_center;
ble_gap_sec_keyset_t keyset;

void init_keyset(void){
    keyset.keys_periph.p_enc_key = &my_enc_key;
    keyset.keys_periph.p_id_key = NULL;
    keyset.keys_periph.p_sign_key = NULL;

    keyset.keys_central.p_enc_key = &my_enc_key_center;
    keyset.keys_central.p_id_key = NULL;
    keyset.keys_central.p_sign_key = NULL;
} 

当 sd_ble_gap_sec_params_repl按如上设置回复后,设备这边就会显示配对码需要让手机输入,因为没有显示 屏这里通过串口打印出来。

上层会收到协议栈的BLE_GAP_EVT_PASSKEY_DISPLAY提交上来的显示事件。直接将配对码打印出来

case BLE_GAP_EVT_PASSKEY_DISPLAY:
    printf("\r\n\r\n step : %d\r\n",++step_count);
    printf("passkey: ");
    ; i < ; i++ ){
        printf("%c",p_ble_evt->evt.gap_evt.params.passkey_display.passkey[i]);
    }
    printf("\r\n");
    break; 

这之后基本都是协议栈内部进行了,当绑定完成后上层会收到协议栈的BLE_GAP_EVT_AUTH_STATUS事件表示完成了秘钥的分发。

我们在收到这个事件后打印相关的秘钥信息

case BLE_GAP_EVT_AUTH_STATUS:
    flag = ;
    printf("\r\n\r\n step : %d\r\n",++step_count);
    printf("keyset dispatch done\r\n");
    printf("LTK :");

    ; i < my_enc_key.enc_info.ltk_len; i++){
        printf("%x",my_enc_key.enc_info.ltk[i]);
    }

    printf("   AUTH: %d ",my_enc_key.enc_info.auth);
    printf("   LTK length: %d \r\n",my_enc_key.enc_info.ltk_len);
    printf("EDIV: %x ",my_enc_key.master_id.ediv);
    printf("rand:");

    ; i < ; i++){
        printf("%x",my_enc_key.master_id.rand[i]);
    }

    printf("\r\n");
    break;

ps: EDIV 和RAND是和LTK一起发送的,你可以将其看做是LTK的标示,当手机以后请求用LTK来进行加密时就会发送给从机EDIV和RAND让其确定要使用的LTK

到这里配对绑定过程就结束了,前面说过绑定的目的是为了下次链接需要安全链路时不再进行繁琐的配对过程,所以一般手机和一个设备绑定过后,当下次再连接时都会直接用以前绑定时的LTK来发起加密请求。

我们在收到这个信息后打印了加密请求的一些信息,打印了我们回复的LTK和请求的EDIV,RAND看是不是和上面绑定过程时分配的一样。

case BLE_GAP_EVT_SEC_INFO_REQUEST:
    printf("\r\n\r\n step : %d\r\n",++step_count);
    printf("enc need: %d  id need:%d   sign need:%d\r\n",
    p_ble_evt->evt.gap_evt.params.sec_info_request.enc_info,
    p_ble_evt->evt.gap_evt.params.sec_info_request.id_info,
    p_ble_evt->evt.gap_evt.params.sec_info_request.sign_info);             

    printf("RSP:  LTK :");
    ; i < my_enc_key.enc_info.ltk_len; i++){
        printf("%x",my_enc_key.enc_info.ltk[i]);
    }

    printf("\r\nEDIV :%x  RANDOM:",
    p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv);

    ; i< ; i++){
        printf("%x",p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand[i]);
    }

    err_code=sd_ble_gap_sec_info_reply(m_conn_handle,  &(my_enc_key.enc_info), NULL, NULL);
    printf("\r\nencryption rsp err_code:%d\r\n",err_code);
    break; 

关于如何让手机发起配对,我们还是设置CCCD的访问安全要求 来实现在点击notify时,设备会返回安全不足,然后手机便会发起配对。具体可以看之前的

静态/动态配对码 的文章。

程序运行后如下图所示,连接上后,点击notify便会触发配对。

首先收到配对请求

之后协议栈会上传配对码,通过串口显示,手机端输入该配对码。

最后配对完成,打印了LTK和EDIV,RAND.

然后断开连接,再次连接设备,可以看到手机这次直接就发加密请求过来了。请求中的EDIV和RAND也和之前绑定时分配的一样。

最后贴一下相关代码,代码都是关于几个事件的处理,我全部都添加在了on_ble_evt这个事件处理函数中。

ble_gap_sec_params_t  g_pair_params;

void init_sec(void)
{
       g_pair_params.bond = ;
       g_pair_params.io_caps = ;
       g_pair_params.oob = ;
       g_pair_params.mitm = ;
       g_pair_params.min_key_size = ;
       g_pair_params.max_key_size = ;

       g_pair_params.kdist_central.enc = ;
       g_pair_params.kdist_central.id = ;
       g_pair_params.kdist_central.sign = ;
       g_pair_params.kdist_periph.enc = ;
       g_pair_params.kdist_periph.id = ;
       g_pair_params.kdist_periph.sign = ;
}

ble_gap_sec_keyset_t  sec_keyset;
ble_gap_conn_sec_mode_t sec_mode;
ble_gap_enc_key_t my_enc_key;
ble_gap_enc_key_t my_enc_key_center;
ble_gap_sec_keyset_t keyset;

void init_keyset(void)
{
    keyset.keys_periph.p_enc_key = &my_enc_key;
    keyset.keys_periph.p_id_key = NULL;
    keyset.keys_periph.p_sign_key = NULL;

    keyset.keys_central.p_enc_key = &my_enc_key_center;
    keyset.keys_central.p_id_key = NULL;
    keyset.keys_central.p_sign_key = NULL;
}

uint8_t step_count = ;

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;
            init_keyset();
            printf("\r\nconnected\r\n");
            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;
            printf("\r\ndisconnected");
            break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            printf("\r\n\r\n step : %d\r\n",++step_count);
            printf("receive pair req\r\n");
            init_sec();
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &g_pair_params, &keyset);
            printf("err_code :%d\r\n",err_code);
            // APP_ERROR_CHECK(err_code);
            break;

        case BLE_GAP_EVT_PASSKEY_DISPLAY:
            printf("\r\n\r\n step : %d\r\n",++step_count);
            printf("passkey: ");
            ; i < ; i++ ){
                printf("%c",p_ble_evt->evt.gap_evt.params.passkey_display.passkey[i]);
            }
            printf("\r\n");
            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, , );
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GAP_EVT_AUTH_STATUS:
            printf("\r\n\r\n step : %d\r\n",++step_count);
            printf("keyset dispatch done\r\n");
            printf("LTK :");
            ; i < my_enc_key.enc_info.ltk_len; i++){
                printf("%x",my_enc_key.enc_info.ltk[i]);
            }

            printf("   AUTH: %d ",my_enc_key.enc_info.auth);
            printf("   LTK length: %d \r\n",my_enc_key.enc_info.ltk_len);
            printf("EDIV: %x ",my_enc_key.master_id.ediv);
            printf("rand:");

            ; i < ; i++){
                printf("%x",my_enc_key.master_id.rand[i]);
            }
            printf("\r\n");
            break;

        case BLE_GAP_EVT_SEC_INFO_REQUEST:
            printf("\r\nenc need: %d  id need:%d   sign need:%d\r\n",p_ble_evt->evt.gap_evt.params.sec_info_request.enc_info,
            p_ble_evt->evt.gap_evt.params.sec_info_request.id_info,
            p_ble_evt->evt.gap_evt.params.sec_info_request.sign_info);
            printf("RSP:  LTK :");
            ; i < my_enc_key.enc_info.ltk_len; i++){
                printf("%x",my_enc_key.enc_info.ltk[i]);
            }
            printf("\r\nEDIV :%x  RANDOM:",p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv);
            ; i< ; i++){
                printf("%x",p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand[i]);
            }

            err_code = sd_ble_gap_sec_info_reply(m_conn_handle, &(my_enc_key.enc_info), NULL, NULL);
            printf("\r\nencryption rsp err_code:%d\r\n",err_code);
            break;

        default:

            // No implementation needed.

            break;
    }

}
 

nrf51822-配对绑定实现过程的更多相关文章

  1. nRF51822 配对之device_manager_init 调用,以及保证 用户数据存储 的Flash 操作不与device manager 模块冲突

    昨天 遇到了一个烦心的问题,被老外客户怼了两句,恼火,很想发火,发现英文不够用,算了,就不跟直肠的鬼佬一般见识.说正事. 最近的一个nRF51822+MT2503 钱包防丢项目,准备接近量产了.昨天做 ...

  2. MVC中Action参数绑定的过程

    一.题外话 上一篇:MVC中Action的执行过程 ControllerContext 封装有了与指定的 RouteBase 和 ControllerBase 实例匹配的 HTTP 请求的信息. 二. ...

  3. ASP.NET MVC5学习笔记之Action参数模型绑定基本过程

    当我们在Controller中定义一个Action,通常会定义一个或多个参数,每个参数称为一个模型,ASP.NET MVC框架提供了一种机制称为模型绑定,会尝试自动从请求的信息中实例化每一个模型并赋值 ...

  4. Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程

    Launcher在应用启动的时候,需要加载AppWidget,shortcut等内容项,通过调用LauncherModel.startLoader(),开始加载的工作.launcherModel中加载 ...

  5. Android Service组件在新进程绑定(bindService)过程

    1.首先看两个样例 (1)进程内 Client端 public class CounterService extends Service implements ICounterService { .. ...

  6. nrf51822-主从通信分析2

    解决第三个问题:如何使能从机上的特征值的 notify功能,使其能通过notify方式发送数据   使能从机的notify功能是通过写0x0001到从机的那个具有notify功能的特征值的CCCD描述 ...

  7. BLE蓝牙通信指令交互过程配对与绑定

    最简单一次蓝牙通信需要以上相关步骤,包括discovery device,connect,pairing,bond等4个主要部分.BLE中主从机建立连接,到配对和绑定的过程如下图:

  8. Android应用程序绑定服务(bindService)的过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6745181 Android应用程序组件Serv ...

  9. Spring绑定请求参数过程以及使用@InitBinder来注册自己的属性处理器

    在工作中,经常会出现前台的请求参数由于无法被正常转型,导致请求无法进到后台的问题. 比如,我有一个User.其性别的属性被定义成了枚举,如下: public enum Gender { MALE(&q ...

随机推荐

  1. 输入框提示文字js

    <input style="margin-right: 0px; padding-right: 0px;" class="text" required=& ...

  2. Objective-C专题,是学习iOS开发的前奏(转)

    第一个OC的类 来源:http://www.cnblogs.com/mjios/archive/2013/04/06/3002814.html 本文目录 一.语法简介 二.用Xcode创建第一个OC的 ...

  3. QUnit使用笔记-4保持原子性与分组

    原子性: 当将许多测试用例放到一起测试的时候,可能会因为相互的副作用而出错:这个时候应该尽可能将他们分别放到test()中测试: 对应测试到Dom,应该尽可能地使用#qunit-fixture,因为它 ...

  4. Java unicode中文编码转换和反转

    参考网址http://www.oschina.net/code/snippet_142385_4297 http://canofy.iteye.com/blog/718659 在java的很多配置文件 ...

  5. extjs 动态添加item

    <html> <p> </p> <head> <title>测试页面</title> <meta http-equiv=& ...

  6. Get function name by address in Linux

    Source file: 1: #define __USE_GNU //import! 2: #include <dlfcn.h> 3: #include <stdio.h> ...

  7. 洛谷 P1009 阶乘之和 Label:高精度

    题目描述 用高精度计算出S=1!+2!+3!+…+n!(n≤50) 其中“!”表示阶乘,例如:5!=5*4*3*2*1. 输入输出格式 输入格式: 一个正整数N. 输出格式: 一个正整数S,表示计算结 ...

  8. TYVJ P1086 Elevator Label:dp

    背景 广东汕头聿怀初中 Train#2 Problem4 描述 现有N种箱子,每种箱子高度H_i,数量C_i.现选取若干箱子堆成一列,且第i种箱子不能放在高度超过A_i的地方.试求最大叠放高度. 输入 ...

  9. 【BZOJ】1038: [ZJOI2008]瞭望塔

    http://www.lydsy.com/JudgeOnline/problem.php?id=1038 题意:给出n个x轴各不相同的二维整点,且升序,n<=300,坐标绝对值<=10^6 ...

  10. SQL中distinct的用法(转自博主:Rain Man)

    在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值.关键词 distinct用于返回唯一不同的值. 表A: 示例1 select distinct nam ...