Generate exact-hash SDK source overrides without modifying dependencies. Harden SSH allocation and algorithm policy, tighten web authentication cleanup, and add focused host contract tests and documentation.
7.9 KiB
SSH protocol policy contracts
Run from the project root:
CCACHE_DISABLE=1 python3 tests/ssh_protocol_policy/run.py
The runner requires the installed, exactly pinned wolfSSH 1.4.20 source, the
production compilation database, its target compiler, Python 3, and a host C99
compiler (CC, default cc). It never downloads dependencies or invokes a
firmware build. All host headers, extracted functions, and binaries are created
in a temporary directory and removed afterward. Every compiler/preprocessor
invocation is limited to 30 seconds; each host executable to 10 seconds.
CCACHE_DISABLE=1 is also forced for subprocesses.
If multiple build environments exist, select the intended profile explicitly:
CCACHE_DISABLE=1 python3 tests/ssh_protocol_policy/run.py \
--compile-commands .pio/build/esp32-s3-devkitc-1-n16r8/compile_commands.json
There is deliberately no host-only mode that could silently skip resolved
production-feature verification. The database must select exactly one generated
security_overrides/wolfssh_internal/internal.c, whose bytes must equal the
in-memory tools/security_overrides.py render_entry() output. Original vendor
compilation, missing/duplicate entries, and stale generated content fail closed
with a reconfiguration diagnostic. The runner does not regenerate anything.
The helper is syntax-checked against real target headers using that generated
translation unit's compile settings.
Production contract and integration
src/ssh_protocol_policy.{c,h} exports:
int ssh_protocol_policy_apply(WOLFSSH_CTX *context);
It applies permanent, borrowed strings for these five context settings:
| Setter suffix | Exact value |
|---|---|
Kex |
curve25519-sha256,ecdh-sha2-nistp256 |
Key |
ecdsa-sha2-nistp256 |
Cipher |
aes128-gcm@openssh.com,aes256-gcm@openssh.com |
Mac |
hmac-sha2-256 |
KeyAccepted |
ssh-ed25519,ecdsa-sha2-nistp256 |
The helper returns WS_SSH_CTX_NULL_E for NULL and otherwise returns the first
non-success setter result, without subsequent calls or fallback. It does not
allocate, free, or publish a context. A failure can leave earlier settings
applied: the caller must discard the candidate, not use it.
The parent integrated the helper in create_context() after host-key import and
full staging-buffer wipe, before callback registration and s_context
publication. Any policy failure frees the unpublished candidate and returns
ESP_FAIL. context.c now executes the actual extracted function with the real
policy helper to test this boundary. Service startup isolation, owner/task
lifecycle, session creation, and complete restart paths remain outside this
focused harness.
The exact-version guard rejects unreviewed wolfSSH versions. The source/config
checks below independently verify the actual feature profile; setter success
alone does not validate an algorithm list. KeyAccepted controls only the
server-sig-algs advertisement in this vendor version. User-key enrollment and
authorization remain enforced by the existing database/authentication path.
Evidence provided
- Pins the SHA-256 of installed
src/internal.candsrc/ssh.c, the application manifest's exact wolfSSH version, and the resolved compiler version macro. Source changes require re-audit, not blind hash refresh. Independently checks the registered override's original-source hash, renders it in memory, and requires exact equality with the actual generated compiler input. Requires all original algorithm tables/default strings and extracted protocol-function bodies to remain unchanged by the override. Negative database cases reject original/missing/duplicate entries and mismatched render output. - Replays the actual generated vendor compile command without output/dependency-writing flags to resolve feature macros, the name/ID/type map, and conditional enums. All seven distinct policy algorithm names must have their expected compiled IDs and categories. Required RNG/software-crypto and Ed25519 streaming settings must remain present; policy-disabling macros are rejected. Negative map cases demonstrate that missing algorithms fail the checker.
- Compiles the real helper against injected setter doubles. Tests NULL without dispatch, all five exact lists in order, negative and positive non-success return propagation at every step, no later calls/fallback, preservation of unapplied fields, context sentinel survival, and retained string pointers. These remain helper-boundary checks; the separate context harness below tests actual caller cleanup and publication.
- Executes actual extracted
src/ssh_transport.ccreate_context()together with the real policy helper: 15 cases covering identity-copy failure, context-allocation failure, both positive/negative key-import failures, positive/negative setter failures at all five steps, and success. Checks error mapping, no later policy calls/callbacks/publication on failure, exactly one free for allocated failed candidates, no free on success, and publication only after all eight callback registrations. Marks the entire synthetic identity staging buffer, including the unused tail; asserts full-capacity wipe before policy initialization, callbacks, and candidate destruction. Context creation and key import necessarily precede that wipe. Buffer checks occur only while the extracted function's stack frame is live, never after return. - Executes the five actual vendor context setter bodies, confirming null-context errors and their acceptance of invalid, empty, and NULL lists. The helper then overwrites all five with the fixed policy.
- Executes actual vendor
NameToId,IdToName,AlgoListSz,CopyNameList,CopyNameListPlus,BuildNameList,SendKexInit, andSendExtInfobodies. The mapping and enum values come from production preprocessing. The five actualSshInitlist-pointer assignments are checked and reused in the reduced layout. - Independently decodes initial and repeated/rekey KEXINIT plaintext payloads: exact KEX/host-key lists, both cipher directions, both MAC directions, compression/language lists, first-packet flag, reserved field, total bounds, canaries, and saved exchange-hash input. Exact equality excludes CBC, CTR, AES192, extra KEX/MAC entries, or an appended default fallback.
- Decodes the actual
server-sig-algsextension with exactly Ed25519/P-256. - Exercises missing-host-key, packet-preparation, saved-payload allocation, and WANT_WRITE behavior with bounded doubles. Checks no send on early failures and preservation of the exact payload on WANT_WRITE.
- Compiles negative older/newer version cases against the production guard.
Limits and deferred validation
Host context/session layouts are reduced doubles, not vendor ABI replicas. The context harness doubles identity copying, key import, context allocation/free, callback registration, and the wipe primitive. It verifies production call order, wipe extent, cleanup, and publication, not vendor destruction or secure-wipe machine code. Synthetic identity bytes are not actual private-key material. Handshake allocation, packet reservation/wrapping/purging, deterministic cookie RNG, big-endian integer writing, and send are doubles; payload encoders and list setters are extracted vendor code. The fixed packet/storage buffers are 1024 bytes. No private key, signature, KEX arithmetic, encryption, MAC, complete SSH packet framing, peer negotiation, socket, device, or scheduling behavior is executed. Repeated KEXINIT tests serialization on rekey, not an entire rekey exchange. The extension test does not establish user-key enforcement.
Hardware/live-client acceptance remains deferred to combined Phase 9: both key types, explicit rejection of excluded algorithms, real rekey, and mixed transport responsiveness. No negotiated-handshake or target pass is implied.
This work does not change TLS policy, dependencies, vendor files, generated assets, global crypto primitives/settings, NVS encryption, or eFuses.