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
1 change: 1 addition & 0 deletions src/dtls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,7 @@ static int Dtls13InitChaChaCipher(RecordNumberCiphers* c, byte* key,

ret = wc_Chacha_SetKey(c->chacha, key, keySize);
if (ret != 0) {
ForceZero(c->chacha, sizeof(ChaCha));
XFREE(c->chacha, heap, DYNAMIC_TYPE_CIPHER);
c->chacha = NULL;
}
Expand Down
140 changes: 140 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3304,6 +3304,10 @@ void FreeCiphers(WOLFSSL* ssl)
ssl->dtlsRecordNumberDecrypt.aes = NULL;
#endif /* BUILD_AES */
#ifdef HAVE_CHACHA
if (ssl->dtlsRecordNumberEncrypt.chacha)
ForceZero(ssl->dtlsRecordNumberEncrypt.chacha, sizeof(ChaCha));
if (ssl->dtlsRecordNumberDecrypt.chacha)
ForceZero(ssl->dtlsRecordNumberDecrypt.chacha, sizeof(ChaCha));
XFREE(ssl->dtlsRecordNumberEncrypt.chacha, ssl->heap, DYNAMIC_TYPE_CIPHER);
XFREE(ssl->dtlsRecordNumberDecrypt.chacha, ssl->heap, DYNAMIC_TYPE_CIPHER);
ssl->dtlsRecordNumberEncrypt.chacha = NULL;
Expand Down Expand Up @@ -18005,6 +18009,15 @@ static int DoHelloRequest(WOLFSSL* ssl, word32 size)
}
#ifdef HAVE_SECURE_RENEGOTIATION
else if (ssl->secure_renegotiation && ssl->secure_renegotiation->enabled) {
/* WOLFSSL_OP_NO_RENEGOTIATION: caller opted into rejecting
* peer-initiated renegotiation. Respond with a no_renegotiation
* warning alert instead of starting a secure renegotiation. */
if (ssl->options.mask & WOLFSSL_OP_NO_RENEGOTIATION) {
WOLFSSL_MSG("Rejecting HelloRequest: WOLFSSL_OP_NO_RENEGOTIATION");
WOLFSSL_LEAVE("DoHelloRequest", 0);
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
return SendAlert(ssl, alert_warning, no_renegotiation);
}
ssl->secure_renegotiation->startScr = 1;
WOLFSSL_LEAVE("DoHelloRequest", 0);
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
Expand Down Expand Up @@ -18737,6 +18750,17 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ssl->secure_renegotiation &&
ssl->secure_renegotiation->enabled)
{
/* WOLFSSL_OP_NO_RENEGOTIATION: caller opted into rejecting
* peer-initiated renegotiation. RFC 5246 7.2.2: no_renegotiation is a
* warning-level alert, so refuse the renegotiation but keep the
* established connection rather than aborting it. Skip the ClientHello
* body and leave handshake state untouched, mirroring the client-side
* HelloRequest refusal in DoHelloRequest(). */
if (ssl->options.mask & WOLFSSL_OP_NO_RENEGOTIATION) {
WOLFSSL_MSG("Refusing renegotiation: WOLFSSL_OP_NO_RENEGOTIATION");
*inOutIdx = expectedIdx;
return SendAlert(ssl, alert_warning, no_renegotiation);
}
WOLFSSL_MSG("Reset handshake state");
XMEMSET(&ssl->msgsReceived, 0, sizeof(MsgsReceived));
ssl->options.serverState = NULL_STATE;
Expand All @@ -18745,6 +18769,8 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ssl->options.acceptState = ACCEPT_FIRST_REPLY_DONE;
ssl->options.handShakeState = NULL_STATE;
ssl->secure_renegotiation->cache_status = SCR_CACHE_NEEDED;
/* Reset for the renegotiation_info presence check below. */
ssl->secure_renegotiation->renegInfoSeen = 0;

ret = InitHandshakeHashes(ssl);
if (ret != 0)
Expand Down Expand Up @@ -22443,6 +22469,24 @@ static void LogAlert(int type)
}

/* process alert, return level */
#ifndef NO_SESSION_CACHE
/* RFC 5246 Section 7.2.2: a TLS 1.2 session whose connection is terminated by a
* fatal alert MUST be invalidated so it cannot be resumed. (TLS 1.3 RFC 8446
* Section 6.2 only requires closing the connection, but evicting here too is
* sound defense-in-depth.) Evict the cached session (which also drops any
* associated ticket). Acts on an established connection or an in-progress
* resumption - both reference a cached session; a brand-new full handshake has
* no cached session to remove. */
static void InvalidateSessionOnFatalAlert(WOLFSSL* ssl)
{
if (ssl == NULL || ssl->ctx == NULL || ssl->session == NULL)
return;
if (!ssl->options.handShakeDone && !ssl->options.resuming)
return;
(void)wolfSSL_SSL_CTX_remove_session(ssl->ctx, ssl->session);
Comment thread
julek-wolfssl marked this conversation as resolved.
}
#endif /* !NO_SESSION_CACHE */

static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
{
byte level;
Expand Down Expand Up @@ -22492,6 +22536,15 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
code != close_notify && code != user_canceled) {
ssl->options.isClosed = 1;
}
#ifndef NO_SESSION_CACHE
/* A fatal alert immediately terminates the connection; invalidate the
* session so it cannot be used to establish new connections. In TLS 1.3
* all error alerts are implicitly fatal (RFC 8446 6.2). */
if (code != close_notify &&
(level == alert_fatal ||
(IsAtLeastTLSv1_3(ssl->version) && code != user_canceled)))
InvalidateSessionOnFatalAlert(ssl);
#endif
}

if (++ssl->options.alertCount >= WOLFSSL_ALERT_COUNT_MAX) {
Expand Down Expand Up @@ -23149,6 +23202,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
/* see if sending SSLv2 client hello */
if ( ssl->options.side == WOLFSSL_SERVER_END &&
ssl->options.clientState == NULL_STATE &&
!ssl->options.handShakeDone &&
ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
!= handshake &&
/* change_cipher_spec here is an error but we want to handle
Expand Down Expand Up @@ -24598,6 +24652,19 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
#endif

#ifndef WOLFSSL_NO_TLS12
/* RFC 5246 6.1: record sequence numbers MUST NOT wrap. Refuse to emit a
* record once the write sequence number has reached its maximum value
* (2^64-1); reusing sequence number 0 with the same keys would break the
* record protection. The caller must renegotiate or close the connection
* instead. DTLS sequence numbers are epoch-scoped and handled elsewhere. */
if (!sizeOnly && !ssl->options.dtls &&
ssl->keys.sequence_number_hi == 0xFFFFFFFFU &&
ssl->keys.sequence_number_lo == 0xFFFFFFFFU) {
WOLFSSL_MSG("TLS write sequence number would wrap");
WOLFSSL_ERROR_VERBOSE(SEQUENCE_NUMBER_E);
return SEQUENCE_NUMBER_E;
}

#ifdef WOLFSSL_ASYNC_CRYPT
ret = WC_NO_PENDING_E;
if (asyncOkay) {
Expand Down Expand Up @@ -27362,6 +27429,17 @@ int SendAlert(WOLFSSL* ssl, int severity, int type)
return BAD_FUNC_ARG;
}

/* InvalidateSessionOnFatalAlert() is defined in the !NO_TLS section, so the
* guard here must match (with NO_TLS there are no TLS sessions to evict). */
#if !defined(NO_SESSION_CACHE) && !defined(NO_TLS)
/* RFC 5246 Section 7.2.2: a fatal alert terminates the connection;
* invalidate the established session so it cannot be resumed. Do this as
* soon as the fatal alert is generated, before the pendingAlert/backpressure
* handling below which can return early without sending the alert now. */
if (severity == alert_fatal)
InvalidateSessionOnFatalAlert(ssl);
#endif

if (ssl->pendingAlert.level != alert_none) {
ret = RetrySendAlert(ssl);
if (ret != 0) {
Expand Down Expand Up @@ -28044,6 +28122,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)

case ECH_REQUIRED_E:
return "ECH offered but rejected by server";

case SEQUENCE_NUMBER_E:
return "Record sequence number would wrap";
}

return "unknown error number";
Expand Down Expand Up @@ -32199,6 +32280,41 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
}
else {
if (DSH_CheckSessionId(ssl)) {
/* RFC 7627 5.3: resumed session EMS state must match the
* ServerHello; abort on mismatch. Stateless (session-ticket)
* resumption - e.g. EAP-FAST, whose PAC is a TLS ticket - binds
* the EMS state in the ticket and need not re-advertise the
* extension, so this applies only to session-ID resumption. */
if (
#ifdef HAVE_SESSION_TICKET
ssl->session->ticketLen == 0 &&
#endif
ssl->session->haveEMS != ssl->options.haveEMS) {
WOLFSSL_MSG("Resumed session EMS state does not match "
"ServerHello EMS state");
SendAlert(ssl, alert_fatal, handshake_failure);
WOLFSSL_ERROR_VERBOSE(EXT_MASTER_SECRET_NEEDED_E);
return EXT_MASTER_SECRET_NEEDED_E;
}
#ifndef NO_RESUME_SUITE_CHECK
/* RFC 5246 Section 7.4.1.3: on resumption the ServerHello
* reuses the previously negotiated cipher suite. Reject a
* server that resumes the session but selects a different
* suite. Skipped for ticket resumption (suite is bound in the
* ticket), consistent with the EMS check above. */
if (
#ifdef HAVE_SESSION_TICKET
ssl->session->ticketLen == 0 &&
#endif
(ssl->options.cipherSuite0 != ssl->session->cipherSuite0 ||
ssl->options.cipherSuite != ssl->session->cipherSuite)) {
WOLFSSL_MSG("Resumed session cipher suite does not match "
"ServerHello cipher suite");
SendAlert(ssl, alert_fatal, illegal_parameter);
WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR);
return MATCH_SUITE_ERROR;
}
#endif /* NO_RESUME_SUITE_CHECK */
if (SetCipherSpecs(ssl) == 0) {
if (!HaveUniqueSessionObj(ssl)) {
WOLFSSL_MSG("Unable to have unique session object");
Expand Down Expand Up @@ -38545,6 +38661,17 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
0) {
TLSX* extension;

#ifdef HAVE_SECURE_RENEGOTIATION
/* SCSV not allowed on a renegotiation ClientHello (RFC 5746 3.5). */
if (ssl->secure_renegotiation &&
ssl->secure_renegotiation->enabled &&
ssl->secure_renegotiation->verifySet) {
WOLFSSL_MSG("SCSV received on renegotiation ClientHello");
SendAlert(ssl, alert_fatal, handshake_failure);
ret = SECURE_RENEGOTIATION_E;
goto out;
}
#endif
/* check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV suite */
ret = TLSX_AddEmptyRenegotiationInfo(&ssl->extensions, ssl->heap);
if (ret != WOLFSSL_SUCCESS) {
Expand Down Expand Up @@ -38775,6 +38902,19 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
*inOutIdx = begin + helloSz; /* skip extensions */
}

#ifdef HAVE_SECURE_RENEGOTIATION
/* renegotiation_info MUST be present on a renegotiation (RFC 5746 3.7). */
if (ssl->secure_renegotiation &&
ssl->secure_renegotiation->enabled &&
ssl->secure_renegotiation->verifySet &&
!ssl->secure_renegotiation->renegInfoSeen) {
WOLFSSL_MSG("Renegotiation ClientHello missing renegotiation_info");
SendAlert(ssl, alert_fatal, handshake_failure);
ret = SECURE_RENEGOTIATION_E;
goto out;
}
#endif /* HAVE_SECURE_RENEGOTIATION */

#ifdef WOLFSSL_DTLS_CID
if (ssl->options.useDtlsCID)
DtlsCIDOnExtensionsParsed(ssl);
Expand Down
3 changes: 3 additions & 0 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -6142,6 +6142,9 @@ static int TLSX_SecureRenegotiation_Parse(WOLFSSL* ssl, const byte* input,
if (ret == WOLFSSL_SUCCESS)
ret = 0;
}
/* renegotiation_info seen (checked by DoClientHello, RFC 5746 3.7) */
if (ssl->secure_renegotiation != NULL)
ssl->secure_renegotiation->renegInfoSeen = 1;
if (ret != 0 && ret != WC_NO_ERR_TRACE(SECURE_RENEGOTIATION_E)) {
}
else if (ssl->secure_renegotiation == NULL) {
Expand Down
6 changes: 5 additions & 1 deletion src/tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -6021,7 +6021,7 @@ static int DoTls13EncryptedExtensions(WOLFSSL* ssl, const byte* input,
i += OPAQUE16_LEN;

/* Extension data. */
if (i - begin + totalExtSz > totalSz)
if (i - begin + totalExtSz != totalSz)
return BUFFER_ERROR;
if ((ret = TLSX_Parse(ssl, input + i, totalExtSz, encrypted_extensions,
NULL))) {
Expand Down Expand Up @@ -6134,6 +6134,10 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input,
}
*inOutIdx += len;

/* No trailing bytes allowed (RFC 8446 4.3.2). */
if ((*inOutIdx - begin) != size)
return BUFFER_ERROR;

#ifdef WOLFSSL_CERT_SETUP_CB
if ((ret = CertSetupCbWrapper(ssl)) != 0)
return ret;
Expand Down
2 changes: 2 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -34420,6 +34420,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_tls12_chacha20_poly1305_bad_tag),
TEST_DECL(test_tls13_null_cipher_bad_hmac),
TEST_DECL(test_scr_verify_data_mismatch),
TEST_DECL(test_scr_no_renegotiation_option),
TEST_DECL(test_helloRequest_no_renegotiation_option),
TEST_DECL(test_tls13_hrr_cipher_suite_mismatch),
TEST_DECL(test_tls13_ticket_age_out_of_window),
TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret),
Expand Down
40 changes: 38 additions & 2 deletions tests/api/test_ossl_rsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,24 @@ int test_wolfSSL_RSA_padding_add_PKCS1_PSS(void)
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em,
RSA_PSS_SALTLEN_DIGEST), 1);

/* Negative test: a tampered PSS encoding must be rejected. Flip a byte in
* the encoded message, confirm failure, then restore and re-verify. */
em[0] ^= 0xFFU;
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em,
RSA_PSS_SALTLEN_DIGEST), 0);
em[0] ^= 0xFFU;
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em,
RSA_PSS_SALTLEN_DIGEST), 1);

/* Negative test: a tampered hash must be rejected by PSS verification. */
{
unsigned char badHash[WC_SHA256_DIGEST_SIZE];
XMEMCPY(badHash, mHash, sizeof(badHash));
badHash[0] ^= 0xFFU;
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, badHash, EVP_sha256(), em,
RSA_PSS_SALTLEN_DIGEST), 0);
}

ExpectIntEQ(RSA_padding_add_PKCS1_PSS(rsa, em, mHash, EVP_sha256(),
RSA_PSS_SALTLEN_MAX_SIGN), 1);
ExpectIntEQ(RSA_verify_PKCS1_PSS(rsa, mHash, EVP_sha256(), em,
Expand Down Expand Up @@ -696,8 +714,8 @@ int test_wolfSSL_RSA_verify(void)
RSA *pubKey = NULL;
X509 *cert = NULL;
const char *text = "Hello wolfSSL !";
unsigned char hash[SHA256_DIGEST_LENGTH];
unsigned char signature[2048/8];
unsigned char hash[SHA256_DIGEST_LENGTH] = {0};
unsigned char signature[2048/8] = {0};
unsigned int signatureLength;
byte *buf = NULL;
BIO *bio = NULL;
Expand Down Expand Up @@ -747,6 +765,24 @@ int test_wolfSSL_RSA_verify(void)
ExpectIntEQ(RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, signature,
signatureLength, pubKey), SSL_SUCCESS);

/* Negative test: a tampered signature must be rejected. Flip a byte in the
* signature, confirm verification fails, then restore it. */
signature[0] ^= 0xFFU;
ExpectIntEQ(RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, signature,
signatureLength, pubKey), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
signature[0] ^= 0xFFU;
/* Sanity: the restored signature verifies again. */
ExpectIntEQ(RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, signature,
signatureLength, pubKey), SSL_SUCCESS);

/* Negative test: a tampered hash must be rejected (the encoded comparison
* string differs). Flip a byte in the hash, confirm failure, then
* restore it. */
hash[0] ^= 0xFFU;
ExpectIntEQ(RSA_verify(NID_sha256, hash, SHA256_DIGEST_LENGTH, signature,
signatureLength, pubKey), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
hash[0] ^= 0xFFU;

ExpectIntEQ(RSA_verify(NID_sha256, NULL, SHA256_DIGEST_LENGTH, NULL,
signatureLength, NULL), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
ExpectIntEQ(RSA_verify(NID_sha256, NULL, SHA256_DIGEST_LENGTH, signature,
Expand Down
Loading
Loading