diff --git a/esp_encrypted_img/examples/pre_encrypted_ota/README.md b/esp_encrypted_img/examples/pre_encrypted_ota/README.md index 2433e3789b..e02e88d8bb 100644 --- a/esp_encrypted_img/examples/pre_encrypted_ota/README.md +++ b/esp_encrypted_img/examples/pre_encrypted_ota/README.md @@ -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 diff --git a/esp_encrypted_img/examples/pre_encrypted_ota/docs/ota_self_signature.gif b/esp_encrypted_img/examples/pre_encrypted_ota/docs/ota_self_signature.gif new file mode 100644 index 0000000000..138665de1a Binary files /dev/null and b/esp_encrypted_img/examples/pre_encrypted_ota/docs/ota_self_signature.gif differ diff --git a/esp_encrypted_img/examples/pre_encrypted_ota/main/tests/test_local_server_ota.c b/esp_encrypted_img/examples/pre_encrypted_ota/main/tests/test_local_server_ota.c index 3c96ac8c36..ce9653e15a 100644 --- a/esp_encrypted_img/examples/pre_encrypted_ota/main/tests/test_local_server_ota.c +++ b/esp_encrypted_img/examples/pre_encrypted_ota/main/tests/test_local_server_ota.c @@ -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 @@ -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; } diff --git a/esp_encrypted_img/examples/pre_encrypted_ota/pytest_pre_encrypted_ota.py b/esp_encrypted_img/examples/pre_encrypted_ota/pytest_pre_encrypted_ota.py index e20a785e07..74bf47fbd1 100644 --- a/esp_encrypted_img/examples/pre_encrypted_ota/pytest_pre_encrypted_ota.py +++ b/esp_encrypted_img/examples/pre_encrypted_ota/pytest_pre_encrypted_ota.py @@ -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 @@ -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) @@ -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 + 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)