chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:04:25 +08:00
commit 548b49ebc0
20937 changed files with 5455372 additions and 0 deletions
@@ -0,0 +1,9 @@
# The following 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.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(https_server)
@@ -0,0 +1,75 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
# HTTPS server
This example creates an HTTPS server with SSL/TLS support using **ESP-TLS** that serves a simple HTML page when you visit its root URL.
For more information, refer to the [esp_https_server component documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_https_server.html).
## How to use example
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
### Configure the project
```
idf.py menuconfig
```
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Certificates
You will need to approve a security exception in your browser. This is because of a self signed
certificate; this will be always the case, unless you preload the CA root into your browser/system
as trusted.
You can generate a new certificate using the OpenSSL command line tool:
```
openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -x509 -days 3650 -out servercert.pem -subj "/CN=ESP32 HTTPS server example" -addext "keyUsage=critical,digitalSignature,keyEncipherment" -addext "subjectAltName=IP:<server_ip_address>"
```
Replace `<server_ip_address>` with your ESP32's actual IP address (e.g., `192.168.20.178`). The Subject Alternative Name (SAN) extension is required for modern browsers to validate the certificate when connecting via IP address.
**Important:** Use a static IP address for your ESP32. If the device uses a dynamic IP address, the IP may change, and you will need to regenerate the certificate with the new IP address each time it changes.
**Note:** The pre-generated certificates in the `main/certs` directory do not include the SAN extension and will not work with modern browsers. You must generate new certificates with the SAN field containing your server's IP address for browser compatibility.
Expiry time and metadata fields can be adjusted in the invocation.
Please see the openssl man pages (man openssl-req) for more details.
It is **strongly recommended** to not reuse the example certificate in your application;
it is included only for demonstration.
## Example Output
```
I (8596) example: Starting server
I (8596) esp_https_server: Starting server
I (8596) esp_https_server: Server listening on port 443
I (8596) example: Registering URI handlers
I (8606) esp_netif_handlers: example_connect: sta ip: 192.168.194.219, mask: 255.255.255.0, gw: 192.168.194.27
I (8616) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.194.219
I (9596) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (9596) example_connect: Connected to example_connect: sta
W (9606) wifi:<ba-add>idx:0 (ifx:0, ee:6d:19:60:f6:0e), tid:0, ssn:2, winSize:64
I (9616) example_connect: - IPv4 address: 192.168.194.219
I (9616) example_connect: - IPv6 address: fe80:0000:0000:0000:266f:28ff:fe80:2c74, type: ESP_IP6_ADDR_IS_LINK_LOCAL
W (14426) wifi:<ba-add>idx:1 (ifx:0, ee:6d:19:60:f6:0e), tid:4, ssn:0, winSize:64
I (84896) esp_https_server: performing session handshake
```
@@ -0,0 +1,16 @@
set(requires esp_https_server nvs_flash)
idf_build_get_property(target IDF_TARGET)
if(${target} STREQUAL "linux")
list(APPEND requires esp_stubs protocol_examples_common)
else()
list(APPEND requires esp_wifi esp_eth)
endif()
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES ${requires}
EMBED_TXTFILES "certs/servercert.pem"
"certs/prvtkey.pem"
"certs/cacert.pem"
"certs/cakey.pem")
@@ -0,0 +1,45 @@
menu "Example Configuration"
config EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
bool "Enable user callback with HTTPS Server"
help
Enable user callback for esp_https_server which can be used to get SSL context (connection information)
E.g. Certificate of the connected client
choice EXAMPLE_ENABLE_HTTPS_SERVER_TLS_VERSION
prompt "Choose TLS version"
default EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_2_ONLY
config EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_2_ONLY
bool "TLS 1.2"
select MBEDTLS_SSL_PROTO_TLS1_2
help
Enable HTTPS server TLS 1.2
config EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_3_ONLY
bool "TLS 1.3"
select MBEDTLS_SSL_PROTO_TLS1_3
help
Enable HTTPS server TLS 1.3
config EXAMPLE_ENABLE_HTTPS_SERVER_TLS_ANY
bool "TLS 1.3 and 1.2"
select MBEDTLS_SSL_PROTO_TLS1_3
select MBEDTLS_SSL_PROTO_TLS1_2
help
Enable HTTPS server TLS 1.3 and 1.2
endchoice
config EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES
bool "Enable HTTPS server custom ciphersuites"
default n
help
Enable HTTPS server custom ciphersuites
config EXAMPLE_ENABLE_SKIP_CLIENT_CERT
bool "Skip client certificate (WARNING: ONLY FOR TESTING PURPOSE, READ HELP)"
default n
select ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
help
Allow clients to connect without providing a client certificate.
This is useful for testing purposes. When enabled, the server request
client certificates but does not require them for the connection to be established.
If a client certificate is provided, it will be verified.
endmenu
@@ -0,0 +1,120 @@
# Certificate Generation Guide
This directory contains certificates for the HTTPS server example. This guide explains how to generate new server and client certificates signed by the existing CA certificate.
## Prerequisites
- OpenSSL installed on your system
- Existing CA certificate (`cacert.pem`) and CA private key (`cakey.pem`)
- Configuration files for certificate extensions (`server_cert.conf` and `client_cert.conf`)
## Generating Server Certificate
Follow these steps to create a new server certificate signed by the CA:
### 1. Generate Server Private Key
```bash
openssl genpkey -algorithm RSA -out new_server.key -pkeyopt rsa_keygen_bits:2048
```
This creates a 2048-bit RSA private key for the server.
### 2. Create Certificate Signing Request (CSR)
```bash
openssl req -new -key new_server.key -out new_server.csr -config server_cert.conf
```
This generates a CSR using the server's private key and the configuration specified in `server_cert.conf`.
### 3. Sign the Server Certificate with CA
```bash
openssl x509 -req -in new_server.csr -CA cacert.pem -CAkey cakey.pem -CAcreateserial -out new_server.pem -days 3650 -extensions v3_req -extfile server_cert.conf
```
This creates the server certificate (`new_server.pem`) valid for 10 years (3650 days), signed by the CA certificate.
## Generating Client Certificate
Follow these steps to create a new client certificate signed by the CA:
### 4. Generate Client Private Key
```bash
openssl genpkey -algorithm RSA -out new_client.key -pkeyopt rsa_keygen_bits:2048
```
This creates a 2048-bit RSA private key for the client.
### 5. Create Certificate Signing Request (CSR)
```bash
openssl req -new -key new_client.key -out new_client.csr -config client_cert.conf
```
This generates a CSR using the client's private key and the configuration specified in `client_cert.conf`.
### 6. Sign the Client Certificate with CA
```bash
openssl x509 -req -in new_client.csr -CA cacert.pem -CAkey cakey.pem -CAcreateserial -out new_client.pem -days 3650 -extensions v3_req -extfile client_cert.conf
```
This creates the client certificate (`new_client.pem`) valid for 10 years (3650 days), signed by the CA certificate.
## Installing the Certificates
### 7. Copy Certificates to Expected Locations
```bash
cp new_server.pem servercert.pem && \
cp new_server.key prvtkey.pem && \
cp new_client.pem client_cert.pem && \
cp new_client.key client_key.pem
```
This copies the newly generated certificates and keys to the filenames expected by the example application.
## File Naming Convention
The example application expects the following files:
- `servercert.pem` - Server certificate
- `prvtkey.pem` - Server private key
- `client_cert.pem` - Client certificate
- `client_key.pem` - Client private key
- `cacert.pem` - CA certificate (for verification)
## Security Notes
⚠️ **Important Security Considerations:**
- The private keys (`prvtkey.pem`, `client_key.pem`, `cakey.pem`) should be kept secure. As these are for demonstration purposes, they are included here, but in a production environment, ensure they are stored securely and access is restricted.
- The certificates in this example directory are for **demonstration purposes only**
- For production use, generate new certificates with appropriate security parameters
- Consider using shorter validity periods for production certificates
- Store private keys with restricted file permissions (e.g., `chmod 600`)
## Verifying Generated Certificates
You can verify the generated certificates using:
```bash
# Verify server certificate
openssl x509 -in servercert.pem -text -noout
# Verify client certificate
openssl x509 -in client_cert.pem -text -noout
# Verify certificate chain
openssl verify -CAfile cacert.pem servercert.pem
openssl verify -CAfile cacert.pem client_cert.pem
```
## Troubleshooting
- If certificate verification fails, ensure the CA certificate and key are valid and match
- Check that the configuration files (`server_cert.conf`, `client_cert.conf`) contain appropriate Subject Alternative Names (SANs) and extensions
- Ensure OpenSSL version is up to date for best compatibility
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDOzCCAiOgAwIBAgIUG/S51QF4EeUkdaqg54oogqIKBZkwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMjUw
NDAyMDcwMzI2WhcNMzUwMzMxMDcwMzI2WjAlMSMwIQYDVQQDDBpFU1AzMiBIVFRQ
UyBzZXJ2ZXIgZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALBint6nP77RCQcmKgwPtTsGK0uClxg+LwKJ3WXuye3oqnnjqJCwMEneXzGdG09T
sA0SyNPwrEgebLCH80an3gWU4pHDdqGHfJQa2jBL290e/5L5MB+6PTs2NKcojK/k
qcZkn58MWXhDW1NpAnJtjVniK2Ksvr/YIYSbyD+JiEs0MGxEx+kOl9d7hRHJaIzd
GF/vO2pl295v1qXekAlkgNMtYIVAjUy9CMpqaQBCQRL+BmPSJRkXBsYk8GPnieS4
sUsp53DsNvCCtWDT6fd9D1v+BB6nDk/FCPKhtjYOwOAZlX4wWNSZpRNr5dfrxKsb
jAn4PCuR2akdF4G8WLUeDWECAwEAAaNjMGEwHQYDVR0OBBYEFMnmdJKOEepXrHI/
ivM6mVqJgAX8MB8GA1UdIwQYMBaAFMnmdJKOEepXrHI/ivM6mVqJgAX8MA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMA0GCSqGSIb3DQEBCwUAA4IBAQBP
AgAagM33DqsDi+UArUxEoqmov1rH0PHXnd/a6Ct/IvNzr0qUH8hW4Lv0tWHfOJY8
pCf7bkejxXlhP/QHb6M+sobN9tN/WupEaeqNg4pCWi+6Caj2uFW9vkQQf2j50lMg
R0oxnd6SMEQArzy3f3yYRp8rliPERY6F2Rtb9HJNh53K51FE60xONPLZ/1dtSgDB
KcJseZfhg6oAUSLjFCYJEn5xa7CsIuQ8Jx2xMo4IkU44BJ8TJS4zw/hP1/vVjjvS
uU2Z0ZOUCQ78/3eMnsFfLMtDUYqXPyhNogm51GeHOR6dk+ICQ+c5gCDkJUnOTqzg
G2JUmXAXxJoUZDfalijl
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwYp7epz++0QkH
JioMD7U7BitLgpcYPi8Cid1l7snt6Kp546iQsDBJ3l8xnRtPU7ANEsjT8KxIHmyw
h/NGp94FlOKRw3ahh3yUGtowS9vdHv+S+TAfuj07NjSnKIyv5KnGZJ+fDFl4Q1tT
aQJybY1Z4itirL6/2CGEm8g/iYhLNDBsRMfpDpfXe4URyWiM3Rhf7ztqZdveb9al
3pAJZIDTLWCFQI1MvQjKamkAQkES/gZj0iUZFwbGJPBj54nkuLFLKedw7DbwgrVg
0+n3fQ9b/gQepw5PxQjyobY2DsDgGZV+MFjUmaUTa+XX68SrG4wJ+DwrkdmpHReB
vFi1Hg1hAgMBAAECggEAaTCnZkl/7qBjLexIryC/CBBJyaJ70W1kQ7NMYfniWwui
f0aRxJgOdD81rjTvkINsPp+xPRQO6oOadjzdjImYEuQTqrJTEUnntbu924eh+2D9
Mf2CAanj0mglRnscS9mmljZ0KzoGMX6Z/EhnuS40WiJTlWlH6MlQU/FDnwC6U34y
JKy6/jGryfsx+kGU/NRvKSru6JYJWt5v7sOrymHWD62IT59h3blOiP8GMtYKeQlX
49om9Mo1VTIFASY3lrxmexbY+6FG8YO+tfIe0tTAiGrkb9Pz6tYbaj9FjEWOv4Vc
+3VMBUVdGJjgqvE8fx+/+mHo4Rg69BUPfPSrpEg7sQKBgQDlL85G04VZgrNZgOx6
pTlCCl/NkfNb1OYa0BELqWINoWaWQHnm6lX8YjrUjwRpBF5s7mFhguFjUjp/NW6D
0EEg5BmO0ePJ3dLKSeOA7gMo7y7kAcD/YGToqAaGljkBI+IAWK5Su5yldrECTQKG
YnMKyQ1MWUfCYEwHtPvFvE5aPwKBgQDFBWXekpxHIvt/B41Cl/TftAzE7/f58JjV
MFo/JCh9TDcH6N5TMTRS1/iQrv5M6kJSSrHnq8pqDXOwfHLwxetpk9tr937VRzoL
CuG1Ar7c1AO6ujNnAEmUVC2DppL/ck5mRPWK/kgLwZSaNcZf8sydRgphsW1ogJin
7g0nGbFwXwKBgQCPoZY07Pr1TeP4g8OwWTu5F6dSvdU2CAbtZthH5q98u1n/cAj1
noak1Srpa3foGMTUn9CHu+5kwHPIpUPNeAZZBpq91uxa5pnkDMp3UrLIRJ2uZyr8
4PxcknEEh8DR5hsM/IbDcrCJQglM19ZtQeW3LKkY4BsIxjDf45ymH407IQKBgE/g
Ul6cPfOxQRlNLH4VMVgInSyyxWx1mODFy7DRrgCuh5kTVh+QUVBM8x9lcwAn8V9/
nQT55wR8E603pznqY/jX0xvAqZE6YVPcw4kpZcwNwL1RhEl8GliikBlRzUL3SsW3
q30AfqEViHPE3XpE66PPo6Hb1ymJCVr77iUuC3wtAoGBAIBrOGunv1qZMfqmwAY2
lxlzRgxgSiaev0lTNxDzZkmU/u3dgdTwJ5DDANqPwJc6b8SGYTp9rQ0mbgVHnhIB
jcJQBQkTfq6Z0H6OoTVi7dPs3ibQJFrtkoyvYAbyk36quBmNRjVh6rc8468bhXYr
v/t+MeGJP/0Zw8v/X2CFll96
-----END PRIVATE KEY-----
@@ -0,0 +1,16 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SG
ST = SG
L = SG
O = ESP32 Client
CN = ESP32 Test Client
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIUeWALux8MJqS983GSSUHotYp9OxgwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMjUx
MDEwMDIzOTIxWhcNMzUxMDA4MDIzOTIxWjBaMQswCQYDVQQGEwJTRzELMAkGA1UE
CAwCU0cxCzAJBgNVBAcMAlNHMRUwEwYDVQQKDAxFU1AzMiBDbGllbnQxGjAYBgNV
BAMMEUVTUDMyIFRlc3QgQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAsWm6XLeoa2dVtUn+hEZMULUSS0RqcE9kPHBQCwRNSlDs6eUSy4Y45SO0
P5aqDLd7vMcDg41YPDbXp6JhvKov2Q7pAVbwhrLzn+M6O7XS+7i8J9PzgjBTKMhC
rLkGBYbyW04STIbgHmQ0G98y6deyXnkqg/d0OzNaB4XgOiRbuxw1IaPX2loTS5aV
hnuo/5tdcyiNMAjio/P1GrkE1OPSUwbdxROjmZW5peFkD4sogW/XH0oMYpMKwyWZ
cHiuj5E4IxeazqaJqzjbXIj7+hTywE2kXZDVK1o02JbkDRhig5NkXv38DyNu8R8W
dvh3GBczZS7lIn+2fjr3soILMOJ/NwIDAQABo28wbTAJBgNVHRMEAjAAMAsGA1Ud
DwQEAwIF4DATBgNVHSUEDDAKBggrBgEFBQcDAjAdBgNVHQ4EFgQUubkj5sTevwdf
SMd2J5WyKkrqtfIwHwYDVR0jBBgwFoAUyeZ0ko4R6lescj+K8zqZWomABfwwDQYJ
KoZIhvcNAQELBQADggEBAJmjtzv5aMkNKw8+EwDrdOHJsw6czOtPeVLTmIy02+Pv
4rvn+4VDCzogM7oBbFzFFGwUmBXqfdmP4wteqtfdCwHboiCzZySPw0O+OMbhQmEF
mCE+k+sCUEWrb3tiraVWh1aipoqOqE0WNcyZ1ZwY1OCMaVYsfFXw3lcND/7VXjp6
13WSO46avn3WvesTS/GhLg8yTJiQ8iPXMEEKhf7BTAn461ZsGR3hnz+DPtWTTOaI
9fCdjvvYsL4+k3+Y10LWUSsmB6kjyzCmx7/I4NeUEGmMAD4PD5dkkksw+TZBobsb
APf44t+dQGYc0dhfym7b+qeW8MTvmx40ZYL52HG7XCU=
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxabpct6hrZ1W1
Sf6ERkxQtRJLRGpwT2Q8cFALBE1KUOzp5RLLhjjlI7Q/lqoMt3u8xwODjVg8Nten
omG8qi/ZDukBVvCGsvOf4zo7tdL7uLwn0/OCMFMoyEKsuQYFhvJbThJMhuAeZDQb
3zLp17JeeSqD93Q7M1oHheA6JFu7HDUho9faWhNLlpWGe6j/m11zKI0wCOKj8/Ua
uQTU49JTBt3FE6OZlbml4WQPiyiBb9cfSgxikwrDJZlweK6PkTgjF5rOpomrONtc
iPv6FPLATaRdkNUrWjTYluQNGGKDk2Re/fwPI27xHxZ2+HcYFzNlLuUif7Z+Ovey
ggsw4n83AgMBAAECggEAMLZZg7yvwzG/0EOtXQ9aQ+y7xavW19iMqqWh7Kx1NlsK
+du6ceR8Obo4cx9AuLYmhPpV5iiImhvq0a3dzSojciNMaeA/sZRwHS4MXrm5YQFj
tEHXgh8XrkJyQC+bTigz4ksI7jc4UU/tGNwLhDaD1LnLKSnoIZsjdJ5XJ0+1WiXa
pyTy5KBtKyhhLzy+toFeheNMbHV2yf2dFEFYM+76DKgXyMwNF1NzDUNhmxJrc/8H
0qb4P/gFgWe1H3ZveY/iOyLzqmqBQw02blug+YHxLfC9Gc7Wz6aY86YxGg1bKjvi
AG1GxtCKhUzBZMH7gGZSQJ+6Beq5cNReEgbSJu2N1QKBgQDnlz6cy22SMORCqUc9
CO+NwzZeywruY8wjhZYx0kKqeC/g5Ama1+2THL7DVR+dgTJOuRvBgBtps/jfQKdz
BpVeIfAH2n6x+62tjnHW274BwQy8SEf7nc4hvEY/PKUSqZ4JAeNISghHgfCMtcoW
Lv7Ehbx+uA1AJPsGwIgCmlxBlQKBgQDEHKoZ2jeLlouYFsJlgEpqcLRQA+z8QsR7
DSNjBZmuC4kKkQuoWhRoyvvgZIxT5xLkKG0eUh7kKsOdJ7Fv448rr6I9A03z9dDw
bGrDl+vKlHSyo+9rfw8HXPG8G1b2X0K9LI8t6wBKHSPt8Bv5CXL9exJVQ33CndMW
iIfpns0imwKBgANqyOK5YbGBhSyyoLl200oNMlUtu8iOsmlnxDKR/qfTRCmWU8n0
G65LA0mQjPne+SYONymgwUbLAAYTRyU8WKHd8FO9Vpc7tnFUI7ve3CvcdFqm2mEN
EAiRZZvzQiBHXmyVmYvsg7jCYxFAcW3oXZv6uTBJePCUWxvbZWZcbrYNAoGAcgWY
gN93XBlzoEHbVNh6a9iLfdpKd4D6a/D/mhsvdxoN267pcECvjR43xAex7zZyrWUz
zGVCwLZ8dWsWp09Pdr7vPTomoKlTifX/PSmfVnFqSFM4aO++9TD8+7mJnkVUsFiw
BqqTyIOY2Ea6fNkZmndr+Vb8T6Mjj/5hx1slOfECgYEAttRU86Duvm8gypIux5xY
CZeYiNCbZ/4+mR5yvfhVpMk2BJyOvtmoe6PnM+VLm+pNjZdK6mkPevXC6ownbyTl
WOOmoxnl0lbZiKBmreNrdSKuAgezAATLQdipzuV8R4Udsko0lF03ZT1uMTBqI0Fs
Kid3fvAzqQtoCLPyRsfKAr0=
-----END PRIVATE KEY-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+8VU37LjhtI2p
/O8QCBdhFzzYv/+3VipAySD7TCCO6coHEs8PyV6z+uQ868PRmkyqBJgbW0/WdSt4
RCTyCraTNMDIWEuoxbFHSLObT6fiMU9vlYcNlZkCMFIEqgd4r5657tQi1ROZ8gOD
Jx3zpW9M5Yl5+jSDzLP7oAJWgbf3rTK/ePk4AU7SWAJGQXcD/C8RLsryyWE0Lps/
X/B24GTXEj+u4Gp761KilDX8M3wLeLvYBb9DZ3rDwCXPJ8ukVc7ui5VZgGTOykVz
u8YUnc0jJI7ptaOMv4oDniJ6aqfGaV8ghwo1xq3Rpbo0jOOeFgjOgM8JyCtbK/4r
sRHiep3rAgMBAAECggEAH3x+V/2CMz3pymk6JsOez1TcpMVsbpgX2Z0RAj94cvic
ZvQ0Dt9e7YDm2CDspoiyMasWRhSVosCpjWh3Sy53Euk1DRR6TXdkF2Qmseq9vW/y
MG1Q2u2bUKAVNk2vc7hKDVETzDakx9L/v0XZC49xPhXvyJx4wm8kEs8883TqmD2/
T5d7Xs7Rxkwo3iduqwHs3jWgJWwi44Il+ATfySAbymTZcPCXxOtWjvStAjgjtMEm
KQYvvb0ZJIpqiPDH0Inq4D8U1AE0i16RlRteN6UB7avEP1v0SHFg1P2hTzWrwjZD
lpgQ5JU6WO8tdtddMuR3u4hO5g6MJ2k5wkdLOjLTuQKBgQDm0ijy2/mCiqvKxaVp
4hdAaTssHxT/+hDtWlcpqqYCgmNX7KRIzfTWLDg3qhENLcrxdBNt8bnhas2/E0/I
IA8NyB9tkQlEXv5DYfUHG7ZFUndBgIftFRLM0Ibfx4A58PjQWwe9jtqSpQJdrEL6
WRXsSBuveAqB4BTMtimUfxRH1wKBgQDTxYt1tij+x+ApVE4k//Vf+loKWOXDO/Bm
R+O1e0mYEViEjpHd2QCEyy6JTYkTktO9yLt2A5rpZiDIA2wqnl0VMUXTbUOvsLNH
UXOpy2rebWgEEZkTq/aFkhH4juLfimT6lJzZ18navPuFIj5Nj1emFU5NHQSOZ/EA
6OPo5eXIDQKBgEDudd57yyDR6anNF89Fbs0LzT2IMNwheImMlGCARNsH2vJs+3oP
lgR5xAbErK9MZn6t7JlNGsEyzlYmFJdzjUiPN2gXGMhHALfr4oXxYcD2hd3DTnl/
KB69unNRJ90k0JmsQe0tNodyK8w2HVFXpjclwcQGvM30P2WnCONhLE9ZAoGAHcKz
OJWi6Ts5m1VHrhdyakyKfs3DbE5uGFGeBJEQ5Jf7cpV+lki6s+7B2XXV/7QwoYkm
Hw2epZI+pR0mBE9BEYtdHrtKOdSBPVKLCJ+Xoy6I4Zl/g6409Mx0ThP2eie+zSA5
crvKmDzas/j9/HRagvKXkGq1izW8Pr572O0F/7kCgYANIGZ2YyGbuRdDxtNd1B/F
MhK9Njbwee8FN5Emxyq/nJJe13gyNDRCmDqUlTDbCXVCyT3fWg6dLVZu0igf3Zf2
+Z8lHBOD1VVBRdsYtRyZ/OvYksjOtBnZ5l4rMMt7MGxhA1Dm/oO/3ub0/dhrXnb+
MD6TRIZ3GhAZ1SPeuhEF5w==
-----END PRIVATE KEY-----
@@ -0,0 +1,20 @@
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = SG
ST = SG
L = SG
O = ESP32
CN = ESP32 HTTPS Server
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = esp32-server
DNS.2 = localhost
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIUeWALux8MJqS983GSSUHotYp9OxcwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMjUx
MDEwMDIzOTAwWhcNMzUxMDA4MDIzOTAwWjBUMQswCQYDVQQGEwJTRzELMAkGA1UE
CAwCU0cxCzAJBgNVBAcMAlNHMQ4wDAYDVQQKDAVFU1AzMjEbMBkGA1UEAwwSRVNQ
MzIgSFRUUFMgU2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
vvFVN+y44bSNqfzvEAgXYRc82L//t1YqQMkg+0wgjunKBxLPD8les/rkPOvD0ZpM
qgSYG1tP1nUreEQk8gq2kzTAyFhLqMWxR0izm0+n4jFPb5WHDZWZAjBSBKoHeK+e
ue7UItUTmfIDgycd86VvTOWJefo0g8yz+6ACVoG3960yv3j5OAFO0lgCRkF3A/wv
ES7K8slhNC6bP1/wduBk1xI/ruBqe+tSopQ1/DN8C3i72AW/Q2d6w8AlzyfLpFXO
7ouVWYBkzspFc7vGFJ3NIySO6bWjjL+KA54iemqnxmlfIIcKNcat0aW6NIzjnhYI
zoDPCcgrWyv+K7ER4nqd6wIDAQABo34wfDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF
4DAiBgNVHREEGzAZggxlc3AzMi1zZXJ2ZXKCCWxvY2FsaG9zdDAdBgNVHQ4EFgQU
U2FQtf+ar/qQtk5hBObVuW/VAjMwHwYDVR0jBBgwFoAUyeZ0ko4R6lescj+K8zqZ
WomABfwwDQYJKoZIhvcNAQELBQADggEBAHS3Nr8OTEWV6s5srMDxnvjry4XmBOU2
gvNxLe7xci6DShCpFK+S+W/Vqmv0I17TEW5cV8Z/P5HKtiYAvNI7Ptc6BTK20Q83
jKC6kSYMlK54zW4ZNbnvI/zwtMNh4YLiLVjSQxMe11qaTu7eOqI49qEmgNP82VPu
JmFvO4W05McdUJ3xxVJVJk3l8ZTjgOTPfL/bbqibYSvewsyJlo2ihIHezP4Au/4Z
2Wpzj1scBDSucDTsWBkXMGdV4tUpUuDYLJF2XqKwIm39IkoxdPnHANC62OtcLawh
FOjfM34YqanizzWwfjNl6OD8vWP4ztIf3J4jONiDnnpVIo6skAGYTyQ=
-----END CERTIFICATE-----
@@ -0,0 +1,7 @@
dependencies:
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
esp_stubs:
path: ${IDF_PATH}/examples/protocols/linux_stubs/esp_stubs
rules:
- if: "target in [linux]"
@@ -0,0 +1,324 @@
/* Simple HTTP + SSL Server 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 <unistd.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "esp_netif.h"
#include "protocol_examples_common.h"
#if !CONFIG_IDF_TARGET_LINUX
#include <esp_wifi.h>
#include <esp_system.h>
#include "esp_eth.h"
#endif // !CONFIG_IDF_TARGET_LINUX
#include <esp_https_server.h>
#include "esp_tls.h"
#include "esp_key_config.h"
#include "sdkconfig.h"
#if CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES
#include "mbedtls/ssl_ciphersuites.h"
#endif // CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES
/* A simple example that demonstrates how to create GET and POST
* handlers and start an HTTPS server.
*/
static const char *TAG = "example";
#ifdef CONFIG_ESP_HTTPS_SERVER_EVENTS
/* Event handler for catching system events */
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == ESP_HTTPS_SERVER_EVENT) {
if (event_id == HTTPS_SERVER_EVENT_ERROR) {
esp_https_server_last_error_t *last_error = (esp_tls_last_error_t *) event_data;
ESP_LOGE(TAG, "Error event triggered: last_error = %s, last_tls_err = %d, tls_flag = %d", esp_err_to_name(last_error->last_error), last_error->esp_tls_error_code, last_error->esp_tls_flags);
}
}
}
#endif // CONFIG_ESP_HTTPS_SERVER_EVENTS
/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
#if CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
static void print_peer_cert_info(const mbedtls_ssl_context *ssl)
{
const mbedtls_x509_crt *cert;
const size_t buf_size = 1024;
char *buf = calloc(buf_size, sizeof(char));
if (buf == NULL) {
ESP_LOGE(TAG, "Out of memory - Callback execution failed!");
return;
}
// Logging the peer certificate info
cert = mbedtls_ssl_get_peer_cert(ssl);
if (cert != NULL) {
mbedtls_x509_crt_info((char *) buf, buf_size - 1, " ", cert);
ESP_LOGI(TAG, "Peer certificate info:\n%s", buf);
} else {
ESP_LOGW(TAG, "Could not obtain the peer certificate!");
}
free(buf);
}
#endif
/**
* Example callback function to get the certificate of connected clients,
* whenever a new SSL connection is created and closed
*
* Can also be used to other information like Socket FD, Connection state, etc.
*
* NOTE: This callback will not be able to obtain the client certificate if the
* following config `Set minimum Certificate Verification mode to Optional` is
* not enabled (enabled by default in this example).
*
* The config option is found here - Component config → ESP-TLS
*
*/
static void https_server_user_callback(esp_https_server_user_cb_arg_t *user_cb)
{
ESP_LOGI(TAG, "User callback invoked!");
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_ssl_context *ssl_ctx = NULL;
#endif
switch(user_cb->user_cb_state) {
case HTTPD_SSL_USER_CB_SESS_CREATE:
ESP_LOGD(TAG, "At session creation");
// Logging the socket FD
int sockfd = -1;
esp_err_t esp_ret;
esp_ret = esp_tls_get_conn_sockfd(user_cb->tls, &sockfd);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Error in obtaining the sockfd from tls context");
break;
}
ESP_LOGI(TAG, "Socket FD: %d", sockfd);
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
ssl_ctx = (mbedtls_ssl_context *) esp_tls_get_ssl_context(user_cb->tls);
if (ssl_ctx == NULL) {
ESP_LOGE(TAG, "Error in obtaining ssl context");
break;
}
// Logging the current ciphersuite
ESP_LOGI(TAG, "Current Ciphersuite: %s", mbedtls_ssl_get_ciphersuite(ssl_ctx));
#endif
break;
case HTTPD_SSL_USER_CB_SESS_CLOSE:
ESP_LOGD(TAG, "At session close");
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
// Logging the peer certificate
ssl_ctx = (mbedtls_ssl_context *) esp_tls_get_ssl_context(user_cb->tls);
if (ssl_ctx == NULL) {
ESP_LOGE(TAG, "Error in obtaining ssl context");
break;
}
print_peer_cert_info(ssl_ctx);
#endif
break;
case HTTPD_SSL_USER_CB_SESS_ERROR:
ESP_LOGD(TAG, "At session error (handshake failed)");
// Get socket FD to log failing client IP address
sockfd = -1;
esp_ret = esp_tls_get_conn_sockfd(user_cb->tls, &sockfd);
if (esp_ret == ESP_OK && sockfd >= 0) {
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
if (getpeername(sockfd, (struct sockaddr*)&addr, &len) == 0) {
char ip_str[INET6_ADDRSTRLEN];
if (addr.ss_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in*)&addr)->sin_addr, ip_str, sizeof(ip_str));
} else if (addr.ss_family == AF_INET6) {
inet_ntop(AF_INET6, &((struct sockaddr_in6*)&addr)->sin6_addr, ip_str, sizeof(ip_str));
} else {
strcpy(ip_str, "unknown");
}
ESP_LOGW(TAG, "SSL handshake failed from client IP: %s", ip_str);
}
}
break;
default:
ESP_LOGE(TAG, "Illegal state!");
return;
}
}
#endif
static const httpd_uri_t root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler
};
static httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
// Start the httpd server
ESP_LOGI(TAG, "Starting server");
httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();
#if CONFIG_IDF_TARGET_LINUX
/* Use non-privileged port on Linux since port 443 requires root */
conf.port_secure = 8443;
#endif
extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");
extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end");
extern const unsigned char cacert_start[] asm("_binary_cacert_pem_start");
extern const unsigned char cacert_end[] asm("_binary_cacert_pem_end");
conf.servercert = servercert_start;
conf.servercert_len = servercert_end - servercert_start;
#if CONFIG_EXAMPLE_ENABLE_SKIP_CLIENT_CERT
conf.client_cert_authmode_optional = true;
#endif // EXAMPLE_ENABLE_SKIP_CLIENT_CERT
conf.cacert_pem = cacert_start;
conf.cacert_len = cacert_end - cacert_start;
extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start");
extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end");
static esp_key_config_t server_key = {
.source = ESP_KEY_SOURCE_BUFFER,
.buffer = {
.data = prvtkey_pem_start,
}
};
server_key.buffer.len = prvtkey_pem_end - prvtkey_pem_start;
conf.server_key = &server_key;
#if CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES
static const int ciphersuites_to_use[] = {
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
0,
};
conf.ciphersuites_list = ciphersuites_to_use;
#else
conf.ciphersuites_list = NULL;
#endif // CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES
#if CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_3_ONLY
conf.tls_version = ESP_TLS_VER_TLS_1_3;
#elif CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_2_ONLY
conf.tls_version = ESP_TLS_VER_TLS_1_2;
#else
conf.tls_version = ESP_TLS_VER_ANY;
#endif // CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_3_ONLY
#if CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK
conf.user_cb = https_server_user_callback;
#endif
esp_err_t ret = httpd_ssl_start(&server, &conf);
if (ESP_OK != ret) {
ESP_LOGI(TAG, "Error starting server!");
return NULL;
}
// Set URI handlers
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &root);
return server;
}
#if !CONFIG_IDF_TARGET_LINUX
static esp_err_t stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
return httpd_ssl_stop(server);
}
static void disconnect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server) {
if (stop_webserver(*server) == ESP_OK) {
*server = NULL;
} else {
ESP_LOGE(TAG, "Failed to stop https server");
}
}
}
static void connect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server == NULL) {
*server = start_webserver();
}
}
#endif // !CONFIG_IDF_TARGET_LINUX
void app_main(void)
{
static httpd_handle_t server = NULL;
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* Register event handlers to start server when Wi-Fi or Ethernet is connected,
* and stop server when disconnection happens.
*/
#if !CONFIG_IDF_TARGET_LINUX
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
#endif // !CONFIG_IDF_TARGET_LINUX
#ifdef CONFIG_ESP_HTTPS_SERVER_EVENTS
ESP_ERROR_CHECK(esp_event_handler_register(ESP_HTTPS_SERVER_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
#endif // CONFIG_ESP_HTTPS_SERVER_EVENTS
/* 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());
#if CONFIG_IDF_TARGET_LINUX
/* On Linux, start the server directly since there are no WiFi/Ethernet events */
server = start_webserver();
while (server) {
sleep(5);
}
#endif // CONFIG_IDF_TARGET_LINUX
}
@@ -0,0 +1,395 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import http.client
import logging
import os
import ssl
import pytest
from common_test_methods import get_env_config_variable
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
CURRENT_FILE_PATH = os.path.dirname(__file__)
CACERT_FILE_PATH = os.path.join(CURRENT_FILE_PATH, './main/certs/cacert.pem')
CLIENT_CERT_PATH = os.path.join(CURRENT_FILE_PATH, './main/certs/client_cert.pem')
CLIENT_KEY_PATH = os.path.join(CURRENT_FILE_PATH, './main/certs/client_key.pem')
success_response = '<h1>Hello Secure World!</h1>'
@pytest.mark.wifi_router
@idf_parametrize('target', ['esp32', 'esp32c3', 'esp32s3'], indirect=['target'])
def test_examples_protocol_https_server_simple(dut: Dut) -> None:
"""
steps: |
1. join AP
2. connect to www.howsmyssl.com:443
3. send http request
"""
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'https_server.bin')
bin_size = os.path.getsize(binary_file)
logging.info(f'https_server_simple_bin_size : {bin_size // 1024}KB')
# start test
logging.info('Waiting to connect with AP')
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
dut.expect('Please input ssid password:')
env_name = 'wifi_router'
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
ap_password = get_env_config_variable(env_name, 'ap_password')
dut.write(f'{ap_ssid} {ap_password}')
# Parse IP address and port of the server
dut.expect(r'Starting server')
got_port = int(dut.expect(r'Server listening on port (\d+)', timeout=30)[1].decode())
got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
# Expected logs
logging.info(f'Got IP : {got_ip}')
logging.info(f'Got Port : {got_port}')
# Try requesting without client certificate and it should fail if client cert is required
logging.info('Performing GET request over an SSL connection with the server without client certificate')
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
# Load the CA certificate from the path
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server without client certificate')
try:
conn.request('GET', '/')
resp = conn.getresponse()
resp.read().decode('utf-8')
except Exception as e:
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_SKIP_CLIENT_CERT') is True:
logging.info('SSL handshake failed without client certificate but was expected to be successful')
raise RuntimeError('Failed to test SSL connection') from e
else:
logging.info('SSL handshake failed without client certificate as expected')
finally:
conn.close()
logging.info('Performing GET request over an SSL connection with the server')
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
ssl_context.load_cert_chain(certfile=CLIENT_CERT_PATH, keyfile=CLIENT_KEY_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server')
conn.request('GET', '/')
resp = conn.getresponse()
dut.expect('performing session handshake')
got_resp = resp.read().decode('utf-8')
if got_resp != success_response:
logging.info('Response obtained does not match with correct response')
raise RuntimeError('Failed to test SSL connection')
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_HTTPS_USER_CALLBACK') is True:
current_cipher = dut.expect(r'Current Ciphersuite(.*)', timeout=5)[0]
logging.info(f'Current Ciphersuite {current_cipher}')
conn.close()
logging.info('Checking user callback: Obtaining client certificate...')
serial_number = dut.expect(r'serial number\s*:([^\n]*)', timeout=5)[0]
issuer_name = dut.expect(r'issuer name\s*:([^\n]*)', timeout=5)[0]
expiry = dut.expect(r'expires on ((.*)\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*)', timeout=5)[
1
].decode()
logging.info(f'Serial No. {serial_number}')
logging.info(f'Issuer Name {issuer_name}')
logging.info(f'Expires on {expiry}')
logging.info('Correct response obtained')
logging.info('SSL connection test successful\nClosing the connection')
@pytest.mark.wifi_router
@pytest.mark.parametrize(
'config',
[
'dynamic_buffer',
],
indirect=True,
)
@idf_parametrize('target', ['esp32', 'esp32c3', 'esp32s3'], indirect=['target'])
def test_examples_protocol_https_server_simple_dynamic_buffers(dut: Dut) -> None:
# Test with mbedTLS dynamic buffer feature
# start test
logging.info('Waiting to connect with AP')
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
dut.expect('Please input ssid password:')
env_name = 'wifi_router'
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
ap_password = get_env_config_variable(env_name, 'ap_password')
dut.write(f'{ap_ssid} {ap_password}')
# Parse IP address and port of the server
dut.expect(r'Starting server')
got_port = int(dut.expect(r'Server listening on port (\d+)', timeout=30)[1].decode())
got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
# Expected logs
logging.info(f'Got IP : {got_ip}')
logging.info(f'Got Port : {got_port}')
logging.info('Performing GET request over an SSL connection with the server')
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
ssl_context.load_cert_chain(certfile=CLIENT_CERT_PATH, keyfile=CLIENT_KEY_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server')
conn.request('GET', '/')
resp = conn.getresponse()
dut.expect('performing session handshake')
got_resp = resp.read().decode('utf-8')
if got_resp != success_response:
logging.info('Response obtained does not match with correct response')
raise RuntimeError('Failed to test SSL connection')
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_HTTPS_USER_CALLBACK') is True:
current_cipher = dut.expect(r'Current Ciphersuite(.*)', timeout=5)[0]
logging.info(f'Current Ciphersuite {current_cipher}')
# Close the connection
conn.close()
logging.info('Checking user callback: Obtaining client certificate...')
serial_number = dut.expect(r'serial number\s*:([^\n]*)', timeout=5)[0]
issuer_name = dut.expect(r'issuer name\s*:([^\n]*)', timeout=5)[0]
expiry = dut.expect(r'expires on\s*:((.*)\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*)', timeout=5)[
1
].decode()
logging.info(f'Serial No. : {serial_number}')
logging.info(f'Issuer Name : {issuer_name}')
logging.info(f'Expires on : {expiry}')
logging.info('Correct response obtained')
logging.info('SSL connection test successful\nClosing the connection')
@pytest.mark.wifi_router
@pytest.mark.parametrize(
'config',
[
'tls1_3',
],
indirect=True,
)
@idf_parametrize('target', ['esp32', 'esp32c3', 'esp32s3'], indirect=['target'])
def test_examples_protocol_https_server_tls1_3(dut: Dut) -> None:
logging.info('Waiting to connect with AP')
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
dut.expect('Please input ssid password:')
env_name = 'wifi_router'
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
ap_password = get_env_config_variable(env_name, 'ap_password')
dut.write(f'{ap_ssid} {ap_password}')
# Parse IP address and port of the server
dut.expect(r'Starting server')
got_port = int(dut.expect(r'Server listening on port (\d+)', timeout=30)[1].decode())
got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
# Expected logs
logging.info(f'Got IP : {got_ip}')
logging.info(f'Got Port : {got_port}')
logging.info('Performing GET request over an SSL connection with the server using TLSv1.3')
# First try requesting without client certificate and it should also pass if client cert is optional or none
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_3
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server without client certificate')
try:
conn.request('GET', '/')
resp = conn.getresponse()
resp.read().decode('utf-8')
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_SKIP_CLIENT_CERT') is True:
dut.expect('performing session handshake')
logging.info('SSL handshake successful without client certificate as expected')
else:
logging.info('SSL handshake successful without client certificate but was expected to fail')
raise RuntimeError('Failed to test SSL connection')
except Exception as e:
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_SKIP_CLIENT_CERT') is True:
logging.info(f'SSL handshake failed without client certificate but was expected to be successful: {e}')
raise RuntimeError('Failed to test SSL connection')
else:
logging.info('SSL handshake failed without client certificate as expected')
finally:
conn.close()
# First try with TLSv1.2 and that should fail
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_2
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
ssl_context.load_cert_chain(certfile=CLIENT_CERT_PATH, keyfile=CLIENT_KEY_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
try:
conn.request('GET', '/')
except ssl.SSLError as e:
logging.info(f'SSL handshake failed with TLSv1.2: {e}')
else:
logging.info('SSL handshake succeeded with TLSv1.2')
raise RuntimeError('This should have failed')
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_3
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server')
conn.request('GET', '/')
resp = conn.getresponse()
dut.expect('performing session handshake')
got_resp = resp.read().decode('utf-8')
if got_resp != success_response:
logging.info('Response obtained does not match with correct response')
raise RuntimeError('Failed to test SSL connection')
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_HTTPS_USER_CALLBACK') is True:
current_cipher = dut.expect(r'Current Ciphersuite(.*)', timeout=5)[0]
logging.info(f'Current Ciphersuite {current_cipher}')
conn.close()
logging.info('Checking user callback: Obtaining client certificate...')
serial_number = dut.expect(r'serial number\s*:([^\n]*)', timeout=5)[0]
issuer_name = dut.expect(r'issuer name\s*:([^\n]*)', timeout=5)[0]
expiry = dut.expect(
r'expires on\s*:((.*)\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*)',
timeout=5,
)[1].decode()
logging.info(f'Serial No. : {serial_number}')
logging.info(f'Issuer Name : {issuer_name}')
logging.info(f'Expires on : {expiry}')
logging.info('Correct response obtained')
logging.info('SSL connection test successful\nClosing the connection')
@pytest.mark.wifi_router
@pytest.mark.parametrize(
'config',
[
'tls1_2_only',
],
indirect=True,
)
@idf_parametrize('target', ['esp32', 'esp32c3', 'esp32s3'], indirect=['target'])
def test_examples_protocol_https_server_tls1_2_only(dut: Dut) -> None:
logging.info('Waiting to connect with AP')
if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True:
dut.expect('Please input ssid password:')
env_name = 'wifi_router'
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
ap_password = get_env_config_variable(env_name, 'ap_password')
dut.write(f'{ap_ssid} {ap_password}')
# Parse IP address and port of the server
dut.expect(r'Starting server')
got_port = int(dut.expect(r'Server listening on port (\d+)', timeout=30)[1].decode())
got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
# Expected logs
logging.info(f'Got IP : {got_ip}')
logging.info(f'Got Port : {got_port}')
logging.info('Performing GET request over an SSL connection with the server using TLSv1.2')
# First try with TLSv1.3 and that should fail
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_3
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cafile=CACERT_FILE_PATH)
# ssl_context.load_verify_locations(cadata=server_cert_pem)
ssl_context.load_cert_chain(certfile=CLIENT_CERT_PATH, keyfile=CLIENT_KEY_PATH)
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
try:
conn.request('GET', '/')
except ssl.SSLError as e:
logging.info(f'SSL handshake failed with TLSv1.3: {e}')
else:
logging.info('SSL handshake succeeded with TLSv1.3')
raise RuntimeError('This should have failed')
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_2
# Also now with TLS1.2, try with a non matching ciphersuite and that should fail
# Server only accepts: DHE-RSA-AES128-SHA256, DHE-RSA-AES256-SHA256,
# ECDHE-RSA-AES256-SHA384, ECDHE-RSA-AES128-SHA256
# Try AES128-GCM-SHA256 which is NOT in the list
ssl_context.set_ciphers('AES128-GCM-SHA256')
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
try:
logging.info('Trying SSL handshake with non-matching ciphersuite (should fail)')
conn.request('GET', '/')
except ssl.SSLError as e:
logging.info(f'SSL handshake failed with non-matching ciphersuite (expected): {e}')
else:
logging.info('SSL handshake succeeded with non-matching ciphersuite')
raise RuntimeError('This should have failed - custom ciphersuites not enforced')
finally:
conn.close()
# Now try with the matching ciphersuite
ssl_context.set_ciphers('ECDHE-RSA-AES128-SHA256')
conn = http.client.HTTPSConnection(got_ip, got_port, context=ssl_context)
logging.info('Performing SSL handshake with the server')
conn.request('GET', '/')
resp = conn.getresponse()
dut.expect('performing session handshake')
got_resp = resp.read().decode('utf-8')
if got_resp != success_response:
logging.info('Response obtained does not match with correct response')
raise RuntimeError('Failed to test SSL connection')
if dut.app.sdkconfig.get('EXAMPLE_ENABLE_HTTPS_USER_CALLBACK') is True:
current_cipher = dut.expect(r'Current Ciphersuite(.*)', timeout=5)[0]
logging.info(f'Current Ciphersuite {current_cipher}')
logging.info('Checking user callback: Obtaining client certificate...')
serial_number = dut.expect(r'serial number\s*:([^\n]*)', timeout=5)[0]
issuer_name = dut.expect(r'issuer name\s*:([^\n]*)', timeout=5)[0]
expiry = dut.expect(
r'expires on\s*:((.*)\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*)',
timeout=5,
)[1].decode()
logging.info(f'Serial No. : {serial_number}')
logging.info(f'Issuer Name : {issuer_name}')
logging.info(f'Expires on : {expiry}')
logging.info('Correct response obtained')
logging.info('SSL connection test successful\nClosing the connection')
@@ -0,0 +1,5 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK=y
CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK=y
CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
@@ -0,0 +1,7 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_EXAMPLE_ENABLE_HTTPS_USER_CALLBACK=y
CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
CONFIG_MBEDTLS_DYNAMIC_FREE_CA_CERT=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
@@ -0,0 +1,3 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_ESP_TLS_SERVER_SESSION_TICKETS=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
@@ -0,0 +1,6 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK=y
CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y
CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_2_ONLY=y
CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_CUSTOM_CIPHERSUITES=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
@@ -0,0 +1,11 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK=y
CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y
CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y
CONFIG_MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE=y
CONFIG_MBEDTLS_SSL_TLS1_3_KEXM_PSK=y
CONFIG_MBEDTLS_SSL_TLS1_3_KEXM_EPHEMERAL=y
CONFIG_MBEDTLS_SSL_TLS1_3_KEXM_PSK_EPHEMERAL=y
CONFIG_EXAMPLE_ENABLE_HTTPS_SERVER_TLS_1_3_ONLY=y
CONFIG_EXAMPLE_ENABLE_SKIP_CLIENT_CERT=y
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y
@@ -0,0 +1,2 @@
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y