From 3057765c38d498815b14fbc794066f5cc411be48 Mon Sep 17 00:00:00 2001 From: Johan Stokking Date: Wed, 6 Mar 2024 21:56:58 +0100 Subject: [PATCH 1/2] feat(websocket): Support DS peripheral for mutual TLS --- components/esp_websocket_client/esp_websocket_client.c | 10 ++++++++++ .../include/esp_websocket_client.h | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c index 4095960383..e0e5ab94f0 100644 --- a/components/esp_websocket_client/esp_websocket_client.c +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -93,6 +93,9 @@ typedef struct { size_t client_cert_len; const char *client_key; size_t client_key_len; +#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL + void *client_ds_data; +#endif bool use_global_ca_store; bool skip_cert_common_name_check; const char *cert_common_name; @@ -528,6 +531,10 @@ static esp_err_t esp_websocket_client_create_transport(esp_websocket_client_hand } else { esp_transport_ssl_set_client_key_data_der(ssl, client->config->client_key, client->config->client_key_len); } +#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL + } else if (client->config->client_ds_data) { + esp_transport_ssl_set_ds_data(ssl, client->config->client_ds_data); +#endif } if (client->config->crt_bundle_attach) { #ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE @@ -693,6 +700,9 @@ esp_websocket_client_handle_t esp_websocket_client_init(const esp_websocket_clie client->config->client_cert_len = config->client_cert_len; client->config->client_key = config->client_key; client->config->client_key_len = config->client_key_len; +#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL + client->config->client_ds_data = config->client_ds_data; +#endif client->config->skip_cert_common_name_check = config->skip_cert_common_name_check; client->config->cert_common_name = config->cert_common_name; client->config->crt_bundle_attach = config->crt_bundle_attach; diff --git a/components/esp_websocket_client/include/esp_websocket_client.h b/components/esp_websocket_client/include/esp_websocket_client.h index 34f85ae327..5f8663406d 100644 --- a/components/esp_websocket_client/include/esp_websocket_client.h +++ b/components/esp_websocket_client/include/esp_websocket_client.h @@ -106,10 +106,13 @@ typedef struct { int buffer_size; /*!< Websocket buffer size */ const char *cert_pem; /*!< Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len. */ size_t cert_len; /*!< Length of the buffer pointed to by cert_pem. May be 0 for null-terminated pem */ - const char *client_cert; /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */ + const char *client_cert; /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key` or `client_ds_data` (if supported) has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */ size_t client_cert_len; /*!< Length of the buffer pointed to by client_cert. May be 0 for null-terminated pem */ - const char *client_key; /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */ + const char *client_key; /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided and `client_ds_data` (if supported) gets ignored. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */ size_t client_key_len; /*!< Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem */ +#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL + void *client_ds_data; /*!< Pointer to the encrypted private key data for SSL mutual authentication using the DS peripheral, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert` has to be provided. It is ignored if `client_key` is provided */ +#endif esp_websocket_transport_t transport; /*!< Websocket transport type, see `esp_websocket_transport_t */ const char *subprotocol; /*!< Websocket subprotocol */ const char *user_agent; /*!< Websocket user-agent */ From 5641123a97cf6be89226a0489a97fec061553b6d Mon Sep 17 00:00:00 2001 From: Suren Gabrielyan Date: Fri, 12 Apr 2024 12:07:36 +0400 Subject: [PATCH 2/2] feat(websocket): Enhance websocket example to test ds-peripherial --- .../examples/target/CMakeLists.txt | 7 ++ .../examples/target/README.md | 36 ++++++++ .../examples/target/main/idf_component.yml | 1 + .../examples/target/main/websocket_example.c | 83 +++++++++++++++++++ .../target/sdkconfig.ci.ds_peripheral | 7 ++ 5 files changed, 134 insertions(+) create mode 100644 components/esp_websocket_client/examples/target/sdkconfig.ci.ds_peripheral diff --git a/components/esp_websocket_client/examples/target/CMakeLists.txt b/components/esp_websocket_client/examples/target/CMakeLists.txt index 6858816b3e..6c10caee75 100644 --- a/components/esp_websocket_client/examples/target/CMakeLists.txt +++ b/components/esp_websocket_client/examples/target/CMakeLists.txt @@ -4,3 +4,10 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(websocket_example) + +# Flash the custom partition named `esp_secure_cert`. +set(partition esp_secure_cert) +idf_build_get_property(project_dir PROJECT_DIR) +set(image_file ${project_dir}/esp_secure_cert_data/${partition}.bin) +partition_table_get_partition_info(offset "--partition-name ${partition}" "offset") +esptool_py_flash_target_image(flash "${partition}" "${offset}" "${image_file}") diff --git a/components/esp_websocket_client/examples/target/README.md b/components/esp_websocket_client/examples/target/README.md index fd47add624..ae5c495885 100644 --- a/components/esp_websocket_client/examples/target/README.md +++ b/components/esp_websocket_client/examples/target/README.md @@ -63,6 +63,42 @@ Please see the openssl man pages (man openssl) for more details. It is **strongly recommended** to not reuse the example certificate in your application; it is included only for demonstration. +#### 3) Configure the DS peripheral + +* i) Install the [esp_secure_cert configuration utility](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools#esp_secure_cert-configuration-tool) with following comma nd: +``` +pip install esp-secure-cert-tool +``` +* ii) The DS peripheral can be configured by executing the following command: + +``` +configure_esp_secure_cert.py -p /* Serial port */ --device-cert /* Device cert */ --private-key /* RSA priv key */ --target_chip /* target chip */ --configure_ds --skip_flash +``` +This command shall generate a partition named `esp_secure_cert.bin` in the `esp_secure_cert_data` directory. This partition would be aumatically detected by the build system and flashed at appropriate offset when `idf.py flash` command is used. For this process, the command must be executed in the current folder only. + +In the command USB COM port is nothing but the serial port to which the ESP chip is connected. see +[check serial port](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html#check-port-on-windows) for more details. +RSA private key is nothing but the client private key ( RSA ) generated in Step 2. + +> Note: More details about the `esp-secure-cert-tool` utility can be found [here](https://github.com/espressif/esp_secure_cert_mgr/tree/main/tools). + +#### 4) Connection cofiguration +* Open the project configuration menu (`idf.py menuconfig`) +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../READM E.md) for more details. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + + ### Build and Flash Build the project and flash it to the board, then run monitor tool to view serial output: diff --git a/components/esp_websocket_client/examples/target/main/idf_component.yml b/components/esp_websocket_client/examples/target/main/idf_component.yml index 0cc1eea4d0..dc7fb22435 100644 --- a/components/esp_websocket_client/examples/target/main/idf_component.yml +++ b/components/esp_websocket_client/examples/target/main/idf_component.yml @@ -1,4 +1,5 @@ dependencies: + espressif/esp_secure_cert_mgr: "^2.4.1" ## Required IDF version idf: ">=5.0" espressif/esp_websocket_client: diff --git a/components/esp_websocket_client/examples/target/main/websocket_example.c b/components/esp_websocket_client/examples/target/main/websocket_example.c index 112e9285c1..8beb317232 100644 --- a/components/esp_websocket_client/examples/target/main/websocket_example.c +++ b/components/esp_websocket_client/examples/target/main/websocket_example.c @@ -30,6 +30,18 @@ #include "esp_event.h" #include +#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL +#include "mbedtls/ssl.h" +#include "mbedtls/pk.h" +#include "mbedtls/x509.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "esp_idf_version.h" +#include "esp_secure_cert_read.h" +#include "esp_crt_bundle.h" +#endif + #define NO_DATA_TIMEOUT_SEC 5 static const char *TAG = "websocket"; @@ -69,6 +81,34 @@ static void get_string(char *line, size_t size) #endif /* CONFIG_WEBSOCKET_URI_FROM_STDIN */ +#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL +static esp_err_t test_ciphertext_validity(esp_ds_data_ctx_t *ds_data, unsigned char *dev_cert, size_t dev_cert_len) +{ + mbedtls_x509_crt crt; + mbedtls_x509_crt_init(&crt); + unsigned char *sig = NULL; + + if (ds_data == NULL || dev_cert == NULL) { + return ESP_ERR_INVALID_ARG; + } + + int ret = mbedtls_x509_crt_parse(&crt, dev_cert, dev_cert_len); + if (ret < 0) { + ESP_LOGE(TAG, "Parsing of device certificate failed, returned %02X", ret); + } + + esp_err_t esp_ret = esp_ds_init_data_ctx(ds_data); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialze the DS context"); + return esp_ret; + } + + const size_t sig_len = 256; + uint32_t hash[8] = {[0 ... 7] = 0xAABBCCDD}; + return esp_ret; +} +#endif + static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; @@ -163,6 +203,49 @@ static void websocket_app_start(void) websocket_cfg.cert_pem = cacert_start; #endif +#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL + uint32_t len = 0; + char *addr = NULL; + esp_err_t esp_ret = ESP_FAIL; + + esp_ds_data_ctx_t *ds_data = NULL; + ESP_LOGI(TAG, "Successfully obtained the ds context before"); + ds_data = esp_secure_cert_get_ds_ctx(); + ESP_LOGI(TAG, "Successfully obtained the ds context after"); + if (ds_data != NULL) { + ESP_LOGI(TAG, "Successfully obtained the ds context"); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->c, ESP_DS_C_LEN, ESP_LOG_DEBUG); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, ds_data->esp_ds_data->iv, ESP_DS_IV_LEN, ESP_LOG_DEBUG); + ESP_LOGI(TAG, "The value of rsa length is %d", ds_data->rsa_length_bits); + ESP_LOGI(TAG, "The value of efuse key id is %d", ds_data->efuse_key_id); + } else { + ESP_LOGE(TAG, "Failed to obtain the ds context"); + } + + /* Read the dev_cert addr again */ + esp_ret = esp_secure_cert_get_device_cert(&addr, &len); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to obtain the dev cert flash address"); + } + + esp_ret = test_ciphertext_validity(ds_data, (unsigned char *)addr, len); + if (esp_ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to validate ciphertext"); + } else { + ESP_LOGI(TAG, "Ciphertext validated succcessfully"); + } + websocket_cfg.client_cert = addr; + websocket_cfg.client_ds_data = ds_data; + // websocket_cfg.crt_bundle_attach = esp_crt_bundle_attach; + websocket_cfg.cert_pem = addr; + // extern const char cacert_start[] asm("_binary_ca_cert_pem_start"); // CA certificate + // websocket_cfg.cert_pem = cacert_start; + // websocket_cfg.client_key = NULL; + +// extern const char cacert_start[] asm("_binary_ca_certificate_public_domain_pem_start"); // CA cert of wss://echo.websocket.event, modify it if using another server +// websocket_cfg.cert_pem = cacert_start; +#endif + #if CONFIG_WS_OVER_TLS_SKIP_COMMON_NAME_CHECK websocket_cfg.skip_cert_common_name_check = true; #endif diff --git a/components/esp_websocket_client/examples/target/sdkconfig.ci.ds_peripheral b/components/esp_websocket_client/examples/target/sdkconfig.ci.ds_peripheral new file mode 100644 index 0000000000..e2c7de1982 --- /dev/null +++ b/components/esp_websocket_client/examples/target/sdkconfig.ci.ds_peripheral @@ -0,0 +1,7 @@ +CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_OFFSET=0xC000 +CONFIG_IDF_TARGET="esp32s2" +CONFIG_WEBSOCKET_URI_FROM_STRING=y +CONFIG_WEBSOCKET_URI="wss://echo.websocket.events" +CONFIG_IDF_TARGET="esp32s2"