fix(esp_http_server): merge release/v6.0 to resolve backport conflict

Merge branch 'release/v6.0' into backport-48352-resolve to resolve a
conflict in esp_httpd_priv.h between this backport's sdkconfig.h
include and the ctrl-socket semaphore's freertos/semphr.h include.
This commit is contained in:
Hrushikesh Bhosale
2026-07-02 11:41:38 +05:30
1121 changed files with 36331 additions and 10307 deletions

View File

@@ -26,6 +26,15 @@ examples/protocols/esp_http_client:
depends_components+:
- esp_http_client
examples/protocols/esp_http_client_mutual_auth:
<<: *default_rules
disable_test:
- if: IDF_TARGET != "esp32c3"
depends_components+:
- esp_http_client
- esp-tls
- esp_secure_cert_mgr
examples/protocols/esp_local_ctrl:
<<: *default_rules
disable+:

View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
idf_build_set_property(MINIMAL_BUILD ON)
project(esp_http_client_mutual_auth)
if(CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS)
# Flash the pre-built esp_secure_cert partition (device cert + DS context) so it
# is included in the QEMU flash image created by esptool merge_bin.
esptool_py_flash_to_partition(
flash esp_secure_cert
"${CMAKE_CURRENT_SOURCE_DIR}/main/certs/esp_secure_cert_data/esp_secure_cert.bin"
)
endif()

View File

@@ -0,0 +1,104 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
# ESP HTTP Client Mutual TLS Authentication Example
This example demonstrates mutual TLS (mTLS) authentication using `esp_http_client`. The server verifies the client's identity via a client certificate, and the client verifies the server using a CA certificate.
Two client key modes are supported, selectable via Kconfig:
| Mode | Kconfig | Client key source |
|------|---------|-------------------|
| **Software keys** (default) | `EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS=n` | PEM files embedded in firmware |
| **DS peripheral** | `EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS=y` | Hardware Digital Signature peripheral via `esp_secure_cert` partition |
## How it works
1. Device connects to Wi-Fi or Ethernet (via `protocol_examples_common`)
2. Prompts for HTTPS server URL on serial console
3. Performs HTTPS GET with mutual TLS — sends client certificate to server
4. Logs the HTTP status code
## Quick start (software key mode)
```bash
idf.py set-target esp32c3
idf.py build flash monitor
```
When prompted, enter the URL of an HTTPS server that requires client certificates. The pre-generated certificates in `main/certs/` are used automatically.
## Certificates
Pre-generated certificates with 10-year lifetime are in `main/certs/`. A single CA signs both client and server certificates. See `main/certs/README.md` for OpenSSL commands to regenerate.
| File | Purpose | Embedded in firmware? |
|------|---------|----------------------|
| `ca_cert.pem` | CA certificate (verifies server) | Yes (both modes) |
| `client_cert.pem` | Client certificate | Yes (software mode only) |
| `client_key.pem` | Client private key | Yes (software mode only) |
| `server_cert.pem` | Server certificate (pytest) | No |
| `server_key.pem` | Server private key (pytest) | No |
## DS peripheral mode
The DS (Digital Signature) peripheral holds the client private key in hardware. The client certificate and DS context are stored in the `esp_secure_cert` partition.
### Prerequisites
- ESP32-C3 or other chip with `SOC_DIG_SIGN_SUPPORTED`
- `esp-secure-cert-tool` (`pip install esp-secure-cert-tool`)
### Provisioning
1. **Generate DS partition and HMAC key** (from the example directory):
```bash
configure_esp_secure_cert.py \
--device-cert main/certs/client_cert.pem \
--target_chip esp32c3 \
--configure_ds --priv_key_algo RSA 2048 \
--skip_flash --keep_ds_data_on_host \
--private-key main/certs/client_key.pem \
--efuse_key_id 1
```
Copy the output files to `main/certs/esp_secure_cert_data/`:
- `esp_secure_cert.bin`
- `hmac_key.bin`
2. **Burn HMAC key to eFuse:**
```bash
# QEMU
idf.py qemu efuse-burn-key --do-not-confirm BLOCK_KEY1 \
main/certs/esp_secure_cert_data/hmac_key.bin HMAC_DOWN_DIGITAL_SIGNATURE
# Hardware (irreversible!)
idf.py efuse-burn-key --do-not-confirm BLOCK_KEY1 \
main/certs/esp_secure_cert_data/hmac_key.bin HMAC_DOWN_DIGITAL_SIGNATURE
```
3. **Build and flash:**
```bash
idf.py -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.qemu_ds" build flash monitor
```
## QEMU testing
Three QEMU tests are included:
- `test_mutual_auth_software_keys` — software key mTLS, expects HTTP 200
- `test_mutual_auth_ds` — DS peripheral mTLS, expects HTTP 200
- `test_mutual_auth_ds_fails_wrong_credentials` — DS with wrong server CA, expects failure
The tests start a local HTTPS server with mutual TLS, inject the URL via serial, and verify device output.
## Troubleshooting
**TLS handshake failed: BAD_SIGNATURE**
The client certificate is not signed by the CA the server expects. Regenerate certs using the commands in `main/certs/README.md`.
**ESP_ERR_HW_CRYPTO_DS_HMAC_FAIL**
The HMAC key in eFuse does not match the key used to create the DS partition. For QEMU, delete the eFuse image and regenerate it from the current `hmac_key.bin`.

View File

@@ -0,0 +1,14 @@
set(requires esp-tls nvs_flash esp_event esp_netif esp_http_client)
set(embed_txt certs/ca_cert.pem)
if(CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS)
list(APPEND requires esp_secure_cert_mgr)
else()
list(APPEND embed_txt certs/client_cert.pem certs/client_key.pem)
endif()
idf_component_register(SRCS "mutual_auth_example.c"
INCLUDE_DIRS "."
PRIV_REQUIRES ${requires}
EMBED_TXTFILES ${embed_txt})

View File

@@ -0,0 +1,12 @@
menu "Example Configuration"
config EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
bool "Use DS peripheral for client authentication" if SOC_DIG_SIGN_SUPPORTED
default n
help
When enabled, the client private key is held by the DS (Digital Signature)
peripheral. The client certificate and DS context are loaded from the
esp_secure_cert partition at runtime. Requires provisioning; see the README.
When disabled (default), the client certificate and private key are embedded
in the firmware as PEM files from main/certs/.
endmenu

View File

@@ -0,0 +1,41 @@
# Certificate Generation for Mutual TLS Example
All certificates are pre-generated with a 10-year lifetime. To regenerate, run
the following OpenSSL commands from this directory (`main/certs/`).
## Generate CA (signs both client and server certs)
openssl req -newkey rsa:2048 -nodes -keyout ca_key.pem -x509 -days 3650 \
-out ca_cert.pem -subj "/CN=Mutual Auth Test CA"
## Generate client certificate (embedded in firmware)
openssl genrsa -out client_key.pem 2048
openssl req -out client.csr -key client_key.pem -new -subj "/CN=esp_mutual_auth_client"
openssl x509 -req -days 3650 -in client.csr -CA ca_cert.pem -CAkey ca_key.pem \
-sha256 -CAcreateserial -out client_cert.pem
rm client.csr ca_cert.srl
## Generate server certificate (used by pytest mTLS server)
openssl genrsa -out server_key.pem 2048
openssl req -out server.csr -key server_key.pem -new -subj "/CN=esp_mutual_auth_server"
openssl x509 -req -days 3650 -in server.csr -CA ca_cert.pem -CAkey ca_key.pem \
-sha256 -CAcreateserial -out server_cert.pem
rm server.csr ca_cert.srl
## Files
| File | Purpose | Embedded in firmware? |
|------|---------|----------------------|
| `ca_cert.pem` | CA certificate (verifies server identity) | Yes |
| `ca_key.pem` | CA private key (signs certs; keep private) | No |
| `client_cert.pem` | Client certificate (sent to server) | Yes (software mode) |
| `client_key.pem` | Client private key | Yes (software mode) |
| `server_cert.pem` | Server certificate (pytest mTLS server) | No |
| `server_key.pem` | Server private key (pytest mTLS server) | No |
## DS peripheral mode
For DS peripheral mode, see `esp_secure_cert_data/` and the DS provisioning
section in the top-level README.

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIUHrbEhg+kRb2wd66oi57asPHvrBYwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjAyMDQwNzMwMDRaFw0zNjAyMDIw
NzMwMDRaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCpug883pCJKGj0cZ/zdJLnjBa9M6lPWscgDFh1wPO1wyOzu25Q
kUykgUE8GE3W7RC4zw4LCUkU06EeVnp6UkXsKktC94OvAeYczsq6Z95WwyF8ktXP
AzW/Lpz202MFpA4edcoczLJEf1GPmoI1RM//V19Op+6nLBNLLTj35yO3CUMjxeh6
ptlxi3PbiFliOVff6DfPcNzp7rh1k+SWoNP5adTN8aHjpoO53wFHhBfiP4zqnLJG
uHFHApGcwyf1SAbERJ4F2tV+xMRVnp2i/Bx9pIY7qVRKdqOswQK6YGSlznfY8RCM
he9B0mZpSa9NFsPdm+Lpt2wxDvuSC/WtMLHBAgMBAAGjUzBRMB0GA1UdDgQWBBRG
ECXpPzDsNPv55ZHXAsKgCIYF6zAfBgNVHSMEGDAWgBRGECXpPzDsNPv55ZHXAsKg
CIYF6zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAFiLooiqC/
LQBoN7J6FyQhsKvly+yX4Vh8ituhQQnt4kRD28ci+GmceqgNlX86e/MMTzTXOCMJ
EIhR4xHd1xg5lnPLPS1o+LXsbyOlU8QTCoYgVkjDVHi4fGnbMIoWaZBt8o3MYgAy
Fc+7A6ZoK9PIbbcRUYPSPGe1loop490tgyNrvyc29lLdg4ljO+Xg3Wpu95bTxe/F
bjWFWZgF6aqAfrT7QZXk9OIfOkDpcb5EsxBySQIZNC06FEtNkwanVWFSMdDm3Axh
Xjuv1HdGyHnS/jcFTZ4EfB76oU0+NuoYtDYF7Fns3Upvg8QZhhs+6Ob5mRKJefRD
HlIA+c6CBmsY
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpug883pCJKGj0
cZ/zdJLnjBa9M6lPWscgDFh1wPO1wyOzu25QkUykgUE8GE3W7RC4zw4LCUkU06Ee
Vnp6UkXsKktC94OvAeYczsq6Z95WwyF8ktXPAzW/Lpz202MFpA4edcoczLJEf1GP
moI1RM//V19Op+6nLBNLLTj35yO3CUMjxeh6ptlxi3PbiFliOVff6DfPcNzp7rh1
k+SWoNP5adTN8aHjpoO53wFHhBfiP4zqnLJGuHFHApGcwyf1SAbERJ4F2tV+xMRV
np2i/Bx9pIY7qVRKdqOswQK6YGSlznfY8RCMhe9B0mZpSa9NFsPdm+Lpt2wxDvuS
C/WtMLHBAgMBAAECggEAAt0DUwvvrQqwG5ibodIwhMS/oPVBSNgPli4zI1hsHB/E
x0xVD/mljPxrvvFrhcHV14JRurSvRZFM7Wu40P24lYOApcpyb6ZE7S09bQ/hx72u
v9Dj18RWeKlXB1B5Yg/al5+1107KPp7Vv8oT4oVsy2JcVqG9ZFdZY3optP/yoay4
oRJprOV4jc8aaLB0+zze/7RJZ4DtpeOWhyNws/cGwwFvaDlncnylAoaDs20MBfsg
VV+glo69rqvn3SCfSXIG5/vyfHS52xPN04kPrpXpu7kvTeJHqCfq4w5LSS0GgBdM
5CbBSpW25AnX9Jqczxhu7Y2rt8kIx+4gnW05z0YB2QKBgQDn9yeQzsXK98R7R6hz
ogKyOYQuNVNjjR7guSD0285QTU+XB8jC2nqiTVPy8Xy1w6udh9SjvqS47yQyySzC
ZCFvT3rwwdWP5pUj89FG7XoX33hQrV4+jbBCcjajeyMxAmMM83ISL9NahsVLWx7K
gdPdxhBiCB8dueF+kVcBf1Bb2QKBgQC7UAjwl45oOIDoXBqN3TDGVodwQM6gn1rY
heYjEUeQmjL6j4e8PiUFl5rHCNavIUr0gkBhnN5hcq2mvfECBEVVm8dlrvHHa4Ti
ujhC6DGgrOUIPok+rXJoELvmrYa72H04uHwkOkESkuHO+gHuRJ7nIxH/BktGKsol
Unp+CNBcKQKBgQCtn+NvkjWeTII2vFYr5xIZkM+NPsDh/Nkl36v5WyU8GgH+zAbL
QnkUTskNSQ/NhV5JFUhmH+Zvvh/cG5RzFDuqc1VUK+HMSg1L0c3NRydiAxStXnby
X1+U/KRFDYAzyNOW+Alj74RFeCbo1pVfgnmwv/W3StjviRhtgiAbsM3XUQKBgQCg
E94P/vWtC9zetxfadVXhqsFEpZ3wlz9EG+p5vaKzaZR3nYIa1eE9zjpwLpWKRaGR
JF9xDGbgUOkmvtzhJFU8vEzEEaZ/DtwaB5tdUqJW9msliIwyDHjhhquOkG28y174
wnEVZNOH1A82m2JbBjnmvon6sJ5T8O2gx8P3QgEPQQKBgQCcpSf/rJxMfEeZf4Kl
EeqSjPwFRKsC/8gBs63VJrmYh2XigL5tI2MnTdgts+4W1xC8avPiRoeaZ8YN6258
n2tVyhJrm3F1cw/tFmLE0kuNkS0jOA/hzFeOkLJTZXR9npBzwNduQQ5i0HqQM9Qi
aIIVlhLZ0Jlqu6SLnJ74n34VnA==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDBDCCAeygAwIBAgIUa91CxumwjyUKK9AHnJ9upekrqlgwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjA1MjkwMzI5MTlaFw0zNjA1MjYw
MzI5MTlaMCIxIDAeBgNVBAMMF2VzcF9odHRwX2NsaWVudF9kc190ZXN0MIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsztCh5DaIh+KDzk6L5CEps8AJ6cw
xJffL/wjZc3yTOCjwDOX7UdDilizLobkzCBMm8lccafts2+jEAVaMrR1pQVO0c8J
5sDk1IHlNJVlq1UxnIl7JvbDjtAWp+cHYMRQSBXmHrkqJl45LPTdjC0FvdsB2oyj
HFmDHp3A3B/BwzdwyBzucG+Xa3OILZI0wsri0oDMyTw6Xg6SoXmLTQhbqCD38Ijt
d9AalLMElM+2j+IH5zVlnG+JpCrvzYy7pY7gIUmjLFBewSpEiPjxWjrFJ0teXyTj
smhd/IK5rVQw2v1wjKT1Wc5lgRDifZTVNRxDIepr+m4Ck3clRfyesSslhQIDAQAB
o0IwQDAdBgNVHQ4EFgQU7jBC4N9rFhRFkzBq0KcXGRsNatcwHwYDVR0jBBgwFoAU
RhAl6T8w7DT7+eWR1wLCoAiGBeswDQYJKoZIhvcNAQELBQADggEBAHWHKGJ0Z4+b
rw25oZ9ZK2pZkQcvAG1HBQxHoNBRmYruitvALnQhq7UuHlAoMFxBmkJ61FRjgA1x
zGAZsfTCy/Agv2lfVf6LiRd+Rl134rzHLlCoTg0tZC5uFcS/6W5mbfXIviYaPRaf
RMgpM1ZPYMF5VsX0B6CJH6I+UHoZVLTb1nz5gu01WiKKULTygW70xyk5B0osiK9C
UDCOQ7jcYrjYNZulwY2qv+Kg3PH6aFR9xT+RUp1fDb0HJk0/M0YDOdh4QUzm29UL
KCCM+E25tan4Nvgg1jQIw3dppWc6dCSE0IgCCVY+O3aZqnfRugHl7NCxJ5pHCyPr
zV9BSaFx8Uk=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzO0KHkNoiH4oP
OTovkISmzwAnpzDEl98v/CNlzfJM4KPAM5ftR0OKWLMuhuTMIEybyVxxp+2zb6MQ
BVoytHWlBU7RzwnmwOTUgeU0lWWrVTGciXsm9sOO0Ban5wdgxFBIFeYeuSomXjks
9N2MLQW92wHajKMcWYMencDcH8HDN3DIHO5wb5drc4gtkjTCyuLSgMzJPDpeDpKh
eYtNCFuoIPfwiO130BqUswSUz7aP4gfnNWWcb4mkKu/NjLuljuAhSaMsUF7BKkSI
+PFaOsUnS15fJOOyaF38grmtVDDa/XCMpPVZzmWBEOJ9lNU1HEMh6mv6bgKTdyVF
/J6xKyWFAgMBAAECggEABhTu3dUbTx4dMVq37+Y/rL4IcNAFC8llgDuA41L43lqf
DoYRqTJfsdbvBEH9Nd9WCiaWivVvpO+XAROqwF626+nBXMWMPfbHV7NYA4/wOmny
fC/UFySMypCivCwkPXMxOt5Mso0w8iAEtlL8Hjtm3vmKrLpUoevNqGEiVWCEfYBv
h7Yc7uKcB90WyZ3LtgmghV46kDFvaSMMdPTqVU6jgCTpq6yBCntpeYz1VqcnDhZt
nU6i/vinJz8BMDXIDbVonie0lZ9tCWnYwHaWCkIPy9Cb5eqlldJfiN1CYz9MFGGs
oymeTF+lrtPphRTakpziDkxBM/AwnIkx5HQTsxF4jwKBgQD882SmrGvbshJLYMVl
4UznqDoP59M7G5kwQ/j579VKkhbrKrV0JvOEYQuJx18d7drDyY7eLRF6KYRLYOhG
FWznMpNhRYQMcWcAqNLHb5W4FSsGGRt8Q1qmmKQmO192FZVveG0czz2FAPY4uSBX
4va+mJszMrnfEApqW8TdOrEwYwKBgQC1ZF5qTQR9wZPj+65mGwfYei1EP1OEE5xc
DWo9fsdDVxv4ykHTpURF+EftMkOF+b/GTgJHyEceZfgm/9dx15EeQuHqpNDjffUD
y/b8GVT7NB2nR2m3ztXYBHsRK7uYGbgVtB+7w1gFpcZRx92SvITy+EjQRTs9z4n/
Ru2y8P+S9wKBgF0bTfIXxz+/xQIf5akBjCg9ANo378Vy/CkK7As7n1vqeCsptk7B
w6L3gaK+UyGWGo80krTvC97Wh06jpfueCU70i9EjIF7gIxTYD3W/efGfQQ3mkfpk
ZGqsBsfX1OSHP1Efl7IiCjf5yafJZMFU1pQDYiUvR8F2iw7pJoZ0AyKXAoGBAKKv
RCaesLqBFTzSC5Y0BBNZcKPXD/ZTCFdfCLviqqBwzfuSmvtRLCx9AzVvcTQFzMP0
TwNGUtKmrat8piPKLLMxVSF3dImz/D3NftSXe6pZEYdn+x8JeK1nR2EdEgDWgE2m
4RcrmhRmm7nZQZZLUgoAOH3iucE0FBZJ7QIiN3X9AoGBAJmGvBoa6ZQpbRXZbVfv
qvz+gzwN2fO3t7iWGaHVCGygnSaplCGl/MfVtb/ak3AG5NcjwDqjld8X7RE9dSk2
l12Y3AtsyaDozLg5KXdwt+F1Pdhju9WB3pd5O25Ocn5JU1jUqL+huW+d7WhUDPO7
PHTgtfI15eDu5LinkNPD73hE
-----END PRIVATE KEY-----

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>B<EFBFBD><EFBFBD>Ĕ<|S<><53>pt3<74>Nk<4E>sc<73> d<><64><10>

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIUcDdlf0FDC5KPEcTnJG58JcXKGtAwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNjA0MDgwMzAzMzdaFw0zNjA0MDUw
MzAzMzdaMCExHzAdBgNVBAMMFmVzcF9tdXR1YWxfYXV0aF9zZXJ2ZXIwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4AfXPbz06C1kwJHSkMWgzVHgz85Pb
bF7aRzImfHR9nfz2hNJ2Yg8Apdxt/xWM1n2uDDlVWjg0vK50X3DK4oz1Lk32//IE
iYajZuiFsO6ZBNHgy1rehJUlk92Z4fklhpEgpVT67Ph6o8yhPDjNgPM03eJsl6yC
lawxETSXU+dOhjzfuiT0IA74UOhTVIfVe8E0AlXGqjrN5QkKP5Xb74w1ovIZPOzy
vWNjnzuwg2MKWHTFOolPpKft4GUqPvMD41qNfymUL46JxtONcKwx//Q5hO0bSAnB
GJ1Brqca366ObF6Xh+dAXALnxVkbpauoJ7ZqdyiJg6YANovawe/oZZXpAgMBAAGj
QjBAMB0GA1UdDgQWBBTOXVwKpLmhvv/2Nqe9E4Hx8VYsrzAfBgNVHSMEGDAWgBRG
ECXpPzDsNPv55ZHXAsKgCIYF6zANBgkqhkiG9w0BAQsFAAOCAQEAdgeCBgBgu8N7
r9ycEytvs1AKxf2P3c6VFOlXF8rRNq/+8a8CoyGm4cF8u7AyvQQJZya6Luv+xg+/
tguXIbw5goNenNkEuUOHRlo39vZ/pHe/HGCAxHatWG8qgoahXCZh3OSEmHfDwEDc
RbYTJXGb98VynFzuxQ4RSDXb+WxhslwGCV2NXrF6gLS1FgZDKpJChmhkvw/ubdxY
QS3fSToWlvs7PSyZi2Ei5XuGfthQPT+PZpyHZufXX9evLDdGOB/5SOz+p0SSBXcZ
he21O0jovx0RDSnnq6kdG+qHfUrwOt5TGJ8tIJQZuKi9VUIMEONC7RMV4u1T00j9
8VtZEG2gfg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4AfXPbz06C1kw
JHSkMWgzVHgz85PbbF7aRzImfHR9nfz2hNJ2Yg8Apdxt/xWM1n2uDDlVWjg0vK50
X3DK4oz1Lk32//IEiYajZuiFsO6ZBNHgy1rehJUlk92Z4fklhpEgpVT67Ph6o8yh
PDjNgPM03eJsl6yClawxETSXU+dOhjzfuiT0IA74UOhTVIfVe8E0AlXGqjrN5QkK
P5Xb74w1ovIZPOzyvWNjnzuwg2MKWHTFOolPpKft4GUqPvMD41qNfymUL46JxtON
cKwx//Q5hO0bSAnBGJ1Brqca366ObF6Xh+dAXALnxVkbpauoJ7ZqdyiJg6YANova
we/oZZXpAgMBAAECggEAHNtEgaUP9K6w7EmF9/Xo7HEpGfnvTHBAqSSUgHqbXiki
RvAU0rdADmRJyapA5pcAm1npZ9w/3O1gwnlGhrAOq6ijq2z/Yadw85ErkpkTt0WN
Ox6eVS/KSrFXWvmYM+1YNxwWfp9zvEU8eGX/ASNhgKnyB1ZQmc3/ygDJFEOhOQFe
rAMntZvoEiQkkQIL45iKLceFfnTQ4iJNLRDvkOxt98OYgjOkVfLZvYXgTTH0r1mn
x2M3ITVVcU4OGs5AFQgbV/9fB5raPCAnroQkRIG69XD+uOJmRX04WvW8Hk9BQXk0
aAkPSa5yPdZbpjsV5aYaQYoQodblN2XKuVxoqmj6bQKBgQD+Bo5lDmhxNGC7x64B
6pTLAmSM2oT/mHRGRUyR+YB/Mf3mw5MhoBwZS5CuB1UaOaFBUx0b8SpfXfXMAhcF
483FcyYKK3B/ZV9u3EfvxCBiiLzwYqLQYaBp7efeQkAJzb+sIj0+ZE70Lh2IjspL
zhFh7WaMaxeU1IAfqvfRaATjewKBgQC5cBY19erRuztFkP2SeNYHlIj0Y1uF6QRX
8ds2QLQsnj7J4WU/5VgTKWTywx7gn82/hWcJAs2k1PEUspRfMLwpChtOxvYHEosM
4a9fCBKlA+KPc4NpSGaC2M9VoJIJrgo7JjzJMFS6qSJ3zbmWUQh1XCLliNWNey2s
LkiNrA0M6wKBgQCG5wxv9nrYw6wrjRuHwQBL33VuqA3Bf0EgoGTNkOcApZflGS/l
x5WkiVDIWvSC/N/6RR1MXYLXKpsCQInhgt0gYsps1CzmOvu3cBxz5IAeU+ei8X7t
kysRllpw2lYP3shPrc9AdxzG6Eae4tXj9AefLegr4iOf0kpIhw8cklUmSQKBgQC1
5qRy9DMO3unqeKq0poHU17hsepZJymSvXBjbpCbZabVP1SC7x95YlY9nr003rKpo
B5UlurE80oFV+0MeCTFZ1IcrBHJMR71MuomL3+BiLGhurTIn8ZRVIBZp+WOnyShS
E1UnSZijrcuY154IPJ7eeK3mmQ5ahY0szA3xoub+VwKBgQDUqNuMGgDmZo5O2Jsz
0J43BbRfqWXyQ9Q/nQQftHoxPEWkEQSoa3y58zWlYJKN4JoUU/UNR7dqdP5rnbhZ
f/pPXWdmhWQQlImgAfqJNEAZscA2NgsW/BOcot7zl/ZMgMOyDNXHoDSHenKPksKb
9POv9y/PnUWSUfG/WFwMEH65iQ==
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,7 @@
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
esp_secure_cert_mgr:
version: ">=2.0.0"
rules:
- if: "idf_version >=5.0"

View File

@@ -0,0 +1,212 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ESP HTTP Client 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 <string.h>
#include <stdio.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "esp_http_client.h"
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
#include "esp_secure_cert_read.h"
#if CONFIG_MBEDTLS_VER_4_X_SUPPORT
#include "psa_crypto_driver_esp_rsa_ds_contexts.h"
#else
#include "esp_rsa_sign_alt.h"
#endif
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "MUTUAL_AUTH";
#define URL_BUF_SIZE 256
/* CA cert for server verification (embedded in both modes) */
extern const char ca_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const char ca_cert_pem_end[] asm("_binary_ca_cert_pem_end");
#if !CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
/* Software mode: client cert and key embedded as PEM */
extern const char client_cert_pem_start[] asm("_binary_client_cert_pem_start");
extern const char client_cert_pem_end[] asm("_binary_client_cert_pem_end");
extern const char client_key_pem_start[] asm("_binary_client_key_pem_start");
extern const char client_key_pem_end[] asm("_binary_client_key_pem_end");
#endif
/**
* Read the server URL from stdin. Used by pytest to inject the mTLS server address.
* Returns ESP_OK on success, ESP_FAIL on error or empty input.
*/
static esp_err_t read_url_from_stdin(char *url_buf, size_t buf_size)
{
example_configure_stdin_stdout();
ESP_LOGI(TAG, "Enter mutual auth server URL:");
if (fgets(url_buf, (int)buf_size, stdin) == NULL) {
ESP_LOGE(TAG, "Failed to read URL from stdin");
return ESP_FAIL;
}
/* Strip trailing newline */
size_t len = strlen(url_buf);
if (len > 0 && url_buf[len - 1] == '\n') {
url_buf[--len] = '\0';
}
if (len == 0) {
ESP_LOGE(TAG, "Empty URL");
return ESP_FAIL;
}
return ESP_OK;
}
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
/**
* DS peripheral mode: load client cert and DS context from esp_secure_cert partition.
*/
static void mutual_auth_with_ds(const char *url)
{
char *dev_cert_buf = NULL;
uint32_t dev_cert_len = 0;
esp_ds_data_ctx_t *ds_ctx = NULL;
esp_err_t ret = esp_secure_cert_get_device_cert(&dev_cert_buf, &dev_cert_len);
if (ret != ESP_OK || dev_cert_buf == NULL || dev_cert_len == 0) {
ESP_LOGE(TAG, "Failed to get device cert: %s", esp_err_to_name(ret));
return;
}
ds_ctx = esp_secure_cert_get_ds_ctx();
if (ds_ctx == NULL) {
ESP_LOGE(TAG, "Failed to get DS context");
esp_secure_cert_free_device_cert(dev_cert_buf);
return;
}
size_t ca_cert_len = ca_cert_pem_end - ca_cert_pem_start;
esp_http_client_config_t config = {
.url = url,
.cert_pem = ca_cert_pem_start,
.cert_len = ca_cert_len,
.client_cert_pem = dev_cert_buf,
.client_cert_len = dev_cert_len,
.ds_data = ds_ctx,
.skip_cert_common_name_check = true,
.timeout_ms = 10000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to init HTTP client");
goto cleanup;
}
esp_err_t err = esp_http_client_perform(client);
int status = esp_http_client_get_status_code(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Mutual Auth Status = %d, content_length = %" PRId64,
status, esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
cleanup:
esp_secure_cert_free_ds_ctx(ds_ctx);
esp_secure_cert_free_device_cert(dev_cert_buf);
}
#else /* Software key mode */
/**
* Software key mode: use embedded PEM client cert and key.
*/
static void mutual_auth_with_software_keys(const char *url)
{
size_t ca_cert_len = ca_cert_pem_end - ca_cert_pem_start;
size_t client_cert_len = client_cert_pem_end - client_cert_pem_start;
size_t client_key_len = client_key_pem_end - client_key_pem_start;
esp_http_client_config_t config = {
.url = url,
.cert_pem = ca_cert_pem_start,
.cert_len = ca_cert_len,
.client_cert_pem = client_cert_pem_start,
.client_cert_len = client_cert_len,
.client_key_pem = client_key_pem_start,
.client_key_len = client_key_len,
.skip_cert_common_name_check = true,
.timeout_ms = 10000,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to init HTTP client");
return;
}
esp_err_t err = esp_http_client_perform(client);
int status = esp_http_client_get_status_code(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Mutual Auth Status = %d, content_length = %" PRId64,
status, esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
#endif /* CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS */
static void mutual_auth_task(void *pvParameters)
{
char url_buf[URL_BUF_SIZE];
if (read_url_from_stdin(url_buf, sizeof(url_buf)) != ESP_OK) {
goto done;
}
#if CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS
mutual_auth_with_ds(url_buf);
#else
mutual_auth_with_software_keys(url_buf);
#endif
done:
ESP_LOGI(TAG, "Finish mutual auth example");
vTaskDelete(NULL);
}
void app_main(void)
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
ESP_LOGI(TAG, "Connected, starting mutual auth example");
xTaskCreate(mutual_auth_task, "mutual_auth", 8192, NULL, 5, NULL);
}

View File

@@ -0,0 +1,8 @@
# Partition table for DS test using esp_secure_cert partition.
# esp_secure_cert_mgr looks for type 0x3F (custom) and name "esp_secure_cert".
# Flash main/certs/esp_secure_cert_data/esp_secure_cert.bin to the esp_secure_cert partition.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
esp_secure_cert, 0x3F, 0x00, , 0x4000,
factory, app, factory, , 0x100000,
Can't render this file because it contains an unexpected character in line 2 and column 61.

View File

@@ -0,0 +1,230 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import http.server
import logging
import multiprocessing
import os
import socket
import ssl
from typing import Any
import pytest
from common_test_methods import get_host_ip4_by_dest_ip
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_qemu.app import QemuApp
from pytest_embedded_qemu.dut import QemuDut
# ---------------------------------------------------------------------------
# mTLS HTTPS server (runs in a subprocess during tests)
# ---------------------------------------------------------------------------
def _https_mtls_request_handler() -> type[http.server.BaseHTTPRequestHandler]:
"""Request handler for mutual TLS: responds 200 OK to GET, logs at INFO."""
class RequestHandler(http.server.BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self) -> None:
logging.info('[HTTPS server] GET from %s', self.address_string())
self.send_response(200)
self.send_header('Content-Length', '0')
self.end_headers()
def log_message(self, _format: str, *args: object) -> None:
logging.info(
'[HTTPS server] %s - - [%s] %s', self.address_string(), self.log_date_time_string(), _format % args
)
return RequestHandler
class _MTLSHTTPServer(http.server.HTTPServer):
"""HTTPServer that wraps each connection with TLS and logs handshake success/failure."""
def __init__(
self,
server_address: tuple[str, int],
RequestHandlerClass: type[http.server.BaseHTTPRequestHandler],
ssl_context: ssl.SSLContext,
) -> None:
super().__init__(server_address, RequestHandlerClass)
self.ssl_context = ssl_context
def get_request(self) -> tuple[socket.socket, Any]:
conn, addr = self.socket.accept()
try:
wrapped = self.ssl_context.wrap_socket(conn, server_side=True)
logging.info('[HTTPS server] TLS handshake OK from %s:%s', addr[0], addr[1])
return wrapped, addr
except ssl.SSLError as e:
logging.warning('[HTTPS server] TLS handshake failed from %s:%s: %s', addr[0], addr[1], e)
conn.close()
raise
def server_bind(self) -> None:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
super().server_bind()
def start_https_server_mutual_tls(
server_cert: str,
server_key: str,
client_ca: str,
host: str,
port: int,
) -> None:
"""
Start an HTTPS server that requires and verifies client certificates (mutual TLS).
Uses server_cert/server_key for the server TLS identity and client_ca to verify the client cert.
"""
request_handler = _https_mtls_request_handler()
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile=server_cert, keyfile=server_key)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.load_verify_locations(cafile=client_ca)
httpd = _MTLSHTTPServer((host, port), request_handler, ssl_context)
logging.info('[HTTPS server] Listening on %s:%s (mTLS)', host, port)
httpd.serve_forever()
# ---------------------------------------------------------------------------
# Paths and constants
# ---------------------------------------------------------------------------
_EXAMPLE_DIR = os.path.dirname(__file__)
_CERTS_DIR = os.path.join(_EXAMPLE_DIR, 'main', 'certs')
_DS_DATA_DIR = os.path.join(_CERTS_DIR, 'esp_secure_cert_data')
_QEMU_EFUSE_FILE = os.path.join(_DS_DATA_DIR, 'qemu_efuse.bin')
_HMAC_KEY_FILE = os.path.join(_DS_DATA_DIR, 'hmac_key.bin')
_ESP_SECURE_CERT_PARTITION = os.path.join(_DS_DATA_DIR, 'esp_secure_cert.bin')
# qemu_extra_args must be a single string (plugin passes it to shlex.split).
_QEMU_EXTRA_DS = (
f'-drive file={_QEMU_EFUSE_FILE},if=none,format=raw,id=efuse '
'-global driver=nvram.esp32c3.efuse,property=drive,value=efuse '
'-global driver=timer.esp32c3.timg,property=wdt_disable,value=true'
)
# ---------------------------------------------------------------------------
# DS eFuse and artifact helpers
# ---------------------------------------------------------------------------
def _ensure_ds_efuse_image() -> None:
"""
Verify DS artifacts (eFuse image, HMAC key, secure cert partition) are present
before QEMU starts. All three are committed under main/certs/esp_secure_cert_data/;
see the DS provisioning section in the example README to regenerate them.
"""
for path in (_ESP_SECURE_CERT_PARTITION, _HMAC_KEY_FILE, _QEMU_EFUSE_FILE):
if not os.path.isfile(path):
pytest.skip(
f'DS test requires {os.path.basename(path)}. '
'See the DS peripheral mode section in the example README for provisioning steps.'
)
@pytest.fixture(autouse=True)
def _ensure_ds_efuse_before_qemu(request: pytest.FixtureRequest) -> None:
"""Ensure eFuse image exists before QEMU starts for DS tests."""
if 'qemu_ds' not in request.node.name:
return
_ensure_ds_efuse_image()
# ---------------------------------------------------------------------------
# Helper: start mTLS server and send URL to device
# ---------------------------------------------------------------------------
def _run_mtls_test(
dut: QemuDut,
server_port: int,
client_ca: str,
expect_success: bool,
) -> None:
"""
Start mTLS server, wait for device to request URL, send it, and verify outcome.
"""
server_cert = os.path.join(_CERTS_DIR, 'server_cert.pem')
server_key = os.path.join(_CERTS_DIR, 'server_key.pem')
server_proc = multiprocessing.Process(
target=start_https_server_mutual_tls,
args=(server_cert, server_key, client_ca, '0.0.0.0', server_port),
)
server_proc.daemon = True
server_proc.start()
logging.info('HTTPS server with mutual TLS started on port %s', server_port)
try:
ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)', timeout=60)[1].decode()
host_ip = get_host_ip4_by_dest_ip(ip_address)
dut.expect('Enter mutual auth server URL:', timeout=30)
dut.write(f'https://{host_ip}:{server_port}\n')
if expect_success:
dut.expect('HTTPS Mutual Auth Status = 200', timeout=30)
logging.info('mTLS test passed: status 200')
else:
dut.expect('mbedtls_ssl_handshake returned -0x7780', timeout=30)
dut.expect('Request failed:', timeout=30)
logging.info('Negative test passed: connection failed as expected')
dut.expect('Finish mutual auth example', timeout=10)
finally:
server_proc.terminate()
server_proc.join(timeout=5)
if server_proc.is_alive():
server_proc.kill()
# ---------------------------------------------------------------------------
# Test: software key mutual TLS
# ---------------------------------------------------------------------------
@pytest.mark.host_test
@pytest.mark.qemu
@pytest.mark.parametrize('config', ['default'], indirect=True)
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
def test_mutual_auth_software_keys(app: QemuApp, dut: QemuDut) -> None:
"""QEMU test: mutual TLS with embedded software client cert + key."""
if os.environ.get('IDF_TOOLCHAIN') == 'clang':
pytest.skip('QEMU mTLS test not supported with clang toolchain (Docker SLIRP networking issue)')
client_ca = os.path.join(_CERTS_DIR, 'ca_cert.pem')
_run_mtls_test(dut, server_port=8070, client_ca=client_ca, expect_success=True)
# ---------------------------------------------------------------------------
# Tests: DS peripheral mutual TLS (migrated from esp_http_client)
# ---------------------------------------------------------------------------
@pytest.mark.host_test
@pytest.mark.qemu
@pytest.mark.parametrize('config', ['qemu_ds'], indirect=True)
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
@pytest.mark.parametrize('qemu_extra_args', [_QEMU_EXTRA_DS], indirect=True)
def test_mutual_auth_ds(app: QemuApp, dut: QemuDut) -> None:
"""QEMU + DS peripheral test: mTLS using DS peripheral for client key."""
if os.environ.get('IDF_TOOLCHAIN') == 'clang':
pytest.skip('DS QEMU test not supported with clang toolchain (Docker SLIRP networking issue)')
client_ca = os.path.join(_CERTS_DIR, 'ca_cert.pem')
_run_mtls_test(dut, server_port=8070, client_ca=client_ca, expect_success=True)
@pytest.mark.host_test
@pytest.mark.qemu
@pytest.mark.parametrize('config', ['qemu_ds'], indirect=True)
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
@pytest.mark.parametrize('qemu_extra_args', [_QEMU_EXTRA_DS], indirect=True)
def test_mutual_auth_ds_fails_wrong_credentials(app: QemuApp, dut: QemuDut) -> None:
"""Negative test: server uses wrong CA, rejects device client cert."""
if os.environ.get('IDF_TOOLCHAIN') == 'clang':
pytest.skip('DS QEMU test not supported with clang toolchain (Docker SLIRP networking issue)')
# Wrong CA: server uses server_cert as client CA (won't verify our client cert)
wrong_ca = os.path.join(_CERTS_DIR, 'server_cert.pem')
_run_mtls_test(dut, server_port=8071, client_ca=wrong_ca, expect_success=False)

View File

@@ -0,0 +1,5 @@
# Software key mutual TLS test over QEMU ethernet
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_ETHERNET_SPI_SUPPORT=n
CONFIG_ETHERNET_OPENETH_SUPPORT=y
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y

View File

@@ -0,0 +1,8 @@
# DS peripheral mutual TLS test over QEMU ethernet
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_esp_secure_cert.csv"
CONFIG_EXAMPLE_MUTUAL_AUTH_USE_HW_RSA_DS=y
CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_ETHERNET_SPI_SUPPORT=n
CONFIG_ETHERNET_OPENETH_SUPPORT=y
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y

View File

@@ -0,0 +1 @@
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=y

View File

@@ -22,7 +22,7 @@ def test_examples_protocol_http_request(dut: Dut) -> None:
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'http_request.bin')
bin_size = os.path.getsize(binary_file)
logging.info('http_request_bin_size : {}KB'.format(bin_size // 1024))
logging.info(f'http_request_bin_size : {bin_size // 1024}KB')
# start test
dut.expect(r'DNS lookup succeeded.', timeout=30)
# check if connected or not
@@ -30,7 +30,7 @@ def test_examples_protocol_http_request(dut: Dut) -> None:
dut.expect(' ... socket send success')
dut.expect(' ... set socket receiving timeout success')
# check server response
dut.expect(r'HTTP/1.0 200 OK')
dut.expect(r'HTTP/1.1 200 OK')
# read from the socket completed
dut.expect('... done reading from socket. Last read return=0 errno=128')
dut.expect(r'(\d)...')

View File

@@ -27,7 +27,7 @@
"unplugin-fonts": "~1.4.0",
"unplugin-vue-components": "~30.0.0",
"unplugin-vue-router": "~0.16.0",
"vite": "~7.1.11",
"vite": "~7.3.2",
"vite-plugin-vue-layouts-next": "~1.0.0",
"vite-plugin-vuetify": "~2.1.2",
"vue-router": "~4.6.3"

View File

@@ -389,6 +389,7 @@ def test_examples_protocol_https_request(dut: Dut) -> None:
@pytest.mark.wifi_ap
@pytest.mark.esp32c2_rev2
@pytest.mark.xtal_26mhz
@pytest.mark.parametrize(
'config, baud',
@@ -399,6 +400,7 @@ def test_examples_protocol_https_request(dut: Dut) -> None:
)
@idf_parametrize('target', ['esp32c2'], indirect=['target'])
def test_examples_protocol_https_request_rom_impl(dut: Dut) -> None:
write_time_to_nvs(dut)
# Connect to AP
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
dut.expect('Please input ssid password:')

View File

@@ -1,6 +1,6 @@
CONFIG_IDF_TARGET="esp32c2"
CONFIG_XTAL_FREQ_26=y
CONFIG_ESP32C2_REV_MIN_200=y
CONFIG_EXAMPLE_CONNECT_WIFI=y
CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y
# TODO: IDF-15012
CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL=n
CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL=y