phase: 02_tls_control_channel
**Verified / implemented (phase 02 final pass)** - All 6 tasks already in `complete/` → verification pass. Device measurement **found a real defect (README bug 11)**: the cert carried its SAN *value* under the OID written from the ASCII text `"2.5.29.17"` instead of DER content bytes `55 1D 11` (`openssl x509 -text` showed `1.10.46.53…`, hex `30 2f 06 09 32 2e 35…`), i.e. **no SAN** → hostname validation impossible while every host check stayed green (they walked the value, never the label). - Fixed in the pure module: `kSanOidTlv`/`sanOidDer()`, `sanExtensionFullDer()` (pinned to the 43 bytes OpenSSL writes for these names), `derCarriesSan()` (OID *and* value); `tls.cpp` uses them + 3 `static_assert`s; `cert` now warns and names the remedy. 33 new checks in `test_tls_pem.cpp`, incl. the pre-fix extension byte-for-byte (`derContains` yes / `derCarriesSan` no). - Corrected a false finding: the earlier "fingerprint rotated once" was an **OpenSSL `-ext` artifact** (`C8:76…` vs the real `61:54:19:30…`, same DER). Re-measured: serial `cert` == live handshake DER hash; page 51 626 B, `sha256 dd16e195…f04e9` == locally assembled page. **Tests / lint (100 % green)** - `bash .agents/validate.sh` → **GATE_EXIT=0**: `RESULT: all suites passed` in the plain *and* `SANITIZE=1` run, **4826 checks / 0 failures** each; `bridge`/`console`/`rollback` **SUCCESS**; `page bytes: 51626`; `README/AGENTS claims: OK`; `repo hygiene: OK (71 tracked files)`. - Footprint: bridge **RAM 30.4 % (99 616 B) / Flash 39.6 % (1 246 145 B)** vs baseline 26.7 %/87 592 & 36.1 %/1 135 293 → **+12 024 B RAM / +110 852 B flash**; TLS alone +6 160 B / +100 008 B (rollback image byte-identical); session heap 187 936 → ~145.5 k (1 session) → ~107.9 k (2). - `ruff check --select F,E9 tools/ tests/` → All checks passed; `bash -n` clean (gate, runner, probe); `-Wall -Wextra` warning-free. **Completion criteria**: 1 ✓ (gate green + RAM/Flash + session heap quoted); 2 ✓ as recorded (boot prints no fingerprint on the NVS-load path, `cert` prints on demand, identity survived reflashes, nothing about the key is printed anywhere — `nvs erase`→first-boot leg **not re-run**); 3 ✓ re-measured (51 626 == gate; `:80` → 301, no page bytes); 4 partial — 426/401/404 + pinning handshake measured, but arming/typing needs the operator's passphrase (`dflt:0`) and the target is this machine, so no input was sent; 5 ✓ (no `.pem/.key/.crt` tracked or on disk; hygiene block ran); 6 ✓ (`[env:rollback]` built every run + README section); 7 ✓. **Documentation**: README — check count 4793→4826 (intro + Quick start), `test_tls_pem` 829→862 with new pins, footprint/TLS-cost rows re-measured, "Ten bugs"→"Eleven bugs" + new entry + both anchors, `cert` row, Troubleshooting names-warning row, `tls_pem.h` architecture entry, nine/ten pure-module wording. AGENTS.md (staleness only): "Ten bugs"→"Eleven bugs" ×2, 4826, and the certificate-names row extended with the DER-OID rule. `.agents/HW_VERIFICATION.md` — 426 body/headers re-measured, fingerprint row correction + `-ext` rule, new SAN row (`failed → fixed in tree, device leg needs nvs erase`) with closing commands; phase report written to `.agents/reports/02_tls_control_channel/02_tls_control_channel__00_phase.a1.md`. **Deviations / live-infra**: made **no** reflash and **no** `nvs erase` (erase also destroys the token and Wi-Fi credentials; flashing puts a live HID keyboard into ROM download mode on this machine). Live steps were read-only `curl`/`openssl` plus one capped `console.py cert` (opening the CDC resets the board — permitted, recorded). No phase/PLAN files touched; everything left in the working tree for the harness to commit. **Next pending phase**: `03_session_takeover` — `01_pending_claim_socket.md`.
This commit is contained in:
+27
-1
@@ -24,7 +24,8 @@
|
||||
# actual command — never inferred from these lines.
|
||||
#
|
||||
# One line per fact, prefix "hw-probe", so the output stays greppable:
|
||||
# serial nodes / keyrelay.local resolution / https / http(+redirect) /
|
||||
# serial nodes / keyrelay.local resolution / https / tls handshake (phase 02:
|
||||
# protocol + cipher via openssl s_client) / http(+redirect) /
|
||||
# uv / node / watch_target.py-capable evdev nodes.
|
||||
set -uo pipefail
|
||||
|
||||
@@ -104,6 +105,31 @@ else
|
||||
echo "hw-probe http skipped — curl not installed; https/http reachability unknown"
|
||||
fi
|
||||
|
||||
# --- 4b. the TLS handshake's details (phase 02 task 03's informational probe) -----
|
||||
# The phase asks for `openssl s_client -connect <ip>:443 -servername
|
||||
# keyrelay.local` reporting protocol and cipher. Informational like everything
|
||||
# else here: one attempt, capped, exit status ignored, no verdict. The verify
|
||||
# failure it will print (self-signed, pinned-once identity) is the EXPECTED shape
|
||||
# of a healthy keyrelay handshake — whether to trust that certificate is a human
|
||||
# decision made against the serial console's fingerprint, never this line's.
|
||||
if have openssl; then
|
||||
tls_host="keyrelay.local"
|
||||
[[ -n "${mdns_out:-}" ]] && tls_host="${mdns_out%%[[:space:]]*}"
|
||||
tls_rc=0
|
||||
tls_fact="$(to 5 openssl s_client -connect "${tls_host}:443" -servername keyrelay.local </dev/null 2>&1)" || tls_rc=$?
|
||||
tls_proto="$(printf '%s\n' "$tls_fact" | sed -n 's/^[[:space:]]*Protocol[[:space:]]*:[[:space:]]*//p' | head -1)"
|
||||
tls_cipher="$(printf '%s\n' "$tls_fact" | sed -n 's/^[[:space:]]*Cipher[[:space:]]*:[[:space:]]*//p' | head -1)"
|
||||
if ((tls_rc == 125)); then
|
||||
echo "hw-probe tls skipped — 'timeout' is not installed and an uncapped s_client can hang; skipped on principle"
|
||||
elif [[ -n "$tls_cipher" && "$tls_cipher" != "(NONE)" ]]; then
|
||||
echo "hw-probe tls ${tls_host}:443 negotiated ${tls_proto:-TLS?} / ${tls_cipher} (a self-signed verify failure is the expected shape; the fingerprint to compare it against is on the serial console)"
|
||||
else
|
||||
echo "hw-probe tls no TLS session on ${tls_host}:443 (rc $tls_rc) — expected until phase 02's listener is flashed; 'unknown ca' ABOVE would mean the session DID complete"
|
||||
fi
|
||||
else
|
||||
echo "hw-probe tls skipped — openssl not installed; protocol/cipher state unknown"
|
||||
fi
|
||||
|
||||
# --- 5./6. the two tools the gate and the suites pin ---------------------------
|
||||
if have uv; then
|
||||
echo "hw-probe uv $(command -v uv) — firmware builds available"
|
||||
|
||||
Reference in New Issue
Block a user