Harden SSH Admission And Credential Input
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user