mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 10:50:53 +02:00
Merge pull request #10582 from julek-wolfssl/fenrir-20260602
Fenrir 2026-06-02: TLS/DTLS correctness, resumption & renegotiation safety fixes
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
+182
@@ -3343,6 +3343,10 @@ void FreeCiphers(WOLFSSL* ssl)
|
||||
ssl->dtlsRecordNumberDecrypt.aes = NULL;
|
||||
#endif /* BUILD_AES */
|
||||
#ifdef HAVE_CHACHA
|
||||
if (ssl->dtlsRecordNumberEncrypt.chacha != NULL)
|
||||
ForceZero(ssl->dtlsRecordNumberEncrypt.chacha, sizeof(ChaCha));
|
||||
if (ssl->dtlsRecordNumberDecrypt.chacha != NULL)
|
||||
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;
|
||||
@@ -18049,6 +18053,17 @@ 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) {
|
||||
int ret;
|
||||
WOLFSSL_MSG("Rejecting HelloRequest: WOLFSSL_OP_NO_RENEGOTIATION");
|
||||
ret = SendAlert(ssl, alert_warning, no_renegotiation);
|
||||
WOLFSSL_LEAVE("DoHelloRequest", ret);
|
||||
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
|
||||
return ret;
|
||||
}
|
||||
ssl->secure_renegotiation->startScr = 1;
|
||||
WOLFSSL_LEAVE("DoHelloRequest", 0);
|
||||
WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
|
||||
@@ -18781,6 +18796,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;
|
||||
@@ -18789,6 +18815,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)
|
||||
@@ -22487,6 +22515,32 @@ 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;
|
||||
/* Don't evict on an unauthenticated record: a TLS 1.3 plaintext alert
|
||||
* received under encryption (current record not decrypted) is rejected (or
|
||||
* ignored) by DoAlert, and the teardown alert routes back here. RFC 8446
|
||||
* 6.2 doesn't require TLS 1.3 eviction; TLS 1.2 alerts are plaintext so are
|
||||
* unaffected. */
|
||||
if (IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
|
||||
!ssl->keys.decryptedCur)
|
||||
return;
|
||||
(void)wolfSSL_SSL_CTX_remove_session(ssl->ctx, ssl->session);
|
||||
}
|
||||
#endif /* !NO_SESSION_CACHE */
|
||||
|
||||
static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
|
||||
{
|
||||
byte level;
|
||||
@@ -22593,6 +22647,15 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
|
||||
*/
|
||||
WOLFSSL_ERROR(*type);
|
||||
}
|
||||
#ifndef NO_SESSION_CACHE
|
||||
/* Validated fatal alert: invalidate the session so it can't be resumed
|
||||
* (RFC 5246 7.2.2; in TLS 1.3 all error alerts are fatal, RFC 8446
|
||||
* 6.2). */
|
||||
if (*type != close_notify &&
|
||||
(level == alert_fatal ||
|
||||
(IsAtLeastTLSv1_3(ssl->version) && *type != user_canceled)))
|
||||
InvalidateSessionOnFatalAlert(ssl);
|
||||
#endif
|
||||
}
|
||||
return level;
|
||||
}
|
||||
@@ -23097,6 +23160,52 @@ static void DropAndRestartProcessReply(WOLFSSL* ssl)
|
||||
#endif /* WOLFSSL_DTLS_DROP_STATS */
|
||||
}
|
||||
#endif /* WOLFSSL_DTLS */
|
||||
|
||||
#ifndef WOLFSSL_NO_TLS12
|
||||
/* On a confirmed TLS 1.2 / DTLS 1.2 client resumption, check the abbreviated
|
||||
* ServerHello's EMS state (RFC 7627 5.3) and cipher suite (RFC 5246 7.4.1.3)
|
||||
* match the resumed session. Called once resumption is confirmed - at a renewed
|
||||
* NewSessionTicket (before SetupSession refreshes the cached values) or the
|
||||
* server ChangeCipherSpec. Deferred from ServerHello because a declined ticket
|
||||
* (RFC 5077 3.4) falls back to a full handshake that must not be rejected.
|
||||
* Returns 0 if consistent, else sends a fatal alert and returns an error. */
|
||||
static int CheckResumptionConsistency(WOLFSSL* ssl)
|
||||
{
|
||||
if (ssl->session == NULL) /* nothing to compare against */
|
||||
return 0;
|
||||
/* EMS must match (RFC 7627 5.3); skip EAP-FAST (session-secret callback). */
|
||||
if (
|
||||
#ifdef HAVE_SECRET_CALLBACK
|
||||
!(ssl->sessionSecretCb != NULL
|
||||
#ifdef HAVE_SESSION_TICKET
|
||||
&& ssl->session->ticketLen > 0
|
||||
#endif
|
||||
) &&
|
||||
#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
|
||||
/* Suite must match (RFC 5246 7.4.1.3), tickets included. Skip when no suite
|
||||
* was retained (both zero = TLS_NULL_WITH_NULL_NULL, e.g. EAP-FAST PAC). */
|
||||
if ((ssl->session->cipherSuite0 != 0 || ssl->session->cipherSuite != 0) &&
|
||||
(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 */
|
||||
return 0;
|
||||
}
|
||||
#endif /* !WOLFSSL_NO_TLS12 */
|
||||
|
||||
/* Process input requests. Return 0 is done, 1 is call again to complete, and
|
||||
negative number is error. If allowSocketErr is set, SOCKET_ERROR_E in
|
||||
ssl->error will be whitelisted. This is useful when the connection has been
|
||||
@@ -23211,6 +23320,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
|
||||
@@ -23926,6 +24036,15 @@ default:
|
||||
}
|
||||
}
|
||||
|
||||
/* Server CCS confirms the abbreviated handshake: validate
|
||||
* the resumed session before installing keys. */
|
||||
if (ssl->options.side == WOLFSSL_CLIENT_END &&
|
||||
ssl->options.resuming) {
|
||||
ret = CheckResumptionConsistency(ssl);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssl->keys.encryptionOn = 1;
|
||||
|
||||
/* setup decrypt keys for following messages */
|
||||
@@ -24660,6 +24779,19 @@ int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_NO_TLS12
|
||||
/* RFC 5246 6.1: sequence numbers MUST NOT wrap. GetSEQIncrement post-
|
||||
* increments, so refuse at hi == lo == 0xFFFFFFFF (2^64-1): that last legal
|
||||
* value is deliberately sacrificed to avoid wrapping to 0 and reusing
|
||||
* sequence number 0. The caller must renegotiate or close. 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) {
|
||||
@@ -27552,6 +27684,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) {
|
||||
@@ -28234,6 +28377,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";
|
||||
@@ -32403,6 +32549,9 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
|
||||
}
|
||||
else {
|
||||
if (DSH_CheckSessionId(ssl)) {
|
||||
/* EMS/suite consistency is checked once resumption is confirmed
|
||||
* (CheckResumptionConsistency), not here: a ticket the server
|
||||
* declines (RFC 5077 3.4) must fall back to a full handshake. */
|
||||
if (SetCipherSpecs(ssl) == 0) {
|
||||
if (!HaveUniqueSessionObj(ssl)) {
|
||||
WOLFSSL_MSG("Unable to have unique session object");
|
||||
@@ -35668,6 +35817,15 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
|
||||
return SESSION_TICKET_EXPECT_E;
|
||||
}
|
||||
|
||||
/* A renewed ticket while resuming confirms resumption; check before the
|
||||
* SetupSession() below refreshes the cached suite/EMS and masks a downgrade.
|
||||
* (The ChangeCipherSpec check covers the no-renewal case.) */
|
||||
if (ssl->options.resuming) {
|
||||
ret = CheckResumptionConsistency(ssl);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (OPAQUE32_LEN > size)
|
||||
return BUFFER_ERROR;
|
||||
|
||||
@@ -38793,6 +38951,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) {
|
||||
@@ -39045,6 +39214,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);
|
||||
|
||||
@@ -6277,6 +6277,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) {
|
||||
|
||||
+5
-2
@@ -6030,7 +6030,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))) {
|
||||
@@ -6168,6 +6168,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;
|
||||
|
||||
/* RFC 8446 Section 4.3.2: the signature_algorithms extension MUST be
|
||||
* present in a CertificateRequest. */
|
||||
if (peerSuites.hashSigAlgoSz == 0) {
|
||||
@@ -6175,7 +6179,6 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input,
|
||||
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
|
||||
return INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_CERT_SETUP_CB
|
||||
if ((ret = CertSetupCbWrapper(ssl)) != 0)
|
||||
return ret;
|
||||
|
||||
@@ -35193,9 +35193,12 @@ TEST_CASE testCases[] = {
|
||||
#endif
|
||||
TEST_DECL(test_tls_ems_downgrade),
|
||||
TEST_DECL(test_tls_ems_resumption_downgrade),
|
||||
TEST_DECL(test_tls_ems_resumption_server_downgrade),
|
||||
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),
|
||||
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
|
||||
@@ -1083,6 +1083,168 @@ int test_tls12_etm_failed_resumption(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* RFC 5246 7.4.1.3: a server resuming a TLS 1.2 session ticket MUST reuse the
|
||||
* session's cipher suite. The ticket is opaque to the client, so the client
|
||||
* cannot rely on the suite being bound inside it and must compare the
|
||||
* ServerHello suite against the suite retained in the cached session (F-5811
|
||||
* does this for session-ID resumption; it must hold for tickets too). This
|
||||
* test establishes a ticket-based session, rewrites the cached session's suite
|
||||
* to emulate a server that resumes the ticket under a different suite, and
|
||||
* asserts the client aborts the resumption with MATCH_SUITE_ERROR. The same
|
||||
* server CTX is reused for the second handshake so its ticket key persists. */
|
||||
int test_tls12_resume_ticket_wrong_suite(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
||||
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
||||
!defined(NO_RESUME_SUITE_CHECK) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
||||
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||
const char* suite = "ECDHE-RSA-AES128-GCM-SHA256";
|
||||
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
||||
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL;
|
||||
WOLFSSL *ssl_c3 = NULL, *ssl_s3 = NULL;
|
||||
WOLFSSL_SESSION *sess = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_memio_ctx test_ctx2;
|
||||
struct test_memio_ctx test_ctx3;
|
||||
int ret;
|
||||
|
||||
/* First handshake: establish a ticket-based TLS 1.2 session. */
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suite), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite), WOLFSSL_SUCCESS);
|
||||
/* Opt the client into TLS 1.2 session tickets so the server issues one. */
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
||||
/* Must be a ticket session to exercise the ticket path. */
|
||||
ExpectIntGT(sess->ticketLen, 0);
|
||||
|
||||
/* Case 1 - downgrading server: change the cached suite so it no longer
|
||||
* matches the suite the server reuses from the ticket, but keep it
|
||||
* non-zero so it still counts as a retained suite. The value only feeds
|
||||
* the comparison (the real keys come from the ServerHello suite), so
|
||||
* flipping it is sufficient and safe. The client must reject the
|
||||
* resumption against the same server CTX (ticket key persists). */
|
||||
if (sess != NULL)
|
||||
sess->cipherSuite = (byte)(sess->cipherSuite ^ 0xFF);
|
||||
|
||||
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c2, suite), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s2, suite), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
|
||||
ret = test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL);
|
||||
ExpectIntNE(ret, 0);
|
||||
ExpectIntEQ(ssl_c2->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));
|
||||
|
||||
/* Case 2 - session that retained no suite (cipherSuite0/cipherSuite both
|
||||
* zero), as for an EAP-FAST PAC whose keys come from the session-secret
|
||||
* callback. There is nothing to compare against, so the check must be
|
||||
* skipped and the resumption must still succeed. */
|
||||
if (sess != NULL) {
|
||||
sess->cipherSuite0 = 0;
|
||||
sess->cipherSuite = 0;
|
||||
}
|
||||
|
||||
XMEMSET(&test_ctx3, 0, sizeof(test_ctx3));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx3, &ctx_c, &ctx_s, &ssl_c3, &ssl_s3,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c3, suite), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s3, suite), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c3), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_session(ssl_c3, sess), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c3, ssl_s3, 10, NULL), 0);
|
||||
ExpectIntEQ(wolfSSL_session_reused(ssl_c3), 1);
|
||||
|
||||
wolfSSL_SESSION_free(sess);
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_free(ssl_c2);
|
||||
wolfSSL_free(ssl_s2);
|
||||
wolfSSL_free(ssl_c3);
|
||||
wolfSSL_free(ssl_s3);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* A ticket the server can't honor must fall back to a full handshake (RFC 5077
|
||||
* 3.4), even under a different suite than the cached ticket session - the
|
||||
* F-5811 suite check must not abort it. The second handshake uses a fresh
|
||||
* server CTX (new ticket key -> decline) offering only suite B while the client
|
||||
* offers B and the session's suite A. */
|
||||
int test_tls12_resume_ticket_decline_fallback(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
||||
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \
|
||||
!defined(NO_RESUME_SUITE_CHECK) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
||||
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||
const char* suiteA = "ECDHE-RSA-AES128-GCM-SHA256";
|
||||
const char* suiteB = "ECDHE-RSA-AES256-GCM-SHA384";
|
||||
const char* suiteBA =
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
|
||||
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL, *ctx_s2 = NULL;
|
||||
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
||||
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL;
|
||||
WOLFSSL_SESSION *sess = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_memio_ctx test_ctx2;
|
||||
|
||||
/* First handshake: establish a ticket-based TLS 1.2 session on suite A. */
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suiteA), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suiteA), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
||||
ExpectIntGT(sess->ticketLen, 0);
|
||||
|
||||
/* Second handshake: fresh server CTX (NULL ctx_s2 -> new ticket key) so the
|
||||
* ticket is declined and the server does a full handshake on suite B. */
|
||||
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s2, &ssl_c2, &ssl_s2,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c2, suiteBA), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s2, suiteB), WOLFSSL_SUCCESS);
|
||||
/* Session cache off so the declining server emits an empty session ID and
|
||||
* the client takes the graceful full-handshake fallback (set on the SSL as
|
||||
* the flag is copied from the CTX at wolfSSL_new() time). */
|
||||
if (ssl_s2 != NULL)
|
||||
ssl_s2->options.sessionCacheOff = 1;
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
|
||||
/* Fallback must succeed (no MATCH_SUITE_ERROR), not resume, and use B. */
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
||||
ExpectIntEQ(wolfSSL_session_reused(ssl_c2), 0);
|
||||
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c2), suiteB);
|
||||
|
||||
wolfSSL_SESSION_free(sess);
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_free(ssl_c2);
|
||||
wolfSSL_free(ssl_s2);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
wolfSSL_CTX_free(ctx_s2);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* wolfSSL_set_session() must reject a TLS 1.2 session when minDowngrade is
|
||||
* set to TLS 1.3. */
|
||||
int test_tls_set_session_min_downgrade(void)
|
||||
|
||||
@@ -35,6 +35,8 @@ int test_tls12_no_null_compression(void);
|
||||
int test_tls12_ec_point_formats_no_uncompressed(void);
|
||||
int test_tls12_ec_point_formats_no_uncompressed_non_ecc(void);
|
||||
int test_tls12_etm_failed_resumption(void);
|
||||
int test_tls12_resume_ticket_wrong_suite(void);
|
||||
int test_tls12_resume_ticket_decline_fallback(void);
|
||||
int test_tls_set_session_min_downgrade(void);
|
||||
int test_tls12_session_id_resumption_sni_mismatch(void);
|
||||
int test_tls13_session_resumption_sni_mismatch(void);
|
||||
@@ -67,6 +69,8 @@ int test_wolfSSL_get_shared_ciphers(void);
|
||||
TEST_DECL_GROUP("tls", \
|
||||
test_tls12_ec_point_formats_no_uncompressed_non_ecc), \
|
||||
TEST_DECL_GROUP("tls", test_tls12_etm_failed_resumption), \
|
||||
TEST_DECL_GROUP("tls", test_tls12_resume_ticket_wrong_suite), \
|
||||
TEST_DECL_GROUP("tls", test_tls12_resume_ticket_decline_fallback), \
|
||||
TEST_DECL_GROUP("tls", test_tls_set_session_min_downgrade), \
|
||||
TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_sni_mismatch), \
|
||||
TEST_DECL_GROUP("tls", test_tls13_session_resumption_sni_mismatch), \
|
||||
|
||||
@@ -153,6 +153,166 @@ int test_tls_ems_resumption_downgrade(void)
|
||||
}
|
||||
|
||||
|
||||
#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_SESSION_CACHE)
|
||||
/* Remove the extended_master_secret extension from the ServerHello record at
|
||||
* the head of the server-to-client memio buffer, patching up the record,
|
||||
* handshake and extension-block lengths so the message still parses.
|
||||
* Returns 0 on success. */
|
||||
static int StripEmsFromServerHello(struct test_memio_ctx* test_ctx)
|
||||
{
|
||||
byte* buf = test_ctx->c_buff;
|
||||
int len = test_ctx->c_len;
|
||||
int recLen;
|
||||
int hsLen;
|
||||
int extsLenIdx;
|
||||
int extsLen;
|
||||
int idx;
|
||||
int extsEnd;
|
||||
|
||||
/* Record header: type(1) version(2) length(2) */
|
||||
if (len < 5 || buf[0] != handshake)
|
||||
return -1;
|
||||
recLen = (buf[3] << 8) | buf[4];
|
||||
if (5 + recLen > len)
|
||||
return -1;
|
||||
/* Handshake header: type(1) length(3) */
|
||||
if (recLen < HANDSHAKE_HEADER_SZ || buf[5] != server_hello)
|
||||
return -1;
|
||||
hsLen = (buf[6] << 16) | (buf[7] << 8) | buf[8];
|
||||
/* Skip version(2), random(32) to the session ID length, then skip the
|
||||
* session ID, cipher suite(2) and compression(1) to the extensions
|
||||
* length. */
|
||||
extsLenIdx = 5 + HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN +
|
||||
OPAQUE8_LEN + buf[5 + HANDSHAKE_HEADER_SZ + OPAQUE16_LEN +
|
||||
RAN_LEN] +
|
||||
OPAQUE16_LEN + OPAQUE8_LEN;
|
||||
if (extsLenIdx + OPAQUE16_LEN > 5 + recLen)
|
||||
return -1;
|
||||
extsLen = (buf[extsLenIdx] << 8) | buf[extsLenIdx + 1];
|
||||
idx = extsLenIdx + OPAQUE16_LEN;
|
||||
extsEnd = idx + extsLen;
|
||||
if (extsEnd > 5 + recLen)
|
||||
return -1;
|
||||
while (idx + 4 <= extsEnd) {
|
||||
int extType = (buf[idx] << 8) | buf[idx + 1];
|
||||
int extLen = (buf[idx + 2] << 8) | buf[idx + 3];
|
||||
int rmLen = 4 + extLen;
|
||||
|
||||
if (idx + rmLen > extsEnd)
|
||||
return -1;
|
||||
if (extType == HELLO_EXT_EXTMS) {
|
||||
XMEMMOVE(buf + idx, buf + idx + rmLen,
|
||||
(size_t)(len - idx - rmLen));
|
||||
recLen -= rmLen;
|
||||
hsLen -= rmLen;
|
||||
extsLen -= rmLen;
|
||||
buf[3] = (byte)(recLen >> 8);
|
||||
buf[4] = (byte)recLen;
|
||||
buf[6] = (byte)(hsLen >> 16);
|
||||
buf[7] = (byte)(hsLen >> 8);
|
||||
buf[8] = (byte)hsLen;
|
||||
buf[extsLenIdx] = (byte)(extsLen >> 8);
|
||||
buf[extsLenIdx + 1] = (byte)extsLen;
|
||||
test_ctx->c_len -= rmLen;
|
||||
/* The ServerHello record sits wholly inside the first buffered
|
||||
* message. */
|
||||
test_ctx->c_msg_sizes[0] -= rmLen;
|
||||
return 0;
|
||||
}
|
||||
idx += rmLen;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Full handshake with EMS, then resume and strip the EMS extension from the
|
||||
* ServerHello in transit. The client must catch the downgrade and abort
|
||||
* (RFC 7627 Section 5.3). useTicket selects session-ticket resumption
|
||||
* instead of session-ID resumption. */
|
||||
static int test_tls_ems_resumption_server_downgrade_ex(int useTicket)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
WOLFSSL_SESSION *session = NULL;
|
||||
|
||||
#ifndef HAVE_SESSION_TICKET
|
||||
(void)useTicket;
|
||||
#endif
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
#ifdef HAVE_SESSION_TICKET
|
||||
if (useTicket)
|
||||
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
ExpectNotNull(session = wolfSSL_get1_session(ssl_c));
|
||||
ExpectTrue(session->haveEMS);
|
||||
#ifdef HAVE_SESSION_TICKET
|
||||
if (useTicket)
|
||||
ExpectIntGT(session->ticketLen, 0);
|
||||
#endif
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
ssl_c = NULL;
|
||||
wolfSSL_free(ssl_s);
|
||||
ssl_s = NULL;
|
||||
test_memio_clear_buffer(&test_ctx, 0);
|
||||
test_memio_clear_buffer(&test_ctx, 1);
|
||||
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS);
|
||||
|
||||
/* ClientHello */
|
||||
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
/* Server flight accepting the resumption */
|
||||
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
/* Drop EMS from the ServerHello to simulate a downgrading server. */
|
||||
ExpectIntEQ(StripEmsFromServerHello(&test_ctx), 0);
|
||||
/* The client must refuse to resume without EMS. */
|
||||
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
||||
WC_NO_ERR_TRACE(EXT_MASTER_SECRET_NEEDED_E));
|
||||
|
||||
wolfSSL_SESSION_free(session);
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* F-5807: a server that resumes an EMS session but omits the
|
||||
* extended_master_secret extension from its ServerHello must be rejected by
|
||||
* the client with EXT_MASTER_SECRET_NEEDED_E (RFC 7627 Section 5.3), on both
|
||||
* session-ID and session-ticket resumption. */
|
||||
int test_tls_ems_resumption_server_downgrade(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_SESSION_CACHE)
|
||||
ExpectIntEQ(test_tls_ems_resumption_server_downgrade_ex(0), TEST_SUCCESS);
|
||||
#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
||||
ExpectIntEQ(test_tls_ems_resumption_server_downgrade_ex(1), TEST_SUCCESS);
|
||||
#endif
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
#if !defined(WOLFSSL_NO_TLS12) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
||||
@@ -350,6 +510,145 @@ int test_scr_verify_data_mismatch(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* F-4144: WOLFSSL_OP_NO_RENEGOTIATION on the server must refuse a
|
||||
* client-initiated renegotiation with a no_renegotiation *warning* while
|
||||
* keeping the established connection alive, rather than aborting it. */
|
||||
int test_scr_no_renegotiation_option(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
WOLFSSL_ALERT_HISTORY history;
|
||||
byte readBuf[16];
|
||||
int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
|
||||
int i;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&history, 0, sizeof(history));
|
||||
test_ctx.c_ciphers = test_ctx.s_ciphers = "ECDHE-RSA-AES128-GCM-SHA256";
|
||||
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
||||
&ssl_s, wolfTLSv1_2_client_method,
|
||||
wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
|
||||
|
||||
/* Server opts into rejecting peer-initiated renegotiation. */
|
||||
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_RENEGOTIATION);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
/* Client initiates renegotiation: it sends a ClientHello and waits for a
|
||||
* ServerHello that never comes. */
|
||||
ExpectIntLT(wolfSSL_Rehandshake(ssl_c), 0);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* Server processes the renegotiation ClientHello. It must refuse without
|
||||
* aborting: the read returns WANT_READ (connection still alive), not a
|
||||
* SECURE_RENEGOTIATION_E fatal error. */
|
||||
ExpectIntLT(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 0);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The refusal was a warning-level no_renegotiation alert. */
|
||||
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &history), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(history.last_tx.level, alert_warning);
|
||||
ExpectIntEQ(history.last_tx.code, no_renegotiation);
|
||||
|
||||
/* The connection is still active and passes data: the server sends
|
||||
* application data which the client receives and decrypts correctly, even
|
||||
* though the client's renegotiation attempt was refused. The client
|
||||
* surfaces the data once it has processed the no_renegotiation warning. */
|
||||
ExpectIntEQ(wolfSSL_write(ssl_s, "hello", 5), 5);
|
||||
for (i = 0; i < 10 && ret != 5; i++)
|
||||
ret = wolfSSL_read(ssl_c, readBuf, sizeof(readBuf));
|
||||
ExpectIntEQ(ret, 5);
|
||||
ExpectIntEQ(XMEMCMP(readBuf, "hello", 5), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* F-4144: WOLFSSL_OP_NO_RENEGOTIATION on the client must refuse a
|
||||
* server-initiated renegotiation (HelloRequest) with a no_renegotiation
|
||||
* *warning* while keeping the established connection alive, rather than
|
||||
* starting a secure renegotiation. */
|
||||
int test_helloRequest_no_renegotiation_option(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
|
||||
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
||||
struct test_memio_ctx test_ctx;
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
WOLFSSL_ALERT_HISTORY history;
|
||||
byte readBuf[16];
|
||||
int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
|
||||
int i;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&history, 0, sizeof(history));
|
||||
test_ctx.c_ciphers = test_ctx.s_ciphers = "ECDHE-RSA-AES128-GCM-SHA256";
|
||||
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
||||
&ssl_s, wolfTLSv1_2_client_method,
|
||||
wolfTLSv1_2_server_method), 0);
|
||||
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
|
||||
|
||||
/* Client opts into rejecting peer-initiated renegotiation. */
|
||||
wolfSSL_set_options(ssl_c, WOLFSSL_OP_NO_RENEGOTIATION);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
/* Server asks the client to renegotiate by sending a HelloRequest, then
|
||||
* waits for the ClientHello that never comes. */
|
||||
ExpectIntLT(wolfSSL_Rehandshake(ssl_s), 0);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* Client processes the HelloRequest. It must refuse without starting a
|
||||
* renegotiation: the read returns WANT_READ (connection still alive). */
|
||||
ExpectIntLT(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), 0);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The refusal was a warning-level no_renegotiation alert. */
|
||||
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &history), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(history.last_tx.level, alert_warning);
|
||||
ExpectIntEQ(history.last_tx.code, no_renegotiation);
|
||||
|
||||
/* The connection is still active and passes data: the client sends
|
||||
* application data which the server receives and decrypts correctly, even
|
||||
* though its renegotiation request was refused. */
|
||||
ExpectIntEQ(wolfSSL_write(ssl_c, "hello", 5), 5);
|
||||
for (i = 0; i < 10 && ret != 5; i++)
|
||||
ret = wolfSSL_read(ssl_s, readBuf, sizeof(readBuf));
|
||||
ExpectIntEQ(ret, 5);
|
||||
ExpectIntEQ(XMEMCMP(readBuf, "hello", 5), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* F-2126: DoTls13ClientHello must reject a second ClientHello whose
|
||||
* cipher suite does not match the server's HelloRetryRequest. The
|
||||
* client offers two suites in CH1 and only a different one in CH2. */
|
||||
|
||||
@@ -24,9 +24,12 @@
|
||||
|
||||
int test_tls_ems_downgrade(void);
|
||||
int test_tls_ems_resumption_downgrade(void);
|
||||
int test_tls_ems_resumption_server_downgrade(void);
|
||||
int test_tls12_chacha20_poly1305_bad_tag(void);
|
||||
int test_tls13_null_cipher_bad_hmac(void);
|
||||
int test_scr_verify_data_mismatch(void);
|
||||
int test_scr_no_renegotiation_option(void);
|
||||
int test_helloRequest_no_renegotiation_option(void);
|
||||
int test_tls13_hrr_cipher_suite_mismatch(void);
|
||||
int test_tls13_ticket_age_out_of_window(void);
|
||||
int test_wolfSSL_DisableExtendedMasterSecret(void);
|
||||
|
||||
+3
-1
@@ -244,7 +244,9 @@ enum wolfSSL_ErrorCodes {
|
||||
|
||||
ECH_REQUIRED_E = -519, /* ECH offered but rejected by server */
|
||||
|
||||
WOLFSSL_LAST_E = -519
|
||||
SEQUENCE_NUMBER_E = -520, /* Record sequence number would wrap */
|
||||
|
||||
WOLFSSL_LAST_E = -520
|
||||
|
||||
/* codes -1000 to -1999 are reserved for wolfCrypt. */
|
||||
};
|
||||
|
||||
+7
-4
@@ -3503,13 +3503,16 @@ enum key_cache_state {
|
||||
|
||||
/* Additional Connection State according to rfc5746 section 3.1 */
|
||||
typedef struct SecureRenegotiation {
|
||||
byte enabled; /* secure_renegotiation flag in rfc */
|
||||
byte verifySet;
|
||||
byte startScr; /* server requested client to start scr */
|
||||
/* Single-bit flags grouped together so they pack into one storage unit. */
|
||||
WC_BITFIELD enabled:1; /* secure_renegotiation flag in rfc */
|
||||
WC_BITFIELD verifySet:1;
|
||||
WC_BITFIELD startScr:1; /* server requested client to start scr */
|
||||
WC_BITFIELD renegInfoSeen:1; /* renegotiation_info ext seen this
|
||||
* handshake (RFC 5746 3.7) */
|
||||
WC_BITFIELD subject_hash_set:1; /* if peer cert hash is set */
|
||||
enum key_cache_state cache_status; /* track key cache state */
|
||||
byte client_verify_data[TLS_FINISHED_SZ]; /* cached */
|
||||
byte server_verify_data[TLS_FINISHED_SZ]; /* cached */
|
||||
byte subject_hash_set; /* if peer cert hash is set */
|
||||
byte subject_hash[KEYID_SIZE]; /* peer cert hash */
|
||||
Keys tmp_keys; /* can't overwrite real keys yet */
|
||||
} SecureRenegotiation;
|
||||
|
||||
Reference in New Issue
Block a user