diff options
Diffstat (limited to 'lib/ssl/src/tls_handshake_1_3.erl')
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 109 |
1 files changed, 72 insertions, 37 deletions
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index a0ae51ed0a..c29366e717 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -39,7 +39,7 @@ %% Create handshake messages -export([certificate/5, certificate_verify/4, - encrypted_extensions/0]). + encrypted_extensions/1]). -export([do_start/2, do_negotiated/2, @@ -61,10 +61,10 @@ %% Create handshake messages %%==================================================================== -server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) -> +server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - Extensions = server_hello_extensions(MsgType, KeyShare, ALPN), + Extensions = server_hello_extensions(MsgType, KeyShare), #server_hello{server_version = {3,3}, %% legacy_version cipher_suite = SecParams#security_parameters.cipher_suite, compression_method = 0, %% legacy attribute @@ -73,6 +73,7 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) -> extensions = Extensions }. + %% The server's extensions MUST contain "supported_versions". %% Additionally, it SHOULD contain the minimal set of extensions %% necessary for the client to generate a correct ClientHello pair. As @@ -80,18 +81,14 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) -> %% extensions that were not first offered by the client in its %% ClientHello, with the exception of optionally the "cookie" (see %% Section 4.2.2) extension. -server_hello_extensions(hello_retry_request = MsgType, KeyShare, _) -> +server_hello_extensions(hello_retry_request = MsgType, KeyShare) -> SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, Extensions = #{server_hello_selected_version => SupportedVersions}, ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); -server_hello_extensions(MsgType, KeyShare, undefined) -> +server_hello_extensions(MsgType, KeyShare) -> SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, Extensions = #{server_hello_selected_version => SupportedVersions}, - ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); -server_hello_extensions(MsgType, KeyShare, ALPN0) -> - Extensions0 = ssl_handshake:add_selected_version(#{}), %% {3,4} (TLS 1.3) - Extensions1 = ssl_handshake:add_alpn(Extensions0, ALPN0), - ssl_handshake:add_server_share(MsgType, Extensions1, KeyShare). + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare). server_hello_random(server_hello, #security_parameters{server_random = Random}) -> @@ -107,10 +104,14 @@ server_hello_random(hello_retry_request, _) -> ?HELLO_RETRY_REQUEST_RANDOM. -%% TODO: implement support for encrypted_extensions -encrypted_extensions() -> +encrypted_extensions(#state{handshake_env = #handshake_env{alpn = undefined}}) -> #encrypted_extensions{ extensions = #{} + }; +encrypted_extensions(#state{handshake_env = #handshake_env{alpn = ALPNProtocol}}) -> + Extensions = ssl_handshake:add_alpn(#{}, ALPNProtocol), + #encrypted_extensions{ + extensions = Extensions }. @@ -433,6 +434,15 @@ certificate_entry(DER) -> %% 79 %% 00 %% 0101010101010101010101010101010101010101010101010101010101010101 +sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) -> + Content = build_content(Context, THash), + try public_key:sign(Content, HashAlgo, PrivateKey) of + Signature -> + {ok, Signature} + catch + error:badarg -> + {error, badarg} + end; sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), @@ -450,7 +460,16 @@ sign(THash, Context, HashAlgo, PrivateKey) -> end. -verify(THash, Context, HashAlgo, Signature, PublicKey) -> +verify(THash, Context, HashAlgo, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> + Content = build_content(Context, THash), + try public_key:verify(Content, HashAlgo, Signature, {PublicKey, PublicKeyParams}) of + Result -> + {ok, Result} + catch + error:badarg -> + {error, badarg} + end; +verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyParams}) -> Content = build_content(Context, THash), %% The length of the Salt MUST be equal to the length of the output @@ -485,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), @@ -512,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)), @@ -655,8 +675,7 @@ do_negotiated(start_handshake, dh_public_value = ClientPublicKey}, ssl_options = #ssl_options{} = SslOpts, key_share = KeyShare, - handshake_env = #handshake_env{tls_handshake_history = _HHistory0, - alpn = ALPN}, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, connection_env = #connection_env{private_key = CertPrivateKey}, static_env = #static_env{ cert_db = CertDbHandle, @@ -671,7 +690,7 @@ do_negotiated(start_handshake, try %% Create server_hello %% Extensions: supported_versions, key_share, (pre_shared_key) - ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0, ALPN), + ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0), {State1, _} = tls_connection:send_handshake(ServerHello, State0), @@ -681,7 +700,7 @@ do_negotiated(start_handshake, State3 = ssl_record:step_encryption_state(State2), %% Create EncryptedExtensions - EncryptedExtensions = encrypted_extensions(), + EncryptedExtensions = encrypted_extensions(State2), %% Encode EncryptedExtensions State4 = tls_connection:queue_handshake(EncryptedExtensions, State3), @@ -863,12 +882,19 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, end. -do_wait_ee(#encrypted_extensions{extensions = _Extensions}, State0) -> +do_wait_ee(#encrypted_extensions{extensions = Extensions}, State0) -> + + ALPNProtocol0 = maps:get(alpn, Extensions, undefined), + ALPNProtocol = get_alpn(ALPNProtocol0), {Ref,_Maybe} = maybe(), try - {State0, wait_cert_cr} + %% Update state + #state{handshake_env = HsEnv} = State0, + State1 = State0#state{handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}}, + + {State1, wait_cert_cr} catch {Ref, {insufficient_security, no_suitable_groups}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); @@ -1023,10 +1049,9 @@ compare_verify_data(_, _) -> {error, decrypt_error}. -send_hello_retry_request(#state{connection_states = ConnectionStates0, - handshake_env = #handshake_env{alpn = ALPN}} = State0, +send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, no_suitable_key, KeyShare, SessionId) -> - ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0, ALPN), + ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0), {State1, _} = tls_connection:send_handshake(ServerHello, State0), %% Update handshake history @@ -1323,11 +1348,6 @@ get_private_key(#key_share_entry{ {_, PrivateKey}}) -> PrivateKey. -%% TODO: implement EC keys -get_public_key({?'rsaEncryption', PublicKey, _}) -> - PublicKey. - - %% X25519, X448 calculate_shared_secret(OthersKey, MyKey, Group) when is_binary(OthersKey) andalso is_binary(MyKey) andalso @@ -1556,13 +1576,11 @@ verify_certificate_verify(#state{ %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. THash = tls_v1:transcript_hash(Context, HKDFAlgo), - PublicKey = get_public_key(PublicKeyInfo), - ContextString = peer_context_string(Role), %% Digital signatures use the hash function defined by the selected signature %% scheme. - case verify(THash, ContextString, HashAlgo, Signature, PublicKey) of + case verify(THash, ContextString, HashAlgo, Signature, PublicKeyInfo) of {ok, true} -> {ok, {State0, wait_finished}}; {ok, false} -> @@ -1714,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. @@ -1761,15 +1783,20 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> {error, {insufficient_security, no_suitable_public_key}}; -%% TODO: Implement support for ECDSA keys! select_sign_algo(_, [], _) -> {error, {insufficient_security, no_suitable_signature_algorithm}}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed %% TLS handshake messages: filter sha-1 and rsa_pkcs1. + %% + %% RSASSA-PSS RSAE algorithms: If the public key is carried in an X.509 + %% certificate, it MUST use the rsaEncryption OID. + %% RSASSA-PSS PSS algorithms: If the public key is carried in an X.509 certificate, + %% it MUST use the RSASSA-PSS OID. case ((PublicKeyAlgo =:= rsa andalso S =:= rsa_pss_rsae) - orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_rsae)) + orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_pss) + orelse (PublicKeyAlgo =:= ecdsa andalso S =:= ecdsa)) andalso lists:member(C, ServerSignAlgs) of true -> @@ -1853,6 +1880,14 @@ get_key_shares(#key_share_server_hello{server_share = ServerShare}) -> get_selected_group(#key_share_hello_retry_request{selected_group = SelectedGroup}) -> SelectedGroup. +get_alpn(ALPNProtocol0) -> + case ssl_handshake:decode_alpn(ALPNProtocol0) of + undefined -> + undefined; + [ALPNProtocol] -> + ALPNProtocol + end. + maybe() -> Ref = erlang:make_ref(), Ok = fun(ok) -> ok; |