Files
ESP32_Serial_Swiss_Army_Knife/tests/web_security/README.md
T
Commander1024 ac80863d80 Remove Legacy Credential Bootstrap Paths
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.
2026-09-08 19:09:26 +02:00

5.8 KiB

Production web-security host regression

Run from the repository root:

python3 tests/web_security/run.py

Requires a C11 compiler, nm, Python 3, and installed mbedTLS 3.6 headers plus libmbedx509 / libmbedcrypto. Verified with host mbedTLS 3.6.7. No downloads, firmware build, device access, asset generation or persistent build outputs. The runner creates adapters and binaries in a temporary directory.

security.c includes the entire unchanged production src/web_security.c. No crypto function, generator, validator, decoder or transaction is extracted or replaced. Actual mbedTLS generates P-256 keys and signed certificates and parses, hashes, checks the key pair and verifies the self-signature. The legacy fixture is independently assembled at fixed little-endian byte offsets from a freshly generated real identity, not a production legacy encoder. No real device secret or fixed private key is checked in.

Host adapters provide a fixed device MAC, Linux getrandom, a lock-ownership assertion, and fault-injectable NVS with staged writes/commit. At every write and commit the adapter checks that production live state is still its predecessor. Legacy wipe calls are counted and checked. Production restart is simulated by clearing only module RAM while retaining the adapter's stored record. These adapters do not prove ESP entropy initialization, allocation failure inside mbedTLS, FreeRTOS concurrency, ESP NVS flash/power-loss semantics, TLS handshakes, HTTPD restarts, target stack margins, or secure physical flash erasure. In particular, modeled failed commits retain predecessor storage; real flash fault and power-loss behavior needs target validation. No claim that NVS logical replacement securely erases historical flash pages.

The suite reports 15 production security groups plus one API/console static absence check. Coverage includes fresh and stored-v2 paths, exact v1 migration, metadata/pair-copy bounds, NVS failures and retries, 21 legacy corruptions, 14 v2 corruptions, unknown sizes, real bad signatures with recomputed hashes, mismatched private keys, wrong-device certificates, RNG/MAC/mutex failures, rotation/reset commit-before-publication, unavailable explicit recovery, generation exhaustion and invalid arguments. Console checks establish removal of legacy command/secret/synchronization references, not runtime console lifecycle execution. Read-only database status and login-failure counters are intentionally retained.

Integration/API contract

Five public functions remain:

  • web_security_init(web_security_load_result_t *)
  • web_security_copy_tls_material(...) (unchanged pair-copy API)
  • web_security_get_certificate_metadata(...) (unchanged metadata)
  • web_security_rotate_certificate(void)
  • web_security_reset_all(void) (TLS only, changed signature)

Removed: two credential functions (show_credentials, rotate_credentials), one credential struct type, three username/password capacity/length constants, and two console operations (web credentials show, web credentials rotate). There is no credential generation/display/synchronization path. Authentication continues to belong to the user database; read-only status does not mutate it. The integration owner must remove legacy startup callers in main.c and adapt other console policy/completion/UI/test callers outside this ownership scope.

Load results retain STORED=0, GENERATED_MISSING=1, and add MIGRATED_V1=2. Repeated successful init returns the remembered result without reloading. Migration must validate and commit before publication; no fallback generation or overwrite follows migration failure. Reset explicitly overwrites missing, valid, or incompatible material, increments a live generation or uses one when no live identity exists, and fails on live generation exhaustion. Rotation requires live material and also fails at UINT32_MAX.

web reset --force retains the old lifecycle: commit first; when running, stop then start, with no start after failed stop; otherwise attempt start. Lifecycle failure does not roll back committed identity. Database accounts are never synchronized, reset or otherwise mutated by these operations.

Storage contract

The namespace/key remain web_sec/material, one blob, no new NVS keys. V1 is exactly 1392 bytes, read only through a private fixed-offset decoder. Its credential layout must still match the shipped admin/24-character URL-safe format, zero padding/reserved fields, and strict TLS validation.

V2 is exactly 1340 bytes (52 bytes smaller), native little-endian ESP32 layout with compile-time offset/size assertions:

Offset Size Field
0 4 schema version = 2
4 2 blob size = 1340
6 2 reserved, zero
8 4 nonzero generation
12 2 private key DER length
14 2 certificate DER length
16 256 private key DER, unused bytes zero
272 1024 certificate DER, unused bytes zero
1296 32 certificate SHA-256 fingerprint
1328 12 reserved, zero

Migration preserves exact DER bytes, fingerprint and generation, including UINT32_MAX; it never regenerates TLS identity. TLS validation includes exact outer DER lengths, P-256 pair consistency, self-signature, fingerprint, device CN/SAN, validity and existing certificate extension policy. Unknown sizes or schema versions fail with ESP_ERR_INVALID_VERSION; malformed known records fail with ESP_ERR_INVALID_RESPONSE (underlying operational failures propagate). Every legacy input buffer is wiped on all post-read exits. The live blob has no credential fields. No new task, queue, mutex, heap allocation or storage key is introduced; the existing mutex remains. Migration adds a bounded 1392-byte transient decoder buffer alongside the 1340-byte candidate; target call-stack high-water usage is unmeasured.