diff options
Diffstat (limited to 'lib/ssl/src/ssl_handshake.erl')
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 641 |
1 files changed, 302 insertions, 339 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 56a1ca81b9..fb30372999 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2022. All Rights Reserved. +%% Copyright Ericsson AB 2013-2023. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ %%---------------------------------------------------------------------- -module(ssl_handshake). +-feature(maybe_expr,enable). -include("ssl_handshake.hrl"). -include("ssl_record.hrl"). @@ -59,7 +60,7 @@ ]). %% Encode --export([encode_handshake/2, encode_hello_extensions/2, encode_extensions/1, encode_extensions/2, +-export([encode_handshake/2, encode_hello_extensions/1, 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, @@ -69,13 +70,13 @@ %% Cipher suites handling -export([available_suites/2, available_signature_algs/2, available_signature_algs/3, - cipher_suites/3, prf/6, select_session/9, supported_ecc/1, + cipher_suites/3, prf/6, select_session/9, premaster_secret/2, premaster_secret/3, premaster_secret/4]). %% Extensions handling -export([client_hello_extensions/10, handle_client_hello_extensions/10, %% Returns server hello extensions - handle_server_hello_extensions/10, select_curve/3, select_curve/4, + handle_server_hello_extensions/10, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, select_hashsign_algs/3, empty_extensions/2, add_server_share/3, add_alpn/2, add_selected_version/1, decode_alpn/1, max_frag_enum/1 @@ -84,11 +85,12 @@ -export([get_cert_params/1, select_own_cert/1, server_name/3, - path_validate/9, path_validation/10, validation_fun_and_state/4, path_validation_alert/1]). +%% Tracing +-export([handle_trace/3]). %%==================================================================== %% Create handshake messages %%==================================================================== @@ -337,10 +339,9 @@ next_protocol(SelectedProtocol) -> %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, - #{server_name_indication := ServerNameIndication, - partial_chain := PartialChain} = SSlOptions, + #{partial_chain := PartialChain} = SSlOptions, CRLDbHandle, Role, Host, Version, CertExt) -> - ServerName = server_name(ServerNameIndication, Host, Role), + ServerName = server_name(SSlOptions, Host, Role), [PeerCert | _ChainCerts ] = ASN1Certs, try PathsAndAnchors = @@ -391,26 +392,29 @@ verify_signature(_, Msg, {HashAlgo, SignAlgo}, Signature, SignAlgo == rsa_pss_pss -> Options = verify_options(SignAlgo, HashAlgo, PubKeyParams), public_key:verify(Msg, HashAlgo, Signature, PubKey, Options); -verify_signature({3, Minor}, Msg, {HashAlgo, SignAlgo}, Signature, {?rsaEncryption, PubKey, PubKeyParams}) - when Minor >= 3 -> +verify_signature(Version, Msg, {HashAlgo, SignAlgo}, Signature, {?rsaEncryption, PubKey, PubKeyParams}) + when ?TLS_GTE(Version, ?TLS_1_2) -> Options = verify_options(SignAlgo, HashAlgo, PubKeyParams), public_key:verify(Msg, HashAlgo, Signature, PubKey, Options); -verify_signature({3, Minor}, {digest, Digest}, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) when Minor =< 2 -> +verify_signature(Version, {digest, Digest}, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) + when ?TLS_LTE(Version, ?TLS_1_1) -> case public_key:decrypt_public(Signature, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of Digest -> true; _ -> false end; -verify_signature({3, 4}, Msg, {_, eddsa}, Signature, {?'id-Ed25519', PubKey, PubKeyParams}) -> +verify_signature(?TLS_1_3, Msg, {_, eddsa}, Signature, {?'id-Ed25519', PubKey, PubKeyParams}) -> public_key:verify(Msg, none, Signature, {PubKey, PubKeyParams}); -verify_signature({3, 4}, Msg, {_, eddsa}, Signature, {?'id-Ed448', PubKey, PubKeyParams}) -> +verify_signature(?TLS_1_3, Msg, {_, eddsa}, Signature, {?'id-Ed448', PubKey, PubKeyParams}) -> public_key:verify(Msg, none, Signature, {PubKey, PubKeyParams}); verify_signature(_, Msg, {HashAlgo, _SignAlg}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> public_key:verify(Msg, HashAlgo, Signature, {PublicKey, PublicKeyParams}); -verify_signature({3, Minor}, _Msg, {_HashAlgo, anon}, _Signature, _) when Minor =< 3 -> +verify_signature(Version, _Msg, {_HashAlgo, anon}, _Signature, _) + when ?TLS_1_X(Version), ?TLS_LTE(Version, ?TLS_1_2) -> true; -verify_signature({3, Minor}, Msg, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) when Minor =< 3-> +verify_signature(Version, Msg, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) + when ?TLS_1_X(Version), ?TLS_LTE(Version, ?TLS_1_2) -> public_key:verify(Msg, HashAlgo, Signature, {PublicKey, PublicKeyParams}). %%-------------------------------------------------------------------- @@ -521,17 +525,13 @@ select_version(RecordCB, ClientVersion, Versions) -> %% Called by TLS 1.2/1.3 Server when "supported_versions" is present %% in ClientHello. %% Input lists are ordered (highest first) -select_supported_version([], _ServerVersions) -> - undefined; -select_supported_version([ClientVersion|T], ServerVersions) -> - case lists:member(ClientVersion, ServerVersions) of - true -> - ClientVersion; - false -> - select_supported_version(T, ServerVersions) +select_supported_version(ClientVersions, ServerVersions) -> + Fn = fun (ClientVersion) -> lists:member(ClientVersion, ServerVersions) end, + case lists:search(Fn, ClientVersions) of + {value, ClientVersion} -> ClientVersion; + false -> undefined end. - %%==================================================================== %% Encode handshake %%==================================================================== @@ -540,14 +540,15 @@ 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} = Version, +encode_handshake(#server_hello{server_version = ServerVersion, 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, Version), + {Major,Minor} = ServerVersion, + ExtensionsBin = encode_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; @@ -564,7 +565,7 @@ encode_handshake(#server_key_params{params_bin = Keys, hashsign = HashSign, encode_handshake(#certificate_request{certificate_types = CertTypes, hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, certificate_authorities = CertAuths}, - {3,3}) -> + ?TLS_1_2) -> HashSigns = << <<(ssl_cipher:signature_scheme(SignatureScheme)):16 >> || SignatureScheme <- HashSignAlgos >>, EncCertAuths = encode_cert_auths(CertAuths), @@ -588,17 +589,15 @@ encode_handshake(#certificate_request{certificate_types = CertTypes, }; encode_handshake(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; -encode_handshake(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> - {?CLIENT_KEY_EXCHANGE, encode_client_key(ExchangeKeys, Version)}; +encode_handshake(#client_key_exchange{exchange_keys = ExchangeKeys}, _Version) -> + {?CLIENT_KEY_EXCHANGE, encode_client_key(ExchangeKeys)}; encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) -> EncSig = enc_sign(HashSign, BinSig, Version), {?CERTIFICATE_VERIFY, EncSig}; encode_handshake(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. -encode_hello_extensions(_, {3, 0}) -> - <<>>; -encode_hello_extensions(Extensions, _) -> +encode_hello_extensions(Extensions) -> encode_extensions(hello_extensions_list(Extensions), <<>>). encode_extensions(Exts) -> @@ -695,6 +694,15 @@ encode_extensions([#sni{hostname = Hostname} | Rest], Acc) -> ?BYTE(?SNI_NAMETYPE_HOST_NAME), ?UINT16(HostLen), HostnameBin/binary, Acc/binary>>); +encode_extensions([#use_srtp{protection_profiles = Profiles, mki = MKI} | Rest], Acc) -> + ProfilesBin = iolist_to_binary(Profiles), + ProfilesLength = byte_size(ProfilesBin), + MKILength = byte_size(MKI), + ExtLength = ProfilesLength + 2 + MKILength + 1, + encode_extensions(Rest, <<?UINT16(?USE_SRTP_EXT), ?UINT16(ExtLength), + ?UINT16(ProfilesLength), ProfilesBin/binary, + ?BYTE(MKILength), MKI/binary, + Acc/binary>>); encode_extensions([#max_frag_enum{enum = MaxFragEnum} | Rest], Acc) -> ExtLength = 1, encode_extensions(Rest, <<?UINT16(?MAX_FRAGMENT_LENGTH_EXT), ?UINT16(ExtLength), ?BYTE(MaxFragEnum), @@ -823,14 +831,12 @@ encode_protocols_advertised_on_server(Protocols) -> extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}. encode_cert_auths(Auths) -> - encode_cert_auths(Auths, []). - -encode_cert_auths([], Acc) -> - list_to_binary(lists:reverse(Acc)); -encode_cert_auths([Auth | Auths], Acc) -> - DNEncodedBin = public_key:pkix_encode('Name', Auth, otp), - DNEncodedLen = byte_size(DNEncodedBin), - encode_cert_auths(Auths, [<<?UINT16(DNEncodedLen), DNEncodedBin/binary>> | Acc]). + DNEncode = fun (Auth) -> + DNEncodedBin = public_key:pkix_encode('Name', Auth, otp), + DNEncodedLen = byte_size(DNEncodedBin), + <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> + end, + list_to_binary(lists:map(DNEncode, Auths)). %%==================================================================== %% Decode handshake @@ -879,7 +885,7 @@ decode_handshake(_Version, ?CERTIFICATE_STATUS, <<?BYTE(?CERTIFICATE_STATUS_TYPE response = ASN1OcspResponse}; decode_handshake(_Version, ?SERVER_KEY_EXCHANGE, Keys) -> #server_key_exchange{exchange_keys = Keys}; -decode_handshake({3, 3} = Version, ?CERTIFICATE_REQUEST, +decode_handshake(?TLS_1_2 = Version, ?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary, ?UINT16(CertAuthsLen), EncCertAuths:CertAuthsLen/binary>>) -> @@ -894,9 +900,8 @@ decode_handshake(_Version, ?CERTIFICATE_REQUEST, certificate_authorities = decode_cert_auths(EncCertAuths, [])}; decode_handshake(_Version, ?SERVER_HELLO_DONE, <<>>) -> #server_hello_done{}; -decode_handshake({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), - Signature:SignLen/binary>>) - when Major == 3, Minor >= 3 -> +decode_handshake(?TLS_1_2, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), + Signature:SignLen/binary>>) -> #certificate_verify{hashsign_algorithm = dec_hashsign(HashSign), signature = Signature}; decode_handshake(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)-> #certificate_verify{signature = Signature}; @@ -1000,16 +1005,15 @@ available_suites(ServerCert, UserSuites, Version, undefined, Curve) -> filter_unavailable_ecc_suites(Curve, Suites); available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) -> Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve), - filter_hashsigns(Suites, [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- Suites], HashSigns, - Version, []). + filter_hashsigns(Suites, [ssl_cipher_format:suite_bin_to_map(Suite) || Suite <- Suites], HashSigns, Version). available_signature_algs(undefined, _) -> undefined; -available_signature_algs(SupportedHashSigns, Version) when Version >= {3, 3} -> +available_signature_algs(SupportedHashSigns, Version) when ?TLS_GTE(Version, ?TLS_1_2) -> case contains_scheme(SupportedHashSigns) of true -> case Version of - {3,3} -> + ?TLS_1_2 -> #hash_sign_algos{hash_sign_algos = ssl_cipher:signature_schemes_1_2(SupportedHashSigns)}; _ -> #signature_algorithms{signature_scheme_list = SupportedHashSigns} @@ -1021,12 +1025,12 @@ available_signature_algs(_, _) -> undefined. available_signature_algs(undefined, SupportedHashSigns, Version) when - Version >= {3,3} -> + ?TLS_GTE(Version, ?TLS_1_2) -> SupportedHashSigns; available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns0, - Version) when Version >= {3,3} -> + Version) when ?TLS_GTE(Version, ?TLS_1_2) -> SupportedHashSigns = - case (Version == {3,3}) andalso contains_scheme(SupportedHashSigns0) of + case (Version == ?TLS_1_2) andalso contains_scheme(SupportedHashSigns0) of true -> ssl_cipher:signature_schemes_1_2(SupportedHashSigns0); false -> @@ -1037,18 +1041,15 @@ available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, Su available_signature_algs(_, _, _) -> undefined. -contains_scheme([]) -> - false; -contains_scheme([Scheme | _]) when is_atom(Scheme) -> - true; -contains_scheme([_| Rest]) -> - contains_scheme(Rest). +contains_scheme(Schemes) -> + lists:any(fun erlang:is_atom/1, Schemes). cipher_suites(Suites, Renegotiation, true) -> %% TLS_FALLBACK_SCSV should be placed last -RFC7507 cipher_suites(Suites, Renegotiation) ++ [?TLS_FALLBACK_SCSV]; cipher_suites(Suites, Renegotiation, false) -> cipher_suites(Suites, Renegotiation). + cipher_suites(Suites, false) -> [?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites]; cipher_suites(Suites, true) -> @@ -1059,7 +1060,8 @@ cipher_suites(Suites, true) -> %% %% Description: use the TLS PRF to generate key material %%-------------------------------------------------------------------- -prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) -> +prf(Version, PRFAlgo, Secret, Label, Seed, WantedLength) + when ?TLS_1_X(Version)-> {ok, tls_v1:prf(PRFAlgo, Secret, Label, Seed, WantedLength)}. select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, SessIdTracker, Session0, Version, SslOpts, CertKeyAlts) -> @@ -1121,8 +1123,9 @@ server_select_cert_key_pair_and_params(CipherSuites, [#{private_key := Key, cert end end. -is_acceptable_cert(Cert, HashSigns, {Major, Minor}) when Major == 3, - Minor >= 3 -> +is_acceptable_cert(Cert, HashSigns, Version) + when ?TLS_1_X(Version), + ?TLS_GTE(Version, ?TLS_1_2) -> {SignAlgo0, Param, _, _, _} = get_cert_params(Cert), SignAlgo = sign_algo(SignAlgo0, Param), is_acceptable_hash_sign(SignAlgo, HashSigns); @@ -1130,12 +1133,6 @@ is_acceptable_cert(_,_,_) -> %% Not negotiable pre TLS-1.2. So if cert is available for version it is acceptable true. -supported_ecc({Major, Minor}) when ((Major == 3) and (Minor >= 1)) orelse (Major > 3) -> - Curves = tls_v1:ecc_curves(Minor), - #elliptic_curves{elliptic_curve_list = Curves}; -supported_ecc(_) -> - #elliptic_curves{elliptic_curve_list = []}. - premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) -> try public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params) @@ -1242,12 +1239,11 @@ client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renego add_tls12_extensions(_Version, #{alpn_advertised_protocols := AlpnAdvertisedProtocols, - next_protocol_selector := NextProtocolSelector, - server_name_indication := ServerNameIndication, - max_fragment_length := MaxFragmentLength} = SslOpts, + max_fragment_length := MaxFragmentLength} = SslOpts, ConnectionStates, Renegotiation) -> SRP = srp_user(SslOpts), + NextProtocolSelector = maps:get(next_protocol_selector, SslOpts, undefined), #{renegotiation_info => renegotiation_info(tls_record, client, ConnectionStates, Renegotiation), srp => SRP, @@ -1255,12 +1251,13 @@ add_tls12_extensions(_Version, next_protocol_negotiation => encode_client_protocol_negotiation(NextProtocolSelector, Renegotiation), - sni => sni(ServerNameIndication), + sni => sni(SslOpts), + use_srtp => use_srtp_ext(SslOpts), max_frag_enum => max_frag_enum(MaxFragmentLength) }. -add_common_extensions({3,4}, +add_common_extensions(?TLS_1_3, HelloExtensions, _CipherSuites, #{eccs := SupportedECCs, @@ -1297,10 +1294,9 @@ add_common_extensions(Version, signature_algs_cert => signature_algs_cert(SignatureCertSchemes)}. -maybe_add_tls13_extensions({3,4}, +maybe_add_tls13_extensions(?TLS_1_3, HelloExtensions0, - #{versions := SupportedVersions, - certificate_authorities := Bool}, + #{versions := SupportedVersions} = Opts, KeyShare, TicketData, CertDbHandle, CertDbRef) -> HelloExtensions1 = @@ -1308,18 +1304,15 @@ maybe_add_tls13_extensions({3,4}, #client_hello_versions{versions = SupportedVersions}}, HelloExtensions2 = maybe_add_key_share(HelloExtensions1, KeyShare), HelloExtensions = maybe_add_pre_shared_key(HelloExtensions2, TicketData), - maybe_add_certificate_auths(HelloExtensions, CertDbHandle, CertDbRef, Bool); + AddCA = maps:get(certificate_authorities, Opts, false), + maybe_add_certificate_auths(HelloExtensions, CertDbHandle, CertDbRef, AddCA); maybe_add_tls13_extensions(_, HelloExtensions, _, _, _, _,_) -> HelloExtensions. -maybe_add_certificate_status_request( - _Version, #{ocsp_stapling := false}, _OcspNonce, HelloExtensions) -> - HelloExtensions; -maybe_add_certificate_status_request( - _Version, #{ocsp_stapling := true, - ocsp_responder_certs := OcspResponderCerts}, - OcspNonce, HelloExtensions) -> +maybe_add_certificate_status_request(_Version, #{ocsp_stapling := OcspStapling}, + OcspNonce, HelloExtensions) -> + OcspResponderCerts = maps:get(ocsp_responder_certs, OcspStapling), OcspResponderList = get_ocsp_responder_list(OcspResponderCerts), OcspRequestExtns = public_key:ocsp_extensions(OcspNonce), Req = #ocsp_status_request{responder_id_list = OcspResponderList, @@ -1328,16 +1321,13 @@ maybe_add_certificate_status_request( status_type = ?CERTIFICATE_STATUS_TYPE_OCSP, request = Req }, - HelloExtensions#{status_request => CertStatusReqExtn}. + HelloExtensions#{status_request => CertStatusReqExtn}; +maybe_add_certificate_status_request(_Version, _SslOpts, _OcspNonce, + HelloExtensions) -> + HelloExtensions. get_ocsp_responder_list(ResponderCerts) -> - get_ocsp_responder_list(ResponderCerts, []). - -get_ocsp_responder_list([], Acc) -> - Acc; -get_ocsp_responder_list([ResponderCert | T], Acc) -> - get_ocsp_responder_list( - T, [public_key:ocsp_responder_id(ResponderCert) | Acc]). + lists:map(fun public_key:ocsp_responder_id/1, ResponderCerts). %% TODO: Add support for PSK key establishment @@ -1441,7 +1431,7 @@ add_alpn(Extensions, ALPN0) -> Extensions#{alpn => ALPN}. add_selected_version(Extensions) -> - SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + SupportedVersions = #server_hello_selected_version{selected_version = ?TLS_1_3}, Extensions#{server_hello_selected_version => SupportedVersions}. kse_remove_private_key(#key_share_entry{ @@ -1472,6 +1462,13 @@ signature_algs_cert(undefined) -> signature_algs_cert(SignatureSchemes) -> #signature_algorithms_cert{signature_scheme_list = SignatureSchemes}. + +use_srtp_ext(#{use_srtp := #{protection_profiles := Profiles, mki := MKI}}) -> + #use_srtp{protection_profiles = Profiles, mki = MKI}; +use_srtp_ext(#{}) -> + undefined. + + handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, Exts, Version, #{secure_renegotiate := SecureRenegotation, @@ -1498,6 +1495,7 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, ConnectionStates, Renegotiation), ec_point_formats => server_ecc_extension(Version, maps:get(ec_point_formats, Exts, undefined)), + use_srtp => use_srtp_ext(Opts), max_frag_enum => ServerMaxFragEnum }, @@ -1519,9 +1517,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, Exts, Version, - #{secure_renegotiate := SecureRenegotation, - next_protocol_selector := NextProtoSelector, - ocsp_stapling := Stapling}, + #{secure_renegotiate := SecureRenegotation} = + SslOpts, ConnectionStates0, Renegotiation, IsNew) -> ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, maps:get(renegotiation_info, Exts, undefined), Random, @@ -1544,7 +1541,7 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, ok end, - case handle_ocsp_extension(Stapling, Exts) of + case handle_ocsp_extension(SslOpts, Exts) of #alert{} = Alert -> Alert; OcspState -> @@ -1560,7 +1557,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, {ConnectionStates, alpn, undefined, OcspState}; undefined -> NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), - Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), + NextProtocolSelector = maps:get(next_protocol_selector, SslOpts, undefined), + Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtocolSelector, Renegotiation), {ConnectionStates, npn, Protocol, OcspState}; {error, Reason} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); @@ -1571,12 +1569,11 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, end end. -select_curve(Client, Server, Version) -> - select_curve(Client, Server, Version, false). +select_curve(Client, Server) -> + select_curve(Client, Server, false). select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves}, #elliptic_curves{elliptic_curve_list = ServerCurves}, - _, ServerOrder) -> case ServerOrder of false -> @@ -1584,25 +1581,25 @@ select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves}, true -> select_shared_curve(ServerCurves, ClientCurves) end; -select_curve(undefined, _, {_,Minor}, _) -> +select_curve(undefined, _, _) -> %% Client did not send ECC extension use default curve if %% ECC cipher is negotiated - case tls_v1:ecc_curves(Minor, [secp256r1]) of + case tls_v1:ecc_curves([secp256r1]) of [] -> %% Curve not supported by cryptolib ECC algorithms can not be negotiated no_curve; [CurveOid] -> {namedCurve, CurveOid} end; -select_curve({supported_groups, Groups}, Server,{_, Minor} = Version, HonorServerOrder) -> +select_curve({supported_groups, Groups}, Server, HonorServerOrder) -> %% TLS-1.3 hello but lesser version chosen TLSCommonCurves = [secp256r1,secp384r1,secp521r1], Curves = [tls_v1:enum_to_oid(tls_v1:group_to_enum(Name)) || Name <- Groups, lists:member(Name, TLSCommonCurves)], - case tls_v1:ecc_curves(Minor, Curves) of + case tls_v1:ecc_curves(Curves) of [] -> - select_curve(undefined, Server, Version, HonorServerOrder); + select_curve(undefined, Server, HonorServerOrder); [_|_] = ClientCurves -> - select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves}, Server, Version, HonorServerOrder) + select_curve(#elliptic_curves{elliptic_curve_list = ClientCurves}, Server, HonorServerOrder) end. %%-------------------------------------------------------------------- @@ -1623,12 +1620,12 @@ select_hashsign(_, _, KeyExAlgo, _, _Version) when KeyExAlgo == dh_anon; %% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have %% negotiated a lower version. select_hashsign({ClientHashSigns, ClientSignatureSchemes}, - Cert, KeyExAlgo, undefined, {3, 3} = Version) -> + Cert, KeyExAlgo, undefined, ?TLS_1_2 = Version) -> select_hashsign({ClientHashSigns, ClientSignatureSchemes}, Cert, KeyExAlgo, tls_v1:default_signature_algs([Version]), Version); select_hashsign({#hash_sign_algos{hash_sign_algos = ClientHashSigns}, ClientSignatureSchemes0}, - Cert, KeyExAlgo, SupportedHashSigns, {3, 3}) -> + Cert, KeyExAlgo, SupportedHashSigns, ?TLS_1_2) -> ClientSignatureSchemes = get_signature_scheme(ClientSignatureSchemes0), {SignAlgo0, Param, PublicKeyAlgo0, _, _} = get_cert_params(Cert), SignAlgo = sign_algo(SignAlgo0, Param), @@ -1685,7 +1682,7 @@ select_hashsign(#certificate_request{ certificate_types = Types}, Cert, SupportedHashSigns, - {3, 3}) -> + ?TLS_1_2) -> {SignAlgo0, Param, PublicKeyAlgo0, _, _} = get_cert_params(Cert), SignAlgo = {_, KeyType} = sign_algo(SignAlgo0, Param), PublicKeyAlgo = ssl_certificate:public_key_type(PublicKeyAlgo0), @@ -1877,9 +1874,9 @@ get_signature_scheme(#signature_algorithms_cert{ %% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}. %%-------------------------------------------------------------------- -select_hashsign_algs(HashSign, _, {3, 3}) when HashSign =/= undefined -> +select_hashsign_algs(HashSign, _, ?TLS_1_2) when HashSign =/= undefined -> HashSign; -select_hashsign_algs(undefined, ?rsaEncryption, {3,3}) -> +select_hashsign_algs(undefined, ?rsaEncryption, ?TLS_1_2) -> {sha, rsa}; select_hashsign_algs(undefined,?'id-ecPublicKey', _) -> {sha, ecdsa}; @@ -1897,6 +1894,8 @@ extension_value(undefined) -> undefined; extension_value(#sni{hostname = HostName}) -> HostName; +extension_value(#use_srtp{protection_profiles = ProtectionProfiles, mki = MKI}) -> + #{protection_profiles => ProtectionProfiles, mki => MKI}; extension_value(#ec_point_formats{ec_point_format_list = List}) -> List; extension_value(#elliptic_curves{elliptic_curve_list = List}) -> @@ -1936,21 +1935,21 @@ extension_value(#psk_key_exchange_modes{ke_modes = Modes}) -> extension_value(#cookie{cookie = Cookie}) -> Cookie. -handle_ocsp_extension(true = Stapling, Extensions) -> +handle_ocsp_extension(#{ocsp_stapling := _OcspStapling}, Extensions) -> case maps:get(status_request, Extensions, false) of undefined -> %% status_request in server hello is empty - #{ocsp_stapling => Stapling, + #{ocsp_stapling => true, ocsp_expect => staple}; false -> %% status_request is missing (not negotiated) - #{ocsp_stapling => Stapling, + #{ocsp_stapling => true, ocsp_expect => no_staple}; _Else -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, status_request_not_empty) end; -handle_ocsp_extension(false = Stapling, Extensions) -> +handle_ocsp_extension(_SslOpts, Extensions) -> case maps:get(status_request, Extensions, false) of false -> %% status_request is missing (not negotiated) - #{ocsp_stapling => Stapling, + #{ocsp_stapling => false, ocsp_expect => no_staple}; _Else -> %% unsolicited status_request ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_status_request) @@ -1973,7 +1972,7 @@ int_to_bin(I) -> %% The end-entity certificate provided by the client MUST contain a %% key that is compatible with certificate_types. -certificate_types(Version) when Version =< {3,3} -> +certificate_types(Version) when ?TLS_LTE(Version, ?TLS_1_2) -> ECDSA = supported_cert_type_or_empty(ecdsa, ?ECDSA_SIGN), RSA = supported_cert_type_or_empty(rsa, ?RSA_SIGN), DSS = supported_cert_type_or_empty(dss, ?DSS_SIGN), @@ -2103,8 +2102,7 @@ path_validation_alert({bad_cert, unknown_critical_extension}) -> path_validation_alert({bad_cert, {revoked, _}}) -> ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED); path_validation_alert({bad_cert, {revocation_status_undetermined, Details}}) -> - Alert = ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE), - Alert#alert{reason = Details}; + ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, Details); path_validation_alert({bad_cert, selfsigned_peer}) -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); path_validation_alert({bad_cert, unknown_ca}) -> @@ -2121,21 +2119,21 @@ digitally_signed(Version, Msg, HashAlgo, PrivateKey, SignAlgo) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, bad_key(PrivateKey))) end. -do_digitally_signed({3, Minor}, Msg, HashAlgo, {#'RSAPrivateKey'{} = Key, - #'RSASSA-PSS-params'{}}, SignAlgo) when Minor >= 3 -> +do_digitally_signed(Version, Msg, HashAlgo, {#'RSAPrivateKey'{} = Key, + #'RSASSA-PSS-params'{}}, SignAlgo) when ?TLS_GTE(Version, ?TLS_1_2) -> Options = signature_options(SignAlgo, HashAlgo), public_key:sign(Msg, HashAlgo, Key, Options); -do_digitally_signed({3, Minor}, {digest, Digest}, _HashAlgo, #'RSAPrivateKey'{} = Key, rsa) when Minor =< 2 -> +do_digitally_signed(Version, {digest, Digest}, _HashAlgo, #'RSAPrivateKey'{} = Key, rsa) when ?TLS_LTE(Version, ?TLS_1_1) -> public_key:encrypt_private(Digest, Key, [{rsa_pad, rsa_pkcs1_padding}]); -do_digitally_signed({3, Minor}, {digest, Digest}, _, - #{algorithm := rsa} = Engine, rsa) when Minor =< 2-> +do_digitally_signed(Version, {digest, Digest}, _, + #{algorithm := rsa} = Engine, rsa) when ?TLS_LTE(Version, ?TLS_1_1) -> crypto:private_encrypt(rsa, Digest, maps:remove(algorithm, Engine), rsa_pkcs1_padding); do_digitally_signed(_, Msg, HashAlgo, #{algorithm := Alg} = Engine, SignAlgo) -> Options = signature_options(SignAlgo, HashAlgo), crypto:sign(Alg, HashAlgo, Msg, maps:remove(algorithm, Engine), Options); -do_digitally_signed({3, Minor}, {digest, _} = Msg , HashAlgo, Key, _) when Minor =< 2 -> +do_digitally_signed(Version, {digest, _} = Msg , HashAlgo, Key, _) when ?TLS_LTE(Version,?TLS_1_1) -> public_key:sign(Msg, HashAlgo, Key); do_digitally_signed(_, Msg, HashAlgo, Key, SignAlgo) -> Options = signature_options(SignAlgo, HashAlgo), @@ -2172,21 +2170,25 @@ bad_key(#{algorithm := rsa}) -> bad_key(#{algorithm := ecdsa}) -> unacceptable_ecdsa_key. -cert_status_check(_, #{ocsp_state := #{ocsp_stapling := true, - ocsp_expect := stapled}}, _VerifyResult, _, _) -> - valid; %% OCSP staple will now be checked by ssl_certifcate:verify_cert_extensions/2 in ssl_certifcate:validate -cert_status_check(OtpCert, #{ocsp_state := #{ocsp_stapling := false}} = SslState, VerifyResult, CertPath, LogLevel) -> +cert_status_check(_, + #{ocsp_state := #{ocsp_stapling := true, + ocsp_expect := stapled}}, + _VerifyResult, _, _) -> + %% OCSP staple will now be checked by + %% ssl_certificate:verify_cert_extensions/2 in ssl_certificate:validate + valid; +cert_status_check(OtpCert, + #{ocsp_state := #{ocsp_stapling := false}} = SslState, + VerifyResult, CertPath, LogLevel) -> maybe_check_crl(OtpCert, SslState, VerifyResult, CertPath, LogLevel); -cert_status_check(_OtpCert, #{ocsp_state := #{ocsp_stapling := true, - ocsp_expect := undetermined}}, +cert_status_check(_OtpCert, + #{ocsp_state := #{ocsp_stapling := true, + ocsp_expect := undetermined}}, _VerifyResult, _CertPath, _LogLevel) -> {bad_cert, {revocation_status_undetermined, not_stapled}}; -cert_status_check(OtpCert, #{ocsp_state := #{ocsp_stapling := best_effort, %% TODO support this ? - ocsp_expect := undetermined}} = SslState, - VerifyResult, CertPath, LogLevel) -> - maybe_check_crl(OtpCert, SslState, VerifyResult, CertPath, LogLevel); -cert_status_check(_OtpCert, #{ocsp_state := #{ocsp_stapling := true, - ocsp_expect := no_staple}}, +cert_status_check(_OtpCert, + #{ocsp_state := #{ocsp_stapling := true, + ocsp_expect := no_staple}}, _VerifyResult, _CertPath, _LogLevel) -> {bad_cert, {revocation_status_undetermined, not_stapled}}. @@ -2242,12 +2244,12 @@ crl_check_same_issuer(OtpCert, _, Dps, Options) -> dps_and_crls(OtpCert, Callback, CRLDbHandle, ext, LogLevel) -> case public_key:pkix_dist_points(OtpCert) of - [] -> - no_dps; - DistPoints -> - Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer, - CRLs = distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle, LogLevel), - dps_and_crls(DistPoints, CRLs, []) + [] -> + no_dps; + DistPoints -> + Issuer = OtpCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.issuer, + CRLs = distpoints_lookup(DistPoints, Issuer, Callback, CRLDbHandle, LogLevel), + [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || DP <- DistPoints, CRL <- CRLs] end; dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer, LogLevel) -> @@ -2266,12 +2268,7 @@ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer, LogLevel) -> end, GenNames), [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs]. -dps_and_crls([], _, Acc) -> - Acc; -dps_and_crls([DP | Rest], CRLs, Acc) -> - DpCRL = [{DP, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs], - dps_and_crls(Rest, CRLs, DpCRL ++ Acc). - + distpoints_lookup([],_, _, _, _) -> []; distpoints_lookup([DistPoint | Rest], Issuer, Callback, CRLDbHandle, LogLevel) -> @@ -2305,10 +2302,12 @@ encrypted_premaster_secret(Secret, RSAPublicKey) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, premaster_encryption_failed)) end. -calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) -> - tls_v1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)). -calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) -> - tls_v1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)). +-spec calc_certificate_verify(ssl_record:ssl_version(), md5sha | ssl:hash(), _, [binary()]) -> binary(). +calc_certificate_verify(Version, HashAlgo, _MasterSecret, Handshake) when ?TLS_1_X(Version) -> + tls_v1:certificate_verify(HashAlgo, lists:reverse(Handshake)). + +calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) when ?TLS_1_X(Version) -> + tls_v1:finished(Role, Version, PrfAlgo, MasterSecret, lists:reverse(Handshake)). master_secret(Version, MasterSecret, #security_parameters{ @@ -2336,11 +2335,12 @@ master_secret(Version, MasterSecret, {MasterSecret, ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState, ServerCipherState, Role)}. -setup_keys({3,N}, PrfAlgo, MasterSecret, - ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) -> - tls_v1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, +setup_keys(Version, PrfAlgo, MasterSecret, + ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) when ?TLS_1_X(Version)-> + tls_v1:setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, IVS). -calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> +calc_master_secret(Version, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) + when ?TLS_LT(Version, ?TLS_1_3) -> tls_v1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom). %% Update pending connection states with parameters exchanged via @@ -2464,50 +2464,48 @@ encode_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B} <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary, ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>. -encode_client_key(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) -> - PKEPMS; -encode_client_key(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) -> +encode_client_key(#encrypted_premaster_secret{premaster_secret = PKEPMS}) -> PKEPMSLen = byte_size(PKEPMS), <<?UINT16(PKEPMSLen), PKEPMS/binary>>; -encode_client_key(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> +encode_client_key(#client_diffie_hellman_public{dh_public = DHPublic}) -> Len = byte_size(DHPublic), <<?UINT16(Len), DHPublic/binary>>; -encode_client_key(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) -> +encode_client_key(#client_ec_diffie_hellman_public{dh_public = DHPublic}) -> Len = byte_size(DHPublic), <<?BYTE(Len), DHPublic/binary>>; -encode_client_key(#client_psk_identity{identity = undefined}, _) -> +encode_client_key(#client_psk_identity{identity = undefined}) -> Id = <<"psk_identity">>, Len = byte_size(Id), <<?UINT16(Len), Id/binary>>; -encode_client_key(#client_psk_identity{identity = Id}, _) -> +encode_client_key(#client_psk_identity{identity = Id}) -> Len = byte_size(Id), <<?UINT16(Len), Id/binary>>; -encode_client_key(Identity = #client_dhe_psk_identity{identity = undefined}, Version) -> - encode_client_key(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version); -encode_client_key(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) -> +encode_client_key(Identity = #client_dhe_psk_identity{identity = undefined}) -> + encode_client_key(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}); +encode_client_key(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}) -> Len = byte_size(Id), DHLen = byte_size(DHPublic), <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>; -encode_client_key(Identity = #client_ecdhe_psk_identity{identity = undefined}, Version) -> - encode_client_key(Identity#client_ecdhe_psk_identity{identity = <<"psk_identity">>}, Version); -encode_client_key(#client_ecdhe_psk_identity{identity = Id, dh_public = DHPublic}, _) -> +encode_client_key(Identity = #client_ecdhe_psk_identity{identity = undefined}) -> + encode_client_key(Identity#client_ecdhe_psk_identity{identity = <<"psk_identity">>}); +encode_client_key(#client_ecdhe_psk_identity{identity = Id, dh_public = DHPublic}) -> Len = byte_size(Id), DHLen = byte_size(DHPublic), <<?UINT16(Len), Id/binary, ?BYTE(DHLen), DHPublic/binary>>; -encode_client_key(Identity = #client_rsa_psk_identity{identity = undefined}, Version) -> - encode_client_key(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version); -encode_client_key(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) -> - EncPMS = encode_client_key(ExchangeKeys, Version), +encode_client_key(Identity = #client_rsa_psk_identity{identity = undefined}) -> + encode_client_key(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}); +encode_client_key(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}) -> + EncPMS = encode_client_key(ExchangeKeys), Len = byte_size(Id), <<?UINT16(Len), Id/binary, EncPMS/binary>>; -encode_client_key(#client_srp_public{srp_a = A}, _) -> +encode_client_key(#client_srp_public{srp_a = A}) -> Len = byte_size(A), <<?UINT16(Len), A/binary>>. enc_sign({_, anon}, _Sign, _Version) -> <<>>; -enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor}) - when Major == 3, Minor >= 3-> +enc_sign({HashAlg, SignAlg}, Signature, Version) + when ?TLS_GTE(Version, ?TLS_1_2)-> SignLen = byte_size(Signature), HashSign = enc_hashsign(HashAlg, SignAlg), <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>; @@ -2561,61 +2559,32 @@ encode_alpn(Protocols, _) -> encode_versions(Versions) -> - encode_versions(lists:reverse(Versions), <<>>). -%% -encode_versions([], Acc) -> - Acc; -encode_versions([{M,N}|T], Acc) -> - encode_versions(T, <<?BYTE(M),?BYTE(N),Acc/binary>>). + << <<?BYTE(M),?BYTE(N)>> || {M,N} <- Versions>>. encode_client_shares(ClientShares) -> - encode_client_shares(ClientShares, <<>>). -%% -encode_client_shares([], Acc) -> - Acc; -encode_client_shares([KeyShareEntry0|T], Acc) -> - KeyShareEntry = encode_key_share_entry(KeyShareEntry0), - encode_client_shares(T, <<Acc/binary,KeyShareEntry/binary>>). + << << (encode_key_share_entry(KeyShareEntry0))/binary >> || KeyShareEntry0 <- ClientShares >>. -encode_key_share_entry(#key_share_entry{ - group = Group, - key_exchange = KeyExchange}) -> +encode_key_share_entry(#key_share_entry{group = Group, + key_exchange = KeyExchange}) -> 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>>). - + << <<?BYTE((choose_psk_key(PskKey)))>> || PskKey <- KEModes>>. +% +choose_psk_key(psk_ke) -> ?PSK_KE; +choose_psk_key(psk_dhe_ke) -> ?PSK_DHE_KE. 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,?UINT32(Age)>>). - + Result = << << ?UINT16((byte_size(Identity))), Identity/binary,?UINT32(Age) >> + || #psk_identity{ identity = Identity, obfuscated_ticket_age = Age} <- Identities >>, + Len = byte_size(Result), + <<?UINT16(Len), Result/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>>). + Result = << << ?BYTE((byte_size(Binder))),Binder/binary >> || Binder <- Binders >>, + Len = byte_size(Result), + <<?UINT16(Len), Result/binary>>. hello_extensions_list(HelloExtensions) -> @@ -2699,8 +2668,6 @@ dec_server_key(<<?UINT16(NLen), N:NLen/binary, dec_server_key(_, KeyExchange, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {unknown_or_malformed_key_exchange, KeyExchange})). -dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> - #encrypted_premaster_secret{premaster_secret = PKEPMS}; dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) -> #encrypted_premaster_secret{premaster_secret = PKEPMS}; dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> @@ -2724,10 +2691,6 @@ dec_client_key(<<?UINT16(Len), Id:Len/binary, ?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN_PSK, _) -> #client_ecdhe_psk_identity{identity = Id, dh_public = DH_Y}; -dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>, - ?KEY_EXCHANGE_RSA_PSK, {3, 0}) -> - #client_rsa_psk_identity{identity = Id, - exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}}; dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA_PSK, _) -> #client_rsa_psk_identity{identity = Id, @@ -2741,27 +2704,27 @@ dec_server_key_params(Len, Keys, Version) -> dec_server_key_signature(Params, Signature, Version). dec_server_key_signature(Params, <<?BYTE(8), ?BYTE(SignAlgo), - ?UINT16(0)>>, {Major, Minor}) - when Major == 3, Minor >= 3 -> + ?UINT16(0)>>, Version) + when ?TLS_GTE(Version, ?TLS_1_2) -> <<?UINT16(Scheme0)>> = <<?BYTE(8), ?BYTE(SignAlgo)>>, Scheme = ssl_cipher:signature_scheme(Scheme0), {Hash, Sign, _} = ssl_cipher:scheme_to_components(Scheme), {Params, {Hash, Sign}, <<>>}; dec_server_key_signature(Params, <<?BYTE(8), ?BYTE(SignAlgo), - ?UINT16(Len), Signature:Len/binary>>, {Major, Minor}) - when Major == 3, Minor >= 3 -> + ?UINT16(Len), Signature:Len/binary>>, Version) + when ?TLS_GTE(Version, ?TLS_1_2) -> <<?UINT16(Scheme0)>> = <<?BYTE(8), ?BYTE(SignAlgo)>>, Scheme = ssl_cipher:signature_scheme(Scheme0), {Hash, Sign, _} = ssl_cipher:scheme_to_components(Scheme), {Params, {Hash, Sign}, Signature}; dec_server_key_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), - ?UINT16(0)>>, {Major, Minor}) - when Major == 3, Minor >= 3 -> + ?UINT16(0)>>, Version) + when ?TLS_GTE(Version, ?TLS_1_2) -> HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, {Params, HashSign, <<>>}; dec_server_key_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), - ?UINT16(Len), Signature:Len/binary>>, {Major, Minor}) - when Major == 3, Minor >= 3 -> + ?UINT16(Len), Signature:Len/binary>>, Version) + when ?TLS_GTE(Version, ?TLS_1_2) -> HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, {Params, HashSign, Signature}; dec_server_key_signature(Params, <<>>, _) -> @@ -2847,7 +2810,7 @@ decode_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version < {3,3} -> + when ?TLS_LT(Version, ?TLS_1_2) -> SignAlgoListLen = Len - 2, <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData, HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || @@ -2857,8 +2820,7 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), #hash_sign_algos{hash_sign_algos = HashSignAlgos}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version =:= {3,3} -> + ExtData:Len/binary, Rest/binary>>, ?TLS_1_2=Version, MessageType, Acc) -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, HashSigns = decode_sign_alg(Version, SignSchemeList), @@ -2867,8 +2829,7 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), #hash_sign_algos{ hash_sign_algos = HashSigns}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version =:= {3,4} -> + ExtData:Len/binary, Rest/binary>>, ?TLS_1_3=Version, MessageType, Acc) -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, SignSchemes = decode_sign_alg(Version, SignSchemeList), @@ -2878,8 +2839,7 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), signature_scheme_list = SignSchemes}}); decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version =:= {3,4} -> + ExtData:Len/binary, Rest/binary>>, ?TLS_1_3=Version, MessageType, Acc) -> SignSchemeListLen = Len - 2, <<?UINT16(SignSchemeListLen), SignSchemeList/binary>> = ExtData, %% Ignore unknown signature algorithms @@ -2918,9 +2878,19 @@ decode_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), ?UINT16(Len), #signature_algorithms_cert{ signature_scheme_list = SignSchemes}}); +decode_extensions(<<?UINT16(?USE_SRTP_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> + <<?UINT16(ProfilesLen), ProfilesBin:ProfilesLen/binary, ?BYTE(MKILen), MKI:MKILen/binary>> = ExtData, + Profiles = [P || <<P:2/binary>> <= ProfilesBin], + decode_extensions(Rest, Version, MessageType, + Acc#{use_srtp => + #use_srtp{ + protection_profiles = Profiles, + mki = MKI}}); + decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version < {3,4} -> + when ?TLS_LT(Version, ?TLS_1_3) -> <<?UINT16(_), EllipticCurveList/binary>> = ExtData, %% Ignore unknown curves Pick = fun(Enum) -> @@ -2938,8 +2908,7 @@ decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), EllipticCurves}}); decode_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), - ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) - when Version =:= {3,4} -> + ExtData:Len/binary, Rest/binary>>, ?TLS_1_3=Version, MessageType, Acc) -> <<?UINT16(_), GroupList/binary>> = ExtData, %% Ignore unknown curves Pick = fun(Enum) -> @@ -2993,8 +2962,7 @@ decode_extensions(<<?UINT16(?SUPPORTED_VERSIONS_EXT), ?UINT16(Len), when Len =:= 2, SelectedVersion =:= 16#0304 -> decode_extensions(Rest, Version, MessageType, Acc#{server_hello_selected_version => - #server_hello_selected_version{selected_version = - {3,4}}}); + #server_hello_selected_version{selected_version = ?TLS_1_3}}); decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len), ExtData:Len/binary, Rest/binary>>, @@ -3113,7 +3081,7 @@ decode_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>> decode_extensions(_, _, _, Acc) -> Acc. -decode_sign_alg({3,3}, SignSchemeList) -> +decode_sign_alg(?TLS_1_2, SignSchemeList) -> %% Ignore unknown signature algorithms Fun = fun(Elem) -> case ssl_cipher:signature_scheme(Elem) of @@ -3138,7 +3106,7 @@ decode_sign_alg({3,3}, SignSchemeList) -> end, lists:filtermap(Fun, [SignScheme || <<?UINT16(SignScheme)>> <= SignSchemeList]); -decode_sign_alg({3,4}, SignSchemeList) -> +decode_sign_alg(?TLS_1_3, SignSchemeList) -> %% Ignore unknown signature algorithms Fun = fun(Elem) -> case ssl_cipher:signature_scheme(Elem) of @@ -3152,7 +3120,7 @@ decode_sign_alg({3,4}, SignSchemeList) -> <<?UINT16(SignScheme)>> <= SignSchemeList]). dec_hashsign(Value) -> - [HashSign] = decode_sign_alg({3,3}, Value), + [HashSign] = decode_sign_alg(?TLS_1_2, Value), HashSign. @@ -3308,14 +3276,11 @@ select_cipher_suite(CipherSuites, Suites, false) -> select_cipher_suite(CipherSuites, Suites, true) -> select_cipher_suite(Suites, CipherSuites). -select_cipher_suite([], _) -> - no_suite; -select_cipher_suite([Suite | ClientSuites], SupportedSuites) -> - case is_member(Suite, SupportedSuites) of - true -> - Suite; - false -> - select_cipher_suite(ClientSuites, SupportedSuites) +select_cipher_suite(ClientSuites, SupportedSuites) -> + F = fun(Suite) -> is_member(Suite, SupportedSuites) end, + case lists:search(F, ClientSuites) of + {value, Suite} -> Suite; + false -> no_suite end. is_member(Suite, SupportedSuites) -> @@ -3350,25 +3315,41 @@ handle_psk_identity(_PSKIdentity, LookupFun) handle_psk_identity(PSKIdentity, {Fun, UserState}) -> Fun(psk, PSKIdentity, UserState). -filter_hashsigns([], [], _, _, Acc) -> - lists:reverse(Acc); -filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Version, - Acc) when KeyExchange == dhe_ecdsa; - KeyExchange == ecdhe_ecdsa -> - do_filter_hashsigns(ecdsa, Suite, Suites, Algos, HashSigns, Version, Acc); -filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Version, - Acc) when KeyExchange == rsa; + +filter_hashsigns(Suites, Algos, HashSigns, Version) -> + %% HashSigns, and Version never change + ZipperF = fun (Suite, #{key_exchange := KeyExchange}) -> {Suite, KeyExchange} end, + SuiteAlgoPairs = lists:zipwith(ZipperF, Suites, Algos), + FilterHashSign = fun ({Suite, Kex}) -> + maybe true ?= filter_hashsigns_helper(Kex, HashSigns, Version), + {true, Suite} + end + end, + lists:filtermap(FilterHashSign, SuiteAlgoPairs). + +filter_hashsigns_helper(KeyExchange, HashSigns, _Version) + when KeyExchange == dhe_ecdsa; + KeyExchange == ecdhe_ecdsa -> + lists:keymember(ecdsa, 2, HashSigns); +filter_hashsigns_helper(KeyExchange, HashSigns, ?TLS_1_2) when KeyExchange == rsa; + KeyExchange == dhe_rsa; + KeyExchange == ecdhe_rsa; + KeyExchange == srp_rsa; + KeyExchange == rsa_psk -> + lists:any(fun (H) -> lists:keymember(H, 2, HashSigns) end, + [rsa, rsa_pss_rsae, rsa_pss_pss]); +filter_hashsigns_helper(KeyExchange, HashSigns, _Version) when KeyExchange == rsa; KeyExchange == dhe_rsa; KeyExchange == ecdhe_rsa; KeyExchange == srp_rsa; KeyExchange == rsa_psk -> - do_filter_hashsigns(rsa, Suite, Suites, Algos, HashSigns, Version, Acc); -filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Version, Acc) when + lists:keymember(rsa, 2, HashSigns); +filter_hashsigns_helper(KeyExchange, HashSigns, _Version) when KeyExchange == dhe_dss; - KeyExchange == srp_dss -> - do_filter_hashsigns(dsa, Suite, Suites, Algos, HashSigns, Version, Acc); -filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Version, - Acc) when + KeyExchange == srp_dss -> + lists:keymember(dsa, 2, HashSigns); + +filter_hashsigns_helper(KeyExchange, _HashSigns, _Version) when KeyExchange == dh_dss; KeyExchange == dh_rsa; KeyExchange == dh_ecdsa; @@ -3377,9 +3358,8 @@ filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], Has %% Fixed DH certificates MAY be signed with any hash/signature %% algorithm pair appearing in the hash_sign extension. The names %% DH_DSS, DH_RSA, ECDH_ECDSA, and ECDH_RSA are historical. - filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]); -filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], HashSigns, Version, - Acc) when + true; +filter_hashsigns_helper(KeyExchange, _HashSigns, _Version) when KeyExchange == dh_anon; KeyExchange == ecdh_anon; KeyExchange == srp_anon; @@ -3387,24 +3367,7 @@ filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], Has KeyExchange == dhe_psk; KeyExchange == ecdhe_psk -> %% In this case hashsigns is not used as the kexchange is anonaymous - filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]). - -do_filter_hashsigns(rsa = SignAlgo, Suite, Suites, Algos, HashSigns, {3,3} = Version, Acc) -> - case (lists:keymember(SignAlgo, 2, HashSigns) orelse - lists:keymember(rsa_pss_rsae, 2, HashSigns) orelse - lists:keymember(rsa_pss_pss, 2, HashSigns)) of - true -> - filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]); - false -> - filter_hashsigns(Suites, Algos, HashSigns, Version, Acc) - end; -do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Version, Acc) -> - case lists:keymember(SignAlgo, 2, HashSigns) of - true -> - filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]); - false -> - filter_hashsigns(Suites, Algos, HashSigns, Version, Acc) - end. + true. filter_unavailable_ecc_suites(no_curve, Suites) -> ECCSuites = ssl_cipher:filter_suites(Suites, #{key_exchange_filters => [fun(ecdh_ecdsa) -> true; @@ -3476,10 +3439,9 @@ handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, SslOpts)- handle_next_protocol_on_server(undefined, _Renegotiation, _SslOpts) -> undefined; - handle_next_protocol_on_server(#next_protocol_negotiation{extension_data = <<>>}, - false, #{next_protocols_advertised := Protocols}) -> - Protocols; + false, SslOpts) -> + maps:get(next_protocols_advertised, SslOpts, undefined); handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension). @@ -3564,10 +3526,13 @@ sign_type(ecdsa) -> server_name(_, _, server) -> undefined; %% Not interesting to check your own name. -server_name(undefined, Host, client) -> - {fallback, Host}; %% Fallback to Host argument to connect -server_name(SNI, _, client) -> - SNI. %% If Server Name Indication is available +server_name(SSLOpts, Host, client) -> + case maps:get(server_name_indication, SSLOpts, undefined) of + undefined -> + {fallback, Host}; %% Fallback to Host argument to connect + SNI -> + SNI %% If Server Name Indication is available + end. client_ecc_extensions(SupportedECCs) -> CryptoSupport = proplists:get_value(public_keys, crypto:supports()), @@ -3599,39 +3564,25 @@ handle_ecc_point_fmt_extension(undefined) -> handle_ecc_point_fmt_extension(_) -> #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}. -advertises_ec_ciphers([]) -> - false; -advertises_ec_ciphers([#{key_exchange := ecdh_ecdsa} | _]) -> - true; -advertises_ec_ciphers([#{key_exchange := ecdhe_ecdsa} | _]) -> - true; -advertises_ec_ciphers([#{key_exchange := ecdh_rsa} | _]) -> - true; -advertises_ec_ciphers([#{key_exchange := ecdhe_rsa} | _]) -> - true; -advertises_ec_ciphers([#{key_exchange := ecdh_anon} | _]) -> - true; -advertises_ec_ciphers([{ecdhe_psk, _,_,_} | _]) -> - true; -advertises_ec_ciphers([_| Rest]) -> - advertises_ec_ciphers(Rest). +advertises_ec_ciphers(ListKex) -> + KeyExchanges = [ecdh_ecdsa, ecdhe_ecdsa, ecdh_rsa, ecdhe_rsa, ecdh_anon], + F = fun (#{key_exchange := Kex}) -> lists:member(Kex, KeyExchanges); + ({ecdhe_psk, _,_,_}) -> true + end, + lists:any(F, ListKex). -select_shared_curve([], _) -> - no_curve; -select_shared_curve([Curve | Rest], Curves) -> - case lists:member(Curve, Curves) of - true -> - {namedCurve, Curve}; - false -> - select_shared_curve(Rest, Curves) +select_shared_curve(SharedCurves, Curves) -> + case lists:search(fun (Curve) -> lists:member(Curve, Curves) end, SharedCurves) of + {value, SharedCurve} -> {namedCurve, SharedCurve}; + false -> no_curve end. -sni(undefined) -> - undefined; -sni(disable) -> - undefined; -sni(Hostname) -> - #sni{hostname = Hostname}. +sni(SslOpts) -> + case maps:get(server_name_indication, SslOpts, undefined) of + undefined -> undefined; + disable -> undefined; + Hostname -> #sni{hostname = Hostname} + end. %% convert max_fragment_length (in bytes) to the RFC 6066 ENUM max_frag_enum(?MAX_FRAGMENT_LENGTH_BYTES_1) -> @@ -3759,14 +3710,14 @@ cert_curve(Cert, ECCCurve0, CipherSuite) -> empty_extensions() -> #{}. -empty_extensions({3,4}, client_hello) -> +empty_extensions(?TLS_1_3, client_hello) -> #{ sni => undefined, %% max_frag_enum => undefined, %% status_request => undefined, elliptic_curves => undefined, signature_algs => undefined, - %% use_srtp => undefined, + use_srtp => undefined, %% heartbeat => undefined, alpn => undefined, %% signed_cert_timestamp => undefined, @@ -3783,8 +3734,8 @@ empty_extensions({3,4}, client_hello) -> %% post_handshake_auth => undefined, signature_algs_cert => undefined }; -empty_extensions({3, 3}, client_hello) -> - Ext = empty_extensions({3,2}, client_hello), +empty_extensions(?TLS_1_2, client_hello) -> + Ext = empty_extensions(?TLS_1_1, client_hello), Ext#{signature_algs => undefined}; empty_extensions(_, client_hello) -> #{renegotiation_info => undefined, @@ -3794,12 +3745,12 @@ empty_extensions(_, client_hello) -> ec_point_formats => undefined, elliptic_curves => undefined, sni => undefined}; -empty_extensions({3,4}, server_hello) -> +empty_extensions(?TLS_1_3, server_hello) -> #{server_hello_selected_version => undefined, key_share => undefined, pre_shared_key => undefined }; -empty_extensions({3,4}, hello_retry_request) -> +empty_extensions(?TLS_1_3, hello_retry_request) -> #{server_hello_selected_version => undefined, key_share => undefined, pre_shared_key => undefined, %% TODO remove! @@ -3857,8 +3808,7 @@ path_validation(TrustedCert, Path, ServerName, Role, CertDbHandle, CertDbRef, CR #{verify_fun := VerifyFun, customize_hostname_check := CustomizeHostnameCheck, crl_check := CrlCheck, - log_level := Level, - depth := Depth} = Opts, + log_level := Level} = Opts, #{cert_ext := CertExt, ocsp_responder_certs := OcspResponderCerts, ocsp_state := OcspState}) -> @@ -3881,7 +3831,7 @@ path_validation(TrustedCert, Path, ServerName, Role, CertDbHandle, CertDbRef, CR ocsp_responder_certs => OcspResponderCerts, ocsp_state => OcspState}, Path, Level), - Options = [{max_path_length, Depth}, + Options = [{max_path_length, maps:get(depth, Opts, ?DEFAULT_DEPTH)}, {verify_fun, ValidationFunAndState}], public_key:pkix_path_validation(TrustedCert, Path, Options). @@ -3890,7 +3840,20 @@ error_to_propagate({error, {bad_cert, root_cert_expired}} = Error, _) -> error_to_propagate(_, Error) -> Error. -path_validation_cb({3,4}) -> +path_validation_cb(?TLS_1_3) -> tls_handshake_1_3; path_validation_cb(_) -> ?MODULE. + +%%%################################################################ +%%%# +%%%# Tracing +%%%# +handle_trace(csp, + {call, {?MODULE, maybe_add_certificate_status_request, + [_Version, SslOpts, + _OcspNonce, _HelloExtensions]}}, + Stack) -> + OcspStapling = maps:get(ocsp_stapling, SslOpts, false), + {io_lib:format("#1 ADD crt status request / OcspStapling option = ~W", + [OcspStapling, 10]), Stack}. |