From c081d4e33eac13f3f2df52ffacb38f99bc15dfc9 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 29 Sep 2025 12:33:03 +0200 Subject: [PATCH] ssl: Assert that hello extensions are unique --- lib/ssl/src/ssl_handshake.erl | 37 ++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index ed647e40c8a3..d88f47dad13f 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -2885,10 +2885,12 @@ decode_extensions(<>, Version, MessageType, Acc) when Len + 2 =:= ExtLen -> ALPN = #alpn{extension_data = ExtensionData}, + assert_unique_extension(alpn, Acc), decode_extensions(Rest, Version, MessageType, Acc#{alpn => ALPN}); decode_extensions(<>, Version, MessageType, Acc) -> NextP = #next_protocol_negotiation{extension_data = ExtensionData}, + assert_unique_extension(next_protocol_negotiation, Acc), decode_extensions(Rest, Version, MessageType, Acc#{next_protocol_negotiation => NextP}); decode_extensions(<>, Version, MessageType, Acc) -> @@ -2900,6 +2902,7 @@ decode_extensions(<> = Info, VerifyInfo end, + assert_unique_extension(renegotiation_info, Acc), decode_extensions(Rest, Version, MessageType, Acc#{renegotiation_info => #renegotiation_info{renegotiated_connection = @@ -2908,6 +2911,7 @@ decode_extensions(<>, Version, MessageType, Acc) when Len == SRPLen + 1 -> + assert_unique_extension(srp, Acc), decode_extensions(Rest, Version, MessageType, Acc#{srp => #srp{username = SRP}}); decode_extensions(<> = ExtData, HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || <> <= SignAlgoList], + assert_unique_extension(signature_algs, Acc), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs => #hash_sign_algos{hash_sign_algos = @@ -2926,6 +2931,7 @@ decode_extensions(<> = ExtData, HashSigns = decode_sign_alg(Version, SignSchemeList), + assert_unique_extension(signature_algs, Acc), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs => #hash_sign_algos{ @@ -2935,6 +2941,7 @@ decode_extensions(<> = ExtData, SignSchemes = decode_sign_alg(Version, SignSchemeList), + assert_unique_extension(signature_algs, Acc), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs => #signature_algorithms{ @@ -2954,6 +2961,7 @@ decode_extensions(<> <= SignSchemeList]), + assert_unique_extension(signature_algs_cert, Acc), decode_extensions(Rest, Version, MessageType, Acc#{signature_algs_cert => #signature_algorithms_cert{ @@ -2963,6 +2971,7 @@ decode_extensions(<>, Version, MessageType, Acc) -> <> = ExtData, Profiles = [P || <> <= ProfilesBin], + assert_unique_extension(use_srtp, Acc), decode_extensions(Rest, Version, MessageType, Acc#{use_srtp => #use_srtp{ @@ -2983,6 +2992,7 @@ decode_extensions(<> <= EllipticCurveList]), + assert_unique_extension(elliptic_curves, Acc), decode_extensions(Rest, Version, MessageType, Acc#{elliptic_curves => #elliptic_curves{elliptic_curve_list = @@ -3001,6 +3011,7 @@ decode_extensions(<> <= GroupList]), + assert_unique_extension(elliptic_curves, Acc), decode_extensions(Rest, Version, MessageType, Acc#{elliptic_curves => #supported_groups{supported_groups = @@ -3010,6 +3021,7 @@ decode_extensions(<>, Version, MessageType, Acc) -> <> = ExtData, ECPointFormats = binary_to_list(ECPointFormatList), + assert_unique_extension(ec_point_formats, Acc), decode_extensions(Rest, Version, MessageType, Acc#{ec_point_formats => #ec_point_formats{ec_point_format_list = @@ -3017,22 +3029,26 @@ decode_extensions(<>, Version, MessageType, Acc) when Len == 0 -> + assert_unique_extension(sni, Acc), decode_extensions(Rest, Version, MessageType, Acc#{sni => #sni{hostname = ""}}); %% Server may send an empty SNI decode_extensions(<>, Version, MessageType, Acc) -> <> = ExtData, + assert_unique_extension(sni, Acc), decode_extensions(Rest, Version, MessageType, Acc#{sni => dec_sni(NameList)}); decode_extensions(<>, Version, MessageType, Acc) -> %% RFC 6066 Section 4 + assert_unique_extension(max_frag_enum, Acc), decode_extensions(Rest, Version, MessageType, Acc#{max_frag_enum => #max_frag_enum{enum = MaxFragEnum}}); decode_extensions(<>, Version, MessageType, Acc) when Len > 2 -> <> = ExtData, + assert_unique_extension(client_hello_versions, Acc), decode_extensions(Rest, Version, MessageType, Acc#{client_hello_versions => #client_hello_versions{ @@ -3041,6 +3057,7 @@ decode_extensions(<>, Version, MessageType, Acc) when Len =:= 2, SelectedVersion =:= 16#0304 -> + assert_unique_extension(server_hello_selected_version, Acc), decode_extensions(Rest, Version, MessageType, Acc#{server_hello_selected_version => #server_hello_selected_version{selected_version = ?TLS_1_3}}); @@ -3049,6 +3066,7 @@ decode_extensions(<>, Version, MessageType = client_hello, Acc) -> <> = ExtData, + assert_unique_extension(key_share, Acc), decode_extensions(Rest, Version, MessageType, Acc#{key_share => #key_share_client_hello{ @@ -3058,6 +3076,7 @@ decode_extensions(<>, Version, MessageType = server_hello, Acc) -> <> = ExtData, + assert_unique_extension(key_share, Acc), decode_extensions(Rest, Version, MessageType, Acc#{key_share => #key_share_server_hello{ @@ -3078,6 +3097,7 @@ decode_extensions(<>, Version, MessageType, Acc) -> <> = ExtData, + assert_unique_extension(psk_key_exchange_modes, Acc), decode_extensions(Rest, Version, MessageType, Acc#{psk_key_exchange_modes => #psk_key_exchange_modes{ @@ -3087,17 +3107,18 @@ decode_extensions(<>, Version, MessageType = client_hello, Acc) -> <> = ExtData, + assert_unique_extension(pre_shared_key, Acc), 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, + assert_unique_extension(pre_shared_key, Acc), decode_extensions(Rest, Version, MessageType, Acc#{pre_shared_key => #pre_shared_key_server_hello{ @@ -3107,6 +3128,7 @@ decode_extensions(<>, Version, MessageType, Acc) when Len == CookieLen + 2 -> + assert_unique_extension(cookie, Acc), decode_extensions(Rest, Version, MessageType, Acc#{cookie => #cookie{cookie = Cookie}}); @@ -3117,6 +3139,7 @@ decode_extensions(<>, Version, MessageType = server_hello, Acc) when Len =:= 0 -> + assert_unique_extension(status_request, Acc), decode_extensions(Rest, Version, MessageType, Acc#{status_request => undefined}); %% RFC8446 4.4.2.1, In TLS1.3, the body of the "status_request" extension @@ -3129,6 +3152,7 @@ decode_extensions(<> -> + assert_unique_extension(status_request, Acc), decode_extensions(Rest, Version, MessageType, Acc#{status_request => #certificate_status{response = ASN1OCSPResponse}}); _Other -> @@ -3137,12 +3161,14 @@ decode_extensions(<>, Version, MessageType, Acc) -> + assert_unique_extension(early_data, Acc), decode_extensions(Rest, Version, MessageType, Acc#{early_data => #early_data_indication{}}); decode_extensions(<>, Version, MessageType, Acc) -> + assert_unique_extension(early_data, Acc), decode_extensions(Rest, Version, MessageType, Acc#{early_data => #early_data_indication_nst{indication = MaxSize}}); @@ -3151,6 +3177,7 @@ decode_extensions(< CertAutsLen = Len - 2, <> = CertAutsExt, + assert_unique_extension(certificate_authorities, Acc), decode_extensions(Rest, Version, MessageType, Acc#{certificate_authorities => #certificate_authorities{authorities = decode_cert_auths(EncCertAuts, [])}}); @@ -3162,6 +3189,14 @@ decode_extensions(<> decode_extensions(_, _, _, Acc) -> Acc. +assert_unique_extension(Ext, Map) -> + case maps:get(Ext, Map, undefined) of + undefined -> + ok; + _ -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, {duplicate_extension, Ext})) + end. + decode_sign_alg(?TLS_1_2, SignSchemeList) -> %% Ignore unknown signature algorithms Fun = fun(Elem) ->