aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_handshake.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_handshake.erl')
-rw-r--r--lib/ssl/src/ssl_handshake.erl154
1 files changed, 139 insertions, 15 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index c6698bc74a..bd2efa9fbb 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -58,7 +58,7 @@
]).
%% Encode
--export([encode_handshake/2, encode_hello_extensions/1, encode_extensions/1, encode_extensions/2,
+-export([encode_handshake/2, encode_hello_extensions/2, encode_extensions/1, encode_extensions/2,
encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]).
%% Decode
-export([decode_handshake/3, decode_vector/1, decode_hello_extensions/4, decode_extensions/3,
@@ -534,14 +534,14 @@ encode_handshake(#next_protocol{selected_protocol = SelectedProtocol}, _Version)
PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
{?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
-encode_handshake(#server_hello{server_version = {Major, Minor},
+encode_handshake(#server_hello{server_version = {Major, Minor} = Version,
random = Random,
session_id = Session_ID,
cipher_suite = CipherSuite,
compression_method = Comp_method,
extensions = Extensions}, _Version) ->
SID_length = byte_size(Session_ID),
- ExtensionsBin = encode_hello_extensions(Extensions),
+ ExtensionsBin = encode_hello_extensions(Extensions, Version),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
@@ -589,7 +589,9 @@ encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = Ha
encode_handshake(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
-encode_hello_extensions(Extensions) ->
+encode_hello_extensions(_, {3, 0}) ->
+ <<>>;
+encode_hello_extensions(Extensions, _) ->
encode_extensions(hello_extensions_list(Extensions), <<>>).
encode_extensions(Exts) ->
@@ -707,7 +709,25 @@ encode_extensions([#key_share_server_hello{server_share = ServerShare0} | Rest],
encode_extensions([#key_share_hello_retry_request{selected_group = Group0} | Rest], Acc) ->
Group = tls_v1:group_to_enum(Group0),
encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT),
- ?UINT16(2), ?UINT16(Group), Acc/binary>>).
+ ?UINT16(2), ?UINT16(Group), Acc/binary>>);
+encode_extensions([#psk_key_exchange_modes{ke_modes = KEModes0} | Rest], Acc) ->
+ KEModes = encode_psk_key_exchange_modes(KEModes0),
+ KEModesLen = byte_size(KEModes),
+ ExtLen = KEModesLen + 1,
+ encode_extensions(Rest, <<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT),
+ ?UINT16(ExtLen), ?BYTE(KEModesLen), KEModes/binary, Acc/binary>>);
+encode_extensions([#pre_shared_key_client_hello{
+ offered_psks = #offered_psks{
+ identities = Identities0,
+ binders = Binders0} = PSKs} | Rest], Acc) ->
+ Identities = encode_psk_identities(Identities0),
+ Binders = encode_psk_binders(Binders0),
+ Len = byte_size(Identities) + byte_size(Binders),
+ encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT),
+ ?UINT16(Len), Identities/binary, Binders/binary, Acc/binary>>);
+encode_extensions([#pre_shared_key_server_hello{selected_identity = Identity} | Rest], Acc) ->
+ encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT),
+ ?UINT16(2), ?UINT16(Identity), Acc/binary>>).
encode_client_protocol_negotiation(undefined, _) ->
@@ -1256,6 +1276,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
%% We also ignore the ALPN extension during renegotiation (see encode_alpn/2).
[Protocol] when not Renegotiation ->
{ConnectionStates, alpn, Protocol};
+ [_] when Renegotiation ->
+ {ConnectionStates, alpn, undefined};
undefined ->
NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined),
Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation),
@@ -1486,8 +1508,12 @@ extension_value(#signature_algorithms_cert{signature_scheme_list = Schemes}) ->
Schemes;
extension_value(#key_share_client_hello{client_shares = ClientShares}) ->
ClientShares;
+extension_value(#key_share_server_hello{server_share = ServerShare}) ->
+ ServerShare;
extension_value(#client_hello_versions{versions = Versions}) ->
- Versions.
+ Versions;
+extension_value(#server_hello_selected_version{selected_version = SelectedVersion}) ->
+ SelectedVersion.
%%--------------------------------------------------------------------
@@ -2089,6 +2115,41 @@ encode_key_share_entry(#key_share_entry{
Len = byte_size(KeyExchange),
<<?UINT16((tls_v1:group_to_enum(Group))),?UINT16(Len),KeyExchange/binary>>.
+encode_psk_key_exchange_modes(KEModes) ->
+ encode_psk_key_exchange_modes(lists:reverse(KEModes), <<>>).
+%%
+encode_psk_key_exchange_modes([], Acc) ->
+ Acc;
+encode_psk_key_exchange_modes([psk_ke|T], Acc) ->
+ encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_KE),Acc/binary>>);
+encode_psk_key_exchange_modes([psk_dhe_ke|T], Acc) ->
+ encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_DHE_KE),Acc/binary>>).
+
+
+encode_psk_identities(Identities) ->
+ encode_psk_identities(Identities, <<>>).
+%%
+encode_psk_identities([], Acc) ->
+ Len = byte_size(Acc),
+ <<?UINT16(Len), Acc/binary>>;
+encode_psk_identities([#psk_identity{
+ identity = Identity,
+ obfuscated_ticket_age = Age}|T], Acc) ->
+ IdLen = byte_size(Identity),
+ encode_psk_identities(T, <<Acc/binary,?UINT16(IdLen),Identity/binary,Age/binary>>).
+
+
+encode_psk_binders(Binders) ->
+ encode_psk_binders(Binders, <<>>).
+%%
+encode_psk_binders([], Acc) ->
+ Len = byte_size(Acc),
+ <<?UINT16(Len), Acc/binary>>;
+encode_psk_binders([Binder|T], Acc) ->
+ Len = byte_size(Binder),
+ encode_psk_binders(T, <<Acc/binary,?BYTE(Len),Binder/binary>>).
+
+
hello_extensions_list(HelloExtensions) ->
[Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined].
@@ -2441,6 +2502,33 @@ decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len),
#key_share_hello_retry_request{
selected_group = tls_v1:enum_to_group(Group)}});
+decode_extensions(<<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) ->
+ <<?BYTE(PLen),KEModes:PLen/binary>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{psk_key_exchange_modes =>
+ #psk_key_exchange_modes{
+ ke_modes = decode_psk_key_exchange_modes(KEModes)}});
+
+decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>,
+ Version, MessageType = client_hello, Acc) ->
+ <<?UINT16(IdLen),Identities:IdLen/binary,?UINT16(BLen),Binders:BLen/binary>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{pre_shared_key =>
+ #pre_shared_key_client_hello{
+ offered_psks = #offered_psks{
+ identities = decode_psk_identities(Identities),
+ binders = decode_psk_binders(Binders)}}});
+
+decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>,
+ Version, MessageType = server_hello, Acc) ->
+ <<?UINT16(Identity)>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{pre_shared_key =>
+ #pre_shared_key_server_hello{
+ selected_identity = Identity}});
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
@@ -2500,6 +2588,38 @@ decode_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
decode_protocols(_Bytes, _Acc) ->
{error, invalid_protocols}.
+
+decode_psk_key_exchange_modes(KEModes) ->
+ decode_psk_key_exchange_modes(KEModes, []).
+%%
+decode_psk_key_exchange_modes(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_key_exchange_modes(<<?BYTE(?PSK_KE), Rest/binary>>, Acc) ->
+ decode_psk_key_exchange_modes(Rest, [psk_ke|Acc]);
+decode_psk_key_exchange_modes(<<?BYTE(?PSK_DHE_KE), Rest/binary>>, Acc) ->
+ decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]).
+
+
+decode_psk_identities(Identities) ->
+ decode_psk_identities(Identities, []).
+%%
+decode_psk_identities(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_identities(<<?UINT16(Len), Identity:Len/binary, Age:4/binary, Rest/binary>>, Acc) ->
+ decode_psk_identities(Rest, [#psk_identity{
+ identity = Identity,
+ obfuscated_ticket_age = Age}|Acc]).
+
+
+decode_psk_binders(Binders) ->
+ decode_psk_binders(Binders, []).
+%%
+decode_psk_binders(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_binders(<<?BYTE(Len), Binder:Len/binary, Rest/binary>>, Acc) ->
+ decode_psk_binders(Rest, [Binder|Acc]).
+
+
%% encode/decode stream of certificate data to/from list of certificate data
certs_to_list(ASN1Certs) ->
certs_to_list(ASN1Certs, []).
@@ -2666,7 +2786,7 @@ filter_unavailable_ecc_suites(_, Suites) ->
handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite,
ClientCipherSuites, Compression,
ConnectionStates0, Renegotiation, SecureRenegotation) ->
- {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0,
+ {ok, ConnectionStates} = handle_renegotiation_info(Version, RecordCB, Role, Info, ConnectionStates0,
Renegotiation, SecureRenegotation,
ClientCipherSuites),
hello_pending_connection_states(RecordCB, Role,
@@ -2936,11 +3056,11 @@ renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
#renegotiation_info{renegotiated_connection = undefined}
end.
-handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
+handle_renegotiation_info(_, _RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
-handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
+handle_renegotiation_info(_, _RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
true ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -2948,10 +3068,10 @@ handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _,
{ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
end;
-handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) ->
+handle_renegotiation_info(_, _RecordCB, _, undefined, ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
-handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
+handle_renegotiation_info(_, _RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
ConnectionStates, true, _, _) ->
ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
CData = maps:get(client_verify_data, ConnectionState),
@@ -2962,7 +3082,7 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co
false ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation))
end;
-handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
+handle_renegotiation_info(_, _RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
ConnectionStates, true, _, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
@@ -2978,11 +3098,13 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation))
end
end;
+handle_renegotiation_info({3,0}, _RecordCB, client, undefined, ConnectionStates, true, _SecureRenegotation, _) ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
-handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
+handle_renegotiation_info(_, RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
-handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
+handle_renegotiation_info(_, RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
true ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}));
@@ -3036,7 +3158,7 @@ empty_extensions({3,4}, client_hello) ->
%% padding => undefined,
key_share => undefined,
pre_shared_key => undefined,
- %% psk_key_exhange_modes => undefined,
+ psk_key_exchange_modes => undefined,
%% early_data => undefined,
%% cookie => undefined,
client_hello_versions => undefined,
@@ -3065,6 +3187,8 @@ empty_extensions({3,4}, hello_retry_request) ->
key_share => undefined,
pre_shared_key => undefined
};
+empty_extensions({3,0}, _) ->
+ empty_extensions();
empty_extensions(_, server_hello) ->
#{renegotiation_info => undefined,
alpn => undefined,