Merge branch 'bugfix/provisioning_sec2_aes_iv_usage_v5.4' into 'release/v5.4'

fix(provisioning): fix incorrect AES-GCM IV usage in security2 scheme (v5.4)

See merge request espressif/esp-idf!37613
This commit is contained in:
Jiang Jiang Jian
2025-03-11 10:40:27 +08:00
13 changed files with 379 additions and 82 deletions
+26 -9
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,6 +20,8 @@
#include "esp_local_ctrl.pb-c.h"
#define ESP_LOCAL_CTRL_VERSION "v1.0"
/* JSON format string for version endpoint */
#define ESP_LOCAL_CTRL_VER_FMT_STR "{\"local_ctrl\":{\"ver\":\"%s\",\"sec_ver\":%d,\"sec_patch_ver\":%d}}"
struct inst_ctx {
protocomm_t *pc;
@@ -136,14 +138,6 @@ esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config)
}
}
ret = protocomm_set_version(local_ctrl_inst_ctx->pc, "esp_local_ctrl/version",
ESP_LOCAL_CTRL_VERSION);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set version endpoint");
esp_local_ctrl_stop();
return ret;
}
protocomm_security_t *proto_sec_handle = NULL;
switch (local_ctrl_inst_ctx->config.proto_sec.version) {
case PROTOCOM_SEC_CUSTOM:
@@ -183,6 +177,29 @@ esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config)
return ret;
}
int sec_ver = 0;
uint8_t sec_patch_ver = 0;
protocomm_get_sec_version(local_ctrl_inst_ctx->pc, &sec_ver, &sec_patch_ver);
const int rsize = snprintf(NULL, 0, ESP_LOCAL_CTRL_VER_FMT_STR, ESP_LOCAL_CTRL_VERSION, sec_ver, sec_patch_ver) + 1;
char *ver_str = malloc(rsize);
if (!ver_str) {
ESP_LOGE(TAG, "Failed to allocate memory for version string");
esp_local_ctrl_stop();
return ESP_ERR_NO_MEM;
}
snprintf(ver_str, rsize, ESP_LOCAL_CTRL_VER_FMT_STR, ESP_LOCAL_CTRL_VERSION, sec_ver, sec_patch_ver);
ESP_LOGD(TAG, "ver_str: %s", ver_str);
ret = protocomm_set_version(local_ctrl_inst_ctx->pc, "esp_local_ctrl/version",
ver_str);
free(ver_str);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set version endpoint");
esp_local_ctrl_stop();
return ret;
}
ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, "esp_local_ctrl/control",
esp_local_ctrl_data_handler, NULL);
if (ret != ESP_OK) {
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -260,6 +260,21 @@ esp_err_t protocomm_set_version(protocomm_t *pc, const char *ep_name,
*/
esp_err_t protocomm_unset_version(protocomm_t *pc, const char *ep_name);
/**
* @brief Get the security version of the protocomm instance
*
* This API will return the security version of the protocomm instance.
*
* @param[in] pc Pointer to the protocomm instance
* @param[out] sec_ver Pointer to the security version
* @param[out] sec_patch_ver Pointer to the security patch version
*
* @return
* - ESP_OK : Success
* - ESP_ERR_INVALID_ARG : Null instance/name arguments
*/
esp_err_t protocomm_get_sec_version(protocomm_t *pc, int *sec_ver, uint8_t *sec_patch_ver);
#ifdef __cplusplus
}
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -86,6 +86,11 @@ typedef struct protocomm_security {
*/
int ver;
/**
* Patch version number of security implementation
*/
uint8_t patch_ver;
/**
* Function for initializing/allocating security
* infrastructure
+13 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -426,3 +426,15 @@ esp_err_t protocomm_unset_version(protocomm_t *pc, const char *ep_name)
return protocomm_remove_endpoint(pc, ep_name);
}
esp_err_t protocomm_get_sec_version(protocomm_t *pc, int *sec_ver, uint8_t *sec_patch_ver)
{
if (pc == NULL || sec_ver == NULL || sec_patch_ver == NULL) {
return ESP_ERR_INVALID_ARG;
}
*sec_ver = pc->sec->ver;
*sec_patch_ver = pc->sec->patch_ver;
return ESP_OK;
}
+72 -12
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,7 +14,8 @@
#include <mbedtls/gcm.h>
#include <mbedtls/error.h>
#include <esp_random.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <protocomm_security.h>
#include <protocomm_security2.h>
@@ -24,6 +25,7 @@
#include "constants.pb-c.h"
#include "esp_srp.h"
#include "endian.h"
static const char *TAG = "security2";
@@ -33,13 +35,21 @@ ESP_EVENT_DEFINE_BASE(PROTOCOMM_SECURITY_SESSION_EVENT);
#define PUBLIC_KEY_LEN (384)
#define CLIENT_PROOF_LEN (64)
#define AES_GCM_KEY_LEN (256)
#define AES_GCM_IV_SIZE (16)
#define AES_GCM_IV_SIZE (12)
#define AES_GCM_TAG_LEN (16)
#define SESSION_ID_LEN (8)
#define SESSION_STATE_CMD0 0 /* Session is not setup: Initial State*/
#define SESSION_STATE_CMD1 1 /* Session is not setup: Cmd0 done */
#define SESSION_STATE_DONE 2 /* Session setup successful */
typedef struct aes_gcm_iv {
uint8_t session_id[SESSION_ID_LEN];
uint32_t counter;
} aes_gcm_iv_t;
static_assert(sizeof(aes_gcm_iv_t) == AES_GCM_IV_SIZE, "Invalid size of AES GCM IV");
typedef struct session {
/* Session data */
uint32_t id;
@@ -65,6 +75,12 @@ static void hexdump(const char *msg, char *buf, int len)
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG);
}
static inline void sec2_gcm_iv_counter_increment(uint8_t *iv_buf)
{
aes_gcm_iv_t *iv = (aes_gcm_iv_t *) iv_buf;
iv->counter = htobe32(be32toh(iv->counter) + 1);
}
static esp_err_t sec2_new_session(protocomm_security_handle_t handle, uint32_t session_id);
static esp_err_t handle_session_command0(session_t *cur_session,
@@ -172,19 +188,21 @@ static esp_err_t handle_session_command0(session_t *cur_session,
out->payload_case = SEC2_PAYLOAD__PAYLOAD_SR0;
out->sr0 = out_resp;
resp->sec_ver = SEC_SCHEME_VERSION__SecScheme2;
resp->proto_case = SESSION_DATA__PROTO_SEC2;
resp->sec2 = out;
cur_session->username_len = in->sc0->client_username.len;
cur_session->username = malloc(cur_session->username_len);
if (!cur_session->username) {
ESP_LOGE(TAG, "Failed to allocate memory!");
esp_srp_free(cur_session->srp_hd);
free(out);
free(out_resp);
return ESP_ERR_NO_MEM;
}
memcpy(cur_session->username, in->sc0->client_username.data, in->sc0->client_username.len);
resp->sec_ver = SEC_SCHEME_VERSION__SecScheme2;
resp->proto_case = SESSION_DATA__PROTO_SEC2;
resp->sec2 = out;
cur_session->state = SESSION_STATE_CMD1;
ESP_LOGD(TAG, "Session setup phase1 done");
@@ -224,15 +242,35 @@ static esp_err_t handle_session_command1(session_t *cur_session,
}
hexdump("Device proof", device_proof, CLIENT_PROOF_LEN);
/* Initialize crypto context */
mbedtls_gcm_init(&cur_session->ctx_gcm);
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
/* Considering the protocomm component is only used after RF ( Wifi/Bluetooth ) is enabled.
* Hence, we can be sure that the RNG generates true random numbers */
esp_fill_random(&cur_session->iv, AES_GCM_IV_SIZE);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
int ret;
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to seed random number generator");
free(device_proof);
return ESP_FAIL;
}
aes_gcm_iv_t *iv = (aes_gcm_iv_t *) cur_session->iv;
ret = mbedtls_ctr_drbg_random(&ctr_drbg, iv->session_id, SESSION_ID_LEN);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate random number");
free(device_proof);
return ESP_FAIL;
}
/* Initialize counter value to 1 */
iv->counter = htobe32(0x1);
hexdump("Initialization vector", (char *)cur_session->iv, AES_GCM_IV_SIZE);
/* Initialize crypto context */
mbedtls_gcm_init(&cur_session->ctx_gcm);
mbed_err = mbedtls_gcm_setkey(&cur_session->ctx_gcm, MBEDTLS_CIPHER_ID_AES, (unsigned char *)cur_session->session_key, AES_GCM_KEY_LEN);
if (mbed_err != 0) {
ESP_LOGE(TAG, "Failure at mbedtls_gcm_setkey_enc with error code : -0x%x", -mbed_err);
@@ -429,6 +467,13 @@ static esp_err_t sec2_encrypt(protocomm_security_handle_t handle,
return ESP_ERR_INVALID_STATE;
}
aes_gcm_iv_t *iv = (aes_gcm_iv_t *) cur_session->iv;
if (be32toh(iv->counter) == 0) {
ESP_LOGE(TAG, "Invalid counter value, restart session");
return ESP_ERR_INVALID_STATE;
}
hexdump("Encrypt IV", (char *)cur_session->iv, AES_GCM_IV_SIZE);
*outlen = inlen + AES_GCM_TAG_LEN;
*outbuf = (uint8_t *) malloc(*outlen);
if (!*outbuf) {
@@ -446,6 +491,9 @@ static esp_err_t sec2_encrypt(protocomm_security_handle_t handle,
}
memcpy(*outbuf + inlen, gcm_tag, AES_GCM_TAG_LEN);
/* Increment counter value for next operation */
sec2_gcm_iv_counter_increment(cur_session->iv);
return ESP_OK;
}
@@ -469,6 +517,13 @@ static esp_err_t sec2_decrypt(protocomm_security_handle_t handle,
return ESP_ERR_INVALID_STATE;
}
aes_gcm_iv_t *iv = (aes_gcm_iv_t *) cur_session->iv;
if (be32toh(iv->counter) == 0) {
ESP_LOGE(TAG, "Invalid counter value, restart session");
return ESP_ERR_INVALID_STATE;
}
hexdump("Decrypt IV", (char *)cur_session->iv, AES_GCM_IV_SIZE);
*outlen = inlen - AES_GCM_TAG_LEN;
*outbuf = (uint8_t *) malloc(*outlen);
if (!*outbuf) {
@@ -482,6 +537,10 @@ static esp_err_t sec2_decrypt(protocomm_security_handle_t handle,
ESP_LOGE(TAG, "Failed at mbedtls_gcm_auth_decrypt : %d", ret);
return ESP_FAIL;
}
/* Increment counter value for next operation */
sec2_gcm_iv_counter_increment(cur_session->iv);
return ESP_OK;
}
@@ -543,6 +602,7 @@ static esp_err_t sec2_req_handler(protocomm_security_handle_t handle,
const protocomm_security_t protocomm_security2 = {
.ver = 2,
.patch_ver = 1,
.init = sec2_init,
.cleanup = sec2_cleanup,
.new_transport_session = sec2_new_session,
+24 -15
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -260,7 +260,15 @@ static cJSON* wifi_prov_get_info_json(void)
/* Version field */
cJSON_AddStringToObject(prov_info_json, "ver", prov_ctx->mgr_info.version);
/* Security field */
int sec_ver = 0;
uint8_t sec_patch_ver = 0;
protocomm_get_sec_version(prov_ctx->pc, &sec_ver, &sec_patch_ver);
assert(sec_ver == prov_ctx->security);
cJSON_AddNumberToObject(prov_info_json, "sec_ver", prov_ctx->security);
cJSON_AddNumberToObject(prov_info_json, "sec_patch_ver", sec_patch_ver);
/* Capabilities field */
cJSON_AddItemToObject(prov_info_json, "cap", prov_capabilities);
@@ -307,19 +315,6 @@ static esp_err_t wifi_prov_mgr_start_service(const char *service_name, const cha
return ret;
}
/* Set version information / capabilities of provisioning service and application */
cJSON *version_json = wifi_prov_get_info_json();
char *version_str = cJSON_Print(version_json);
ret = protocomm_set_version(prov_ctx->pc, "proto-ver", version_str);
free(version_str);
cJSON_Delete(version_json);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set version endpoint");
scheme->prov_stop(prov_ctx->pc);
protocomm_delete(prov_ctx->pc);
return ret;
}
/* Set protocomm security type for endpoint */
if (prov_ctx->security == 0) {
#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0
@@ -356,6 +351,21 @@ static esp_err_t wifi_prov_mgr_start_service(const char *service_name, const cha
return ret;
}
/* Set version information / capabilities of provisioning service and application */
cJSON *version_json = wifi_prov_get_info_json();
char *version_str = cJSON_Print(version_json);
ESP_LOGD(TAG, "version_str :%s:", version_str);
ret = protocomm_set_version(prov_ctx->pc, "proto-ver", version_str);
free(version_str);
cJSON_Delete(version_json);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set version endpoint");
scheme->prov_stop(prov_ctx->pc);
protocomm_delete(prov_ctx->pc);
return ret;
}
prov_ctx->wifi_prov_handlers = malloc(sizeof(wifi_prov_config_handlers_t));
ret = get_wifi_prov_handlers(prov_ctx->wifi_prov_handlers);
if (ret != ESP_OK) {
@@ -1651,7 +1661,6 @@ esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const
#endif
prov_ctx->security = security;
esp_timer_create_args_t wifi_connect_timer_conf = {
.callback = wifi_connect_timer_cb,
.arg = NULL,