diff options
author | Péter Dimitrov <[email protected]> | 2019-05-09 16:32:35 +0200 |
---|---|---|
committer | Péter Dimitrov <[email protected]> | 2019-05-21 13:22:36 +0200 |
commit | 7240ecfc3ac931cfc7700f1b7e83d75353296c53 (patch) | |
tree | 749332299beb8d379598c36d8bdc7fe85b632bad | |
parent | d32991afaf3fc5f9f73e3e2448672bb9a1b80101 (diff) | |
download | otp-7240ecfc3ac931cfc7700f1b7e83d75353296c53.tar.gz otp-7240ecfc3ac931cfc7700f1b7e83d75353296c53.tar.bz2 otp-7240ecfc3ac931cfc7700f1b7e83d75353296c53.zip |
ssl: Implement basic TLS 1.3 client
-rw-r--r-- | lib/ssl/src/tls_connection.erl | 11 | ||||
-rw-r--r-- | lib/ssl/src/tls_connection_1_3.erl | 53 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake.erl | 39 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 275 |
4 files changed, 343 insertions, 35 deletions
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 61281a3fb2..9c8c3b9352 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -394,6 +394,7 @@ queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_h handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, flight_buffer = Flight0 ++ [BinHandshake]}. + send_handshake_flight(#state{static_env = #static_env{socket = Socket, transport_cb = Transport}, flight_buffer = Flight} = State0) -> @@ -659,10 +660,16 @@ hello(internal, #server_hello{} = Hello, case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> %%TODO ssl_connection:handle_own_alert(Alert, ReqVersion, hello, - State#state{connection_env = CEnv#connection_env{negotiated_version = ReqVersion}}); + State#state{connection_env = + CEnv#connection_env{negotiated_version = ReqVersion}}); + %% Legacy TLS 1.2 and older {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, - Version, NewId, ConnectionStates, ProtoExt, Protocol, State) + Version, NewId, ConnectionStates, ProtoExt, Protocol, State); + %% TLS 1.3 + {next_state, wait_sh} -> + %% Continue in TLS 1.3 'wait_sh' state + {next_state, wait_sh, State, [{next_event, internal, Hello}]} end; hello(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 76cdebc76f..a6b589236f 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -112,7 +112,10 @@ negotiated/4, wait_cert/4, wait_cv/4, - wait_finished/4 + wait_finished/4, + wait_sh/4, + wait_ee/4, + wait_cert_cr/4 ]). @@ -183,3 +186,51 @@ wait_finished(internal, end; wait_finished(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_sh(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_sh(internal, #server_hello{} = Hello, State0, _Module) -> + case tls_handshake_1_3:do_wait_sh(Hello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_sh, State0); + {State1, start} -> + %% TODO: Implement hello_retry_request + {next_state, start, State1, []}; + {State1, wait_ee} -> + {Record, State} = tls_connection:next_record(State1), + tls_connection:next_event(wait_ee, Record, State) + end; +wait_sh(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_ee(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_ee(internal, #encrypted_extensions{} = EE, State0, _Module) -> + case tls_handshake_1_3:do_wait_ee(EE, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_ee, State0); + {State1, NextState} -> + {Record, State} = tls_connection:next_record(State1), + tls_connection:next_event(NextState, Record, State) + end; +wait_ee(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_cert_cr(internal, #change_cipher_spec{}, State0, _Module) -> + {Record, State} = tls_connection:next_record(State0), + tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cv, State0); + {State1, NextState} -> + {Record, State} = tls_connection:next_record(State1), + tls_connection:next_event(NextState, Record, State) + end; +wait_cert_cr(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 2480e05097..c132f75eae 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -105,7 +105,7 @@ client_hello(Host, Port, ConnectionStates, {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | - undefined} | #alert{}. + undefined} | {atom(), atom()} |#alert{}. %% %% Description: Handles a received hello message %%-------------------------------------------------------------------- @@ -148,29 +148,48 @@ hello(#server_hello{server_version = {Major, Minor}, %% %% - If "supported_version" is present (ServerHello): %% - Abort handshake with an "illegal_parameter" alert -hello(#server_hello{server_version = Version, +hello(#server_hello{server_version = LegacyVersion, + random = Random, + cipher_suite = CipherSuite, + compression_method = Compression, + session_id = SessionId, extensions = #{server_hello_selected_version := - #server_hello_selected_version{selected_version = Version}} + #server_hello_selected_version{selected_version = Version} = HelloExt} }, - #ssl_options{versions = SupportedVersions}, - _ConnectionStates0, _Renegotiation) -> - case tls_record:is_higher({3,4}, Version) of + #ssl_options{versions = SupportedVersions} = SslOpt, + ConnectionStates0, Renegotiation) -> + %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension + %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version + %% number for TLS 1.2. + %% The "supported_versions" extension is supported from TLS 1.2. + case LegacyVersion > {3,3} orelse + LegacyVersion =:= {3,3} andalso Version < {3,3} of true -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); false -> case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> - %% Implement TLS 1.3 statem ??? - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); + case Version of + {3,3} -> + %% TLS 1.2 ServerHello with "supported_versions" (special case) + handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, + Compression, HelloExt, SslOpt, + ConnectionStates0, Renegotiation); + {3,4} -> + %% TLS 1.3 + {next_state, wait_sh} + end; false -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) end end; -hello(#server_hello{server_version = Version, random = Random, +hello(#server_hello{server_version = Version, + random = Random, cipher_suite = CipherSuite, compression_method = Compression, - session_id = SessionId, extensions = HelloExt}, + session_id = SessionId, + extensions = HelloExt}, #ssl_options{versions = SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> case tls_record:is_acceptable_version(Version, SupportedVersions) of diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 8a4ad922e1..d7f45d984a 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -46,7 +46,10 @@ do_negotiated/2, do_wait_cert/2, do_wait_cv/2, - do_wait_finished/2]). + do_wait_finished/2, + do_wait_sh/2, + do_wait_ee/2, + do_wait_cert_cr/2]). %%==================================================================== %% Create handshake messages @@ -79,6 +82,9 @@ server_hello_random(server_hello, #security_parameters{server_random = Random}) %% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 %% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C server_hello_random(hello_retry_request, _) -> + hello_retry_request_random(). + +hello_retry_request_random() -> crypto:hash(sha256, "HelloRetryRequest"). @@ -436,7 +442,6 @@ do_start(#client_hello{cipher_suites = ClientCiphers, signature_algs = ServerSignAlgs, supported_groups = ServerGroups0}, session = #session{own_certificate = Cert}} = State0) -> - ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), ServerGroups = get_supported_groups(ServerGroups0), @@ -515,7 +520,7 @@ do_negotiated(start_handshake, own_certificate = OwnCert, ecc = SelectedGroup, sign_alg = SignatureScheme, - dh_public_value = ClientKey}, + dh_public_value = ClientPublicKey}, ssl_options = #ssl_options{} = SslOpts, key_share = KeyShare, handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, @@ -526,6 +531,8 @@ do_negotiated(start_handshake, socket = _Socket, transport_cb = _Transport} } = State0) -> + ServerPrivateKey = get_server_private_key(KeyShare), + {Ref,Maybe} = maybe(), try @@ -536,7 +543,7 @@ do_negotiated(start_handshake, {State1, _} = tls_connection:send_handshake(ServerHello, State0), State2 = - calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, State1), + calculate_handshake_secrets(ClientPublicKey, ServerPrivateKey, SelectedGroup, State1), State3 = ssl_record:step_encryption_state(State2), @@ -596,7 +603,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> end. -do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> +do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, + #state{static_env = #static_env{role = server}} = State0) -> {Ref,Maybe} = maybe(), try Maybe(verify_signature_algorithm(State0, CertificateVerify)), @@ -608,9 +616,28 @@ do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {verify, badarg}), State}; {Ref, {{handshake_failure, Reason}, State}} -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} + end; +do_wait_cv(#certificate_verify_1_3{} = _CertificateVerify, + #state{static_env = #static_env{role = client}} = State0) -> + {Ref,_Maybe} = maybe(), + try + %% TODO: implement validation + %% Maybe(verify_signature_algorithm(State0, CertificateVerify)), + %% Maybe(verify_certificate_verify(State0, CertificateVerify)) + {State0, wait_finished} + catch + {Ref, {{bad_certificate, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; + {Ref, {badarg, State}} -> + {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {verify, badarg}), State}; + {Ref, {{handshake_failure, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} end. + + + do_wait_finished(#finished{verify_data = VerifyData}, #state{connection_states = _ConnectionStates0, session = #session{session_id = _SessionId, @@ -619,6 +646,7 @@ do_wait_finished(#finished{verify_data = VerifyData}, key_share = _KeyShare, handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, static_env = #static_env{ + role = server, cert_db = _CertDbHandle, cert_db_ref = _CertDbRef, socket = _Socket, @@ -639,6 +667,141 @@ do_wait_finished(#finished{verify_data = VerifyData}, catch {Ref, decrypt_error} -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + end; +do_wait_finished(#finished{verify_data = _VerifyData}, + #state{connection_states = _ConnectionStates0, + session = #session{session_id = _SessionId, + own_certificate = _OwnCert}, + ssl_options = #ssl_options{} = _SslOpts, + key_share = _KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + static_env = #static_env{ + role = client, + cert_db = _CertDbHandle, + cert_db_ref = _CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> + {Ref,_Maybe} = maybe(), + + try + %% Maybe(validate_client_finished(State0, VerifyData)), + + %% Maybe send Certificate + CertificateVerify + + %% Send Finished + %% Create Finished + Finished = finished(State0), + + %% Encode Finished + State1 = tls_connection:queue_handshake(Finished, State0), + + %% Send first flight + {State2, _} = tls_connection:send_handshake_flight(State1), + + State3 = calculate_traffic_secrets(State2), + + %% Configure traffic keys + ssl_record:step_encryption_state(State3) + + catch + {Ref, decrypt_error} -> + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + end. + + + +do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, + session_id = SessionId, + extensions = Extensions} = ServerHello, + #state{connection_states = _ConnectionStates0, + key_share = ClientKeyShare0, + ssl_options = #ssl_options{ciphers = _ClientCiphers, + signature_algs = _ClientSignAlgs, + supported_groups = _ClientGroups}, + session = #session{own_certificate = _Cert}} = State0) -> + ServerKeyShare0 = maps:get(key_share, Extensions, undefined), + ServerKeyShare = get_key_shares(ServerKeyShare0), + ClientKeyShare = get_key_shares(ClientKeyShare0), + + {Ref,Maybe} = maybe(), + try + %% Go to state 'start' if server replies with 'HelloRetryRequest'. + Maybe(maybe_hello_retry_request(ServerHello, State0)), + + %% TODO: implement validation + %% Maybe(validate_cipher_suite(SelectedCipherSuite, ClientCiphers)), + %% Maybe(validate_key_share(ServerKeyShare, ClientGroups)), + + %% Get server public key + {SelectedGroup, ServerPublicKey} = get_server_public_key(ServerKeyShare), + + {_, ClientPrivateKey} = get_client_private_key([SelectedGroup], ClientKeyShare), + + %% Update state + State1 = update_start_state(State0, SelectedCipherSuite, ClientKeyShare0, SessionId, + SelectedGroup, todo, ServerPublicKey), + + State2 = calculate_handshake_secrets(ServerPublicKey, ClientPrivateKey, SelectedGroup, State1), + + State3 = ssl_record:step_encryption_state(State2), + + {State3, wait_ee} + + catch + {Ref, {new_state, State, StateName}} -> + {State, StateName}; + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_wait_ee(#encrypted_extensions{extensions = _Extensions} = _EE, + #state{connection_states = _ConnectionStates0, + ssl_options = #ssl_options{ciphers = _ServerCiphers, + signature_algs = _ServerSignAlgs, + supported_groups = _ServerGroups0}, + session = #session{own_certificate = _Cert}} = State0) -> + {Ref,_Maybe} = maybe(), + try + {State0, wait_cert_cr} + catch + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_wait_cert_cr(#certificate_1_3{} = _Certificate, State0) -> + {Ref,_Maybe} = maybe(), + try + %% TODO: implement validation + %% Maybe(process_server_certificate(Certificate, State0)) + {State0, wait_cv} + catch + {Ref, {certificate_required, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); + {Ref, {{certificate_unknown, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); + {Ref, {{internal_error, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); + {Ref, {{handshake_failure, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) end. @@ -652,6 +815,25 @@ do_wait_finished(#finished{verify_data = VerifyData}, %% {error, {not_implemented, State, Reason}}. +%% For reasons of backward compatibility with middleboxes (see +%% Appendix D.4), the HelloRetryRequest message uses the same structure +%% as the ServerHello, but with Random set to the special value of the +%% SHA-256 of "HelloRetryRequest": +%% +%% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 +%% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C +%% +%% Upon receiving a message with type server_hello, implementations MUST +%% first examine the Random value and, if it matches this value, process +%% it as described in Section 4.1.4). +maybe_hello_retry_request(#server_hello{cipher_suite = Random}, State0) -> + case Random =:= hello_retry_request_random() of + true -> + {error, State0, start}; + false -> + ok + end. + %% Recipients of Finished messages MUST verify that the contents are %% correct and if incorrect MUST terminate the connection with a @@ -861,7 +1043,7 @@ message_hash(ClientHello1, HKDFAlgo) -> crypto:hash(HKDFAlgo, ClientHello1)]. -calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, +calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, #state{connection_states = ConnectionStates, handshake_env = #handshake_env{ @@ -874,9 +1056,8 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, %% Calculate handshake_secret PSK = binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo)), EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}), - PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} - IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), + IKM = calculate_shared_secret(PublicKey, PrivateKey, SelectedGroup), HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret), %% Calculate [sender]_handshake_traffic_secret @@ -899,10 +1080,13 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey). -calculate_traffic_secrets(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - tls_handshake_history = HHistory}} = State0) -> + +calculate_traffic_secrets(#state{ + static_env = #static_env{role = Role}, + connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}} = State0) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, read), #security_parameters{prf_algorithm = HKDFAlgo, @@ -913,7 +1097,7 @@ calculate_traffic_secrets(#state{connection_states = ConnectionStates, tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret), %% Get the correct list messages for the handshake context. - Messages = get_handshake_context(HHistory), + Messages = get_handshake_context(Role, HHistory), %% Calculate [sender]_application_traffic_secret_0 ClientAppTrafficSecret0 = @@ -966,9 +1150,11 @@ calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) public_key:compute_key(Point, MyKey). -update_pending_connection_states(#state{connection_states = - CS = #{pending_read := PendingRead0, - pending_write := PendingWrite0}} = State, +update_pending_connection_states(#state{ + static_env = #static_env{role = server}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, HandshakeSecret, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey) -> @@ -977,8 +1163,23 @@ update_pending_connection_states(#state{connection_states = PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV, WriteFinishedKey), State#state{connection_states = CS#{pending_read => PendingRead, + pending_write => PendingWrite}}; +update_pending_connection_states(#state{ + static_env = #static_env{role = client}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, + HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey, + WriteKey, WriteIV, WriteFinishedKey) -> + PendingRead = update_connection_state(PendingRead0, HandshakeSecret, + WriteKey, WriteIV, WriteFinishedKey), + PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey), + State#state{connection_states = CS#{pending_read => PendingRead, pending_write => PendingWrite}}. + update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, HandshakeSecret, Key, IV, FinishedKey) -> %% Store secret @@ -1071,13 +1272,20 @@ get_handshake_context_cv({[<<15,_/binary>>|Messages], _}) -> %% %% Drop all client messages from the front of the iolist using the property that %% incoming messages are binaries. -get_handshake_context({Messages, _}) -> - get_handshake_context(Messages); -get_handshake_context([H|T]) when is_binary(H) -> - get_handshake_context(T); -get_handshake_context(L) -> +get_handshake_context(server, {Messages, _}) -> + get_handshake_context_server(Messages); +get_handshake_context(client, {Messages, _}) -> + get_handshake_context_client(Messages). + +get_handshake_context_server([H|T]) when is_binary(H) -> + get_handshake_context_server(T); +get_handshake_context_server(L) -> L. +%% TODO: tested only with basic handshake! +get_handshake_context_client([_H|T]) -> + T. + %% If sent by a client, the signature algorithm used in the signature %% MUST be one of those present in the supported_signature_algorithms @@ -1197,6 +1405,27 @@ get_client_public_key([Group|Groups], ClientShares, PreferredGroup) -> get_client_public_key(Groups, ClientShares, PreferredGroup) end. +get_client_private_key([Group|_] = Groups, ClientShares) -> + get_client_private_key(Groups, ClientShares, Group). +%% +get_client_private_key(_, [], PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([], _, PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([Group|Groups], ClientShares, PreferredGroup) -> + case lists:keysearch(Group, 2, ClientShares) of + {value, {_, _, {_, ClientPrivateKey}}} -> + {Group, ClientPrivateKey}; + {value, {_, _, #'ECPrivateKey'{} = ClientPrivateKey}} -> + {Group, ClientPrivateKey}; + false -> + get_client_private_key(Groups, ClientShares, PreferredGroup) + end. + + +get_server_public_key({key_share_entry, Group, PublicKey}) -> + {Group, PublicKey}. + %% get_client_public_key(Group, ClientShares) -> %% case lists:keysearch(Group, 2, ClientShares) of @@ -1331,7 +1560,9 @@ get_supported_groups(#supported_groups{supported_groups = Groups}) -> Groups. get_key_shares(#key_share_client_hello{client_shares = ClientShares}) -> - ClientShares. + ClientShares; +get_key_shares(#key_share_server_hello{server_share = ServerShare}) -> + ServerShare. maybe() -> Ref = erlang:make_ref(), |