diff --git a/ChangeLog.d/tls13-post-finished-alerts.txt b/ChangeLog.d/tls13-post-finished-alerts.txt new file mode 100644 index 0000000000..79eaabf758 --- /dev/null +++ b/ChangeLog.d/tls13-post-finished-alerts.txt @@ -0,0 +1,13 @@ +Bugfix + * In a TLS 1.3 server, protect alerts sent after the server Finished + message, such as those sent when a required client certificate is + missing or invalid, with the application traffic keys, as required by + RFC 8446 section 4.4.4. Previously these alerts were encrypted with the + handshake traffic keys, so the peer reported a MAC failure instead of + decoding the alert. Fixes #10877. + * In TLS 1.3, send the alerts described in RFC 8446 section 4.4.2.4. A + server that requires a client certificate and receives none now sends a + certificate_required alert, and a client that receives an empty server + Certificate message now sends a decode_error alert. Both previously sent + the no_certificate alert, which is reserved in TLS 1.3. Fixes #10720, + fixes #10900, fixes #10724, fixes #10884. diff --git a/library/ssl_tls13_generic.c b/library/ssl_tls13_generic.c index 60c0f2348c..c430b8dbac 100644 --- a/library/ssl_tls13_generic.c +++ b/library/ssl_tls13_generic.c @@ -677,16 +677,14 @@ static int ssl_tls13_validate_certificate(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_SRV_C) if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { - /* The client was asked for a certificate but didn't send - * one. The client should know what's going on, so we - * don't send an alert. - */ + /* No client certificate: optional auth continues, required auth + * aborts with certificate_required (RFC 8446 4.4.2.4). */ ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING; if (authmode == MBEDTLS_SSL_VERIFY_OPTIONAL) { return 0; } else { MBEDTLS_SSL_PEND_FATAL_ALERT( - MBEDTLS_SSL_ALERT_MSG_NO_CERT, + MBEDTLS_SSL_ALERT_MSG_CERT_REQUIRED, MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE); return MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE; } @@ -698,9 +696,10 @@ static int ssl_tls13_validate_certificate(mbedtls_ssl_context *ssl) * certificate chain. (Last paragraph before 4.4.2.1 in RFC 8446: "The * server's certificate_list MUST always be non-empty.") With authmode * optional/none, we continue the handshake if we can't validate the - * server's cert, but we still break it if no certificate was sent. */ + * server's cert, but we still break it if no certificate was sent. + * RFC 8446, section 4.4.2.4 requires a "decode_error" alert here. */ if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { - MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_NO_CERT, + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE); return MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE; } diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index 38f2fe9370..741ac7e46b 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -2888,6 +2888,14 @@ static int ssl_tls13_write_server_finished(mbedtls_ssl_context *ssl) return ret; } + /* RFC 8446 4.4.4: records after the server Finished use the application + * keys. Switch the outbound transform now; inbound stays on the handshake + * (or early-data) keys until the client's second flight. */ + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Switch to application keys for outbound traffic " + "( K_send = application )")); + mbedtls_ssl_set_outbound_transform(ssl, ssl->transform_application); + #if defined(MBEDTLS_SSL_EARLY_DATA) if (ssl->handshake->early_data_accepted) { /* See RFC 8446 section A.2 for more information */ diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index fd57757aaa..1efcc8ad78 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -13310,7 +13310,10 @@ run_test "TLS 1.3: Server side check - mbedtls with client empty certificate" -s "tls13 server state: MBEDTLS_SSL_SERVER_CERTIFICATE" \ -s "=> write certificate request" \ -s "SSL - No client certification received from the client, but required by the authentication mode" \ + -s "send alert level=2 message=116" \ -c "client state: MBEDTLS_SSL_CERTIFICATE_REQUEST" \ + -c "is a fatal alert message (msg 116)" \ + -C "0x7180" \ -s "=> parse client hello" \ -s "<= parse client hello"