Harden SSH Admission And Credential Input

This commit is contained in:
2026-09-15 20:49:04 +02:00
parent 436c27adb1
commit 751dfb9ddb
32 changed files with 1751 additions and 39 deletions
+96
View File
@@ -0,0 +1,96 @@
# Pinned wolfSSH authentication control-flow contract
Run from the repository root:
```sh
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 preprocesses the actual wolfSSH `internal.c` compile command (`-E -dM`) and
checks this reviewed profile:
- `LIBWOLFSSH_VERSION_HEX == 0x01004020` (1.4.20).
- RSA disabled; ECDSA and Ed25519 not disabled.
- Certificates, `none` authentication and `NO_FAILURE_ON_REJECTED` not 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 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`:
```text
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 function definitions by balancing braces after
masking comments/string literals. It does not rewrite their bodies:
- `GetBoolean`, `GetUint32`, `GetSize`, `GetStringRef`
- `DoUserAuthRequestPassword`, `DoUserAuthRequestPublicKey`, `DoUserAuthRequest`
- `SendUserAuthKeyboardRequest`, `GetAllowedAuth`
- `SendChannelData`
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 cases cover:
- 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 the installed parser's
callback on 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 `SendChannelData` copies the bounded consumed prefix before returning a
positive count, both on send success and `WS_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.
## 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.