mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user