Files
ESP32_Serial_Swiss_Army_Knife/docs/security_hardening.md
T
Commander1024 436c27adb1 Enforce crash-safe build policy
Add compile-time checks for silent reboot, disabled core dumps, and
disabled debugger-aware panic handling. Include regression coverage,
hardening guidance, and update Phase 8/9 project status.
2026-09-15 20:11:40 +02:00

8.6 KiB

Security hardening — Phase 9

Status: in progress. Phase 8 is complete at the accepted 8D.22 scope. Phase 9 starts with 9A crash/debug build policy; host matrix and firmware build validation passed on 2026-09-15, and target-hardware validation has not run. This document records policy and procedures, not unrun passes or production certification.

Scope and threat model

Reduce network abuse, accidental diagnostic disclosure and unnecessary secret retention while preserving one UART1 broker writer, isolated observers and binary transparency. UART0 remains trusted physical administration/recovery; native USB remains network-independent UART1 access, not an admin console. Whole-device reboot interrupts every transport.

Secure boot and encrypted NVS are explicitly excluded by user preference. Physical flash/RAM extraction and firmware replacement remain outside the threat model even after Phase 9. There is no commitment to flash/PSRAM encryption, eFuse provisioning or physical JTAG restrictions. Software debugger-aware configuration checks do not disable physical debug access by fuse.

9A does not change partitions, at-rest encryption, dependencies or generated assets, and requires no upload or erase as part of host/build validation. The unused nvs_key and coredump partitions remain for layout compatibility. Disabling new dumps does not clear old coredump contents. Logical NVS replacement, reset and credential rotation are not secure erasure; historical plaintext copies can remain.

9A supported build baseline

src/security_build_policy.c enforces the following at compile time, with explicit settings in sdkconfig.defaults:

  • Require CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y and CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=y.
  • Reject enabled core-dump support, flash or UART dump destinations.
  • Reject panic print/reboot, print/halt and GDBstub modes, plus runtime GDBstub.
  • Reject CONFIG_ESP_DEBUG_OCDAWARE and CONFIG_FREERTOS_DEBUG_OCDAWARE when enabled.

The host matrix in tests/security_build_policy/run.py compiles the actual guard against synthetic configurations; it is not merely a text check of defaults. Existing generated SDK configuration must also satisfy the guard: defaults alone are not evidence of the effective build configuration.

Silent panic reboot deliberately sacrifices panic text, register dumps and backtraces for reduced crash disclosure. Reset-reason/boot information and ordinary status/logging can remain; neither silence across the full boot sequence nor general log redaction is guaranteed. A monitor exception decoder cannot reconstruct a backtrace that was never emitted.

Operational profiles

These are handling and validation profiles of the same supported build baseline, not separate PlatformIO environments or selectable security overrides.

Profile Operational rules
Development Keep the guard enabled; use synthetic credentials and controlled serial payloads for fault investigation. Keep UART0 recovery available. Review captures before sharing.
Test Use an isolated, expendable target and synthetic secrets; record exact source/configuration, host/build results and device observations. Exercise crashes and recovery without exporting raw memory.
Production Use the same guard, restrict physical/network access, verify device identity through trusted UART0, and apply reviewed provisioning/rotation/recovery procedures. Readiness remains pending Phase 9 review and target evidence.

If richer crash debugging is essential, use an isolated synthetic-secret build outside this supported baseline. It requires explicit reviewed changes to the source policy and applicable configuration; no bypass flag is provided. Do not use real credentials or deploy that build as production firmware. Restore and revalidate the supported policy before release.

Raw flash, RAM and dumps can contain Wi-Fi passwords, private keys, password verifiers, session material and serial payloads. Treat them as secret-bearing and do not export them as routine diagnostics. Prefer bounded status/counter observations and reviewed synthetic-secret reproductions. Restrict any exceptional artifacts and define retention/deletion before collecting them; deletion is not a secure-erase guarantee.

Validation gates

Host and build — passed 2026-09-15

From the repository root:

python3 tests/security_build_policy/run.py
pio run
python3 tests/security_build_policy/run.py --sdkconfig-header .pio/build/esp32-s3-devkitc-1-n16r8/config/sdkconfig.h

Validation: 17 synthetic/default compile cases passed; the generated SDK-header check passed as the eighteenth case. pio run passed with 94,196 B linked RAM and 1,828,565 B flash. The initial host invocation was blocked by the compiler wrapper's read-only cache location; rerunning with CCACHE_DISABLE=1 passed. Independent policy review and git diff --check passed. No upload, erase, eFuse operation or target test was performed.

Record the revision, compiler/build outcome and effective configuration. Confirm that the matrix accepts the supported configuration, rejects each prohibited option independently, and rejects absent/disabled required settings. Confirm the normal firmware build compiles the guard. A rejected unsafe configuration is an expected negative-test result, not a firmware build pass. Neither these commands nor a successful build proves target panic behavior.

Target hardware — not run; required for 9A acceptance

  1. On an isolated synthetic-secret target, record the tested image/configuration and capture UART0 at 115200 baud. Verify normal boot, UART0 administration, native USB UART1 access, HTTPS and SSH before fault testing.
  2. Through separately reviewed test-only fault injection, trigger a controlled panic with the supported build policy intact. Verify reboot rather than halt/debugger wait, no panic register/backtrace output and no UART core dump. Record any remaining boot/reset information; do not promise complete UART silence.
  3. Verify no new flash core dump is written using a reviewed target-side pass/fail check that does not export partition contents. Distinguish old partition contents from a new write; do not erase the partition merely to claim this test passed.
  4. After reboot, verify UART0 recovery and USB serial access, then authenticated HTTPS/SSH and broker writer/observer behavior. With network services unavailable, verify UART0 and native USB still work. Review routine status/log output using synthetic secrets; this is bounded evidence, not universal redaction proof.
  5. Record outcomes and limitations before marking 9A complete. Device flashing/fault injection requires a separately authorized hardware session; no eFuse changes, partition migration or erase is required by this policy.

Staged next work

  • Next: bounded SSH cross-connection throttling. src/ssh_transport.c currently closes after three counted failed authentication attempts in a session; slot reset/reconnect resets the budget. Not every protocol message is counted (for example, an unsigned public-key probe is not a failed signed authentication). src/web_cookie_auth.c already limits credential verifications to five per 60-second fixed global window, shared across clients; this is not a sliding window or persistent account lockout, and auth lifecycle restart resets its state. Define reconnect-resistant SSH admission/backoff, bounded state and secret-free counters without blocking established sessions, UART0 or USB.
  • Later: zeroization and protocol policy. Audit secret lifetimes and failure cleanup across application, mbedTLS, wolfSSL and wolfSSH; review crypto algorithms, certificate trust, browser security headers and password policy. Existing wipes are not proof that every library/stack/PSRAM copy is cleared.
  • Later: maintenance and lifecycle. Review ESP-IDF/wolfSSL/wolfSSH advisories and dependency licenses, then plan any upgrades separately. Complete provisioning, key rotation, backup, factory reset, recovery and decommissioning runbooks without claiming physical-extraction resistance or secure erasure.
  • Phase 10: OTA trust. Define independent image-signature verification, trust-anchor provisioning, rotation/revocation, rollback/downgrade and recovery policy without secure boot. Authenticated transport alone is not image-signing policy, and OTA signature checks cannot prevent physical firmware replacement.

See the roadmap, electrical procedures and administration regressions for wider gates. Production readiness remains pending; Phase 8 acceptance is not reopened by these follow-ups.