Add Authenticated HTTPS Admin Foundation
This commit is contained in:
@@ -12,7 +12,7 @@ Universal wireless serial adaptor firmware for the ESP32-S3.
|
||||
- 8 MB octal PSRAM
|
||||
- Adafruit MAX3243 full-pinout RS-232 breakout, product 5988
|
||||
|
||||
The firmware has completed **Phase 0 hardware characterization**, the **Phase 1 serial-core foundation**, the **Phase 2 transport-neutral session broker**, and the first real broker transport through native USB CDC-ACM. The current phase adds persistent multi-profile Wi-Fi station configuration and managed AP fallback/AP+STA operation. The MAX3243 diagnostics and recovery consoles remain available. No electrical test starts automatically; UART1 starts when requested explicitly or when a host opens native USB CDC.
|
||||
The firmware has completed **Phase 0 hardware characterization**, the **Phase 1 serial-core foundation**, the **Phase 2 transport-neutral session broker**, native USB CDC-ACM, and the **Phase 4 Wi-Fi foundation**. The current Phase 5A foundation adds authenticated HTTPS with a persistent device-specific identity and physical-console recovery. WebSocket/xterm.js serial transport is deliberately deferred to the next web phase. The MAX3243 diagnostics and recovery consoles remain available. No electrical test starts automatically; UART1 starts when requested explicitly or when a host opens native USB CDC.
|
||||
|
||||
## Hardware wiring
|
||||
|
||||
@@ -24,14 +24,14 @@ The N16R8 target has 16 MiB flash and 8 MiB octal PSRAM. PlatformIO uses the cus
|
||||
|
||||
| Partition | Offset | Size | Purpose |
|
||||
|---|---:|---:|---|
|
||||
| `nvs` | `0x009000` | 512 KiB | Serial configuration and future Wi-Fi/provisioning data |
|
||||
| `nvs` | `0x009000` | 512 KiB | Serial, Wi-Fi, HTTPS credential, and certificate/key data |
|
||||
| `otadata` | `0x089000` | 8 KiB | Active OTA-slot selection metadata |
|
||||
| `phy_init` | `0x08B000` | 4 KiB | Optional PHY initialization data |
|
||||
| `nvs_key` | `0x08C000` | 4 KiB | Reserved for future encrypted-NVS keys |
|
||||
| `coredump` | `0x08D000` | 128 KiB | Reserved for flash core dumps |
|
||||
| `ota_0` | `0x0B0000` | 4 MiB | Primary application/OTA slot |
|
||||
| `ota_1` | `0x4B0000` | 4 MiB | Alternate application/OTA slot |
|
||||
| `storage` | `0x8B0000` | 7488 KiB | Future LittleFS web assets, certificates, logs, and files |
|
||||
| `storage` | `0x8B0000` | 7488 KiB | Future LittleFS web assets, logs, and files |
|
||||
|
||||
Application offsets are aligned to the ESP32-S3's required 64 KiB boundary. The final storage partition ends at `0x1000000`, exactly the end of the 16 MiB flash chip.
|
||||
|
||||
@@ -68,7 +68,7 @@ pio device monitor -b 115200
|
||||
|
||||
The firmware starts an interactive console on UART0 with the prompt `serial-tool>`. Type `help` to display concise root-command descriptions. This USB-to-UART device normally appears as `/dev/ttyUSB*`; it is separate from the native USB CDC serial transport described below.
|
||||
|
||||
The console provides line editing, history for the current session, terminal-aware cursor movement, and Tab completion. ESP-IDF supplies completion for root commands; the project extends it to nested `debug`, `serial`, `broker`, `usb`, and `wifi` subcommands plus safe fixed values such as AP policy and serial framing. Password values are intentionally never completion candidates.
|
||||
The console provides line editing, history for the current session, terminal-aware cursor movement, and Tab completion. ESP-IDF supplies completion for root commands; the project extends it to nested `debug`, `serial`, `broker`, `usb`, `wifi`, and `web` subcommands plus safe fixed values such as AP policy and serial framing. Password values are intentionally never completion candidates.
|
||||
|
||||
ESP-IDF normally probes terminal cursor support once while constructing the UART REPL. If the board boots without a terminal attached, that probe times out and selects dumb mode. The DevKit's USB-to-UART bridge exposes no host-open signal that firmware can monitor, and entering enhanced mode before a terminal exists would block linenoise while it waits for a cursor-position response.
|
||||
|
||||
@@ -208,7 +208,7 @@ Power down and remove the DE-9 pin 3-to-2 jumper before connecting an external s
|
||||
|
||||
### Wi-Fi foundation
|
||||
|
||||
Wi-Fi is managed independently of the serial-session broker in this phase. It provides network connectivity and recovery access-point policy for the later HTTPS/WebSocket transport, but it does not yet run HTTP, HTTPS, DNS interception, a captive portal, NAPT, or any TCP serial listener.
|
||||
Wi-Fi is managed independently of the serial-session broker. It provides network connectivity and recovery access-point policy for the HTTPS service. The firmware does not run plaintext HTTP, DNS interception, a captive portal, NAPT, or any TCP serial listener.
|
||||
|
||||
Configuration uses four fixed station-profile slots. Lower numeric priority values are tried first, with slot number breaking ties. Profiles support WPA2/WPA3 mixed operation or require WPA3-SAE. ESP-IDF's station threshold can express “WPA2 or stronger” but not a strict WPA2-only maximum, so the configuration does not pretend to offer a distinct WPA2-only mode. Each profile attempt has a 12-second association/DHCP deadline. After all enabled profiles fail, the manager uses exponential retry delays from 2 to 60 seconds.
|
||||
|
||||
@@ -264,7 +264,7 @@ Wi-Fi credentials currently reside as plaintext in the application-owned `wifi_a
|
||||
#### Wi-Fi validation
|
||||
|
||||
1. Boot with no station profiles. `wifi status` should report `ap-only`, and the generated SSID should be visible from another device.
|
||||
2. Use `wifi ap show-secret`, join the AP, confirm a `192.168.4.x` lease, and run `wifi ping 192.168.4.1`. No web page is expected yet.
|
||||
2. Use `wifi ap show-secret`, join the AP, confirm a `192.168.4.x` lease, and run `wifi ping 192.168.4.1`. The authenticated HTTPS page should be reachable at `https://192.168.4.1/`.
|
||||
3. Configure and enable a WPA2/WPA3 station profile using the example above. `wifi status` should progress through `connecting`, `waiting-ip`, and `online` and display the acquired address, channel, RSSI, and negotiated authentication.
|
||||
4. Reboot and verify profile and AP credential persistence.
|
||||
5. Configure two profiles with different priorities, make the first unavailable, and verify failover to the second after its timeout.
|
||||
@@ -273,6 +273,69 @@ Wi-Fi credentials currently reside as plaintext in the application-owned `wifi_a
|
||||
8. Test `wifi stop`, `wifi start`, and `wifi reconnect` while confirming UART0 and native USB serial operation remain unaffected.
|
||||
9. If available, test a WPA3-only profile and a wrong password, then inspect the disconnect reason and counters.
|
||||
|
||||
### Phase 5A authenticated HTTPS foundation
|
||||
|
||||
One ESP-IDF HTTPS server listens on TCP port 443 across whichever AP and station interfaces are active. There is no plaintext port 80 listener. This slice intentionally exposes only:
|
||||
|
||||
```text
|
||||
GET /
|
||||
GET /api/status
|
||||
```
|
||||
|
||||
Both endpoints require HTTP Basic authentication over TLS. `/` is a small self-contained status landing page; `/api/status` returns JSON containing uptime plus non-secret Wi-Fi, serial-service, broker, native-USB, and HTTPS state/counters. No WebSocket, xterm.js terminal, or web broker client exists in Phase 5A.
|
||||
|
||||
On first boot, the device generates and persists:
|
||||
|
||||
- username `admin` and a random 24-character Base64URL-safe password;
|
||||
- an ECDSA P-256 private key;
|
||||
- a device-specific self-signed SHA-256 certificate valid from 2025-01-01 through 2049-12-31;
|
||||
- certificate SANs for `192.168.4.1` and a MAC-suffixed name such as `esp32-sak-a1b2c3.local`.
|
||||
|
||||
The certificate and credentials remain stable across ordinary reboot and OTA-slot changes until explicitly rotated. The `.local` name is included for future hostname discovery, but this phase does not yet advertise mDNS; use the AP address or the station address reported by `wifi status`.
|
||||
|
||||
The physical UART0 administration console provides:
|
||||
|
||||
```text
|
||||
web
|
||||
web help
|
||||
web status
|
||||
web start|stop
|
||||
web counters|clear-counters
|
||||
web credentials show
|
||||
web credentials rotate --force
|
||||
web certificate info
|
||||
web certificate rotate --force
|
||||
web reset --force
|
||||
```
|
||||
|
||||
`web` and `web help` print the same usage summary. `web credentials show` is the intended first-boot credential-retrieval path. Rotation and reset operations write NVS immediately rather than creating RAM-only secrets. Destructive operations require a literal `--force`; `web reset --force` is also the recovery path for an incompatible or damaged `web_sec/material` blob and starts HTTPS with the recovered material. Invalid stored material is never overwritten automatically, and HTTPS failure never disables UART0, native USB, the serial core, or Wi-Fi recovery.
|
||||
|
||||
The self-signed certificate is not trusted by browsers or host tools by default. After retrieving the password, validate from a host connected to the fallback AP with:
|
||||
|
||||
```sh
|
||||
curl -k -u 'admin:YOUR_24_CHARACTER_PASSWORD' https://192.168.4.1/
|
||||
curl -k -u 'admin:YOUR_24_CHARACTER_PASSWORD' https://192.168.4.1/api/status
|
||||
```
|
||||
|
||||
A request without credentials should return `401 Unauthorized` and a `WWW-Authenticate` challenge:
|
||||
|
||||
```sh
|
||||
curl -k -i https://192.168.4.1/api/status
|
||||
```
|
||||
|
||||
Inspect and compare the live certificate with `web certificate info`:
|
||||
|
||||
```sh
|
||||
openssl s_client -connect 192.168.4.1:443 -servername esp32-sak-device.local </dev/null 2>/dev/null \
|
||||
| openssl x509 -noout -subject -issuer -dates -fingerprint -sha256
|
||||
```
|
||||
|
||||
Replace the example SNI name with the DNS SAN printed by `web certificate info`. SNI is not required for this single-certificate server, but supplying the device name makes the test representative of future hostname use. Repeat the fingerprint check after reboot to confirm persistence, then optionally test each explicit rotation command and verify that only the requested material changes.
|
||||
|
||||
HTTPS is memory-bounded to two simultaneous client sockets; ESP-IDF documents approximately 40 KiB per TLS socket. Basic authentication is acceptable here only because plaintext HTTP is disabled. It is an initial administration mechanism, not the final authorization design.
|
||||
|
||||
**Current security limitation:** the web password and ECDSA private key are stored as plaintext in the application-owned `web_sec/material` NVS blob, just as Wi-Fi credentials are currently plaintext in `wifi_app/config`. The reserved `nvs_key` partition does not activate NVS encryption. ESP-IDF 5.5 also keeps an internal heap copy of the active TLS private key and does not guarantee zeroization when that allocation is freed. Do not treat the current firmware as resistant to physical flash or RAM extraction; NVS encryption, flash encryption, secure boot, protected OTA, secret-aware core-dump handling, and framework-level key zeroization belong to the later hardening phase.
|
||||
|
||||
### Phase 0 diagnostics
|
||||
|
||||
The top-level `status` command retains quick MAX3243 signal-state inspection. Potentially disruptive hardware-characterization operations are grouped below `debug` so the primary help page stays concise:
|
||||
|
||||
Reference in New Issue
Block a user