Skip to content

Commit

Permalink
Merge pull request #461 from hrushikesh430/feat/add_local_server_in_e…
Browse files Browse the repository at this point in the history
…sp_encrypted_ota_pytest

feat(pre_encrypted_ota): Added HTTPS server in pytest
  • Loading branch information
mahavirj authored Jan 16, 2025
2 parents acfcdcf + ca347ea commit 52be198
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 4 deletions.
24 changes: 24 additions & 0 deletions esp_encrypted_img/examples/pre_encrypted_ota/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ idf.py build flash

* An encrypted image is automatically generated by build system. Upload the generated encrypted image (`build/pre_encrypted_ota_secure.bin`) to a server for performing OTA update.

### Configure and start python based HTTPS Server

After a successful build, we need to create a self-signed certificate and run a simple HTTPS server as follows:

![create_self_signed_certificate](https://raw.githubusercontent.com/espressif/idf-extra-components/master/esp_encrypted_img/examples/pre_encrypted_ota/docs/ota_self_signature.gif)

* Create server_certs directory, Navigate to server_certs directory `cd server_certs`.
* To create a new self-signed certificate and key, run the command `openssl req -x509 -newkey rsa:2048 -keyout ca_key.pem -out ca_cert.pem -days 365 -nodes`.
* When prompted for the `Common Name (CN)`, enter the name of the server that the "ESP-Dev-Board" will connect to. When running this example from a development machine, this is probably the IP address. The HTTPS client will check that the `CN` matches the address given in the HTTPS URL.

You can start the server using following instructions:

After the successful build, start the local python based HTTPS server using the certificate and key present in the 'server_certs' directory (certificate: ca_cert.pem and key: ca_key.pem).

To start the server use the following command -
```
python pytest_pre_encrypted_ota.py build 8070 server_certs
```

1. build - build directory (where the new firmware image is present) will be exposed
2. 8070 - server port (user can use any port)
3. server_certs - cert directory where the certificate and key is present (here same ca_cert.pem is used in main/pre_encrypted_ota.c and server_certs dir). If user wants to use own certificate and key just pass the directory name, in which the certificate and key is present.

* Note - If you don't want to create certificates then just run the `pytest_pre_encrypted_ota.py` without passing `server_certs` directory, the server will use the hardcoded certificates present in `pytest_pre_encrypted_ota.py`

## Configuration

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ static esp_err_t root_head_handler(httpd_req_t *req)
return httpd_resp_send(req, NULL, binary_size); // No body for HEAD method
}

static const httpd_uri_t root = {
static const httpd_uri_t get_root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler
};

static const httpd_uri_t root1 = {
static const httpd_uri_t head_root = {
.uri = "/",
.method = HTTP_HEAD,
.handler = root_head_handler
Expand Down Expand Up @@ -184,7 +184,7 @@ esp_err_t example_test_start_webserver(void)

// Set URI handlers
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &root);
httpd_register_uri_handler(server, &root1);
httpd_register_uri_handler(server, &get_root);
httpd_register_uri_handler(server, &head_root);
return ESP_OK;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import http.server
import multiprocessing
import os
import sys

import socket
import ssl
from typing import Optional
from typing import Callable

import pexpect
Expand All @@ -17,6 +20,78 @@
host_ip = '127.0.0.1'
server_port = 443

server_cert = '-----BEGIN CERTIFICATE-----\n'\
'MIIDKzCCAhOgAwIBAgIUBxM3WJf2bP12kAfqhmhhjZWv0ukwDQYJKoZIhvcNAQEL\n'\
'BQAwJTEjMCEGA1UEAwwaRVNQMzIgSFRUUFMgc2VydmVyIGV4YW1wbGUwHhcNMTgx\n'\
'MDE3MTEzMjU3WhcNMjgxMDE0MTEzMjU3WjAlMSMwIQYDVQQDDBpFU1AzMiBIVFRQ\n'\
'UyBzZXJ2ZXIgZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n'\
'ALBint6nP77RCQcmKgwPtTsGK0uClxg+LwKJ3WXuye3oqnnjqJCwMEneXzGdG09T\n'\
'sA0SyNPwrEgebLCH80an3gWU4pHDdqGHfJQa2jBL290e/5L5MB+6PTs2NKcojK/k\n'\
'qcZkn58MWXhDW1NpAnJtjVniK2Ksvr/YIYSbyD+JiEs0MGxEx+kOl9d7hRHJaIzd\n'\
'GF/vO2pl295v1qXekAlkgNMtYIVAjUy9CMpqaQBCQRL+BmPSJRkXBsYk8GPnieS4\n'\
'sUsp53DsNvCCtWDT6fd9D1v+BB6nDk/FCPKhtjYOwOAZlX4wWNSZpRNr5dfrxKsb\n'\
'jAn4PCuR2akdF4G8WLUeDWECAwEAAaNTMFEwHQYDVR0OBBYEFMnmdJKOEepXrHI/\n'\
'ivM6mVqJgAX8MB8GA1UdIwQYMBaAFMnmdJKOEepXrHI/ivM6mVqJgAX8MA8GA1Ud\n'\
'EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADiXIGEkSsN0SLSfCF1VNWO3\n'\
'emBurfOcDq4EGEaxRKAU0814VEmU87btIDx80+z5Dbf+GGHCPrY7odIkxGNn0DJY\n'\
'W1WcF+DOcbiWoUN6DTkAML0SMnp8aGj9ffx3x+qoggT+vGdWVVA4pgwqZT7Ybntx\n'\
'bkzcNFW0sqmCv4IN1t4w6L0A87ZwsNwVpre/j6uyBw7s8YoJHDLRFT6g7qgn0tcN\n'\
'ZufhNISvgWCVJQy/SZjNBHSpnIdCUSJAeTY2mkM4sGxY0Widk8LnjydxZUSxC3Nl\n'\
'hb6pnMh3jRq4h0+5CZielA4/a+TdrNPv/qok67ot/XJdY3qHCCd8O2b14OVq9jo=\n'\
'-----END CERTIFICATE-----\n'

server_key = '-----BEGIN PRIVATE KEY-----\n'\
'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwYp7epz++0QkH\n'\
'JioMD7U7BitLgpcYPi8Cid1l7snt6Kp546iQsDBJ3l8xnRtPU7ANEsjT8KxIHmyw\n'\
'h/NGp94FlOKRw3ahh3yUGtowS9vdHv+S+TAfuj07NjSnKIyv5KnGZJ+fDFl4Q1tT\n'\
'aQJybY1Z4itirL6/2CGEm8g/iYhLNDBsRMfpDpfXe4URyWiM3Rhf7ztqZdveb9al\n'\
'3pAJZIDTLWCFQI1MvQjKamkAQkES/gZj0iUZFwbGJPBj54nkuLFLKedw7DbwgrVg\n'\
'0+n3fQ9b/gQepw5PxQjyobY2DsDgGZV+MFjUmaUTa+XX68SrG4wJ+DwrkdmpHReB\n'\
'vFi1Hg1hAgMBAAECggEAaTCnZkl/7qBjLexIryC/CBBJyaJ70W1kQ7NMYfniWwui\n'\
'f0aRxJgOdD81rjTvkINsPp+xPRQO6oOadjzdjImYEuQTqrJTEUnntbu924eh+2D9\n'\
'Mf2CAanj0mglRnscS9mmljZ0KzoGMX6Z/EhnuS40WiJTlWlH6MlQU/FDnwC6U34y\n'\
'JKy6/jGryfsx+kGU/NRvKSru6JYJWt5v7sOrymHWD62IT59h3blOiP8GMtYKeQlX\n'\
'49om9Mo1VTIFASY3lrxmexbY+6FG8YO+tfIe0tTAiGrkb9Pz6tYbaj9FjEWOv4Vc\n'\
'+3VMBUVdGJjgqvE8fx+/+mHo4Rg69BUPfPSrpEg7sQKBgQDlL85G04VZgrNZgOx6\n'\
'pTlCCl/NkfNb1OYa0BELqWINoWaWQHnm6lX8YjrUjwRpBF5s7mFhguFjUjp/NW6D\n'\
'0EEg5BmO0ePJ3dLKSeOA7gMo7y7kAcD/YGToqAaGljkBI+IAWK5Su5yldrECTQKG\n'\
'YnMKyQ1MWUfCYEwHtPvFvE5aPwKBgQDFBWXekpxHIvt/B41Cl/TftAzE7/f58JjV\n'\
'MFo/JCh9TDcH6N5TMTRS1/iQrv5M6kJSSrHnq8pqDXOwfHLwxetpk9tr937VRzoL\n'\
'CuG1Ar7c1AO6ujNnAEmUVC2DppL/ck5mRPWK/kgLwZSaNcZf8sydRgphsW1ogJin\n'\
'7g0nGbFwXwKBgQCPoZY07Pr1TeP4g8OwWTu5F6dSvdU2CAbtZthH5q98u1n/cAj1\n'\
'noak1Srpa3foGMTUn9CHu+5kwHPIpUPNeAZZBpq91uxa5pnkDMp3UrLIRJ2uZyr8\n'\
'4PxcknEEh8DR5hsM/IbDcrCJQglM19ZtQeW3LKkY4BsIxjDf45ymH407IQKBgE/g\n'\
'Ul6cPfOxQRlNLH4VMVgInSyyxWx1mODFy7DRrgCuh5kTVh+QUVBM8x9lcwAn8V9/\n'\
'nQT55wR8E603pznqY/jX0xvAqZE6YVPcw4kpZcwNwL1RhEl8GliikBlRzUL3SsW3\n'\
'q30AfqEViHPE3XpE66PPo6Hb1ymJCVr77iUuC3wtAoGBAIBrOGunv1qZMfqmwAY2\n'\
'lxlzRgxgSiaev0lTNxDzZkmU/u3dgdTwJ5DDANqPwJc6b8SGYTp9rQ0mbgVHnhIB\n'\
'jcJQBQkTfq6Z0H6OoTVi7dPs3ibQJFrtkoyvYAbyk36quBmNRjVh6rc8468bhXYr\n'\
'v/t+MeGJP/0Zw8v/X2CFll96\n'\
'-----END PRIVATE KEY-----\n'

def start_https_server(ota_image_dir: str, server_ip: str, port: int, server_file: Optional[str] = None, key_file: Optional[str] = None) -> None:
os.chdir(ota_image_dir)

if server_file is None:
server_file = os.path.join(ota_image_dir, 'server_cert.pem')
cert_file_handle = open(server_file, 'w+')
cert_file_handle.write(server_cert)
cert_file_handle.close()

if key_file is None:
key_file = os.path.join(ota_image_dir, 'server_key.pem')
key_file_handle = open('server_key.pem', 'w+')
key_file_handle.write(server_key)
key_file_handle.close()

httpd = http.server.HTTPServer((server_ip, port), http.server.SimpleHTTPRequestHandler)

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile=server_file, keyfile=key_file)

httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True)
httpd.serve_forever()

@pytest.mark.generic
def test_examples_protocol_pre_encrypted_ota_example(dut: Dut) -> None:
bin_path = os.path.join(dut.app.binary_path, enc_bin_name)
Expand All @@ -36,3 +111,20 @@ def test_examples_protocol_pre_encrypted_ota_example(dut: Dut) -> None:
finally:
pass

if __name__ == '__main__':
if sys.argv[2:]: # if two or more arguments are provided
# Usage: python pytest_pre_encrypted_ota.py <image_dir> <server_port> <cert_dir>
this_dir = os.path.dirname(os.path.realpath(__file__))
bin_dir = os.path.join(this_dir, sys.argv[1])
port = int(sys.argv[2])
cert_dir = bin_dir if not sys.argv[3:] else os.path.join(this_dir, sys.argv[3]) # optional argument
print(f'Starting HTTPS server at "https://0.0.0.0:{port}"')
server_file=os.path.join(cert_dir, 'ca_cert.pem')
key_file=os.path.join(cert_dir, 'ca_key.pem')
#check if file exits
if not os.path.exists(server_file):
server_file = None
if not os.path.exists(key_file):
key_file = None

start_https_server(bin_dir, '', port, server_file, key_file)

0 comments on commit 52be198

Please sign in to comment.