summaryrefslogtreecommitdiff
path: root/CVE-2022-37026.patch
diff options
context:
space:
mode:
Diffstat (limited to 'CVE-2022-37026.patch')
-rw-r--r--CVE-2022-37026.patch593
1 files changed, 593 insertions, 0 deletions
diff --git a/CVE-2022-37026.patch b/CVE-2022-37026.patch
new file mode 100644
index 0000000..44b66e0
--- /dev/null
+++ b/CVE-2022-37026.patch
@@ -0,0 +1,593 @@
+From cd5024867e7b7d3a6e94194af9e01e1fb77e36c9 Mon Sep 17 00:00:00 2001
+From: Ingela Anderton Andin <ingela@erlang.org>
+Date: Tue, 24 May 2022 17:52:02 +0200
+Subject: [PATCH] ssl: Enhanch handling of unexpected messages
+
+Origin:
+https://github.com/erlang/otp/commit/cd5024867e7b7d3a6e94194af9e01e1fb77e36c9
+https://github.com/erlang/otp/commit/6a1baa36e4e6c1b682e8b48e0c141602e0b8e6e5
+
+Make better use of gen_statem. Rename flag and values to better names.
+---
+ lib/ssl/src/dtls_connection.erl | 25 ++++-
+ lib/ssl/src/ssl_connection.hrl | 6 +-
+ lib/ssl/src/ssl_gen_statem.erl | 3 -
+ lib/ssl/src/tls_connection.erl | 21 +++-
+ lib/ssl/src/tls_dtls_connection.erl | 155 +++++++++++++++++-----------
+ lib/ssl/src/tls_gen_connection.erl | 23 ++++-
+ lib/ssl/src/tls_handshake_1_3.erl | 8 +-
+ lib/ssl/test/ssl_npn_SUITE.erl | 8 +-
+ 8 files changed, 171 insertions(+), 78 deletions(-)
+
+diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
+index 78348826e471..5a85bf8016ed 100644
+--- a/lib/ssl/src/dtls_connection.erl
++++ b/lib/ssl/src/dtls_connection.erl
+@@ -46,7 +46,8 @@
+ %% ClientKeyExchange \
+ %% CertificateVerify* Flight 5
+ %% [ChangeCipherSpec] /
+-%% Finished --------> /
++%% NextProtocol* /
++%% Finished --------> /
+ %%
+ %% [ChangeCipherSpec] \ Flight 6
+ %% <-------- Finished /
+@@ -64,7 +65,8 @@
+ %% <-------- Finished / part 2
+ %%
+ %% [ChangeCipherSpec] \ Abbrev Flight 3
+-%% Finished --------> /
++%% NextProtocol* /
++%% Finished --------> /
+ %%
+ %%
+ %% Message Flights for Abbbriviated Handshake
+@@ -140,6 +142,7 @@
+ user_hello/3,
+ wait_ocsp_stapling/3,
+ certify/3,
++ wait_cert_verify/3,
+ cipher/3,
+ abbreviated/3,
+ connection/3]).
+@@ -462,6 +465,24 @@ certify(state_timeout, Event, State) ->
+ certify(Type, Event, State) ->
+ gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
++
++%%--------------------------------------------------------------------
++-spec wait_cert_verify(gen_statem:event_type(), term(), #state{}) ->
++ gen_statem:state_function_result().
++%%--------------------------------------------------------------------
++wait_cert_verify(enter, _Event, State0) ->
++ {State, Actions} = handle_flight_timer(State0),
++ {keep_state, State, Actions};
++wait_cert_verify(info, Event, State) ->
++ gen_info(Event, ?FUNCTION_NAME, State);
++wait_cert_verify(state_timeout, Event, State) ->
++ handle_state_timeout(Event, ?FUNCTION_NAME, State);
++wait_cert_verify(Type, Event, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
++ try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State)
++ catch throw:#alert{} = Alert ->
++ ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
++ end.
++
+ %%--------------------------------------------------------------------
+ -spec cipher(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
+index 4f9584bb9ffe..9534ae023446 100644
+--- a/lib/ssl/src/ssl_connection.hrl
++++ b/lib/ssl/src/ssl_connection.hrl
+@@ -115,7 +115,7 @@
+ %% need to worry about packet loss in TLS. In DTLS we
+ %% need to track DTLS handshake seqnr
+ flight_buffer = [] :: list() | map(),
+- client_certificate_requested = false :: boolean(),
++ client_certificate_status = not_requested :: not_requested | requested | empty | needs_verifying | verified,
+ protocol_specific = #{} :: map(),
+ session :: #session{} | secret_printout(),
+ key_share,
+@@ -147,8 +147,8 @@
+ %% session_cache_cb - not implemented
+ %% crl_db - not implemented
+ %% client_hello_version - Bleichenbacher mitigation in TLS 1.2
+-%% client_certificate_requested - Built into TLS 1.3 state machine
+-%% key_algorithm - not used
++%% client_certificate_status - only uses non_requested| requested
++%% key_algorithm - only uses not_requested and requested
+ %% diffie_hellman_params - used in TLS 1.2 ECDH key exchange
+ %% diffie_hellman_keys - used in TLS 1.2 ECDH key exchange
+ %% psk_identity - not used
+diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl
+index e6268b4876cf..cde74b6acf00 100644
+--- a/lib/ssl/src/ssl_gen_statem.erl
++++ b/lib/ssl/src/ssl_gen_statem.erl
+@@ -669,9 +669,6 @@ handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName,
+ Connection:handle_protocol_record(TLSorDTLSRecord, StateName, State);
+ handle_common_event(timeout, hibernate, _, _) ->
+ {keep_state_and_data, [hibernate]};
+-handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName,
+- #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
+- handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, StateName, State);
+ handle_common_event({timeout, handshake}, close, _StateName, #state{start_or_recv_from = StartFrom} = State) ->
+ {stop_and_reply,
+ {shutdown, user_timeout},
+diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
+index 8f25e5a3cd43..d87d6c15a224 100644
+--- a/lib/ssl/src/tls_connection.erl
++++ b/lib/ssl/src/tls_connection.erl
+@@ -34,6 +34,7 @@
+ %% ClientKeyExchange \
+ %% CertificateVerify* Flight 3 part 1
+ %% [ChangeCipherSpec] /
++%% NextProtocol*
+ %% Finished --------> / Flight 3 part 2
+ %% [ChangeCipherSpec]
+ %% <-------- Finished Flight 4
+@@ -48,6 +49,7 @@
+ %% [ChangeCipherSpec]
+ %% <-------- Finished Abbrev Flight 2 part 2
+ %% [ChangeCipherSpec]
++%% NextProtocol*
+ %% Finished --------> Abbrev Flight 3
+ %% Application Data <-------> Application Data
+ %%
+@@ -70,13 +72,14 @@
+ %% |
+ %% New session | Resumed session
+ %% WAIT_OCSP_STAPELING CERTIFY <----------------------------------> ABBRIVIATED
+-%%
++%% WAIT_CERT_VERIFY
+ %% <- Possibly Receive -- | |
+-%% OCSP Stapel ------> | Flight 3 part 1 |
++%% OCSP Stapel/CertVerify -> | Flight 3 part 1 |
+ %% | |
+ %% V | Abbrev Flight 2 part 2 to Abbrev Flight 3
+ %% CIPHER |
+ %% | |
++%% | |
+ %% | Fligth 3 part 2 to Flight 4 |
+ %% | |
+ %% V V
+@@ -121,6 +124,7 @@
+ user_hello/3,
+ wait_ocsp_stapling/3,
+ certify/3,
++ wait_cert_verify/3,
+ cipher/3,
+ abbreviated/3,
+ connection/3]).
+@@ -303,6 +307,19 @@ certify(info, Event, State) ->
+ certify(Type, Event, State) ->
+ tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State).
+
++
++%%--------------------------------------------------------------------
++-spec wait_cert_verify(gen_statem:event_type(), term(), #state{}) ->
++ gen_statem:state_function_result().
++%%--------------------------------------------------------------------
++wait_cert_verify(info, Event, State) ->
++ gen_info(Event, ?FUNCTION_NAME, State);
++wait_cert_verify(Type, Event, #state{connection_env = #connection_env{negotiated_version = Version}} = State) ->
++ try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State)
++ catch throw:#alert{} = Alert ->
++ ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
++ end.
++
+ %%--------------------------------------------------------------------
+ -spec cipher(gen_statem:event_type(), term(), #state{}) ->
+ gen_statem:state_function_result().
+diff --git a/lib/ssl/src/tls_dtls_connection.erl b/lib/ssl/src/tls_dtls_connection.erl
+index 02c55f3941b6..7d659baea0e7 100644
+--- a/lib/ssl/src/tls_dtls_connection.erl
++++ b/lib/ssl/src/tls_dtls_connection.erl
+@@ -54,6 +54,7 @@
+ user_hello/3,
+ abbreviated/3,
+ certify/3,
++ wait_cert_verify/3,
+ wait_ocsp_stapling/3,
+ cipher/3,
+ connection/3,
+@@ -319,7 +320,7 @@ certify(internal, #certificate{asn1_certificates = []},
+ ssl_options = #{verify := verify_peer,
+ fail_if_no_peer_cert := false}} =
+ State0) ->
+- Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false});
++ Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_status = empty});
+ certify(internal, #certificate{},
+ #state{static_env = #static_env{role = server},
+ connection_env = #connection_env{negotiated_version = Version},
+@@ -344,16 +345,21 @@ certify(internal, #certificate{asn1_certificates = [Peer|_]} = Cert,
+ ocsp_stapling_state = #{ocsp_expect := Status} = OcspState},
+ connection_env = #connection_env{
+ negotiated_version = Version},
+- ssl_options = Opts} = State) when Status =/= staple ->
++ ssl_options = Opts} = State0) when Status =/= staple ->
+ OcspInfo = ocsp_info(OcspState, Opts, Peer),
+ case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef,
+ Opts, CRLDbInfo, Role, Host,
+ ensure_tls(Version), OcspInfo) of
+ {PeerCert, PublicKeyInfo} ->
+- handle_peer_cert(Role, PeerCert, PublicKeyInfo,
+- State#state{client_certificate_requested = false}, Connection, []);
++ State = case Role of
++ server ->
++ State0#state{client_certificate_status = needs_verifying};
++ client ->
++ State0
++ end,
++ handle_peer_cert(Role, PeerCert, PublicKeyInfo, State, Connection, []);
+ #alert{} = Alert ->
+- ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
++ ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
+ end;
+ certify(internal, #server_key_exchange{exchange_keys = Keys},
+ #state{static_env = #static_env{role = client,
+@@ -421,7 +427,7 @@ certify(internal, #certificate_request{},
+ %% The client does not have a certificate and will send an empty reply, the server may fail
+ %% or accept the connection by its own preference. No signature algorihms needed as there is
+ %% no certificate to verify.
+- Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_requested = true});
++ Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_status = requested});
+ certify(internal, #certificate_request{} = CertRequest,
+ #state{static_env = #static_env{role = client,
+ protocol_cb = Connection},
+@@ -435,7 +441,7 @@ certify(internal, #certificate_request{} = CertRequest,
+ ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
+ NegotiatedHashSign ->
+ Connection:next_event(?FUNCTION_NAME, no_record,
+- State#state{client_certificate_requested = true,
++ State#state{client_certificate_status = requested,
+ handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = NegotiatedHashSign}})
+ end;
+ %% PSK and RSA_PSK might bypass the Server-Key-Exchange
+@@ -514,14 +520,6 @@ certify(internal, #server_hello_done{},
+ #alert{} = Alert ->
+ ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0)
+ end;
+-certify(internal = Type, #client_key_exchange{} = Msg,
+- #state{static_env = #static_env{role = server},
+- client_certificate_requested = true,
+- connection_env = #connection_env{negotiated_version = Version},
+- ssl_options = #{fail_if_no_peer_cert := true}} = State) ->
+- %% We expect a certificate here
+- Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type, Msg}}),
+- ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
+ certify(internal, #client_key_exchange{exchange_keys = Keys},
+ State = #state{handshake_env = #handshake_env{kex_algorithm = KeyAlg},
+ static_env = #static_env{protocol_cb = Connection},
+@@ -539,37 +537,53 @@ certify(Type, Event, State) ->
+ ssl_gen_statem:handle_common_event(Type, Event, ?FUNCTION_NAME, State).
+
+ %%--------------------------------------------------------------------
+--spec cipher(gen_statem:event_type(),
+- #hello_request{} | #certificate_verify{} | #finished{} | term(),
++-spec wait_cert_verify(gen_statem:event_type(),
++ #hello_request{} | #certificate_verify{} | term(),
+ #state{}) ->
+ gen_statem:state_function_result().
+ %%--------------------------------------------------------------------
+-cipher({call, From}, Msg, State) ->
+- handle_call(Msg, From, ?FUNCTION_NAME, State);
+-cipher(info, Msg, State) ->
+- handle_info(Msg, ?FUNCTION_NAME, State);
+-cipher(internal, #certificate_verify{signature = Signature,
+- hashsign_algorithm = CertHashSign},
+- #state{static_env = #static_env{role = server,
+- protocol_cb = Connection},
+- handshake_env = #handshake_env{tls_handshake_history = Hist,
+- kex_algorithm = KexAlg,
+- public_key_info = PubKeyInfo} = HsEnv,
+- connection_env = #connection_env{negotiated_version = Version},
+- session = #session{master_secret = MasterSecret}
+- } = State) ->
++wait_cert_verify(internal, #certificate_verify{signature = Signature,
++ hashsign_algorithm = CertHashSign},
++ #state{static_env = #static_env{role = server,
++ protocol_cb = Connection},
++ client_certificate_status = needs_verifying,
++ handshake_env = #handshake_env{tls_handshake_history = Hist,
++ kex_algorithm = KexAlg,
++ public_key_info = PubKeyInfo},
++ connection_env = #connection_env{negotiated_version = Version},
++ session = #session{master_secret = MasterSecret} = Session0
++ } = State) ->
+
+ TLSVersion = ssl:tls_version(Version),
+- %% Use negotiated value if TLS-1.2 otherwhise return default
++ %% Use negotiated value if TLS-1.2 otherwise return default
+ HashSign = negotiated_hashsign(CertHashSign, KexAlg, PubKeyInfo, TLSVersion),
+ case ssl_handshake:certificate_verify(Signature, PubKeyInfo,
+ TLSVersion, HashSign, MasterSecret, Hist) of
+ valid ->
+- Connection:next_event(?FUNCTION_NAME, no_record,
+- State#state{handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = HashSign}});
++ Connection:next_event(cipher, no_record,
++ State#state{client_certificate_status = verified,
++ session = Session0#session{sign_alg = HashSign}});
+ #alert{} = Alert ->
+- ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State)
++ throw(Alert)
+ end;
++
++wait_cert_verify(internal, #hello_request{}, _) ->
++ keep_state_and_data;
++wait_cert_verify(Type, Event, State) ->
++ ssl_gen_statem:handle_common_event(Type, Event, ?FUNCTION_NAME, State).
++
++%%--------------------------------------------------------------------
++-spec cipher(gen_statem:event_type(),
++ #hello_request{} | #finished{} | term(),
++ #state{}) ->
++ gen_statem:state_function_result().
++%%--------------------------------------------------------------------
++cipher({call, From}, Msg, State) ->
++ handle_call(Msg, From, ?FUNCTION_NAME, State);
++cipher(info, Msg, State) ->
++ handle_info(Msg, ?FUNCTION_NAME, State);
++
++
+ %% client must send a next protocol message if we are expecting it
+ cipher(internal, #finished{},
+ #state{static_env = #static_env{role = server},
+@@ -609,6 +623,7 @@ cipher(internal, #next_protocol{selected_protocol = SelectedProtocol},
+ Connection:next_event(?FUNCTION_NAME, no_record,
+ State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol,
+ expecting_next_protocol_negotiation = false}});
++
+ cipher(internal, #change_cipher_spec{type = <<1>>},
+ #state{handshake_env = HsEnv,
+ static_env = #static_env{protocol_cb = Connection},
+@@ -881,12 +896,12 @@ handle_peer_cert_key(_, _, _, _, State) ->
+ certify_client(#state{static_env = #static_env{role = client,
+ cert_db = CertDbHandle,
+ cert_db_ref = CertDbRef},
+- client_certificate_requested = true,
++ client_certificate_status = requested,
+ session = #session{own_certificates = OwnCerts}}
+ = State, Connection) ->
+ Certificate = ssl_handshake:certificate(OwnCerts, CertDbHandle, CertDbRef, client),
+ Connection:queue_handshake(Certificate, State);
+-certify_client(#state{client_certificate_requested = false} = State, _) ->
++certify_client(#state{client_certificate_status = not_requested} = State, _) ->
+ State.
+
+ verify_client_cert(#state{static_env = #static_env{role = client},
+@@ -894,7 +909,7 @@ verify_client_cert(#state{static_env = #static_env{role = client},
+ cert_hashsign_algorithm = HashSign},
+ connection_env = #connection_env{negotiated_version = Version,
+ private_key = PrivateKey},
+- client_certificate_requested = true,
++ client_certificate_status = requested,
+ session = #session{master_secret = MasterSecret,
+ own_certificates = OwnCerts}} = State, Connection) ->
+
+@@ -907,7 +922,7 @@ verify_client_cert(#state{static_env = #static_env{role = client},
+ #alert{} = Alert ->
+ throw(Alert)
+ end;
+-verify_client_cert(#state{client_certificate_requested = false} = State, _) ->
++verify_client_cert(#state{client_certificate_status = not_requested} = State, _) ->
+ State.
+
+ client_certify_and_key_exchange(#state{connection_env = #connection_env{negotiated_version = Version}} =
+@@ -917,7 +932,7 @@ client_certify_and_key_exchange(#state{connection_env = #connection_env{negotiat
+ {State2, Actions} = finalize_handshake(State1, certify, Connection),
+ State = State2#state{
+ %% Reinitialize
+- client_certificate_requested = false},
++ client_certificate_status = not_requested},
+ Connection:next_event(cipher, no_record, State, Actions)
+ catch
+ throw:#alert{} = Alert ->
+@@ -936,8 +951,8 @@ server_certify_and_key_exchange(State0, Connection) ->
+
+ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS},
+ #state{connection_env = #connection_env{private_key = Key},
+- handshake_env = #handshake_env{client_hello_version = {Major, Minor} = Version}}
+- = State, Connection) ->
++ handshake_env = #handshake_env{client_hello_version = {Major, Minor} = Version},
++ client_certificate_status = CCStatus} = State, Connection) ->
+ FakeSecret = make_premaster_secret(Version, rsa),
+ %% Countermeasure for Bleichenbacher attack always provide some kind of premaster secret
+ %% and fail handshake later.RFC 5246 section 7.4.7.1.
+@@ -955,56 +970,74 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS
+ catch
+ #alert{description = ?DECRYPT_ERROR} ->
+ FakeSecret
+- end,
+- calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
++ end,
++ calculate_master_secret(PremasterSecret, State, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
+ #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
+- kex_keys = {_, ServerDhPrivateKey}}
++ kex_keys = {_, ServerDhPrivateKey}},
++ client_certificate_status = CCStatus
+ } = State,
+ Connection) ->
+ PremasterSecret = ssl_handshake:premaster_secret(ClientPublicDhKey, ServerDhPrivateKey, Params),
+- calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
++ calculate_master_secret(PremasterSecret, State, Connection, certify, client_kex_next_state(CCStatus));
+
+ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientPublicEcDhPoint},
+- #state{handshake_env = #handshake_env{kex_keys = ECDHKey}} = State, Connection) ->
++ #state{handshake_env = #handshake_env{kex_keys = ECDHKey},
++ client_certificate_status = CCStatus
++ } = State, Connection) ->
+ PremasterSecret = ssl_handshake:premaster_secret(#'ECPoint'{point = ClientPublicEcDhPoint}, ECDHKey),
+- calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
++ calculate_master_secret(PremasterSecret, State, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_psk_identity{} = ClientKey,
+ #state{ssl_options =
+- #{user_lookup_fun := PSKLookup}} = State0,
++ #{user_lookup_fun := PSKLookup},
++ client_certificate_status = CCStatus
++ } = State0,
+ Connection) ->
+ PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
+- calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
++ calculate_master_secret(PremasterSecret, State0, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
+ #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
+ kex_keys = {_, ServerDhPrivateKey}},
+ ssl_options =
+- #{user_lookup_fun := PSKLookup}} = State0,
++ #{user_lookup_fun := PSKLookup},
++ client_certificate_status = CCStatus
++ } = State0,
+ Connection) ->
+ PremasterSecret =
+ ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
+- calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
++ calculate_master_secret(PremasterSecret, State0, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
+ #state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey},
+ ssl_options =
+- #{user_lookup_fun := PSKLookup}} = State,
++ #{user_lookup_fun := PSKLookup},
++ client_certificate_status = CCStatus
++ } = State,
+ Connection) ->
+ PremasterSecret =
+ ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup),
+- calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
++ calculate_master_secret(PremasterSecret, State, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
+- #state{connection_env = #connection_env{private_key = Key},
++ #state{connection_env = #connection_env{private_key = PrivateKey},
+ ssl_options =
+- #{user_lookup_fun := PSKLookup}} = State0,
++ #{user_lookup_fun := PSKLookup},
++ client_certificate_status = CCStatus} = State0,
+ Connection) ->
+- PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
+- calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
++ PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PrivateKey, PSKLookup),
++ calculate_master_secret(PremasterSecret, State0, Connection, certify, client_kex_next_state(CCStatus));
+ certify_client_key_exchange(#client_srp_public{} = ClientKey,
+ #state{handshake_env = #handshake_env{srp_params = Params,
+- kex_keys = Key}
++ kex_keys = Key},
++ client_certificate_status = CCStatus
+ } = State0, Connection) ->
+ PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, Params),
+- calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher).
++ calculate_master_secret(PremasterSecret, State0, Connection, certify, client_kex_next_state(CCStatus)).
++
++client_kex_next_state(needs_verifying) ->
++ wait_cert_verify;
++client_kex_next_state(empty) ->
++ cipher;
++client_kex_next_state(not_requested) ->
++ cipher.
+
+ certify_server(#state{handshake_env = #handshake_env{kex_algorithm = KexAlg}} =
+ State, _) when KexAlg == dh_anon;
+@@ -1334,7 +1367,7 @@ request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
+ Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef,
+ HashSigns, TLSVersion),
+ State = Connection:queue_handshake(Msg, State0),
+- State#state{client_certificate_requested = true};
++ State#state{client_certificate_status = requested};
+
+ request_client_cert(#state{ssl_options = #{verify := verify_none}} =
+ State, _) ->
+diff --git a/lib/ssl/src/tls_gen_connection.erl b/lib/ssl/src/tls_gen_connection.erl
+index 5da87e79d6be..37106ea6ff21 100644
+--- a/lib/ssl/src/tls_gen_connection.erl
++++ b/lib/ssl/src/tls_gen_connection.erl
+@@ -349,7 +349,28 @@ next_event(StateName, #alert{} = Alert, State, Actions) ->
+ {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}.
+
+ %%% TLS record protocol level application data messages
+-handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName,
++handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA}, StateName,
++ #state{static_env = #static_env{role = server},
++ connection_env = #connection_env{negotiated_version = Version},
++ handshake_env = #handshake_env{renegotiation = {false, first}}
++ } = State) when StateName == initial_hello;
++ StateName == hello;
++ StateName == certify;
++ StateName == wait_cert_verify;
++ StateName == wait_ocsp_stapling;
++ StateName == abbreviated;
++ StateName == cipher
++ ->
++ %% Application data can not be sent before initial handshake pre TLS-1.3.
++ Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, application_data_before_initial_handshake),
++ ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State);
++handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA}, start = StateName,
++ #state{static_env = #static_env{role = server},
++ connection_env = #connection_env{negotiated_version = Version}
++ } = State) ->
++ Alert = ?ALERT_REC(?FATAL, ?DECODE_ERROR, invalid_tls_13_message),
++ ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State);
++handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName,
+ #state{start_or_recv_from = From,
+ socket_options = #socket_options{active = false}} = State0) when From =/= undefined ->
+ case ssl_gen_statem:read_application_data(Data, State0) of
+diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
+index a6014739c681..7fd1b8767201 100644
+--- a/lib/ssl/src/tls_handshake_1_3.erl
++++ b/lib/ssl/src/tls_handshake_1_3.erl
+@@ -1187,7 +1187,7 @@ maybe_append_change_cipher_spec(#state{
+ maybe_append_change_cipher_spec(State, Bin) ->
+ {State, Bin}.
+
+-maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) ->
++maybe_queue_cert_cert_cv(#state{client_certificate_status = not_requested} = State) ->
+ {ok, State};
+ maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0,
+ session = #session{session_id = _SessionId,
+@@ -1408,7 +1408,7 @@ create_change_cipher_spec(#state{ssl_options = #{log_level := LogLevel}}) ->
+
+ process_certificate_request(#certificate_request_1_3{},
+ #state{session = #session{own_certificates = undefined}} = State) ->
+- {ok, {State#state{client_certificate_requested = true}, wait_cert}};
++ {ok, {State#state{client_certificate_status = requested}, wait_cert}};
+
+ process_certificate_request(#certificate_request_1_3{
+ extensions = Extensions},
+@@ -1427,11 +1427,11 @@ process_certificate_request(#certificate_request_1_3{
+ %% Check if server supports signature algorithm of client certificate
+ case check_cert_sign_algo(SignAlgo, SignHash, ServerSignAlgs, ServerSignAlgsCert) of
+ ok ->
+- {ok, {State#state{client_certificate_requested = true,
++ {ok, {State#state{client_certificate_status = requested,
+ session = Session#session{sign_alg = SelectedSignAlg}}, wait_cert}};
+ {error, _} ->
+ %% Certificate not supported: send empty certificate in state 'wait_finished'
+- {ok, {State#state{client_certificate_requested = true,
++ {ok, {State#state{client_certificate_status = requested,
+ session = Session#session{own_certificates = undefined}}, wait_cert}}
+ end
+ catch
+diff --git a/lib/ssl/test/ssl_npn_SUITE.erl b/lib/ssl/test/ssl_npn_SUITE.erl
+index 81c75ecff04a..914563b782ed 100644
+--- a/lib/ssl/test/ssl_npn_SUITE.erl
++++ b/lib/ssl/test/ssl_npn_SUITE.erl
+@@ -68,14 +68,18 @@
+ all() ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+- {group, 'tlsv1'}
++ {group, 'tlsv1'},
++ {group, 'dtlsv1.2'},
++ {group, 'dtlsv1'}
+ ].
+
+ groups() ->
+ [
+ {'tlsv1.2', [], next_protocol_tests()},
+ {'tlsv1.1', [], next_protocol_tests()},
+- {'tlsv1', [], next_protocol_tests()}
++ {'tlsv1', [], next_protocol_tests()},
++ {'dtlsv1.2', [], next_protocol_tests()},
++ {'dtlsv1', [], next_protocol_tests()}
+ ].
+
+ next_protocol_tests() ->