#!/usr/bin/env python3 """Compile unchanged production security implementation with real host mbedTLS.""" from pathlib import Path import subprocess import re import tempfile ROOT = Path(__file__).resolve().parents[2] HEADERS = { "esp_err.h": """#pragma once typedef int esp_err_t; #define ESP_OK 0 #define ESP_FAIL -1 #define ESP_ERR_INVALID_ARG 1 #define ESP_ERR_INVALID_STATE 2 #define ESP_ERR_INVALID_SIZE 3 #define ESP_ERR_INVALID_VERSION 4 #define ESP_ERR_INVALID_RESPONSE 5 #define ESP_ERR_NO_MEM 6 #define ESP_ERR_TIMEOUT 7 """, "esp_mac.h": """#pragma once #include #include "esp_err.h" #define ESP_MAC_WIFI_SOFTAP 1 esp_err_t esp_read_mac(uint8_t *, int); """, "freertos/FreeRTOS.h": """#pragma once #define portMAX_DELAY 0xffffffffU #define pdTRUE 1 """, "freertos/semphr.h": """#pragma once typedef void *SemaphoreHandle_t; SemaphoreHandle_t xSemaphoreCreateMutex(void); int xSemaphoreTake(SemaphoreHandle_t, unsigned); int xSemaphoreGive(SemaphoreHandle_t); """, "nvs.h": """#pragma once #include #include "esp_err.h" typedef int nvs_handle_t; #define NVS_READONLY 0 #define NVS_READWRITE 1 #define ESP_ERR_NVS_NOT_FOUND 10 #define ESP_ERR_NVS_TYPE_MISMATCH 11 #define ESP_ERR_NVS_INVALID_LENGTH 12 esp_err_t nvs_open(const char *, int, nvs_handle_t *); esp_err_t nvs_get_blob(nvs_handle_t, const char *, void *, size_t *); esp_err_t nvs_set_blob(nvs_handle_t, const char *, const void *, size_t); esp_err_t nvs_commit(nvs_handle_t); void nvs_close(nvs_handle_t); """, } with tempfile.TemporaryDirectory(prefix="web-security-") as directory: out = Path(directory) for name, text in HEADERS.items(): path = out / name path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text) executable = out / "security" subprocess.run([ "cc", "-std=c11", "-Wall", "-Wextra", "-Werror", "-O1", "-g", "-I", str(out), "-I", str(ROOT / "src"), str(ROOT / "tests/web_security/security.c"), "-lmbedx509", "-lmbedcrypto", "-o", str(executable), ], check=True) subprocess.run([str(executable)], check=True) symbols = subprocess.check_output(["nm", "-g", str(executable)], text=True) assert "web_security_show_credentials" not in symbols assert "web_security_rotate_credentials" not in symbols assert set(re.findall(r" T (web_security_\w+)$", symbols, re.MULTILINE)) == { "web_security_init", "web_security_copy_tls_material", "web_security_get_certificate_metadata", "web_security_rotate_certificate", "web_security_reset_all", "web_security_get_identity_snapshot", "web_security_reserve_identity", "web_security_replace_reserved", "web_security_release_identity", } header = (ROOT / "src/web_security.h").read_text() assert "web_security_credentials_t" not in header assert "WEB_SECURITY_PASSWORD" not in header assert "WEB_SECURITY_USERNAME" not in header console = (ROOT / "src/web_console.c").read_text() for forbidden in ('"credentials"', "web credentials", "user_database_sync_legacy", "synchronize_migrated", "Password:"): assert forbidden not in console, forbidden assert "web_server_replace_identity(0, 0, reset, &committed)" in console assert set(re.findall(r"\b(user_database_\w+)\s*\(", console)) == { "user_database_get_snapshot", } print("PASS exact public/owner API and legacy credential/console DB-mutation absence")