diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata index 7949cc7d..a528874f 100644 --- a/crypto/err/ssl.errordata +++ b/crypto/err/ssl.errordata @@ -34,7 +34,6 @@ SSL,125,CERTIFICATE_VERIFY_FAILED SSL,126,CERT_CB_ERROR SSL,127,CERT_LENGTH_MISMATCH SSL,128,CHANNEL_ID_NOT_P256 -SSL,279,CHANNEL_ID_ON_EARLY_DATA SSL,129,CHANNEL_ID_SIGNATURE_INVALID SSL,130,CIPHER_OR_HASH_UNAVAILABLE SSL,131,CLIENTHELLO_PARSE_FAILED @@ -183,6 +182,7 @@ SSL,1117,TOO_MUCH_READ_EARLY_DATA SSL,270,TOO_MUCH_SKIPPED_EARLY_DATA SSL,221,UNABLE_TO_FIND_ECDH_PARAMETERS SSL,222,UNEXPECTED_EXTENSION +SSL,279,UNEXPECTED_EXTENSION_ON_EARLY_DATA SSL,223,UNEXPECTED_MESSAGE SSL,224,UNEXPECTED_OPERATOR_IN_GROUP SSL,225,UNEXPECTED_RECORD diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 63651b5c..9a414ca1 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -4878,7 +4878,7 @@ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span out_prefix, #define SSL_R_TICKET_ENCRYPTION_FAILED 276 #define SSL_R_ALPN_MISMATCH_ON_EARLY_DATA 277 #define SSL_R_WRONG_VERSION_ON_EARLY_DATA 278 -#define SSL_R_CHANNEL_ID_ON_EARLY_DATA 279 +#define SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA 279 #define SSL_R_NO_SUPPORTED_VERSIONS_ENABLED 280 #define SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE 281 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 diff --git a/ssl/custom_extensions.cc b/ssl/custom_extensions.cc index d86bd484..dff7041b 100644 --- a/ssl/custom_extensions.cc +++ b/ssl/custom_extensions.cc @@ -71,14 +71,6 @@ static int custom_ext_add_hello(SSL_HANDSHAKE *hs, CBB *extensions) { return 1; } - if (ssl->cert->enable_early_data) { - /* TODO(svaldez): Support Custom Extensions with 0-RTT. For now the caller - * is expected not to configure both together. - * https://crbug.com/boringssl/173. */ - OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); - return 0; - } - for (size_t i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) { const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i); diff --git a/ssl/internal.h b/ssl/internal.h index 931ac827..534a1cec 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -1249,6 +1249,8 @@ struct SSL_HANDSHAKE { unsigned received_hello_retry_request:1; + unsigned received_custom_extension:1; + /* accept_psk_mode stores whether the client's PSK mode is compatible with our * preferences. */ unsigned accept_psk_mode:1; diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc index 9c4aa7ff..80e6f7b0 100644 --- a/ssl/s3_both.cc +++ b/ssl/s3_both.cc @@ -137,6 +137,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) scts_requested(0), needs_psk_binder(0), received_hello_retry_request(0), + received_custom_extension(0), accept_psk_mode(0), cert_request(0), certificate_status_expected(0), diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc index 39f4be62..1dd834bd 100644 --- a/ssl/t1_lib.cc +++ b/ssl/t1_lib.cc @@ -2795,7 +2795,6 @@ static int ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs, hs->extensions.received = 0; hs->custom_extensions.received = 0; - CBS extensions; CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len); while (CBS_len(&extensions) != 0) { @@ -2919,6 +2918,7 @@ static int ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs, tls_extension_find(&ext_index, type); if (ext == NULL) { + hs->received_custom_extension = 1; if (!custom_ext_parse_serverhello(hs, out_alert, type, &extension)) { return 0; } diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 7e64fe5f..2ae697cb 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -8980,22 +8980,6 @@ func addCustomExtensionTests() { flags: []string{flag}, }) - // 0-RTT is not currently supported with Custom Extensions. - testCases = append(testCases, testCase{ - testType: testType, - name: "CustomExtensions-" + suffix + "-EarlyData", - config: Config{ - MaxVersion: VersionTLS13, - Bugs: ProtocolBugs{ - CustomExtension: expectedContents, - ExpectedCustomExtension: &expectedContents, - }, - }, - shouldFail: true, - expectedError: ":CUSTOM_EXTENSION_ERROR:", - flags: []string{flag, "-enable-early-data"}, - }) - // If the parse callback fails, the handshake should also fail. testCases = append(testCases, testCase{ testType: testType, @@ -9090,6 +9074,121 @@ func addCustomExtensionTests() { }) } + // If the client sends both early data and custom extension, the handshake + // should succeed as long as both the extensions aren't returned by the + // server. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "CustomExtensions-Client-EarlyData-None", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + ExpectedCustomExtension: &expectedContents, + AlwaysRejectEarlyData: true, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-client-custom-extension", + "-enable-early-data", + "-expect-early-data-info", + "-expect-reject-early-data", + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "CustomExtensions-Client-EarlyData-EarlyDataAccepted", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + ExpectedCustomExtension: &expectedContents, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-client-custom-extension", + "-enable-early-data", + "-expect-early-data-info", + "-expect-accept-early-data", + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "CustomExtensions-Client-EarlyData-CustomExtensionAccepted", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + AlwaysRejectEarlyData: true, + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-client-custom-extension", + "-enable-early-data", + "-expect-early-data-info", + "-expect-reject-early-data", + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "CustomExtensions-Client-EarlyDataAndCustomExtensions", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + SendEarlyDataExtension: true, + }, + }, + resumeSession: true, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:", + flags: []string{ + "-enable-client-custom-extension", + "-enable-early-data", + "-expect-early-data-info", + }, + }) + + // If the server receives both early data and custom extension, only the + // custom extension should be accepted. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CustomExtensions-Server-EarlyDataAccepted", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + ExpectEarlyDataAccepted: false, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-server-custom-extension", + "-enable-early-data", + "-expect-early-data-info", + }, + }) + // The custom extension add callback should not be called if the client // doesn't send the extension. testCases = append(testCases, testCase{ @@ -11661,7 +11760,7 @@ func addTLS13HandshakeTests() { resumeSession: true, expectChannelID: true, shouldFail: true, - expectedError: ":CHANNEL_ID_ON_EARLY_DATA:", + expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:", flags: []string{ "-enable-early-data", "-expect-early-data-info", diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc index 83066be5..98e70a7f 100644 --- a/ssl/tls13_client.cc +++ b/ssl/tls13_client.cc @@ -417,8 +417,8 @@ static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) { OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA); return ssl_hs_error; } - if (ssl->s3->tlsext_channel_id_valid) { - OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_ON_EARLY_DATA); + if (ssl->s3->tlsext_channel_id_valid || hs->received_custom_extension) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA); return ssl_hs_error; } } diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc index 2b802c47..a3ed8a7f 100644 --- a/ssl/tls13_server.cc +++ b/ssl/tls13_server.cc @@ -380,6 +380,8 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { hs->early_data_offered && /* Channel ID is incompatible with 0-RTT. */ !ssl->s3->tlsext_channel_id_valid && + /* Custom extensions is incompatible with 0-RTT. */ + hs->custom_extensions.received == 0 && /* The negotiated ALPN must match the one in the ticket. */ ssl->s3->alpn_selected_len == session->early_alpn_len && OPENSSL_memcmp(ssl->s3->alpn_selected, session->early_alpn,