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.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# Production web-security host regression
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```sh
|
||||
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.
|
||||
Reference in New Issue
Block a user