Skip to content
Merged
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
42 changes: 30 additions & 12 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -22420,6 +22420,9 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
byte level;
byte code;
word32 dataSz = (word32)ssl->curSize;
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
int ignorePtAlert;
#endif

#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
if (ssl->hsInfoOn)
Expand Down Expand Up @@ -22448,9 +22451,19 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
code = input[(*inOutIdx)++];
*type = code;
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
/* Don't process alert when TLS 1.3 and encrypting but plaintext alert. */
if (!IsAtLeastTLSv1_3(ssl->version) || !IsEncryptionOn(ssl, 0) ||
ssl->keys.decryptedCur)
/* A plaintext alert received in TLS 1.3 once we are decrypting is only
* tolerated while still in the handshake and before the peer has sent an
* encrypted message. The peer sequence number is reset to zero each time
* decryption keys are installed and incremented for each record decrypted,
* so a non-zero value means the peer has sent an encrypted message and a
* plaintext alert is treated as an error. */
ignorePtAlert = IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
!ssl->keys.decryptedCur && !ssl->options.handShakeDone &&
ssl->keys.peer_sequence_number_hi == 0 &&
ssl->keys.peer_sequence_number_lo == 0;

/* Don't record an ignored plaintext alert in the alert history. */
if (!ignorePtAlert)
#endif
{
ssl->alert_history.last_rx.code = code;
Expand Down Expand Up @@ -22481,16 +22494,21 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
!ssl->keys.decryptedCur)
{
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
/* Ignore alert if TLS 1.3 and encrypting but was plaintext alert. */
*type = invalid_alert;
level = alert_none;

#else
/* Unexpected message when encryption is on and alert not encrypted. */
SendAlert(ssl, alert_fatal, unexpected_message);
WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
return PARSE_ERROR;
if (ignorePtAlert) {
/* Ignore plaintext alert: TLS 1.3, decrypting, and the peer has
* not yet sent an encrypted handshake message. */
*type = invalid_alert;
level = alert_none;
}
else
#endif
{
/* Unexpected message when encryption is on and alert not
* encrypted. */
SendAlert(ssl, alert_fatal, unexpected_message);
WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
return PARSE_ERROR;
}
}
else {
if (*type == close_notify) {
Expand Down
123 changes: 123 additions & 0 deletions tests/api/test_tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -4542,6 +4542,129 @@ int test_tls13_plaintext_alert(void)

wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
ssl = NULL;
ctx = NULL;

/* Negative test: a plaintext alert must NOT be ignored once the peer has
* responded with an encrypted handshake message. Complete a handshake so
* the peer is encrypting, then feed the client a plaintext alert. */
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM)
{
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
/* Plaintext alert record: fatal (2), handshake_failure (40). */
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
char data[16];

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);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

/* Drop any post-handshake data (e.g. session tickets) queued for the
* client and feed it only the plaintext alert. */
test_memio_clear_buffer(&test_ctx, 1);
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)ptAlert,
(int)sizeof(ptAlert)), 0);

/* Plaintext alert is rejected as the peer is encrypting. */
ExpectIntLT(wolfSSL_read(ssl_c, data, (int)sizeof(data)), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
WC_NO_ERR_TRACE(PARSE_ERROR));

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
}

/* Negative test (server): a plaintext alert must NOT be ignored once the
* client has sent an encrypted handshake message, even before the
* handshake is complete. Use client authentication so that the client
* sends an encrypted Certificate message before Finished. */
{
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
/* Plaintext alert record: fatal (2), handshake_failure (40). */
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
int end = 0;

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);
/* Server requires a client certificate. */
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile,
NULL) == WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS()) {
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
ExpectTrue(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
CERT_FILETYPE) == WOLFSSL_SUCCESS);
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
CERT_FILETYPE) == WOLFSSL_SUCCESS);

/* Client Hello. */
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* Server flight including CertificateRequest. */
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
/* Client flight: [CCS,] Certificate, CertificateVerify, Finished. */
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);

/* Find the end of the first encrypted record (outer content type
* application_data) the client sent - the Certificate message. */
while (end + 5 <= test_ctx.s_len) {
byte recType = test_ctx.s_buff[end];
end += 5 + ((test_ctx.s_buff[end + 3] << 8) |
test_ctx.s_buff[end + 4]);
if (recType == 0x17)
break;
}
ExpectIntLE(end, test_ctx.s_len);
ExpectIntGT(end, 0);
/* Remove the records after it (CertificateVerify and Finished),
* working backwards a message at a time. */
while (EXPECT_SUCCESS() && test_ctx.s_len > end) {
int i;
int msgOff = 0;

for (i = 0; i < test_ctx.s_msg_count - 1; i++)
msgOff += test_ctx.s_msg_sizes[i];
if (msgOff >= end) {
/* Last message is wholly after the Certificate record. */
ExpectIntEQ(test_memio_drop_message(&test_ctx, 0,
test_ctx.s_msg_count - 1), 0);
}
else {
/* Last message also holds the records to keep. */
ExpectIntEQ(test_memio_remove_from_buffer(&test_ctx, 0, end,
test_ctx.s_len - end), 0);
}
}
/* Follow the encrypted Certificate message with a plaintext alert. */
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
(const char*)ptAlert, (int)sizeof(ptAlert)), 0);

/* Plaintext alert is rejected as the client has sent an encrypted
* handshake message. */
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
WC_NO_ERR_TRACE(PARSE_ERROR));

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
}
#endif
#else
/* Fail on plaintext alert when encryption keys on. */

Expand Down
Loading