forked from espressif/esp-idf
Merge branch 'feat/dynamic_buffer_tls1.3' into 'master'
feat(mbedtls): add support for dynamic buffer for TLS1.3 Closes IDFGH-14708, IDF-12469, IDF-9178, and IDF-1725 See merge request espressif/esp-idf!38258
This commit is contained in:
@@ -160,6 +160,11 @@ int esp_tls_conn_destroy(esp_tls_t *tls)
|
||||
ret = close(tls->sockfd);
|
||||
}
|
||||
esp_tls_internal_event_tracker_destroy(tls->error_handle);
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
if (tls->client_session) {
|
||||
free(tls->client_session);
|
||||
}
|
||||
#endif // CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
free(tls);
|
||||
tls = NULL;
|
||||
return ret;
|
||||
@@ -180,6 +185,10 @@ esp_tls_t *esp_tls_init(void)
|
||||
}
|
||||
_esp_tls_net_init(tls);
|
||||
tls->sockfd = -1;
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
tls->client_session = NULL;
|
||||
tls->client_session_len = 0;
|
||||
#endif // CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
return tls;
|
||||
}
|
||||
|
||||
|
@@ -190,12 +190,38 @@ esp_tls_client_session_t *esp_mbedtls_get_client_session(esp_tls_t *tls)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_tls_client_session_t *client_session = (esp_tls_client_session_t*)calloc(1, sizeof(esp_tls_client_session_t));
|
||||
esp_tls_client_session_t *client_session = NULL;
|
||||
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3
|
||||
/* For TLS 1.3, check if the session ticket is saved in the esp-tls context */
|
||||
if (mbedtls_ssl_get_version_number(&tls->ssl) == MBEDTLS_SSL_VERSION_TLS1_3) {
|
||||
if (tls->client_session == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
client_session = calloc(1, sizeof(esp_tls_client_session_t));
|
||||
if (client_session == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for client session ctx");
|
||||
return NULL;
|
||||
}
|
||||
/* If the session ticket is saved in the esp-tls context, load it into the client session */
|
||||
int ret = mbedtls_ssl_session_load(&client_session->saved_session, tls->client_session, tls->client_session_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in loading the client ssl session");
|
||||
free(client_session);
|
||||
return NULL;
|
||||
}
|
||||
return client_session;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* In case of TLS 1.2, the session context is available as long as the connection is active */
|
||||
client_session = (esp_tls_client_session_t*)calloc(1, sizeof(esp_tls_client_session_t));
|
||||
if (client_session == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for client session ctx");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the session ticket from the mbedtls context and load it into the client session */
|
||||
int ret = mbedtls_ssl_get_session(&tls->ssl, &(client_session->saved_session));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in obtaining the client ssl session");
|
||||
@@ -259,18 +285,81 @@ ssize_t esp_mbedtls_read(esp_tls_t *tls, char *data, size_t datalen)
|
||||
{
|
||||
|
||||
ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);
|
||||
#if CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS
|
||||
// If a post-handshake message is received, connection state is changed to `MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET`
|
||||
// Call mbedtls_ssl_read() till state is `MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET` or return code is `MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET`
|
||||
// to process session tickets in TLS 1.3 connection
|
||||
#if defined(CONFIG_MBEDTLS_SSL_PROTO_TLS1_3)
|
||||
/*
|
||||
* As per RFC 8446, section 4.6.1 the server may send a NewSessionTicket message at any time after the
|
||||
* client Finished message.
|
||||
* If a post-handshake message is received, connection state is changed to `MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET`
|
||||
* Call mbedtls_ssl_read() till state is `MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET` or return code is `MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET`
|
||||
* to process session tickets in TLS 1.3 connection.
|
||||
* This handshake message should be processed by mbedTLS and not by the application.
|
||||
*/
|
||||
if (mbedtls_ssl_get_version_number(&tls->ssl) == MBEDTLS_SSL_VERSION_TLS1_3) {
|
||||
while (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET || tls->ssl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET) {
|
||||
ESP_LOGD(TAG, "got session ticket in TLS 1.3 connection, retry read");
|
||||
#if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
if (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) {
|
||||
esp_tls_client_session_t *tls13_saved_client_session = calloc(1, sizeof(esp_tls_client_session_t));
|
||||
if (tls13_saved_client_session == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for client session ctx");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
ret = mbedtls_ssl_get_session(&tls->ssl, &tls13_saved_client_session->saved_session);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in getting the client ssl session");
|
||||
free(tls13_saved_client_session);
|
||||
tls13_saved_client_session = NULL;
|
||||
return ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED;
|
||||
}
|
||||
ESP_LOGD(TAG, "Session ticket received");
|
||||
|
||||
size_t session_ticket_len = 0;
|
||||
ret = mbedtls_ssl_session_save(&tls13_saved_client_session->saved_session, NULL, 0, &session_ticket_len);
|
||||
if (ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) {
|
||||
ESP_LOGE(TAG, "Error in getting the client ssl session length");
|
||||
free(tls13_saved_client_session);
|
||||
tls13_saved_client_session = NULL;
|
||||
return ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Session ticket length: %zu", session_ticket_len);
|
||||
if (tls->client_session != NULL) {
|
||||
free(tls->client_session);
|
||||
tls->client_session = NULL;
|
||||
}
|
||||
/* Allocate memory for the session ticket */
|
||||
tls->client_session = calloc(1, session_ticket_len);
|
||||
if (tls->client_session == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for client session ctx");
|
||||
free(tls13_saved_client_session);
|
||||
tls13_saved_client_session = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ret = mbedtls_ssl_session_save(&tls13_saved_client_session->saved_session, (unsigned char *)tls->client_session, session_ticket_len, &session_ticket_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in saving the client ssl session");
|
||||
mbedtls_print_error_msg(ret);
|
||||
free(tls->client_session);
|
||||
tls->client_session = NULL;
|
||||
free(tls13_saved_client_session);
|
||||
tls13_saved_client_session = NULL;
|
||||
return ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Session ticket saved in the client session context");
|
||||
tls->client_session_len = session_ticket_len;
|
||||
mbedtls_ssl_session_free(&tls13_saved_client_session->saved_session);
|
||||
free(tls13_saved_client_session);
|
||||
tls13_saved_client_session = NULL;
|
||||
}
|
||||
#endif // CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
/* After handling the session ticket, we need to attempt to read again
|
||||
* to either get application data or process another ticket */
|
||||
ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS
|
||||
|
||||
#endif // CONFIG_MBEDTLS_SSL_PROTO_TLS1_3
|
||||
if (ret < 0) {
|
||||
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
|
||||
return 0;
|
||||
@@ -762,9 +851,14 @@ esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t
|
||||
ESP_LOGD(TAG, "Enabling client-side tls session ticket support");
|
||||
mbedtls_ssl_conf_session_tickets(&tls->conf, MBEDTLS_SSL_SESSION_TICKETS_ENABLED);
|
||||
mbedtls_ssl_conf_renegotiation(&tls->conf, MBEDTLS_SSL_RENEGOTIATION_ENABLED);
|
||||
|
||||
#endif /* CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS */
|
||||
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3
|
||||
#if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS || CONFIG_MBEDTLS_DYNAMIC_BUFFER
|
||||
mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&tls->conf, MBEDTLS_SSL_SESSION_TICKETS_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (cfg->crt_bundle_attach != NULL) {
|
||||
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
ESP_LOGD(TAG, "Use certificate bundle");
|
||||
|
@@ -68,6 +68,10 @@ struct esp_tls {
|
||||
bool use_ecdsa_peripheral; /*!< Use the ECDSA peripheral for the private key operations. */
|
||||
uint8_t ecdsa_efuse_blk; /*!< The efuse block number where the ECDSA key is stored. */
|
||||
#endif
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS
|
||||
unsigned char *client_session; /*!< Pointer for the serialized client session ticket context. */
|
||||
size_t client_session_len; /*!< Length of the serialized client session ticket context. */
|
||||
#endif /* CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS */
|
||||
#elif CONFIG_ESP_TLS_USING_WOLFSSL
|
||||
void *priv_ctx;
|
||||
void *priv_ssl;
|
||||
|
@@ -351,6 +351,7 @@ if(CONFIG_MBEDTLS_DYNAMIC_BUFFER)
|
||||
set(WRAP_FUNCTIONS
|
||||
mbedtls_ssl_write_client_hello
|
||||
mbedtls_ssl_handshake_client_step
|
||||
mbedtls_ssl_tls13_handshake_client_step
|
||||
mbedtls_ssl_handshake_server_step
|
||||
mbedtls_ssl_read
|
||||
mbedtls_ssl_write
|
||||
|
@@ -172,10 +172,10 @@ menu "mbedTLS"
|
||||
default 4 if MBEDTLS_DEBUG_LEVEL_VERBOSE
|
||||
|
||||
menu "mbedTLS v3.x related"
|
||||
# NOTE: MBEDTLS_DYNAMIC_BUFFER feature is not supported with TLS 1.3 yet. Ref: IDF-4762
|
||||
config MBEDTLS_SSL_PROTO_TLS1_3
|
||||
bool "Support TLS 1.3 protocol"
|
||||
depends on MBEDTLS_TLS_ENABLED && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE && !MBEDTLS_DYNAMIC_BUFFER
|
||||
depends on MBEDTLS_TLS_ENABLED && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
|
||||
select MBEDTLS_CLIENT_SSL_SESSION_TICKETS if MBEDTLS_DYNAMIC_BUFFER
|
||||
select MBEDTLS_HKDF_C
|
||||
default n
|
||||
|
||||
|
221
components/mbedtls/port/dynamic/dynamic_buffer_architecture.md
Normal file
221
components/mbedtls/port/dynamic/dynamic_buffer_architecture.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# Dynamic Buffer Management in mbedTLS for ESP-IDF
|
||||
|
||||
## Executive Summary
|
||||
|
||||
ESP-IDF implements a dynamic buffer management system for mbedTLS to optimize memory usage during TLS/SSL connections. This architecture significantly reduces RAM requirements on memory-constrained ESP devices by intelligently allocating buffers only when needed and sizing them according to actual message requirements rather than worst-case scenarios.
|
||||
|
||||
## The Problem We're Solving
|
||||
|
||||
Standard TLS implementations allocate large static buffers that:
|
||||
- Reserve memory for the entire connection lifetime
|
||||
- Are sized for worst-case scenarios (large certificates, messages)
|
||||
- Remain allocated even when not in use
|
||||
|
||||
On memory-constrained IoT devices like ESP32, this traditional approach is inefficient and limits the number of concurrent connections possible.
|
||||
|
||||
## Our Solution: The Dynamic Buffer Approach
|
||||
|
||||
Instead of static allocation, our system:
|
||||
1. **Allocates buffers only when needed**
|
||||
2. **Right-sizes buffers** based on actual message requirements
|
||||
3. **Releases memory** when it's not needed
|
||||
4. **Preserves critical state** in small cache buffers
|
||||
|
||||
## How It Works: A Conceptual View
|
||||
|
||||
### Buffer Lifecycle
|
||||
|
||||
1. **Starting state**: Begin with minimal or no buffer allocation
|
||||
2. **Just before data transmission/reception**: Allocate right-sized buffer
|
||||
3. **During data processing**: Use the allocated buffer
|
||||
4. **After processing**: Replace large buffer with small cache buffer
|
||||
5. **Repeat** as needed during the connection
|
||||
|
||||
### Key Concepts Illustrated
|
||||
|
||||
#### Transmission (TX) Buffer Handling
|
||||
|
||||
```
|
||||
[Small idle buffer] → [Right-sized TX buffer] → [Back to small buffer]
|
||||
```
|
||||
|
||||
#### Reception (RX) Buffer Handling
|
||||
|
||||
```
|
||||
[No buffer] → [Header buffer] → [Right-sized RX buffer] → [Small cache buffer]
|
||||
```
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
Our implementation follows these steps for each operation:
|
||||
|
||||
1. **Before operation**: Check if we need to allocate or resize a buffer
|
||||
2. **During operation**: Use the standard mbedTLS functions with our buffers
|
||||
3. **After operation**: Shrink or release buffers that are no longer needed
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. Custom Buffer Structure
|
||||
|
||||
We use a custom buffer structure that includes metadata:
|
||||
|
||||
```c
|
||||
struct esp_mbedtls_ssl_buf {
|
||||
esp_mbedtls_ssl_buf_states state; // CACHED or NOT_CACHED
|
||||
unsigned int len; // Buffer size
|
||||
unsigned char buf[0]; // Flexible array for actual data
|
||||
};
|
||||
```
|
||||
|
||||
This structure allows us to:
|
||||
|
||||
- Track whether a buffer contains important state
|
||||
- Store the buffer's size for dynamic resizing
|
||||
- Use a flexible array member for efficient memory layout
|
||||
|
||||
### 2. Buffer States
|
||||
|
||||
- **CACHED**: Contains important cryptographic state that must be preserved
|
||||
- **NOT_CACHED**: Can be safely replaced or released
|
||||
|
||||
The state tracking is critical for maintaining TLS protocol security while optimizing memory.
|
||||
|
||||
### 3. Critical State Preservation
|
||||
|
||||
When replacing large buffers with small cache buffers, we preserve:
|
||||
|
||||
- **SSL counter values**: Used for replay protection
|
||||
- **Initialization vectors**: Required for encryption/decryption
|
||||
|
||||
These small amounts of cryptographic state must be maintained between operations to keep the TLS connection secure.
|
||||
|
||||
### 4. Memory Management Functions
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|---------|
|
||||
| `esp_mbedtls_add_tx_buffer()` | Allocates a transmission buffer sized for the outgoing message |
|
||||
| `esp_mbedtls_free_tx_buffer()` | Replaces TX buffer with small cache buffer after transmission |
|
||||
| `esp_mbedtls_add_rx_buffer()` | Reads record header and allocates right-sized reception buffer |
|
||||
| `esp_mbedtls_free_rx_buffer()` | Replaces RX buffer with small cache buffer after processing |
|
||||
|
||||
### 5. Function Wrapping
|
||||
|
||||
We intercept key mbedTLS functions using GCC's function wrapping:
|
||||
|
||||
```c
|
||||
int __wrap_mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len) {
|
||||
// 1. Allocate right-sized buffer
|
||||
// 2. Call original function
|
||||
// 3. Free buffer when done
|
||||
}
|
||||
```
|
||||
|
||||
This allows seamless integration without modifying the mbedTLS source code.
|
||||
|
||||
## The Handshake Process Design
|
||||
|
||||
During TLS handshaking, memory needs change dramatically between steps. Our system tracks the handshake state and manages memory accordingly:
|
||||
|
||||
### Client-Side Handshake Memory Management
|
||||
|
||||
| Handshake Step | Memory Action |
|
||||
|----------------|---------------|
|
||||
| Client Hello | Allocate TX buffer |
|
||||
| Server Hello | Allocate RX buffer |
|
||||
| Server Certificate | Allocate RX buffer |
|
||||
| Certificate Verify | Allocate TX buffer |
|
||||
| Free certificate resources | Release CA certificates |
|
||||
| Client Key Exchange | Allocate TX buffer |
|
||||
| Change Cipher Spec | Small buffer |
|
||||
| Finished | Small buffers |
|
||||
|
||||
|
||||
|
||||
## Implementation Design: SSL Read Operation
|
||||
|
||||
The dynamic buffer allocation for SSL read operations follows this design:
|
||||
|
||||
1. First, read just the TLS record header:
|
||||
|
||||
```c
|
||||
// Read just the header to determine full message size
|
||||
ssl->in_hdr = msg_head;
|
||||
ssl->in_len = msg_head + 3;
|
||||
mbedtls_ssl_fetch_input(ssl, mbedtls_ssl_in_hdr_len(ssl));
|
||||
|
||||
// Parse header to get message length
|
||||
esp_mbedtls_parse_record_header(ssl);
|
||||
in_msglen = ssl->in_msglen;
|
||||
```
|
||||
|
||||
2. Once we know the exact message size, allocate a buffer that's precisely sized:
|
||||
|
||||
```c
|
||||
// Allocate buffer of right size
|
||||
buffer_len = in_msglen + overhead; // Add necessary TLS overhead
|
||||
esp_buf = mbedtls_calloc(1, SSL_BUF_HEAD_OFFSET_SIZE + buffer_len);
|
||||
|
||||
// Initialize and set up buffer
|
||||
esp_mbedtls_init_ssl_buf(esp_buf, buffer_len);
|
||||
init_rx_buffer(ssl, esp_buf->buf);
|
||||
```
|
||||
|
||||
3. After processing, preserve critical state and free the large buffer:
|
||||
|
||||
```c
|
||||
// Save critical state (counters and IVs)
|
||||
memcpy(buf, ssl->in_ctr, 8);
|
||||
memcpy(buf + 8, ssl->in_iv, 8);
|
||||
|
||||
// Free large buffer
|
||||
esp_mbedtls_free_buf(ssl->in_buf);
|
||||
|
||||
// Allocate small cache buffer
|
||||
esp_buf = mbedtls_calloc(1, SSL_BUF_HEAD_OFFSET_SIZE + 16);
|
||||
esp_mbedtls_init_ssl_buf(esp_buf, 16);
|
||||
|
||||
// Restore critical state in small buffer
|
||||
memcpy(esp_buf->buf, buf, 16);
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The dynamic buffer system includes configurable options:
|
||||
|
||||
- `CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT`: Free CA certificates after verification
|
||||
- `CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA`: Free DHM parameters and key material when no longer needed
|
||||
|
||||
These can be enabled in ESP-IDF's menuconfig system.
|
||||
|
||||
## Integration Architecture
|
||||
|
||||
The implementation uses function wrapping to seamlessly integrate with mbedTLS:
|
||||
|
||||
```
|
||||
Application → mbedTLS API → Our Wrapper Functions → Original mbedTLS Functions
|
||||
```
|
||||
|
||||
Key wrapped functions include:
|
||||
- `mbedtls_ssl_setup`: Initialize with minimal buffers
|
||||
- `mbedtls_ssl_read`/`mbedtls_ssl_write`: Dynamic buffer management during I/O
|
||||
- `mbedtls_ssl_handshake_client_step`: Handshake-aware memory management
|
||||
- `mbedtls_ssl_free`: Clean up all allocated memory
|
||||
|
||||
## Design Benefits
|
||||
|
||||
The dynamic buffer management design provides several key benefits:
|
||||
|
||||
1. **Memory Efficiency**: Significantly reduced peak memory usage
|
||||
2. **Scalability**: Adapts to different TLS message sizes dynamically
|
||||
3. **Transparency**: Application code doesn't need to be aware of the memory optimization
|
||||
4. **Compatibility**: Maintains full mbedTLS functionality
|
||||
|
||||
## Summary
|
||||
|
||||
The dynamic buffer management system in ESP-IDF's mbedTLS port follows a sophisticated architecture that:
|
||||
1. Allocates only what's needed, when it's needed
|
||||
2. Preserves critical state in small buffers
|
||||
3. Is aware of the TLS handshake flow
|
||||
4. Releases memory as soon as it's no longer required
|
||||
|
||||
This architecture enables more efficient use of RAM on memory-constrained devices while maintaining the security guarantees of the TLS protocol.
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -352,6 +352,8 @@ int esp_mbedtls_add_rx_buffer(mbedtls_ssl_context *ssl)
|
||||
ESP_LOGD(TAG, "mbedtls_ssl_fetch_input reads data times out");
|
||||
} else if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
ESP_LOGD(TAG, "mbedtls_ssl_fetch_input wants to read more data");
|
||||
} else if (ret == MBEDTLS_ERR_SSL_CONN_EOF) {
|
||||
ESP_LOGD(TAG, "mbedtls_ssl_fetch_input connection EOF");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "mbedtls_ssl_fetch_input error=%d", -ret);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -9,9 +9,11 @@
|
||||
|
||||
int __real_mbedtls_ssl_handshake_client_step(mbedtls_ssl_context *ssl);
|
||||
int __real_mbedtls_ssl_write_client_hello(mbedtls_ssl_context *ssl);
|
||||
int __real_mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl);
|
||||
|
||||
int __wrap_mbedtls_ssl_handshake_client_step(mbedtls_ssl_context *ssl);
|
||||
int __wrap_mbedtls_ssl_write_client_hello(mbedtls_ssl_context *ssl);
|
||||
int __wrap_mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl);
|
||||
|
||||
static const char *TAG = "SSL client";
|
||||
|
||||
@@ -52,6 +54,15 @@ static int manage_resource(mbedtls_ssl_context *ssl, bool add)
|
||||
|
||||
|
||||
case MBEDTLS_SSL_SERVER_HELLO:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_rx_buffer(ssl));
|
||||
} else {
|
||||
if (!ssl->MBEDTLS_PRIVATE(keep_current_message)) {
|
||||
CHECK_OK(esp_mbedtls_free_rx_buffer(ssl));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MBEDTLS_SSL_ENCRYPTED_EXTENSIONS:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_rx_buffer(ssl));
|
||||
} else {
|
||||
@@ -121,6 +132,13 @@ static int manage_resource(mbedtls_ssl_context *ssl, bool add)
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, buffer_len));
|
||||
}
|
||||
break;
|
||||
case MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY:
|
||||
if (add) {
|
||||
size_t buffer_len = MBEDTLS_SSL_OUT_BUFFER_LEN;
|
||||
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, buffer_len));
|
||||
}
|
||||
break;
|
||||
case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE:
|
||||
if (add) {
|
||||
size_t buffer_len = MBEDTLS_SSL_OUT_BUFFER_LEN;
|
||||
@@ -191,6 +209,39 @@ static int manage_resource(mbedtls_ssl_context *ssl, bool add)
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE)
|
||||
case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, MBEDTLS_SSL_OUT_BUFFER_LEN));
|
||||
}
|
||||
break;
|
||||
case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, MBEDTLS_SSL_OUT_BUFFER_LEN));
|
||||
}
|
||||
break;
|
||||
#if defined(MBEDTLS_SSL_EARLY_DATA)
|
||||
case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, MBEDTLS_SSL_OUT_BUFFER_LEN));
|
||||
}
|
||||
break;
|
||||
case MBEDTLS_SSL_END_OF_EARLY_DATA:
|
||||
size_t buffer_len = MBEDTLS_SSL_OUT_BUFFER_LEN;
|
||||
|
||||
CHECK_OK(esp_mbedtls_add_tx_buffer(ssl, buffer_len));
|
||||
break;
|
||||
#endif /* MBEDTLS_SSL_EARLY_DATA */
|
||||
#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */
|
||||
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
|
||||
case MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET:
|
||||
if (add) {
|
||||
CHECK_OK(esp_mbedtls_add_rx_buffer(ssl));
|
||||
} else {
|
||||
CHECK_OK(esp_mbedtls_free_rx_buffer(ssl));
|
||||
}
|
||||
break;
|
||||
#endif /* MBEDTLS_SSL_SESSION_TICKETS */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -209,6 +260,17 @@ int __wrap_mbedtls_ssl_handshake_client_step(mbedtls_ssl_context *ssl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __wrap_mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl)
|
||||
{
|
||||
CHECK_OK(manage_resource(ssl, true));
|
||||
|
||||
CHECK_OK(__real_mbedtls_ssl_tls13_handshake_client_step(ssl));
|
||||
|
||||
CHECK_OK(manage_resource(ssl, false));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __wrap_mbedtls_ssl_write_client_hello(mbedtls_ssl_context *ssl)
|
||||
{
|
||||
CHECK_OK(manage_resource(ssl, true));
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -347,6 +347,43 @@ int __wrap_mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t
|
||||
|
||||
ret = __real_mbedtls_ssl_read(ssl, buf, len);
|
||||
|
||||
#if CONFIG_MBEDTLS_SSL_PROTO_TLS1_3
|
||||
/*
|
||||
* As per RFC 8446, section 4.6.1 the server may send a NewSessionTicket message at any time after the
|
||||
* client Finished message.
|
||||
* If a post-handshake message is received, connection state is changed to `MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET`
|
||||
* and when the message is parsed, the return value is `MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET`.
|
||||
* When the session ticket is parsed, reduce the ssl->in_msglen by the length of the
|
||||
* NewSessionTicket message.
|
||||
*/
|
||||
if (mbedtls_ssl_get_version_number(ssl) == MBEDTLS_SSL_VERSION_TLS1_3) {
|
||||
if (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) {
|
||||
ESP_LOGD(TAG, "got session ticket in TLS 1.3 connection, retry read");
|
||||
|
||||
/* At this stage, we have received a NewSessionTicket messages
|
||||
* We should decrement the ssl->in_msglen by the length of the
|
||||
* NewSessionTicket message.
|
||||
*
|
||||
* The NewSessionTicket message has been parsed internally by mbedTLS
|
||||
* and it is stored in the mbedTLS context. This msglen size update
|
||||
* is also handled by mbedTLS but in case of dynamic buffer,
|
||||
* we need to free the rx buffer if it is allocated
|
||||
* and prepare for the next read. So we have to update the msglen
|
||||
* by ourselves and free the rx buffer if no more data is available.
|
||||
*/
|
||||
if (ssl->MBEDTLS_PRIVATE(in_hslen) < ssl->MBEDTLS_PRIVATE(in_msglen)) {
|
||||
ssl->MBEDTLS_PRIVATE(in_msglen) -= ssl->MBEDTLS_PRIVATE(in_hslen);
|
||||
memmove(ssl->MBEDTLS_PRIVATE(in_msg), ssl->MBEDTLS_PRIVATE(in_msg) + ssl->MBEDTLS_PRIVATE(in_hslen),
|
||||
ssl->MBEDTLS_PRIVATE(in_msglen));
|
||||
MBEDTLS_PUT_UINT16_BE(ssl->MBEDTLS_PRIVATE(in_msglen), ssl->in_len, 0);
|
||||
} else {
|
||||
ssl->MBEDTLS_PRIVATE(in_msglen) = 0;
|
||||
}
|
||||
ssl->MBEDTLS_PRIVATE(in_hslen) = 0;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_MBEDTLS_SSL_PROTO_TLS1_3
|
||||
|
||||
if (rx_done(ssl)) {
|
||||
CHECK_OK(esp_mbedtls_free_rx_buffer(ssl));
|
||||
}
|
||||
|
@@ -23,4 +23,11 @@ menu "Example Configuration"
|
||||
bool
|
||||
default y if EXAMPLE_LOCAL_SERVER_URL = "FROM_STDIN"
|
||||
|
||||
config EXAMPLE_SSL_PROTO_TLS1_3_CLIENT
|
||||
bool "Enable TLS 1.3 client test"
|
||||
default n
|
||||
select MBEDTLS_SSL_PROTO_TLS1_3
|
||||
help
|
||||
Enable TLS 1.3 client test support for the example.
|
||||
|
||||
endmenu
|
||||
|
@@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
@@ -46,9 +46,15 @@
|
||||
#include "time_sync.h"
|
||||
|
||||
/* Constants that aren't configurable in menuconfig */
|
||||
#define WEB_SERVER "www.howsmyssl.com"
|
||||
#ifdef CONFIG_EXAMPLE_SSL_PROTO_TLS1_3_CLIENT
|
||||
#define WEB_SERVER "tls13.browserleaks.com"
|
||||
#define WEB_PORT "443"
|
||||
#define WEB_URL "https://tls13.browserleaks.com/tls"
|
||||
#else
|
||||
#define WEB_SERVER "howsmyssl.com"
|
||||
#define WEB_PORT "443"
|
||||
#define WEB_URL "https://www.howsmyssl.com/a/check"
|
||||
#endif
|
||||
|
||||
#define SERVER_URL_MAX_SZ 256
|
||||
|
||||
@@ -85,9 +91,15 @@ extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_
|
||||
extern const uint8_t local_server_cert_pem_start[] asm("_binary_local_server_cert_pem_start");
|
||||
extern const uint8_t local_server_cert_pem_end[] asm("_binary_local_server_cert_pem_end");
|
||||
#if CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
|
||||
#if defined(CONFIG_EXAMPLE_SSL_PROTO_TLS1_3_CLIENT)
|
||||
static const int server_supported_ciphersuites[] = {MBEDTLS_TLS1_3_AES_256_GCM_SHA384, MBEDTLS_TLS1_3_AES_128_CCM_SHA256, 0};
|
||||
static const int server_unsupported_ciphersuites[] = {MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, 0};
|
||||
#else
|
||||
static const int server_supported_ciphersuites[] = {MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0};
|
||||
static const int server_unsupported_ciphersuites[] = {MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, 0};
|
||||
#endif
|
||||
#endif // CONFIG_EXAMPLE_SSL_PROTO_TLS1_3_CLIENT
|
||||
#endif // CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
|
||||
static esp_tls_client_session_t *tls_client_session = NULL;
|
||||
static bool save_client_session = false;
|
||||
@@ -119,14 +131,6 @@ static void https_get_request(esp_tls_cfg_t cfg, const char *WEB_SERVER_URL, con
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
|
||||
/* The TLS session is successfully established, now saving the session ctx for reuse */
|
||||
if (save_client_session) {
|
||||
esp_tls_free_client_session(tls_client_session);
|
||||
tls_client_session = esp_tls_get_client_session(tls);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t written_bytes = 0;
|
||||
do {
|
||||
ret = esp_tls_conn_write(tls,
|
||||
@@ -166,6 +170,14 @@ static void https_get_request(esp_tls_cfg_t cfg, const char *WEB_SERVER_URL, con
|
||||
putchar('\n'); // JSON output doesn't have a newline at end
|
||||
} while (1);
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
|
||||
/* The TLS session is successfully established, now saving the session ctx for reuse */
|
||||
if (save_client_session) {
|
||||
esp_tls_free_client_session(tls_client_session);
|
||||
tls_client_session = esp_tls_get_client_session(tls);
|
||||
}
|
||||
#endif
|
||||
|
||||
cleanup:
|
||||
esp_tls_conn_destroy(tls);
|
||||
exit:
|
||||
@@ -251,6 +263,9 @@ static void https_get_request_using_already_saved_session(const char *url)
|
||||
ESP_LOGI(TAG, "https_request using saved client session");
|
||||
esp_tls_cfg_t cfg = {
|
||||
.client_session = tls_client_session,
|
||||
.cacert_buf = (const unsigned char *) local_server_cert_pem_start,
|
||||
.cacert_bytes = local_server_cert_pem_end - local_server_cert_pem_start,
|
||||
.skip_common_name = true,
|
||||
};
|
||||
https_get_request(cfg, url, LOCAL_SRV_REQUEST);
|
||||
esp_tls_free_client_session(tls_client_session);
|
||||
|
@@ -125,6 +125,90 @@ def test_examples_protocol_https_request_cli_session_tickets(dut: Dut) -> None:
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@pytest.mark.ethernet
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'ssldyn_tls1_3',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@pytest.mark.parametrize('erase_nvs', ['y'], indirect=True)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_examples_protocol_https_request_dynamic_buffers_tls1_3(dut: Dut) -> None:
|
||||
# Check for tls 1.3 connection using crt bundle with mbedtls dynamic resource enabled
|
||||
# check and log bin size
|
||||
binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
|
||||
# start https server
|
||||
server_port = 8070
|
||||
server_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_cert.pem')
|
||||
key_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_key.pem')
|
||||
thread1 = multiprocessing.Process(target=start_https_server, args=(server_file, key_file, '0.0.0.0', server_port))
|
||||
thread1.daemon = True
|
||||
thread1.start()
|
||||
logging.info('The server started on localhost:{}'.format(server_port))
|
||||
|
||||
dut.expect('Loaded app from partition at offset', timeout=30)
|
||||
try:
|
||||
try:
|
||||
ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
|
||||
print('Connected to AP/Ethernet with IP: {}'.format(ip_address))
|
||||
host_ip = get_host_ip4_by_dest_ip(ip_address)
|
||||
dut.expect('Start https_request example', timeout=30)
|
||||
print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port)))
|
||||
dut.write('https://' + host_ip + ':' + str(server_port))
|
||||
except pexpect.exceptions.TIMEOUT:
|
||||
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet')
|
||||
# Check for connection using already saved client session
|
||||
try:
|
||||
dut.expect('https_request to local server', timeout=30)
|
||||
dut.expect(
|
||||
['Connection established...', 'Reading HTTP response...', 'HTTP/1.1 200 OK', 'connection closed'],
|
||||
expect_all=True,
|
||||
)
|
||||
except Exception:
|
||||
logging.info('Failed to connect to local https server"')
|
||||
raise
|
||||
|
||||
try:
|
||||
dut.expect('https_request using saved client session', timeout=20)
|
||||
dut.expect(
|
||||
['Connection established...', 'Reading HTTP response...', 'HTTP/1.1 200 OK', 'connection closed'],
|
||||
expect_all=True,
|
||||
)
|
||||
except Exception:
|
||||
logging.info('Failed the test for "https_request using saved client session"')
|
||||
raise
|
||||
# only check if one connection is established
|
||||
logging.info('Testing for "https_request using crt bundle" with mbedtls dynamic resource enabled')
|
||||
try:
|
||||
dut.expect('https_request using crt bundle', timeout=30)
|
||||
dut.expect(
|
||||
[
|
||||
'Connection established...',
|
||||
'Reading HTTP response...',
|
||||
'HTTP/1.1 200 OK',
|
||||
'TLS 1.3',
|
||||
'connection closed',
|
||||
],
|
||||
expect_all=True,
|
||||
)
|
||||
except Exception:
|
||||
logging.info(
|
||||
'Failed the test for "https_request using crt bundle" with TLS 1.3 '
|
||||
'when mbedtls dynamic resource was enabled'
|
||||
)
|
||||
raise
|
||||
logging.info(
|
||||
'Passed the test for "https_request using crt bundle" with TLS 1.3 when '
|
||||
'mbedtls dynamic resource was enabled'
|
||||
)
|
||||
finally:
|
||||
thread1.terminate()
|
||||
|
||||
|
||||
@pytest.mark.ethernet
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
|
15
examples/protocols/https_request/sdkconfig.ci.ssldyn_tls1_3
Normal file
15
examples/protocols/https_request/sdkconfig.ci.ssldyn_tls1_3
Normal file
@@ -0,0 +1,15 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_EXAMPLE_SSL_PROTO_TLS1_3_CLIENT=y
|
||||
CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS=y
|
||||
CONFIG_EXAMPLE_LOCAL_SERVER_URL="FROM_STDIN"
|
||||
CONFIG_EXAMPLE_LOCAL_SERVER_URL_FROM_STDIN=y
|
@@ -391,6 +391,7 @@ def test_examples_protocol_simple_ota_example_with_verify_app_signature_on_updat
|
||||
'config',
|
||||
[
|
||||
'tls1_3',
|
||||
'tls1_3_only_dynamic',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
|
@@ -0,0 +1,13 @@
|
||||
CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL="FROM_STDIN"
|
||||
CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y
|
||||
CONFIG_EXAMPLE_CONNECT_ETHERNET=y
|
||||
CONFIG_EXAMPLE_CONNECT_WIFI=n
|
||||
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
|
||||
CONFIG_EXAMPLE_ETH_PHY_IP101=y
|
||||
CONFIG_EXAMPLE_ETH_MDC_GPIO=23
|
||||
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
|
||||
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5
|
||||
CONFIG_EXAMPLE_ETH_PHY_ADDR=1
|
||||
CONFIG_EXAMPLE_CONNECT_IPV6=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
Reference in New Issue
Block a user