搞定蓝牙——第五篇(SMP)

原理

Security Manager(简称SM,不要想歪)

按照前面的试验,两个设备可以通过ble通讯了,但是,这两个demo都是带有spp的,也就是透明传输的意思,也就是说,这两个设备的ble通讯在空中的数据是透明的,也就意味着会被别的设备捕获到,然后被破解出来,如果用来传输一些私密的东西那当然是不安全的了,所以,ble协议栈中还有一个协议专门做这个事情(给空中传输的数据进行加密),Security Manager Procotol,简称SMP。

秘钥

我们知道,数据的加密是需要一套加密算法的,既能按照算法推演出加密后的数据,又能将加密的数据解密出来。这个时候这套算法就一定有个参数来修改(如果是固定,别人捕获了这个数据按照这套算法来就可以解密了),这个参数叫做秘钥。

配对

那么,接收数据的那个ble设备怎么知道发送数据的ble的秘钥呢,这个时候就需要在发送数据前协商一下了,这个过程叫配对(Pairing)。
你想用手机连接蓝牙手环,如果隔壁大哥和你有一个一样的手环,这两个设备的名字设备的名字都是一样的,那么,你怎么知道你连接的是你的手环呢?我们可以在手环屏幕上显示,有一个手机要连接手环,然后加一个按钮,用户自己确定是不是要连接,为了防止大哥也正在连接大哥的手环,可以在手机显示一串数字,在手环显示同样的数字,我们只需要判断是不是相同数字就知道了。这个过程叫做鉴权,属于配对的一个小步骤,这串数字叫做key。

鉴权有两类,

  1. OOB,这种事不需要人来参与的,例如通过两者本来已经连接的wifi来交互这个key
  2. MITM,需要人来确认key的。这里有几种方式:
    A. 一个设备显示key,另外一个设备输入key
    B. 两个设备显示相同key,用户确认
    C. 两个设备都需要输入同样的key
    D. 两个设备都不需要输入,这种就是没有鉴权了
    当然,有些ble设备没有屏幕没有按键的,就无法输入key也无法查看key了,这就需要按照ble设备来决定使用哪种鉴权方式了,称为IO Capabilities。

有这六种方式。
NoInputNoOutput
DisplayOnly
NoInputNoOutput1
DisplayYesNo
KeyboardOnly
KeyboardDisplay

我们发现,我们手机连接手环只需要在买回来第一次输入key,后面就不需要了,这是因为,手机和手环的ble中的秘钥已经被存储到各自的存储器里面了,使用下次直接使用就行,这个行为叫做绑定。

秘钥生成

通过上面的步骤已经拿到key了,但是这个key可能会被旁边人偷窥到,这个key就不能直接用来作为秘钥了,两个ble这个时候使用这个key来作为临时秘钥来建立临时连接,然后使用这个临时秘钥来传输各自生成的真正的秘钥,最后使用这个真正的秘钥来加密要传输的数据。

特定秘钥分发

最后还有一步,就是ble其他协议还有一些秘钥和一些敏感数据,通过最后真正安全连接后来发送,称为特定秘钥分发。

原理总结

总结一下,SMP是一个给传输数据加密的安全管理协议。分为三大块,配对,秘钥生成,特定秘钥分发。配对其实有两种配对方式,LE legacy pairing和LE Secure Connections,后者更安全,蓝牙4.2以后才有,需要双方都支持这种。鉴权有两大类,OOB和MITM,主要取决于是不是需要人来参与,当然,设备优先使用OOB。在MITM中使用哪种鉴权方式,取决于两个设备是否能输入、是否能显示,具体看下图。
在这里插入图片描述
在这里插入图片描述

ESP32程序

初始化

打开官方demo
在这里插入图片描述

前面初始化部分和上一章的一样,SMP部分增加下面的初始化

/* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
    esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;     //bonding with peer device after authentication
    esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;           //set the IO capability to No output No input
    uint8_t key_size = 16;      //the key size should be 7~16 bytes
    uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    //set static passkey
    uint32_t passkey = 123456;
    uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_DISABLE;
    uint8_t oob_support = ESP_BLE_OOB_DISABLE;
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t));
    /* If your BLE device acts as a Slave, the init_key means you hope which types of key of the master should distribute to you,
    and the response key means which key you can distribute to the master;
    If your BLE device acts as a master, the response key means you hope which types of key of the slave should distribute to you,
    and the init key means which key you can distribute to the slave. */
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));

都是使用esp_ble_gap_set_security_param()这个函数,通过传递不同的参数来实现不同的功能。我们来分析这些代码。

  1. ESP_BLE_SM_SET_STATIC_PASSKEY 设置静态秘钥,也就是手机连接手环的时候,手机显示的那个数字。
  2. ESP_BLE_SM_SET_STATIC_PASSKEY 本地设备的身份验证要求,这个参数有如下选择
#define ESP_LE_AUTH_NO_BOND                 0x00                                     /*!< 0*/                     /* relate to BTM_LE_AUTH_NO_BOND in stack/btm_api.h */
#define ESP_LE_AUTH_BOND                    0x01                                     /*!< 1 << 0 */               /* relate to BTM_LE_AUTH_BOND in stack/btm_api.h */
#define ESP_LE_AUTH_REQ_MITM                (1 << 2)                                 /*!< 1 << 2 */               /* relate to BTM_LE_AUTH_REQ_MITM in stack/btm_api.h */
#define ESP_LE_AUTH_REQ_BOND_MITM           (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)/*!< 0101*/
#define ESP_LE_AUTH_REQ_SC_ONLY             (1 << 3)                                 /*!< 1 << 3 */               /* relate to BTM_LE_AUTH_REQ_SC_ONLY in stack/btm_api.h */
#define ESP_LE_AUTH_REQ_SC_BOND             (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY)            /*!< 1001 */  /* relate to BTM_LE_AUTH_REQ_SC_BOND in stack/btm_api.h */
#define ESP_LE_AUTH_REQ_SC_MITM             (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY)        /*!< 1100 */  /* relate to BTM_LE_AUTH_REQ_SC_MITM in stack/btm_api.h */
#define ESP_LE_AUTH_REQ_SC_MITM_BOND        (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND)   /*!< 1101 */  /* relate to BTM_LE_AUTH_REQ_SC_MITM_BOND in stack/btm_api.h */

是否绑定、使用MITI、使用Secure Connections模式(简写SC)
3. ESP_BLE_SM_IOCAP_MODE IO Capabilities选择
4. ESP_BLE_SM_MAX_KEY_SIZE 这个是设置最后两个ble生成的秘钥的长度,而不是用户在手机看到的静态秘钥的长度,这个数值越大加密等级越高,7-16之间
5. ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH 选择是否是只接收指定的认证模式,也就是如果选择“是”,那么设备只接受在初始化的时候设置的认证模式(MITM模式和绑定),否则不接受连接。比如参数 ESP_BLE_SM_AUTHEN_REQ_MODE设置为ESP_LE_AUTH_REQ_SC_MITM_BOND,那么对端设备该参数也需要设置为ESP_LE_AUTH_REQ_SC_MITM_BOND。
6. ESP_BLE_SM_OOB_SUPPORT 是否开启OOB
7. ESP_BLE_SM_SET_INIT_KEY 初始化特定秘钥
8. ESP_BLE_SM_SET_RSP_KEY 分发特定秘钥

GAP回调函数

初始化完成后,其他操作在GAP回调完成,比上一章的GAP回调多出以下代码

case ESP_GAP_BLE_PASSKEY_REQ_EVT:                           /* passkey request event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT");
        /* Call the following function to input the passkey which is displayed on the remote device */
        //esp_ble_passkey_reply(heart_rate_profile_tab[HEART_PROFILE_APP_IDX].remote_bda, true, 0x00);
        break;
    case ESP_GAP_BLE_OOB_REQ_EVT: {
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_OOB_REQ_EVT");
        uint8_t tk[16] = {1}; //If you paired with OOB, both devices need to use the same tk
        esp_ble_oob_req_reply(param->ble_security.ble_req.bd_addr, tk, sizeof(tk));
        break;
    }
    case ESP_GAP_BLE_LOCAL_IR_EVT:                               /* BLE local IR event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT");
        break;
    case ESP_GAP_BLE_LOCAL_ER_EVT:                               /* BLE local ER event */
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT");
        break;
    case ESP_GAP_BLE_NC_REQ_EVT:
        /* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
        show the passkey number to the user to confirm it with the number displayed by peer device. */
        esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true);
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_NC_REQ_EVT, the passkey Notify number:%d", param->ble_security.key_notif.passkey);
        break;
    case ESP_GAP_BLE_SEC_REQ_EVT:
        /* send the positive(true) security response to the peer device to accept the security request.
        If not accept the security request, should send the security response with negative(false) accept value*/
        esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
        break;
    case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:  ///the app will receive this evt when the IO  has Output capability and the peer device IO has Input capability.
        ///show the passkey number to the user to input it in the peer device.
        ESP_LOGI(GATTS_TABLE_TAG, "The passkey Notify number:%06d", param->ble_security.key_notif.passkey);
        break;
    case ESP_GAP_BLE_KEY_EVT:
        //shows the ble key info share with peer device to the user.
        ESP_LOGI(GATTS_TABLE_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
        break;
    case ESP_GAP_BLE_AUTH_CMPL_EVT: {
        esp_bd_addr_t bd_addr;
        memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
        ESP_LOGI(GATTS_TABLE_TAG, "remote BD_ADDR: %08x%04x",\
                (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
                (bd_addr[4] << 8) + bd_addr[5]);
        ESP_LOGI(GATTS_TABLE_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
        ESP_LOGI(GATTS_TABLE_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
        if(!param->ble_security.auth_cmpl.success) {
            ESP_LOGI(GATTS_TABLE_TAG, "fail reason = 0x%x",param->ble_security.auth_cmpl.fail_reason);
        } else {
            ESP_LOGI(GATTS_TABLE_TAG, "auth mode = %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode));
        }
        show_bonded_devices();
        break;
    }
    case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: {
        ESP_LOGD(GATTS_TABLE_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT status = %d", param->remove_bond_dev_cmpl.status);
        ESP_LOGI(GATTS_TABLE_TAG, "ESP_GAP_BLE_REMOVE_BOND_DEV");
        ESP_LOGI(GATTS_TABLE_TAG, "-----ESP_GAP_BLE_REMOVE_BOND_DEV----");
        esp_log_buffer_hex(GATTS_TABLE_TAG, (void *)param->remove_bond_dev_cmpl.bd_addr, sizeof(esp_bd_addr_t));
        ESP_LOGI(GATTS_TABLE_TAG, "------------------------------------");
        break;
    }
    case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT:
        if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS){
            ESP_LOGE(GATTS_TABLE_TAG, "config local privacy failed, error status = %x", param->local_privacy_cmpl.status);
            break;
        }

        esp_err_t ret = esp_ble_gap_config_adv_data(&heart_rate_adv_config);
        if (ret){
            ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
        }else{
            adv_config_done |= ADV_CONFIG_FLAG;
        }

        ret = esp_ble_gap_config_adv_data(&heart_rate_scan_rsp_config);
        if (ret){
            ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
        }else{
            adv_config_done |= SCAN_RSP_CONFIG_FLAG;
        }

大部分只是打印一些信息。
ESP_GAP_BLE_SEC_REQ_EVT,表示收到来自对端的保护连接请求,使用esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);如果本设备接受则开启安全请求。
ESP_GAP_BLE_NC_REQ_EVT、ESP_GAP_BLE_PASSKEY_NOTIF_EVT、ESP_GAP_BLE_KEY_EVT,这三个都是根据本设备和对端设备的IO Capabilities来进入的。
ESP_GAP_BLE_AUTH_CMPL_EVT,完成安全认证就会进入这个事件,然后调用一个函数show_bonded_devices();主要在屏幕显示认证完成并且绑定的设备信息。
ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT,当调用esp_ble_gap_config_local_privacy()来配置本地设备的隐私模式时,该事件会在操作完成后回调给应用程序,这个时候就会生成一个随机地址,然后使用随机地址配置广播数据。这个地址是每隔一段时间自动更改的,更改后会发生ESP_GAP_BLE_LOCAL_IR_EVT事件。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS SMP(Symmetric Multi-Processing,对称多处理)是一个支持多核处理器的操作系统。它是FreeRTOS实时操作系统的一个扩展版本,为多核处理器的并行计算提供了支持。 FreeRTOS SMP具有以下特点和优势: 1. 多核支持:FreeRTOS SMP允许在多核处理器上对任务进行并行处理。每个核心都可以独立地运行一个实例的FreeRTOS内核,而这些内核之间可以进行任务和资源的共享。 2. 调度器:FreeRTOS SMP的调度器可以利用多核处理器的并行能力,实现任务的优先级调度和负载均衡。它可以根据任务的优先级自动分配处理器核心,以提高整个系统的性能和响应能力。 3. 任务同步和通信:FreeRTOS SMP提供了多个任务同步和通信机制,如信号量、互斥锁、消息队列等。这些机制可以有效地实现多核处理器上任务之间的同步和通信,保证数据的一致性和安全性。 4. 内存管理:FreeRTOS SMP通过提供内核堆管理器和内存保护机制,有效地管理多个核心上的内存资源,防止内存泄漏和冲突。 5. 架构无关性:FreeRTOS SMP的设计和实现是与底层处理器架构无关的,可以方便地移植到不同的多核处理器上。 通过使用FreeRTOS SMP,开发人员可以充分利用多核处理器的计算能力,提高系统的并行处理能力和性能,同时实现任务间的同步和通信。它广泛应用于各种多核平台上,如嵌入式系统、网络设备、工业自动化等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值