Give the sniffer's X25519 static ephemeral key an RNG

curve25519 blinding is enabled by default for the C implementation, in
settings.h, and draws from the private key's own RNG on every shared secret.
The sniffer built its key with wc_curve25519_init_ex() followed by
wc_Curve25519PrivateKeyDecode(), and neither sets one, so the shared secret
reached wc_RNG_GenerateBlock() with a NULL RNG and came back BAD_FUNC_ARG
while every argument it was given was valid. SetupKeys() then reported a
server and client key mismatch and no X25519 session could be read at all.

The library's own static ephemeral path already calls
wc_curve25519_set_rng() under the same guard, and the sniffer already calls
wc_ecc_set_rng() for the ECC key a few lines above. Only X25519 was missed.

Regenerate the two X25519 captures. Both were committed as a bare 24 byte
pcap header with no packets in them, so those legs had never tested anything;
they now hold real traffic and fail against the unfixed sniffer.

Drop the has_packets guard added earlier in this branch. It existed to skip
the empty captures, and with every capture holding packets it is dead code.
Removing it is also the better behaviour: snifftest already fails a capture
that yields no plaintext, so an empty file now fails the suite loudly instead
of being skipped, which is what a missing fixture deserves.
pull/11319/head
Tobias Frauenschlaeger 2026-08-25 08:04:28 +00:00 committed by Tobias Frauenschläger
parent 960551cee0
commit d065b14508
5 changed files with 33 additions and 40 deletions

View File

@ -172,18 +172,22 @@
## Fixes
* **Fix (sniffer could not decrypt Encrypt-Then-MAC sessions)**: the sniffer
never handled the `encrypt_then_mac` extension (RFC 7366) in the ServerHello,
so for a CBC suite it passed the trailing MAC to the block decrypt along with
the ciphertext, the length was not a multiple of the block size, and every
record failed. Since wolfSSL peers negotiate it by default, this covered
most TLS 1.2 CBC captures. A build without Encrypt-Then-MAC support now
reports that, once the negotiated suite is known to be a block cipher, rather
than failing every record with a generic decrypt error. Both the
`client_key_exchange` handler and the TLS 1.3 ServerHello path also overwrote
a specific error with "Server Client Key Mismatch", which hid the reason a
session could not be decrypted; they now keep an error that has already been
described.
* **Fix (sniffer could not decrypt Encrypt-Then-MAC or X25519 sessions)**: the
sniffer never handled the `encrypt_then_mac` extension (RFC 7366) in the
ServerHello, so for a CBC suite it passed the trailing MAC to the block
decrypt along with the ciphertext, the length was not a multiple of the block
size, and every record failed. Since wolfSSL peers negotiate it by default,
this covered most TLS 1.2 CBC captures. Separately, curve25519 blinding
draws from the private key's own RNG, which the sniffer's static ephemeral
key never had, so every X25519 shared secret failed with `BAD_FUNC_ARG` and
no X25519 traffic could be read; the key now gets `wc_curve25519_set_rng()`,
as the library's own static ephemeral path already did. A build without
Encrypt-Then-MAC support now reports that, once the negotiated suite is known
to be a block cipher, rather than failing every record with a generic decrypt
error. Both the `client_key_exchange` handler and the TLS 1.3 ServerHello
path also overwrote a specific error with "Server Client Key Mismatch", which
hid the reason a session could not be decrypted; they now keep an error that
has already been described.
* **Fix (sniffer reported plaintext lengths that included the MAC or AEAD
tag)**: the length returned to the caller was taken from the record size

View File

@ -104,24 +104,8 @@ fi
RESULT=0
# A capture file holding only the 24 byte pcap header has no packets in it and
# cannot test anything. Report it and skip the leg rather than passing silently.
# Regenerate missing captures with scripts/sniffer-gen.sh.
has_packets() {
local pcap=$1
if [ ! -s "$pcap" ]; then
echo -e "\nWARNING: $pcap is missing, skipping\n"
return 1
fi
if [ "$(wc -c < "$pcap")" -le 24 ]; then
echo -e "\nWARNING: $pcap holds no packets, skipping. Regenerate it with scripts/sniffer-gen.sh\n"
return 1
fi
return 0
}
# TLS v1.2 Static RSA Test
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes && has_packets ./scripts/sniffer-static-rsa.pcap
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes
then
echo -e "\nStarting snifftest on sniffer-static-rsa.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-static-rsa.pcap -key ./certs/server-key.pem -server 127.0.0.1 -port 11111
@ -133,7 +117,7 @@ fi
# TLS v1.2 CBC Encrypt-Then-MAC Test (RFC 7366). The decrypted lengths are
# compared against known good output, since Encrypt-Then-MAC changes how much
# of a record is payload and a wrong length still decrypts.
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes && test $has_etm == yes && has_packets ./scripts/sniffer-tls12-etm.pcap
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes && test $has_etm == yes
then
echo -e "\nStarting snifftest on sniffer-tls12-etm.pcap...\n"
@ -160,7 +144,7 @@ then
fi
# TLS v1.2 Static RSA Test (IPv6)
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes && has_packets ./scripts/sniffer-ipv6.pcap
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes
then
echo -e "\nStarting snifftest on sniffer-ipv6.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-ipv6.pcap -key ./certs/server-key.pem -server ::1 -port 11111
@ -207,8 +191,6 @@ then
[[ $tlsver == "tls12" && $has_tlsv12 == "no" ]] && continue
[[ $tlsver == "tls13" && $has_tlsv13 == "no" ]] && continue
has_packets ./scripts/sniffer-$tlsver-keylog.pcap || continue
echo -e "\nStarting snifftest on sniffer-$tlsver-keylog.pcap...\n"
TMPFILE=$(mktemp)
@ -235,7 +217,7 @@ then
fi
# TLS v1.3 sniffer test ECC
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes && has_packets ./scripts/sniffer-tls13-ecc.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-ecc.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-ecc.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111
@ -245,7 +227,7 @@ then
fi
# TLS v1.3 sniffer test DH
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes && has_packets ./scripts/sniffer-tls13-dh.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-dh.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-dh.pcap -key ./certs/statickeys/dh-ffdhe2048.pem -server 127.0.0.1 -port 11111
@ -255,7 +237,7 @@ then
fi
# TLS v1.3 sniffer test X25519
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes && has_packets ./scripts/sniffer-tls13-x25519.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-x25519.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-x25519.pcap -key ./certs/statickeys/x25519.pem -server 127.0.0.1 -port 11111
@ -265,7 +247,7 @@ then
fi
# TLS v1.3 sniffer test ECC resumption
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes && test $session_ticket == yes && has_packets ./scripts/sniffer-tls13-ecc-resume.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes && test $session_ticket == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-ecc-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-ecc-resume.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111
@ -275,7 +257,7 @@ then
fi
# TLS v1.3 sniffer test DH
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes && test $session_ticket == yes && has_packets ./scripts/sniffer-tls13-dh-resume.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes && test $session_ticket == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-dh-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-dh-resume.pcap -key ./certs/statickeys/dh-ffdhe2048.pem -server 127.0.0.1 -port 11111
@ -285,7 +267,7 @@ then
fi
# TLS v1.3 sniffer test X25519
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes && test $session_ticket == yes && has_packets ./scripts/sniffer-tls13-x25519-resume.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes && test $session_ticket == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-x25519-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-x25519-resume.pcap -key ./certs/statickeys/x25519.pem -server 127.0.0.1 -port 11111
@ -295,7 +277,7 @@ then
fi
# TLS v1.3 sniffer test hello_retry_request (HRR) with ECDHE
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes && has_packets ./scripts/sniffer-tls13-hrr.pcap
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes
then
echo -e "\nStarting snifftest on sniffer-tls13-hrr.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -expectdata -pcap ./scripts/sniffer-tls13-hrr.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111

Binary file not shown.

View File

@ -2958,6 +2958,13 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session,
#endif
}
}
#ifdef WOLFSSL_CURVE25519_BLINDING
/* Blinding draws from the key's RNG on every shared secret. */
if (ret == 0) {
ret = wc_curve25519_set_rng(&args->key->priv.x25519,
session->sslServer->rng);
}
#endif
if (ret == 0) {
idx = 0;
ret = wc_Curve25519PrivateKeyDecode(args->keyBuf->buffer, &idx,