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.
5.8 KiB
Pinned wolfSSH authentication control-flow contract
Run from the repository root:
CCACHE_DISABLE=1 python3 tests/wolfssh_auth_contract/run.py
Requires Python 3, a host C compiler (CC, default cc), installed managed
wolfSSH, and an existing firmware compilation database/toolchain. No packages
are downloaded and no firmware build or device commands run. Generated C and
the executable live in a temporary directory and are removed on exit. Compile,
preprocess and execution subprocesses have 30/30/10-second limits.
The runner prefers the sole .pio/build/*/compile_commands.json, otherwise the
root database. Select another existing database with --compile-commands PATH.
It requires the actual generated wolfSSH internal.c compilation input to equal
tools/security_overrides.py's rendering of the pinned original, preprocesses
that compile command (-E -dM), and checks this reviewed profile:
LIBWOLFSSH_VERSION_HEX == 0x01004020(1.4.20).- RSA disabled; ECDSA and Ed25519 not disabled.
- Certificates,
noneauthentication andNO_FAILURE_ON_REJECTEDnot defined.
For hosts without the ESP compiler/database, explicitly use --host-only.
This prints a SKIP for production feature verification; it still checks the
source/version/pin and executes the rendered host contract with the reviewed
feature profile. A stale compilation database is not proof of the next firmware build's
configuration.
What executes
run.py checks the exact application wolfSSH pin, installed version header and
reviewed SHA-256 of managed_components/wolfssl__wolfssh/src/internal.c:
81ff1f9166708abd5c2911e9fe57c0aee01c88b5d3f68c909ee8a856d37f36a9
Any same-version source change fails before compilation. Re-audit before updating this hash; do not automatically bless a dependency update.
The runner extracts actual overridden production function definitions by balancing braces after masking comments/string literals. Extraction does not rewrite their bodies; the separately verified build overlay does:
GetBoolean,GetUint32,GetSize,GetStringRefDoUserAuthRequestPassword,DoUserAuthRequestPublicKey,DoUserAuthRequestSendUserAuthKeyboardRequest,GetAllowedAuthSendChannelData
Callback data structures, auth result constants and method masks are extracted
from the installed public header. contract.c supplies small session/context
models, name/algorithm lookup, crypto and packet-output doubles. Binary request
fixtures execute the extracted parsers; ordered event traces assert callback,
hashing/signature and response order, rather than inspecting source substrings.
The 35 baseline cases cover (with the stricter malformed-password contract):
- Ed25519 and ECDSA: signed authorization rejection (
INVALID_PUBLICKEY,FAILURE,REJECTED,INVALID_USER,INVALID_AUTHTYPE) never hashes, verifies or calls the result callback. - Both unsigned probe outcomes: no signature work/result callback; an accepted probe sends PK_OK but does not complete authentication.
- Bad signatures, good signatures, success-result veto, ignored failure-result
callback return, and auth
WOULD_BLOCK. - Password success/failure, rejected password change, and rejection before the callback for a truncated new-password-length field. No password result callback.
- Disabled
none, unknown methods/key algorithms and truncated signed framing. - Direct keyboard-interactive dispatch invokes a registered non-NULL rejecting prompt callback, returns error and purges without preparing/building/sending a prompt. The actual library still writes the message-ID byte into its existing output buffer on this path; the test models that buffer and checks this detail.
- The actual advertised-method builder excludes keyboard despite the registered keyboard callback, because the allowed-types callback overrides the defaults.
- Actual
SendChannelDatacopies the bounded consumed prefix before returning a positive count, both on send success andWS_WANT_WRITE. Wiping that caller prefix leaves the library copy intact. A blocked flush of earlier data returns a negative code without consuming new data.
A further 100 generated-parser cases test short/missing flags and lengths,
truncated/oversized/UINT32_MAX password and replacement-password lengths,
checked initial offsets and canaries, no callback on malformed fields, preserved
username/service/method prefixes, and suffix wiping before response emission.
They include success, invalid/backend/rejected outcomes, password changes, no
callback, callback-modified credential pointers/lengths, and pending retry.
WS_AUTH_PENDING deliberately preserves bytes; the project's synchronous
callbacks do not use it. This is not an unconditional async secret-wipe promise.
Limits / ownership
This is a library parser/control-flow regression, not application callback integration coverage. Its rejecting keyboard callback models the parent's registration and return policy; it does not prove production registration, admission counters, awaiting-result state, principal promotion, or admin wiping. Those belong to the separate application unit suite.
Crypto helpers are instrumented doubles; algorithm name lookup is limited to fixture names. Packet construction/network send, hashing and session internals are modeled. This does not validate cryptographic correctness, encrypted packet decoding, real sockets, asynchronous re-entry, complete malformed-input safety, allocation failure, memory erasure throughout wolfSSH, or device behavior. The send test proves only the extracted copy/consumed control flow with successful packet preparation/bundling and the specified send outcomes—not the entire admin transmit loop or TLS/SSH buffer lifecycle.
All Phase9 hardware validation remains deferred to the combined phase.