From 83f9c5c032cc502df38b52d8a5b0389657b37801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Wed, 17 Jul 2019 16:40:04 +0200 Subject: ssl: Implement psk_key_exchange_modes extension Implement encode/decode of psk_key_exchange_modes. --- lib/ssl/src/ssl_handshake.erl | 40 +++++++++++++++++++++++++++++++++++++-- lib/ssl/src/tls_handshake_1_3.hrl | 36 +++++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 14 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 488e4bb72a..07c7f10f2b 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -709,7 +709,13 @@ 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(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, <>). encode_client_protocol_negotiation(undefined, _) -> @@ -2093,6 +2099,17 @@ encode_key_share_entry(#key_share_entry{ Len = byte_size(KeyExchange), <>. +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, <>); +encode_psk_key_exchange_modes([psk_dhe_ke|T], Acc) -> + encode_psk_key_exchange_modes(T, <>). + + hello_extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined]. @@ -2445,6 +2462,13 @@ decode_extensions(<>, Version, MessageType, Acc) -> + <> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{psk_key_exchange_modes => + #psk_key_exchange_modes{ + ke_modes = decode_psk_key_exchange_modes(KEModes)}}); %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. @@ -2504,6 +2528,18 @@ decode_protocols(<>, 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(<>, Acc) -> + decode_psk_key_exchange_modes(Rest, [psk_ke|Acc]); +decode_psk_key_exchange_modes(<>, Acc) -> + decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]). + + %% encode/decode stream of certificate data to/from list of certificate data certs_to_list(ASN1Certs) -> certs_to_list(ASN1Certs, []). @@ -3042,7 +3078,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, diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl index 7ae1b93e1c..78756c69db 100644 --- a/lib/ssl/src/tls_handshake_1_3.hrl +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -74,29 +74,41 @@ y % opaque Y[coordinate_length]; }). +%% RFC 8446 4.2.9. Pre-Shared Key Exchange Modes + +%% enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode; -define(PSK_KE, 0). -define(PSK_DHE_KE, 1). --record(psk_keyexchange_modes, { +-record(psk_key_exchange_modes, { ke_modes % ke_modes<1..255> }). + +%% RFC 8446 4.2.10. Early Data Indication -record(empty, { }). -record(early_data_indication, { indication % uint32 max_early_data_size (new_session_ticket) | %% #empty{} (client_hello, encrypted_extensions) }). --record(psk_identity, { - identity, % opaque identity<1..2^16-1> - obfuscated_ticket_age % uint32 - }). --record(offered_psks, { - psk_identity, %identities<7..2^16-1>; - psk_binder_entry %binders<33..2^16-1>, opaque PskBinderEntry<32..255> - }). --record(pre_shared_keyextension,{ - extension %OfferedPsks (client_hello) | uint16 selected_identity (server_hello) - }). + +%% RFC 8446 4.2.11. Pre-Shared Key Extension +-record(psk_identity, + { + identity, % opaque identity<1..2^16-1> + obfuscated_ticket_age % uint32 + }). + +-record(offered_psks, + { + identities, % PskIdentity identities<7..2^16-1>; + binders % PskBinderEntry binders<33..2^16-1>; opaque PskBinderEntry<32..255> + }). + +-record(pre_shared_key, + { + data % OfferedPsks (client_hello) | uint16 selected_identity (server_hello) + }). %% RFC 8446 B.3.1.2. -record(cookie, { -- cgit v1.2.3 From c45076cbbcb954434234dc7b33fee68692768cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Mon, 22 Jul 2019 11:25:46 +0200 Subject: ssl: Implement pre_shared_key extension Implement encode/decode of pre_shared_key. --- lib/ssl/src/ssl_handshake.erl | 78 ++++++++++++++++++++++++++++++++++++++- lib/ssl/src/tls_handshake_1_3.hrl | 15 +++++++- 2 files changed, 90 insertions(+), 3 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 07c7f10f2b..0585aebcb2 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -715,7 +715,19 @@ encode_extensions([#psk_key_exchange_modes{ke_modes = KEModes0} | Rest], Acc) -> KEModesLen = byte_size(KEModes), ExtLen = KEModesLen + 1, encode_extensions(Rest, <>). + ?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, <>); +encode_extensions([#pre_shared_key_server_hello{selected_identity = Identity} | Rest], Acc) -> + encode_extensions(Rest, <>). encode_client_protocol_negotiation(undefined, _) -> @@ -2110,6 +2122,30 @@ encode_psk_key_exchange_modes([psk_dhe_ke|T], Acc) -> encode_psk_key_exchange_modes(T, <>). +encode_psk_identities(Identities) -> + encode_psk_identities(Identities, <<>>). +%% +encode_psk_identities([], Acc) -> + Len = byte_size(Acc), + <>; +encode_psk_identities([#psk_identity{ + identity = Identity, + obfuscated_ticket_age = Age}|T], Acc) -> + IdLen = byte_size(Identity), + encode_psk_identities(T, <>). + + +encode_psk_binders(Binders) -> + encode_psk_binders(Binders, <<>>). +%% +encode_psk_binders([], Acc) -> + Len = byte_size(Acc), + <>; +encode_psk_binders([Binder|T], Acc) -> + Len = byte_size(Binder), + encode_psk_binders(T, <>). + + hello_extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined]. @@ -2470,6 +2506,26 @@ decode_extensions(<>, + Version, MessageType = client_hello, Acc) -> + <> = 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(<>, + Version, MessageType = server_hello, Acc) -> + <> = 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. decode_extensions(<>, Version, MessageType, Acc) -> @@ -2540,6 +2596,26 @@ decode_psk_key_exchange_modes(<>, 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(<>, 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(<>, 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, []). diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl index 78756c69db..eb85f216c8 100644 --- a/lib/ssl/src/tls_handshake_1_3.hrl +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -105,9 +105,20 @@ binders % PskBinderEntry binders<33..2^16-1>; opaque PskBinderEntry<32..255> }). --record(pre_shared_key, +%% struct { +%% select (Handshake.msg_type) { +%% case client_hello: OfferedPsks; +%% case server_hello: uint16 selected_identity; +%% }; +%% } PreSharedKeyExtension; +-record(pre_shared_key_client_hello, { - data % OfferedPsks (client_hello) | uint16 selected_identity (server_hello) + offered_psks + }). + +-record(pre_shared_key_server_hello, + { + selected_identity }). %% RFC 8446 B.3.1.2. -- cgit v1.2.3 From cbc8e9ce89ce30e3be90d9ad500becd3c26370e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Tue, 23 Jul 2019 15:57:58 +0200 Subject: ssl: Document option supported_groups --- lib/ssl/src/ssl.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 20b1e85ceb..ce639e8fde 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -128,7 +128,8 @@ tls_alert/0, srp_param_type/0, named_curve/0, - sign_scheme/0]). + sign_scheme/0, + group/0]). %% ------------------------------------------------------------------------------------------------------- @@ -243,7 +244,7 @@ secp160r2. % exported -type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | - ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. + ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. % exported -type srp_param_type() :: srp_1024 | srp_1536 | @@ -296,6 +297,7 @@ {ciphers, cipher_suites()} | {eccs, [named_curve()]} | {signature_algs_cert, signature_schemes()} | + {supported_groups, supported_groups()} | {secure_renegotiate, secure_renegotiation()} | {depth, allowed_cert_chain_length()} | {verify_fun, custom_verify()} | @@ -342,6 +344,7 @@ -type protocol_versions() :: [protocol_version()]. -type signature_algs() :: [{hash(), sign_algo()}]. -type signature_schemes() :: [sign_scheme()]. +-type supported_groups() :: [group()]. -type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: any()}. -type padding_check() :: boolean(). -type beast_mitigation() :: one_n_minus_one | zero_n | disabled. -- cgit v1.2.3 From 73b526ce765dc7ac71fdae349da44941d8201d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Wed, 24 Jul 2019 11:11:07 +0200 Subject: ssl: Implement option honor_cipher_order in TLS 1.3 --- lib/ssl/src/tls_handshake_1_3.erl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 6aeb9f5663..c29366e717 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -504,7 +504,8 @@ do_start(#client_hello{cipher_suites = ClientCiphers, ssl_options = #ssl_options{ciphers = ServerCiphers, signature_algs = ServerSignAlgs, supported_groups = ServerGroups0, - alpn_preferred_protocols = ALPNPreferredProtocols}, + alpn_preferred_protocols = ALPNPreferredProtocols, + honor_cipher_order = HonorCipherOrder}, session = #session{own_certificate = Cert}} = State0) -> ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), @@ -531,7 +532,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% cipher suite, an (EC)DHE group and key share for key establishment, %% and a signature algorithm/certificate pair to authenticate itself to %% the client. - Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), + Cipher = Maybe(select_cipher_suite(HonorCipherOrder, ClientCiphers, ServerCiphers)), Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), Maybe(validate_client_key_share(ClientGroups, ClientShares)), @@ -1731,15 +1732,19 @@ handle_alpn([ServerProtocol|T], ClientProtocols) -> end. -select_cipher_suite([], _) -> +select_cipher_suite(_, [], _) -> {error, no_suitable_cipher}; -select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> +%% If honor_cipher_order is set to true, use the server's preference for +%% cipher suite selection. +select_cipher_suite(true, ClientCiphers, ServerCiphers) -> + select_cipher_suite(false, ServerCiphers, ClientCiphers); +select_cipher_suite(false, [Cipher|ClientCiphers], ServerCiphers) -> case lists:member(Cipher, tls_v1:suites('TLS_v1.3')) andalso lists:member(Cipher, ServerCiphers) of true -> {ok, Cipher}; false -> - select_cipher_suite(ClientCiphers, ServerCiphers) + select_cipher_suite(false, ClientCiphers, ServerCiphers) end. -- cgit v1.2.3 From b08621c8a9bf4f8e780de8bba022ad902308cfc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Thu, 25 Jul 2019 14:16:34 +0200 Subject: ssl: Fix handshake pause in TLS 1.3 --- lib/ssl/src/ssl_handshake.erl | 6 +++++- lib/ssl/src/tls_record_1_3.erl | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 0585aebcb2..bd2efa9fbb 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1508,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. %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl index 74321a1ae2..d713062284 100644 --- a/lib/ssl/src/tls_record_1_3.erl +++ b/lib/ssl/src/tls_record_1_3.erl @@ -138,6 +138,15 @@ decode_cipher_text(#ssl_tls{type = ?ALERT, {#ssl_tls{type = ?ALERT, version = {3,4}, %% Internally use real version fragment = <<2,47>>}, ConnectionStates0}; +%% TLS 1.3 server can receive a User Cancelled Alert when handshake is +%% paused and then cancelled on the client side. +decode_cipher_text(#ssl_tls{type = ?ALERT, + version = ?LEGACY_VERSION, + fragment = <<2,90>>}, + ConnectionStates0) -> + {#ssl_tls{type = ?ALERT, + version = {3,4}, %% Internally use real version + fragment = <<2,90>>}, ConnectionStates0}; %% RFC8446 - TLS 1.3 %% D.4. Middlebox Compatibility Mode %% - If not offering early data, the client sends a dummy -- cgit v1.2.3