Harden wolfSSL and wolfSSH validation

Enable validated ECC imports and X25519 all-zero rejection through
PUBLIC build policy. Tighten wolfSSH parser bounds, overflow handling,
and signature framing with guard-page and crypto vector contracts.
This commit is contained in:
2026-09-15 23:54:39 +02:00
parent c010e1a1d5
commit 4d3bb490c9
17 changed files with 1495 additions and 99 deletions
+91 -74
View File
@@ -1,97 +1,114 @@
# Bounded CVE-2025-12888 mitigation
# Bounded wolf crypto compile policy
Scope: project-owned build configuration only, retaining wolfSSL 5.8.2~1 and
wolfSSH 1.4.20 pins and unmodified managed sources. This is not an upstream
upgrade, blanket security clearance, or mitigation of other listed advisories.
Project-owned configuration for pinned wolfSSL 5.8.2~1 / wolfSSH 1.4.20;
no installed vendor edits, dependency upgrades, generated override edits or
blanket security clearance. See the [key-validation review](../../docs/ssh_key_validation_review.md)
for exact source hashes, applicability, upstream guidance and remaining gaps.
## Upstream and installed evidence
## Policy
On 2026-09-15, inspected official
[PR9275 files](https://api.github.com/repos/wolfSSL/wolfssl/pulls/9275/files)
([PR](https://github.com/wolfSSL/wolfssl/pull/9275), head reported by the files
API: `c161cbd9f3fa1247382bb5b6269c7379222cabf5`). Its `settings.h` patch
selects `CURVE25519_SMALL`, `ED25519_SMALL`, `CURVE448_SMALL`, and `ED448_SMALL`
under `__xtensa__`: Xtensa compilers have generated non-constant-time assembly
from the fast C implementation; upstream says the small implementation is not
known to have those issues. This is upstream mitigation guidance, not proof of
constant-time execution on our compiler/device.
Installed `include/user_settings.h` enables X25519 and Ed25519. Installed
`wolfssl/wolfcrypt/settings.h` automatically enables X25519 blinding only for
non-small math; `wolfcrypt/src/curve25519.c` rejects blinding with small math.
`fe_low_mem.c` and `ge_low_mem.c` provide the small implementations and already
have entries in the production compilation database. Small flags change public
key layout/signatures: never mix old library objects with newly compiled callers.
Root `CMakeLists.txt` sets both small flags before component processing, alongside
the existing global crypto controls. `cmake/wolf_crypto_policy.cmake` attaches a
forced-include resolved-settings guard to wolfSSL with PUBLIC propagation to its
consumers, including wolfSSH and application code. The guard rejects missing
algorithms/small flags, incompatible blinding, and future 448 enablement pending
explicit review. No blinding-disable macro or vendor source patch is needed.
RNG callback and software AES/SHA settings remain unchanged.
- Existing root definitions `CURVE25519_SMALL` / `ED25519_SMALL` follow
[PR9275](https://github.com/wolfSSL/wolfssl/pull/9275)'s Xtensa mitigation.
Small math is incompatible with this version's X25519 blinding; do not mix
ABI-sensitive library and consumer settings. Curve448/Ed448 require review.
- `cmake/wolf_crypto_policy.cmake` now PUBLIC-defines
`WOLFSSL_VALIDATE_ECC_IMPORT` and `WOLFSSL_ECDHX_SHARED_NOT_ZERO`, enabling
existing upstream P256 import and X25519 result checks. The root already
includes this module; no root edit is needed.
- The PUBLIC forced-include guard checks resolved settings and rejects missing
requirements and known ECC validator-disabling/hardware-stub configurations.
Existing RNG callback and software AES/SHA controls are unchanged.
## Commands
From the repository root, after the parent regenerates/builds the firmware:
```sh
# Offline host subset; no target compiler/database required:
python3 tests/wolf_crypto_policy/run.py --host-only
# Explicit candidate replay before production reconfiguration:
python3 tests/wolf_crypto_policy/run.py --candidate
# Strict production evidence after the parent reconfigures/builds:
python3 tests/wolf_crypto_policy/run.py
```
Optional explicit database:
Optional database argument:
```sh
python3 tests/wolf_crypto_policy/run.py --compile-commands .pio/build/esp32-s3-devkitc-1-n16r8/compile_commands.json
```
Strict mode requires the actual compile commands to carry the policy guard and
uses their actual compiler, include paths and definitions without adding small
flags. Missing/ambiguous entries, absent policy, wrong architecture, incompatible
macros, compiler errors and failed vectors fail the test. It preprocesses and
syntax-checks ten translation units: Curve25519, Ed25519, fast and small field/group
math, wolfSSH `ssh.c`, generated wolfSSH `internal.c`, application transport and
security. Two additional actual-settings checks remove each small flag and must
fail. It does not modify generated sources or compile databases.
Candidate mode injects all four policy definitions and the guard into saved
commands. It is **not production configuration/build evidence**. Strict mode
injects nothing, and must fail with stale commands lacking the new flags.
No mode runs PlatformIO, regenerates overrides or communicates with a device.
Before the parent reconfigures, explicitly test the candidate using old commands:
## Coverage
```sh
python3 tests/wolf_crypto_policy/run.py --candidate
```
All modes:
This injects the two small flags and the guard and labels its output **CANDIDATE
replay**, not production configuration evidence. It does not run CMake/PlatformIO.
Host-only subset:
- 20 fail-closed guard matrix cases.
- A temporary CMake project includes the actual production policy module and
verifies PUBLIC definitions/guard across wolf library → SSH → app targets.
This is a stand-in graph, not an ESP-IDF build.
- Compile installed small implementations and run RFC7748 X25519 and RFC8032
Ed25519 vectors plus corrupted-signature rejection. Check two nontrivial
low-order X25519 points that pass the vendor public precheck but must fail
shared-secret calculation without copying output; also reject zero/one inputs.
- Compile installed TFM ECC, ASN template, signature and supporting primitives.
Test explicit/inferred P256 import of valid G and rejection of off-curve,
infinity, out-of-range, truncated and wrong-tag inputs; valid ECDH; raw and
DER-wrapper ECDSA valid/invalid verification; valid SEC1 private DER decoding
without a pre-attached RNG and rejection of an invalid embedded public point.
Test scalar/nonce values are deliberately public test values, never real keys.
```sh
python3 tests/wolf_crypto_policy/run.py --host-only
```
Target modes additionally:
All modes run eight guard matrix cases and compile the installed vendor small
implementations into a temporary host executable: RFC7748 section 6.1 X25519
shared secret, RFC8032 section 7.1 test 1 Ed25519 empty-message verification, and
rejection of a corrupted signature. Host settings are deliberately minimal,
with streaming verification enabled and unused functions garbage-collected;
they are not the ESP-IDF runtime/entropy/hardware configuration. No synthetic
implementation substitutes for the tested arithmetic. Requirements: Python 3,
`cc`/linker, installed managed component; target checks also require the existing
Xtensa toolchain, generated headers and compile database. Commands are bounded;
temporary outputs are removed automatically.
- Pin original wolfSSH `internal.c` and wolfCrypt `ecc.c`, `curve25519.c`,
`signature.c`; locate the actual generated wolfSSH compile input and compare
seven complete audited crypto/auth/hash function bodies: five must remain
identical, while ECC/Ed25519 authentication must match independently specified
exact label/framing deltas reconstructed from the hash-pinned original, with
exact anchor counts. Expectations are not imported from the generator. Any
additional change requires re-audit, not repinning or skipping a body. This
does not validate the entire override generator. Generated hash is printed.
- Replay actual Xtensa compiler/includes for macro and syntax checks of twelve
translation units: ECC, signature wrapper, Curve25519, Ed25519, fast/small
field/group math, wolfSSH `ssh.c`, generated `internal.c`, application SSH
transport and security. Confirm internal `HAVE_ECC_CHECK_PUBKEY_ORDER` in ECC.
- Four negative actual-settings tests remove one policy flag at a time.
## Validation and remaining gates
Requirements: Python 3, CMake, host `cc`/linker, installed managed sources;
target modes also require the existing Xtensa toolchain, generated headers and
compilation database. Subprocesses have time bounds and temporary artifacts are
removed. No replacement crypto implementation or crypto success double is used.
Implemented validation: candidate replay passed all ten macro/syntax checks,
eight guard cases, two real-settings rejection cases, and the three host vector
checks. Initial host harness compilation exposed a disabled SHA256 declaration
dependency and omitted small-math source files; the harness was corrected to use
the installed small source files explicitly.
## Evidence and remaining gates — 2026-09-15
The parent must run the normal full build and then strict mode above. A build was
explicitly not run for this task. Existing compile-database success alone would
not prove the linked/flashed image matches it. No device operations were run.
Still required: target SSH X25519 negotiation, Ed25519 authentication, rekey,
combined service load, stack/heap reserves and handshake latency/deadline checks.
Small implementations may reduce performance; no target timing, side-channel
measurement, interoperability or resource claim is made. Host vectors are narrow
correctness checks, not exhaustive cryptographic validation.
Follow-up strict production run PASS without candidate injection, including all
host tests (20 guard cases, three-target CMake propagation, real crypto and ASN
vectors), seven complete source-body comparisons with reviewed exact parser
deltas, twelve target macro/syntax checks and four negative target-settings
cases. Earlier candidate and host-only runs also passed; the final ASN-decode
cases passed in candidate and strict runs.
Earlier development runs required correcting fixture settings/linkage; they
are not additional production failures. Host settings retain TFM, timing
resistance and small-stack allocation for ECC, but differ in word size,
allocator, OS entropy and hardware/compiler configuration. No sanitizer,
exhaustive fuzzing, allocation-failure injection or timing result is claimed.
The parent reports `pio run` PASS: 94,340 B linked RAM / 1,768,949 B flash.
This agent did not run PlatformIO or devices; local strict checks validate the
saved production compile profile, not a flashed image. Hardware tests remain
necessary for both KEX algorithms, P256/Ed25519 authentication, host-key loading,
rekey, malformed-key failure/cleanup, combined load, stack/heap reserves and
handshake deadlines. Extra import validation has real CPU/allocation cost.
The parser owner separately fixed ECC/Ed25519 labels, ECC nested exact bounds
and Ed25519 outer consumption in the generated input. Those changes are checked
by this suite's exact source contract, not supplied by crypto compile flags.
The parser suite was reviewed, not rerun in this follow-up; its crypto doubles
establish parser gating, not real signature arithmetic. Broader ordering/state
review, standalone ECC key-blob semantics outside application checks, generic
wolfSSL digest/OID API hardening as applicable, and hardware gates remain open;
see the review for evidence and limits.