Decouple user provisioning from HTTPS identity storage while retaining compatible v1 user records and migrating TLS material to the credential-free v2 format. Add focused security regression coverage and update operator documentation.
86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
#!/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
|
|
""",
|
|
"esp_mac.h": """#pragma once
|
|
#include <stdint.h>
|
|
#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
|
|
""",
|
|
"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 <stddef.h>
|
|
#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",
|
|
}
|
|
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_security_reset_all()" in console
|
|
assert set(re.findall(r"\b(user_database_\w+)\s*\(", console)) == {
|
|
"user_database_get_snapshot",
|
|
}
|
|
print("PASS exact five-function API and legacy credential/console DB-mutation absence")
|