diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index d3ec20c7331..ed82f14879d 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -425,6 +425,19 @@ "--disable-sha512", "--disable-sha3", "--enable-aesgcm=small", "--enable-sp-math", "--enable-sp=smallec256", "--disable-sp-asm", "CPPFLAGS=-DNO_WOLFSSL_SERVER -DWOLFSSL_NO_TLS12 -DNO_SESSION_CACHE -DWOLFSSL_AES_NO_UNROLL -DUSE_SLOW_SHA256 -DWOLFSSL_NO_ASYNC_IO -DWOLFSSL_DTLS_ONLY"]}, +{"name": "no-tlsv12-ocspstapling", + "comment": "--disable-tlsv12 with the two feature axes that reach TLS 1.2-only code from a TLS 1.3 build: OCSP stapling, whose chain-status path calls ProcessCSR_ex, and the OpenSSL compatibility layer, which is where most of the tests that hard-code a TLS 1.2 method live. Nothing else covers the plain option - dtls13-client-minimal above reaches the define through CPPFLAGS and strips most of the crypto with it, and psk.yml's static-psk-lowresource-tls13 pairs it with --disable-asn.", + "configure": ["--disable-tlsv12", "--enable-ocspstapling", "--enable-opensslextra"]}, +{"name": "no-tlsv12-ocspstapling2", + "comment": "status_request_v2 without status_request, which is the one stapling shape where the TLS 1.3 chain-status path is not compiled: ProcessCSR_ex then has only its TLS 1.2 caller, so compiling TLS 1.2 out left it with none and -Wunused-function broke the build. no-tlsv12-ocspstapling above enables v1 and cannot reach it.", + "configure": ["--disable-tlsv12", "--enable-ocspstapling2"]}, +{"name": "no-tlsv12-dtls13", + "comment": "DTLS 1.3 with the TLS 1.2 handshake compiled out, both sides. dtls13-client-minimal above covers the same version pair but is client-only through NO_WOLFSSL_SERVER, so the DTLS 1.3 server path under WOLFSSL_NO_TLS12 has no other coverage. This is also the one combination configure still accepts once TLS 1.2 is gone: DTLS without --enable-dtls13 is rejected, because DTLS 1.2 is the TLS 1.2 handshake. Connection ID and session tickets are on because both pull in DTLS 1.2-only test and record-layer code that the version gating has to reach.", + "configure": ["--disable-tlsv12", "--enable-dtls", "--enable-dtls13", + "--enable-dtlscid", "--enable-session-ticket"]}, +{"name": "no-tlsv12-sniffer", + "comment": "The sniffer picks its master-secret derivation, resumption key derivation, Finished parsing and record decryption by version, and with WOLFSSL_NO_TLS12 only the TLS 1.3 halves of those are compiled in. sniffer-curves-enckeys keeps TLS 1.2 enabled, so it never builds them that way; scripts/sniffer-testsuite.test probes snifftest for the versions it was built with and runs the TLS 1.3 legs here.", + "configure": ["--enable-sniffer", "--disable-tlsv12"]}, {"name": "opensslextra-no-filesystem-no-bio", "minutes": 0.9, "configure": ["--enable-opensslextra", "--disable-filesystem", "CPPFLAGS=-DNO_BIO"]}, {"name": "no-examples-no-malloc", "minutes": 0.8, diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b48daa5a1e..9736e31f674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1913,17 +1913,17 @@ else() endif() endif() +# TLS (enabled by default; disable for wolfCrypt-only) +add_option("WOLFSSL_TLS" "Enable TLS (default: enabled)" "yes" "yes;no") +if(NOT WOLFSSL_TLS) + list(APPEND WOLFSSL_DEFINITIONS "-DNO_TLS") +endif() + # TLSv1.2 add_option("WOLFSSL_TLSV12" "Enable TLS versions 1.2 (default: enabled)" "yes" "yes;no") -if(NOT WOLFSSL_TLSV12) - list(APPEND WOLFSSL_DEFINITIONS - "-DWOLFSSL_NO_TLS12" - "-DNO_OLD_TLS") -endif() - # TODO: - TLSv1.0 # - SSLv3 # - Stack size @@ -2586,6 +2586,42 @@ if(WOLFSSL_SESSION_TICKET) "-DHAVE_SESSION_TICKET") endif() +if(NOT WOLFSSL_TLSV12) + # A wolfCrypt-only build has no handshake to be left without, so only a + # build that keeps TLS has to agree on which versions remain. + if(WOLFSSL_TLS) + # Compiling the pre-TLS-1.3 handshake out leaves the versions built on + # top of it with nothing to run on, so reject them the way configure does. + if(WOLFSSL_OLD_TLS) + message(FATAL_ERROR + "WOLFSSL_TLSV12=no cannot be combined with WOLFSSL_OLD_TLS=yes: " + "TLS 1.0 and 1.1 use the TLS 1.2 handshake.") + endif() + if(NOT WOLFSSL_TLS13) + message(FATAL_ERROR + "WOLFSSL_TLSV12=no needs WOLFSSL_TLS13=yes: no TLS version " + "would be left to negotiate.") + endif() + if(WOLFSSL_MCAST) + message(FATAL_ERROR + "WOLFSSL_TLSV12=no cannot be combined with WOLFSSL_MCAST: " + "multicast rides on DTLS 1.2 and its NULL cipher suite.") + endif() + if(WOLFSSL_DTLS AND NOT WOLFSSL_DTLS13) + message(FATAL_ERROR + "WOLFSSL_TLSV12=no with WOLFSSL_DTLS=yes needs " + "WOLFSSL_DTLS13=yes: DTLS 1.2 uses the TLS 1.2 handshake.") + endif() + endif() + # A TLS 1.3-only build without session tickets has no session to cache. + if(WOLFSSL_TLS13 AND NOT WOLFSSL_SESSION_TICKET) + list(APPEND WOLFSSL_DEFINITIONS "-DNO_SESSION_CACHE") + endif() + # NO_OLD_TLS is already in the list: the check above leaves + # WOLFSSL_OLD_TLS off, which is what adds it. + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_TLS12") +endif() + add_option("WOLFSSL_TICKET_NONCE_MALLOC" "Enable dynamic allocation of ticket nonces (default: disabled)" "no" "yes;no") @@ -3993,12 +4029,6 @@ if(NOT WOLFSSL_STATICMEMORY STREQUAL "no") endif() endif() -# TLS (enabled by default; disable for wolfCrypt-only) -add_option("WOLFSSL_TLS" "Enable TLS (default: enabled)" "yes" "yes;no") -if(NOT WOLFSSL_TLS) - list(APPEND WOLFSSL_DEFINITIONS "-DNO_TLS") -endif() - # SHA-256 (enabled by default) add_option("WOLFSSL_SHA256" "Enable SHA-256 (default: enabled)" "yes" "yes;no") if(NOT WOLFSSL_SHA256) diff --git a/ChangeLog.md b/ChangeLog.md index f3107ffb0bf..e0b6c62a64a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,29 @@ ## Behavioral Changes +* **Behavioral change (`--disable-tlsv12` compiles TLS 1.2 out)**: the option + set the summary line and a few derived settings, but never defined + `WOLFSSL_NO_TLS12`, so the whole TLS 1.2 implementation was still built and a + peer could still negotiate it. The only place the define was added is a FIPS + bundle, whose condition skips it when the user asked for the option, so no + autotools configuration reached it; the CMake `WOLFSSL_TLSV12=no` path was + unaffected. The option now defines it, which is what the bundles that turn + the version off, `--enable-tinytls13` among them, have been documented as + doing. A build that passes `--disable-tlsv12` and still expects to negotiate + TLS 1.2 has to stop passing it. Because the pre-TLS-1.3 handshake is now + compiled out, configure rejects the combinations that depend on it: + `--enable-oldtls`, which builds on the TLS 1.2 handshake; TLS 1.3 off, which + would leave no version to negotiate; and DTLS without DTLS 1.3, for the same + reason on the datagram side; and multicast, which rides on DTLS 1.2 and its + NULL cipher suite. CMake rejects the same four for `-DWOLFSSL_TLSV12=no`; + it used to accept `-DWOLFSSL_OLD_TLS=yes` beside it and define `NO_OLD_TLS` + anyway, so the reported option and the build disagreed. The sniffer, the + examples and the test suite pick their code paths by version where they used + to assume TLS 1.2 was present, so `--disable-tlsv12`, that with + `--enable-ocspstapling --enable-opensslextra`, `--enable-sniffer + --disable-tlsv12` and `--enable-dtls --enable-dtls13 --enable-dtlscid + --enable-session-ticket --disable-tlsv12` now build and test cleanly. + * **Behavioral change (`wc_PufReadSram` health tests the raw SRAM readout)**: the raw readout is now health tested before the context accepts it, and a readout that cannot be SRAM power-on noise is rejected with `PUF_READ_E` diff --git a/cmake/options.h.in b/cmake/options.h.in index 0dba372e988..a411feb55c0 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -233,6 +233,8 @@ extern "C" { #cmakedefine NO_RC4 #undef NO_RSA #cmakedefine NO_RSA +#undef NO_SESSION_CACHE +#cmakedefine NO_SESSION_CACHE #undef NO_SESSION_CACHE_REF #cmakedefine NO_SESSION_CACHE_REF #undef NO_SHA diff --git a/configure.ac b/configure.ac index 5e303026b33..6a9cea2500e 100644 --- a/configure.ac +++ b/configure.ac @@ -9424,6 +9424,18 @@ then AM_CFLAGS="$AM_CFLAGS -DNO_SESSION_CACHE" fi +# Compile out TLS 1.2 itself, not just its derived settings. Runs after the +# bundles that turn the version off, so --enable-tinytls13 and friends land +# here too. The check keeps the option idempotent for the FIPS bundles that +# add the define themselves. +if test "$ENABLED_TLSV12" = "no" +then + case "$AM_CFLAGS" in + *-DWOLFSSL_NO_TLS12*) ;; + *) AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_TLS12" ;; + esac +fi + # PKCS7 AC_ARG_ENABLE([pkcs7], [AS_HELP_STRING([--enable-pkcs7],[Enable PKCS7 (default: disabled)])], @@ -13106,6 +13118,30 @@ AS_IF([(test "x$ENABLED_DTLS" = "xno") && \ [AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS" ENABLED_DTLS=yes]) +# --disable-tlsv12 compiles the pre-TLS-1.3 handshake out, so the versions and +# features built on top of it cannot be left on, and something has to be left +# to negotiate. Runs here because old TLS, DTLS and the version flags are all +# final by this point. +if test "$ENABLED_TLS" != "no" && test "$ENABLED_TLSV12" = "no" +then + if test "$ENABLED_OLD_TLS" = "yes" + then + AC_MSG_ERROR([cannot disable tlsv12 while old TLS is enabled: TLS 1.0 and 1.1 use the TLS 1.2 handshake. Add --disable-oldtls, and drop --enable-tlsv10 or --enable-sslv3 if passed, since they turn it back on.]) + fi + if test "$ENABLED_TLS13" = "no" + then + AC_MSG_ERROR([cannot disable tlsv12 with tls13 disabled: no TLS version would be left to negotiate.]) + fi + if test "$ENABLED_MCAST" = "yes" + then + AC_MSG_ERROR([cannot disable tlsv12 with mcast enabled: multicast rides on DTLS 1.2 and its NULL cipher suite. Add --disable-mcast.]) + fi + if test "$ENABLED_DTLS" = "yes" && test "$ENABLED_DTLS13" = "no" + then + AC_MSG_ERROR([cannot disable tlsv12 while DTLS is enabled without dtls13: DTLS 1.2 uses the TLS 1.2 handshake. Add --enable-dtls13, or drop what enables DTLS (sctp, srtp, mcast and strongswan turn it on too).]) + fi +fi + # Multicast requires the null cipher AS_IF([test "x$ENABLED_NULL_CIPHER" = "xno" && \ test "x$ENABLED_MCAST" = "xyes"], diff --git a/examples/benchmark/dtls_bench.c b/examples/benchmark/dtls_bench.c index 5df106a1ad2..21b22f480cb 100644 --- a/examples/benchmark/dtls_bench.c +++ b/examples/benchmark/dtls_bench.c @@ -124,6 +124,7 @@ static double now_sec(void) return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; } +#ifndef NO_WOLFSSL_CLIENT /* Post-handshake send sink. Pretends every byte was transmitted but does * nothing; the kernel's UDP/IP path never runs. Used via -z to measure the * pure wolfSSL encrypt-and-frame ceiling, free of any I/O cost. */ @@ -132,6 +133,7 @@ static int dtls_bench_sink_send(WOLFSSL* ssl, char* buf, int sz, void* ctx) (void)ssl; (void)buf; (void)ctx; return sz; } +#endif /* !NO_WOLFSSL_CLIENT */ static void print_stats(const char* dir, long long bytes, double sec) { @@ -338,6 +340,9 @@ static int bind_to_iface(int fd, const char* ifname) #endif } +/* Only the DTLS legs below need these; the plain-UDP baseline does not. */ +#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT) + static WOLFSSL_METHOD* pick_method(int version, int isServer) { if (version == 13) { @@ -404,6 +409,8 @@ static int set_mtu(WOLFSSL* ssl, int mtu) #endif } +#endif /* !NO_WOLFSSL_SERVER || !NO_WOLFSSL_CLIENT */ + /* ----- Plain-UDP baseline (-n) ----- */ static int udp_server(const cfg_t* c) @@ -523,6 +530,8 @@ static int udp_client(const cfg_t* c) /* ----- DTLS server ----- */ +#ifndef NO_WOLFSSL_SERVER + static int dtls_server(const cfg_t* c) { int ret = 1; @@ -658,8 +667,12 @@ static int dtls_server(const cfg_t* c) return ret; } +#endif /* !NO_WOLFSSL_SERVER */ + /* ----- DTLS client ----- */ +#ifndef NO_WOLFSSL_CLIENT + static int dtls_client(const cfg_t* c) { int ret = 1; @@ -797,6 +810,8 @@ static int dtls_client(const cfg_t* c) return ret; } +#endif /* !NO_WOLFSSL_CLIENT */ + int main(int argc, char** argv) { cfg_t c; @@ -807,7 +822,20 @@ int main(int argc, char** argv) if (c.plainUdp) { return c.isServer ? udp_server(&c) : udp_client(&c); } - return c.isServer ? dtls_server(&c) : dtls_client(&c); + if (c.isServer) { +#ifndef NO_WOLFSSL_SERVER + return dtls_server(&c); +#else + fprintf(stderr, "DTLS server not compiled in!\n"); + return 1; +#endif + } +#ifndef NO_WOLFSSL_CLIENT + return dtls_client(&c); +#else + fprintf(stderr, "DTLS client not compiled in!\n"); + return 1; +#endif } #else /* DTLS_BENCH_ENABLED */ diff --git a/examples/client/client.c b/examples/client/client.c index 0887423d82b..16c40612290 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -2470,6 +2470,12 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) ((func_args*)args)->return_code = -1; /* error state */ +#ifdef HAVE_PK_CALLBACKS + /* The ECC callbacks read keyGenCnt whether or not certificates are + * compiled in, so this cannot sit inside the NO_CERTS block below. */ + XMEMSET(&pkCbInfo, 0, sizeof(pkCbInfo)); +#endif + #ifndef NO_RSA verifyCert = caCertFile; ourCert = cliCertFile; @@ -3418,7 +3424,8 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) method = wolfDTLSv1_3_client_method_ex; break; #endif /* WOLFSSL_DTLS13 */ - #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) + #if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)) && \ + !defined(WOLFSSL_NO_TLS12) case -3: method = wolfDTLSv1_2_method_ex; break; @@ -5116,6 +5123,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) exit: +#ifdef HAVE_PK_CALLBACKS + CleanupPkCallbackContexts(&pkCbInfo); +#endif + #ifdef WOLFSSL_WOLFSENTRY_HOOKS wolfsentry_ret = wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(&wolfsentry, NULL)); diff --git a/examples/echoclient/echoclient.c b/examples/echoclient/echoclient.c index 9e4267484bb..c3a9c5e08b1 100644 --- a/examples/echoclient/echoclient.c +++ b/examples/echoclient/echoclient.c @@ -134,7 +134,8 @@ void echoclient_test(void* args) #endif #if !defined(NO_TLS) - #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER) && \ + !defined(WOLFSSL_NO_TLS12) method = wolfTLSv1_2_client_method(); #else method = wolfSSLv23_client_method(); @@ -174,10 +175,13 @@ void echoclient_test(void* args) err_sys("can't load ca buffer"); #endif -#if defined(WOLFSSL_SNIFFER) +#if defined(WOLFSSL_SNIFFER) && !defined(WOLFSSL_NO_TLS12) /* Only set if not running testsuite */ if (XSTRSTR(argv[0], "testsuite") == NULL) { - /* don't use EDH, can't sniff tmp keys */ + /* don't use EDH, can't sniff tmp keys. A TLS 1.3 sniffer needs a key + * log file or static ephemeral keys instead, so this static RSA suite + * is only pinned where TLS 1.2 exists. Advisory: a build without the + * suite's ciphers keeps the default list. */ SSL_CTX_set_cipher_list(ctx, "AES256-SHA"); } #endif diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 65985b77350..b73ed3b15a7 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -151,7 +151,8 @@ THREAD_RETURN WOLFSSL_THREAD echoserver_test(void* args) tcp_listen(&sockfd, &port, useAnyAddr, 0, 0); #if !defined(NO_TLS) - #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER) + #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER) && \ + !defined(WOLFSSL_NO_TLS12) method = wolfTLSv1_2_server_method(); #else method = wolfSSLv23_server_method(); @@ -264,10 +265,13 @@ THREAD_RETURN WOLFSSL_THREAD echoserver_test(void* args) } #endif -#if defined(WOLFSSL_SNIFFER) +#if defined(WOLFSSL_SNIFFER) && !defined(WOLFSSL_NO_TLS12) /* Only set if not running testsuite */ if (XSTRSTR(argv[0], "testsuite") == NULL) { - /* don't use EDH, can't sniff tmp keys */ + /* don't use EDH, can't sniff tmp keys. A TLS 1.3 sniffer needs a key + * log file or static ephemeral keys instead, so this static RSA suite + * is only pinned where TLS 1.2 exists. Advisory: a build without the + * suite's ciphers keeps the default list. */ wolfSSL_CTX_set_cipher_list(ctx, "AES256-SHA"); } #endif diff --git a/examples/server/server.c b/examples/server/server.c index aebd9a9c524..41ada9fc131 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -1878,6 +1878,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) ((func_args*)args)->return_code = -1; /* error state */ +#ifdef HAVE_PK_CALLBACKS + /* The ECC callbacks read keyGenCnt whether or not certificates are + * compiled in, so this cannot sit inside the NO_CERTS block below. */ + XMEMSET(&pkCbInfo, 0, sizeof(pkCbInfo)); +#endif + #ifndef NO_RSA verifyCert = cliCertFile; ourCert = svrCertFile; @@ -2784,7 +2790,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) method = wolfDTLSv1_3_server_method_ex; break; #endif - #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) + #if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)) && \ + !defined(WOLFSSL_NO_TLS12) case -3: method = wolfDTLSv1_2_method_ex; break; @@ -4195,6 +4202,10 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) exit: +#ifdef HAVE_PK_CALLBACKS + CleanupPkCallbackContexts(&pkCbInfo); +#endif + #ifdef WOLFSSL_WOLFSENTRY_HOOKS wolfsentry_ret = wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(&wolfsentry, NULL)); diff --git a/scripts/pkcallbacks.test b/scripts/pkcallbacks.test index 753bf7d621c..b350356e0ca 100755 --- a/scripts/pkcallbacks.test +++ b/scripts/pkcallbacks.test @@ -105,15 +105,24 @@ restore_file_system() { } trap restore_file_system EXIT +# $1 is the version flag pair to run with, empty for the build's default. run_test() { - echo -e "\nStarting example server for pkcallbacks test...\n" + version_args="$1" + + echo -e "\nStarting example server for pkcallbacks test ${version_args:-default}...\n" + + # Back to the port zero hack for every run: the previous run left its + # allocated port here, and reusing a now-known port is exactly what the + # hack exists to avoid when another make check is running alongside. + pk_port=0 remove_ready_file # starts the server on pk_port, -R generates ready file to be used as a # mutex lock, -P does pkcallbacks. We capture the processid # into the variable server_pid - $TIMEOUT_KILL_2M ./examples/server/server -P -R "$ready_file" -p $pk_port & + $TIMEOUT_KILL_2M ./examples/server/server -P -R "$ready_file" -p $pk_port \ + $version_args & server_pid=$! counter=0 @@ -137,12 +146,13 @@ run_test() { pk_port=`cat "$ready_file"` # starts client on pk_port with pkcallbacks, captures the output from client - capture_out=$(./examples/client/client -P -p $pk_port 2>&1) + capture_out=$(./examples/client/client -P -p $pk_port $version_args 2>&1) client_result=$? if [ $client_result != 0 ] then echo -e "client failed!" + echo "$capture_out" do_cleanup exit 1 fi @@ -161,8 +171,19 @@ run_test() { ######### begin program ######### -# run the test -run_test +# The build's default version, then each version explicitly. Running only the +# default used to hide that the ECC PK callbacks could not complete a TLS v1.3 +# handshake: the default is TLS v1.2 wherever it is compiled in, so the 1.3 +# path was never taken here. +run_test "" + +if ./examples/client/client -V | grep -q 3; then + run_test "-v 3" +fi + +if ./examples/client/client -V | grep -q 4; then + run_test "-v 4" +fi # If we get to this, success echo "Success!" diff --git a/src/internal.c b/src/internal.c index e32fe421eb7..4d238584936 100644 --- a/src/internal.c +++ b/src/internal.c @@ -15777,8 +15777,15 @@ int CopyDecodedAcertToX509(WOLFSSL_X509_ACERT* x509, DecodedAcert* dAcert) } #endif /* WOLFSSL_ACERT */ +/* ProcessCSR() below needs this block under TLS 1.2, and TLS 1.3 reaches + * ProcessCSR_ex() from ProcessPeerCertsChainOCSPStatusCheck(), which is built + * for status_request only. Naming both keeps a status_request_v2-only build + * with TLS 1.2 compiled out from having no caller left. */ #if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ - defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && !defined(WOLFSSL_NO_TLS12) + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && \ + (!defined(WOLFSSL_NO_TLS12) || \ + (defined(HAVE_OCSP) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST))) #if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) #ifndef NO_WOLFSSL_SERVER static int CsrDoStatusVerifyCb(WOLFSSL* ssl, byte* input, word32 inputSz, word32 idx, @@ -15803,8 +15810,10 @@ static int CsrDoStatusVerifyCb(WOLFSSL* ssl, byte* input, word32 inputSz, word32 } return ret; } -#endif +#endif /* !NO_WOLFSSL_SERVER */ +/* Parses one certificate_status message. TLS 1.3 reads the per-certificate + * entries of the chain through this, so it is not tied to TLS 1.2. */ static int ProcessCSR_ex(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 status_length, int idx) { @@ -15927,11 +15936,13 @@ static int ProcessCSR_ex(WOLFSSL* ssl, byte* input, word32* inOutIdx, return ret; } +#ifndef WOLFSSL_NO_TLS12 static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 status_length) { return ProcessCSR_ex(ssl, input, inOutIdx, status_length, 0); } +#endif /* !WOLFSSL_NO_TLS12 */ #endif #endif @@ -26609,7 +26620,7 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input, const byte* encInput = NULL; #endif -#ifdef WOLFSSL_DTLS_CID +#if defined(WOLFSSL_DTLS_CID) && !defined(WOLFSSL_NO_TLS12) byte cidSz = 0; #endif @@ -27534,7 +27545,9 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest, /* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED is an * explicit positive assertion of revocation and must not be ignored. * OCSP_NO_URL just means there is no responder to staple from; - * stapling stays best-effort. */ + * stapling stays best-effort. OCSP_INVALID_STATUS is not suppressed + * here the way it is for the chain: this is the leaf the peer asked + * about, so failing to reach its responder fails the handshake. */ if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) || ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) || ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) { @@ -28581,10 +28594,15 @@ int SendCertificateStatus(WOLFSSL* ssl) * OCSP_CERT_REVOKED is an explicit positive * assertion of revocation and must not be * ignored. OCSP_NO_URL just means there is no - * responder to staple from; stapling stays - * best-effort. */ + * responder to staple from, and + * OCSP_INVALID_STATUS covers every other result + * the stapler could not turn into a usable + * response - an unreachable responder, or a + * cached entry with no raw response kept; + * stapling stays best-effort. */ if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) || ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) || + ret == WC_NO_ERR_TRACE(OCSP_INVALID_STATUS) || ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) { ret = 0; } @@ -28610,10 +28628,14 @@ int SendCertificateStatus(WOLFSSL* ssl) /* Suppressing soft-fail responder errors. * OCSP_CERT_REVOKED is an explicit positive assertion of * revocation and must not be ignored. OCSP_NO_URL just - * means there is no responder to staple from; stapling - * stays best-effort. */ + * means there is no responder to staple from, and + * OCSP_INVALID_STATUS covers every other result the + * stapler could not turn into a usable response - an + * unreachable responder, or a cached entry with no raw + * response kept; stapling stays best-effort. */ if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) || ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) || + ret == WC_NO_ERR_TRACE(OCSP_INVALID_STATUS) || ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) { ret = 0; } diff --git a/src/sniffer.c b/src/sniffer.c index 989e85697ee..51110647f0a 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -542,7 +542,7 @@ typedef struct Flags { #endif byte gotFinished; /* processed finished */ byte secRenegEn; /* secure renegotiation enabled */ -#if !defined(HAVE_ENCRYPT_THEN_MAC) || defined(WOLFSSL_AEAD_ONLY) +#if !defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) byte etmUnsupported; /* peer negotiated RFC 7366, we cannot */ #endif #ifdef WOLFSSL_ASYNC_CRYPT @@ -2638,7 +2638,7 @@ static void FreeSetupKeysArgs(WOLFSSL* ssl, void* pArgs) } /* Process Keys */ -#if !defined(HAVE_ENCRYPT_THEN_MAC) || defined(WOLFSSL_AEAD_ONLY) +#if !defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) /* RFC 7366 only covers block ciphers and a peer must not negotiate it for an * AEAD or stream suite, so a session that asked for it is still readable here * unless the negotiated suite turns out to be a block cipher. @@ -3396,7 +3396,7 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, ret = WOLFSSL_FATAL_ERROR; break; } - #if !defined(HAVE_ENCRYPT_THEN_MAC) || defined(WOLFSSL_AEAD_ONLY) + #if !defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) if (CheckEncryptThenMac(session, error) != 0) { ret = WOLFSSL_FATAL_ERROR; break; } @@ -3424,10 +3424,20 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, else #endif /* WOLFSSL_TLS13 */ { +#ifndef WOLFSSL_NO_TLS12 ret = MakeMasterSecret(session->sslServer); ret += MakeMasterSecret(session->sslClient); ret += SetKeysSide(session->sslServer, ENCRYPT_AND_DECRYPT_SIDE); ret += SetKeysSide(session->sslClient, ENCRYPT_AND_DECRYPT_SIDE); +#else + /* No master secret is computed here, so installing cipher state + * would be wrong. */ + SetError(UNSUPPORTED_TLS_VER_STR, error, session, + FATAL_ERROR_STATE); + session->verboseErr = 1; + ret = WOLFSSL_FATAL_ERROR; + break; +#endif } if (ret != 0) { SetError(BAD_DERIVE_STR, error, session, FATAL_ERROR_STATE); @@ -3850,7 +3860,7 @@ static int DoResume(SnifferSession* session, char* error) return WOLFSSL_FATAL_ERROR; } -#if !defined(HAVE_ENCRYPT_THEN_MAC) || defined(WOLFSSL_AEAD_ONLY) +#if !defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) if (CheckEncryptThenMac(session, error) != 0) return WOLFSSL_FATAL_ERROR; #endif @@ -3875,6 +3885,7 @@ static int DoResume(SnifferSession* session, char* error) else #endif { +#ifndef WOLFSSL_NO_TLS12 if (IsTLS(session->sslServer)) { ret = DeriveTlsKeys(session->sslServer); ret += DeriveTlsKeys(session->sslClient); @@ -3887,6 +3898,12 @@ static int DoResume(SnifferSession* session, char* error) } ret += SetKeysSide(session->sslServer, ENCRYPT_AND_DECRYPT_SIDE); ret += SetKeysSide(session->sslClient, ENCRYPT_AND_DECRYPT_SIDE); +#else + /* No keys were derived, so installing cipher state would be wrong. */ + SetError(UNSUPPORTED_TLS_VER_STR, error, session, FATAL_ERROR_STATE); + session->verboseErr = 1; + return WOLFSSL_FATAL_ERROR; +#endif } if (ret != 0) { @@ -4010,7 +4027,7 @@ static int ProcessServerHello(int msgSz, const byte* input, int* sslBytes, #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) session->sslServer->options.encThenMac = 0; session->sslClient->options.encThenMac = 0; -#else +#elif !defined(WOLFSSL_AEAD_ONLY) session->flags.etmUnsupported = 0; #endif @@ -4138,7 +4155,7 @@ static int ProcessServerHello(int msgSz, const byte* input, int* sslBytes, session->sslServer->options.encThenMac = 1; session->sslClient->options.encThenMac = 1; break; - #else + #elif !defined(WOLFSSL_AEAD_ONLY) case EXT_ENCRYPT_THEN_MAC: /* The session negotiated RFC 7366, but this build cannot * strip the MAC ahead of decryption. Only a block cipher @@ -4786,8 +4803,14 @@ static int ProcessFinished(const byte* input, int size, int* sslBytes, else #endif { +#ifndef WOLFSSL_NO_TLS12 ret = DoFinished(ssl, input, &inOutIdx, (word32)size, (word32)*sslBytes, SNIFF); +#else + SetError(UNSUPPORTED_TLS_VER_STR, error, session, FATAL_ERROR_STATE); + session->verboseErr = 1; + return WOLFSSL_FATAL_ERROR; +#endif } *sslBytes -= (int)inOutIdx; @@ -5132,6 +5155,9 @@ static int DoHandShake(const byte* input, int* sslBytes, /* For ciphers that use AEAD use the encrypt routine to * bypass the auth tag checking */ +/* The record layout below TLS 1.3 carries an explicit IV and its own + * additional data, so this path exists only where TLS 1.2 does. */ +#ifndef WOLFSSL_NO_TLS12 static int DecryptDo(WOLFSSL* ssl, byte* plain, const byte* input, word16 sz) { @@ -5384,13 +5410,16 @@ static int DecryptTls(WOLFSSL* ssl, byte* plain, const byte* input, return ret; } +#endif /* !WOLFSSL_NO_TLS12 */ /* Decrypt input message into output, adjust output steam if needed */ static const byte* DecryptMessage(WOLFSSL* ssl, const byte* input, word32 sz, byte* output, int* error, int* advance, RecordLayerHeader* rh) { +#ifndef WOLFSSL_AEAD_ONLY int ivExtra = 0; +#endif int ret; word32 macExtra = 0; @@ -5416,8 +5445,13 @@ static const byte* DecryptMessage(WOLFSSL* ssl, const byte* input, word32 sz, else #endif { +#ifndef WOLFSSL_NO_TLS12 XMEMCPY(&ssl->curRL, rh, RECORD_HEADER_SZ); ret = DecryptTls(ssl, output, input, sz - macExtra); +#else + *error = VERSION_ERROR; + return NULL; +#endif } #ifdef WOLFSSL_ASYNC_CRYPT /* for async the symmetric operations are blocking */ @@ -5438,11 +5472,13 @@ static const byte* DecryptMessage(WOLFSSL* ssl, const byte* input, word32 sz, ssl->curSize = sz; ssl->keys.encryptSz = sz; +#ifndef WOLFSSL_AEAD_ONLY if (ssl->options.tls1_1 && ssl->specs.cipher_type == block) { output += ssl->specs.block_size; /* go past TLSv1.1 IV */ ivExtra = ssl->specs.block_size; *advance = ssl->specs.block_size; } +#endif if (ssl->specs.cipher_type == aead) { *advance = ssl->specs.aead_mac_size; @@ -5453,6 +5489,7 @@ static const byte* DecryptMessage(WOLFSSL* ssl, const byte* input, word32 sz, else ssl->keys.padSz = ssl->specs.hash_size; +#ifndef WOLFSSL_AEAD_ONLY if (ssl->specs.cipher_type == block) { /* last pad bytes indicates length */ word32 pad = 0; @@ -5462,6 +5499,7 @@ static const byte* DecryptMessage(WOLFSSL* ssl, const byte* input, word32 sz, } ssl->keys.padSz += pad; } +#endif #ifdef WOLFSSL_TLS13 if (IsAtLeastTLSv1_3(ssl->version)) { @@ -6313,6 +6351,7 @@ static int FindNextRecordInAssembly(SnifferSession* session, return 0; } +#ifndef WOLFSSL_AEAD_ONLY else if (ssl->specs.cipher_type == block) { int ivPos = (int)(curr->end - curr->begin - ssl->specs.block_size + 1); @@ -6329,6 +6368,7 @@ static int FindNextRecordInAssembly(SnifferSession* session, #endif } } +#endif /* !WOLFSSL_AEAD_ONLY */ Trace(DROPPING_LOST_FRAG_STR); #ifdef WOLFSSL_SNIFFER_STATS diff --git a/src/tls.c b/src/tls.c index f600e08b10e..49b4e80661e 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3745,9 +3745,13 @@ int ProcessChainOCSPRequest(WOLFSSL* ssl) /* Suppressing soft-fail responder errors. OCSP_CERT_REVOKED * is an explicit positive assertion of revocation and must * not be ignored. OCSP_NO_URL just means there is no - * responder to staple from; stapling stays best-effort. */ + * responder to staple from, and OCSP_INVALID_STATUS covers + * every other result the stapler could not turn into a usable + * response - an unreachable responder, or a cached entry with + * no raw response kept; stapling stays best-effort. */ if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) || ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL) || + ret == WC_NO_ERR_TRACE(OCSP_INVALID_STATUS) || ret == WC_NO_ERR_TRACE(OCSP_NO_URL)) { ret = 0; } diff --git a/tests/api.c b/tests/api.c index 3618a957733..845910c1279 100644 --- a/tests/api.c +++ b/tests/api.c @@ -6872,7 +6872,7 @@ int test_wolfSSL_client_server_nofail_memio(test_ssl_cbf* client_cb, #ifdef HAVE_IO_TESTS_DEPENDENCIES #ifdef WOLFSSL_SESSION_EXPORT -#ifdef WOLFSSL_DTLS +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) /* set up function for sending session information */ static int test_export(WOLFSSL* inSsl, byte* buf, word32 sz, void* userCtx) { @@ -8085,7 +8085,8 @@ THREAD_RETURN WOLFSSL_THREAD run_wolfssl_server(void* args) #ifdef WOLFSSL_ENCRYPTED_KEYS wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); #endif -#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS) +#if defined(WOLFSSL_SESSION_EXPORT) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) if (callbacks->method == wolfDTLSv1_2_server_method) { if (wolfSSL_CTX_dtls_set_export(ctx, test_export) != WOLFSSL_SUCCESS) goto cleanup; @@ -10052,7 +10053,8 @@ static int test_wolfSSL_UseMaxFragment(void) wolfSSL_CTX_free(ctx); #if defined(OPENSSL_EXTRA) && defined(HAVE_MAX_FRAGMENT) && \ - defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) /* check negotiated max fragment size */ { WOLFSSL *ssl_c = NULL; @@ -12828,7 +12830,7 @@ static int test_wolfSSL_mcast(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; byte preMasterSecret[512]; @@ -12857,7 +12859,7 @@ static int test_wolfSSL_mcast(void) wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); #endif /* WOLFSSL_DTLS && WOLFSSL_MULTICAST && (WOLFSSL_TLS13 || - * WOLFSSL_SNIFFER) */ + * WOLFSSL_SNIFFER) && !NO_WOLFSSL_CLIENT && !WOLFSSL_NO_TLS12 */ return EXPECT_RESULT(); } @@ -15431,7 +15433,8 @@ static int test_wolfSSL_set1_host(void) #if defined(OPENSSL_ALL) && !defined(NO_RSA) && !defined(NO_CERTS) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) + defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) && \ + !defined(WOLFSSL_NO_TLS12) static int test_wolfSSL_get_client_ciphers_ctx_ready(WOLFSSL_CTX* ctx) { EXPECT_DECLS; @@ -15474,7 +15477,8 @@ static int test_wolfSSL_get_client_ciphers(void) EXPECT_DECLS; #if defined(OPENSSL_ALL) && !defined(NO_RSA) && !defined(NO_CERTS) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) + defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) && \ + !defined(WOLFSSL_NO_TLS12) test_ssl_cbf server_cb; test_ssl_cbf client_cb; @@ -15553,7 +15557,8 @@ static int test_wolfSSL_CTX_set_client_CA_list(void) ExpectIntEQ(sk_X509_NAME_find(names, name), i); } -#if !defined(SINGLE_THREADED) && defined(SESSION_CERTS) +#if !defined(SINGLE_THREADED) && defined(SESSION_CERTS) && \ + !defined(WOLFSSL_NO_TLS12) { tcp_ready ready; func_args server_args; @@ -18721,6 +18726,7 @@ static int test_wolfSSL_Tls13_ECH_tamper_client(void) #if defined(HAVE_IO_TESTS_DEPENDENCIES) && \ defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && \ defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) +#ifndef WOLFSSL_NO_TLS12 static int post_auth_version_cb(WOLFSSL* ssl) { EXPECT_DECLS; @@ -18749,6 +18755,7 @@ static int post_auth_version_client_cb(WOLFSSL* ssl) #endif return EXPECT_RESULT(); } +#endif /* !WOLFSSL_NO_TLS12 */ static int post_auth_cb(WOLFSSL* ssl) { @@ -18784,6 +18791,7 @@ static int test_wolfSSL_Tls13_postauth(void) test_ssl_cbf server_cbf; test_ssl_cbf client_cbf; +#ifndef WOLFSSL_NO_TLS12 /* test version failure doing post auth with TLS 1.2 connection */ XMEMSET(&server_cbf, 0, sizeof(server_cbf)); XMEMSET(&client_cbf, 0, sizeof(client_cbf)); @@ -18795,6 +18803,7 @@ static int test_wolfSSL_Tls13_postauth(void) ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cbf, &server_cbf, NULL), TEST_SUCCESS); +#endif /* !WOLFSSL_NO_TLS12 */ /* tests on post auth with TLS 1.3 */ XMEMSET(&server_cbf, 0, sizeof(server_cbf)); @@ -24029,7 +24038,8 @@ static int test_wolfSSL_OPENSSL_hexstr2buf(void) static int test_wolfSSL_sk_CIPHER_description(void) { EXPECT_DECLS; -#if !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) +#if !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(WOLFSSL_NO_TLS12) const long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION; int i; int numCiphers = 0; @@ -37114,7 +37124,8 @@ static int test_short_session_id(void) #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_SECURE_RENEGOTIATION) + defined(HAVE_IO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) static WOLFSSL_SESSION* test_wolfSSL_SCR_after_resumption_session = NULL; @@ -37735,7 +37746,17 @@ static int test_revoked_loaded_int_cert(void) -#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* The parameter table in test_self_signed_stapling() must not come out empty: + * status_request_v2 is a TLS v1.2-only extension, and TLS v1.3 can only be + * exercised through status_request v1, so a build that has just one of the two + * with the matching version compiled out has nothing left to run. */ +#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + ((defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST)) || \ + (!defined(WOLFSSL_NO_TLS12) && \ + (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)))) +#define TEST_SELF_SIGNED_STAPLING + #ifdef HAVE_CERTIFICATE_STATUS_REQUEST static int test_self_signed_stapling_client_v1_ctx_ready(WOLFSSL_CTX* ctx) { @@ -37747,7 +37768,7 @@ static int test_self_signed_stapling_client_v1_ctx_ready(WOLFSSL_CTX* ctx) } #endif -#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(WOLFSSL_NO_TLS12) static int test_self_signed_stapling_client_v2_ctx_ready(WOLFSSL_CTX* ctx) { EXPECT_DECLS; @@ -37767,23 +37788,18 @@ static int test_self_signed_stapling_client_v2_multi_ctx_ready(WOLFSSL_CTX* ctx) } #endif -#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ - || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) static int test_self_signed_stapling_server_ctx_ready(WOLFSSL_CTX* ctx) { EXPECT_DECLS; ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(ctx), 1); return EXPECT_RESULT(); } -#endif -#endif +#endif /* TEST_SELF_SIGNED_STAPLING */ static int test_self_signed_stapling(void) { EXPECT_DECLS; -#if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ - defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && \ - !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +#ifdef TEST_SELF_SIGNED_STAPLING test_ssl_cbf client_cbf; test_ssl_cbf server_cbf; size_t i; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index f91ea99eb1c..a5fc5bf47fa 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -48,7 +48,8 @@ int test_dtls12_basic_connection_id(void) { EXPECT_DECLS; -#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS_CID) +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS_CID) && !defined(WOLFSSL_NO_TLS12) unsigned char client_cid[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; unsigned char server_cid[] = { 0, 1, 2, 3, 4, 5 }; unsigned char readBuf[40]; @@ -1116,6 +1117,8 @@ int test_dtls13_cid_msg_malformed(void) int test_dtls_version_checking(void) { +/* The test drives a DTLS 1.2 handshake, which needs TLS 1.2. */ +#ifndef WOLFSSL_NO_TLS12 EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; @@ -1161,6 +1164,9 @@ int test_dtls_version_checking(void) wolfSSL_CTX_free(ctx_s); #endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_DTLS */ return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif } #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) @@ -1341,6 +1347,7 @@ int test_dtls_drop_invalid_record_during_handshake(void) { EXPECT_DECLS; +#ifndef WOLFSSL_NO_TLS12 /* Client drops a corrupted server flight: unknown type, then over-length. */ ExpectIntEQ(test_dtls_drop_invalid_record(wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, 0, 1), TEST_SUCCESS); @@ -1353,6 +1360,7 @@ int test_dtls_drop_invalid_record_during_handshake(void) wolfDTLSv1_2_server_method, 1, 0), TEST_SUCCESS); ExpectIntEQ(test_dtls_drop_invalid_record(wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, 1, 1), TEST_SUCCESS); +#endif #ifdef WOLFSSL_DTLS13 /* Same silent-drop behavior on the DTLS 1.3 receive path (all four @@ -1583,6 +1591,8 @@ int test_dtls13_oversized_msg_length(void) #if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) int test_dtls12_short_read(void) { +/* The test drives a DTLS 1.2 handshake, which needs TLS 1.2. */ +#ifndef WOLFSSL_NO_TLS12 EXPECT_DECLS; WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; @@ -1634,6 +1644,9 @@ int test_dtls12_short_read(void) } return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif } #else int test_dtls12_short_read(void) @@ -1645,6 +1658,8 @@ int test_dtls12_short_read(void) #if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) int test_dtls12_record_length_mismatch(void) { +/* The test drives a DTLS 1.2 handshake, which needs TLS 1.2. */ +#ifndef WOLFSSL_NO_TLS12 EXPECT_DECLS; WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; @@ -1681,11 +1696,24 @@ int test_dtls12_record_length_mismatch(void) ExpectIntEQ(ret, TEST_SUCCESS); return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif } int test_dtls_record_cross_boundaries(void) { +/* A record must not span datagrams in either DTLS version, so run whichever + * one the build has. */ +#if !defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_DTLS13) EXPECT_DECLS; +#ifdef WOLFSSL_NO_TLS12 + #define TEST_DTLS_CLIENT_METHOD wolfDTLSv1_3_client_method + #define TEST_DTLS_SERVER_METHOD wolfDTLSv1_3_server_method +#else + #define TEST_DTLS_CLIENT_METHOD wolfDTLSv1_2_client_method + #define TEST_DTLS_SERVER_METHOD wolfDTLSv1_2_server_method +#endif WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; @@ -1696,11 +1724,20 @@ int test_dtls_record_cross_boundaries(void) /* Setup DTLS contexts */ ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, - wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), + TEST_DTLS_CLIENT_METHOD, TEST_DTLS_SERVER_METHOD), 0); /* Complete handshake */ ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* The handshake does not drain the transport, and DTLS 1.3 can leave an + * ACK behind, so start from an empty buffer to keep the two records that + * follow at indices 0 and 1. DTLS 1.2 leaves nothing, and that is still + * worth checking rather than clearing away. */ +#ifdef WOLFSSL_NO_TLS12 + test_memio_clear_buffer(&test_ctx, 0); +#else + ExpectIntEQ(test_ctx.s_len, 0); +#endif /* create a first record in the buffer */ wolfSSL_SetLoggingPrefix("client"); @@ -1744,7 +1781,12 @@ int test_dtls_record_cross_boundaries(void) wolfSSL_CTX_free(ctx_s); wolfSSL_CTX_free(ctx_c); +#undef TEST_DTLS_CLIENT_METHOD +#undef TEST_DTLS_SERVER_METHOD return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif } #else int test_dtls12_record_length_mismatch(void) @@ -1759,6 +1801,8 @@ int test_dtls_record_cross_boundaries(void) int test_dtls_short_ciphertext(void) { +/* The test drives a DTLS 1.2 handshake, which needs TLS 1.2. */ +#ifndef WOLFSSL_NO_TLS12 EXPECT_DECLS; WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; @@ -1801,6 +1845,9 @@ int test_dtls_short_ciphertext(void) ExpectIntEQ(ret, TEST_SUCCESS); return EXPECT_RESULT(); +#else + return TEST_SKIPPED; +#endif } #else int test_dtls_drop_invalid_record_during_handshake(void) @@ -2960,7 +3007,8 @@ int test_dtls_set_session_min_downgrade(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ - defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET) + defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; WOLFSSL_SESSION *sess = NULL; @@ -7629,7 +7677,7 @@ int test_wolfSSL_dtls_srtp_keying_material(void) #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) static int test_dtls_mcast_highwater_cb(unsigned short peerId, unsigned int maxSeq, unsigned int curSeq, void* ctx) { @@ -7646,7 +7694,7 @@ int test_wolfSSL_mcast_peers(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; int hwCtx = 0; @@ -7891,7 +7939,7 @@ int test_wolfSSL_set_mtu_compat(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(OPENSSL_EXTRA) && \ (defined(WOLFSSL_SCTP) || defined(WOLFSSL_DTLS_MTU)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -7944,7 +7992,7 @@ int test_wolfSSL_CTX_mcast_set_member_id(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; ExpectIntEQ(wolfSSL_CTX_mcast_set_member_id(NULL, 0), @@ -7967,7 +8015,7 @@ int test_wolfSSL_mcast_read(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; word16 id = 0; @@ -8080,8 +8128,16 @@ int test_wolfSSL_dtls_got_timeout(void) int test_wolfSSL_DTLS_SetCookieSecret(void) { EXPECT_DECLS; +/* Only the argument handling is under test, which is the same in either DTLS + * version, so run whichever one the build has. */ #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER) && \ + (!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_DTLS13)) && \ (defined(NO_CERTS) || !defined(NO_RSA)) +#ifdef WOLFSSL_NO_TLS12 + #define TEST_DTLS_COOKIE_METHOD wolfDTLSv1_3_server_method +#else + #define TEST_DTLS_COOKIE_METHOD wolfDTLSv1_2_server_method +#endif WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; byte secret1[32]; @@ -8094,7 +8150,7 @@ int test_wolfSSL_DTLS_SetCookieSecret(void) ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(NULL, secret1, sizeof(secret1)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); - ExpectNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method())); + ExpectNotNull(ctx = wolfSSL_CTX_new(TEST_DTLS_COOKIE_METHOD())); #ifndef NO_CERTS /* A server WOLFSSL needs a key and certificate set on the context. */ ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, CERT_FILETYPE), @@ -8117,6 +8173,7 @@ int test_wolfSSL_DTLS_SetCookieSecret(void) wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); +#undef TEST_DTLS_COOKIE_METHOD #endif return EXPECT_RESULT(); } @@ -8126,7 +8183,7 @@ int test_wolfSSL_set_secret(void) EXPECT_DECLS; #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_MULTICAST) && \ (defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)) && \ - !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; byte preMasterSecret[16]; diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index c9c0c8a44b6..67233e17e7a 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -1390,8 +1390,11 @@ int test_tls12_ec_point_formats_no_uncompressed_non_ecc(void) int test_dtls_fallback_scsv(void) { EXPECT_DECLS; +/* The fallback is to DTLS 1.2, so the server needs that version to downgrade + * to for the alert to be the one under test. */ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ - defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) + defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) && \ + !defined(WOLFSSL_NO_TLS12) const byte clientHello[] = { /* DTLS record header: handshake, DTLS 1.2, epoch 0, seq 0, length 56 */ 0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index b387e5beb92..9b136a6bee0 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -9243,7 +9243,9 @@ int test_tls13_post_handshake_auth_late_allow(void) int test_tls13_downgrade_sentinel(void) { EXPECT_DECLS; -#if defined(WOLFSSL_TLS13) && \ +/* The sentinel marks a downgrade to TLS 1.2 or below, so the test needs a + * server of that version to produce one. */ +#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) WOLFSSL_CTX *ctx_c = NULL; diff --git a/tests/api/test_tls13_bounds.c b/tests/api/test_tls13_bounds.c index 054ccb4f8b0..ba503e82aba 100644 --- a/tests/api/test_tls13_bounds.c +++ b/tests/api/test_tls13_bounds.c @@ -417,6 +417,16 @@ static int test_tls13b_ch_find_ext(const byte* rec, int rec_sz, word16 type, } return -1; } + +#endif /* guards */ + +#if defined(WOLFSSL_TLS13) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \ + (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ + defined(HAVE_ED448)) + /* A mutually authenticated TLS 1.3 handshake with a chosen key type on both * ends. DoTls13CertificateVerify()'s peer-key / peerSigAlgo dispatch has one * arm per algorithm and the group only ever ran the RSA one, so each arm's diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 1b9a51be8d2..7fca5a37720 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -929,8 +929,9 @@ int test_certificate_authorities_certificate_request(void) { #ifdef WOLFSSL_DTLS13 {wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, 1}, #endif -#if defined(WOLFSSL_DTLS) && (defined(OPENSSL_ALL) || \ - defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)) +#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) && \ + (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \ + defined(HAVE_LIGHTY)) {wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, 1}, #endif }; @@ -1074,7 +1075,8 @@ static int certificate_authorities_server_cb(WOLFSSL *ssl, void *_arg) { #if defined(HAVE_TRUSTED_CA) && !defined(NO_SHA) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ - !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(WOLFSSL_NO_TLS12) /* Walk the TLSX list to find an extension by type. Avoids calling the * WOLFSSL_LOCAL TLSX_Find which is not available in shared library builds. */ static TLSX* test_TLSX_find_ext(TLSX* list, TLSX_Type type) @@ -1093,7 +1095,8 @@ int test_TLSX_TCA_Find(void) EXPECT_DECLS; #if defined(HAVE_TRUSTED_CA) && !defined(NO_SHA) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ - !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) + !defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(WOLFSSL_NO_TLS12) /* Two different 20-byte SHA1 ids */ byte id_A[WC_SHA_DIGEST_SIZE]; byte id_B[WC_SHA_DIGEST_SIZE]; diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index baff3de2ee6..863ae6f3d91 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -1395,7 +1395,7 @@ int test_TLSX_CSR_parse(void) #endif #if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_TLS) && \ - !defined(NO_WOLFSSL_SERVER) + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) { WOLFSSL_CTX* ctx2 = test_tls_parse_server_ctx( wolfTLSv1_2_server_method()); diff --git a/tests/api/test_x509.c b/tests/api/test_x509.c index faa710891da..6b13580076a 100644 --- a/tests/api/test_x509.c +++ b/tests/api/test_x509.c @@ -460,7 +460,7 @@ int test_x509_set_serialNumber(void) #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ !defined(NO_RSA) && !defined(NO_WOLFSSL_CLIENT) && \ - !defined(NO_WOLFSSL_SERVER) + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) /* Verify callback that accepts all certificates regardless of errors. */ static int accept_all_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) @@ -558,7 +558,7 @@ int test_x509_time_field_overread_via_tls(void) #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \ !defined(NO_RSA) && !defined(NO_WOLFSSL_CLIENT) && \ - !defined(NO_WOLFSSL_SERVER) + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) struct test_memio_ctx test_ctx; WOLFSSL_CTX* ctx_c = NULL; WOLFSSL_CTX* ctx_s = NULL; diff --git a/wolfssl/test.h b/wolfssl/test.h index 055b5b43916..57906a3b793 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -3432,39 +3432,116 @@ static WC_INLINE int wolfSSL_PrintStatsConn(WOLFSSL_MEM_CONN_STATS* stats) #ifdef HAVE_PK_CALLBACKS +/* How many generated ECC keys one connection can have to hold at once. A TLS + * v1.3 client offers a key share per group it is willing to start with, and + * the example client offers SM2 alongside secp256r1 where both are built, so + * one is not enough; a HelloRetryRequest then adds the group the server + * names. */ +#define PKCB_MAX_ECC_KEYGEN 4 + typedef struct PkCbInfo { const char* ourKey; -#ifdef TEST_PK_PRIVKEY - union { - #ifdef HAVE_ECC - /* only ECC PK callback with TLS v1.2 needs this */ - ecc_key ecc; - #endif - } keyGen; - int hasKeyGen; +#ifdef HAVE_ECC + /* Our own ephemeral keys, kept out of the library's key objects. TLS v1.3 + * generates each key share in the key gen callback and computes the shared + * secret in a later callback that is handed only the peer's key, so the + * private halves have to live here until the group is settled. Held by + * pointer: an ecc_key is several kilobytes, and this struct is a stack + * local in the example client and server. */ + ecc_key* keyGen[PKCB_MAX_ECC_KEYGEN]; + int keyGenCnt; #endif } PkCbInfo; #ifdef HAVE_ECC -static WC_INLINE int myEccKeyGen(WOLFSSL* ssl, ecc_key* key, word32 keySz, - int ecc_curve, void* ctx) +/* The key we generated for a curve, or NULL if we have none for it. */ +static WC_INLINE ecc_key* myEccKeptKey(PkCbInfo* cbInfo, int ecc_curve) { - int ret; - PkCbInfo* cbInfo = (PkCbInfo*)ctx; - ecc_key* new_key; + int i; + + if (cbInfo == NULL) + return NULL; + + for (i = 0; i < cbInfo->keyGenCnt; i++) { + if (cbInfo->keyGen[i]->dp != NULL && + cbInfo->keyGen[i]->dp->id == ecc_curve) { + return cbInfo->keyGen[i]; + } + } + + return NULL; +} +static WC_INLINE void myEccFreeKeptKeys(PkCbInfo* cbInfo) +{ + int i; + + if (cbInfo == NULL) + return; + + for (i = 0; i < cbInfo->keyGenCnt; i++) { + wc_ecc_free(cbInfo->keyGen[i]); + XFREE(cbInfo->keyGen[i], NULL, DYNAMIC_TYPE_ECC); + cbInfo->keyGen[i] = NULL; + } + cbInfo->keyGenCnt = 0; +} + +/* Whether this connection has to keep its own private key. TEST_PK_PRIVKEY + * asks for it on every version to model an application that never hands the + * library a private key; TLS v1.3 needs it either way, because the shared + * secret callback only receives the peer's key. Test the 1.3 versions rather + * than ordering the enum: the DTLS values sort above the TLS ones, so DTLS + * v1.2 would otherwise be taken for a 1.3. */ +static WC_INLINE int myEccKeepPrivKey(WOLFSSL* ssl) +{ #ifdef TEST_PK_PRIVKEY - new_key = cbInfo ? &cbInfo->keyGen.ecc : key; + (void)ssl; + return 1; #else - new_key = key; + int version = wolfSSL_GetVersion(ssl); + + return version == WOLFSSL_TLSV1_3 || version == WOLFSSL_DTLSV1_3; #endif +} - (void)ssl; - (void)cbInfo; +static WC_INLINE int myEccKeyGen(WOLFSSL* ssl, ecc_key* key, word32 keySz, + int ecc_curve, void* ctx) +{ + int ret; + PkCbInfo* cbInfo = (PkCbInfo*)ctx; + ecc_key* new_key = key; WOLFSSL_PKMSG("PK ECC KeyGen: keySz %u, Curve ID %d\n", keySz, ecc_curve); + if (cbInfo != NULL && myEccKeepPrivKey(ssl)) { + /* A key we already kept for this curve is stale - a TLS v1.3 client + * regenerates its key share when the server names a group in a + * HelloRetryRequest - so release it and take its slot back. */ + new_key = myEccKeptKey(cbInfo, ecc_curve); + if (new_key != NULL) { + wc_ecc_free(new_key); + } + else if (cbInfo->keyGenCnt < PKCB_MAX_ECC_KEYGEN) { + new_key = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, + DYNAMIC_TYPE_ECC); + if (new_key == NULL) { + WOLFSSL_PKMSG("PK ECC KeyGen: out of memory\n"); + return MEMORY_E; + } + /* Zero it before it is counted: a wc_ecc_init() failure below + * still leaves it for myEccFreeKeptKeys() to release. */ + XMEMSET(new_key, 0, sizeof(ecc_key)); + cbInfo->keyGen[cbInfo->keyGenCnt++] = new_key; + } + else { + WOLFSSL_PKMSG("PK ECC KeyGen: no room to keep curve %d\n", + ecc_curve); + return MEMORY_E; + } + } + ret = wc_ecc_init(new_key); if (ret == 0) { WC_RNG *rng = wolfSSL_GetRNG(ssl); @@ -3472,7 +3549,6 @@ static WC_INLINE int myEccKeyGen(WOLFSSL* ssl, ecc_key* key, word32 keySz, /* create new key */ ret = wc_ecc_make_key_ex(rng, (int) keySz, new_key, ecc_curve); - #ifdef TEST_PK_PRIVKEY if (ret == 0 && new_key != key) { byte qx[MAX_ECC_BYTES], qy[MAX_ECC_BYTES]; word32 qxLen = sizeof(qx), qyLen = sizeof(qy); @@ -3486,10 +3562,6 @@ static WC_INLINE int myEccKeyGen(WOLFSSL* ssl, ecc_key* key, word32 keySz, (void)qxLen; (void)qyLen; } - if (ret == 0 && cbInfo != NULL) { - cbInfo->hasKeyGen = 1; - } - #endif } WOLFSSL_PKMSG("PK ECC KeyGen: ret %d\n", ret); @@ -3620,32 +3692,39 @@ static WC_INLINE int myEccSharedSecret(WOLFSSL* ssl, ecc_key* otherKey, /* for client: create and export public key */ if (side == WOLFSSL_CLIENT_END) { - #ifdef TEST_PK_PRIVKEY - privKey = cbInfo ? &cbInfo->keyGen.ecc : &tmpKey; - #else - privKey = &tmpKey; - #endif pubKey = otherKey; /* TLS v1.2 and older we must generate a key here for the client only. - * TLS v1.3 calls key gen early with key share. Test the 1.3 versions - * rather than ordering the enum: the DTLS values sort above the TLS - * ones, so DTLS v1.2 would otherwise be taken for a 1.3 and skipped. */ + * TLS v1.3 calls key gen early with each key share it offers, and + * myEccKeepPrivKey() made that callback leave the private halves in + * cbInfo for us; the peer's key names the group the server settled on. + * Test the 1.3 versions rather than ordering the enum: the DTLS values + * sort above the TLS ones, so DTLS v1.2 would otherwise be taken for a + * 1.3 and skipped. */ if (version != WOLFSSL_TLSV1_3 && version != WOLFSSL_DTLSV1_3) { - ret = myEccKeyGen(ssl, privKey, 0, otherKey->dp->id, ctx); + ret = myEccKeyGen(ssl, &tmpKey, 0, otherKey->dp->id, ctx); if (ret == 0) { + privKey = myEccKeptKey(cbInfo, otherKey->dp->id); + if (privKey == NULL) + privKey = &tmpKey; ret = wc_ecc_export_x963(privKey, pubKeyDer, pubKeySz); } } + else { + privKey = myEccKeptKey(cbInfo, otherKey->dp->id); + if (privKey == NULL) { + WOLFSSL_PKMSG("PK ECC PMS: no key kept for curve %d\n", + otherKey->dp->id); + ret = ECC_CURVE_OID_E; + } + } } /* for server: import public key */ else if (side == WOLFSSL_SERVER_END) { - #ifdef TEST_PK_PRIVKEY - privKey = cbInfo ? &cbInfo->keyGen.ecc : otherKey; - #else - privKey = otherKey; - #endif + privKey = myEccKeptKey(cbInfo, otherKey->dp->id); + if (privKey == NULL) + privKey = otherKey; pubKey = &tmpKey; ret = wc_ecc_import_x963_ex(pubKeyDer, *pubKeySz, pubKey, @@ -3655,7 +3734,7 @@ static WC_INLINE int myEccSharedSecret(WOLFSSL* ssl, ecc_key* otherKey, ret = BAD_FUNC_ARG; } - if (privKey == NULL || pubKey == NULL) { + if (ret == 0 && (privKey == NULL || pubKey == NULL)) { ret = BAD_FUNC_ARG; } @@ -3678,13 +3757,6 @@ static WC_INLINE int myEccSharedSecret(WOLFSSL* ssl, ecc_key* otherKey, #endif } -#ifdef TEST_PK_PRIVKEY - if (cbInfo && cbInfo->hasKeyGen) { - wc_ecc_free(&cbInfo->keyGen.ecc); - cbInfo->hasKeyGen = 0; - } -#endif - wc_ecc_free(&tmpKey); WOLFSSL_PKMSG("PK ECC PMS: ret %d, PubKeySz %u, OutLen %u\n", ret, *pubKeySz, *outlen); @@ -4708,6 +4780,11 @@ static WC_INLINE void SetupPkCallbacks(WOLFSSL_CTX* ctx) static WC_INLINE void SetupPkCallbackContexts(WOLFSSL* ssl, void* myCtx) { #ifdef HAVE_ECC + /* The kept keys belong to one connection: whatever a previous one left + * behind is stale, and a TLS v1.2 handshake would otherwise derive + * with it instead of the key the library just handed us. */ + myEccFreeKeptKeys((PkCbInfo*)myCtx); + wolfSSL_SetEccKeyGenCtx(ssl, myCtx); wolfSSL_SetEccSignCtx(ssl, myCtx); wolfSSL_SetEccVerifyCtx(ssl, myCtx); @@ -4761,6 +4838,15 @@ static WC_INLINE void SetupPkCallbackContexts(WOLFSSL* ssl, void* myCtx) #endif } +/* Release what the callbacks kept for the last connection. */ +static WC_INLINE void CleanupPkCallbackContexts(void* myCtx) +{ + (void)myCtx; + #ifdef HAVE_ECC + myEccFreeKeptKeys((PkCbInfo*)myCtx); + #endif +} + #endif /* HAVE_PK_CALLBACKS */ #ifdef USE_WOLFSSL_IO