Skip to content

Commit 2d186b3

Browse files
authored
Merge pull request #10537 from SparkiDev/tls13_pt_alert_before_enc
TLS 1.3 plaintext alert: ignore before seeing encrypted
2 parents 4bf2d52 + b0757c1 commit 2d186b3

2 files changed

Lines changed: 153 additions & 12 deletions

File tree

src/internal.c

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22468,6 +22468,9 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
2246822468
byte level;
2246922469
byte code;
2247022470
word32 dataSz = (word32)ssl->curSize;
22471+
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
22472+
int ignorePtAlert;
22473+
#endif
2247122474

2247222475
#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
2247322476
if (ssl->hsInfoOn)
@@ -22496,9 +22499,19 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
2249622499
code = input[(*inOutIdx)++];
2249722500
*type = code;
2249822501
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
22499-
/* Don't process alert when TLS 1.3 and encrypting but plaintext alert. */
22500-
if (!IsAtLeastTLSv1_3(ssl->version) || !IsEncryptionOn(ssl, 0) ||
22501-
ssl->keys.decryptedCur)
22502+
/* A plaintext alert received in TLS 1.3 once we are decrypting is only
22503+
* tolerated while still in the handshake and before the peer has sent an
22504+
* encrypted message. The peer sequence number is reset to zero each time
22505+
* decryption keys are installed and incremented for each record decrypted,
22506+
* so a non-zero value means the peer has sent an encrypted message and a
22507+
* plaintext alert is treated as an error. */
22508+
ignorePtAlert = IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
22509+
!ssl->keys.decryptedCur && !ssl->options.handShakeDone &&
22510+
ssl->keys.peer_sequence_number_hi == 0 &&
22511+
ssl->keys.peer_sequence_number_lo == 0;
22512+
22513+
/* Don't record an ignored plaintext alert in the alert history. */
22514+
if (!ignorePtAlert)
2250222515
#endif
2250322516
{
2250422517
ssl->alert_history.last_rx.code = code;
@@ -22529,16 +22542,21 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
2252922542
!ssl->keys.decryptedCur)
2253022543
{
2253122544
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
22532-
/* Ignore alert if TLS 1.3 and encrypting but was plaintext alert. */
22533-
*type = invalid_alert;
22534-
level = alert_none;
22535-
22536-
#else
22537-
/* Unexpected message when encryption is on and alert not encrypted. */
22538-
SendAlert(ssl, alert_fatal, unexpected_message);
22539-
WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
22540-
return PARSE_ERROR;
22545+
if (ignorePtAlert) {
22546+
/* Ignore plaintext alert: TLS 1.3, decrypting, and the peer has
22547+
* not yet sent an encrypted handshake message. */
22548+
*type = invalid_alert;
22549+
level = alert_none;
22550+
}
22551+
else
2254122552
#endif
22553+
{
22554+
/* Unexpected message when encryption is on and alert not
22555+
* encrypted. */
22556+
SendAlert(ssl, alert_fatal, unexpected_message);
22557+
WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
22558+
return PARSE_ERROR;
22559+
}
2254222560
}
2254322561
else {
2254422562
if (*type == close_notify) {

tests/api/test_tls13.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4542,6 +4542,129 @@ int test_tls13_plaintext_alert(void)
45424542

45434543
wolfSSL_free(ssl);
45444544
wolfSSL_CTX_free(ctx);
4545+
ssl = NULL;
4546+
ctx = NULL;
4547+
4548+
/* Negative test: a plaintext alert must NOT be ignored once the peer has
4549+
* responded with an encrypted handshake message. Complete a handshake so
4550+
* the peer is encrypting, then feed the client a plaintext alert. */
4551+
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM)
4552+
{
4553+
WOLFSSL_CTX* ctx_c = NULL;
4554+
WOLFSSL_CTX* ctx_s = NULL;
4555+
WOLFSSL* ssl_c = NULL;
4556+
WOLFSSL* ssl_s = NULL;
4557+
struct test_memio_ctx test_ctx;
4558+
/* Plaintext alert record: fatal (2), handshake_failure (40). */
4559+
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
4560+
char data[16];
4561+
4562+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
4563+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
4564+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
4565+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
4566+
4567+
/* Drop any post-handshake data (e.g. session tickets) queued for the
4568+
* client and feed it only the plaintext alert. */
4569+
test_memio_clear_buffer(&test_ctx, 1);
4570+
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)ptAlert,
4571+
(int)sizeof(ptAlert)), 0);
4572+
4573+
/* Plaintext alert is rejected as the peer is encrypting. */
4574+
ExpectIntLT(wolfSSL_read(ssl_c, data, (int)sizeof(data)), 0);
4575+
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
4576+
WC_NO_ERR_TRACE(PARSE_ERROR));
4577+
4578+
wolfSSL_free(ssl_c);
4579+
wolfSSL_free(ssl_s);
4580+
wolfSSL_CTX_free(ctx_c);
4581+
wolfSSL_CTX_free(ctx_s);
4582+
}
4583+
4584+
/* Negative test (server): a plaintext alert must NOT be ignored once the
4585+
* client has sent an encrypted handshake message, even before the
4586+
* handshake is complete. Use client authentication so that the client
4587+
* sends an encrypted Certificate message before Finished. */
4588+
{
4589+
WOLFSSL_CTX* ctx_c = NULL;
4590+
WOLFSSL_CTX* ctx_s = NULL;
4591+
WOLFSSL* ssl_c = NULL;
4592+
WOLFSSL* ssl_s = NULL;
4593+
struct test_memio_ctx test_ctx;
4594+
/* Plaintext alert record: fatal (2), handshake_failure (40). */
4595+
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
4596+
int end = 0;
4597+
4598+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
4599+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
4600+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
4601+
/* Server requires a client certificate. */
4602+
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile,
4603+
NULL) == WOLFSSL_SUCCESS);
4604+
if (EXPECT_SUCCESS()) {
4605+
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
4606+
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
4607+
}
4608+
ExpectTrue(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
4609+
CERT_FILETYPE) == WOLFSSL_SUCCESS);
4610+
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
4611+
CERT_FILETYPE) == WOLFSSL_SUCCESS);
4612+
4613+
/* Client Hello. */
4614+
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
4615+
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
4616+
/* Server flight including CertificateRequest. */
4617+
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
4618+
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
4619+
/* Client flight: [CCS,] Certificate, CertificateVerify, Finished. */
4620+
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
4621+
4622+
/* Find the end of the first encrypted record (outer content type
4623+
* application_data) the client sent - the Certificate message. */
4624+
while (end + 5 <= test_ctx.s_len) {
4625+
byte recType = test_ctx.s_buff[end];
4626+
end += 5 + ((test_ctx.s_buff[end + 3] << 8) |
4627+
test_ctx.s_buff[end + 4]);
4628+
if (recType == 0x17)
4629+
break;
4630+
}
4631+
ExpectIntLE(end, test_ctx.s_len);
4632+
ExpectIntGT(end, 0);
4633+
/* Remove the records after it (CertificateVerify and Finished),
4634+
* working backwards a message at a time. */
4635+
while (EXPECT_SUCCESS() && test_ctx.s_len > end) {
4636+
int i;
4637+
int msgOff = 0;
4638+
4639+
for (i = 0; i < test_ctx.s_msg_count - 1; i++)
4640+
msgOff += test_ctx.s_msg_sizes[i];
4641+
if (msgOff >= end) {
4642+
/* Last message is wholly after the Certificate record. */
4643+
ExpectIntEQ(test_memio_drop_message(&test_ctx, 0,
4644+
test_ctx.s_msg_count - 1), 0);
4645+
}
4646+
else {
4647+
/* Last message also holds the records to keep. */
4648+
ExpectIntEQ(test_memio_remove_from_buffer(&test_ctx, 0, end,
4649+
test_ctx.s_len - end), 0);
4650+
}
4651+
}
4652+
/* Follow the encrypted Certificate message with a plaintext alert. */
4653+
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
4654+
(const char*)ptAlert, (int)sizeof(ptAlert)), 0);
4655+
4656+
/* Plaintext alert is rejected as the client has sent an encrypted
4657+
* handshake message. */
4658+
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
4659+
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
4660+
WC_NO_ERR_TRACE(PARSE_ERROR));
4661+
4662+
wolfSSL_free(ssl_c);
4663+
wolfSSL_free(ssl_s);
4664+
wolfSSL_CTX_free(ctx_c);
4665+
wolfSSL_CTX_free(ctx_s);
4666+
}
4667+
#endif
45454668
#else
45464669
/* Fail on plaintext alert when encryption keys on. */
45474670

0 commit comments

Comments
 (0)