aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ssl/doc/src/standards_compliance.xml8
-rw-r--r--lib/ssl/src/ssl_connection.hrl3
-rw-r--r--lib/ssl/src/ssl_handshake.erl10
-rw-r--r--lib/ssl/src/tls_connection_1_3.erl4
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl166
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl186
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_test_lib.erl41
8 files changed, 311 insertions, 118 deletions
diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml
index 3bd86178c8..3a472d4776 100644
--- a/lib/ssl/doc/src/standards_compliance.xml
+++ b/lib/ssl/doc/src/standards_compliance.xml
@@ -340,8 +340,8 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell>
- <cell align="left" valign="middle"><em>NC</em></cell>
- <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle"><em>C</em></cell>
+ <cell align="left" valign="middle"><em>22.1</em></cell>
</row>
<row>
<cell align="left" valign="middle"></cell>
@@ -479,8 +479,8 @@
<row>
<cell align="left" valign="middle"></cell>
<cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell>
- <cell align="left" valign="middle"><em>NC</em></cell>
- <cell align="left" valign="middle"></cell>
+ <cell align="left" valign="middle"><em>C</em></cell>
+ <cell align="left" valign="middle"><em>22.1</em></cell>
</row>
<row>
<cell align="left" valign="middle"></cell>
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index ff7207a8ce..844368c761 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -66,6 +66,7 @@
sni_hostname = undefined,
expecting_next_protocol_negotiation = false ::boolean(),
next_protocol = undefined :: undefined | binary(),
+ alpn = undefined, %% Used in TLS 1.3
negotiated_protocol,
hashsign_algorithm = {undefined, undefined},
cert_hashsign_algorithm = {undefined, undefined},
@@ -76,7 +77,7 @@
srp_params :: #srp_user{} | secret_printout() | 'undefined',
public_key_info :: ssl_handshake:public_key_info() | 'undefined',
premaster_secret :: binary() | secret_printout() | 'undefined',
- server_psk_identity :: binary() | 'undefined' % server psk identity hint
+ server_psk_identity :: binary() | 'undefined' % server psk identity hint
}).
-record(connection_env, {
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index b51ba0fa2d..53676ab355 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -76,7 +76,8 @@
handle_client_hello_extensions/9, %% Returns server hello extensions
handle_server_hello_extensions/9, select_curve/2, select_curve/3,
select_hashsign/4, select_hashsign/5,
- select_hashsign_algs/3, empty_extensions/2, add_server_share/3
+ select_hashsign_algs/3, empty_extensions/2, add_server_share/3,
+ add_alpn/2, add_selected_version/1, decode_alpn/1
]).
-export([get_cert_params/1,
@@ -1165,6 +1166,13 @@ add_server_share(hello_retry_request, Extensions,
Extensions#{key_share => #key_share_hello_retry_request{
selected_group = Group}}.
+add_alpn(Extensions, ALPN0) ->
+ ALPN = encode_alpn([ALPN0], false),
+ Extensions#{alpn => ALPN}.
+
+add_selected_version(Extensions) ->
+ SupportedVersions = #server_hello_selected_version{selected_version = {3,4}},
+ Extensions#{server_hello_selected_version => SupportedVersions}.
kse_remove_private_key(#key_share_entry{
group = Group,
diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl
index 821b7000cc..117e4f059d 100644
--- a/lib/ssl/src/tls_connection_1_3.erl
+++ b/lib/ssl/src/tls_connection_1_3.erl
@@ -228,8 +228,8 @@ wait_cert_cr(internal, #change_cipher_spec{}, State, _Module) ->
tls_connection:next_event(?FUNCTION_NAME, no_record, State);
wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0, _Module) ->
case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of
- #alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State0);
+ {#alert{} = Alert, State} ->
+ ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State);
{State1, NextState} ->
tls_connection:next_event(NextState, no_record, State1)
end;
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 12ab2015aa..4de51c9a35 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -39,8 +39,7 @@
%% Create handshake messages
-export([certificate/5,
certificate_verify/4,
- encrypted_extensions/0,
- server_hello/4]).
+ encrypted_extensions/0]).
-export([do_start/2,
do_negotiated/2,
@@ -62,10 +61,10 @@
%% Create handshake messages
%%====================================================================
-server_hello(MsgType, SessionId, KeyShare, ConnectionStates) ->
+server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) ->
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- Extensions = server_hello_extensions(MsgType, KeyShare),
+ Extensions = server_hello_extensions(MsgType, KeyShare, ALPN),
#server_hello{server_version = {3,3}, %% legacy_version
cipher_suite = SecParams#security_parameters.cipher_suite,
compression_method = 0, %% legacy attribute
@@ -74,10 +73,26 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates) ->
extensions = Extensions
}.
-server_hello_extensions(MsgType, KeyShare) ->
+%% The server's extensions MUST contain "supported_versions".
+%% Additionally, it SHOULD contain the minimal set of extensions
+%% necessary for the client to generate a correct ClientHello pair. As
+%% with the ServerHello, a HelloRetryRequest MUST NOT contain any
+%% extensions that were not first offered by the client in its
+%% ClientHello, with the exception of optionally the "cookie" (see
+%% Section 4.2.2) extension.
+server_hello_extensions(hello_retry_request = MsgType, KeyShare, _) ->
SupportedVersions = #server_hello_selected_version{selected_version = {3,4}},
Extensions = #{server_hello_selected_version => SupportedVersions},
- ssl_handshake:add_server_share(MsgType, Extensions, KeyShare).
+ ssl_handshake:add_server_share(MsgType, Extensions, KeyShare);
+server_hello_extensions(MsgType, KeyShare, undefined) ->
+ SupportedVersions = #server_hello_selected_version{selected_version = {3,4}},
+ Extensions = #{server_hello_selected_version => SupportedVersions},
+ ssl_handshake:add_server_share(MsgType, Extensions, KeyShare);
+server_hello_extensions(MsgType, KeyShare, ALPN0) ->
+ Extensions0 = ssl_handshake:add_selected_version(#{}), %% {3,4} (TLS 1.3)
+ Extensions1 = ssl_handshake:add_alpn(Extensions0, ALPN0),
+ ssl_handshake:add_server_share(MsgType, Extensions1, KeyShare).
+
server_hello_random(server_hello, #security_parameters{server_random = Random}) ->
Random;
@@ -469,7 +484,8 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
#state{connection_states = _ConnectionStates0,
ssl_options = #ssl_options{ciphers = ServerCiphers,
signature_algs = ServerSignAlgs,
- supported_groups = ServerGroups0},
+ supported_groups = ServerGroups0,
+ alpn_preferred_protocols = ALPNPreferredProtocols},
session = #session{own_certificate = Cert}} = State0) ->
ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined),
ClientGroups = get_supported_groups(ClientGroups0),
@@ -478,6 +494,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
ClientShares0 = maps:get(key_share, Extensions, undefined),
ClientShares = get_key_shares(ClientShares0),
+ ClientALPN0 = maps:get(alpn, Extensions, undefined),
+ ClientALPN = ssl_handshake:decode_alpn(ClientALPN0),
+
ClientSignAlgs = get_signature_scheme_list(
maps:get(signature_algs, Extensions, undefined)),
ClientSignAlgsCert = get_signature_scheme_list(
@@ -486,6 +505,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
{Ref,Maybe} = maybe(),
try
+ %% Handle ALPN extension if ALPN is configured
+ ALPNProtocol = Maybe(handle_alpn(ALPNPreferredProtocols, ClientALPN)),
+
%% If the server does not select a PSK, then the server independently selects a
%% cipher suite, an (EC)DHE group and key share for key establishment,
%% and a signature algorithm/certificate pair to authenticate itself to
@@ -511,8 +533,14 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
%% Generate server_share
KeyShare = ssl_cipher:generate_server_share(Group),
- State1 = update_start_state(State0, Cipher, KeyShare, SessionId,
- Group, SelectedSignAlg, ClientPubKey),
+ State1 = update_start_state(State0,
+ #{cipher => Cipher,
+ key_share => KeyShare,
+ session_id => SessionId,
+ group => Group,
+ sign_alg => SelectedSignAlg,
+ peer_public_key => ClientPubKey,
+ alpn => ALPNProtocol}),
%% 4.1.4. Hello Retry Request
%%
@@ -522,10 +550,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
%% the handshake.
Maybe(send_hello_retry_request(State1, ClientPubKey, KeyShare, SessionId))
- %% TODO:
- %% - session handling
- %% - handle extensions: ALPN
- %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats)
+ %% TODO: session handling
catch
{Ref, {insufficient_security, no_suitable_groups}} ->
@@ -537,7 +562,9 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
{Ref, {insufficient_security, no_suitable_signature_algorithm}} ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm");
{Ref, {insufficient_security, no_suitable_public_key}} ->
- ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key)
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key);
+ {Ref, no_application_protocol} ->
+ ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)
end;
%% TLS Client
do_start(#server_hello{cipher_suite = SelectedCipherSuite,
@@ -588,8 +615,11 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions),
%% Update state
- State1 = update_start_state(State0, SelectedCipherSuite, ClientKeyShare, SessionId,
- SelectedGroup, undefined, undefined),
+ State1 = update_start_state(State0,
+ #{cipher => SelectedCipherSuite,
+ key_share => ClientKeyShare,
+ session_id => SessionId,
+ group => SelectedGroup}),
%% Replace ClientHello1 with a special synthetic handshake message
State2 = replace_ch1_with_message_hash(State1),
@@ -625,7 +655,8 @@ do_negotiated(start_handshake,
dh_public_value = ClientPublicKey},
ssl_options = #ssl_options{} = SslOpts,
key_share = KeyShare,
- handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
+ handshake_env = #handshake_env{tls_handshake_history = _HHistory0,
+ alpn = ALPN},
connection_env = #connection_env{private_key = CertPrivateKey},
static_env = #static_env{
cert_db = CertDbHandle,
@@ -640,7 +671,7 @@ do_negotiated(start_handshake,
try
%% Create server_hello
%% Extensions: supported_versions, key_share, (pre_shared_key)
- ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0),
+ ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0, ALPN),
{State1, _} = tls_connection:send_handshake(ServerHello, State0),
@@ -702,6 +733,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) ->
{?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State};
{Ref, {{handshake_failure, Reason}, State}} ->
{?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State};
+ {Ref, {#alert{} = Alert, State}} ->
+ {Alert, State};
{#alert{} = Alert, State} ->
{Alert, State}
end.
@@ -801,8 +834,12 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite,
{_, ClientPrivateKey} = get_client_private_key([SelectedGroup], ClientKeyShare),
%% Update state
- State1 = update_start_state(State0, SelectedCipherSuite, ClientKeyShare0, SessionId,
- SelectedGroup, undefined, ServerPublicKey),
+ State1 = update_start_state(State0,
+ #{cipher => SelectedCipherSuite,
+ key_share => ClientKeyShare0,
+ session_id => SessionId,
+ group => SelectedGroup,
+ peer_public_key => ServerPublicKey}),
State2 = calculate_handshake_secrets(ServerPublicKey, ClientPrivateKey, SelectedGroup, State1),
@@ -858,7 +895,9 @@ do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) ->
{Ref, {{internal_error, Reason}, _State}} ->
?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason);
{Ref, {{handshake_failure, Reason}, _State}} ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason)
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason);
+ {Ref, {#alert{} = Alert, State}} ->
+ {Alert, State}
end;
do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) ->
{Ref,Maybe} = maybe(),
@@ -984,9 +1023,10 @@ compare_verify_data(_, _) ->
{error, decrypt_error}.
-send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0,
+send_hello_retry_request(#state{connection_states = ConnectionStates0,
+ handshake_env = #handshake_env{alpn = ALPN}} = State0,
no_suitable_key, KeyShare, SessionId) ->
- ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0),
+ ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0, ALPN),
{State1, _} = tls_connection:send_handshake(ServerHello, State0),
%% Update handshake history
@@ -1076,13 +1116,11 @@ process_certificate(#certificate_1_3{certificate_list = Certs0},
State = store_peer_cert(State0, PeerCert, PublicKeyInfo),
{ok, {State, wait_cv}};
{error, Reason} ->
- State1 = calculate_traffic_secrets(State0),
- State = ssl_record:step_encryption_state(State1),
+ State = update_encryption_state(Role, State0),
{error, {Reason, State}};
- #alert{} = Alert ->
- State1 = calculate_traffic_secrets(State0),
- State = ssl_record:step_encryption_state(State1),
- {Alert, State}
+ {ok, #alert{} = Alert} ->
+ State = update_encryption_state(Role, State0),
+ {error, {Alert, State}}
end;
false ->
State1 = calculate_traffic_secrets(State0),
@@ -1106,6 +1144,17 @@ is_supported_signature_algorithm([BinCert|_], SignAlgs0) ->
lists:member(Scheme, SignAlgs).
+%% Sets correct encryption state when sending Alerts in shared states that use different secrets.
+%% - If client: use handshake secrets.
+%% - If server: use traffic secrets as by this time the client's state machine
+%% already stepped into the 'connection' state.
+update_encryption_state(server, State0) ->
+ State1 = calculate_traffic_secrets(State0),
+ ssl_record:step_encryption_state(State1);
+update_encryption_state(client, State) ->
+ State.
+
+
validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) ->
ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role),
[PeerCert | ChainCerts ] = Certs,
@@ -1126,9 +1175,9 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHand
{ok, {PublicKeyInfo,_}} ->
{ok, {PeerCert, PublicKeyInfo}};
{error, Reason} ->
- ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts,
- SslOptions, Options,
- CertDbHandle, CertDbRef)
+ {ok, ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts,
+ SslOptions, Options,
+ CertDbHandle, CertDbRef)}
end
catch
error:{badmatch,{asn1, Asn1Reason}} ->
@@ -1337,11 +1386,24 @@ update_connection_state(ConnectionState = #{security_parameters := SecurityParam
cipher_state => cipher_init(Key, IV, FinishedKey)}.
+update_start_state(State, Map) ->
+ Cipher = maps:get(cipher, Map, undefined),
+ KeyShare = maps:get(key_share, Map, undefined),
+ SessionId = maps:get(session_id, Map, undefined),
+ Group = maps:get(group, Map, undefined),
+ SelectedSignAlg = maps:get(sign_alg, Map, undefined),
+ PeerPublicKey = maps:get(peer_public_key, Map, undefined),
+ ALPNProtocol = maps:get(alpn, Map, undefined),
+ update_start_state(State, Cipher, KeyShare, SessionId,
+ Group, SelectedSignAlg, PeerPublicKey,
+ ALPNProtocol).
+%%
update_start_state(#state{connection_states = ConnectionStates0,
+ handshake_env = #handshake_env{} = HsEnv,
connection_env = CEnv,
session = Session} = State,
Cipher, KeyShare, SessionId,
- Group, SelectedSignAlg, ClientPubKey) ->
+ Group, SelectedSignAlg, PeerPublicKey, ALPNProtocol) ->
#{security_parameters := SecParamsR0} = PendingRead =
maps:get(pending_read, ConnectionStates0),
#{security_parameters := SecParamsW0} = PendingWrite =
@@ -1352,11 +1414,12 @@ update_start_state(#state{connection_states = ConnectionStates0,
ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR},
pending_write => PendingWrite#{security_parameters => SecParamsW}},
State#state{connection_states = ConnectionStates,
+ handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol},
key_share = KeyShare,
session = Session#session{session_id = SessionId,
ecc = Group,
sign_alg = SelectedSignAlg,
- dh_public_value = ClientPubKey,
+ dh_public_value = PeerPublicKey,
cipher_suite = Cipher},
connection_env = CEnv#connection_env{negotiated_version = {3,4}}}.
@@ -1628,19 +1691,28 @@ get_server_public_key({key_share_entry, Group, PublicKey}) ->
{Group, PublicKey}.
-%% get_client_public_key(Group, ClientShares) ->
-%% case lists:keysearch(Group, 2, ClientShares) of
-%% {value, {_, _, ClientPublicKey}} ->
-%% ClientPublicKey;
-%% false ->
-%% %% 4.1.4. Hello Retry Request
-%% %%
-%% %% The server will send this message in response to a ClientHello
-%% %% message if it is able to find an acceptable set of parameters but the
-%% %% ClientHello does not contain sufficient information to proceed with
-%% %% the handshake.
-%% no_suitable_key
-%% end.
+%% RFC 7301 - Application-Layer Protocol Negotiation Extension
+%% It is expected that a server will have a list of protocols that it
+%% supports, in preference order, and will only select a protocol if the
+%% client supports it. In that case, the server SHOULD select the most
+%% highly preferred protocol that it supports and that is also
+%% advertised by the client. In the event that the server supports no
+%% protocols that the client advertises, then the server SHALL respond
+%% with a fatal "no_application_protocol" alert.
+handle_alpn(undefined, _) ->
+ {ok, undefined};
+handle_alpn([], _) ->
+ {error, no_application_protocol};
+handle_alpn([_|_], undefined) ->
+ {ok, undefined};
+handle_alpn([ServerProtocol|T], ClientProtocols) ->
+ case lists:member(ServerProtocol, ClientProtocols) of
+ true ->
+ {ok, ServerProtocol};
+ false ->
+ handle_alpn(T, ClientProtocols)
+ end.
+
select_cipher_suite([], _) ->
{error, no_suitable_cipher};
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 82548cc23c..ce4479020e 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -272,7 +272,11 @@ tls13_test_group() ->
tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client,
tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client,
tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client,
- tls13_connection_information].
+ tls13_connection_information,
+ tls13_ssl_server_with_alpn_ssl_client,
+ tls13_ssl_server_with_alpn_ssl_client_empty_alpn,
+ tls13_ssl_server_with_alpn_ssl_client_bad_alpn,
+ tls13_ssl_server_with_alpn_ssl_client_alpn].
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
@@ -5597,11 +5601,7 @@ tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) ->
Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- ssl_test_lib:check_result(Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -5635,11 +5635,7 @@ tls13_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) ->
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ClientOpts}]),
- ssl_test_lib:check_result(Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -5791,11 +5787,7 @@ tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) ->
Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- ssl_test_lib:check_result(Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -5831,11 +5823,7 @@ tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) ->
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ClientOpts}]),
- ssl_test_lib:check_result(Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -5991,13 +5979,7 @@ tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) ->
Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- ssl_test_lib:check_result(
- Server,
- {error,
- {tls_alert,
- {insufficient_security,
- "received SERVER ALERT: Fatal - Insufficient Security - "
- "\"No suitable signature algorithm\""}}}),
+ ssl_test_lib:check_server_alert(Server, insufficient_security),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -6030,13 +6012,7 @@ tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client(Config) ->
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ClientOpts}]),
- ssl_test_lib:check_result(
- Server,
- {error,
- {tls_alert,
- {insufficient_security,
- "received SERVER ALERT: Fatal - Insufficient Security - "
- "\"No suitable signature algorithm\""}}}),
+ ssl_test_lib:check_server_alert(Server, insufficient_security),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -6070,12 +6046,7 @@ tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) -
Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- ssl_test_lib:check_result(
- Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -6114,12 +6085,7 @@ tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client(Config) ->
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ClientOpts}]),
- ssl_test_lib:check_result(
- Server,
- {error,
- {tls_alert,
- {certificate_required,
- "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}),
+ ssl_test_lib:check_server_alert(Server, certificate_required),
ssl_test_lib:close(Server),
ssl_test_lib:close_port(Client).
@@ -6147,6 +6113,132 @@ tls13_connection_information(Config) ->
ssl_test_lib:close_port(Client).
+tls13_ssl_server_with_alpn_ssl_client() ->
+ [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client"}].
+
+tls13_ssl_server_with_alpn_ssl_client(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(Client).
+
+
+tls13_ssl_server_with_alpn_ssl_client_empty_alpn() ->
+ [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with empty ALPN"}].
+
+tls13_ssl_server_with_alpn_ssl_client_empty_alpn(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_advertised_protocols, []}|ClientOpts0],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_server_alert(Server, no_application_protocol),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(Client).
+
+
+tls13_ssl_server_with_alpn_ssl_client_bad_alpn() ->
+ [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with bad ALPN"}].
+
+tls13_ssl_server_with_alpn_ssl_client_bad_alpn(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_advertised_protocols, [<<1,2,3,4>>]}|ClientOpts0],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_server_alert(Server, no_application_protocol),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(Client).
+
+tls13_ssl_server_with_alpn_ssl_client_alpn() ->
+ [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with correct ALPN"}].
+
+tls13_ssl_server_with_alpn_ssl_client_alpn(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {alpn_advertised_protocols, [<<1,2,3,4>>, <<5,6>>]}|ClientOpts0],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(Client).
+
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 55dee9a48f..c6982bb928 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -40,6 +40,7 @@
%%--------------------------------------------------------------------
all() ->
[
+ {group, 'tlsv1.3'},
{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
@@ -50,6 +51,7 @@ all() ->
groups() ->
[
+ {'tlsv1.3', [], all_protocol_groups()},
{'tlsv1.2', [], all_protocol_groups()},
{'tlsv1.1', [], all_protocol_groups()},
{'tlsv1', [], all_protocol_groups()},
@@ -300,7 +302,13 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
{from, self()},
{options, [{active, Active} | BadClientOpts]}]),
- ssl_test_lib:check_server_alert(Server, Client, handshake_failure).
+ Version = proplists:get_value(version,Config),
+ case Version of
+ 'tlsv1.3' ->
+ ssl_test_lib:check_server_alert(Server, Client, certificate_required);
+ _ ->
+ ssl_test_lib:check_server_alert(Server, Client, handshake_failure)
+ end.
%%--------------------------------------------------------------------
server_require_peer_cert_empty_ok() ->
@@ -853,6 +861,7 @@ invalid_signature_server(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer} | ClientOpts]}]),
ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
+
%%--------------------------------------------------------------------
invalid_signature_client() ->
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 6294ce3739..0318cc81e3 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -428,41 +428,42 @@ check_result(Pid, Msg) ->
{got, Unexpected}},
ct:fail(Reason)
end.
+
check_server_alert(Pid, Alert) ->
receive
{Pid, {error, {tls_alert, {Alert, STxt}}}} ->
check_server_txt(STxt),
+ ok;
+ {Pid, {error, closed}} ->
ok
end.
check_server_alert(Server, Client, Alert) ->
receive
{Server, {error, {tls_alert, {Alert, STxt}}}} ->
check_server_txt(STxt),
- receive
- {Client, {error, {tls_alert, {Alert, CTxt}}}} ->
- check_client_txt(CTxt),
- ok;
- {Client, {error, closed}} ->
- ok
- end
+ check_client_alert(Client, Alert)
end.
check_client_alert(Pid, Alert) ->
receive
{Pid, {error, {tls_alert, {Alert, CTxt}}}} ->
check_client_txt(CTxt),
+ ok;
+ {Pid, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} ->
+ check_client_txt(CTxt),
+ ok;
+ {Pid, {error, closed}} ->
ok
end.
check_client_alert(Server, Client, Alert) ->
receive
{Client, {error, {tls_alert, {Alert, CTxt}}}} ->
check_client_txt(CTxt),
- receive
- {Server, {error, {tls_alert, {Alert, STxt}}}} ->
- check_server_txt(STxt),
- ok;
- {Server, {error, closed}} ->
- ok
- end
+ check_server_alert(Server, Alert);
+ {Client, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} ->
+ check_client_txt(CTxt),
+ ok;
+ {Client, {error, closed}} ->
+ ok
end.
check_server_txt("TLS server" ++ _) ->
ok;
@@ -1103,7 +1104,15 @@ run_client_error(Opts) ->
Options = proplists:get_value(options, Opts),
ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]),
Error = Transport:connect(Host, Port, Options),
- Pid ! {self(), Error}.
+ case Error of
+ {error, {tls_alert, _}} ->
+ Pid ! {self(), Error};
+ {ok, _Socket} ->
+ receive
+ {ssl_error, _, {tls_alert, _}} = SslError ->
+ Pid ! {self(), SslError}
+ end
+ end.
accepters(N) ->
accepters([], N).
@@ -1642,6 +1651,8 @@ is_tls_version('dtlsv1.2') ->
true;
is_tls_version('dtlsv1') ->
true;
+is_tls_version('tlsv1.3') ->
+ true;
is_tls_version('tlsv1.2') ->
true;
is_tls_version('tlsv1.1') ->