From eb7ff34c891deb2aa3e748d77bd45b9941ad7c87 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Fri, 20 May 2022 09:58:07 +0530 Subject: [PATCH] protocomm: Added SRP6a implementation as the security version 2. 1) Rename srp component to esp_srp 2) Remove dependency on hkdf sha 3) Restructure protocomm component APIs to make them more flexible for allowing multiple security versions 4) esp_srp: convert API return type from int to esp_err_t 5) esp_srp: Formatting changes 6) Added mbedtls_gcm instead of aes_ctr Co-authored-by: Laukik hase --- .../esp_local_ctrl/src/esp_local_ctrl.c | 2 +- components/protocomm/CMakeLists.txt | 11 +- .../protocomm/include/common/protocomm.h | 37 +- .../include/security/protocomm_security.h | 58 +- .../include/security/protocomm_security2.h | 26 + components/protocomm/src/common/protocomm.c | 83 +-- .../protocomm/src/common/protocomm_priv.h | 22 +- .../protocomm/src/crypto/srp6a/esp_srp.c | 546 ++++++++++++++++++ .../protocomm/src/crypto/srp6a/esp_srp_mpi.c | 148 +++++ .../src/crypto/srp6a/include/esp_srp.h | 103 ++++ .../src/crypto/srp6a/include/esp_srp_mpi.h | 52 ++ components/protocomm/src/security/security0.c | 25 +- components/protocomm/src/security/security1.c | 23 +- components/protocomm/src/security/security2.c | 539 +++++++++++++++++ components/protocomm/test/test_protocomm.c | 20 +- .../include/wifi_provisioning/manager.h | 8 +- components/wifi_provisioning/src/manager.c | 56 +- .../wifi_prov_mgr/main/app_main.c | 33 +- .../wifi_prov_mgr/sdkconfig.defaults | 4 + tools/ci/check_copyright_ignore.txt | 4 - 20 files changed, 1642 insertions(+), 158 deletions(-) create mode 100644 components/protocomm/include/security/protocomm_security2.h create mode 100644 components/protocomm/src/crypto/srp6a/esp_srp.c create mode 100644 components/protocomm/src/crypto/srp6a/esp_srp_mpi.c create mode 100644 components/protocomm/src/crypto/srp6a/include/esp_srp.h create mode 100644 components/protocomm/src/crypto/srp6a/include/esp_srp_mpi.h create mode 100644 components/protocomm/src/security/security2.c diff --git a/components/esp_local_ctrl/src/esp_local_ctrl.c b/components/esp_local_ctrl/src/esp_local_ctrl.c index c99b96782b..3a8b9f74b0 100644 --- a/components/esp_local_ctrl/src/esp_local_ctrl.c +++ b/components/esp_local_ctrl/src/esp_local_ctrl.c @@ -164,7 +164,7 @@ esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config) break; } ret = protocomm_set_security(local_ctrl_inst_ctx->pc, "esp_local_ctrl/session", - proto_sec_handle, local_ctrl_inst_ctx->config.proto_sec.pop); + proto_sec_handle, local_ctrl_inst_ctx->config.proto_sec.pop, NULL); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to set session endpoint"); esp_local_ctrl_stop(); diff --git a/components/protocomm/CMakeLists.txt b/components/protocomm/CMakeLists.txt index 06bc742191..cd9245ae2f 100644 --- a/components/protocomm/CMakeLists.txt +++ b/components/protocomm/CMakeLists.txt @@ -1,18 +1,21 @@ set(include_dirs include/common - include/security - include/transports) -set(priv_include_dirs proto-c src/common) + include/security + include/transports) +set(priv_include_dirs proto-c src/common src/crypto/srp6a/include) set(srcs "src/common/protocomm.c" "src/security/security0.c" "src/security/security1.c" + "src/security/security2.c" "proto-c/constants.pb-c.c" "proto-c/sec0.pb-c.c" "proto-c/sec1.pb-c.c" "proto-c/sec2.pb-c.c" "proto-c/session.pb-c.c" "src/transports/protocomm_console.c" - "src/transports/protocomm_httpd.c") + "src/transports/protocomm_httpd.c" + "src/crypto/srp6a/esp_srp.c" + "src/crypto/srp6a/esp_srp_mpi.c") if(CONFIG_BT_ENABLED) if(CONFIG_BT_BLUEDROID_ENABLED) diff --git a/components/protocomm/include/common/protocomm.h b/components/protocomm/include/common/protocomm.h index b5c57992cc..e2704a3364 100644 --- a/components/protocomm/include/common/protocomm.h +++ b/components/protocomm/include/common/protocomm.h @@ -1,16 +1,8 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -189,13 +181,17 @@ esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t se * - An endpoint must be bound to a valid protocomm instance, * created using `protocomm_new()`. * - The choice of security can be any `protocomm_security_t` instance. - * Choices `protocomm_security0` and `protocomm_security1` are readily available. - * - * @param[in] pc Pointer to the protocomm instance - * @param[in] ep_name Endpoint identifier(name) string - * @param[in] sec Pointer to endpoint security instance - * @param[in] pop Pointer to proof of possession for authenticating a client + * Choices `protocomm_security0` and `protocomm_security1` and `protocomm_security2` are readily available. * + * @param[in] pc Pointer to the protocomm instance + * @param[in] ep_name Endpoint identifier(name) string + * @param[in] sec Pointer to endpoint security instance + * @param[in] sec_params Pointer to security params (NULL if not needed) + * The pointer should contain the security params struct + * of appropriate security version. + * For protocomm security version 1 and 2 + * sec_params should contain pointer to struct of type + * protocomm_security1_params_t and protocmm_security2_params_t respectively. * @return * - ESP_OK : Success * - ESP_FAIL : Error adding endpoint / Endpoint with this name already exists @@ -205,8 +201,7 @@ esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t se */ esp_err_t protocomm_set_security(protocomm_t *pc, const char *ep_name, const protocomm_security_t *sec, - const protocomm_security_pop_t *pop); - + const void *sec_params); /** * @brief Remove endpoint security for a protocomm instance * diff --git a/components/protocomm/include/security/protocomm_security.h b/components/protocomm/include/security/protocomm_security.h index 04ca8b3355..48bf06ab63 100644 --- a/components/protocomm/include/security/protocomm_security.h +++ b/components/protocomm/include/security/protocomm_security.h @@ -1,16 +1,8 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -21,9 +13,9 @@ extern "C" { #endif /** - * @brief Proof Of Possession for authenticating a secure session + * @brief Protocomm Security 1 parameters: Proof Of Possession */ -typedef struct protocomm_security_pop { +typedef struct protocomm_security1_params { /** * Pointer to buffer containing the proof of possession data */ @@ -33,7 +25,35 @@ typedef struct protocomm_security_pop { * Length (in bytes) of the proof of possession data */ uint16_t len; -} protocomm_security_pop_t; +} protocomm_security1_params_t; + +typedef protocomm_security1_params_t protocomm_security_pop_t __attribute__((deprecated("Use protocomm_security1_params_t instead"))); + +/** + * @brief Protocomm Security 2 parameters: Salt and Verifier + * + */ +typedef struct protocomm_security2_params { + /** + * Pointer to the buffer containing the salt + */ + const char *salt; + + /** + * Length (in bytes) of the salt + */ + uint16_t salt_len; + + /** + * Pointer to the buffer containing the verifier + */ + const char *verifier; + + /** + * Length (in bytes) of the verifier + */ + uint16_t verifier_len; +} protocomm_security2_params_t; typedef void * protocomm_security_handle_t; @@ -80,7 +100,7 @@ typedef struct protocomm_security { * request and establishing secure session */ esp_err_t (*security_req_handler)(protocomm_security_handle_t handle, - const protocomm_security_pop_t *pop, + const void *sec_params, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, @@ -92,7 +112,7 @@ typedef struct protocomm_security { esp_err_t (*encrypt)(protocomm_security_handle_t handle, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, - uint8_t *outbuf, ssize_t *outlen); + uint8_t **outbuf, ssize_t *outlen); /** * Function which implements the decryption algorithm @@ -100,7 +120,7 @@ typedef struct protocomm_security { esp_err_t (*decrypt)(protocomm_security_handle_t handle, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, - uint8_t *outbuf, ssize_t *outlen); + uint8_t **outbuf, ssize_t *outlen); } protocomm_security_t; #ifdef __cplusplus diff --git a/components/protocomm/include/security/protocomm_security2.h b/components/protocomm/include/security/protocomm_security2.h new file mode 100644 index 0000000000..26b652a3f2 --- /dev/null +++ b/components/protocomm/include/security/protocomm_security2.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Protocomm security version 2 implementation + * + * This is a full fledged security implementation using + * key exchange based on SRP6a (RFC 5054) + * and AES-GCM encryption/decryption + */ +extern const protocomm_security_t protocomm_security2; + +#ifdef __cplusplus +} +#endif diff --git a/components/protocomm/src/common/protocomm.c b/components/protocomm/src/common/protocomm.c index 9af6fe6f3c..8a6433170e 100644 --- a/components/protocomm/src/common/protocomm.c +++ b/components/protocomm/src/common/protocomm.c @@ -53,10 +53,9 @@ void protocomm_delete(protocomm_t *pc) if (pc->sec && pc->sec->cleanup) { pc->sec->cleanup(pc->sec_inst); } - if (pc->pop) { - free(pc->pop); + if (pc->sec_params) { + free(pc->sec_params); } - free(pc); } @@ -199,14 +198,9 @@ esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t se } else if (ep->flag & REQ_EP) { if (pc->sec && pc->sec->decrypt) { /* Decrypt the data first */ - uint8_t *dec_inbuf = (uint8_t *) malloc(inlen); - if (!dec_inbuf) { - ESP_LOGE(TAG, "Failed to allocate decrypt buf len %d", inlen); - return ESP_ERR_NO_MEM; - } - - ssize_t dec_inbuf_len = inlen; - ret = pc->sec->decrypt(pc->sec_inst, session_id, inbuf, inlen, dec_inbuf, &dec_inbuf_len); + ssize_t dec_inbuf_len = 0; + uint8_t *dec_inbuf = NULL; + ret = pc->sec->decrypt(pc->sec_inst, session_id, inbuf, inlen, &dec_inbuf, &dec_inbuf_len); if (ret != ESP_OK) { ESP_LOGE(TAG, "Decryption of response failed for endpoint %s", ep_name); free(dec_inbuf); @@ -229,17 +223,10 @@ esp_err_t protocomm_req_handle(protocomm_t *pc, const char *ep_name, uint32_t se /* We don't need decrypted data anymore */ free(dec_inbuf); - /* Encrypt response to be sent back */ - uint8_t *enc_resp = (uint8_t *) malloc(plaintext_resp_len); - if (!enc_resp) { - ESP_LOGE(TAG, "Failed to allocate encrypt buf len %d", plaintext_resp_len); - free(plaintext_resp); - return ESP_ERR_NO_MEM; - } - - ssize_t enc_resp_len = plaintext_resp_len; + uint8_t *enc_resp = NULL; + ssize_t enc_resp_len = 0; ret = pc->sec->encrypt(pc->sec_inst, session_id, plaintext_resp, plaintext_resp_len, - enc_resp, &enc_resp_len); + &enc_resp, &enc_resp_len); if (ret != ESP_OK) { ESP_LOGE(TAG, "Encryption of response failed for endpoint %s", ep_name); @@ -278,7 +265,7 @@ static int protocomm_common_security_handler(uint32_t session_id, if (pc->sec && pc->sec->security_req_handler) { return pc->sec->security_req_handler(pc->sec_inst, - pc->pop, session_id, + pc->sec_params, session_id, inbuf, inlen, outbuf, outlen, priv_data); @@ -289,7 +276,7 @@ static int protocomm_common_security_handler(uint32_t session_id, esp_err_t protocomm_set_security(protocomm_t *pc, const char *ep_name, const protocomm_security_t *sec, - const protocomm_security_pop_t *pop) + const void *sec_params) { if ((pc == NULL) || (ep_name == NULL) || (sec == NULL)) { return ESP_ERR_INVALID_ARG; @@ -317,22 +304,42 @@ esp_err_t protocomm_set_security(protocomm_t *pc, const char *ep_name, } pc->sec = sec; - if (pop) { - pc->pop = malloc(sizeof(protocomm_security_pop_t)); - if (pc->pop == NULL) { - ESP_LOGE(TAG, "Error allocating Proof of Possession"); - if (pc->sec && pc->sec->cleanup) { - pc->sec->cleanup(pc->sec_inst); - pc->sec_inst = NULL; - pc->sec = NULL; + /* sec params is not needed and thus checked in case of security 0 */ + if (pc->sec->ver == 1) { + if (sec_params) { + pc->sec_params = calloc(1, sizeof(protocomm_security1_params_t)); + if (pc->sec_params == NULL) { + ESP_LOGE(TAG, "Error allocating memory for security1 params"); + ret = ESP_ERR_NO_MEM; + goto cleanup; } - - protocomm_remove_endpoint(pc, ep_name); - return ESP_ERR_NO_MEM; + memcpy((void *)pc->sec_params, sec_params, sizeof(protocomm_security1_params_t)); + } + } else if (pc->sec->ver == 2) { + if (sec_params) { + pc->sec_params = calloc(1, sizeof(protocomm_security2_params_t)); + if (pc->sec_params == NULL) { + ESP_LOGE(TAG, "Error allocating memory for security2 params"); + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + memcpy((void *)pc->sec_params, sec_params, sizeof(protocomm_security2_params_t)); + } else { + ESP_LOGE(TAG, "Security params cannot be null"); + ret = ESP_ERR_INVALID_ARG; + goto cleanup; } - memcpy((void *)pc->pop, pop, sizeof(protocomm_security_pop_t)); } return ESP_OK; + +cleanup: + if (pc->sec && pc->sec->cleanup) { + pc->sec->cleanup(pc->sec_inst); + pc->sec_inst = NULL; + pc->sec = NULL; + } + protocomm_remove_endpoint(pc, ep_name); + return ret; } esp_err_t protocomm_unset_security(protocomm_t *pc, const char *ep_name) @@ -347,9 +354,9 @@ esp_err_t protocomm_unset_security(protocomm_t *pc, const char *ep_name) pc->sec = NULL; } - if (pc->pop) { - free(pc->pop); - pc->pop = NULL; + if (pc->sec_params) { + free(pc->sec_params); + pc->sec_params = NULL; } return protocomm_remove_endpoint(pc, ep_name); diff --git a/components/protocomm/src/common/protocomm_priv.h b/components/protocomm/src/common/protocomm_priv.h index 0757562049..1de6b468dc 100644 --- a/components/protocomm/src/common/protocomm_priv.h +++ b/components/protocomm/src/common/protocomm_priv.h @@ -1,16 +1,8 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -68,8 +60,8 @@ struct protocomm { /* Handle to the security layer instance */ protocomm_security_handle_t sec_inst; - /* Pointer to proof of possession object */ - protocomm_security_pop_t *pop; + /* Pointer to security params */ + void *sec_params; /* Head of the singly linked list for storing endpoint handlers */ SLIST_HEAD(eptable_t, protocomm_ep) endpoints; diff --git a/components/protocomm/src/crypto/srp6a/esp_srp.c b/components/protocomm/src/crypto/srp6a/esp_srp.c new file mode 100644 index 0000000000..79159bc41f --- /dev/null +++ b/components/protocomm/src/crypto/srp6a/esp_srp.c @@ -0,0 +1,546 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +// This file contains SRP6a implementation based on RFC 5054 +#include + +#include "esp_log.h" +#include "esp_err.h" + +#include +#include "esp_srp_mpi.h" +#include "esp_srp.h" + +#define SHA512_HASH_SZ 64 + +static const char *TAG = "srp6a"; + +static void hexdump_mpi(const char *name, esp_mpi_t *bn) +{ + int len = 0; + char *str = esp_mpi_to_bin(bn, &len); + if (str) { + ESP_LOGD(TAG, "%s ->", name); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, str, len, ESP_LOG_DEBUG); + free(str); + } +} + +/************************* SRP Stuff *************************/ +static const char N_3072[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const char g_3072[] = { 5 }; + + +esp_err_t esp_srp_init(esp_srp_handle_t *hd, esp_ng_type_t ng) +{ + if (hd->allocated) { + esp_srp_free(hd); + } + + memset(hd, 0, sizeof(*hd)); + hd->allocated = 1; + + hd->ctx = esp_mpi_ctx_new(); + if (! hd->ctx) { + goto error; + } + if (ng != ESP_NG_3072) { + goto error; + } + + hd->n = esp_mpi_new_from_bin(N_3072, sizeof(N_3072)); + hd->bytes_n = N_3072; + hd->len_n = sizeof(N_3072); + if (! hd->n) { + goto error; + } + + hd->g = esp_mpi_new_from_bin(g_3072, sizeof(g_3072)); + hd->bytes_g = g_3072; + hd->len_g = sizeof(g_3072); + if (! hd->g) { + goto error; + } + hd->type = ng; + return ESP_OK; +error: + esp_srp_free(hd); + return ESP_FAIL; +} + +void esp_srp_free(esp_srp_handle_t *hd) +{ + if (hd->allocated != 1) { + return; + } + + if (hd->ctx) { + esp_mpi_ctx_free(hd->ctx); + } + if (hd->n) { + esp_mpi_free(hd->n); + } + if (hd->g) { + esp_mpi_free(hd->g); + } + if (hd->s) { + esp_mpi_free(hd->s); + } + if (hd->bytes_s) { + free(hd->bytes_s); + } + if (hd->v) { + esp_mpi_free(hd->v); + } + if (hd->B) { + esp_mpi_free(hd->B); + } + if (hd->bytes_B) { + free(hd->bytes_B); + } + if (hd->b) { + esp_mpi_free(hd->b); + } + if (hd->A) { + esp_mpi_free(hd->A); + } + if (hd->bytes_A) { + free(hd->bytes_A); + } + if (hd->session_key) { + free(hd->session_key); + } + memset(hd, 0, sizeof(*hd)); +} + +static esp_mpi_t *calculate_x(char *bytes_salt, int salt_len, const char *username, int username_len, const char *pass, int pass_len) +{ + unsigned char digest[SHA512_HASH_SZ]; + mbedtls_sha512_context ctx; + ESP_LOGD(TAG, "Username: %s | Passphrase: %s | Passphrase length: %d", username, pass, pass_len); + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + mbedtls_sha512_update(&ctx, (unsigned char *)username, username_len); + mbedtls_sha512_update(&ctx, (unsigned char *)":", 1); + mbedtls_sha512_update(&ctx, (unsigned char *)pass, pass_len); + mbedtls_sha512_finish(&ctx, digest); + + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + mbedtls_sha512_update(&ctx, (unsigned char *)bytes_salt, salt_len); + mbedtls_sha512_update(&ctx, digest, sizeof(digest)); + mbedtls_sha512_finish(&ctx, digest); + mbedtls_sha512_free(&ctx); + + return esp_mpi_new_from_bin((char *)digest, sizeof(digest)); +} + +static esp_mpi_t *calculate_padded_hash(esp_srp_handle_t *hd, const char *a, int len_a, const char *b, int len_b) +{ + unsigned char digest[SHA512_HASH_SZ]; + mbedtls_sha512_context ctx; + int pad_len; + char *s = NULL; + + if (len_a > len_b) { + pad_len = hd->len_n - len_b; + } else { + pad_len = hd->len_n - len_a; + } + + if (pad_len) { + s = malloc(pad_len); + if (s) { + memset(s, 0, pad_len); + } + } + + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + /* PAD (a) */ + if (s && (len_a != hd->len_n)) { + mbedtls_sha512_update(&ctx, (unsigned char *)s, hd->len_n - len_a); + } + + mbedtls_sha512_update(&ctx, (unsigned char *)a, len_a); + + /* PAD (b) */ + if (s && (len_b != hd->len_n)) { + mbedtls_sha512_update(&ctx, (unsigned char *)s, hd->len_n - len_b); + } + + mbedtls_sha512_update(&ctx, (unsigned char *)b, len_b); + + mbedtls_sha512_finish(&ctx, digest); + mbedtls_sha512_free(&ctx); + + if (s) { + free(s); + } + + return esp_mpi_new_from_bin((char *)digest, sizeof(digest)); +} + +/* k = SHA (N, PAD(g)) + * + * https://tools.ietf.org/html/draft-ietf-tls-srp-08 + */ +static esp_mpi_t *calculate_k(esp_srp_handle_t *hd) +{ + return calculate_padded_hash(hd, hd->bytes_n, hd->len_n, hd->bytes_g, hd->len_g); +} + +static esp_mpi_t *calculate_u(esp_srp_handle_t *hd, char *A, int len_A) +{ + return calculate_padded_hash(hd, A, len_A, hd->bytes_B, hd->len_B); +} + +esp_err_t __esp_srp_srv_pubkey(esp_srp_handle_t *hd, char **bytes_B, int *len_B) +{ + esp_mpi_t *k = calculate_k(hd); + esp_mpi_t *kv = NULL; + esp_mpi_t *gb = NULL; + if (!k) { + goto error; + } + hexdump_mpi("k", k); + + hd->b = esp_mpi_new(); + if (!hd->b) { + goto error; + } + esp_mpi_get_rand(hd->b, 256, -1, 0); + hexdump_mpi("b", hd->b); + + /* B = kv + g^b */ + kv = esp_mpi_new(); + gb = esp_mpi_new(); + hd->B = esp_mpi_new(); + if (!kv || !gb || ! hd->B) { + goto error; + } + esp_mpi_a_mul_b_mod_c(kv, k, hd->v, hd->n, hd->ctx); + esp_mpi_a_exp_b_mod_c(gb, hd->g, hd->b, hd->n, hd->ctx); + esp_mpi_a_add_b_mod_c(hd->B, kv, gb, hd->n, hd->ctx); + hd->bytes_B = esp_mpi_to_bin(hd->B, len_B); + hd->len_B = *len_B; + *bytes_B = hd->bytes_B; + + esp_mpi_free(k); + esp_mpi_free(kv); + esp_mpi_free(gb); + return ESP_OK; +error: + if (k) { + esp_mpi_free(k); + } + if (kv) { + esp_mpi_free(kv); + } + if (gb) { + esp_mpi_free(gb); + } + if (hd->B) { + esp_mpi_free(hd->B); + hd->B = NULL; + } + if (hd->b) { + esp_mpi_free(hd->b); + hd->b = NULL; + } + return ESP_FAIL; +} + +esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int username_len, const char *pass, int pass_len, int salt_len, + char **bytes_B, int *len_B, char **bytes_salt) +{ + /* Get Salt */ + int str_salt_len; + esp_mpi_t *x = NULL; + hd->s = esp_mpi_new(); + if (! hd->s) { + goto error; + } + + esp_mpi_get_rand(hd->s, 8 * salt_len, -1, 0); + *bytes_salt = esp_mpi_to_bin(hd->s, &str_salt_len); + if (! *bytes_salt) { + goto error; + } + + hd->bytes_s = *bytes_salt; + hd->len_s = salt_len; + ESP_LOGD(TAG, "Salt ->"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, *bytes_salt, str_salt_len, ESP_LOG_DEBUG); + + /* Calculate X which is simply a hash for all these things */ + x = calculate_x(*bytes_salt, str_salt_len, username, username_len, pass, pass_len); + if (! x) { + goto error; + } + hexdump_mpi("x", x); + + /* v = g^x % N */ + hd->v = esp_mpi_new(); + if (! hd->v) { + goto error; + } + esp_mpi_a_exp_b_mod_c(hd->v, hd->g, x, hd->n, hd->ctx); + hexdump_mpi("Verifier", hd->v); + + if (__esp_srp_srv_pubkey(hd, bytes_B, len_B) < 0 ) { + goto error; + } + + esp_mpi_free(x); + return ESP_OK; + +error: + if (hd->s) { + esp_mpi_free(hd->s); + hd->s = NULL; + } + if (*bytes_salt) { + free(*bytes_salt); + *bytes_salt = NULL; + hd->bytes_s = NULL; + hd->len_s = 0; + } + if (x) { + esp_mpi_free(x); + x = NULL; + } + if (hd->v) { + esp_mpi_free(hd->v); + hd->v = NULL; + } + return ESP_FAIL; +} + +esp_err_t esp_srp_srv_pubkey_from_salt_verifier(esp_srp_handle_t *hd, char **bytes_B, int *len_B) +{ + return __esp_srp_srv_pubkey(hd, bytes_B, len_B); +} + +esp_err_t esp_srp_set_salt_verifier(esp_srp_handle_t *hd, const char *salt, int salt_len, + const char *verifier, int verifier_len) +{ + hd->bytes_s = malloc(salt_len); + if (!hd->bytes_s) { + goto error; + } + memcpy(hd->bytes_s, salt, salt_len); + hd->len_s = salt_len; + + hd->s = esp_mpi_new_from_bin(salt, salt_len); + if (!hd->s) { + goto error; + } + + hd->v = esp_mpi_new_from_bin(verifier, verifier_len); + if (!hd->v) { + goto error; + } + return ESP_OK; + +error: + if (hd->bytes_s) { + free(hd->bytes_s); + hd->bytes_s = NULL; + hd->len_s = 0; + } + if (hd->s) { + esp_mpi_free(hd->s); + hd->s = NULL; + } + if (hd->v) { + esp_mpi_free(hd->v); + hd->v = NULL; + } + return ESP_FAIL; +} + +esp_err_t esp_srp_get_session_key(esp_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, int *len_key) +{ + esp_mpi_t *u = NULL; + esp_mpi_t *vu = NULL; + esp_mpi_t *avu = NULL; + esp_mpi_t *S = NULL; + + char *bytes_S; + int len_S; + + u = vu = avu = S = NULL; + bytes_S = NULL; + + hd->bytes_A = malloc(len_A); + if (! hd->bytes_A) { + goto error; + } + memcpy(hd->bytes_A, bytes_A, len_A); + hd->len_A = len_A; + + hd->A = esp_mpi_new_from_bin(bytes_A, len_A); + if (! hd->A) { + goto error; + } + u = calculate_u(hd, bytes_A, len_A); + if (! u) { + goto error; + } + hexdump_mpi("u", u); + + /* S = (A v^u)^b */ + vu = esp_mpi_new(); + avu = esp_mpi_new(); + S = esp_mpi_new(); + if (!vu || !avu || !S ) { + goto error; + } + + esp_mpi_a_exp_b_mod_c(vu, hd->v, u, hd->n, hd->ctx); + esp_mpi_a_mul_b_mod_c(avu, hd->A, vu, hd->n, hd->ctx); + esp_mpi_a_exp_b_mod_c(S, avu, hd->b, hd->n, hd->ctx); + hexdump_mpi("S", S); + + bytes_S = esp_mpi_to_bin(S, &len_S); + hd->session_key = malloc(SHA512_HASH_SZ); + if (!hd->session_key || ! bytes_S) { + goto error; + } + + mbedtls_sha512((unsigned char *)bytes_S, len_S, (unsigned char *)hd->session_key, 0); + *bytes_key = hd->session_key; + *len_key = SHA512_HASH_SZ; + + free(bytes_S); + esp_mpi_free(vu); + esp_mpi_free(avu); + esp_mpi_free(S); + esp_mpi_free(u); + return ESP_OK; +error: + if (bytes_S) { + free(bytes_S); + } + if (vu) { + esp_mpi_free(vu); + } + if (avu) { + esp_mpi_free(avu); + } + if (S) { + esp_mpi_free(S); + } + if (u) { + esp_mpi_free(u); + } + if (hd->session_key) { + free(hd->session_key); + hd->session_key = NULL; + } + if (hd->A) { + esp_mpi_free(hd->A); + hd->A = NULL; + } + if (hd->bytes_A) { + free(hd->bytes_A); + hd->bytes_A = NULL; + } + return ESP_FAIL; +} + +esp_err_t esp_srp_exchange_proofs(esp_srp_handle_t *hd, char *username, uint16_t username_len, char *bytes_user_proof, char *bytes_host_proof) +{ + /* First calculate M */ + unsigned char hash_n[SHA512_HASH_SZ]; + unsigned char hash_g[SHA512_HASH_SZ]; + unsigned char hash_n_xor_g[SHA512_HASH_SZ]; + int i; + + unsigned char hash_I[SHA512_HASH_SZ]; + mbedtls_sha512((unsigned char *)username, username_len, (unsigned char *)hash_I, 0); + mbedtls_sha512((unsigned char *)hd->bytes_n, hd->len_n, (unsigned char *)hash_n, 0); + + int pad_len = hd->len_n - hd->len_g; + char *s = calloc(pad_len, sizeof(char)); + if (!s) { + return ESP_ERR_NO_MEM; + } + + mbedtls_sha512_context ctx; + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + mbedtls_sha512_update(&ctx, (unsigned char *)s, pad_len); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_g, hd->len_g); + mbedtls_sha512_finish(&ctx, hash_g); + mbedtls_sha512_free(&ctx); + + for (i = 0; i < SHA512_HASH_SZ; i++) { + hash_n_xor_g[i] = hash_n[i] ^ hash_g[i]; + } + + unsigned char digest[SHA512_HASH_SZ]; + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + mbedtls_sha512_update(&ctx, hash_n_xor_g, SHA512_HASH_SZ); + mbedtls_sha512_update(&ctx, hash_I, SHA512_HASH_SZ); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_s, hd->len_s); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_A, hd->len_A); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_B, hd->len_B); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->session_key, SHA512_HASH_SZ); + mbedtls_sha512_finish(&ctx, digest); + mbedtls_sha512_free(&ctx); + + ESP_LOGD(TAG, "M ->"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, (char *)digest, sizeof(digest), ESP_LOG_DEBUG); + + if (memcmp(bytes_user_proof, digest, SHA512_HASH_SZ) != 0) { + return ESP_FAIL; + } + + /* M is now validated, let's proceed to H(AMK) */ + mbedtls_sha512_init(&ctx); + mbedtls_sha512_starts(&ctx, 0); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->bytes_A, hd->len_A); + mbedtls_sha512_update(&ctx, digest, SHA512_HASH_SZ); + mbedtls_sha512_update(&ctx, (unsigned char *)hd->session_key, SHA512_HASH_SZ); + mbedtls_sha512_finish(&ctx, (unsigned char *)bytes_host_proof); + mbedtls_sha512_free(&ctx); + + ESP_LOGD(TAG, "AMK ->"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, (char *)bytes_host_proof, SHA512_HASH_SZ, ESP_LOG_DEBUG); + + if (s) { + free(s); + } + return ESP_OK; +} diff --git a/components/protocomm/src/crypto/srp6a/esp_srp_mpi.c b/components/protocomm/src/crypto/srp6a/esp_srp_mpi.c new file mode 100644 index 0000000000..c02cbab000 --- /dev/null +++ b/components/protocomm/src/crypto/srp6a/esp_srp_mpi.c @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_srp_mpi.h" + +esp_mpi_t *esp_mpi_new(void) +{ + esp_mpi_t *a = (esp_mpi_t *)malloc(sizeof (esp_mpi_t)); + if (!a) { + return NULL; + } + mbedtls_mpi_init(a); + return a; +} + +esp_mpi_t *esp_mpi_new_from_hex(const char *hex) +{ + esp_mpi_t *a = esp_mpi_new(); + if (!a) { + return NULL; + } + + int ret = mbedtls_mpi_read_string(a, 16, hex); + if (ret != 0) { + printf("mbedtls_mpi_read_string() failed, returned %x\n", ret); + return NULL; + } + return a; +} + +esp_mpi_t *esp_mpi_new_from_bin(const char *str, int str_len) +{ + esp_mpi_t *a = esp_mpi_new(); + if (!a) { + return NULL; + } + + int ret = mbedtls_mpi_read_binary(a, (unsigned char *)str, str_len); + if (ret != 0) { + printf("mbedtls_mpi_read_binary() failed, returned %x\n", ret); + return NULL; + } + return a; +} + +void esp_mpi_free(esp_mpi_t *bn) +{ + if (bn) { + mbedtls_mpi_free(bn); + free(bn); + } +} + +esp_mpi_ctx_t *esp_mpi_ctx_new(void) +{ + esp_mpi_t *bn = esp_mpi_new(); + return ( esp_mpi_ctx_t *)bn; +} + +void esp_mpi_ctx_free(esp_mpi_ctx_t *ctx) +{ + esp_mpi_free((esp_mpi_t *)ctx); +} + +unsigned int esp_mpi_sizeof(esp_mpi_t *bn) +{ + return mbedtls_mpi_size(bn); +} + +char *esp_mpi_to_bin(esp_mpi_t *bn, int *len) +{ + *len = esp_mpi_sizeof(bn); + char *p = malloc(*len); + if (!p) { + return NULL; + } + + int ret = mbedtls_mpi_write_binary(bn, (unsigned char *)p, *len); + if (ret != 0) { + printf("mbedtls_mpi_read_string() failed, returned %x\n", ret); + return NULL; + } + return p; +} +int esp_get_random(void *ctx, unsigned char *data, size_t len) +{ + (void) ctx; + esp_fill_random(data, len); + return 0; +} + +int esp_mpi_get_rand(esp_mpi_t *bn, int bits, int top, int bottom) +{ + (void) top; + (void) bottom; + return mbedtls_mpi_fill_random(bn, bits / 8, esp_get_random, NULL); +} + +int esp_mpi_a_exp_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx) +{ + return mbedtls_mpi_exp_mod(result, a, b, c, (esp_mpi_t *) ctx); +} + +int esp_mpi_a_mul_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx) +{ + (void) ctx; + int res; + mbedtls_mpi t; + mbedtls_mpi_init(&t); + res = mbedtls_mpi_mul_mpi(&t, a, b); + if (res != 0) { + printf("mbedtls_mpi_mul_mpi(), returned %x\n", res); + return res; + } + res = mbedtls_mpi_mod_mpi(result, &t, c); + if (res != 0) { + printf("mbedtls_mpi_mod_mpi() failed, returned %x\n", res); + return res; + } + mbedtls_mpi_free(&t); + + return res; +} + +int esp_mpi_a_add_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx) +{ + (void) ctx; + int res; + mbedtls_mpi t; + + mbedtls_mpi_init(&t); + res = mbedtls_mpi_add_mpi(&t, a, b); + if (res != 0) { + printf("mbedtls_mpi_add_mpi() failed, returned %x\n", res); + return res; + } + res = mbedtls_mpi_mod_mpi(result, &t, c); + if (res != 0) { + printf("mbedtls_mpi_mod_mpi(), returned %x\n", res); + return res; + } + mbedtls_mpi_free(&t); + + return res; +} diff --git a/components/protocomm/src/crypto/srp6a/include/esp_srp.h b/components/protocomm/src/crypto/srp6a/include/esp_srp.h new file mode 100644 index 0000000000..388fab7acb --- /dev/null +++ b/components/protocomm/src/crypto/srp6a/include/esp_srp.h @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "esp_srp_mpi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + /* SRP specific: + * N = 3072 bit large safe prime, + * g = generator */ + ESP_NG_3072 = 0, +} esp_ng_type_t; + +typedef struct esp_srp_handle { + int allocated; + esp_ng_type_t type; + esp_mpi_ctx_t *ctx; + + /* N + * the bytes_n simply points to the static array + */ + esp_mpi_t *n; + const char *bytes_n; + int len_n; + + /* g + * the bytes_g simply points to the static array + */ + esp_mpi_t *g; + const char *bytes_g; + int len_g; + + /* Salt */ + esp_mpi_t *s; + char *bytes_s; + int len_s; + /* Verifier */ + esp_mpi_t *v; + /* B */ + esp_mpi_t *B; + char *bytes_B; + int len_B; + /* b */ + esp_mpi_t *b; + /* A */ + esp_mpi_t *A; + char *bytes_A; + int len_A; + /* K - session key*/ + char *session_key; +} esp_srp_handle_t; + +int esp_srp_init(esp_srp_handle_t *hd, esp_ng_type_t ng); + +void esp_srp_free(esp_srp_handle_t *hd); + +/* Returns B (pub key) and salt + * + * *bytes_B MUST NOT BE FREED BY THE CALLER + * *bytes_salt MUST NOT BE FREE BY THE CALLER + * + */ +esp_err_t esp_srp_srv_pubkey(esp_srp_handle_t *hd, const char *username, int username_len, const char *pass, int pass_len, int salt_len, + char **bytes_B, int *len_B, char **bytes_salt); + +/* Set the Salt and Verifier pre-generated for a given password. + * This should be used only if the actual password is not available. + * The public key can then be generated using esp_srp_srv_pubkey_from_salt_verifier() + * and not esp_srp_srv_pubkey() + */ +esp_err_t esp_srp_set_salt_verifier(esp_srp_handle_t *hd, const char *salt, int salt_len, + const char *verifier, int verifier_len); + +/* Returns B (pub key) when the salt and verifier are set using esp_srp_set_salt_verifier() + * + * *bytes_B MUST NOT BE FREED BY THE CALLER + */ +esp_err_t esp_srp_srv_pubkey_from_salt_verifier(esp_srp_handle_t *hd, char **bytes_B, int *len_B); + +/* Returns bytes_key + * *bytes_key MUST NOT BE FREED BY THE CALLER + */ +esp_err_t esp_srp_get_session_key(esp_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, int *len_key); + +/* Exchange proofs + * Returns 1 if user's proof is ok. Also 1 when is returned, bytes_host_proof contains our proof. + * + * bytes_user_proof is parameter in + * bytes_host_proof is parameter out (should be SHA512_DIGEST_LENGTH) bytes in size + */ +esp_err_t esp_srp_exchange_proofs(esp_srp_handle_t *hd, char *username, uint16_t username_len, char *bytes_user_proof, char *bytes_host_proof); + +#ifdef __cplusplus +} +#endif diff --git a/components/protocomm/src/crypto/srp6a/include/esp_srp_mpi.h b/components/protocomm/src/crypto/srp6a/include/esp_srp_mpi.h new file mode 100644 index 0000000000..46f07bc9ee --- /dev/null +++ b/components/protocomm/src/crypto/srp6a/include/esp_srp_mpi.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "stdlib.h" +#include "string.h" +#include "stdio.h" + +#include "mbedtls/bignum.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "esp_random.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef mbedtls_mpi esp_mpi_t; +typedef esp_mpi_t esp_mpi_ctx_t; + +esp_mpi_t *esp_mpi_new(void); + +esp_mpi_t *esp_mpi_new_from_hex(const char *hex); + +esp_mpi_t *esp_mpi_new_from_bin(const char *str, int str_len); + +void esp_mpi_free(esp_mpi_t *bn); + +esp_mpi_ctx_t *esp_mpi_ctx_new(void); + +void esp_mpi_ctx_free(esp_mpi_ctx_t *ctx); + +unsigned int esp_mpi_sizeof(esp_mpi_t *bn); + +char *esp_mpi_to_bin(esp_mpi_t *bn, int *len); + +int esp_get_random(void *ctx, unsigned char *data, size_t len); + +int esp_mpi_get_rand(esp_mpi_t *bn, int bits, int top, int bottom); + +int esp_mpi_a_exp_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx); + +int esp_mpi_a_mul_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx); + +int esp_mpi_a_add_b_mod_c(esp_mpi_t *result, esp_mpi_t *a, esp_mpi_t *b, esp_mpi_t *c, esp_mpi_ctx_t *ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/protocomm/src/security/security0.c b/components/protocomm/src/security/security0.c index 11de0e824a..8eaecb09c4 100644 --- a/components/protocomm/src/security/security0.c +++ b/components/protocomm/src/security/security0.c @@ -1,16 +1,8 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include @@ -29,8 +21,7 @@ static const char* TAG = "security0"; static esp_err_t sec0_session_setup(uint32_t session_id, - SessionData *req, SessionData *resp, - const protocomm_security_pop_t *pop) + SessionData *req, SessionData *resp) { Sec0Payload *out = (Sec0Payload *) malloc(sizeof(Sec0Payload)); S0SessionResp *s0resp = (S0SessionResp *) malloc(sizeof(S0SessionResp)); @@ -66,7 +57,7 @@ static void sec0_session_setup_cleanup(uint32_t session_id, SessionData *resp) } static esp_err_t sec0_req_handler(protocomm_security_handle_t handle, - const protocomm_security_pop_t *pop, + const void *sec_params, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, @@ -88,7 +79,7 @@ static esp_err_t sec0_req_handler(protocomm_security_handle_t handle, } session_data__init(&resp); - ret = sec0_session_setup(session_id, req, &resp, pop); + ret = sec0_session_setup(session_id, req, &resp); if (ret != ESP_OK) { ESP_LOGE(TAG, "Session setup error %d", ret); session_data__free_unpacked(req, NULL); diff --git a/components/protocomm/src/security/security1.c b/components/protocomm/src/security/security1.c index dca71315bd..316747b673 100644 --- a/components/protocomm/src/security/security1.c +++ b/components/protocomm/src/security/security1.c @@ -37,7 +37,6 @@ which are undefined if the following flag is not defined */ #include #include #include -#include #include #include @@ -193,7 +192,7 @@ static esp_err_t sec1_new_session(protocomm_security_handle_t handle, uint32_t s static esp_err_t handle_session_command0(session_t *cur_session, uint32_t session_id, SessionData *req, SessionData *resp, - const protocomm_security_pop_t *pop) + const protocomm_security1_params_t *pop) { ESP_LOGD(TAG, "Request to handle setup0_command"); Sec1Payload *in = (Sec1Payload *) req->sec1; @@ -375,7 +374,7 @@ exit_cmd0: static esp_err_t sec1_session_setup(session_t *cur_session, uint32_t session_id, SessionData *req, SessionData *resp, - const protocomm_security_pop_t *pop) + const protocomm_security1_params_t *pop) { Sec1Payload *in = (Sec1Payload *) req->sec1; esp_err_t ret; @@ -502,17 +501,13 @@ static esp_err_t sec1_cleanup(protocomm_security_handle_t handle) static esp_err_t sec1_decrypt(protocomm_security_handle_t handle, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, - uint8_t *outbuf, ssize_t *outlen) + uint8_t **outbuf, ssize_t *outlen) { session_t *cur_session = (session_t *) handle; if (!cur_session) { return ESP_ERR_INVALID_ARG; } - if (*outlen < inlen) { - return ESP_ERR_INVALID_ARG; - } - if (!cur_session || cur_session->id != session_id) { ESP_LOGE(TAG, "Session with ID %d not found", session_id); return ESP_ERR_INVALID_STATE; @@ -524,8 +519,14 @@ static esp_err_t sec1_decrypt(protocomm_security_handle_t handle, } *outlen = inlen; + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "Failed to allocate encrypt/decrypt buf len %d", *outlen); + return ESP_ERR_NO_MEM; + } + int ret = mbedtls_aes_crypt_ctr(&cur_session->ctx_aes, inlen, &cur_session->nc_off, - cur_session->rand, cur_session->stb, inbuf, outbuf); + cur_session->rand, cur_session->stb, inbuf, *outbuf); if (ret != 0) { ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with error code : %d", ret); return ESP_FAIL; @@ -534,7 +535,7 @@ static esp_err_t sec1_decrypt(protocomm_security_handle_t handle, } static esp_err_t sec1_req_handler(protocomm_security_handle_t handle, - const protocomm_security_pop_t *pop, + const void *sec_params, uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, uint8_t **outbuf, ssize_t *outlen, @@ -567,7 +568,7 @@ static esp_err_t sec1_req_handler(protocomm_security_handle_t handle, } session_data__init(&resp); - ret = sec1_session_setup(cur_session, session_id, req, &resp, pop); + ret = sec1_session_setup(cur_session, session_id, req, &resp, (protocomm_security1_params_t *) sec_params); if (ret != ESP_OK) { ESP_LOGE(TAG, "Session setup error %d", ret); session_data__free_unpacked(req, NULL); diff --git a/components/protocomm/src/security/security2.c b/components/protocomm/src/security/security2.c new file mode 100644 index 0000000000..e13d2a7d09 --- /dev/null +++ b/components/protocomm/src/security/security2.c @@ -0,0 +1,539 @@ +/* + * SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "session.pb-c.h" +#include "sec2.pb-c.h" +#include "constants.pb-c.h" + +#include "esp_srp.h" + +static const char *TAG = "security2"; + +#define SALT_LEN (16) +#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_TAG_LEN (16) + +#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 session { + /* Session data */ + uint32_t id; + uint8_t state; + /* Currently fixing the salt length to 16, we may keep it flexible */ + char *username; + uint16_t username_len; + char *salt; + uint16_t salt_len; + char *verifier; + uint16_t verifier_len; + char *client_pubkey; + uint16_t client_pubkey_len; + char *session_key; + uint16_t session_key_len; + uint8_t iv[AES_GCM_IV_SIZE]; + /* mbedtls context data for AES-GCM */ + mbedtls_gcm_context ctx_gcm; + esp_srp_handle_t *srp_hd; +} session_t; + +static void hexdump(const char *msg, char *buf, int len) +{ + ESP_LOGD(TAG, "%s ->", msg); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG); +} + +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, + uint32_t session_id, + SessionData *req, SessionData *resp, + const protocomm_security2_params_t *sv) +{ + ESP_LOGD(TAG, "Request to handle setup0_command"); + Sec2Payload *in = (Sec2Payload *) req->sec2; + + if (cur_session->state != SESSION_STATE_CMD0) { + ESP_LOGW(TAG, "Invalid state of session %d (expected %d). Restarting session.", + SESSION_STATE_CMD0, cur_session->state); + sec2_new_session(cur_session, session_id); + } + + if (in->sc0->client_pubkey.len != PUBLIC_KEY_LEN) { + ESP_LOGE(TAG, "Invalid public key length"); + return ESP_ERR_INVALID_ARG; + } + + if (in->sc0->client_username.len <= 0) { + ESP_LOGE(TAG, "Invalid username"); + return ESP_ERR_INVALID_ARG; + } + + cur_session->username_len = in->sc0->client_username.len; + cur_session->username = calloc(cur_session->username_len, sizeof(char)); + if (!cur_session->username) { + ESP_LOGE(TAG, "Failed to allocate memory!"); + return ESP_ERR_NO_MEM; + } + memcpy(cur_session->username, in->sc0->client_username.data, in->sc0->client_username.len); + ESP_LOGD(TAG, "Username: %.*s", cur_session->username_len, cur_session->username); + + cur_session->client_pubkey = calloc(PUBLIC_KEY_LEN, sizeof(char)); + if (!cur_session->client_pubkey ) { + ESP_LOGE(TAG, "Failed to allocate memory!"); + return ESP_ERR_NO_MEM; + } + memcpy(cur_session->client_pubkey, in->sc0->client_pubkey.data, PUBLIC_KEY_LEN); + cur_session->client_pubkey_len = PUBLIC_KEY_LEN; + hexdump("Client Public Key", cur_session->client_pubkey, PUBLIC_KEY_LEN); + + /* Initialize mu srp context */ + cur_session->srp_hd = calloc(1, sizeof(esp_srp_handle_t)); + if (!cur_session->srp_hd) { + ESP_LOGE(TAG, "Failed to allocate security context!"); + return ESP_ERR_NO_MEM; + } + + if (esp_srp_init(cur_session->srp_hd, ESP_NG_3072) != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialise security context!"); + return ESP_FAIL; + } + + char *device_pubkey = NULL; + int device_pubkey_len = 0; + + cur_session->salt = (char *)sv->salt; + cur_session->salt_len = sv->salt_len; + cur_session->verifier = (char *)sv->verifier; + cur_session->verifier_len = sv->verifier_len; + ESP_LOGI(TAG, "Using salt and verifier to generate public key..."); + + if (sv != NULL && sv->salt != NULL && sv->salt_len != 0 && sv->verifier != NULL && sv->verifier_len != 0) { + if (esp_srp_set_salt_verifier(cur_session->srp_hd, cur_session->salt, cur_session->salt_len, cur_session->verifier, cur_session->verifier_len) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set salt and verifier!"); + return ESP_FAIL; + } + if (esp_srp_srv_pubkey_from_salt_verifier(cur_session->srp_hd, &device_pubkey, &device_pubkey_len) != ESP_OK) { + ESP_LOGE(TAG, "Failed to device public key!"); + return ESP_FAIL; + } + } + + hexdump("Device Public Key", device_pubkey, device_pubkey_len); + + if (esp_srp_get_session_key(cur_session->srp_hd, cur_session->client_pubkey, cur_session->client_pubkey_len, + &cur_session->session_key, (int *)&cur_session->session_key_len) != ESP_OK) { + ESP_LOGE(TAG, "Failed to generate device session key!"); + return ESP_FAIL; + } + hexdump("Session Key", cur_session->session_key, cur_session->session_key_len); + + + Sec2Payload *out = (Sec2Payload *) malloc(sizeof(Sec2Payload)); + S2SessionResp0 *out_resp = (S2SessionResp0 *) malloc(sizeof(S2SessionResp0)); + if (!out || !out_resp) { + ESP_LOGE(TAG, "Error allocating memory for response0"); + free(out); + free(out_resp); + return ESP_ERR_NO_MEM; + } + + sec2_payload__init(out); + s2_session_resp0__init(out_resp); + + out_resp->status = STATUS__Success; + + out_resp->device_pubkey.data = (uint8_t *)device_pubkey; + out_resp->device_pubkey.len = device_pubkey_len; + + out_resp->device_salt.data = (uint8_t *)cur_session->salt; + out_resp->device_salt.len = cur_session->salt_len; + + out->msg = SEC2_MSG_TYPE__S2Session_Response0; + 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->state = SESSION_STATE_CMD1; + + ESP_LOGD(TAG, "Session setup phase1 done"); + return ESP_OK; +} + +static esp_err_t handle_session_command1(session_t *cur_session, + uint32_t session_id, + SessionData *req, SessionData *resp) +{ + ESP_LOGD(TAG, "Request to handle setup1_command"); + Sec2Payload *in = (Sec2Payload *) req->sec2; + int mbed_err = -0x0001; + + if (cur_session->state != SESSION_STATE_CMD1) { + ESP_LOGE(TAG, "Invalid state of session %d (expected %d)", SESSION_STATE_CMD1, cur_session->state); + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_FALSE(in->sc1->client_proof.len == CLIENT_PROOF_LEN, ESP_FAIL, TAG, "The client proof length does not match"); + + hexdump("Client proof", (char * ) in->sc1->client_proof.data, in->sc1->client_proof.len); + + char *device_proof = calloc(CLIENT_PROOF_LEN, sizeof(char)); + if (!device_proof) { + ESP_LOGE(TAG, "Failed to allocate memory!"); + return ESP_ERR_NO_MEM; + } + + if (esp_srp_exchange_proofs(cur_session->srp_hd, cur_session->username, cur_session->username_len, (char * ) in->sc1->client_proof.data, device_proof) != ESP_OK) { + ESP_LOGE(TAG, "Failed to authenticate client proof!"); + return ESP_FAIL; + } + hexdump("Device proof", device_proof, CLIENT_PROOF_LEN); + + /* Initialize crypto context */ + mbedtls_gcm_init(&cur_session->ctx_gcm); + + /* 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); + + hexdump("Initialization vector", (char *)cur_session->iv, AES_GCM_IV_SIZE); + + 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); + mbedtls_gcm_free(&cur_session->ctx_gcm); + return ESP_FAIL; + } + + Sec2Payload *out = (Sec2Payload *) malloc(sizeof(Sec2Payload)); + S2SessionResp1 *out_resp = (S2SessionResp1 *) malloc(sizeof(S2SessionResp1)); + if (!out || !out_resp) { + ESP_LOGE(TAG, "Error allocating memory for response1"); + free(out); + free(out_resp); + mbedtls_gcm_free(&cur_session->ctx_gcm); + return ESP_ERR_NO_MEM; + } + + sec2_payload__init(out); + s2_session_resp1__init(out_resp); + out_resp->status = STATUS__Success; + + out_resp->device_proof.data = (uint8_t *)device_proof; + out_resp->device_proof.len = CLIENT_PROOF_LEN; + + out_resp->device_nonce.data = cur_session->iv; + out_resp->device_nonce.len = AES_GCM_IV_SIZE; + + out->msg = SEC2_MSG_TYPE__S2Session_Response1; + out->payload_case = SEC2_PAYLOAD__PAYLOAD_SR1; + out->sr1 = out_resp; + + resp->sec_ver = SEC_SCHEME_VERSION__SecScheme2; + resp->proto_case = SESSION_DATA__PROTO_SEC2; + resp->sec2 = out; + + cur_session->state = SESSION_STATE_DONE; + ESP_LOGD(TAG, "Secure session established successfully"); + return ESP_OK; +} + +static esp_err_t sec2_session_setup(session_t *cur_session, + uint32_t session_id, + SessionData *req, SessionData *resp, + const protocomm_security2_params_t *sv) +{ + Sec2Payload *in = (Sec2Payload *) req->sec2; + esp_err_t ret; + + if (!in) { + ESP_LOGE(TAG, "Empty session data"); + return ESP_ERR_INVALID_ARG; + } + + switch (in->msg) { + case SEC2_MSG_TYPE__S2Session_Command0: + ret = handle_session_command0(cur_session, session_id, req, resp, sv); + break; + case SEC2_MSG_TYPE__S2Session_Command1: + ret = handle_session_command1(cur_session, session_id, req, resp); + break; + default: + ESP_LOGE(TAG, "Invalid security message type"); + ret = ESP_ERR_INVALID_ARG; + } + + return ret; + +} + +static void sec2_session_setup_cleanup(session_t *cur_session, uint32_t session_id, SessionData *resp) +{ + Sec2Payload *out = resp->sec2; + + if (!out) { + return; + } + + switch (out->msg) { + case SEC2_MSG_TYPE__S2Session_Response0: { + S2SessionResp0 *out_resp0 = out->sr0; + if (out_resp0) { + free(out_resp0); + } + break; + } + case SEC2_MSG_TYPE__S2Session_Response1: { + S2SessionResp1 *out_resp1 = out->sr1; + if (out_resp1) { + free(out_resp1->device_proof.data); + free(out_resp1); + } + break; + } + default: + break; + } + free(out); + + return; +} + +static esp_err_t sec2_close_session(protocomm_security_handle_t handle, uint32_t session_id) +{ + session_t *cur_session = (session_t *) handle; + if (!cur_session) { + return ESP_ERR_INVALID_ARG; + } + + if (!cur_session || cur_session->id != session_id) { + ESP_LOGE(TAG, "Attempt to close invalid session"); + return ESP_ERR_INVALID_STATE; + } + + if (cur_session->state == SESSION_STATE_DONE) { + /* Free GCM context data */ + mbedtls_gcm_free(&cur_session->ctx_gcm); + } + + free(cur_session->username); + free(cur_session->client_pubkey); + + if (cur_session->srp_hd) { + esp_srp_free(cur_session->srp_hd); + free(cur_session->srp_hd); + } + + memset(cur_session, 0, sizeof(session_t)); + cur_session->id = -1; + return ESP_OK; +} + +static esp_err_t sec2_new_session(protocomm_security_handle_t handle, uint32_t session_id) +{ + session_t *cur_session = (session_t *) handle; + if (!cur_session) { + return ESP_ERR_INVALID_ARG; + } + + if (cur_session->id != -1) { + /* Only one session is allowed at a time */ + ESP_LOGE(TAG, "Closing old session with id %u", cur_session->id); + sec2_close_session(cur_session, session_id); + } + + cur_session->id = session_id; + return ESP_OK; +} + +static esp_err_t sec2_init(protocomm_security_handle_t *handle) +{ + if (!handle) { + return ESP_ERR_INVALID_ARG; + } + session_t *cur_session = (session_t *) calloc(1, sizeof(session_t)); + if (!cur_session) { + ESP_LOGE(TAG, "Error allocating new session"); + return ESP_ERR_NO_MEM; + } + cur_session->id = -1; + *handle = (protocomm_security_handle_t) cur_session; + return ESP_OK; +} + +static esp_err_t sec2_cleanup(protocomm_security_handle_t handle) +{ + session_t *cur_session = (session_t *) handle; + if (cur_session) { + sec2_close_session(handle, cur_session->id); + } + free(handle); + return ESP_OK; +} + +static esp_err_t sec2_encrypt(protocomm_security_handle_t handle, + uint32_t session_id, + const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen) +{ + session_t *cur_session = (session_t *) handle; + if (!cur_session) { + return ESP_ERR_INVALID_ARG; + } + + if (!cur_session || cur_session->id != session_id) { + ESP_LOGE(TAG, "Session with ID %d not found", session_id); + return ESP_ERR_INVALID_STATE; + } + + if (cur_session->state != SESSION_STATE_DONE) { + ESP_LOGE(TAG, "Secure session not established"); + return ESP_ERR_INVALID_STATE; + } + + *outlen = inlen + AES_GCM_TAG_LEN; + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "Failed to allocate encrypt buf len %d", *outlen); + return ESP_ERR_NO_MEM; + } + uint8_t gcm_tag[AES_GCM_TAG_LEN]; + + int ret = mbedtls_gcm_crypt_and_tag(&cur_session->ctx_gcm, MBEDTLS_GCM_ENCRYPT, inlen, cur_session->iv, + AES_GCM_IV_SIZE, NULL, 0, inbuf, + *outbuf, AES_GCM_TAG_LEN, gcm_tag); + if (ret != 0) { + ESP_LOGE(TAG, "Failed at mbedtls_gcm_crypt_and_tag with error code : %d", ret); + return ESP_FAIL; + } + memcpy(*outbuf + inlen, gcm_tag, AES_GCM_TAG_LEN); + + return ESP_OK; +} + +static esp_err_t sec2_decrypt(protocomm_security_handle_t handle, + uint32_t session_id, + const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen) +{ + session_t *cur_session = (session_t *) handle; + if (!cur_session) { + return ESP_ERR_INVALID_ARG; + } + + if (!cur_session || cur_session->id != session_id) { + ESP_LOGE(TAG, "Session with ID %d not found", session_id); + return ESP_ERR_INVALID_STATE; + } + + if (cur_session->state != SESSION_STATE_DONE) { + ESP_LOGE(TAG, "Secure session not established"); + return ESP_ERR_INVALID_STATE; + } + + *outlen = inlen - AES_GCM_TAG_LEN; + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "Failed to allocate decrypt buf len %d", *outlen); + return ESP_ERR_NO_MEM; + } + + int ret = mbedtls_gcm_auth_decrypt(&cur_session->ctx_gcm, inlen - AES_GCM_TAG_LEN, cur_session->iv, + AES_GCM_IV_SIZE, NULL, 0, inbuf + (inlen - AES_GCM_TAG_LEN), AES_GCM_TAG_LEN, inbuf, *outbuf); + if (ret != 0) { + ESP_LOGE(TAG, "Failed at mbedtls_gcm_auth_decrypt : %d", ret); + return ESP_FAIL; + } + return ESP_OK; +} + +static esp_err_t sec2_req_handler(protocomm_security_handle_t handle, + const void *sec_params, + uint32_t session_id, + const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, + void *priv_data) +{ + session_t *cur_session = (session_t *) handle; + if (!cur_session) { + ESP_LOGE(TAG, "Invalid session context data"); + return ESP_ERR_INVALID_ARG; + } + + if (session_id != cur_session->id) { + ESP_LOGE(TAG, "Invalid session ID : %d (expected %d)", session_id, cur_session->id); + return ESP_ERR_INVALID_STATE; + } + + SessionData *req; + SessionData resp; + esp_err_t ret; + + req = session_data__unpack(NULL, inlen, inbuf); + if (!req) { + ESP_LOGE(TAG, "Unable to unpack setup_req"); + return ESP_ERR_INVALID_ARG; + } + if (req->sec_ver != protocomm_security2.ver) { + ESP_LOGE(TAG, "Security version mismatch. Closing connection"); + session_data__free_unpacked(req, NULL); + return ESP_ERR_INVALID_ARG; + } + + session_data__init(&resp); + ret = sec2_session_setup(cur_session, session_id, req, &resp, (protocomm_security2_params_t *) sec_params); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Session setup error %d", ret); + session_data__free_unpacked(req, NULL); + return ESP_FAIL; + } + + resp.sec_ver = req->sec_ver; + session_data__free_unpacked(req, NULL); + + *outlen = session_data__get_packed_size(&resp); + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "System out of memory"); + return ESP_ERR_NO_MEM; + } + session_data__pack(&resp, *outbuf); + + sec2_session_setup_cleanup(cur_session, session_id, &resp); + return ESP_OK; +} + +const protocomm_security_t protocomm_security2 = { + .ver = 2, + .init = sec2_init, + .cleanup = sec2_cleanup, + .new_transport_session = sec2_new_session, + .close_transport_session = sec2_close_session, + .security_req_handler = sec2_req_handler, + .encrypt = sec2_encrypt, + .decrypt = sec2_decrypt, +}; diff --git a/components/protocomm/test/test_protocomm.c b/components/protocomm/test/test_protocomm.c index 16dfcc7f09..8fab4d1181 100644 --- a/components/protocomm/test/test_protocomm.c +++ b/components/protocomm/test/test_protocomm.c @@ -49,7 +49,7 @@ typedef struct { uint32_t id; uint8_t sec_ver; uint8_t weak; - const protocomm_security_pop_t *pop; + const protocomm_security1_params_t *pop; uint8_t device_pubkey[PUBLIC_KEY_LEN]; uint8_t client_pubkey[PUBLIC_KEY_LEN]; uint8_t sym_key[PUBLIC_KEY_LEN]; @@ -187,7 +187,7 @@ static esp_err_t verify_response0(session_t *session, SessionData *resp) } flip_endian(session->sym_key, PUBLIC_KEY_LEN); - const protocomm_security_pop_t *pop = session->pop; + const protocomm_security1_params_t *pop = session->pop; if (pop != NULL && pop->data != NULL && pop->len != 0) { ESP_LOGD(TAG, "Adding proof of possession"); uint8_t sha_out[PUBLIC_KEY_LEN]; @@ -638,7 +638,7 @@ esp_err_t test_req_handler (uint32_t session_id, return ESP_OK; } -static esp_err_t start_test_service(uint8_t sec_ver, const protocomm_security_pop_t *pop) +static esp_err_t start_test_service(uint8_t sec_ver, const protocomm_security1_params_t *pop) { test_pc = protocomm_new(); if (test_pc == NULL) { @@ -686,7 +686,7 @@ static esp_err_t test_security1_no_encryption (void) ESP_LOGI(TAG, "Starting Security 1 no encryption test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; @@ -753,7 +753,7 @@ static esp_err_t test_security1_session_overflow (void) ESP_LOGI(TAG, "Starting Security 1 session overflow test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; @@ -831,7 +831,7 @@ static esp_err_t test_security1_wrong_pop (void) ESP_LOGI(TAG, "Starting Security 1 wrong auth test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; @@ -862,7 +862,7 @@ static esp_err_t test_security1_wrong_pop (void) } const char *wrong_pop_data = "wrong pop"; - protocomm_security_pop_t wrong_pop = { + protocomm_security1_params_t wrong_pop = { .data = (const uint8_t *)wrong_pop_data, .len = strlen(wrong_pop_data) }; @@ -893,7 +893,7 @@ static esp_err_t test_security1_insecure_client (void) ESP_LOGI(TAG, "Starting Security 1 insecure client test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; @@ -945,7 +945,7 @@ static esp_err_t test_security1_weak_session (void) ESP_LOGI(TAG, "Starting Security 1 weak session test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; @@ -1057,7 +1057,7 @@ static esp_err_t test_security1 (void) ESP_LOGI(TAG, "Starting Sec1 test"); const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { + protocomm_security1_params_t pop = { .data = (const uint8_t *)pop_data, .len = strlen(pop_data) }; diff --git a/components/wifi_provisioning/include/wifi_provisioning/manager.h b/components/wifi_provisioning/include/wifi_provisioning/manager.h index 06532a8e51..16330de85d 100644 --- a/components/wifi_provisioning/include/wifi_provisioning/manager.h +++ b/components/wifi_provisioning/include/wifi_provisioning/manager.h @@ -199,7 +199,9 @@ typedef enum wifi_prov_security { * + proof of possession (pop) based authentication * + AES-CTR encryption */ - WIFI_PROV_SECURITY_1 + WIFI_PROV_SECURITY_1, + + WIFI_PROV_SECURITY_2 } wifi_prov_security_t; /** @@ -299,8 +301,8 @@ esp_err_t wifi_prov_mgr_is_provisioned(bool *provisioned); * - ESP_FAIL : Failed to start provisioning service * - ESP_ERR_INVALID_STATE : Provisioning manager not initialized or already started */ -esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop, - const char *service_name, const char *service_key); +esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop, const char *salt, + const char *verifier, const char *service_name, const char *service_key); /** * @brief Stop provisioning service diff --git a/components/wifi_provisioning/src/manager.c b/components/wifi_provisioning/src/manager.c index 65dc66eb50..acc16f882a 100644 --- a/components/wifi_provisioning/src/manager.c +++ b/components/wifi_provisioning/src/manager.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "wifi_provisioning_priv.h" @@ -93,6 +94,9 @@ struct wifi_prov_mgr_ctx { /* Pointer to proof of possession */ protocomm_security_pop_t pop; + /* Pointer to salt and verifier */ + protocomm_security_sv_t sv; + /* Handle for Provisioning Auto Stop timer */ esp_timer_handle_t autostop_timer; @@ -307,10 +311,13 @@ static esp_err_t wifi_prov_mgr_start_service(const char *service_name, const cha /* Set protocomm security type for endpoint */ if (prov_ctx->security == 0) { ret = protocomm_set_security(prov_ctx->pc, "prov-session", - &protocomm_security0, NULL); + &protocomm_security0, NULL, NULL); } else if (prov_ctx->security == 1) { ret = protocomm_set_security(prov_ctx->pc, "prov-session", - &protocomm_security1, &prov_ctx->pop); + &protocomm_security1, &prov_ctx->pop, NULL); + } else if (prov_ctx->security == 2) { + ret = protocomm_set_security(prov_ctx->pc, "prov-session", + &protocomm_security2, NULL, &prov_ctx->sv); } else { ESP_LOGE(TAG, "Unsupported protocomm security version %d", prov_ctx->security); ret = ESP_ERR_INVALID_ARG; @@ -1330,6 +1337,7 @@ void wifi_prov_mgr_deinit(void) if (!service_was_running && !prov_ctx) { ESP_LOGD(TAG, "Manager already de-initialized"); RELEASE_LOCK(prov_ctx_lock); + vSemaphoreDelete(prov_ctx_lock); return; } @@ -1380,10 +1388,12 @@ void wifi_prov_mgr_deinit(void) if (esp_event_post(WIFI_PROV_EVENT, WIFI_PROV_DEINIT, NULL, 0, portMAX_DELAY) != ESP_OK) { ESP_LOGE(TAG, "Failed to post event WIFI_PROV_DEINIT"); } + + vSemaphoreDelete(prov_ctx_lock); } -esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop, - const char *service_name, const char *service_key) +esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const char *pop, const char *salt, + const char *verifier, const char *service_name, const char *service_key) { uint8_t restore_wifi_flag = 0; @@ -1459,17 +1469,37 @@ esp_err_t wifi_prov_mgr_start_provisioning(wifi_prov_security_t security, const /* Initialize app data */ if (security == WIFI_PROV_SECURITY_0) { prov_ctx->mgr_info.capabilities.no_sec = true; - } else if (pop) { - prov_ctx->pop.len = strlen(pop); - prov_ctx->pop.data = malloc(prov_ctx->pop.len); - if (!prov_ctx->pop.data) { - ESP_LOGE(TAG, "Unable to allocate PoP data"); - ret = ESP_ERR_NO_MEM; + } else if (security == WIFI_PROV_SECURITY_1) { + if (pop) { + prov_ctx->pop.len = strlen(pop); + prov_ctx->pop.data = malloc(prov_ctx->pop.len); + if (!prov_ctx->pop.data) { + ESP_LOGE(TAG, "Unable to allocate PoP data"); + ret = ESP_ERR_NO_MEM; + goto err; + } + memcpy((void *)prov_ctx->pop.data, pop, prov_ctx->pop.len); + } else { + prov_ctx->mgr_info.capabilities.no_pop = true; + } + } else if (security == WIFI_PROV_SECURITY_2) { + if (salt != NULL && verifier != NULL) { + prov_ctx->sv.salt_len = 4; + prov_ctx->sv.verifier_len = 384; + prov_ctx->sv.salt = malloc(prov_ctx->sv.salt_len); + prov_ctx->sv.verifier = malloc(prov_ctx->sv.verifier_len); + if (!prov_ctx->sv.salt || !prov_ctx->sv.salt) { + ESP_LOGE(TAG, "Unable to allocate salt-verifier data"); + ret = ESP_ERR_NO_MEM; + goto err; + } + memcpy((void *)prov_ctx->sv.salt, salt, prov_ctx->sv.salt_len); + memcpy((void *)prov_ctx->sv.verifier, verifier, prov_ctx->sv.verifier_len); + } else { + ESP_LOGE(TAG, "Salt and verifier cannot be NULL!"); + ret = ESP_ERR_INVALID_ARG; goto err; } - memcpy((void *)prov_ctx->pop.data, pop, prov_ctx->pop.len); - } else { - prov_ctx->mgr_info.capabilities.no_pop = true; } prov_ctx->security = security; diff --git a/examples/provisioning/wifi_prov_mgr/main/app_main.c b/examples/provisioning/wifi_prov_mgr/main/app_main.c index 81925e2e7b..4e3900a431 100644 --- a/examples/provisioning/wifi_prov_mgr/main/app_main.c +++ b/examples/provisioning/wifi_prov_mgr/main/app_main.c @@ -32,6 +32,33 @@ static const char *TAG = "app"; +const char sec2_salt[4] = {0xc1, 0x85, 0x74, 0xfe}; + +const char sec2_verifier[384] = {0x88, 0x98, 0x2a, 0xd3, 0xb1, 0x8a, 0xf6, 0xe3, 0x00, 0x5d, 0x34, 0x63, 0x56, 0x73, 0x2a, 0x03, + 0x7b, 0x1c, 0xae, 0xe8, 0x2e, 0x59, 0x03, 0x48, 0x68, 0x00, 0xba, 0xa1, 0xb0, 0xff, 0x12, 0xca, + 0xa7, 0xc9, 0xff, 0x69, 0xaf, 0x7a, 0x1e, 0x53, 0x98, 0x15, 0x79, 0xd1, 0x11, 0x83, 0x68, 0xf2, + 0x8c, 0x7a, 0xcf, 0x8b, 0x1b, 0x25, 0x2e, 0x0c, 0xcb, 0xdc, 0x6a, 0x7c, 0xda, 0x2e, 0x6d, 0x5b, + 0xcc, 0xaa, 0x8c, 0x6e, 0x92, 0x07, 0xd8, 0x48, 0x08, 0xd1, 0xbb, 0x72, 0x80, 0x65, 0xfd, 0x7d, + 0x77, 0xfb, 0x76, 0x14, 0x62, 0x58, 0xdd, 0xc4, 0xbf, 0x6b, 0xe4, 0x48, 0x83, 0xc4, 0x0b, 0x83, + 0x16, 0xa7, 0xf5, 0x1c, 0xd5, 0x9a, 0x47, 0xbc, 0xb3, 0xcd, 0x7c, 0x15, 0x5c, 0x6d, 0x40, 0x6e, + 0xaf, 0xa6, 0x7c, 0x49, 0x12, 0x07, 0x74, 0xa3, 0xf3, 0x98, 0x8d, 0x12, 0x69, 0xbd, 0xbc, 0x29, + 0xe8, 0x2b, 0x03, 0x82, 0x50, 0x5e, 0xb2, 0x95, 0xbd, 0x7d, 0x36, 0x43, 0x20, 0xd1, 0x0f, 0xbc, + 0x0e, 0xd5, 0x92, 0xe4, 0xe2, 0x64, 0xe6, 0x90, 0x66, 0xf5, 0x27, 0xee, 0x7e, 0xf1, 0x02, 0x35, + 0x8c, 0xea, 0xe7, 0x96, 0x0b, 0x00, 0x57, 0x54, 0xfa, 0x9e, 0x08, 0x75, 0xb9, 0x36, 0x71, 0x5e, + 0x68, 0xb8, 0x66, 0xff, 0x0c, 0x1b, 0x2e, 0xbe, 0x23, 0x8b, 0x37, 0xc1, 0xd5, 0x3b, 0xf7, 0x7f, + 0xcf, 0xb7, 0x65, 0x09, 0xda, 0x2b, 0x01, 0xbb, 0xd0, 0x5e, 0xf6, 0xd3, 0x21, 0xae, 0x33, 0x3d, + 0xb7, 0xdf, 0xfb, 0xb6, 0x88, 0xbe, 0x9e, 0xb5, 0xef, 0xd8, 0x74, 0x58, 0x08, 0xef, 0x8a, 0x40, + 0xa2, 0xda, 0x63, 0xe1, 0xb9, 0x71, 0x21, 0x46, 0xae, 0x45, 0x1f, 0x76, 0x14, 0xbf, 0x74, 0xb6, + 0x2e, 0xe2, 0x81, 0x7e, 0x5e, 0x99, 0x15, 0x2a, 0xd7, 0x73, 0xbf, 0x0a, 0x00, 0xe8, 0xea, 0x3c, + 0xbe, 0xb7, 0x70, 0xb3, 0x63, 0xa2, 0x5c, 0xff, 0x37, 0xbd, 0x40, 0x9a, 0xa9, 0x6c, 0xde, 0x1e, + 0xc0, 0x73, 0xd3, 0x00, 0x00, 0x89, 0x78, 0x16, 0x3b, 0x86, 0x36, 0x99, 0x36, 0xa9, 0x7c, 0x92, + 0xa2, 0xea, 0x92, 0x37, 0x1f, 0x13, 0xd4, 0x1a, 0x88, 0x3d, 0x2b, 0x49, 0xb1, 0x27, 0xb6, 0x8c, + 0xa5, 0x54, 0xf7, 0xba, 0x1b, 0x3e, 0x13, 0xe4, 0x52, 0xfc, 0xa2, 0x92, 0xa1, 0x77, 0x21, 0x02, + 0x16, 0xd9, 0xd1, 0x52, 0xb5, 0x2a, 0x6a, 0xc7, 0xe6, 0x2d, 0x1c, 0x26, 0x33, 0x89, 0x28, 0x36, + 0xeb, 0xcb, 0x4b, 0x19, 0x77, 0x6a, 0xe7, 0x2e, 0x1a, 0x53, 0x3e, 0xe1, 0xf4, 0x12, 0x94, 0x2a, + 0x90, 0xca, 0x47, 0x65, 0xdc, 0xca, 0xc5, 0x5c, 0xb3, 0xea, 0x70, 0xf9, 0xf0, 0xe1, 0x17, 0xc3, + 0xfb, 0x9d, 0xbd, 0xf6, 0x27, 0x3d, 0xa9, 0xd0, 0x4c, 0xb7, 0x6f, 0xe6, 0x21, 0x25, 0x0f, 0x29}; + /* Signal Wi-Fi events on this event-group */ const int WIFI_CONNECTED_EVENT = BIT0; static EventGroupHandle_t wifi_event_group; @@ -254,7 +281,8 @@ void app_main(void) * using X25519 key exchange and proof of possession (pop) and AES-CTR * for encryption/decryption of messages. */ - wifi_prov_security_t security = WIFI_PROV_SECURITY_1; + // wifi_prov_security_t security = WIFI_PROV_SECURITY_1; + wifi_prov_security_t security = WIFI_PROV_SECURITY_2; /* Do we want a proof-of-possession (ignored if Security 0 is selected): * - this should be a string with length > 0 @@ -300,7 +328,8 @@ void app_main(void) */ wifi_prov_mgr_endpoint_create("custom-data"); /* Start provisioning service */ - ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key)); + // ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, pop, NULL, NULL, service_name, service_key)); + ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, NULL, sec2_salt, sec2_verifier, service_name, service_key)); /* The handler for the optional endpoint created above. * This call must be made after starting the provisioning, and only if the endpoint diff --git a/examples/provisioning/wifi_prov_mgr/sdkconfig.defaults b/examples/provisioning/wifi_prov_mgr/sdkconfig.defaults index 9125e679d1..c3c976440b 100644 --- a/examples/provisioning/wifi_prov_mgr/sdkconfig.defaults +++ b/examples/provisioning/wifi_prov_mgr/sdkconfig.defaults @@ -9,3 +9,7 @@ CONFIG_BT_NIMBLE_ENABLED=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +CONFIG_EXAMPLE_RESET_PROVISIONED=y +CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION=n +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index dc7a97620d..6357443c7d 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1072,8 +1072,6 @@ components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp components/openthread/include/esp_openthread.h components/openthread/include/esp_openthread_lock.h components/openthread/include/esp_openthread_netif_glue.h -components/protocomm/include/common/protocomm.h -components/protocomm/include/security/protocomm_security.h components/protocomm/include/security/protocomm_security0.h components/protocomm/include/security/protocomm_security1.h components/protocomm/include/transports/protocomm_console.h @@ -1093,8 +1091,6 @@ components/protocomm/python/sec0_pb2.py components/protocomm/python/sec1_pb2.py components/protocomm/python/sec2_pb2.py components/protocomm/python/session_pb2.py -components/protocomm/src/common/protocomm_priv.h -components/protocomm/src/security/security0.c components/pthread/pthread_cond_var.c components/pthread/pthread_internal.h components/pthread/pthread_local_storage.c