Merge pull request #6717 from bigbrett/sniffer-keylogfile

sniffer keylog file support
This commit is contained in:
JacobBarthelmeh 2023-08-22 14:06:27 -06:00 committed by GitHub
commit 3033371abc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 787 additions and 107 deletions

View File

@ -1324,6 +1324,17 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS_MTU"
fi
# KeyLog file export
AC_ARG_ENABLE([keylog-export],
[AS_HELP_STRING([--enable-keylog-export],[Enable (DANGEROUS INSECURE) exporting TLS secrets to an NSS keylog file (default: disabled)])],
[ ENABLED_KEYLOG_EXPORT=$enableval ],
[ ENABLED_KEYLOG_EXPORT=no ]
)
if test "$ENABLED_KEYLOG_EXPORT" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DSHOW_SECRETS -DHAVE_SECRET_CALLBACK -DWOLFSSL_SSLKEYLOGFILE"
fi
# TLS v1.3 Draft 18 (Note: only final TLS v1.3 supported, here for backwards build compatibility)
AC_ARG_ENABLE([tls13-draft18],
[AS_HELP_STRING([--enable-tls13-draft18],[Enable wolfSSL TLS v1.3 Draft 18 (default: disabled)])],
@ -9449,6 +9460,7 @@ echo " * PSA: $ENABLED_PSA"
echo " * System CA certs: $ENABLED_SYS_CA_CERTS"
echo " * ERR Queues per Thread: $ENABLED_ERRORQUEUEPERTHREAD"
echo " * rwlock: $ENABLED_RWLOCK"
echo " * keylog export: $ENABLED_KEYLOG_EXPORT"
echo ""
echo "---"

View File

@ -46,7 +46,7 @@ run_sequence() {
run_test "TLS13-AES128-GCM-SHA256" "-v 4" "-v 4"
run_test "TLS13-AES256-GCM-SHA384" "-v 4" "-v 4"
run_test "TLS13-CHACHA20-POLY1305-SHA256" "-v 4" "-v 4"
elif [ "$1" == "tls12" ]; then # TLS v1.2
elif [ "$1" == "tls12" ] || [ "$1" == "tls12-keylog" ]; then # TLS v1.2
run_test "ECDHE-ECDSA-AES128-GCM-SHA256" "-v 3 -A ./certs/ca-ecc-cert.pem -k ./certs/ecc-key.pem -c ./certs/intermediate/server-chain-ecc.pem -V" "-v 3 -A ./certs/ca-ecc-cert.pem -k ./certs/ecc-client-key.pem -c ./certs/intermediate/client-chain-ecc.pem -C"
run_test "ECDHE-ECDSA-AES256-GCM-SHA384" "-v 3 -A ./certs/ca-ecc-cert.pem -k ./certs/ecc-key.pem -c ./certs/intermediate/server-chain-ecc.pem -V" "-v 3 -A ./certs/ca-ecc-cert.pem -k ./certs/ecc-client-key.pem -c ./certs/intermediate/client-chain-ecc.pem -C"
elif [ "$1" == "tls13-dh-resume" ] || [ "$1" == "tls13-ecc-resume" ]; then # TLS v1.3 Resumption
@ -69,19 +69,37 @@ run_sequence() {
fi
}
run_capture(){
run_capture() {
local config_flags=()
echo -e "\nconfiguring and building wolfssl ($1)..."
./configure --enable-sniffer $2 1>/dev/null || exit $?
# Add default flags
config_flags+=(--enable-sniffer)
# If additional arguments are provided, add them to the array
if [ -n "$2" ]; then
# Convert string into an array, respecting quoted strings as a single element
eval "config_flags+=($2)"
fi
./configure "${config_flags[@]}" 1>/dev/null || exit $?
make 1>/dev/null || exit $?
echo "starting capture"
tcpdump -i lo -n port 11111 -w ./scripts/sniffer-${1}.pcap -U &
tcpdump_pid=$!
run_sequence $1
sleep 1
kill -15 $tcpdump_pid; tcpdump_pid=0
if [ "$1" == "tls12-keylog" ]; then
cp ./sslkeylog.log ./scripts/sniffer-${1}.sslkeylog
fi
}
run_capture "tls12" ""
run_capture "tls12-keylog" "--enable-enc-then-mac=no --enable-keylog-export CFLAGS='-Wno-cpp -DWOLFSSL_SNIFFER_KEYLOGFILE'"
run_capture "tls13-ecc" ""
run_capture "tls13-ecc-resume" "--enable-session-ticket"
run_capture "tls13-dh" "--disable-ecc"

View File

@ -59,6 +59,12 @@ has_static_rsa=no
if [ $? -eq 0 ]; then
has_static_rsa=yes
fi
# ./configure --enable-sniffer CFLAGS="-DWOLFSSL_SNIFFER_KEYLOGFILE"
has_keylog=no
./sslSniffer/sslSnifferTest/snifftest -? 2>&1 | grep -- 'ssl_keylog_file'
if [ $? -eq 0 ]; then
has_keylog=yes
fi
RESULT=0
@ -66,8 +72,8 @@ RESULT=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
then
echo -e "\nStaring snifftest on testsuite.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-static-rsa.pcap ./certs/server-key.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-static-rsa.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-static-rsa.pcap -key ./certs/server-key.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest static RSA failed\n" && exit 1
@ -77,16 +83,45 @@ fi
if test $RESULT -eq 0 && test $has_rsa == yes && test $has_tlsv12 == yes && test $has_static_rsa == yes
then
echo -e "\nStaring snifftest on sniffer-ipv6.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-ipv6.pcap ./certs/server-key.pem ::1 11111
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-ipv6.pcap -key ./certs/server-key.pem -server ::1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest (ipv6) failed\n" && exit 1
fi
# TLS v1.2 sniffer keylog file test: runs sniffer on pcap and associated keylog file and compares decrypted traffic with known good output.
# To regenerate the known good output, run `scripts/sniffer-gen.sh` to regenerate the pcap and keylog file, then run the sniffer on it
# with the same arguments as in the test below, but redirect output to `./scripts/sniffer-tls12-keylog.out`.
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_keylog == yes
then
echo -e "\nStaring snifftest on sniffer-tls12-keylog.pcap...\n"
TMPFILE=$(mktemp)
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest keylog test failed: unable to create tmpfile\n" && rm $TMPFILE && exit 1
./sslSniffer/sslSnifferTest/snifftest \
-pcap scripts/sniffer-tls12-keylog.pcap \
-keylogfile scripts/sniffer-tls12-keylog.sslkeylog \
-server 127.0.0.1 -port 11111 > $TMPFILE
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest keylog test failed: snifftest returned $RESULT\n" && rm $TMPFILE && exit 1
# sed '1d' strips out first line, which contains wolfSSL version
sed '1d' $TMPFILE | diff - <(sed '1d' scripts/sniffer-tls12-keylog.out)
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest keylog test failed: snifftest diff returned $RESULT\n" && rm $TMPFILE && exit 1
rm $TMPFILE
fi
# TLS v1.3 sniffer test ECC
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_ecc == yes
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-ecc.pcap ./certs/statickeys/ecc-secp256r1.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-ecc.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-ecc.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 ECC failed\n" && exit 1
@ -95,7 +130,8 @@ fi
# TLS v1.3 sniffer test DH
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-dh.pcap ./certs/statickeys/dh-ffdhe2048.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-dh.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-dh.pcap -key ./certs/statickeys/dh-ffdhe2048.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 DH failed\n" && exit 1
@ -104,7 +140,8 @@ fi
# TLS v1.3 sniffer test X25519
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-x25519.pcap ./certs/statickeys/x25519.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-x25519.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-x25519.pcap -key ./certs/statickeys/x25519.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 X25519 failed\n" && exit 1
@ -113,7 +150,8 @@ 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
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-ecc-resume.pcap ./certs/statickeys/ecc-secp256r1.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-ecc-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-ecc-resume.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 ECC failed\n" && exit 1
@ -122,7 +160,8 @@ fi
# TLS v1.3 sniffer test DH
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_dh == yes && test $session_ticket == yes
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-dh-resume.pcap ./certs/statickeys/dh-ffdhe2048.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-dh-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-dh-resume.pcap -key ./certs/statickeys/dh-ffdhe2048.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 DH failed\n" && exit 1
@ -131,7 +170,8 @@ fi
# TLS v1.3 sniffer test X25519
if test $RESULT -eq 0 && test $has_tlsv13 == yes && test $has_x25519 == yes && test $session_ticket == yes
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-x25519-resume.pcap ./certs/statickeys/x25519.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-x25519-resume.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-x25519-resume.pcap -key ./certs/statickeys/x25519.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 X25519 failed\n" && exit 1
@ -140,12 +180,12 @@ 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
then
./sslSniffer/sslSnifferTest/snifftest ./scripts/sniffer-tls13-hrr.pcap ./certs/statickeys/ecc-secp256r1.pem 127.0.0.1 11111
echo -e "\nStaring snifftest on sniffer-tls13-hrr.pcap...\n"
./sslSniffer/sslSnifferTest/snifftest -pcap ./scripts/sniffer-tls13-hrr.pcap -key ./certs/statickeys/ecc-secp256r1.pem -server 127.0.0.1 -port 11111
RESULT=$?
[ $RESULT -ne 0 ] && echo -e "\nsnifftest TLS v1.3 HRR failed\n" && exit 1
fi
echo -e "\nSuccess!\n"
exit 0

View File

@ -0,0 +1,7 @@
snifftest 5.6.3
sniffer features: key_callback tls_v13 tls_v12 static_ephemeral sni extended_master rsa dh ecc rsa_static dh_static ssl_keylog_file
SSL App Data(26:14):hello wolfssl!
SSL App Data(27:22):I hear you fa shizzle!
SSL App Data(57:14):hello wolfssl!
SSL App Data(58:22):I hear you fa shizzle!

Binary file not shown.

View File

@ -0,0 +1,12 @@
CLIENT_RANDOM 3827fef5d4172f3753d81661dbc228b41adcb2357e04e493f8d9d4d4a85777d3 5240740265eaa6a8622805728bf53fd88b546b1523e4b9c3d4b6573471bc081ce9f074520df99873c0c447d3a37ebdc6
CLIENT_RANDOM 3827fef5d4172f3753d81661dbc228b41adcb2357e04e493f8d9d4d4a85777d3 5240740265eaa6a8622805728bf53fd88b546b1523e4b9c3d4b6573471bc081ce9f074520df99873c0c447d3a37ebdc6
CLIENT_RANDOM 8d793a1160661700dc686746be0e77a01dcf94472971bfbb517c6d7d179b7bcd ac612c7b9292ad6bc5304176b9dcde81ee488b6adb63bb6917cbf38a0775e9e334766839e091506972450e77ba6ce977
CLIENT_RANDOM 8d793a1160661700dc686746be0e77a01dcf94472971bfbb517c6d7d179b7bcd ac612c7b9292ad6bc5304176b9dcde81ee488b6adb63bb6917cbf38a0775e9e334766839e091506972450e77ba6ce977
CLIENT_RANDOM 4a1d3695145e5136a2914756962f848f033b62d3a9b714f7e659ae3f133d2527 118442e0edd05696d1566eb73693a9a1316d24ac62e024f92e685c540eaec31a463e19091d45b63cfc8539d3bd11915b
CLIENT_RANDOM 4a1d3695145e5136a2914756962f848f033b62d3a9b714f7e659ae3f133d2527 118442e0edd05696d1566eb73693a9a1316d24ac62e024f92e685c540eaec31a463e19091d45b63cfc8539d3bd11915b
CLIENT_RANDOM 307abe19ea84d9b45621df5b89fee8d2f9ac66eb4303cf9303cf6e957ad1d75d dfb9bb0d29579a0b2f35be65982954f33268c30ea8709985a45c95633c1c6e94cbfdebe625bda975572921b4462d5153
CLIENT_RANDOM 307abe19ea84d9b45621df5b89fee8d2f9ac66eb4303cf9303cf6e957ad1d75d dfb9bb0d29579a0b2f35be65982954f33268c30ea8709985a45c95633c1c6e94cbfdebe625bda975572921b4462d5153
CLIENT_RANDOM 41ad4bceb3b900ffbc77f9b0c67d69a62f2b1d490f91b2af496cf6e78371900d 9752ea66a193ac04e4a20aca3c7160faa2637efb927d00c2a2d90b77e2e7875a760ee76f9ce509e549f8303625a2fd59
CLIENT_RANDOM 41ad4bceb3b900ffbc77f9b0c67d69a62f2b1d490f91b2af496cf6e78371900d 9752ea66a193ac04e4a20aca3c7160faa2637efb927d00c2a2d90b77e2e7875a760ee76f9ce509e549f8303625a2fd59
CLIENT_RANDOM 596ffcdec477ac0b24e0958ecd7c1fc7cc5b37337bac90803b864e3edbad8780 2f86705d0c4fb7e92c7cb1ef2f104955724d5a0b5abd18478d39c1dd96222b4462e4382982bec26e9a231ec970c2d509
CLIENT_RANDOM 596ffcdec477ac0b24e0958ecd7c1fc7cc5b37337bac90803b864e3edbad8780 2f86705d0c4fb7e92c7cb1ef2f104955724d5a0b5abd18478d39c1dd96222b4462e4382982bec26e9a231ec970c2d509

View File

@ -7502,7 +7502,7 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
#endif
#if defined(HAVE_SECRET_CALLBACK) && defined(SHOW_SECRETS) && \
defined(WOLFSSL_SSLKEYLOGFILE)
defined(WOLFSSL_SSLKEYLOGFILE) && defined(WOLFSSL_TLS13)
(void)wolfSSL_set_tls13_secret_cb(ssl, tls13ShowSecrets, NULL);
#endif
@ -17651,7 +17651,7 @@ int ChachaAEADEncrypt(WOLFSSL* ssl, byte* out, const byte* input,
*
* Return 0 on success negative values in error case
*/
static int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
word16 sz)
{
byte add[AEAD_AUTH_DATA_SZ];

View File

@ -373,6 +373,9 @@ static const char* const msgTable[] =
"Setting up keys",
"Unsupported TLS Version",
"Server Client Key Mismatch",
/* 99 */
"Invalid or missing keylog file",
};
@ -436,6 +439,11 @@ typedef struct SnifferServer {
NamedKey* namedKeys; /* mapping of names and keys */
wolfSSL_Mutex namedKeysMutex; /* mutex for namedKey list */
#endif
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
byte useKeyLogFile; /* True if session secrets are coming from a
keylog file */
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
struct SnifferServer* next; /* for list */
} SnifferServer;
@ -652,6 +660,22 @@ static void UpdateMissedDataSessions(void)
static WOLFSSL_GLOBAL int CryptoDeviceId = INVALID_DEVID;
#endif
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
static int addSecretNode(unsigned char* clientRandom,
unsigned char* masterSecret,
char* error);
static void hexToBin(const char* hex, unsigned char* bin, int binLength);
static int parseKeyLogFile(const char* fileName, char* error);
static unsigned char* findMasterSecret(unsigned char* clientRandom);
static void freeSecretList(void);
static int snifferSecretCb(unsigned char* client_random,
unsigned char* output_secret);
static void setSnifferSecretCb(SnifferSession* session);
static int addKeyLogSnifferServerHelper(const char* address,
int port,
char* error);
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
/* Initialize overall Sniffer */
void ssl_InitSniffer_ex(int devId)
@ -867,8 +891,16 @@ void ssl_FreeSniffer(void)
}
ServerList = NULL;
UNLOCK_SESSION();
UNLOCK_SERVER_LIST();
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
freeSecretList();
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
#ifndef WOLFSSL_SNIFFER_NO_RECOVERY
wc_FreeMutex(&RecoveryMutex);
#endif
@ -1162,8 +1194,14 @@ static void TraceSetServer(const char* srv, int port, const char* keyFile)
{
if (TraceOn) {
XFPRINTF(TraceFile, "\tTrying to install a new Sniffer Server with\n");
XFPRINTF(TraceFile, "\tserver: %s, port: %d, keyFile: %s\n", srv, port,
keyFile);
if (keyFile != NULL) {
XFPRINTF(TraceFile, "\tserver: %s, port: %d, keyFile: %s\n",
srv, port, keyFile);
}
else {
XFPRINTF(TraceFile, "\tserver: %s, port: %d\n",
srv, port);
}
}
}
@ -1732,6 +1770,7 @@ static int CreateWatchSnifferServer(char* error)
#endif
/* Caller locks ServerListMutex */
static int SetNamedPrivateKey(const char* name, const char* address, int port,
const char* keyFile, int keySz, int typeKey, const char* password,
@ -1780,10 +1819,11 @@ static int SetNamedPrivateKey(const char* name, const char* address, int port,
if (serverIp.ip4 == XINADDR_NONE) {
#ifdef FUSION_RTOS
if (XINET_PTON(AF_INET6, address, serverIp.ip6,
sizeof(serverIp.ip4)) == 1) {
sizeof(serverIp.ip4)) == 1)
#else
if (XINET_PTON(AF_INET6, address, serverIp.ip6) == 1) {
if (XINET_PTON(AF_INET6, address, serverIp.ip6) == 1)
#endif
{
serverIp.version = IPV6;
}
}
@ -2463,6 +2503,17 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session,
}
#endif
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
if (session->context->useKeyLogFile) {
ret = 0;
XMEMSET(args, 0, sizeof(SetupKeysArgs));
/* We want to skip all the key setup and go right to master secret generation, which is
* where we inject the master secret obtained from the keylog file */
ssl->options.asyncState = TLS_ASYNC_FINALIZE;
}
#endif
switch (ssl->options.asyncState) {
case TLS_ASYNC_BEGIN:
{
@ -3084,12 +3135,17 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session,
case TLS_ASYNC_FINALIZE:
{
/* store for client side as well */
XMEMCPY(session->sslClient->arrays->preMasterSecret,
session->sslServer->arrays->preMasterSecret,
session->sslServer->arrays->preMasterSz);
session->sslClient->arrays->preMasterSz =
session->sslServer->arrays->preMasterSz;
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
if (!session->context->useKeyLogFile)
#endif /* !WOLFSSL_SNIFFER_KEYLOGFILE */
{
/* store for client side as well */
XMEMCPY(session->sslClient->arrays->preMasterSecret,
session->sslServer->arrays->preMasterSecret,
session->sslServer->arrays->preMasterSz);
session->sslClient->arrays->preMasterSz =
session->sslServer->arrays->preMasterSz;
}
#ifdef SHOW_SECRETS
PrintSecret("pre master secret",
@ -4556,14 +4612,21 @@ static int DoHandShake(const byte* input, int* sslBytes,
Trace(GOT_CERT_REQ_STR);
break;
case server_key_exchange:
#ifdef WOLFSSL_SNIFFER_STATS
INC_STAT(SnifferStats.sslEphemeralMisses);
#endif
Trace(GOT_SERVER_KEY_EX_STR);
/* can't know temp key passively */
SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE);
session->verboseErr = 1;
ret = -1;
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
if (!session->context->useKeyLogFile)
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
{
/* can't know temp key passively */
SetError(BAD_CIPHER_SPEC_STR, error, session, FATAL_ERROR_STATE);
session->verboseErr = 1;
ret = -1;
#if defined(WOLFSSL_SNIFFER_STATS)
INC_STAT(SnifferStats.sslEphemeralMisses);
#endif /* WOLFSSL_SNIFFER_STATS */
}
break;
case encrypted_extensions:
Trace(GOT_ENC_EXT_STR);
@ -4720,6 +4783,8 @@ static int DecryptDo(WOLFSSL* ssl, byte* plain, const byte* input,
case wolfssl_aes_gcm:
case wolfssl_aes_ccm: /* GCM AEAD macros use same size as CCM */
{
/* For ciphers that use AEAD use the encrypt routine to
* bypass the auth tag checking */
wc_AesAuthEncryptFunc aes_auth_fn;
#ifdef WOLFSSL_ASYNC_CRYPT
@ -4749,7 +4814,7 @@ static int DecryptDo(WOLFSSL* ssl, byte* plain, const byte* input,
input + AESGCM_EXP_IV_SZ,
sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
ssl->decrypt.nonce, AESGCM_NONCE_SZ,
ssl->decrypt.additional, ssl->specs.aead_mac_size,
ssl->decrypt.additional, AEAD_AUTH_DATA_SZ,
NULL, 0)) < 0) {
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_PENDING_E) {
@ -4782,7 +4847,7 @@ static int DecryptDo(WOLFSSL* ssl, byte* plain, const byte* input,
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && \
!defined(NO_CHAPOL_AEAD)
case wolfssl_chacha:
ret = ChachaAEADEncrypt(ssl, plain, input, sz);
ret = ChachaAEADDecrypt(ssl, plain, input, sz);
break;
#endif
@ -5122,6 +5187,13 @@ static SnifferSession* CreateSession(IpInfo* ipInfo, TcpInfo* tcpInfo,
/* put server back into server mode */
session->sslServer->options.side = WOLFSSL_SERVER_END;
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
if (session->context->useKeyLogFile) {
setSnifferSecretCb(session);
}
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
row = SessionHash(ipInfo, tcpInfo);
/* add it to the session table */
@ -6492,10 +6564,10 @@ static int RemoveFatalSession(IpInfo* ipInfo, TcpInfo* tcpInfo,
SnifferSession* session, char* error)
{
if (session && session->flags.fatalError == FATAL_ERROR_STATE) {
RemoveSession(session, ipInfo, tcpInfo, 0);
if (!session->verboseErr) {
SetError(FATAL_ERROR_STR, error, NULL, 0);
}
RemoveSession(session, ipInfo, tcpInfo, 0);
return 1;
}
return 0;
@ -7132,6 +7204,367 @@ int ssl_PollSniffer(WOLF_EVENT** events, int maxEvents, WOLF_EVENT_FLAG flags,
}
#endif
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
#define CLIENT_RANDOM_LABEL_LENGTH 13
#define CLIENT_RANDOM_LENGTH 32
#define MASTER_SECRET_LENGTH 48
#define CLIENT_RANDOM_BITS ((CLIENT_RANDOM_LENGTH) * 8)
typedef struct SecretNode {
unsigned char clientRandom[CLIENT_RANDOM_LENGTH];
unsigned char masterSecret[MASTER_SECRET_LENGTH];
struct SecretNode* next;
} SecretNode;
/* Default to the same size hash table as the session table,
* but allow user to override */
#ifndef WOLFSSL_SNIFFER_KEYLOGFILE_HASH_TABLE_SIZE
#define WOLFSSL_SNIFFER_KEYLOGFILE_HASH_TABLE_SIZE HASH_SIZE
#endif
static THREAD_LS_T WOLFSSL_GLOBAL
SecretNode*
secretHashTable[WOLFSSL_SNIFFER_KEYLOGFILE_HASH_TABLE_SIZE] = {NULL};
#ifndef HAVE_C___ATOMIC
static WOLFSSL_GLOBAL wolfSSL_Mutex secretListMutex;
#endif
static unsigned int secretHashFunction(unsigned char* clientRandom);
#ifdef HAVE_C___ATOMIC
#define LOCK_SECRET_LIST() WC_DO_NOTHING
#define UNLOCK_SECRET_LIST() WC_DO_NOTHING
#else
#define LOCK_SECRET_LIST() wc_LockMutex(&secretListMutex)
#define UNLOCK_SECRET_LIST() wc_UnLockMutex(&secretListMutex)
#endif
/*
* Basic polynomial hash function that maps a 32-byte client random value to an
* array index
*/
static unsigned int secretHashFunction(unsigned char* clientRandom)
{
int i = 0;
unsigned int hash = 0;
for (i = 0; i < CLIENT_RANDOM_LENGTH; i++) {
hash = (hash * CLIENT_RANDOM_BITS + clientRandom[i])
% WOLFSSL_SNIFFER_KEYLOGFILE_HASH_TABLE_SIZE;
}
return hash;
}
static int addSecretNode(unsigned char* clientRandom,
unsigned char* masterSecret,
char* error)
{
unsigned int index = 0;
SecretNode* newSecretNode = NULL;
newSecretNode = (SecretNode*)XMALLOC(sizeof(SecretNode),
NULL,
DYNAMIC_TYPE_SNIFFER_KEYLOG_NODE);
if (newSecretNode == NULL) {
SetError(MEMORY_STR, error, NULL, 0);
return WOLFSSL_SNIFFER_ERROR;
}
XMEMCPY(newSecretNode->clientRandom, clientRandom, CLIENT_RANDOM_LENGTH);
XMEMCPY(newSecretNode->masterSecret, masterSecret, MASTER_SECRET_LENGTH);
LOCK_SECRET_LIST();
index = secretHashFunction(clientRandom);
newSecretNode->next = NULL;
if (secretHashTable[index] == NULL) {
secretHashTable[index] = newSecretNode;
}
else {
SecretNode* current = secretHashTable[index];
while (current != NULL) {
if (memcmp(current->clientRandom,
clientRandom,
CLIENT_RANDOM_LENGTH) == 0) {
/* No need for a new node, since it already exists */
fprintf(stderr, "Found duplicate client random value in "
"keylog file. Rejecting.\n");
XFREE(newSecretNode, NULL, DYNAMIC_TYPE_SNIFFER_KEYLOG_NODE);
break;
}
if (current->next == NULL) {
current->next = newSecretNode;
break;
}
current = current->next;
}
}
UNLOCK_SECRET_LIST();
return 0;
}
/*
* Looks up a master secret for a given client random from the keylog file
*/
static unsigned char* findMasterSecret(unsigned char* clientRandom)
{
unsigned char* secret = NULL;
SecretNode* node = NULL;
unsigned int index = 0;
LOCK_SECRET_LIST();
index = secretHashFunction(clientRandom);
node = secretHashTable[index];
while (node != NULL) {
if (XMEMCMP(node->clientRandom,
clientRandom, CLIENT_RANDOM_LENGTH) == 0) {
secret = node->masterSecret;
break;
}
node = node->next;
}
UNLOCK_SECRET_LIST();
return secret;
}
static void hexToBin(const char* hex, unsigned char* bin, int binLength)
{
int i = 0;
for (i = 0; i < binLength; i++) {
sscanf(hex + 2 * i, "%02hhx", &bin[i]);
}
}
static int parseKeyLogFile(const char* fileName, char* error)
{
const char CLIENT_RANDOM_LABEL_STR[] = "CLIENT_RANDOM";
unsigned char clientRandom[CLIENT_RANDOM_LENGTH];
unsigned char masterSecret[MASTER_SECRET_LENGTH];
FILE* file = NULL;
int ret = 0;
/* +1 for null terminator */
char clientRandomLabel[CLIENT_RANDOM_LABEL_LENGTH + 1] = {0};
/* 2 chars for Hexadecimal representation, plus null terminator */
char clientRandomHex[2 * CLIENT_RANDOM_LENGTH + 1] = {0};
char masterSecretHex[2 * MASTER_SECRET_LENGTH + 1] = {0};
file = fopen(fileName, "r");
if (file == NULL) {
fprintf(stderr, "Could not open keylog file: %s\n", fileName);
SetError(KEYLOG_FILE_INVALID, error, NULL, 0);
return WOLFSSL_SNIFFER_ERROR;
}
while (fscanf(file, "%13s %64s %96s",
clientRandomLabel, clientRandomHex, masterSecretHex) == 3) {
if (XSTRCMP(clientRandomLabel, CLIENT_RANDOM_LABEL_STR) == 0) {
hexToBin(clientRandomHex, clientRandom, CLIENT_RANDOM_LENGTH);
hexToBin(masterSecretHex, masterSecret, MASTER_SECRET_LENGTH);
ret = addSecretNode(clientRandom, masterSecret, error);
if (ret != 0) {
fclose(file);
return ret;
}
}
}
fclose(file);
return 0;
}
static void freeSecretList(void)
{
int i = 0;
LOCK_SECRET_LIST();
for (i=0; i<WOLFSSL_SNIFFER_KEYLOGFILE_HASH_TABLE_SIZE; i++)
{
SecretNode* current = secretHashTable[i];
SecretNode * next = NULL;
while (current != NULL) {
next = current->next;
XFREE(current, NULL, DYNAMIC_TYPE_SNIFFER_KEYLOG_NODE);
current = next;
}
}
UNLOCK_SECRET_LIST();
}
/*
* Looks up secret based on client random and copies it to output_secret
*/
static int snifferSecretCb(unsigned char* client_random,
unsigned char* output_secret)
{
unsigned char* secret = NULL;
if (client_random == NULL || output_secret == NULL) {
return WOLFSSL_SNIFFER_FATAL_ERROR;
}
/* get secret from secret table based on client random */
secret = findMasterSecret(client_random);
if (secret != NULL) {
XMEMCPY(output_secret, secret, MASTER_SECRET_LENGTH);
return 0;
}
/* didn't find the secret */
return WOLFSSL_SNIFFER_ERROR;
}
static void setSnifferSecretCb(SnifferSession* session)
{
session->context->useKeyLogFile = 1;
session->sslServer->snifferSecretCb = snifferSecretCb;
session->sslClient->snifferSecretCb = snifferSecretCb;
}
/*
* Helper function that creates a sniffer server object that can decrypt using
* a keylog file, and adds it to the server list
*
* NOTE: the caller is responsible for locking and unlocking the server list
*/
static int addKeyLogSnifferServerHelper(const char* address,
int port,
char* error)
{
IpAddrInfo serverIp = {0};
SnifferServer *sniffer = NULL;
TraceHeader();
TraceSetServer(address, port, NULL);
serverIp.version = IPV4;
serverIp.ip4 = XINET_ADDR(address);
if (serverIp.ip4 == XINADDR_NONE) {
#ifdef FUSION_RTOS
if (XINET_PTON(AF_INET6, address, serverIp.ip6,
sizeof(serverIp.ip4)) == 1)
#else
if (XINET_PTON(AF_INET6, address, serverIp.ip6) == 1)
#endif
{
serverIp.version = IPV6;
}
}
sniffer = ServerList;
while (sniffer != NULL &&
(!MatchAddr(sniffer->server, serverIp) || sniffer->port != port)) {
sniffer = sniffer->next;
}
if (sniffer == NULL) {
sniffer = (SnifferServer*)XMALLOC(sizeof(SnifferServer),
NULL, DYNAMIC_TYPE_SNIFFER_SERVER);
if (sniffer == NULL) {
SetError(MEMORY_STR, error, NULL, 0);
return WOLFSSL_SNIFFER_ERROR;
}
InitSnifferServer(sniffer);
XSTRNCPY(sniffer->address, address, MAX_SERVER_ADDRESS-1);
sniffer->address[MAX_SERVER_ADDRESS-1] = '\0';
sniffer->server = serverIp;
sniffer->port = port;
sniffer->ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
if (!sniffer->ctx) {
SetError(MEMORY_STR, error, NULL, 0);
FreeSnifferServer(sniffer);
return WOLFSSL_SNIFFER_ERROR;
}
#if defined(WOLF_CRYPTO_CB) || defined(WOLFSSL_ASYNC_CRYPT)
if (CryptoDeviceId != INVALID_DEVID)
wolfSSL_CTX_SetDevId(sniffer->ctx, CryptoDeviceId);
#endif
sniffer->next = ServerList;
ServerList = sniffer;
}
else {
printf("SESSION ALREADY EXISTS\n");
}
/* Tag the new or existing server as requiring keylog support to
* decrypt, otherwise it won't be useable */
sniffer->useKeyLogFile = 1;
return 0;
}
/*
* Creates a sniffer server that is able to decrypt using secrets from a
* keylog file, and adds it to the server list
*
* If a server at the address and port already exists, it will be marked
* for keylog file decryption
*/
int ssl_CreateKeyLogSnifferServer(const char* address, int port, char* error)
{
int ret = 0;
if (address == NULL) {
SetError(KEYLOG_FILE_INVALID, error, NULL, 0);
return WOLFSSL_SNIFFER_ERROR;
}
LOCK_SERVER_LIST();
ret = addKeyLogSnifferServerHelper(address, port, error);
UNLOCK_SERVER_LIST();
return ret;
}
/*
* Loads secrets to decrypt TLS traffic from a keylog file. Only sniffer
* servers registered with ssl_createKeyLogSnifferServer() will be able to
* decrypt using these secrets
*/
int ssl_LoadSecretsFromKeyLogFile(const char* keylogfile, char* error)
{
if (keylogfile == NULL) {
SetError(KEYLOG_FILE_INVALID, error, NULL, 0);
return WOLFSSL_SNIFFER_ERROR;
}
return parseKeyLogFile(keylogfile, error);
}
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
#undef ERROR_OUT
#endif /* WOLFSSL_SNIFFER */

View File

@ -516,6 +516,19 @@ int MakeTlsMasterSecret(WOLFSSL* ssl)
{
int ret;
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE)
/* If this is called from a sniffer session with keylog file support, obtain
* the master secret from the callback */
if (ssl->snifferSecretCb != NULL) {
ret = ssl->snifferSecretCb(ssl->arrays->clientRandom, ssl->arrays->masterSecret);
if (ret != 0) {
return ret;
}
ret = DeriveTlsKeys(ssl);
return ret;
}
#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */
#ifdef HAVE_EXTENDED_MASTER
if (ssl->options.haveEMS) {
word32 hashSz = HSHASH_SZ;

View File

@ -13729,6 +13729,12 @@ int tls13ShowSecrets(WOLFSSL* ssl, int id, const unsigned char* secret,
str = "SERVER_TRAFFIC_SECRET_0"; break;
case EXPORTER_SECRET:
str = "EXPORTER_SECRET"; break;
default:
#ifdef WOLFSSL_SSLKEYLOGFILE_OUTPUT
XFCLOSE(fp);
#endif
return BAD_FUNC_ARG;
break;
}
fprintf(fp, "%s ", str);

View File

@ -39,13 +39,18 @@ The STARTTLS option allows the sniffer to receive and ignore plaintext before re
`./configure --enable-sniffer CPPFLAGS=-DSTARTTLS_ALLOWED`
The SSL KeyLog file option enables the sniffer to decrypt TLS traffic using the master secret obtained from a [NSS keylog file](https://web.archive.org/web/20220531072242/https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html). This allows the sniffer to decrypt all TLS traffic, even for TLS connections using ephemeral cipher suites. Currently, sniffer keylog file support is limited to TLSv1.2 traffic. WolfSSL can be configured to export a keylog file using the `-DSHOW_SECRETS -DHAVE_SECRET_CALLBACK -DWOLFSSL_SSLKEYLOGFILE` macros, independently from the sniffer feature (NOTE: never do this in a production environment, as it is inherently insecure). To enable sniffer support for keylog files,
use the following configure command line and build as before:
`./configure --enable-sniffer CPPFLAGS=-DWOLFSSL_SNIFFER_KEYLOGFILE`
All options may be enabled with the following configure command line:
```sh
./configure --enable-sniffer \
CPPFLAGS="-DWOLFSSL_SNIFFER_STATS -DWOLFSSL_SNIFFER_WATCH \
-DWOLFSSL_SNIFFER_STORE_DATA_CB -DWOLFSSL_SNIFFER_CHAIN_INPUT \
-DSTARTTLS_ALLOWED"
-DSTARTTLS_ALLOWED -DWOLFSSL_SNIFFER_KEYLOGFILE"
```
To add some other cipher support to the sniffer, you can add options like:
@ -88,7 +93,11 @@ To build with OCTEON III support for a Linux host:
## Command Line Options
The wolfSSL sniffer includes a test application `snifftest` in the `sslSniffer/sslSnifferTest/` directory. The command line application has several options that can be passed in at runtime to change the default behavior of the application. To execute a “live” sniff just run the application without any parameters and then pick an interface to sniff on followed by the port.
The wolfSSL sniffer includes a test application `snifftest` in the `sslSniffer/sslSnifferTest/` directory. The command line application has two sniffing modes: "live" mode and "offline" mode. In "live" mode, the application will prompt the user for network information and other parameters and then actively sniff real network traffic on an interface. In "offline" mode, the user provides the application with a pcap file and other network information via command line arguments, and the sniffer
will then decrypt the relevant TLS traffic captured in the pcap file.
### Live Sniff Mode
To execute a “live” sniff just run the application without any parameters and then pick an interface to sniff on followed by the port.
An example startup may look like this:
@ -116,41 +125,50 @@ The above example sniffs on the localhost interface (lo0) with the default wolfS
Trace output will be written to a file named `tracefile.txt`.
To decode a previously saved pcap file you will need to enter a few parameters.
### Offline Sniff Mode
The following table lists the accepted inputs in saved file mode.
Offline mode allows traffic to be decoded from a previously saved pcap file. To run the sniffer in offline mode, you will need to provide the application with some command line arguments, some of which are mandatory and some of which are optional
The following table lists the accepted inputs in offline mode.
Synopsis:
`snifftest dumpFile pemKey [server] [port] [password] [threads]`
`snifftest -pcap pcap_arg -key key_arg [-password password_arg] [-server server_arg] [-port port_arg] [-keylogfile keylogfile_arg] [-threads threads_arg]`
`snifftest` Options Summary:
```
Option Description Default Value
dumpFile A previously saved pcap file NA
pemKey The servers private key in PEM format NA
server The servers IP address (v4 or v6) 127.0.0.1
port The server port to sniff 443
password Private Key Password if required NA
threads The number of threads to run with 5
Option Description Default Value Mandatory
pcap_arg A previously saved pcap file NA Y
key_arg The servers private key in PEM format NA Y
password_arg Private Key Password if required NA N
server_arg The servers IP address (v4 or v6) 127.0.0.1 N
port_arg The server port to sniff 443 N
threads The number of threads to run with 5 N
keylogfile_arg Keylog file containing decryption secrets NA N
```
To decode a pcap file named test.pcap with a server key file called myKey.pem that was generated on the localhost with a server at port 443 just use:
`./snifftest test.pcap myKey.pem`
`./snifftest -pcap test.pcap -key myKey.pem`
If the server was on 10.0.1.2 and on port 12345 you could instead use:
`./snifftest test.pcap myKey.pem 10.0.1.2 12345`
`./snifftest -pcap test.pcap -key myKey.pem -server 10.0.1.2 -port 12345`
If the server was on localhost using IPv6 and on port 12345 you could instead use:
`./snifftest test.pcap myKey.pem ::1 12345`
`./snifftest -pcap test.pcap -key myKey.pem -server ::1 -port 12345`
If you wanted to use 15 threads to decode `test.pcap` and your key does not require a password, you could use a dummy password and run:
`./snifftest test.pcap myKey.pem 10.0.1.2 12345 pass 15`
`./snifftest -pcap test.pcap -key myKey.pem -server 10.0.1.2 -port 12345 -password pass -threads 15`
If the server exported its secrets in a [NSS keylog file](https://web.archive.org/web/20220531072242/https://firefox-source-docs.mozilla.org/security/nss/legacy/key_log_format/index.html)
named "sslkeylog.log", you could decrypt the traffic using:
`./snifftest -pcap test.pcap -key myKey.pem -server 10.0.1.2 -port 12345 -keylogfile /path/to/sslkeylog.log`
## API Usage
@ -164,7 +182,7 @@ Use the include `#include <wolfssl/sniffer.h>`.
void ssl_InitSniffer(void);
```
Initializes the wolfSSL sniffer for use and should be called once per application.
Initializes the wolfSSL sniffer for use and should be called once per application.
### ssl_FreeSniffer
@ -285,8 +303,8 @@ Return Values:
### ssl_SetEphemeralKey
```c
int ssl_SetEphemeralKey(const char* address, int port,
const char* keyFile, int typeKey,
int ssl_SetEphemeralKey(const char* address, int port,
const char* keyFile, int typeKey,
const char* password, char* error)
```
Creates a sniffer session based on the `serverAddress` and `port` inputs using ECC or DH static ephemeral key.
@ -300,6 +318,35 @@ Return Values:
* 0 on success
* -1 if a problem occurred, the string error will hold a message describing the problem
### ssl_LoadSecretsFromKeyLogFile
```c
int ssl_LoadSecretsFromKeyLogFile(const char* keylogfile, char* error)
```
Loads secrets to decrypt TLS traffic from a keylog file. Only sniffer servers registered with `ssl_createKeyLogSnifferServer()` will be able to decrypt using these secrets
This function requires that sniffer keylog file support (`WOLFSSL_SNIFFER_KEYLOGFILE`) is enabled in the build. Keylog file sniffing is only supported for TLS 1.2 traffic.
Return Values:
* 0 on success
* -1 if a problem occurred, the string error will hold a message describing the problem
### ssl_CreateKeyLogSnifferServer
```c
int ssl_CreateKeyLogSnifferServer(const char* address, int port, char* error)
```
Creates a sniffer session based on `serverAddress` and `port`, and uses secrets obtained from a keylog file to decrypt traffic. Keylog files should be loaded using `ssl_LoadSecretsFromKeyLogFile()`.
This function requires that sniffer keylog file support (`WOLFSSL_SNIFFER_KEYLOGFILE`) is enabled in the build. Keylog file sniffing is only supported for TLS 1.2 traffic.
Return Values:
* 0 on success
* -1 if a problem occurred, the string error will hold a message describing the problem
### ssl_DecodePacket
```c
@ -525,7 +572,7 @@ Return Values:
### ssl_SetWatchKey_buffer
```c
int ssl_SetWatchKey_buffer(void* vSniffer, const unsigned char* key,
int ssl_SetWatchKey_buffer(void* vSniffer, const unsigned char* key,
unsigned int keySz, int keyType, char* error);
```

View File

@ -144,6 +144,8 @@ enum {
#endif
#endif
#define DEFAULT_SERVER_IP "127.0.0.1"
#define DEFAULT_SERVER_PORT (443)
#ifdef WOLFSSL_SNIFFER_WATCH
static const byte rsaHash[] = {
@ -470,20 +472,12 @@ static void show_appinfo(void)
#ifdef WOLFSSL_STATIC_DH
"dh_static "
#endif
#ifdef WOLFSSL_SNIFFER_KEYLOGFILE
"ssl_keylog_file "
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
"\n\n"
);
}
static void show_usage(void)
{
printf("usage:\n");
printf("\t./snifftest\n");
printf("\t\tprompts for options\n");
#ifdef THREADED_SNIFFTEST
printf("\t./snifftest dump pemKey [server] [port] [password] [threads]\n");
#else
printf("\t./snifftest dump pemKey [server] [port] [password]\n");
#endif
}
typedef struct SnifferPacket {
byte* packet;
@ -955,7 +949,6 @@ int main(int argc, char** argv)
int ret = 0;
int hadBadPacket = 0;
int inum = 0;
int port = 0;
int saveFile = 0;
int i = 0, defDev = 0;
int packetNumber = 0;
@ -963,9 +956,13 @@ int main(int argc, char** argv)
char err[PCAP_ERRBUF_SIZE];
char filter[32];
const char *keyFilesSrc = NULL;
#ifdef WOLFSSL_SNIFFER_KEYLOGFILE
const char *sslKeyLogFile = NULL;
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
char keyFilesBuf[MAX_FILENAME_SZ];
char keyFilesUser[MAX_FILENAME_SZ];
const char *server = NULL;
const char *server = DEFAULT_SERVER_IP;
int port = DEFAULT_SERVER_PORT;
const char *sniName = NULL;
const char *passwd = NULL;
pcap_if_t *d;
@ -977,18 +974,13 @@ int main(int argc, char** argv)
workerThreadCount = 1;
#else
workerThreadCount = 5;
if (argc >= 7)
workerThreadCount = XATOI(argv[6]);
#endif
SnifferWorker workers[workerThreadCount];
int used[workerThreadCount];
#endif
show_appinfo();
signal(SIGINT, sig_handler);
#ifndef THREADED_SNIFFTEST
#ifndef _WIN32
ssl_InitSniffer(); /* dll load on Windows */
@ -1140,51 +1132,117 @@ int main(int argc, char** argv)
}
}
}
else if (argc >= 3) {
saveFile = 1;
pcap = pcap_open_offline(argv[1], err);
if (pcap == NULL) {
printf("pcap_open_offline failed %s\n", err);
ret = -1;
}
else {
/* defaults for server and port */
port = 443;
server = "127.0.0.1";
keyFilesSrc = argv[2];
else {
char *pcapFile = NULL;
if (argc >= 4)
server = argv[3];
if (argc >= 5)
port = XATOI(argv[4]);
if (argc >= 6)
passwd = argv[5];
ret = load_key(NULL, server, port, keyFilesSrc, passwd, err);
if (ret != 0) {
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-pcap") == 0 && i + 1 < argc) {
pcapFile = argv[++i];
}
else if (strcmp(argv[i], "-key") == 0 && i + 1 < argc) {
keyFilesSrc = argv[++i];
}
else if (strcmp(argv[i], "-server") == 0 && i + 1 < argc) {
server = argv[++i];
}
else if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
port = XATOI(argv[++i]);
}
else if (strcmp(argv[i], "-password") == 0 && i + 1 < argc) {
passwd = argv[++i];
}
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
else if (strcmp(argv[i], "-keylogfile") == 0 && i + 1 < argc) {
sslKeyLogFile = argv[++i];
}
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
#if defined(THREADED_SNIFFTEST)
else if (strcmp(argv[i], "-threads") == 0 && i + 1 < argc) {
workerThreadCount = XATOI(argv[++i]);
}
#endif /* THREADED_SNIFFTEST */
else {
fprintf(stderr, "Invalid option or missing argument: %s\n", argv[i]);
fprintf(stderr, "Usage: %s -pcap pcap_arg -key key_arg"
" [-password password_arg] [-server server_arg] [-port port_arg]"
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
" [-keylogfile keylogfile_arg]"
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
#if defined(THREADED_SNIFFTEST)
" [-threads threads_arg]"
#endif /* THREADED_SNIFFTEST */
"\n", argv[0]);
exit(EXIT_FAILURE);
}
}
if (!pcapFile) {
fprintf(stderr, "Error: -pcap option is required.\n");
exit(EXIT_FAILURE);
}
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
/* If we offer keylog support, then user must provide EITHER a pubkey
* OR a keylog file but NOT both */
if ((!keyFilesSrc && !sslKeyLogFile) || (keyFilesSrc && sslKeyLogFile)) {
fprintf(stderr, "Error: either -key OR -keylogfile option required but NOT both.\n");
exit(EXIT_FAILURE);
}
#else
if (!keyFilesSrc) {
fprintf(stderr, "Error: -key option is required.\n");
exit(EXIT_FAILURE);
}
#endif
saveFile = 1;
pcap = pcap_open_offline(pcapFile , err);
if (pcap == NULL) {
fprintf(stderr, "pcap_open_offline failed %s\n", err);
err_sys(err);
}
else {
#if defined(WOLFSSL_SNIFFER_KEYLOGFILE)
if (sslKeyLogFile != NULL) {
ret = ssl_LoadSecretsFromKeyLogFile(sslKeyLogFile, err);
if (ret != 0) {
fprintf(stderr, "ERROR=%d, unable to load secrets from keylog file\n",ret);
err_sys(err);
}
ret = ssl_CreateKeyLogSnifferServer(server, port, err);
if (ret != 0) {
fprintf(stderr, "ERROR=%d, unable to create keylog sniffer server\n",ret);
err_sys(err);
}
}
else
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
{
ret = load_key(NULL, server, port, keyFilesSrc, passwd, err);
if (ret != 0) {
fprintf(stderr, "Failed to load key\n");
err_sys(err);
}
}
/* Only let through TCP/IP packets */
ret = pcap_compile(pcap, &pcap_fp, "(ip6 or ip) and tcp", 0, 0);
if (ret != 0) {
printf("pcap_compile failed %s\n", pcap_geterr(pcap));
fprintf(stderr, "pcap_compile failed %s\n", pcap_geterr(pcap));
exit(EXIT_FAILURE);
}
ret = pcap_setfilter(pcap, &pcap_fp);
if (ret != 0) {
printf("pcap_setfilter failed %s\n", pcap_geterr(pcap));
fprintf(stderr, "pcap_setfilter failed %s\n", pcap_geterr(pcap));
exit(EXIT_FAILURE);
}
}
}
else {
show_usage();
exit(EXIT_FAILURE);
}
if (ret != 0)
err_sys(err);
@ -1193,6 +1251,9 @@ int main(int argc, char** argv)
frame = NULL_IF_FRAME_LEN;
#ifdef THREADED_SNIFFTEST
SnifferWorker workers[workerThreadCount];
int used[workerThreadCount];
XMEMSET(used, 0, sizeof(used));
XMEMSET(&workers, 0, sizeof(workers));

View File

@ -279,6 +279,10 @@
#include <wolfssl/wolfcrypt/hpke.h>
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE)
#include <wolfssl/sniffer.h>
#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */
#ifdef __cplusplus
extern "C" {
#endif
@ -2204,6 +2208,8 @@ WOLFSSL_LOCAL int ALPN_Select(WOLFSSL* ssl);
WOLFSSL_LOCAL int ChachaAEADEncrypt(WOLFSSL* ssl, byte* out, const byte* input,
word16 sz); /* needed by sniffer */
WOLFSSL_LOCAL int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
word16 sz); /* needed by sniffer */
#ifdef WOLFSSL_TLS13
WOLFSSL_LOCAL int DecryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
@ -5839,6 +5845,11 @@ struct WOLFSSL {
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
WOLFSSL_EchConfig* echConfigs;
#endif
#if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_KEYLOGFILE)
SSLSnifferSecretCb snifferSecretCb;
#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_KEYLOGFILE */
};
/*

View File

@ -313,6 +313,21 @@ SSL_SNIFFER_API int ssl_PollSniffer(WOLF_EVENT** events, int maxEvents,
#endif /* WOLFSSL_ASYNC_CRYPT */
#ifdef WOLFSSL_SNIFFER_KEYLOGFILE
WOLFSSL_API
SSL_SNIFFER_API int ssl_CreateKeyLogSnifferServer(const char* address,
int port,
char* error);
WOLFSSL_API
SSL_SNIFFER_API int ssl_LoadSecretsFromKeyLogFile(const char* keylogfile,
char* error);
typedef int (*SSLSnifferSecretCb)(unsigned char* client_random,
unsigned char* output_secret);
#endif /* WOLFSSL_SNIFFER_KEYLOGFILE */
#ifdef __cplusplus

View File

@ -142,6 +142,8 @@
#define SNIFFER_KEY_SETUP_STR 96
#define UNSUPPORTED_TLS_VER_STR 97
#define KEY_MISMATCH_STR 98
#define KEYLOG_FILE_INVALID 99
/* !!!! also add to msgTable in sniffer.c and .rc file !!!! */

View File

@ -1,5 +1,5 @@
STRINGTABLE
STRINGTABLE
{
1, "Out of Memory"
2, "New SSL Sniffer Server Registered"
@ -60,7 +60,7 @@ STRINGTABLE
48, "Wrong Protocol type"
49, "Packet Short for header processing"
50, "Got Unknown Record Type"
51, "Can't Open Trace File"
52, "Session in Fatal Error State"
53, "Partial SSL record received"
@ -72,7 +72,7 @@ STRINGTABLE
58, "Received an Overlap Duplicate Packet"
59, "Received an Overlap Reassembly Begin Duplicate Packet"
60, "Received an Overlap Reassembly End Duplicate Packet"
61, "Missed the Client Hello Entirely"
62, "Got Hello Request msg"
63, "Got Session Ticket msg"
@ -118,4 +118,6 @@ STRINGTABLE
96, "Setting up keys"
97, "Unsupported TLS Version"
98, "Server Client Key Mismatch"
99, "Invalid or missing keylog file"
}

View File

@ -1023,7 +1023,8 @@ typedef struct w64wrapper {
DYNAMIC_TYPE_SNIFFER_PB_BUFFER = 1003,
DYNAMIC_TYPE_SNIFFER_TICKET_ID = 1004,
DYNAMIC_TYPE_SNIFFER_NAMED_KEY = 1005,
DYNAMIC_TYPE_SNIFFER_KEY = 1006
DYNAMIC_TYPE_SNIFFER_KEY = 1006,
DYNAMIC_TYPE_SNIFFER_KEYLOG_NODE = 1007
};
/* max error buffer string size */