ESP32入门基础之安全连接SMP-server
Posted while(1)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32入门基础之安全连接SMP-server相关的知识,希望对你有一定的参考价值。
文章目录
1 gatt_security_server工程的理解
文章连接 Gatt Security Server Example Walkthrough
- 可能涉及到的英文缩写
临时密钥(Temporary Key:TK)
短期密钥(Short Term Key:STK)
长期秘钥(Long-Term Key:LTK)
身份解析秘钥(Identity Resolving Key:IRK)
连接签名解析秘钥(Connection Signature Resolving Key:CSRK)
1.1 工程简介
通过安全配置,作为从设备的GATT Server可以与主设备建立绑定,并在两者之间建立加密链路。BLE安全涉及三个相互关联的概念:配对(pairing)、绑定(bonding )和加密(encryption)。配对关注点是所需的安全特性和密钥类型的交换。此外,配对过程负责共享密钥的生成和交换。核心规范定义了传统配对(legacy pairing)和安全连接配对(Secure Connections pairing),ESP32对两种配对方式均支持。一旦共享密钥交换完成,就会建立一个临时加密链接来交换短期密钥和长期密钥。绑定指的是为后续连接存储交换的密钥,以便它们不必再次传输。最后,加密是指使用AES-128引擎和共享密钥对纯文本数据进行加密。服务器属性也可以定义为只允许加密的写和读消息。在通信的任何一点,从设备总是可以通过向另一个对等设备发出安全请求来要求开始加密,另一个对等设备通过调用API返回一个安全响应。
1.2 设置安全参数
ESP32需要一系列安全参数来定义如何构建配对请求和响应。GATT服务器构建的配对响应包包括input/output capabilities(IO Capability)、安全连接配对、经过认证的中间人(MITM)保护或无安全要求。在本例中,这个过程是在app_main()函数中完成的。配对请求由发起者发送,在本例中,发起者是远程GATT客户端。本例中实现的ESP32服务器接收配对请求并使用一个配对响应进行应答,该响应包含相同的安全参数,以便两个设备就可用资源和适用的配对算法达成一致(Just Works or Passkey Entry)。配对请求和配对响应命令都有以下参数:
-
IO Capability: 描述设备是否具有输入/输出功能,如显示器或键盘。
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//set the IO capability to No Input No Output
IO Capability 还可以设置为如下值,
ESP_IO_CAP_OUT 0 /*!< DisplayOnly */ ESP_IO_CAP_IO 1 /*!< DisplayYesNo */ ESP_IO_CAP_IN 2 /*!< KeyboardOnly */ ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */ ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */
-
OOB(out of band) Flag:描述设备是否支持带外密钥交换,例如使用NFC或Wi-Fi交换密钥作为TKs.
-
Authorization Request: 指示所请求的安全属性,如绑定、安全连接(SC)、MITM保护或无,这些属性将出现在配对请求和响应包中。
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND; //bonding with peer device after authentication
Authorization Request 还可以设置为如下值,
ESP_LE_AUTH_NO_BOND: No bonding. ESP_LE_AUTH_BOND: Bonding is performed. ESP_LE_AUTH_REQ_MITM: MITM Protection is enabled. ESP_LE_AUTH_REQ_SC_ONLY: Secure Connections without bonding enabled. ESP_LE_AUTH_REQ_SC_BOND: Secure Connections with bonding enabled. ESP_LE_AUTH_REQ_SC_MITM: Secure Connections with MITM Protection and no bonding enabled. ESP_LE_AUTH_REQ_SC_MITM_BOND: Secure Connections with MITM Protection and bonding enabled.
-
Maximum Encryption Key Size:最大加密密钥大小(以字节为单位)。
uint8_t key_size = 16; //the key size should be 7~16 bytes
-
Initiator Key Distribution/Generation: 指示发起者在传输特定密钥分发阶段请求分发/生成或使用哪些密钥。在配对请求中,请求这些密钥,而在配对响应中,确认这些密钥已分发。
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
发起者通过设置EncKey和IdKey掩码来分发LTK和IRK密钥。
-
Responder Key Distribution/Generation: 指示发起方请求响应方在传输特定密钥分发阶段分发/生成或使用哪些密钥。在配对请求中,请求这些密钥,而在配对响应中,确认这些密钥已分发。
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
响应者通过设置EncKey和IdKey掩码来分发LTK和IRK密钥。
定义完参数后,使用esp_ble_gap_set_security_param()
函数设置参数,该函数设置参数类型、参数值和参数长度:
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_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));
这些信息足以让BLE协议栈执行配对过程,包括配对确认和密钥生成。该过程对用户是不可见的,由BLE协议栈自动执行。
1.3 连接并绑定对端设备
前面设置的安全参数存储在本地,稍后当主设备连接到从设备时使用。每当远端设备连接到本地GATT服务器时,都会触发连接事件ESP_GATTS_CONNECT_EVT
。此事件通过调用esp_ble_set_encryption()
函数来执行配对和绑定过程,而且该函数将远端设备地址和将要执行的加密类型作为参数。然后BLE协议栈在后台执行实际的配对过程。在本例中,加密包括MITM保护。
case ESP_GATTS_CONNECT_EVT:
//start security connect with peer device when receive the connect event sent by the master.
esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT_MITM);
break;
可用的加密类型有:
ESP_BLE_SEC_NONE
ESP_BLE_SEC_ENCRYPT
ESP_BLE_SEC_ENCRYPT_NO_MITM
ESP_BLE_SEC_ENCRYPT_MITM
ESP_BLE_SEC_ENCRYPT
和ESP_BLE_SEC_ENCRYPT_NO_MITM
之间的区别在于,以前的连接可能具有需要升级的安全级别,因此需要再次交换密钥。
在本例中,I/O Capability 被设置为No Input No Output,因此使用了Just Works配对方法,该方法不需要生成随机的6位密钥 (详情请参阅下表)。用户可以修改示例以设置I/O Capability ,而不使用No Input No Output。因此,根据远程设备的I/O Capability ,可能会在ESP32上生成一个密钥,该密钥通过ESP_GAP_BLE_PASSKEY_NOTIF_EVT
事件呈现给用户。
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_LOGE(GATTS_TABLE_TAG, "The passkey Notify number:%d", param->ble_security.key_notif.passkey);
break;
决定使用哪种算法的 input and output capabilities 的组合有如下:
- Just Work:只工作
只在工作时实现加密通信,两设备使用的是默认的TK值,即6个0。对于这种方式是一个不可靠的加密链路,它不能防止MITM攻击。这种方式使用时可靠的前提是,确保在配对绑定时能保证没有MITM攻击,那么在之后的连接中加密的数据是无法被其它设备窃听的,也就是说这种方式保护的是将来的加密链路安全,但是不能保护配对绑定过程。 - Passkey Entry:输入密码
TK的共享并不是通过无线传输的,而是通过人为方式使两个设备使用的临时密钥一样。对于输入密钥来说,实现的方式 是 : 两 个 设 备 中 , 一 个 蓝 牙 设 备 在 自 己 的 显 示 屏 上 显 示[000000,999999]之间的随机 6 位数;而操作人员看到这 6 位数后,将这 6 位数在另一个蓝牙设备中输入,从而实现两个设备的 TK 值一样。
输入的虽然是6位数,但是还是会将其前面补充0补为128bit。
1.4 交换秘钥
当客户端连接到服务器并且配对成功时,将交换init_key和rsp_key安全参数所指示的密钥。在本例中,生成并分发了以下密钥:
- Local device LTK
- Local device IRK
- Local device CSRK
- Peer device LTK
- Peer device IRK
注意,仅在本例中,不交换对端设备的CSRK。对于每个密钥交换消息,会触发ESP_GAP_BLE_KEY_EVT
事件,该事件可用于打印接收到的密钥类型:
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;
当密钥成功交换时,配对过程就完成了。这将触发ESP_GAP_BLE_AUTH_CMPL_EVT
事件,该事件用于打印信息,如远端设备、地址类型和配对状态:
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");
break;
1.5 属性的安全权限
在定义服务器的属性时,可以为写和读事件设置不同的权限。属性权限可以是:
Permission to read
Permission to read with encryption
Permission to read with encryption and MITM protection
Permission to write
Permission to write with encryption
Permission to write with encryption and MITM protection
Permission to signed write
Permission to signed write with MITM protection
这些权限在API中定义为:
ESP_GATT_PERM_READ
ESP_GATT_PERM_READ_ENCRYPTED
ESP_GATT_PERM_READ_ENC_MITM
ESP_GATT_PERM_WRITE
ESP_GATT_PERM_WRITE_ENCRYPTED
ESP_GATT_PERM_WRITE_ENC_MITM
ESP_GATT_PERM_WRITE_SIGNED
ESP_GATT_PERM_WRITE_SIGNED_MITM
在创建服务表时,每个属性都可以具有读写权限,可以加密也可以不加密。当一个属性具有加密的权限,而没有相应的安全许可的对等设备试图读或写该属性时,本地主机发送一个授权不足错误。在本例中,使用加密权限定义了以下属性:
…
// Body Sensor Location Characteristic Value
[HRS_IDX_BOBY_SENSOR_LOC_VAL] =
ESP_GATT_AUTO_RSP,
ESP_UUID_LEN_16,
(uint8_t *)&body_sensor_location_uuid,
ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t),
sizeof(body_sensor_loc_val),
(uint8_t *)body_sensor_loc_val
,
…
// Heart Rate Control Point Characteristic Value
[HRS_IDX_HR_CTNL_PT_VAL] =
ESP_GATT_AUTO_RSP,
ESP_UUID_LEN_16,
(uint8_t *)&heart_rate_ctrl_point,
ESP_GATT_PERM_WRITE_ENCRYPTED|ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t),
sizeof(heart_ctrl_point),
(uint8_t *)heart_ctrl_point
,
…
1.6 安全要求
在主设备和从设备之间的通信过程中,从设备可以通过发出安全请求命令随时请求开始加密。该命令将在主设备上触发ESP_GAP_BLE_SEC_REQ_EVT
事件,该事件将向对端设备应答一个积极(true)的安全响应以接受请求,或应答一个消极(false)的安全响应以拒绝请求。在本例中,此事件调用esp_ble_gap_security_rsp()
函数来应答启动加密响应。
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;
以上是关于ESP32入门基础之安全连接SMP-server的主要内容,如果未能解决你的问题,请参考以下文章