diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 8570fb5cac6..97781a55ebb 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -73,6 +73,7 @@ jobs: -DWOLFSSL_X963KDF:BOOL=yes -DWOLFSSL_DILITHIUM:BOOL=yes -DWOLFSSL_PKCS11:BOOL=yes \ -DWOLFSSL_ECCSI:BOOL=yes -DWOLFSSL_SAKKE:BOOL=yes -DWOLFSSL_SIPHASH:BOOL=yes \ -DWOLFSSL_WC_RSA_DIRECT:BOOL=yes -DWOLFSSL_PUBLIC_MP:BOOL=yes \ + -DWOLFSSL_CERT_WITH_EXTERN_PSK:BOOL=yes \ -DWOLFSSL_EXTRA_PQC_HYBRIDS:BOOL=yes -DWOLFSSL_TLS_NO_MLKEM_STANDALONE:BOOL=no \ .. cmake --build . diff --git a/.github/workflows/psk.yml b/.github/workflows/psk.yml index f1cd0c64df3..aac00b5e322 100644 --- a/.github/workflows/psk.yml +++ b/.github/workflows/psk.yml @@ -18,6 +18,7 @@ jobs: matrix: config: [ # Add new configs here + '--enable-psk --enable-cert-with-extern-psk --disable-mlkem', '--enable-psk --disable-mlkem C_EXTRA_FLAGS="-DWOLFSSL_STATIC_PSK -DWOLFSSL_OLDTLS_SHA2_CIPHERSUITES"', '--enable-psk --disable-mlkem C_EXTRA_FLAGS=-DWOLFSSL_STATIC_PSK --disable-rsa --disable-ecc --disable-dh', '--disable-oldtls --disable-tls13 --enable-psk -disable-rsa --disable-dh -disable-ecc --disable-asn C_EXTRA_FLAGS=-DWOLFSSL_STATIC_PSK --enable-lowresource --enable-singlethreaded --disable-asm --disable-errorstrings --disable-pkcs12 --disable-sha3 --disable-sha224 --disable-sha384 --disable-sha512 --disable-sha --disable-md5 -disable-aescbc --disable-chacha --disable-poly1305 --disable-coding --disable-sp-math-all --disable-mlkem', diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c7716f9b15..b59946f4984 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,6 +376,24 @@ if(WOLFSSL_POSTAUTH) endif() endif() +# Certificate Authentication with External PSK (RFC 8773bis) +add_option("WOLFSSL_CERT_WITH_EXTERN_PSK" + "Enable Certificate Authentication with External PSKs for TLS 1.3 (default: disabled)" + "no" "yes;no") + +if(WOLFSSL_CERT_WITH_EXTERN_PSK) + if(NOT WOLFSSL_TLS13) + message(WARNING "TLS 1.3 is disabled - disabling cert-with-extern-psk") + override_cache(WOLFSSL_CERT_WITH_EXTERN_PSK "no") + elseif(NOT WOLFSSL_PSK) + message(WARNING "PSK is disabled - disabling cert-with-extern-psk") + override_cache(WOLFSSL_CERT_WITH_EXTERN_PSK "no") + else() + list(APPEND WOLFSSL_DEFINITIONS + "-DWOLFSSL_CERT_WITH_EXTERN_PSK") + endif() +endif() + # Hello Retry Request Cookie add_option("WOLFSSL_HRR_COOKIE" "Enable the server to send Cookie Extension in HRR with state (default: disabled)" diff --git a/cmake/options.h.in b/cmake/options.h.in index 985b54241d6..12ab8285e40 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -308,6 +308,8 @@ extern "C" { #cmakedefine WOLFSSL_DTLS13 #undef WOLFSSL_DTLS_CH_FRAG #cmakedefine WOLFSSL_DTLS_CH_FRAG +#undef WOLFSSL_CERT_WITH_EXTERN_PSK +#cmakedefine WOLFSSL_CERT_WITH_EXTERN_PSK #undef WOLFSSL_EITHER_SIDE #cmakedefine WOLFSSL_EITHER_SIDE #undef WOLFSSL_ENCRYPTED_KEYS @@ -427,4 +429,3 @@ extern "C" { #endif /* WOLFSSL_OPTIONS_H */ - diff --git a/configure.ac b/configure.ac index 97445a22091..1244377b79a 100644 --- a/configure.ac +++ b/configure.ac @@ -5142,6 +5142,27 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PSK_ONE_ID" fi +# Certificate Authentication with External PSK (RFC 8773bis) +AC_ARG_ENABLE([cert-with-extern-psk], + [AS_HELP_STRING([--enable-cert-with-extern-psk],[Enable Certificate Authentication with External PSKs for TLS 1.3 (default: disabled)])], + [ ENABLED_CERT_WITH_EXTERN_PSK=$enableval ], + [ ENABLED_CERT_WITH_EXTERN_PSK=no ] + ) +if test "$ENABLED_CERT_WITH_EXTERN_PSK" = "yes" +then + if test "$ENABLED_TLS13" = "no" + then + AC_MSG_NOTICE([TLS 1.3 is disabled - disabling cert-with-extern-psk]) + ENABLED_CERT_WITH_EXTERN_PSK="no" + elif test "$ENABLED_PSK" = "no" + then + AC_MSG_NOTICE([PSK is disabled - disabling cert-with-extern-psk]) + ENABLED_CERT_WITH_EXTERN_PSK="no" + else + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CERT_WITH_EXTERN_PSK" + fi +fi + # ERROR STRINGS AC_ARG_ENABLE([errorstrings], [AS_HELP_STRING([--enable-errorstrings],[Enable error strings table (default: enabled)])], diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 5ac332afabd..e33ef60f791 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -14721,6 +14721,75 @@ void wolfSSL_CTX_set_psk_server_tls13_callback(WOLFSSL_CTX* ctx, void wolfSSL_set_psk_server_tls13_callback(WOLFSSL* ssl, wc_psk_server_tls13_callback cb); +/*! + \ingroup Setup + + \brief Enable or disable TLS 1.3 certificate authentication with external + PSK (RFC8773bis) on a context. + + When enabled, wolfSSL advertises and accepts the + `tls_cert_with_extern_psk` extension for TLS 1.3 handshakes using external + PSKs. Any non-zero \p state value enables the feature and zero disables it. + + Availability: + - Built with `--enable-tls13 --enable-psk --enable-cert-with-extern-psk` + - Or with `WOLFSSL_TLS13` and `WOLFSSL_CERT_WITH_EXTERN_PSK` defined + + \param [in,out] ctx a pointer to a WOLFSSL_CTX structure, created with + wolfSSL_CTX_new(). + \param [in] state 0 to disable, non-zero to enable. + + \return WOLFSSL_SUCCESS on success. + \return WOLFSSL_FAILURE when \p ctx is NULL. + + _Example_ + \code + WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if (wolfSSL_CTX_set_cert_with_extern_psk(ctx, 1) != WOLFSSL_SUCCESS) { + /* handle error */ + } + \endcode + + \sa wolfSSL_set_cert_with_extern_psk + \sa wolfSSL_CTX_set_psk_client_tls13_callback + \sa wolfSSL_CTX_set_psk_server_tls13_callback +*/ +int wolfSSL_CTX_set_cert_with_extern_psk(WOLFSSL_CTX* ctx, int state); + +/*! + \ingroup Setup + + \brief Enable or disable TLS 1.3 certificate authentication with external + PSK (RFC8773bis) on a connection. + + This call applies to a single WOLFSSL object. Any non-zero \p state value + enables the feature and zero disables it. + + Availability: + - Built with `--enable-tls13 --enable-psk --enable-cert-with-extern-psk` + - Or with `WOLFSSL_TLS13` and `WOLFSSL_CERT_WITH_EXTERN_PSK` defined + + \param [in,out] ssl a pointer to a WOLFSSL structure, created using + wolfSSL_new(). + \param [in] state 0 to disable, non-zero to enable. + + \return WOLFSSL_SUCCESS on success. + \return WOLFSSL_FAILURE when \p ssl is NULL. + + _Example_ + \code + WOLFSSL* ssl = wolfSSL_new(ctx); + if (wolfSSL_set_cert_with_extern_psk(ssl, 1) != WOLFSSL_SUCCESS) { + /* handle error */ + } + \endcode + + \sa wolfSSL_CTX_set_cert_with_extern_psk + \sa wolfSSL_set_psk_client_tls13_callback + \sa wolfSSL_set_psk_server_tls13_callback +*/ +int wolfSSL_set_cert_with_extern_psk(WOLFSSL* ssl, int state); + /*! \ingroup Setup diff --git a/examples/client/client.c b/examples/client/client.c index 12b63c13986..5b495fc8325 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -1209,7 +1209,7 @@ static int ClientWriteRead(WOLFSSL* ssl, const char* msg, int msgSz, /* 4. add the same message into Japanese section */ /* (will be translated later) */ /* 5. add printf() into suitable position of Usage() */ -static const char* client_usage_msg[][80] = { +static const char* client_usage_msg[][81] = { /* English */ { " NOTE: All files relative to wolfSSL home dir\n", /* 0 */ @@ -1454,24 +1454,28 @@ static const char* client_usage_msg[][80] = { #ifndef NO_PSK "--openssl-psk Use TLS 1.3 PSK callback compatible with OpenSSL\n", /* 73 */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + "--psk-with-certs Use TLS 1.3 PSK with certificates\n", /* 74 */ +#endif #ifdef HAVE_RPK - "--rpk Use RPK for the defined certificates\n", /* 74 */ + "--rpk Use RPK for the defined certificates\n", /* 75 */ #endif - "--files-are-der Specified files are in DER, not PEM format\n", /* 75 */ + "--files-are-der Specified files are in DER, not PEM format\n", /* 76 */ #ifdef WOLFSSL_SYS_CRYPTO_POLICY - "--crypto-policy \n", /* 76 */ + "--crypto-policy \n", /* 77 */ #endif #ifdef HAVE_ECC_BRAINPOOL - "--bpKs Use Brainpool ECC group for key share\n", /* 77 */ + "--bpKs Use Brainpool ECC group for key share\n", /* 78 */ #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) "--ech Use Encrypted Client Hello with base64 encoded " "ECH configs\n", - /* 78 */ + /* 79 */ #endif "\n" "For simpler wolfSSL TLS client examples, visit\n" - "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 79 */ + "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 80 */ NULL, }, #ifndef NO_MULTIBYTE_PRINT @@ -1720,20 +1724,24 @@ static const char* client_usage_msg[][80] = { #ifndef NO_PSK "--openssl-psk Use TLS 1.3 PSK callback compatible with OpenSSL\n", /* 73 */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + "--psk-with-certs Use TLS 1.3 PSK with certificates\n", /* 74 */ +#endif #ifdef HAVE_RPK - "--rpk Use RPK for the defined certificates\n", /* 74 */ + "--rpk Use RPK for the defined certificates\n", /* 75 */ #endif - "--files-are-der Specified files are in DER, not PEM format\n", /* 75 */ + "--files-are-der Specified files are in DER, not PEM format\n", /* 76 */ #ifdef WOLFSSL_SYS_CRYPTO_POLICY - "--crypto-policy \n", /* 76 */ + "--crypto-policy \n", /* 77 */ #endif #ifdef HAVE_ECC_BRAINPOOL - "--bpKs Use Brainpool ECC group for key share\n", /* 77 */ + "--bpKs Use Brainpool ECC group for key share\n", /* 78 */ #endif "\n" "より簡単なwolfSSL TLS クライアントの例については" "下記にアクセスしてください\n" - "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 78 */ + "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 79 */ NULL, }, #endif @@ -1969,6 +1977,10 @@ static void Usage(void) #ifndef NO_PSK printf("%s", msg[++msgid]); /* --openssl-psk */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + printf("%s", msg[++msgid]); /* --psk-with-certs */ +#endif #ifdef HAVE_RPK printf("%s", msg[++msgid]); /* --rpk */ #endif @@ -2168,6 +2180,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) { "ech", 1, 271 }, +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + { "psk-with-certs", 0, 272 }, #endif { 0, 0, 0 } }; @@ -2176,6 +2192,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) int minVersion = CLIENT_INVALID_VERSION; int usePsk = 0; int opensslPsk = 0; + int usePskWithCerts = 0; int useAnon = 0; int sendGET = 0; int benchmark = 0; @@ -2415,6 +2432,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) (void)pqcAlg; (void)opensslPsk; (void)fileFormat; + (void)usePskWithCerts; StackTrap(); /* Reinitialize the global myVerifyAction. */ @@ -3070,6 +3088,12 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) echConfigs64 = myoptarg; break; #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + case 272: + usePskWithCerts = 1; + break; +#endif default: Usage(); @@ -3080,6 +3104,18 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) myoptind = 0; /* reset for test cases */ #endif /* !WOLFSSL_VXWORKS */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + if (usePskWithCerts) { + usePsk = 1; + if (noPskDheKe) { + LOG_ERROR("--psk-with-certs requires PSK key exchange with (EC)DHE"); + Usage(); + XEXIT_T(MY_EX_USAGE); + } + } +#endif + if (externalTest) { /* detect build cases that wouldn't allow test against wolfssl.com */ int done = 0; @@ -3486,6 +3522,14 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) wolfSSL_CTX_set_psk_client_tls13_callback(ctx, my_psk_client_tls13_cb); } +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + if (usePskWithCerts) { + if (wolfSSL_CTX_set_cert_with_extern_psk(ctx, 1) != WOLFSSL_SUCCESS) { + wolfSSL_CTX_free(ctx); ctx = NULL; + err_sys("client can't enable cert_with_extern_psk"); + } + } +#endif #endif if (defaultCipherList == NULL) { #if defined(HAVE_AESGCM) && !defined(NO_DH) @@ -3637,7 +3681,8 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) #endif } - if (!usePsk && !useAnon && !useVerifyCb && myVerifyAction != VERIFY_FORCE_FAIL) { + if ((!usePsk || usePskWithCerts) && !useAnon && !useVerifyCb && + myVerifyAction != VERIFY_FORCE_FAIL) { #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \ (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \ !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR) @@ -3721,10 +3766,11 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) myVerifyAction == VERIFY_USE_PREVERIFY) { wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, myVerify); } - else if (!usePsk && !useAnon && doPeerCheck == 0) { + else if ((!usePsk || usePskWithCerts) && !useAnon && doPeerCheck == 0) { wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL); } - else if (!usePsk && !useAnon && myVerifyAction == VERIFY_OVERRIDE_DATE_ERR) { + else if ((!usePsk || usePskWithCerts) && !useAnon && + myVerifyAction == VERIFY_OVERRIDE_DATE_ERR) { wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, myVerify); } #endif /* !NO_CERTS */ diff --git a/examples/server/server.c b/examples/server/server.c index d72555bb202..0e85c1db022 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -943,7 +943,7 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, /* 4. add the same message into Japanese section */ /* (will be translated later) */ /* 5. add printf() into suitable position of Usage() */ -static const char* server_usage_msg[][70] = { +static const char* server_usage_msg[][71] = { /* English */ { " NOTE: All files relative to wolfSSL home dir\n", /* 0 */ @@ -1133,25 +1133,28 @@ static const char* server_usage_msg[][70] = { #ifdef HAVE_SUPPORTED_CURVES "--onlyPskDheKe Must use DHE key exchange with PSK\n", /* 64 */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + "--psk-with-certs Use TLS 1.3 PSK with certificates\n", /* 65 */ +#endif #ifdef WOLFSSL_DUAL_ALG_CERTS - "--altPrivKey Generate alternative signature with this key.\n", - /* 65 */ + "--altPrivKey Generate alternative signature with this key.\n", /* 66 */ #endif #ifdef WOLFSSL_SYS_CRYPTO_POLICY - "--crypto-policy \n", /* 66 */ + "--crypto-policy \n", /* 67 */ #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) "--ech Generate Encrypted Client Hello config with " "public name \n", - /* 67 */ + /* 68 */ "--ech-suite HPKE suite to use for ech config.\n" " Supplied as 3 integers (ex: 32,1,3)\n", - /* 68 */ + /* 69 */ #endif "\n" "For simpler wolfSSL TLS server examples, visit\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 69 */ + /* 70 */ NULL, }, #ifndef NO_MULTIBYTE_PRINT @@ -1356,26 +1359,29 @@ static const char* server_usage_msg[][70] = { #ifdef HAVE_SUPPORTED_CURVES "--onlyPskDheKe Must use DHE key exchange with PSK\n", /* 64 */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + "--psk-with-certs Use TLS 1.3 PSK with certificates\n", /* 65 */ +#endif #ifdef WOLFSSL_DUAL_ALG_CERTS - "--altPrivKey Generate alternative signature with this key.\n", - /* 65 */ + "--altPrivKey Generate alternative signature with this key.\n", /* 66 */ #endif #ifdef WOLFSSL_SYS_CRYPTO_POLICY - "--crypto-policy \n", /* 66 */ + "--crypto-policy \n", /* 67 */ #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) "--ech Generate Encrypted Client Hello config with " "public name \n", - /* 67 */ + /* 68 */ "--ech-suite HPKE suite to use for ech config.\n" " Supplied as 3 integers (ex: 32,1,3)\n", - /* 68 */ + /* 69 */ #endif "\n" "より簡単なwolfSSL TSL クライアントの例については" "下記にアクセスしてください\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 69 */ + /* 70 */ NULL, }, #endif @@ -1532,6 +1538,10 @@ static void Usage(void) #ifdef HAVE_SUPPORTED_CURVES printf("%s", msg[++msgId]); /* --onlyPskDheKe */ #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + printf("%s", msg[++msgId]); /* --psk-with-certs */ +#endif #ifdef WOLFSSL_DUAL_ALG_CERTS printf("%s", msg[++msgId]); /* --altPrivKey */ #endif @@ -1664,6 +1674,10 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) { "ech", 1, 269 }, { "ech-suite", 1, 270 }, +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + { "psk-with-certs", 0, 271 }, #endif { 0, 0, 0 } }; @@ -1681,6 +1695,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) word16 port = wolfSSLPort; int usePsk = 0; int usePskPlus = 0; + int usePskWithCerts = 0; int useAnon = 0; int doDTLS = 0; int dtlsUDP = 0; @@ -1917,6 +1932,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) (void)pqcAlg; (void)usePqc; (void)altPrivKey; + (void)usePskWithCerts; #ifdef WOLFSSL_TIRTOS fdOpenSession(Task_self()); @@ -2607,6 +2623,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) break; #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + case 271: + usePskWithCerts = 1; + break; +#endif case -1: default: @@ -2618,6 +2640,18 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) myoptind = 0; /* reset for test cases */ #endif /* !WOLFSSL_VXWORKS */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + if (usePskWithCerts) { + usePsk = 1; + if (noPskDheKe) { + LOG_ERROR("--psk-with-certs requires PSK key exchange with (EC)DHE"); + Usage(); + XEXIT_T(MY_EX_USAGE); + } + } +#endif + /* Can only use DTLS over UDP or SCTP, can't do both. */ if (dtlsUDP && dtlsSCTP) { err_sys_ex(runWithErrors, "Cannot use DTLS with both UDP and SCTP."); @@ -2940,7 +2974,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif #if !defined(NO_CERTS) - if ((!usePsk || usePskPlus) && !useAnon && !(loadCertKeyIntoSSLObj == 1)) { + if ((!usePsk || usePskPlus || usePskWithCerts) && !useAnon && + !(loadCertKeyIntoSSLObj == 1)) { #if defined(NO_FILESYSTEM) && defined(USE_CERT_BUFFERS_2048) if (wolfSSL_CTX_use_certificate_chain_buffer_format(ctx, server_cert_der_2048, sizeof_server_cert_der_2048, @@ -2984,7 +3019,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #ifdef HAVE_PK_CALLBACKS pkCbInfo.ourKey = ourKey; #endif - if ((!usePsk || usePskPlus) && !useAnon + if ((!usePsk || usePskPlus || usePskWithCerts) && !useAnon && !(loadCertKeyIntoSSLObj == 1) #if defined(HAVE_PK_CALLBACKS) && defined(TEST_PK_PRIVKEY) && !pkCallbacks @@ -3026,6 +3061,14 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) SSL_CTX_set_psk_server_callback(ctx, my_psk_server_cb); #ifdef WOLFSSL_TLS13 wolfSSL_CTX_set_psk_server_tls13_callback(ctx, my_psk_server_tls13_cb); +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + if (usePskWithCerts) { + if (wolfSSL_CTX_set_cert_with_extern_psk(ctx, 1) != WOLFSSL_SUCCESS) { + err_sys_ex(runWithErrors, + "server can't enable cert_with_extern_psk"); + } + } +#endif #endif if (sendPskIdentityHint == 1) SSL_CTX_use_psk_identity_hint(ctx, "cyassl server"); @@ -3314,7 +3357,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) /* Support for loading private key and cert using WOLFSSL object */ #if !defined(NO_CERTS) - if ((!usePsk || usePskPlus) && !useAnon && loadCertKeyIntoSSLObj) { + if ((!usePsk || usePskPlus || usePskWithCerts) && !useAnon && + loadCertKeyIntoSSLObj) { #if defined(NO_FILESYSTEM) && defined(USE_CERT_BUFFERS_2048) if (wolfSSL_use_certificate_chain_buffer_format(ssl, server_cert_der_2048, sizeof_server_cert_der_2048, @@ -3331,7 +3375,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif } - if ((!usePsk || usePskPlus) && !useAnon && + if ((!usePsk || usePskPlus || usePskWithCerts) && !useAnon && loadCertKeyIntoSSLObj #if defined(HAVE_PK_CALLBACKS) && defined(TEST_PK_PRIVKEY) && !pkCallbacks diff --git a/src/internal.c b/src/internal.c index 3555a96eb59..a7e7fb24e00 100644 --- a/src/internal.c +++ b/src/internal.c @@ -2820,6 +2820,10 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap) #if defined(WOLFSSL_TLS13) && !defined(HAVE_SUPPORTED_CURVES) ctx->noPskDheKe = 1; #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + /* Disabled by default - opt in through API. */ + ctx->certWithExternPsk = 0; +#endif #endif #if defined(WOLFSSL_QT) && !defined(NO_PSK) @@ -7928,6 +7932,9 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) #ifdef HAVE_SUPPORTED_CURVES ssl->options.onlyPskDheKe = ctx->onlyPskDheKe; #endif /* HAVE_SUPPORTED_CURVES */ + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + ssl->options.certWithExternPsk = ctx->certWithExternPsk; + #endif #endif /* HAVE_SESSION_TICKET || !NO_PSK */ #if defined(WOLFSSL_POST_HANDSHAKE_AUTH) ssl->options.postHandshakeAuth = ctx->postHandshakeAuth; diff --git a/src/ssl.c b/src/ssl.c index 07434f66ce1..67e9be5dd57 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -8087,6 +8087,25 @@ int wolfSSL_set_compression(WOLFSSL* ssl) ssl->options.haveECC, TRUE, ssl->options.haveStaticECC, ssl->options.useAnon, TRUE, TRUE, TRUE, TRUE, ssl->options.side); } + +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + int wolfSSL_CTX_set_cert_with_extern_psk(WOLFSSL_CTX* ctx, int state) + { + if (ctx == NULL) + return WOLFSSL_FAILURE; + ctx->certWithExternPsk = (byte)(state != 0); + return WOLFSSL_SUCCESS; + } + + int wolfSSL_set_cert_with_extern_psk(WOLFSSL* ssl, int state) + { + if (ssl == NULL) + return WOLFSSL_FAILURE; + ssl->options.certWithExternPsk = (word16)(state != 0); + return WOLFSSL_SUCCESS; + } +#endif + #ifdef OPENSSL_EXTRA /** * set call back function for psk session use diff --git a/src/tls.c b/src/tls.c index 535b43826bd..88e82b3c657 100644 --- a/src/tls.c +++ b/src/tls.c @@ -12346,6 +12346,104 @@ int TLSX_PreSharedKey_Use(TLSX** extensions, const byte* identity, word16 len, #endif +/******************************************************************************/ +/* Certificate Authentication with External Pre-Shared Key */ +/******************************************************************************/ + +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + +static int TLSX_Cert_With_Extern_Psk_GetSize(byte msgType, word16* pSz) +{ + (void)pSz; + if (msgType == client_hello || msgType == server_hello) { + return 0; + } + else { + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } +} + +static int TLSX_Cert_With_Extern_Psk_Write(byte* output, byte msgType, + word16* pSz) +{ + (void)output; + (void)pSz; + if (msgType == client_hello || msgType == server_hello) { + return 0; + } + else { + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; + } +} + +static int TLSX_Cert_With_Extern_Psk_Parse(WOLFSSL* ssl, byte msgType) +{ + TLSX* ext; + int err; + + if (msgType == client_hello) { + if (!ssl->options.certWithExternPsk) + return 0; + err = TLSX_Cert_With_Extern_Psk_Use(ssl); + if (err != 0) + return err; + /* TLSX_Cert_With_Extern_Psk_Use() sets resp=1 by convention, but the + * server has not yet decided to echo the extension back. Clear it + * here; CheckPreSharedKeys() will set resp=1 only after confirming that + * an external (non-ticket) PSK was matched. */ + ext = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK); + if (ext != NULL) + ext->resp = 0; + return 0; + } + + if (msgType == server_hello) { + if (TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) == NULL) { + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } + ssl->options.certWithExternPsk = 1; + return 0; + } + + WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); + return SANITY_MSG_E; +} + +int TLSX_Cert_With_Extern_Psk_Use(WOLFSSL* ssl) +{ + int ret = 0; + TLSX* extension; + + extension = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK); + if (extension == NULL) { + ret = TLSX_Push(&ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK, NULL, + ssl->heap); + if (ret != 0) + return ret; + extension = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK); + if (extension == NULL) + return MEMORY_E; + } + extension->resp = 1; + return 0; +} + +#define PSK_WITH_CERT_GET_SIZE TLSX_Cert_With_Extern_Psk_GetSize +#define PSK_WITH_CERT_WRITE TLSX_Cert_With_Extern_Psk_Write +#define PSK_WITH_CERT_PARSE TLSX_Cert_With_Extern_Psk_Parse + +#else + +#define PSK_WITH_CERT_GET_SIZE(a, b) 0 +#define PSK_WITH_CERT_WRITE(a, b, c) 0 +#define PSK_WITH_CERT_PARSE(a, b) 0 + +#endif /* WOLFSSL_TLS13 && WOLFSSL_CERT_WITH_EXTERN_PSK */ + /******************************************************************************/ /* PSK Key Exchange Modes */ /******************************************************************************/ @@ -14635,6 +14733,11 @@ void TLSX_FreeAll(TLSX* list, void* heap) case TLSX_PSK_KEY_EXCHANGE_MODES: WOLFSSL_MSG("PSK Key Exchange Modes extension free"); break; + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + case TLSX_CERT_WITH_EXTERN_PSK: + WOLFSSL_MSG("Cert with external PSK extension free"); + break; + #endif #endif #endif @@ -14833,6 +14936,11 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, case TLSX_PSK_KEY_EXCHANGE_MODES: ret = PKM_GET_SIZE((byte)extension->val, msgType, &length); break; + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + case TLSX_CERT_WITH_EXTERN_PSK: + ret = PSK_WITH_CERT_GET_SIZE(msgType, &length); + break; + #endif #endif #endif case TLSX_KEY_SHARE: @@ -15065,6 +15173,12 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, ret = PKM_WRITE((byte)extension->val, output + offset, msgType, &offset); break; + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + case TLSX_CERT_WITH_EXTERN_PSK: + WOLFSSL_MSG("Cert with external PSK extension to write"); + ret = PSK_WITH_CERT_WRITE(output + offset, msgType, &offset); + break; + #endif #endif #endif case TLSX_KEY_SHARE: @@ -15975,15 +16089,31 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer) modes = 1 << PSK_KE; } #if !defined(NO_DH) || defined(HAVE_ECC) || \ - defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) + defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) if (!ssl->options.noPskDheKe) { modes |= 1 << PSK_DHE_KE; } + #endif + #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + if (ssl->options.certWithExternPsk) { + /* RFC8773bis requires psk_dhe_ke with cert_with_extern_psk. */ + modes |= 1 << PSK_DHE_KE; + } #endif ret = TLSX_PskKeyModes_Use(ssl, modes); if (ret != 0) return ret; } + + #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + if (usingPSK && ssl->options.certWithExternPsk) { + ret = TLSX_Cert_With_Extern_Psk_Use(ssl); + if (ret != 0) + return ret; + /* Require server confirmation before using cert-with-PSK path. */ + ssl->options.certWithExternPsk = 0; + } + #endif #endif #if defined(WOLFSSL_POST_HANDSHAKE_AUTH) if (!isServer && ssl->options.postHandshakeAuth) { @@ -16488,6 +16618,9 @@ int TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType, word16* pLength) #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_CERT_WITH_EXTERN_PSK)); + #endif #endif } #if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) @@ -16544,6 +16677,9 @@ int TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType, word16* pLength) #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_CERT_WITH_EXTERN_PSK)); + #endif #endif #ifdef HAVE_CERTIFICATE_STATUS_REQUEST TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); @@ -16636,6 +16772,9 @@ int TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType, word16* pOffset #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_CERT_WITH_EXTERN_PSK)); + #endif #endif } else @@ -16692,6 +16831,9 @@ int TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType, word16* pOffset #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_CERT_WITH_EXTERN_PSK)); + #endif #endif #ifdef HAVE_CERTIFICATE_STATUS_REQUEST TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_STATUS_REQUEST)); @@ -16864,6 +17006,8 @@ static word16 TLSX_GetMinSize_Client(word16* type) return WOLFSSL_CKE_MIN_SIZE_CLIENT; case TLSXT_PSK_KEY_EXCHANGE_MODES: return WOLFSSL_PKM_MIN_SIZE_CLIENT; + case TLSXT_CERT_WITH_EXTERN_PSK: + return WOLFSSL_CWEP_MIN_SIZE_CLIENT; case TLSXT_CERTIFICATE_AUTHORITIES: return WOLFSSL_CAN_MIN_SIZE_CLIENT; case TLSXT_POST_HANDSHAKE_AUTH: @@ -16933,6 +17077,8 @@ static word16 TLSX_GetMinSize_Server(const word16 *type) return WOLFSSL_CKE_MIN_SIZE_SERVER; case TLSXT_PSK_KEY_EXCHANGE_MODES: return WOLFSSL_PKM_MIN_SIZE_SERVER; + case TLSXT_CERT_WITH_EXTERN_PSK: + return WOLFSSL_CWEP_MIN_SIZE_SERVER; case TLSXT_CERTIFICATE_AUTHORITIES: return WOLFSSL_CAN_MIN_SIZE_SERVER; case TLSXT_POST_HANDSHAKE_AUTH: @@ -16973,6 +17119,11 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, #endif #if defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) int pskDone = 0; +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) + int secondClientHello = 0; + int prevHasPskWithCert = 0; #endif byte seenType[SEMAPHORE_SIZE]; /* Seen known extensions. */ @@ -16981,6 +17132,15 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, /* No known extensions seen yet. */ XMEMSET(seenType, 0, sizeof(seenType)); +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) + if (IsAtLeastTLSv1_3(ssl->version) && msgType == client_hello && + ssl->msgsReceived.got_client_hello == 2) { + secondClientHello = 1; + prevHasPskWithCert = + TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL; + } +#endif while (ret == 0 && offset < length) { word16 type; @@ -17429,6 +17589,25 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, ret = PKM_PARSE(ssl, input + offset, size, msgType); break; + + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + case TLSX_CERT_WITH_EXTERN_PSK: + WOLFSSL_MSG("Cert with external PSK extension received"); + #ifdef WOLFSSL_DEBUG_TLS + WOLFSSL_BUFFER(input + offset, size); + #endif + + if (!IsAtLeastTLSv1_3(ssl->version)) + break; + + if (msgType != client_hello && msgType != server_hello) { + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } + + ret = PSK_WITH_CERT_PARSE(ssl, msgType); + break; + #endif #endif #ifdef WOLFSSL_EARLY_DATA @@ -17653,6 +17832,64 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType, ssl->options.noPskDheKe = 1; } #endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) + if (IsAtLeastTLSv1_3(ssl->version)) { + int hasPskWithCert = !IS_OFF(seenType, + TLSX_ToSemaphore(TLSX_CERT_WITH_EXTERN_PSK)); + if (hasPskWithCert) { + int hasPsk = !IS_OFF(seenType, TLSX_ToSemaphore(TLSX_PRE_SHARED_KEY)); + int hasPskModes = !IS_OFF(seenType, + TLSX_ToSemaphore(TLSX_PSK_KEY_EXCHANGE_MODES)); + int hasKeyShare = !IS_OFF(seenType, TLSX_ToSemaphore(TLSX_KEY_SHARE)); + int hasSg = !IS_OFF(seenType, + TLSX_ToSemaphore(TLSX_SUPPORTED_GROUPS)); +#ifdef WOLFSSL_EARLY_DATA + int hasEarlyData = !IS_OFF(seenType, TLSX_ToSemaphore(TLSX_EARLY_DATA)); +#endif + + if (msgType == client_hello && isRequest) { + TLSX* pskm; + /* RFC8773bis: CH2 after HRR must keep CH1's extension set. */ + if (secondClientHello && !prevHasPskWithCert) { + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } + /* RFC8773bis: cert_with_extern_psk depends on these extensions. */ + if (!hasPsk || !hasPskModes || !hasKeyShare || !hasSg) { + WOLFSSL_ERROR_VERBOSE(EXT_MISSING); + return EXT_MISSING; + } +#ifdef WOLFSSL_EARLY_DATA + /* External PSK + certificate mode forbids 0-RTT in CH. */ + if (hasEarlyData) { + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } +#endif + pskm = TLSX_Find(ssl->extensions, TLSX_PSK_KEY_EXCHANGE_MODES); + /* RFC8773bis requires client support for psk_dhe_ke mode. */ + if (pskm == NULL || (pskm->val & (1 << PSK_DHE_KE)) == 0) { + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } + } + else if (msgType == server_hello && !isRequest) { + /* SH confirming cert_with_extern_psk must also confirm PSK and KSE. */ + if (!hasPsk || !hasKeyShare) { + WOLFSSL_ERROR_VERBOSE(EXT_MISSING); + return EXT_MISSING; + } + } + } + else if (msgType == client_hello && isRequest && secondClientHello && + prevHasPskWithCert) { + /* RFC8773bis: reject dropping the extension in CH2 after HRR. */ + WOLFSSL_ERROR_VERBOSE(EXT_NOT_ALLOWED); + return EXT_NOT_ALLOWED; + } + } +#endif #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) /* RFC 8446 Section 9.2: ClientHello with KeyShare must * contain SupportedGroups and vice-versa. */ diff --git a/src/tls13.c b/src/tls13.c index e63e824d795..3ebbc10f54e 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -4281,8 +4281,17 @@ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello) } if (!clientHello) { - /* CLIENT: using PSK for peer authentication. */ - ssl->options.peerAuthGood = 1; +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (ssl->options.certWithExternPsk) { + /* Certificate authentication is still required. */ + ssl->options.peerAuthGood = 0; + } + else +#endif + { + /* CLIENT: using PSK for peer authentication. */ + ssl->options.peerAuthGood = 1; + } } } #endif @@ -5691,6 +5700,20 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, XMEMSET(ssl->arrays->psk_key, 0, MAX_PSK_KEY_LEN); } else { +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (ssl->options.certWithExternPsk && psk->resumption) { + /* RFC8773bis mode requires external PSK, not ticket resumption. */ + WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); + return PSK_KEY_ERROR; + } + if (ssl->options.certWithExternPsk && ssl->options.shSentKeyShare == 0) { + /* RFC8773bis Sec. 3: cert_with_extern_psk requires psk_dhe_ke; + * a ServerHello without a key_share confirms only psk_ke. */ + WOLFSSL_MSG("cert_with_extern_psk: ServerHello missing key_share"); + WOLFSSL_ERROR_VERBOSE(EXT_MISSING); + return EXT_MISSING; + } +#endif if ((ret = SetupPskKey(ssl, psk, 0)) != 0) return ret; ssl->options.pskNegotiated = 1; @@ -6035,10 +6058,15 @@ static int FindPsk(WOLFSSL* ssl, PreSharedKey* psk, const byte* suite, int* err) ret = FindPskSuite(ssl, psk, ssl->arrays->psk_key, &ssl->arrays->psk_keySz, suite, &found, foundSuite); if (ret == 0 && found) { + /* This identity matched via external PSK callback, not ticket resume. */ + psk->resumption = 0; /* Default to ciphersuite if cb doesn't specify. */ ssl->options.resuming = 0; /* Don't send certificate request when using PSK. */ - ssl->options.verifyPeer = 0; +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (!ssl->options.certWithExternPsk) +#endif + ssl->options.verifyPeer = 0; /* PSK age is always zero. */ if (psk->ticketAge != 0) { @@ -6088,6 +6116,9 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, byte binderKey[WC_MAX_DIGEST_SIZE]; byte binder[WC_MAX_DIGEST_SIZE]; word32 binderLen; +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) + int certWithExternOffered = 0; +#endif #ifdef NO_PSK (void) suite; /* to avoid unused var warning when not used */ @@ -6102,6 +6133,10 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, WOLFSSL_MSG("No pre shared extension keys found"); return BAD_FUNC_ARG; } +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) + certWithExternOffered = + TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL; +#endif /* Look through all client's pre-shared keys for a match. */ for (current = (PreSharedKey*)ext->data; current != NULL; @@ -6146,6 +6181,26 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, sizeof(psk_sess_free_cb_ctx)); } if (ret == WOLFSSL_TICKET_RET_OK) { +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(HAVE_SESSION_TICKET) + /* RFC 8773bis Sect. 5.1: all PSKs listed alongside + * tls_cert_with_extern_psk MUST be external PSKs. A successfully + * decrypted session ticket identity is a resumption PSK, so the + * server MUST abort with illegal_parameter regardless of whether + * the ticket would otherwise be acceptable. Check here, before + * DoClientTicketFinalize, to avoid polluting ssl->session with + * ticket state that will not be used. */ + if (certWithExternOffered) { + if (current->sess_free_cb != NULL) { + current->sess_free_cb(ssl, current->sess, + ¤t->sess_free_cb_ctx); + current->sess = NULL; + XMEMSET(¤t->sess_free_cb_ctx, 0, + sizeof(psk_sess_free_cb_ctx)); + } + WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); + return PSK_KEY_ERROR; + } +#endif ret = DoClientTicketCheck(ssl, current, ssl->timeout, suite); if (ret == 0) DoClientTicketFinalize(ssl, current->it, current->sess); @@ -6372,6 +6427,12 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, if (*usingPSK != 0) { word32 modes; +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + int usingCertWithExternPsk = 0; + TLSX* certExt = NULL; + TLSX* pskExt = NULL; + PreSharedKey* chosenPsk = NULL; +#endif #ifdef WOLFSSL_EARLY_DATA TLSX* extEarlyData; @@ -6405,14 +6466,62 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, } modes = ext->val; + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + certExt = TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK); + if (certExt != NULL) { + pskExt = TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY); + if (pskExt != NULL) + chosenPsk = (PreSharedKey*)pskExt->data; + while (chosenPsk != NULL && !chosenPsk->chosen) + chosenPsk = chosenPsk->next; + if (chosenPsk == NULL || chosenPsk->resumption) { + WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); + return PSK_KEY_ERROR; + } + if ((modes & (1 << PSK_DHE_KE)) == 0) { + WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); + return PSK_KEY_ERROR; + } + usingCertWithExternPsk = 1; + ssl->options.certWithExternPsk = 1; + ret = PickHashSigAlgo(ssl, clSuites->hashSigAlgo, + clSuites->hashSigAlgoSz, 1); + if (ret != 0) + return ret; + ssl->options.sendVerify = SEND_CERT; + certExt->resp = 1; + } + else { + ssl->options.certWithExternPsk = 0; + } + #endif + +#ifndef HAVE_SUPPORTED_CURVES + #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (usingCertWithExternPsk) { + WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR); + return PSK_KEY_ERROR; + } + #endif +#endif #ifdef HAVE_SUPPORTED_CURVES ext = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); /* Use (EC)DHE for forward-security if possible. */ - if ((modes & (1 << PSK_DHE_KE)) != 0 && !ssl->options.noPskDheKe && - ext != NULL) { - /* Only use named group used in last session. */ - ssl->namedGroup = ssl->session->namedGroup; - + if (((modes & (1 << PSK_DHE_KE)) != 0 && !ssl->options.noPskDheKe && + ext != NULL) +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + || usingCertWithExternPsk +#endif + ) { + if (ext == NULL) { + WOLFSSL_ERROR_VERBOSE(EXT_MISSING); + return EXT_MISSING; + } + /* Resumption path uses previous session group. */ +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (!usingCertWithExternPsk) +#endif + ssl->namedGroup = ssl->session->namedGroup; *usingPSK = 2; /* generate new ephemeral key */ } else if (ssl->options.onlyPskDheKe) { @@ -6434,6 +6543,10 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, } #ifdef WOLFSSL_PSK_ID_PROTECTION else { +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + TLSX_Remove(&ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK, ssl->heap); + ssl->options.certWithExternPsk = 0; +#endif #ifndef NO_CERTS if (ssl->buffers.certChainCnt != 0) return 0; @@ -7260,7 +7373,11 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #if (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ defined(HAVE_TLS_EXTENSIONS) - if (!args->usingPSK) + if (!args->usingPSK +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + || ssl->options.certWithExternPsk +#endif + ) #endif { #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) @@ -12639,7 +12756,11 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) /* Server's authenticating with PSK must not send this. */ if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.serverState == SERVER_CERT_COMPLETE && - ssl->options.pskNegotiated) { + ssl->options.pskNegotiated +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + && !ssl->options.certWithExternPsk +#endif + ) { WOLFSSL_MSG("Certificate received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; @@ -12703,7 +12824,11 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Server's authenticating with PSK must not send this. */ - if (ssl->options.pskNegotiated) { + if (ssl->options.pskNegotiated +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + && !ssl->options.certWithExternPsk +#endif + ) { WOLFSSL_MSG("CertificateRequest received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; @@ -12744,7 +12869,11 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) /* Server's authenticating with PSK must not send this. */ - if (ssl->options.pskNegotiated) { + if (ssl->options.pskNegotiated +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + && !ssl->options.certWithExternPsk +#endif + ) { WOLFSSL_MSG("CertificateVerify received while using PSK"); WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); return SANITY_MSG_E; @@ -12799,11 +12928,25 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) * using PSK. */ #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) if (ssl->options.pskNegotiated) { - if (ssl->options.serverState != +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + if (ssl->options.certWithExternPsk) { + if (ssl->options.serverState != + SERVER_CERT_VERIFY_COMPLETE) { + WOLFSSL_MSG("Finished received out of order - " + "cert_with_extern_psk"); + WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); + return OUT_OF_ORDER_E; + } + } + else +#endif + { + if (ssl->options.serverState != SERVER_ENCRYPTED_EXTENSIONS_COMPLETE) { - WOLFSSL_MSG("Finished received out of order - PSK"); - WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); - return OUT_OF_ORDER_E; + WOLFSSL_MSG("Finished received out of order - PSK"); + WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E); + return OUT_OF_ORDER_E; + } } } else @@ -12841,7 +12984,11 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type) } #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - if (!ssl->options.pskNegotiated) + if (!ssl->options.pskNegotiated +#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK + || ssl->options.certWithExternPsk +#endif + ) #endif { /* Must have received a Certificate message from client if diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 34ddcc94c81..7c8f94c6b96 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -863,6 +863,196 @@ int test_tls13_apis(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) +int test_tls13_cert_with_extern_psk_apis(void) +{ + EXPECT_DECLS; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(NULL, 0), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(NULL, 0), WOLFSSL_FAILURE); + + ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + ExpectNotNull(ctx); + ssl = wolfSSL_new(ctx); + ExpectNotNull(ssl); + + if (EXPECT_SUCCESS()) { + /* Any non-zero value enables cert_with_extern_psk. */ + ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, -1), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, 2), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, -1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, 1), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 1), WOLFSSL_SUCCESS); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + + return EXPECT_RESULT(); +} +#else +int test_tls13_cert_with_extern_psk_apis(void) +{ + return TEST_SKIPPED; +} +#endif + +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* 32-byte external PSK (SHA-256 digest size) used by cwep test callbacks. */ +static const unsigned char test_tls13_cwep_psk[32] = { + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A +}; + +static unsigned int test_tls13_cwep_client_cb(WOLFSSL* ssl, const char* hint, + char* identity, unsigned int id_max_len, unsigned char* key, + unsigned int key_max_len) +{ + (void)ssl; + (void)hint; + if (id_max_len == 0 || key_max_len < sizeof(test_tls13_cwep_psk)) + return 0; + XSTRNCPY(identity, "cwep_client", id_max_len); + XMEMCPY(key, test_tls13_cwep_psk, sizeof(test_tls13_cwep_psk)); + return (unsigned int)sizeof(test_tls13_cwep_psk); +} + +static unsigned int test_tls13_cwep_server_cb(WOLFSSL* ssl, const char* id, + unsigned char* key, unsigned int key_max_len) +{ + (void)ssl; + if (key_max_len < sizeof(test_tls13_cwep_psk) || id == NULL) + return 0; + if (XSTRCMP(id, "cwep_client") != 0) + return 0; + XMEMCPY(key, test_tls13_cwep_psk, sizeof(test_tls13_cwep_psk)); + return (unsigned int)sizeof(test_tls13_cwep_psk); +} +#endif + +int test_tls13_cert_with_extern_psk_handshake(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); +#if defined(HAVE_ECC) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) + ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile, + CERT_FILETYPE) == WOLFSSL_SUCCESS); + ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile, + CERT_FILETYPE) == WOLFSSL_SUCCESS); +#endif + wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(ssl_c->options.pskNegotiated, 1); + ExpectIntEQ(ssl_s->options.pskNegotiated, 1); + ExpectIntEQ(ssl_c->options.certWithExternPsk, 1); + ExpectIntEQ(ssl_s->options.certWithExternPsk, 1); + ExpectIntEQ(ssl_c->msgsReceived.got_certificate, 1); + ExpectIntEQ(ssl_c->msgsReceived.got_certificate_verify, 1); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +int test_tls13_cert_with_extern_psk_requires_key_share(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); +#if defined(HAVE_ECC) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) + ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile, + CERT_FILETYPE) == WOLFSSL_SUCCESS); + ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile, + CERT_FILETYPE) == WOLFSSL_SUCCESS); +#endif + wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS); + /* Omit key_share in CH1 to force the server to send an HRR. */ + ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS); + + /* CH1: client -> server (no key_share). */ + ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), + WOLFSSL_ERROR_WANT_READ); + + /* HRR: server reads CH1, sends HRR requesting a key_share group. */ + ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.serverState, + SERVER_HELLO_RETRY_REQUEST_COMPLETE); + + /* Complete the handshake: client sends CH2 (with key_share), server + * responds with SH + cert + cert-verify + Finished, client finishes. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + + /* Verify that cert_with_extern_psk was negotiated end-to-end. */ + ExpectIntEQ(ssl_c->options.pskNegotiated, 1); + ExpectIntEQ(ssl_s->options.pskNegotiated, 1); + ExpectIntEQ(ssl_c->options.certWithExternPsk, 1); + ExpectIntEQ(ssl_s->options.certWithExternPsk, 1); + ExpectIntEQ(ssl_c->msgsReceived.got_certificate, 1); + ExpectIntEQ(ssl_c->msgsReceived.got_certificate_verify, 1); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ !defined(NO_WOLFSSL_SERVER) && defined(HAVE_ECC) && \ defined(BUILD_TLS_AES_128_GCM_SHA256) && \ @@ -3467,4 +3657,3 @@ int test_tls13_derive_keys_no_key(void) return EXPECT_RESULT(); } - diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index d8b95a41906..1a30f0d0406 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -42,6 +42,9 @@ int test_tls13_plaintext_alert(void); int test_tls13_warning_alert_is_fatal(void); int test_tls13_cert_req_sigalgs(void); int test_tls13_derive_keys_no_key(void); +int test_tls13_cert_with_extern_psk_apis(void); +int test_tls13_cert_with_extern_psk_handshake(void); +int test_tls13_cert_with_extern_psk_requires_key_share(void); #define TEST_TLS13_DECLS \ TEST_DECL_GROUP("tls13", test_tls13_apis), \ @@ -61,6 +64,9 @@ int test_tls13_derive_keys_no_key(void); TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert), \ TEST_DECL_GROUP("tls13", test_tls13_warning_alert_is_fatal), \ TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs), \ - TEST_DECL_GROUP("tls13", test_tls13_derive_keys_no_key) + TEST_DECL_GROUP("tls13", test_tls13_derive_keys_no_key), \ + TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_apis), \ + TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_handshake), \ + TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_requires_key_share) #endif /* WOLFCRYPT_TEST_TLS13_H */ diff --git a/tests/test-fails.conf b/tests/test-fails.conf index 955a6c67ba5..8e6b19c32ef 100644 --- a/tests/test-fails.conf +++ b/tests/test-fails.conf @@ -213,3 +213,13 @@ -v 4 -l TLS13-AES128-GCM-SHA256 -x + +# server TLSv1.3 PSK with certificates invalid with -K +-v 4 +--psk-with-certs +-K + +# client TLSv1.3 PSK with certificates invalid with -K +-v 4 +--psk-with-certs +-K diff --git a/tests/test-tls13-psk.conf b/tests/test-tls13-psk.conf index 46a37047058..ccedf208540 100644 --- a/tests/test-tls13-psk.conf +++ b/tests/test-tls13-psk.conf @@ -75,6 +75,19 @@ -l TLS13-AES256-GCM-SHA384 --onlyPskDheKe +# server TLSv1.3 PSK with certificates (cert_with_extern_psk) +-v 4 +-s +--psk-with-certs +-l TLS13-AES128-GCM-SHA256 +-d + +# client TLSv1.3 PSK with certificates (cert_with_extern_psk) +-v 4 +-s +--psk-with-certs +-l TLS13-AES128-GCM-SHA256 + # Disabling ChaCha20 results in failures. # server TLSv1.3 PSK # CHACHA20 only supported diff --git a/wolfssl/internal.h b/wolfssl/internal.h index e12bdc12396..f365e6e57c0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2961,6 +2961,7 @@ typedef struct Options Options; #define TLSXT_SERVER_CERTIFICATE 0x0014 /* RFC8446 */ #define TLSXT_ENCRYPT_THEN_MAC 0x0016 /* RFC 7366 */ #define TLSXT_EXTENDED_MASTER_SECRET 0x0017 /* HELLO_EXT_EXTMS */ +#define TLSXT_CERT_WITH_EXTERN_PSK 0x0021 /* RFC 8773bis */ #define TLSXT_SESSION_TICKET 0x0023 #define TLSXT_PRE_SHARED_KEY 0x0029 #define TLSXT_EARLY_DATA 0x002a @@ -3018,6 +3019,9 @@ typedef enum { #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) TLSX_PSK_KEY_EXCHANGE_MODES = TLSXT_PSK_KEY_EXCHANGE_MODES, + #if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + TLSX_CERT_WITH_EXTERN_PSK = TLSXT_CERT_WITH_EXTERN_PSK, + #endif #endif #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) TLSX_CERTIFICATE_AUTHORITIES = TLSXT_CERTIFICATE_AUTHORITIES, @@ -3741,6 +3745,9 @@ WOLFSSL_LOCAL int TLSX_PreSharedKey_Use(TLSX** extensions, const byte* identity, void* heap); WOLFSSL_LOCAL int TLSX_PreSharedKey_Parse_ClientHello(TLSX** extensions, const byte* input, word16 length, void* heap); +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && defined(WOLFSSL_TLS13) +WOLFSSL_LOCAL int TLSX_Cert_With_Extern_Psk_Use(WOLFSSL* ssl); +#endif /* The possible Pre-Shared Key key exchange modes. */ enum PskKeyExchangeMode { @@ -3940,6 +3947,9 @@ struct WOLFSSL_CTX { #ifdef HAVE_SUPPORTED_CURVES byte onlyPskDheKe:1; /* Only use (EC)DHE with PSK */ #endif +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + byte certWithExternPsk:1; /* Use tls_cert_with_extern_psk extension */ +#endif #endif #endif /* WOLFSSL_TLS13 */ byte mutualAuth:1; /* Mutual authentication required */ @@ -5069,6 +5079,9 @@ struct Options { #ifdef HAVE_SUPPORTED_CURVES word16 onlyPskDheKe:1; /* Only use (EC)DHE with PSK */ #endif +#if defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + word16 certWithExternPsk:1; /* Cert auth with external PSK */ +#endif #endif word16 partialWrite:1; /* only one msg per write call */ word16 quietShutdown:1; /* don't send close notify */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 1c0301ab66e..7422480b553 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -3205,6 +3205,11 @@ enum { /* ssl Constants */ wc_psk_server_tls13_callback cb); WOLFSSL_API void wolfSSL_set_psk_server_tls13_callback(WOLFSSL* ssl, wc_psk_server_tls13_callback cb); +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) + WOLFSSL_API int wolfSSL_CTX_set_cert_with_extern_psk(WOLFSSL_CTX* ctx, + int state); + WOLFSSL_API int wolfSSL_set_cert_with_extern_psk(WOLFSSL* ssl, int state); #endif WOLFSSL_API void* wolfSSL_get_psk_callback_ctx(WOLFSSL* ssl); WOLFSSL_API int wolfSSL_set_psk_callback_ctx(WOLFSSL* ssl, void* psk_ctx); @@ -6283,6 +6288,12 @@ WOLFSSL_API const unsigned char* wolfSSL_dtls_cid_parse(const unsigned char* msg #ifndef WOLFSSL_PKM_MIN_SIZE_SERVER #define WOLFSSL_PKM_MIN_SIZE_SERVER 0 #endif +#ifndef WOLFSSL_CWEP_MIN_SIZE_CLIENT + #define WOLFSSL_CWEP_MIN_SIZE_CLIENT 0 +#endif +#ifndef WOLFSSL_CWEP_MIN_SIZE_SERVER + #define WOLFSSL_CWEP_MIN_SIZE_SERVER 0 +#endif #ifndef WOLFSSL_CSR2_MIN_SIZE_CLIENT #define WOLFSSL_CSR2_MIN_SIZE_CLIENT 7 #endif diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 32f08095fd5..0308358d116 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5054,6 +5054,14 @@ extern void uITRON4_free(void *p) ; !defined(HAVE_SESSION_TICKET) && defined(NO_PSK) #error "Early data requires session tickets (HAVE_SESSION_TICKET) or PSK" #endif +#if !defined(WOLFCRYPT_ONLY) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + !defined(WOLFSSL_TLS13) + #error "cert_with_extern_psk requires TLS 1.3 (WOLFSSL_TLS13)" +#endif +#if !defined(WOLFCRYPT_ONLY) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ + defined(NO_PSK) + #error "cert_with_extern_psk requires PSK support" +#endif /* DES3 TLS Suite Rule - auto-disable DES3 TLS suites when DES3 is disabled */ #if !defined(WOLFCRYPT_ONLY) && !defined(NO_DES3_TLS_SUITES) && \