Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14125,6 +14125,71 @@ int wolfSSL_CTX_no_dhe_psk(WOLFSSL_CTX* ctx);
*/
int wolfSSL_no_dhe_psk(WOLFSSL* ssl);

/*!
\ingroup Setup

\brief This function is called on a TLS v1.3 / DTLS v1.3 context to require
that an external Pre-Shared Key is negotiated for the handshake to succeed.
When set, a handshake that completes without negotiating an external PSK is
aborted with PSK_MISSING_ERROR instead of falling back to a certificate
handshake, so the PSK acts as an additional security factor. The requirement
keys off the external-PSK callback (it has no effect unless one is
registered) and session-ticket resumption is exempt. To preserve forward
secrecy a mandatory external PSK must also use an (EC)DHE key exchange; a
pure psk_ke handshake is rejected with PSK_KEY_ERROR. This applies to TLS 1.3
and DTLS 1.3 only; in (D)TLS 1.2 the use of a PSK is determined by the
negotiated cipher suite, so a mandatory PSK is instead configured by
restricting the cipher suite list to (preferably (EC)DHE-)PSK suites.

\param [in,out] ctx a pointer to a WOLFSSL_CTX structure, created using
wolfSSL_CTX_new().

\return BAD_FUNC_ARG if ctx is NULL or not at least TLS v1.3.
\return 0 if successful.

_Example_
\code
int ret;
WOLFSSL_CTX* ctx;
...
ret = wolfSSL_CTX_require_psk(ctx);
if (ret != 0) {
// failed to make a PSK mandatory
}
\endcode

\sa wolfSSL_require_psk
*/
int wolfSSL_CTX_require_psk(WOLFSSL_CTX* ctx);

/*!
\ingroup Setup

\brief This function is called on a TLS v1.3 / DTLS v1.3 wolfSSL object to
require that an external Pre-Shared Key is negotiated for the handshake to
succeed. See wolfSSL_CTX_require_psk() for the full behaviour.

\param [in,out] ssl a pointer to a WOLFSSL structure, created using
wolfSSL_new().

\return BAD_FUNC_ARG if ssl is NULL or not at least TLS v1.3.
\return 0 if successful.

_Example_
\code
int ret;
WOLFSSL* ssl;
...
ret = wolfSSL_require_psk(ssl);
if (ret != 0) {
// failed to make a PSK mandatory
}
\endcode

\sa wolfSSL_CTX_require_psk
*/
int wolfSSL_require_psk(WOLFSSL* ssl);

/*!
\ingroup IO

Expand Down
5 changes: 5 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -7278,6 +7278,7 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
ssl->options.verifyNone = ctx->verifyNone;
ssl->options.failNoCert = ctx->failNoCert;
ssl->options.failNoCertxPSK = ctx->failNoCertxPSK;
ssl->options.failNoPSK = ctx->failNoPSK;
ssl->options.sendVerify = ctx->sendVerify;

ssl->options.partialWrite = ctx->partialWrite;
Expand Down Expand Up @@ -27954,6 +27955,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
case DUPE_ENTRY_E:
return "duplicate entry error";

case PSK_MISSING_ERROR:
return "psk missing error";

case GETTIME_ERROR:
return "gettimeofday() error";

Expand Down Expand Up @@ -36208,6 +36212,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
return missing_extension;
case WC_NO_ERR_TRACE(MATCH_SUITE_ERROR):
case WC_NO_ERR_TRACE(MISSING_HANDSHAKE_DATA):
case WC_NO_ERR_TRACE(PSK_MISSING_ERROR):
return handshake_failure;
case WC_NO_ERR_TRACE(VERSION_ERROR):
return wolfssl_alert_protocol_version;
Expand Down
90 changes: 88 additions & 2 deletions src/tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -4386,7 +4386,11 @@ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello)
return PSK_KEY_ERROR;
}
}
else if (ssl->options.onlyPskDheKe) {
else if (ssl->options.onlyPskDheKe ||
(ssl->options.failNoPSK && !psk->resumption)) {
/* A mandatory external PSK (failNoPSK) must be combined with
* (EC)DHE for forward secrecy, so reject a pure psk_ke
* negotiation. Session-ticket resumption is exempt. */
WOLFSSL_ERROR_VERBOSE(PSK_KEY_ERROR);
return PSK_KEY_ERROR;
}
Expand Down Expand Up @@ -5905,6 +5909,13 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
while (psk != NULL && !psk->chosen)
psk = psk->next;
if (psk == NULL) {
/* havePSK is only set by an external-PSK callback, so a peer
* relying solely on session-ticket resumption is unaffected. */
if (ssl->options.havePSK && ssl->options.failNoPSK) {
WOLFSSL_MSG("Server did not negotiate a mandatory PSK");
WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR);
return PSK_MISSING_ERROR;
}
ssl->options.resuming = 0;
ssl->arrays->psk_keySz = 0;
XMEMSET(ssl->arrays->psk_key, 0, MAX_PSK_KEY_LEN);
Expand Down Expand Up @@ -6668,6 +6679,16 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
#endif
if (usingPSK)
*usingPSK = 0;

/* No PSK extension at all: if a mandatory external PSK is configured,
* refuse the connection rather than continue without one. havePSK is
* only set by an external-PSK callback, so a peer relying solely on
* session-ticket resumption is unaffected. */
if (ssl->options.havePSK && ssl->options.failNoPSK) {
WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR);
return PSK_MISSING_ERROR;
}

/* Hash data up to binders for deriving binders in PSK extension. */
ret = HashInput(ssl, input, (int)helloSz);
return ret;
Expand Down Expand Up @@ -6729,6 +6750,16 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
#endif

if (!*usingPSK) {
/* No suitable PSK was negotiated. When a mandatory external PSK is
* configured, fail with a dedicated error instead of falling back to a
* certificate handshake. This must run before the no-certificate
* BAD_BINDER check below so a PSK-only server (no cert) still reports
* PSK_MISSING_ERROR. havePSK is only set by an external-PSK callback, so
* a peer relying solely on session-ticket resumption is unaffected. */
if (ssl->options.havePSK && ssl->options.failNoPSK) {
WOLFSSL_ERROR_VERBOSE(PSK_MISSING_ERROR);
return PSK_MISSING_ERROR;
}
#ifndef NO_CERTS
if (ssl->buffers.certificate == NULL
#ifdef WOLFSSL_CERT_SETUP_CB
Expand Down Expand Up @@ -6890,7 +6921,11 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
ssl->namedGroup = ssl->session->namedGroup;
*usingPSK = 2; /* generate new ephemeral key */
}
else if (ssl->options.onlyPskDheKe) {
else if (ssl->options.onlyPskDheKe ||
(ssl->options.failNoPSK && !ssl->options.resuming)) {
/* A mandatory external PSK (failNoPSK) must be combined with
* (EC)DHE for forward secrecy, so reject a pure psk_ke
* negotiation. Session-ticket resumption is exempt. */
return PSK_KEY_ERROR;
}
else
Expand All @@ -6909,6 +6944,8 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
}
else {
#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK
/* If no PSK is found, we remove the extension to make sure it
* is not sent back to the client */
TLSX_Remove(&ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK, ssl->heap);
ssl->options.certWithExternPsk = 0;
#endif
Expand Down Expand Up @@ -11849,6 +11886,21 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#endif

#if !defined(NO_CERTS) && !defined(NO_PSK) && \
defined(WOLFSSL_CERT_WITH_EXTERN_PSK)
/* Verify the server sent a certificate if requested */
if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.pskNegotiated &&
ssl->options.failNoCert) {
if ((TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) != NULL) &&
(!ssl->options.havePeerCert || !ssl->options.havePeerVerify)) {
ret = NO_PEER_CERT;
WOLFSSL_MSG("TLS v1.3 server did not present peer cert");
DoCertFatalAlert(ssl, ret);
goto cleanup;
}
}
#endif

/* check against totalSz */
if (*inOutIdx + size > totalSz) {
ret = BUFFER_E;
Expand Down Expand Up @@ -14965,6 +15017,40 @@ int wolfSSL_only_dhe_psk(WOLFSSL* ssl)
}
#endif /* HAVE_SUPPORTED_CURVES */

/* Require that an external Pre-Shared Key is negotiated for the handshake to
* succeed. TLS 1.3 / DTLS 1.3 only - in (D)TLS 1.2 the use of a PSK is
* determined by the negotiated cipher suite, so a mandatory PSK is configured
* there by restricting the cipher suite list to PSK suites.
*
* ctx The SSL/TLS CTX object.
* returns BAD_FUNC_ARG when ctx is NULL or not at least TLS v1.3, 0 on success.
*/
int wolfSSL_CTX_require_psk(WOLFSSL_CTX* ctx)
{
if (ctx == NULL || !IsAtLeastTLSv1_3(ctx->method->version))
return BAD_FUNC_ARG;

ctx->failNoPSK = 1;

return 0;
}

/* Require that an external Pre-Shared Key is negotiated for the handshake to
* succeed. See wolfSSL_CTX_require_psk().
*
* ssl The SSL/TLS object.
* returns BAD_FUNC_ARG when ssl is NULL or not at least TLS v1.3, 0 on success.
*/
int wolfSSL_require_psk(WOLFSSL* ssl)
{
if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version))
return BAD_FUNC_ARG;

ssl->options.failNoPSK = 1;

return 0;
}

int Tls13UpdateKeys(WOLFSSL* ssl)
{
if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version))
Expand Down
2 changes: 1 addition & 1 deletion tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -28250,7 +28250,7 @@ static int error_test(void)
#endif
{ -9, WC_SPAN1_FIRST_E + 1 },
{ -300, -300 },
{ -335, -336 },
{ -336, -336 },
{ -346, -349 },
{ -356, -356 },
{ -358, -358 },
Expand Down
Loading
Loading