Merge branch 'feature/esp_ds_sw_support' into 'master'

Added support for  RSA  sign using DS peripheral (only for ESP32-S2)

Closes IDF-1626

See merge request espressif/esp-idf!9477
This commit is contained in:
Mahavir Jain 2020-09-23 20:39:04 +08:00
commit 4a3b5b73a8
20 changed files with 1283 additions and 23 deletions

View File

@ -24,6 +24,15 @@ menu "ESP-TLS"
Enable use of Secure Element for ESP-TLS, this enables internal support for
ATECC608A peripheral on ESPWROOM32SE, which can be used for TLS connection.
config ESP_TLS_USE_DS_PERIPHERAL
bool "Use Digital Signature (DS) Peripheral with ESP-TLS"
depends on IDF_TARGET_ESP32S2 && ESP_TLS_USING_MBEDTLS
default y
help
Enable use of the Digital Signature Peripheral for ESP-TLS.The DS peripheral
can only be used when it is appropriately configured for TLS.
Consult the ESP-TLS documentation in ESP-IDF Programming Guide for more details.
config ESP_TLS_SERVER
bool "Enable ESP-TLS Server"
default n

View File

@ -205,6 +205,7 @@ typedef struct esp_tls_cfg {
/*!< Function pointer to esp_crt_bundle_attach. Enables the use of certification
bundle for server verification, must be enabled in menuconfig */
void *ds_data; /*!< Pointer for digital signature peripheral context */
} esp_tls_cfg_t;
#ifdef CONFIG_ESP_TLS_SERVER

View File

@ -44,6 +44,11 @@ static const atcacert_def_t* cert_def = NULL;
static esp_err_t esp_set_atecc608a_pki_context(esp_tls_t *tls, esp_tls_cfg_t *cfg);
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
#if defined(CONFIG_ESP_TLS_USE_DS_PERIPHERAL)
#include "rsa_sign_alt.h"
static esp_err_t esp_mbedtls_init_pk_ctx_for_ds(const void *pki);
#endif /* CONFIG_ESP_TLS_USE_DS_PERIPHERAL */
static const char *TAG = "esp-tls-mbedtls";
static mbedtls_x509_crt *global_cacert = NULL;
@ -56,6 +61,9 @@ typedef struct esp_tls_pki_t {
unsigned int privkey_pem_bytes;
const unsigned char *privkey_password;
unsigned int privkey_password_len;
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
void *esp_ds_data;
#endif
} esp_tls_pki_t;
esp_err_t esp_create_mbedtls_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
@ -125,6 +133,9 @@ int esp_mbedtls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
ret = mbedtls_ssl_handshake(&tls->ssl);
if (ret == 0) {
tls->conn_state = ESP_TLS_DONE;
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
esp_ds_release_ds_lock();
#endif
return 1;
} else {
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
@ -247,6 +258,9 @@ void esp_mbedtls_cleanup(esp_tls_t *tls)
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
atcab_release();
#endif
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
esp_ds_release_ds_lock();
#endif
}
static esp_err_t set_ca_cert(esp_tls_t *tls, const unsigned char *cacert, size_t cacert_len)
@ -272,7 +286,6 @@ static esp_err_t set_pki_context(esp_tls_t *tls, const esp_tls_pki_t *pki)
int ret;
if (pki->publiccert_pem_buf != NULL &&
pki->privkey_pem_buf != NULL &&
pki->public_cert != NULL &&
pki->pk_key != NULL) {
@ -286,8 +299,22 @@ static esp_err_t set_pki_context(esp_tls_t *tls, const esp_tls_pki_t *pki)
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
pki->privkey_password, pki->privkey_password_len);
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
if (pki->esp_ds_data != NULL) {
ret = esp_mbedtls_init_pk_ctx_for_ds(pki);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize pk context for ");
return ret;
}
} else
#endif
if (pki->privkey_pem_buf != NULL) {
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
pki->privkey_password, pki->privkey_password_len);
} else {
return ESP_ERR_INVALID_ARG;
}
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
@ -477,6 +504,35 @@ esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t
ESP_LOGE(TAG, "Please enable secure element support for ESP-TLS in menuconfig");
return ESP_FAIL;
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
} else if (cfg->ds_data != NULL) {
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
if (cfg->clientcert_pem_buf == NULL) {
ESP_LOGE(TAG, "Client certificate is also required with the DS parameters");
return ESP_ERR_INVALID_STATE;
}
esp_ds_set_session_timeout(cfg->timeout_ms);
/* set private key pointer to NULL since the DS peripheral with its own configuration data is used */
esp_tls_pki_t pki = {
.public_cert = &tls->clientcert,
.pk_key = &tls->clientkey,
.publiccert_pem_buf = cfg->clientcert_buf,
.publiccert_pem_bytes = cfg->clientcert_bytes,
.privkey_pem_buf = NULL,
.privkey_pem_bytes = 0,
.privkey_password = NULL,
.privkey_password_len = 0,
.esp_ds_data = cfg->ds_data,
};
esp_err_t esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client pki context for the DS peripheral, returned %02x", esp_ret);
return esp_ret;
}
#else
ESP_LOGE(TAG, "Please enable the DS peripheral support for the ESP-TLS in menuconfig. (only supported for the ESP32-S2 chip)");
return ESP_FAIL;
#endif
} else if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
esp_tls_pki_t pki = {
.public_cert = &tls->clientcert,
@ -671,3 +727,25 @@ static esp_err_t esp_set_atecc608a_pki_context(esp_tls_t *tls, esp_tls_cfg_t *cf
return ESP_OK;
}
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
static esp_err_t esp_mbedtls_init_pk_ctx_for_ds(const void *pki)
{
int ret = -1;
/* initialize the mbedtls pk context with rsa context */
mbedtls_rsa_context rsakey;
mbedtls_rsa_init(&rsakey, MBEDTLS_RSA_PKCS_V15, 0);
if ((ret = mbedtls_pk_setup_rsa_alt(((const esp_tls_pki_t*)pki)->pk_key, &rsakey, NULL, esp_ds_rsa_sign,
esp_ds_get_keylen )) != 0) {
ESP_LOGE(TAG, "Error in mbedtls_pk_setup_rsa_alt, returned %02x", ret);
return ESP_FAIL;
}
ret = esp_ds_init_data_ctx(((const esp_tls_pki_t*)pki)->esp_ds_data);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize DS parameters from nvs");
return ESP_FAIL;
}
ESP_LOGD(TAG, "DS peripheral params initialized.");
return ESP_OK;
}
#endif /* CONFIG_ESP_TLS_USE_DS_PERIPHERAL */

View File

@ -90,6 +90,10 @@ target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp_hardware.c"
"${COMPONENT_DIR}/port/${idf_target}/sha.c"
)
if(CONFIG_ESP_TLS_USE_DS_PERIPHERAL)
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp32s2/esp_rsa_sign_alt.c")
endif()
# Note: some mbedTLS hardware acceleration can be enabled/disabled by config.
#
# We don't need to filter aes.c as this uses a different prefix (esp_aes_x) and the

View File

@ -0,0 +1,250 @@
// Copyright 2020 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.
#include "esp_ds.h"
#include "rsa_sign_alt.h"
#include "esp32s2/rom/digital_signature.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
static const char *TAG = "ESP_RSA_SIGN_ALT";
#define SWAP_INT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
#include "mbedtls/rsa.h"
#include "mbedtls/rsa_internal.h"
#include "mbedtls/oid.h"
#include "mbedtls/platform_util.h"
#include <string.h>
static hmac_key_id_t s_esp_ds_hmac_key_id;
static esp_ds_data_t *s_ds_data;
static SemaphoreHandle_t s_ds_lock;
static uint32_t s_timeout_ms = 0;
/* key length in bytes = (esp_digital_signature_length_t key + 1 ) * FACTOR_KEYLEN_IN_BYTES */
#define FACTOR_KEYLEN_IN_BYTES 4
/* Lock for the DS session, other TLS connections trying to use the DS peripheral will be blocked
* till this DS session is completed (i.e. TLS handshake for this connection is completed) */
static void __attribute__((constructor)) esp_ds_conn_lock (void)
{
if ((s_ds_lock = xSemaphoreCreateMutex()) == NULL) {
ESP_EARLY_LOGE(TAG, "mutex for the DS session lock could not be created");
}
}
void esp_ds_set_session_timeout(int timeout)
{
/* add additional offset of 1000 ms to have enough time for deleting the TLS connection and free the previous ds context after exceeding timeout value (this offset also helps when timeout is set to 0) */
if (timeout > s_timeout_ms) {
s_timeout_ms = timeout + 1000;
}
}
esp_err_t esp_ds_init_data_ctx(esp_ds_data_ctx_t *ds_data)
{
if (ds_data == NULL || ds_data->esp_ds_data == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* mutex is given back when the DS context is freed after the TLS handshake is completed or in case of failure (at cleanup) */
if ((xSemaphoreTake(s_ds_lock, s_timeout_ms / portTICK_PERIOD_MS) != pdTRUE)) {
ESP_LOGE(TAG, "ds_lock could not be obtained in specified time");
return ESP_FAIL;
}
s_ds_data = ds_data->esp_ds_data;
s_esp_ds_hmac_key_id = (hmac_key_id_t) ds_data->efuse_key_id;
/* calculate the rsa_length in terms of esp_digital_signature_length_t which is required for the internal DS API */
s_ds_data->rsa_length = (ds_data->rsa_length_bits / 32) - 1;
return ESP_OK;
}
void esp_ds_release_ds_lock(void)
{
/* Give back the semaphore (DS lock) */
xSemaphoreGive(s_ds_lock);
}
size_t esp_ds_get_keylen(void *ctx)
{
/* calculating the rsa_length in bytes */
return ((s_ds_data->rsa_length + 1) * FACTOR_KEYLEN_IN_BYTES);
}
static int rsa_rsassa_pkcs1_v15_encode( mbedtls_md_type_t md_alg,
unsigned int hashlen,
const unsigned char *hash,
size_t dst_len,
unsigned char *dst )
{
size_t oid_size = 0;
size_t nb_pad = dst_len;
unsigned char *p = dst;
const char *oid = NULL;
/* Are we signing hashed or raw data? */
if ( md_alg != MBEDTLS_MD_NONE ) {
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type( md_alg );
if ( md_info == NULL ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
if ( mbedtls_oid_get_oid_by_md( md_alg, &oid, &oid_size ) != 0 ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
hashlen = mbedtls_md_get_size( md_info );
/* Double-check that 8 + hashlen + oid_size can be used as a
* 1-byte ASN.1 length encoding and that there's no overflow. */
if ( 8 + hashlen + oid_size >= 0x80 ||
10 + hashlen < hashlen ||
10 + hashlen + oid_size < 10 + hashlen ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
/*
* Static bounds check:
* - Need 10 bytes for five tag-length pairs.
* (Insist on 1-byte length encodings to protect against variants of
* Bleichenbacher's forgery attack against lax PKCS#1v1.5 verification)
* - Need hashlen bytes for hash
* - Need oid_size bytes for hash alg OID.
*/
if ( nb_pad < 10 + hashlen + oid_size ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
nb_pad -= 10 + hashlen + oid_size;
} else {
if ( nb_pad < hashlen ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
nb_pad -= hashlen;
}
/* Need space for signature header and padding delimiter (3 bytes),
* and 8 bytes for the minimal padding */
if ( nb_pad < 3 + 8 ) {
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
nb_pad -= 3;
/* Now nb_pad is the amount of memory to be filled
* with padding, and at least 8 bytes long. */
/* Write signature header and padding */
*p++ = 0;
*p++ = MBEDTLS_RSA_SIGN;
memset( p, 0xFF, nb_pad );
p += nb_pad;
*p++ = 0;
/* Are we signing raw data? */
if ( md_alg == MBEDTLS_MD_NONE ) {
memcpy( p, hash, hashlen );
return ( 0 );
}
/* Signing hashed data, add corresponding ASN.1 structure
*
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithmIdentifier,
* digest Digest }
* DigestAlgorithmIdentifier ::= AlgorithmIdentifier
* Digest ::= OCTET STRING
*
* Schematic:
* TAG-SEQ + LEN [ TAG-SEQ + LEN [ TAG-OID + LEN [ OID ]
* TAG-NULL + LEN [ NULL ] ]
* TAG-OCTET + LEN [ HASH ] ]
*/
*p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED;
*p++ = (unsigned char)( 0x08 + oid_size + hashlen );
*p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED;
*p++ = (unsigned char)( 0x04 + oid_size );
*p++ = MBEDTLS_ASN1_OID;
*p++ = (unsigned char) oid_size;
memcpy( p, oid, oid_size );
p += oid_size;
*p++ = MBEDTLS_ASN1_NULL;
*p++ = 0x00;
*p++ = MBEDTLS_ASN1_OCTET_STRING;
*p++ = (unsigned char) hashlen;
memcpy( p, hash, hashlen );
p += hashlen;
/* Just a sanity-check, should be automatic
* after the initial bounds check. */
if ( p != dst + dst_len ) {
mbedtls_platform_zeroize( dst, dst_len );
return ( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
return ( 0 );
}
int esp_ds_rsa_sign( void *ctx,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
int mode, mbedtls_md_type_t md_alg, unsigned int hashlen,
const unsigned char *hash, unsigned char *sig )
{
esp_ds_context_t *esp_ds_ctx;
esp_err_t ds_r;
int ret = -1;
uint32_t *signature = heap_caps_malloc_prefer((s_ds_data->rsa_length + 1) * FACTOR_KEYLEN_IN_BYTES, 2, MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
if (signature == NULL) {
ESP_LOGE(TAG, "Could not allocate memory for internal DS operations");
return -1;
}
if ((ret = (rsa_rsassa_pkcs1_v15_encode( md_alg, hashlen, hash, ((s_ds_data->rsa_length + 1) * FACTOR_KEYLEN_IN_BYTES), sig ))) != 0) {
ESP_LOGE(TAG, "Error in pkcs1_v15 encoding, returned %02x", ret);
heap_caps_free(signature);
return -1;
}
for ( int i = 0; i < (s_ds_data->rsa_length + 1); i++) {
signature[i] = SWAP_INT32(((uint32_t *)sig)[(s_ds_data->rsa_length + 1) - (i + 1)]);
}
ds_r = esp_ds_start_sign((const void *)signature,
s_ds_data,
s_esp_ds_hmac_key_id,
&esp_ds_ctx);
if (ds_r != ESP_OK) {
ESP_LOGE(TAG, "Error in esp_ds_start_sign, returned %02x ", ds_r);
heap_caps_free(signature);
return -1;
}
ds_r = esp_ds_finish_sign((void *)signature, esp_ds_ctx);
if (ds_r != ESP_OK) {
ESP_LOGE(TAG, "Error in esp_ds_finish sign, returned %02X ", ds_r);
heap_caps_free(signature);
return -1;
}
for ( int i = 0; i < (s_ds_data->rsa_length + 1); i++) {
((uint32_t *)sig)[i] = SWAP_INT32(((uint32_t *)signature)[(s_ds_data->rsa_length + 1) - (i + 1)]);
}
heap_caps_free(signature);
return 0;
}

View File

@ -0,0 +1,86 @@
// Copyright 2020 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.
#pragma once
#ifndef _ESP_RSA_SIGN_ALT_H_
#define _ESP_RSA_SIGN_ALT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_ds.h"
#include "mbedtls/md.h"
/**
* @brief ESP-DS data context
*
* @note This structure includes encrypted private key parameters such as ciphertext_c, initialization vector, efuse_key_id, RSA key length, which are obtained when DS peripheral is configured.
*/
/* Context for encrypted private key data required for DS */
typedef struct esp_ds_data_ctx {
esp_ds_data_t *esp_ds_data;
uint8_t efuse_key_id; /* efuse block id in which DS_KEY is stored e.g. 0,1*/
uint16_t rsa_length_bits; /* length of RSA private key in bits e.g. 2048 */
} esp_ds_data_ctx_t;
/**
* @brief Initializes internal DS data context
*
* This function allocates and initializes internal ds data context which is used for Digital Signature operation.
*
* @in ds_data ds_data context containing encrypted private key parameters
* @return
* - ESP_OK In case of succees
* - ESP_ERR_NO_MEM In case internal context could not be allocated.
* - ESP_ERR_INVALID_ARG in case input parametrers are NULL
*
*/
esp_err_t esp_ds_init_data_ctx(esp_ds_data_ctx_t *ds_data);
/**
*
* @brief Release the ds lock acquired for the DS operation (then the DS peripheral can be used for other TLS connection)
*
*/
void esp_ds_release_ds_lock(void);
/**
*
* @brief Alternate implementation for mbedtls_rsa_rsassa_pkcs1_v15_sign, Internally makes use
* of DS module to perform hardware accelerated RSA sign operation
*/
int esp_ds_rsa_sign( void *ctx,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
int mode, mbedtls_md_type_t md_alg, unsigned int hashlen,
const unsigned char *hash, unsigned char *sig );
/*
* @brief Get RSA key length in bytes from internal DS context
*
* @return RSA key length in bytes
*/
size_t esp_ds_get_keylen(void *ctx);
/*
* @brief Set timeout (equal to TLS session timeout), so that DS module usage can be synchronized in case of multiple TLS connections using DS module,
*/
void esp_ds_set_session_timeout(int timeout);
#ifdef __cplusplus
}
#endif
#endif /* _ESP_RSA_SIGN_ALT_H_ */

View File

@ -0,0 +1,39 @@
// Copyright 2020 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.
#pragma once
#ifndef _RSA_SIGN_ALT_H_
#define _RSA_SIGN_ALT_H_
#ifdef __cpluscplus
extern "C" {
#endif
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
#include "esp32s2/esp_rsa_sign_alt.h"
#else
#error "DS configuration flags not activated, please enable required menuconfig flags"
#endif
#ifdef __cpluscplus
}
#endif
#endif

View File

@ -141,6 +141,16 @@ void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t);
*/
void esp_transport_ssl_use_secure_element(esp_transport_handle_t t);
/**
* @brief Set the ds_data handle in ssl context.(used for the digital signature operation)
*
* @param t ssl transport
* ds_data the handle for ds data params
*/
void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data);
/**
* @brief Set PSK key and hint for PSK server/client verification in esp-tls component.
* Important notes:

View File

@ -303,6 +303,14 @@ static int ssl_get_socket(esp_transport_handle_t t)
return -1;
}
void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
if (t && ssl) {
ssl->cfg.ds_data = ds_data;
}
}
esp_transport_handle_t esp_transport_ssl_init(void)
{
esp_transport_handle_t t = esp_transport_init();

View File

@ -20,7 +20,7 @@ Upon signature calculation invocation, the software only specifies which eFuse k
Key Generation
--------------
Both the HMAC key and the RSA private key have to be created and stored before the DS module can be used.
Both the HMAC key and the RSA private key have to be created and stored before the DS peripheral can be used.
This needs to be done in software on the {IDF_TARGET_NAME} or alternatively on a host.
For this context, the IDF provides :cpp:func:`esp_efuse_write_block` to set the HMAC key and :cpp:func:`esp_hmac_calculate` to encrypt the private RSA key parameters.
@ -46,6 +46,37 @@ Once the calculation has finished, :cpp:func:`esp_ds_finish_sign` can be called
To create signatures of arbitrary messages, the input is normally a hash of the actual message, padded up to the required length.
An API to do this is planned in the future.
Configure the DS peripheral for a TLS connection
------------------------------------------------
The DS peripheral on ESP32-S2 chip must be configured before it can be used for a TLS connection.
The configuration involves the following steps -
1) Randomly generate a 256 bit value called the `Initialization Vector` (IV).
2) Randomly generate a 256 bit value called the `HMAC_KEY`.
3) Calculate the encrypted private key paramters from the client private key (RSA) and the parameters generated in the above steps.
4) Then burn the 256 bit `HMAC_KEY` on the efuse, which can only be read by the DS peripheral.
For more technical details visit the `Digital Signature` chapter in `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`_.
To configure the DS peripheral for development purposes, you can use the python script :example_file:`configure_ds.py<protocols/mqtt/ssl_ds/configure_ds.py>`.
More details about the `configure_ds.py` script can be found at :example_file:`mqtt example README <protocols/mqtt/ssl_ds/README.md>`.
The encrypted private key parameters obtained after the DS peripheral configuration are then to be kept in flash. Furthermore, they are to be passed to the DS peripheral which makes use of those parameters for the Digital Signature operation.
:doc:`Non Volatile Storage<../storage/nvs_flash>` can be used to store the encrypted private key parameters in flash.
The script :example_file:`configure_ds.py<protocols/mqtt/ssl_ds/configure_ds.py>` creates an NVS partition for the encrypted private key parameters. Then the script flashes this partition onto the {IDF_TARGET_NAME}.
The application then needs to read the DS data from NVS, which can be done with the function `esp_read_ds_data_from_nvs` in file :example_file:`ssl_mutual_auth/main/app_main.c <protocols/mqtt/ssl_mutual_auth/main/app_main.c>`
The process of initializing the DS peripheral and then performing the Digital Signature operation is done internally with help of `ESP-TLS`. Please refer to `Digital Signature with ESP-TLS` in :doc:`ESP-TLS <../protocols/esp_tls>` for more details.
As mentioned in the `ESP-TLS` documentation, the application only needs to provide the encrypted private key parameters to the esp_tls context (as `ds_data`), which internally performs
all necessary operations for initializing the DS peripheral and then performing the DS operation.
Example for SSL Mutual Authentication using DS
----------------------------------------------
The example :example:`ssl_ds<protocols/mqtt/ssl_ds>` shows how to use the DS peripheral for mutual authentication. The example uses `mqtt_client` (Implemented through `ESP-MQTT`)
to connect to broker test.mosquitto.org using ssl transport with mutual authentication. The ssl part is internally performed with `ESP-TLS`.
See :example_file:`example README<protocols/mqtt/ssl_ds/README.md>` for more details.
API Reference
-------------

View File

@ -88,37 +88,73 @@ SSL/TLS libraries and with all respective configurations set to default.
.. note:: `These values are subject to change with change in configuration options and version of respective libraries`.
ATECC608A (Secure Element) with ESP-TLS
--------------------------------------------------
.. only:: esp32
ESP-TLS provides support for using ATECC608A cryptoauth chip with ESP32-WROOM-32SE.
Use of ATECC608A is supported only when ESP-TLS is used with mbedTLS as its underlying SSL/TLS stack.
ESP-TLS uses mbedtls as its underlying TLS/SSL stack by default unless changed manually.
ATECC608A (Secure Element) with ESP-TLS
--------------------------------------------------
.. note:: ATECC608A chip on ESP32-WROOM-32SE must be already configured and provisioned, for details refer `esp_cryptoauth_utility <https://github.com/espressif/esp-cryptoauthlib/blob/master/esp_cryptoauth_utility/README.md#esp_cryptoauth_utility>`_
ESP-TLS provides support for using ATECC608A cryptoauth chip with ESP32-WROOM-32SE.
Use of ATECC608A is supported only when ESP-TLS is used with mbedTLS as its underlying SSL/TLS stack.
ESP-TLS uses mbedtls as its underlying TLS/SSL stack by default unless changed manually.
To enable the secure element support, and use it in you project for TLS connection, you will have to follow below steps
.. note:: ATECC608A chip on ESP32-WROOM-32SE must be already configured and provisioned, for details refer `esp_cryptoauth_utility <https://github.com/espressif/esp-cryptoauthlib/blob/master/esp_cryptoauth_utility/README.md#esp_cryptoauth_utility>`_
1) Add `esp-cryptoauthlib <https://github.com/espressif/esp-cryptoauthlib>`_ in your project, for details please refer `esp-cryptoauthlib with ESP_IDF <https://github.com/espressif/esp-cryptoauthlib#how-to-use-esp-cryptoauthlib-with-esp-idf>`_
To enable the secure element support, and use it in you project for TLS connection, you will have to follow below steps
2) Enable following menuconfig option::
1) Add `esp-cryptoauthlib <https://github.com/espressif/esp-cryptoauthlib>`_ in your project, for details please refer `esp-cryptoauthlib with ESP_IDF <https://github.com/espressif/esp-cryptoauthlib#how-to-use-esp-cryptoauthlib-with-esp-idf>`_
menuconfig->Component config->ESP-TLS->Use Secure Element (ATECC608A) with ESP-TLS
2) Enable following menuconfig option::
3) Select type of ATECC608A chip with following option::
menuconfig->Component config->ESP-TLS->Use Secure Element (ATECC608A) with ESP-TLS
menuconfig->Component config->esp-cryptoauthlib->Choose Type of ATECC608A chip
3) Select type of ATECC608A chip with following option::
to know more about different types of ATECC608A chips and how to obtain type of ATECC608A connected to your ESP module please visit `ATECC608A chip type <https://github.com/espressif/esp-cryptoauthlib/blob/master/esp_cryptoauth_utility/README.md#find-type-of-atecc608a-chip-connected-to-esp32-wroom32-se>`_
menuconfig->Component config->esp-cryptoauthlib->Choose Type of ATECC608A chip
4) Enable use of ATECC608A in ESP-TLS by providing following config option in `esp_tls_cfg_t`
to know more about different types of ATECC608A chips and how to obtain type of ATECC608A connected to your ESP module please visit `ATECC608A chip type <https://github.com/espressif/esp-cryptoauthlib/blob/master/esp_cryptoauth_utility/README.md#find-type-of-atecc608a-chip-connected-to-esp32-wroom32-se>`_
.. code-block:: c
4) Enable use of ATECC608A in ESP-TLS by providing following config option in `esp_tls_cfg_t`
esp_tls_cfg_t cfg = {
/* other configurations options */
.use_secure_element = true,
};
.. code-block:: c
esp_tls_cfg_t cfg = {
/* other configurations options */
.use_secure_element = true,
};
.. only:: esp32s2
.. _digital-signature-with-esp-tls:
Digital Signature with ESP-TLS
------------------------------
ESP-TLS provides support for using the Digital Signature (DS) with ESP32-S2.
Use of the DS for TLS is supported only when ESP-TLS is used with mbedTLS (default stack) as its underlying SSL/TLS stack.
For more details on Digital Signature, please refer to the :doc:`Digital Signature Documentation </api-reference/peripherals/ds>`. The technical details of Digital Signature such as
how to calculate private key parameters can be found at `{IDF_TARGET_NAME} Technical Reference Manual <{IDF_TARGET_TRM_EN_URL}>`_.
The DS peripheral must be configured before it can be used to perform Digital Signature, see `Configure the DS Peripheral` in :doc:`Digital Signature </api-reference/peripherals/ds>`.
.. note:: As the DS peripheral support is only available for ESP32-S2, the idf-target should be set to ESP32-S2. See `Selecting the Target` in :doc:`build-system.</api-guides/build-system>`.
The DS peripheral must be initlized with the required encrypted private key parameters (obtained when the DS peripheral is configured). ESP-TLS internally initializes the DS peripheral when
provided with the required DS context (DS parameters). Please see the below code snippet for passing the DS context to esp-tls context. The DS context passed to the esp-tls context should not be freed till the TLS connection is deleted.
.. code-block:: c
#include "esp_tls.h"
esp_ds_data_ctx_t *ds_ctx;
/* initialize ds_ctx with encrypted private key parameters, which can be read from the nvs or
provided through the application code */
esp_tls_cfg_t cfg = {
.clientcert_buf = /* the client cert */,
.clientcert_bytes = /* length of the client cert */,
/* other configurations options */
.ds_data = (void *)ds_ctx,
};
.. note:: When using Digital Signature for the TLS connection, along with the other required params, only the client cert (`clientcert_buf`) and the DS params (`ds_data`) are required and the client key (`clientkey_buf`) can be set to NULL.
* An example of mutual authentication with the DS peripheral can be found at :example:`ssl mutual auth<protocols/mqtt/ssl_mutual_auth>` which internally uses (ESP-TLS) for the TLS connection.
API Reference
-------------

View File

@ -0,0 +1,13 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mqtt_ssl_ds)
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/client.crt" TEXT)
target_add_binary_data(${CMAKE_PROJECT_NAME}.elf "main/mosquitto.org.crt" TEXT)

View File

@ -0,0 +1,131 @@
| Supported Targets | ESP32-S2 |
# ESP-MQTT SSL Mutual Authentication with Digital Signature
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Espressif's ESP32-S2 MCU has a built-in Digital Signature (DS) Peripheral, which provides hardware acceleration for RSA signature. More details can be found at [Digital Signature with ESP-TLS](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/protocols/esp_tls.html#digital-signature-with-esp-tls).
This example connects to the broker test.mosquitto.org using ssl transport with client certificate(RSA) and as a demonstration subscribes/unsubscribes and sends a message on certain topic.The RSA signature operation required in the ssl connection is performed with help of the Digital Signature (DS) peripheral.
(Please note that the public broker is maintained by the community so may not be always available, for details please visit http://test.mosquitto.org)
It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
## How to use example
### Hardware Required
This example can be executed on any ESP32-S2 board (which has a built-in DS peripheral), the only required interface is WiFi and connection to internet.
### Configure the project
#### 1) Selecting the target
As the project is to be built for the target ESP32-S2, it should be selected with the following command
```
idf.py set-target esp32s2
```
More detials can be found at [Selecting the target](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html#selecting-the-target).
#### 2) Generate your client key and certificate
Navigate to the main directory
```
cd main
```
Generate a client key and a CSR. When you are generating the CSR, do not use the default values. At a minimum, the CSR must include the Country, Organisation and Common Name fields.
```
openssl genrsa -out client.key
openssl req -out client.csr -key client.key -new
```
Paste the generated CSR in the [Mosquitto test certificate signer](https://test.mosquitto.org/ssl/index.php), click Submit and copy the downloaded `client.crt` in the `main` directory.
Please note, that the supplied file `client.crt` in the `main` directory is only a placeholder for your client certificate (i.e. the example "as is" would compile but would not connect to the broker)
#### 3) Configure the DS peripheral
* The DS peripheral can be configured with the python script [configure_ds.py](README.md#configure_ds-py) by executing the following command
```
python configure_ds.py --port /* USB COM port */ --private_key /* RSA priv key */
```
In the command USB COM port is nothing but the serial port to which the ESP32-S2 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.
#### 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](../../README.md) for more details.
* When using Make build system, set `Default serial port` under `Serial flasher config`.
### 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.
## Example Output
```
I (3714) event: sta ip: 192.168.0.139, mask: 255.255.255.0, gw: 192.168.0.2
I (3714) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
I (3964) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
I (4164) MQTTS_EXAMPLE: MQTT_EVENT_CONNECTED
I (4174) MQTTS_EXAMPLE: sent publish successful, msg_id=41464
I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=17886
I (4174) MQTTS_EXAMPLE: sent subscribe successful, msg_id=42970
I (4184) MQTTS_EXAMPLE: sent unsubscribe successful, msg_id=50241
I (4314) MQTTS_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=41464
I (4484) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=17886
I (4484) MQTTS_EXAMPLE: sent publish successful, msg_id=0
I (4684) MQTTS_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=42970
I (4684) MQTTS_EXAMPLE: sent publish successful, msg_id=0
I (4884) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19
I (4884) MQTTS_EXAMPLE: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=data
I (5194) MQTT_CLIENT: deliver_publish, message_length_read=19, message_length=19
I (5194) MQTTS_EXAMPLE: MQTT_EVENT_DATA
TOPIC=/topic/qos0
DATA=data
```
### configure_ds.py
The script [configure_ds.py](./configure_ds.py) is used for configuring the DS peripheral on the ESP32-S2 SoC. The steps in the script are based on technical details of certain operations in the Digital Signature calculation, which can be found at Digital Signature Section of [ESP32-S2 TRM](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
The configuration script performs the following steps -
1. Take the client private key ( RSA key ) as input.
(*required parameter for the script)
can be provided with
```
python configure_ds.py --private-key /* path to client (rsa) prv key */
```
2. Randomly Calculate the `HMAC_KEY` and the `initialization vector`(IV).Then calculate the encrypted private key parameters from client private key (step i) and newly generated parameters. These encrypted private key parameters are required for the DS peripheral to perform the Digital Signature operation.
3. Store `HMAC_KEY` in one of the efuse key blocks (in the hardware).
The ID of the efuse key block ( should be in range 1-5) can be provided with the following option. ( default value of 1 is used if not provided),
```
python configure_ds.py --efuse_key_id /* key id in range 1-5 */ --burn_key
```
Currently for development purposes, the `HMAC_KEY` is stored in the efuse key block without read protection so that read operation can be performed on the same key block.
> You can burn (write) a key on an efuse key block only once.Please use a different block ID, if you want to use a different `HMAC_KEY` for the DS operation.
4. Create an NVS partition of the name `pre_prov.csv` (in `esp_ds_data` folder) which contains the required encrypted private key parameters. A bin file of the nvs partition (`pre_prov.bin`) is also created and is flashed on the device. As we have added a custom partition, the example is set to use the custom partition table by adding the required option in `sdkconfig.defaults`.
5. (optional) The script can be made to print the summary of the efuse on the chip by providing the following option.When this option is enabled, no other operations in the script are performed.
```
python configure_ds.py --summary
```
> A list of all the supported options in the script can be obtained by executing `python configure_ds.py --help`.

View File

@ -0,0 +1,314 @@
#!/usr/bin/env python
# Copyright 2020 Espressif Systems (Shanghai) Co., 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.
import argparse
import os
import sys
import hashlib
import hmac
import struct
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.utils import int_to_bytes
try:
import esptool
import espefuse
except ImportError:
idf_path = os.getenv("IDF_PATH")
if not idf_path or not os.path.exists(idf_path):
raise Exception("IDF_PATH not found")
sys.path.insert(0, os.path.join(idf_path, "components", "esptool_py", "esptool"))
import esptool
import espefuse
try:
import nvs_partition_gen as nvs_gen
except ImportError:
idf_path = os.getenv("IDF_PATH")
if not idf_path or not os.path.exists(idf_path):
raise Exception("IDF_PATH not found")
sys.path.insert(0, os.path.join(idf_path, "components", "nvs_flash", "nvs_partition_generator"))
import nvs_partition_gen as nvs_gen
esp_ds_data_dir = 'esp_ds_data'
# hmac_key_file is generated when HMAC_KEY is calculated, it is used when burning HMAC_KEY to efuse
hmac_key_file = esp_ds_data_dir + '/hmac_key.bin'
# csv and bin filenames are default filenames for nvs partition files created with this script
csv_filename = esp_ds_data_dir + '/pre_prov.csv'
bin_filename = esp_ds_data_dir + '/pre_prov.bin'
def load_privatekey(key_file_path, password=None):
key_file = open(key_file_path, 'rb')
key = key_file.read()
key_file.close()
return serialization.load_pem_private_key(key, password=password, backend=default_backend())
def number_as_bytes(number, pad_bits=None):
"""
Given a number, format as a little endian array of bytes
"""
result = int_to_bytes(number)[::-1]
while pad_bits is not None and len(result) < (pad_bits // 8):
result += b'\x00'
return result
def calculate_ds_parameters(privkey, priv_key_pass):
private_key = load_privatekey(privkey, priv_key_pass)
if not isinstance(private_key, rsa.RSAPrivateKey):
print("Only RSA private keys are supported")
sys.exit(-1)
priv_numbers = private_key.private_numbers()
pub_numbers = private_key.public_key().public_numbers()
Y = priv_numbers.d
M = pub_numbers.n
key_size = private_key.key_size
supported_key_size = [1024, 2048, 3072, 4096]
if key_size not in supported_key_size:
print("Key size not supported, supported sizes are" + str(supported_key_size))
sys.exit(-1)
hmac_key = os.urandom(32)
with open(hmac_key_file, 'wb') as key_file:
key_file.write(hmac_key)
iv = os.urandom(16)
rr = 1 << (key_size * 2)
rinv = rr % pub_numbers.n
mprime = - rsa._modinv(M, 1 << 32)
mprime &= 0xFFFFFFFF
length = key_size // 32 - 1
aes_key = hmac.HMAC(hmac_key, b"\xFF" * 32, hashlib.sha256).digest()
md_in = number_as_bytes(Y, 4096) + \
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
struct.pack("<II", mprime, length) + \
iv
assert len(md_in) == 12480 / 8
md = hashlib.sha256(md_in).digest()
# Y4096 || M4096 || Rb4096 || M_prime32 || LENGTH32 || MD256 || 0x08*8
p = number_as_bytes(Y, 4096) + \
number_as_bytes(M, 4096) + \
number_as_bytes(rinv, 4096) + \
md + \
struct.pack("<II", mprime, length) + \
b'\x08' * 8
assert len(p) == 12672 / 8
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
c = encryptor.update(p) + encryptor.finalize()
return c, iv, key_size
class DefineArgs(object):
def __init__(self, attributes):
for key, value in attributes.items():
self.__setattr__(key, value)
def efuse_summary(esp,args):
efuses, _efuse_operations = espefuse.get_efuses(esp, esp.CHIP_NAME, False, False, False)
summary_args = DefineArgs({
'baud': 115200,
'before': 'default_reset',
'chip': esp.CHIP_NAME,
'debug': False,
'do_not_confirm': False,
'file': sys.stdout,
'mode':'w',
'encding': 'utf-8',
'format': 'summary',
'operation': 'summary',
'port':args.port,
})
print("\n\n\n\t---SUMMARY START---\n")
espefuse.summary(esp, efuses, summary_args)
print("\n\t---SUMMARY END---\n\n")
def efuse_burn_key(esp, args):
efuses, efuse_operations = espefuse.get_efuses(esp, esp.CHIP_NAME, False, False, False)
if args.efuse_key_id is None:
print("efuse Key id cannot be None")
sys.exit(-1)
key_file = open(hmac_key_file, 'rb')
# First element of _KEYBLOCKS is config data so add offset of 1
key_block = efuses._KEYBLOCKS[args.efuse_key_id + 1][0]
burn_key_args = DefineArgs({
'baud': 115200,
'before': 'default_reset',
'chip': esp.CHIP_NAME,
'debug': False,
'do_not_confirm': False,
'block': [key_block],
'keyfile': [key_file],
'keypurpose': ['HMAC_DOWN_DIGITAL_SIGNATURE'],
'operation': 'burn_key',
'force_write_always': False,
'no_read_protect': True,
'no_write_protect': False,
'port': args.port,
})
try:
efuse_operations.burn_key(esp, efuses, burn_key_args, None)
key_file.close()
except esptool.FatalError:
print("\nERROR: The provided key block already contains previously burned key, please use a different key block ID")
sys.exit(-1)
def generate_csv_file(c, iv, hmac_key_id, key_size, csv_file):
with open(csv_file, 'wt', encoding='utf8') as f:
f.write("# This is a generated csv file containing required parameters for the Digital Signature operaiton\n")
f.write("key,type,encoding,value\nesp_ds_ns,namespace,,\n")
f.write("esp_ds_c,data,hex2bin,%s\n" % (c.hex()))
f.write("esp_ds_iv,data,hex2bin,%s\n" % (iv.hex()))
f.write("esp_ds_key_id,data,u8,%d\n" % (hmac_key_id))
f.write("esp_ds_rsa_len,data,u16,%d\n" % (key_size))
def generate_nvs_partition(input_filename, output_filename):
nvs_args = DefineArgs({
'input': input_filename,
'outdir': os.getcwd(),
'output': output_filename,
'size': hex(0x3000),
'version': 2,
'keyfile':None,
})
nvs_gen.generate(nvs_args, is_encr_enabled=False, encr_key=None)
def flash_nvs_partition(bin_path, addr, esp):
esp.connect()
print(bin_path)
abs_bin_path = os.path.dirname(os.path.abspath(__file__)) + '/' + bin_path
print(abs_bin_path)
if (os.path.exists(abs_bin_path) is False):
print("NVS partition not found")
sys.exit(-1)
with open(bin_path, 'rb') as nvs_file:
flash_file = [(addr, nvs_file)]
flash_args = DefineArgs({
'flash_size': '4MB',
'flash_mode': 'qio',
'flash_freq': '80m',
'addr_filename': flash_file,
'no_stub': False,
'compress': False,
'verify': False,
'encrypt': False,
'erase_all': False,
})
esp.change_baud(baud=921600)
esptool.write_flash(esp, flash_args)
def main():
parser = argparse.ArgumentParser(description='''Provision the ESPWROOM32SE device with
device_certificate and signer_certificate required for TLS authentication''')
parser.add_argument(
'--private-key',
dest='privkey',
default='main/client.key',
metavar='relative/path/to/client-priv-key',
help='relative path(from secure_cert_mfg.py) to signer certificate private key')
parser.add_argument(
"--pwd", '--password',
dest='priv_key_pass',
metavar='[password]',
help='the password associated with the private key')
parser.add_argument(
'--summary',
dest='summary',action='store_true',
help='Provide this option to print efuse summary the chip')
parser.add_argument(
'--efuse_key_id',
dest='efuse_key_id', type=int, choices=range(1,6),
metavar='[key_id] ',
default=1,
help='Provide the efuse key_id which contains/will contain HMAC_KEY, default is 1')
parser.add_argument(
"--port", '-p',
dest='port',
metavar='[port]',
required=True,
help='UART com port to which ESP device is connected')
parser.add_argument(
'--overwrite',
dest='overwrite', action='store_true',
help='Overwrite previously generated keys')
args = parser.parse_args()
esp = esptool.ESPLoader.detect_chip(args.port,baud=115200)
if (esp.CHIP_NAME != 'ESP32-S2'):
print("Only ESP32S2 chip is supported")
sys.exit(-1)
if args.summary is not False:
efuse_summary(esp, args)
sys.exit(0)
if (os.path.exists(esp_ds_data_dir) is False):
os.makedirs(esp_ds_data_dir)
else:
if (args.overwrite is False):
print("WARNING: previous ecrypted private key data exists.\nIf you want to overwrite,"
" please execute your command with providing \"--overwrite\" option")
sys.exit(0)
else:
print("overwriting previous encrypted private key data, as you have provided \"--overwrite\" option")
c, iv, key_size = calculate_ds_parameters(args.privkey, args.priv_key_pass)
efuse_burn_key(esp, args)
generate_csv_file(c, iv, args.efuse_key_id, key_size, csv_filename)
generate_nvs_partition(csv_filename, bin_filename)
flash_nvs_partition(bin_filename, 0x10000, esp)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "app_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,212 @@
/* MQTT Mutual Authentication Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "mqtt_client.h"
/* pre_prov - name of partition containing encrypted prv key parameters ( It is set as such to synchronize it with the pre provisioning service */
#define NVS_PARTITION_NAME "pre_prov"
/* esp_ds_ns - namespace used for defining values in esp_ds_nvs */
#define NVS_NAMESPACE "esp_ds_ns"
/* esp_ds_key_id - efuse key block id where 256 bit key is stored, which will be read by
* DS module to perform DS operation */
#define NVS_EFUSE_KEY_ID "esp_ds_key_id"
/* esp_ds_rsa_len - length of RSA private key (in bits) which is encrypted */
#define NVS_RSA_LEN "esp_ds_rsa_len"
/* following entries denote key(ASCII string) for particular value in key-value pair of esp_ds_nvs (which are defined in esp_ds_ns) */
/* ciphertext_c - encrypted RSA private key, see ESP32-S2 Techincal Reference Manual for more details */
#define NVS_CIPHER_C "esp_ds_c"
/* initialization vector (iv) - 256 bit value used to encrypt RSA private key (to generate ciphertext_c) */
#define NVS_IV "esp_ds_iv"
static const char *TAG = "MQTTS_EXAMPLE";
extern const uint8_t client_cert_pem_start[] asm("_binary_client_crt_start");
extern const uint8_t client_cert_pem_end[] asm("_binary_client_crt_end");
extern const uint8_t server_cert_pem_start[] asm("_binary_mosquitto_org_crt_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_mosquitto_org_crt_end");
static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event)
{
esp_mqtt_client_handle_t client = event->client;
int msg_id;
// your_context_t *context = event->context;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
void *esp_read_ds_data_from_nvs(void)
{
esp_ds_data_ctx_t *ds_data_ctx;
ds_data_ctx = (esp_ds_data_ctx_t *)malloc(sizeof(esp_ds_data_ctx_t));
if (ds_data_ctx == NULL) {
ESP_LOGE(TAG, "Error in allocating memory for esp_ds_data_context");
goto exit;
}
ds_data_ctx->esp_ds_data = (esp_ds_data_t *)calloc(1, sizeof(esp_ds_data_t));
if (ds_data_ctx->esp_ds_data == NULL) {
ESP_LOGE(TAG, "Could not allocate memory for DS data handle ");
goto exit;
}
nvs_handle_t esp_ds_nvs_handle;
esp_err_t esp_ret;
esp_ret = nvs_flash_init_partition(NVS_PARTITION_NAME);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Error in esp_ds_nvs partition init, returned %02x", esp_ret);
goto exit;
}
esp_ret = nvs_open_from_partition(NVS_PARTITION_NAME, NVS_NAMESPACE,
NVS_READONLY, &esp_ds_nvs_handle);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Error in esp_ds_nvs partition open, returned %02x", esp_ret);
goto exit;
}
esp_ret = nvs_get_u8(esp_ds_nvs_handle, NVS_EFUSE_KEY_ID, &ds_data_ctx->efuse_key_id);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Error in efuse_key_id value from nvs, returned %02x", esp_ret);
goto exit;
}
esp_ret = nvs_get_u16(esp_ds_nvs_handle, NVS_RSA_LEN, &ds_data_ctx->rsa_length_bits);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Error in reading rsa key length value from nvs, returned %02x", esp_ret);
goto exit;
}
size_t blob_length = ESP_DS_C_LEN;
esp_ret = nvs_get_blob(esp_ds_nvs_handle, NVS_CIPHER_C, (void *)(ds_data_ctx->esp_ds_data->c), &blob_length);
if ((esp_ret != ESP_OK) || (blob_length != ESP_DS_C_LEN)) {
ESP_LOGE(TAG, "Error in reading initialization vector value from nvs,bytes_read = %d, returned %02x", blob_length, esp_ret);
goto exit;
}
blob_length = ESP_DS_IV_LEN;
esp_ret = nvs_get_blob(esp_ds_nvs_handle, NVS_IV, (void *)(ds_data_ctx->esp_ds_data->iv), &blob_length);
if ((esp_ret != ESP_OK) || (blob_length != ESP_DS_IV_LEN)) {
ESP_LOGE(TAG, "Error in reading initialization vector value from nvs,bytes_read = %d, returned %02x", blob_length, esp_ret);
goto exit;
}
return (void *)ds_data_ctx;
exit:
if (ds_data_ctx != NULL) {
free(ds_data_ctx->esp_ds_data);
}
free(ds_data_ctx);
return NULL;
}
static void mqtt_app_start(void)
{
/* The context is used by the DS peripheral, should not be freed */
void *ds_data = esp_read_ds_data_from_nvs();
if (ds_data == NULL) {
ESP_LOGE(TAG, "Error in reading DS data from NVS");
vTaskDelete(NULL);
}
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtts://test.mosquitto.org:8884",
.event_handle = mqtt_event_handler,
.cert_pem = (const char *)server_cert_pem_start,
.client_cert_pem = (const char *)client_cert_pem_start,
.client_key_pem = NULL,
.ds_data = ds_data,
};
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_start(client);
}
void app_main(void)
{
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
mqtt_app_start();
}

View File

@ -0,0 +1 @@
Please paste your client certificate here (follow instructions in README.md)

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
-----END CERTIFICATE-----
---

View File

@ -0,0 +1,7 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
pre_prov,data,nvs,0x10000,0x3000,
factory,app,factory,0x20000,1M,
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs,data,nvs,0x9000,24K,
4 phy_init,data,phy,0xf000,4K,
5 pre_prov,data,nvs,0x10000,0x3000,
6 factory,app,factory,0x20000,1M,

View File

@ -0,0 +1,2 @@
CONFIG_PARTITION_TABLE_CUSTOM=y