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
+222 -1
View File
@@ -119,6 +119,227 @@ TLS_POLICY = """ /* mbedTLS retains this pointer: it must outlive every serve
# DHCP uses equivalent remaining-length checks to avoid forming pointers beyond
# the input object. Keep original upstream notices verbatim, rather than changing
# their copyright year; the central project modification notice is separate.
# Bounded server parser subset of official wolfSSL/wolfssh PRs 892, 881,
# and 880 (reviewed alongside PR 899). Keep the 1.4.20 state machine and
# password/async edits below. GetSize already uses safe remaining lengths.
WOLFSSH_PARSER_EDITS = (
Edit("""int GetString(char* s, word32* sSz, const byte* buf, word32 len, word32 *idx)
{
int result;
word32 strSz;
result = GetUint32(&strSz, buf, len, idx);
""", """int GetString(char* s, word32* sSz, const byte* buf, word32 len, word32 *idx)
{
int result;
word32 strSz;
if (*sSz == 0)
return WS_BUFFER_E;
result = GetSize(&strSz, buf, len, idx);
"""),
Edit(""" result = GetUint32(&sz, buf, len, idx);
if (result == WS_SUCCESS) {
result = WS_BUFFER_E;
if (*idx < len && sz <= len - *idx) {""", """ result = GetSize(&sz, buf, len, idx);
if (result == WS_SUCCESS) {
result = WS_BUFFER_E;
if (*idx <= len && sz <= len - *idx) {"""),
Edit(""" word32 dataSz;
word32 begin = *idx;
WOLFSSH_UNUSED(ssh);
WOLFSSH_UNUSED(len);
ato32(buf + begin, &dataSz);
begin += LENGTH_SZ + dataSz;
*idx = begin;
return WS_SUCCESS;""", """ WOLFSSH_UNUSED(ssh);
return GetSkip(buf, len, idx);"""),
Edit(""" WOLFSSH_UNUSED(len);
ato32(buf + begin, &nameSz);
begin += LENGTH_SZ;
if (begin + nameSz > len || nameSz >= WOLFSSH_MAX_NAMESZ) {
return WS_BUFFER_E;
}
WMEMCPY(serviceName, buf + begin, nameSz);
begin += nameSz;
serviceName[nameSz] = 0;
*idx = begin;
WLOG(WS_LOG_DEBUG, "Requesting service: %s", serviceName);""", """ int ret = GetSize(&nameSz, buf, len, &begin);
/* Preserve 1.4.20's service-name limit; GetString normally truncates. */
if (ret != WS_SUCCESS || nameSz >= sizeof(serviceName))
return WS_BUFFER_E;
begin = *idx;
nameSz = sizeof(serviceName);
ret = GetString(serviceName, &nameSz, buf, len, &begin);
if (ret != WS_SUCCESS)
return ret;
*idx = begin;
WLOG(WS_LOG_DEBUG, "Requesting service: %s", serviceName);"""),
Edit(""" channel->peerWindowSz += bytesToAdd;
WLOG(WS_LOG_INFO, " update peerWindowSz = %u",
channel->peerWindowSz);""", """ if (bytesToAdd > (word32)0xFFFFFFFFU - channel->peerWindowSz) {
ret = WS_OVERFLOW_E;
}
else {
channel->peerWindowSz += bytesToAdd;
WLOG(WS_LOG_INFO, " update peerWindowSz = %u",
channel->peerWindowSz);
}"""),
Edit(""" if (publicKeyTypeSz != pk->publicKeyTypeSz &&
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Public Key's type does not match public key type");""", """ if (publicKeyTypeSz != pk->publicKeyTypeSz ||
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Public Key's type does not match public key type");"""),
Edit(""" if (publicKeyTypeSz != pk->publicKeyTypeSz &&
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = GetStringRef(&rSz, &r, pk->signature, pk->signatureSz, &i);""", """ if (publicKeyTypeSz != pk->publicKeyTypeSz ||
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = GetStringRef(&rSz, &r, pk->signature, pk->signatureSz, &i);"""),
# Local framing correction: GetSize proves i + sz cannot overflow. Bound
# both mpints to that sub-blob, then reject unconsumed inner/outer bytes.
Edit(""" if (publicKeyTypeSz != pk->publicKeyTypeSz ||
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = GetStringRef(&rSz, &r, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = GetStringRef(&sSz, &s, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = wc_ecc_rs_raw_to_sig(r, rSz, s, sSz,""", """ if (publicKeyTypeSz != pk->publicKeyTypeSz ||
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
/* GetSize bounded sz by signatureSz - i: this end cannot wrap. */
sz += i;
ret = GetStringRef(&rSz, &r, pk->signature, sz, &i);
}
if (ret == WS_SUCCESS) {
ret = GetStringRef(&sSz, &s, pk->signature, sz, &i);
}
if (ret == WS_SUCCESS && (i != sz || sz != pk->signatureSz))
ret = WS_BUFFER_E;
if (ret == WS_SUCCESS) {
ret = wc_ecc_rs_raw_to_sig(r, rSz, s, sSz,"""),
# PR 880's remaining current-feature label checks (Ed25519).
Edit(""" if (publicKeyTypeSz != pk->publicKeyTypeSz
&& WMEMCMP(publicKeyType,
pk->publicKeyType, publicKeyTypeSz) != 0) {""", """ if (publicKeyTypeSz != pk->publicKeyTypeSz
|| WMEMCMP(publicKeyType,
pk->publicKeyType, publicKeyTypeSz) != 0) {"""),
Edit(""" if (publicKeyTypeSz != pk->publicKeyTypeSz &&
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
if (ret == WS_SUCCESS) {
ret = wc_ed25519_verify_msg_init(pk->signature + i, sz,""", """ if (publicKeyTypeSz != pk->publicKeyTypeSz ||
WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
WLOG(WS_LOG_DEBUG,
"Signature's type does not match public key type");
ret = WS_INVALID_ALGO_ID;
}
}
if (ret == WS_SUCCESS) {
/* Get the size of the signature blob. */
ret = GetSize(&sz, pk->signature, pk->signatureSz, &i);
}
/* The signature string must consume the enclosing signature field. */
if (ret == WS_SUCCESS && sz != pk->signatureSz - i)
ret = WS_BUFFER_E;
if (ret == WS_SUCCESS) {
ret = wc_ed25519_verify_msg_init(pk->signature + i, sz,"""),
)
ENTRIES = (
Entry("dhcpserver", "lwip", "idf",
"components/lwip/apps/dhcpserver/dhcpserver.c",
@@ -206,7 +427,7 @@ ENTRIES = (
), target="mbedx509"),
Entry("wolfssh_internal", "wolfssl__wolfssh", "project",
"managed_components/wolfssl__wolfssh/src/internal.c",
"81ff1f9166708abd5c2911e9fe57c0aee01c88b5d3f68c909ee8a856d37f36a9", (
"81ff1f9166708abd5c2911e9fe57c0aee01c88b5d3f68c909ee8a856d37f36a9", WOLFSSH_PARSER_EDITS + (
Edit(""" WS_UserAuthData_Password* pw = NULL;
int ret = WS_SUCCESS;
""", """ WS_UserAuthData_Password* pw = NULL;