aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
authorPéter Dimitrov <[email protected]>2018-12-17 09:41:58 +0100
committerPéter Dimitrov <[email protected]>2019-01-11 09:59:12 +0100
commit5550d8265860adec290aaf4a9498ec88b1a31386 (patch)
tree76eaf91ea3ce6b45af6a53c89e5ea775094e2f08 /lib/ssl
parent20333cce220322c9387622b944b03ad42e9cc3aa (diff)
downloadotp-5550d8265860adec290aaf4a9498ec88b1a31386.tar.gz
otp-5550d8265860adec290aaf4a9498ec88b1a31386.tar.bz2
otp-5550d8265860adec290aaf4a9498ec88b1a31386.zip
ssl: Refactor state 'negotiated'
Change-Id: I1a2e9b1b639cae0d78b6d25d7b6e761a2d90b7b1
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/src/ssl_cipher.erl3
-rw-r--r--lib/ssl/src/tls_connection_1_3.erl199
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl175
3 files changed, 213 insertions, 164 deletions
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 46885130d3..bf64ed8b69 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -94,7 +94,8 @@ security_parameters_1_3(SecParams, CipherSuite) ->
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
- prf_algorithm = PrfHashAlg}. %% HKDF hash algorithm
+ prf_algorithm = PrfHashAlg, %% HKDF hash algorithm
+ cipher_type = ?AEAD}.
%%--------------------------------------------------------------------
-spec cipher_init(cipher_enum(), binary(), binary()) -> #cipher_state{}.
diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl
index 9f7a82223e..ceaf6e8bca 100644
--- a/lib/ssl/src/tls_connection_1_3.erl
+++ b/lib/ssl/src/tls_connection_1_3.erl
@@ -134,7 +134,43 @@ start(internal,
end.
-%% TODO: move these functions
+
+negotiated(internal,
+ Map,
+ #state{connection_states = ConnectionStates0,
+ session = #session{session_id = SessionId,
+ own_certificate = OwnCert},
+ ssl_options = #ssl_options{} = SslOpts,
+ key_share = KeyShare,
+ tls_handshake_history = HHistory0,
+ private_key = CertPrivateKey,
+ static_env = #static_env{
+ cert_db = CertDbHandle,
+ cert_db_ref = CertDbRef,
+ socket = Socket,
+ transport_cb = Transport}} = State0, _Module) ->
+ Env = #{connection_states => ConnectionStates0,
+ session_id => SessionId,
+ own_certificate => OwnCert,
+ cert_db => CertDbHandle,
+ cert_db_ref => CertDbRef,
+ ssl_options => SslOpts,
+ key_share => KeyShare,
+ tls_handshake_history => HHistory0,
+ transport_cb => Transport,
+ socket => Socket,
+ private_key => CertPrivateKey},
+ case tls_handshake_1_3:do_negotiated(Map, Env) of
+ #alert{} = Alert ->
+ ssl_connection:handle_own_alert(Alert, {3,4}, negotiated, State0);
+ M ->
+ %% update connection_states with cipher
+ State = update_state(State0, M),
+ {next_state, wait_flight2, State, [{next_event, internal, M}]}
+
+ end.
+
+
update_state(#state{connection_states = ConnectionStates0,
session = Session} = State,
#{cipher := Cipher,
@@ -152,164 +188,3 @@ update_state(#state{connection_states = ConnectionStates0,
State#state{connection_states = ConnectionStates,
key_share = KeyShare,
session = Session#session{session_id = SessionId}}.
-
-
-negotiated(internal,
- Map,
- #state{connection_states = ConnectionStates0,
- session = #session{session_id = SessionId,
- own_certificate = OwnCert},
- ssl_options = #ssl_options{} = SslOpts,
- key_share = KeyShare,
- tls_handshake_history = HHistory0,
- private_key = CertPrivateKey,
- static_env = #static_env{
- cert_db = CertDbHandle,
- cert_db_ref = CertDbRef,
- socket = Socket,
- transport_cb = Transport}}, _Module) ->
- %% Create server_hello
- %% Extensions: supported_versions, key_share, (pre_shared_key)
- ServerHello = tls_handshake_1_3:server_hello(SessionId, KeyShare,
- ConnectionStates0, Map),
- %% Update handshake_history (done in encode!)
- %% Encode handshake
- {BinMsg, ConnectionStates1, HHistory1} =
- tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0),
- %% Send server_hello
- tls_connection:send(Transport, Socket, BinMsg),
- Report = #{direction => outbound,
- protocol => 'tls_record',
- message => BinMsg},
- Msg = #{direction => outbound,
- protocol => 'handshake',
- message => ServerHello},
- ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}),
- ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}),
-
- #{security_parameters := SecParamsR} =
- ssl_record:pending_connection_state(ConnectionStates1, read),
- #security_parameters{prf_algorithm = HKDFAlgo,
- cipher_suite = CipherSuite} = SecParamsR,
-
- %% Calculate handshake_secret
- EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, <<>>}),
-
- ClientKey = maps:get(client_share, Map), %% Raw data?! What about EC?
- SelectedGroup = maps:get(group, Map),
- SignatureScheme = maps:get(sign_alg, Map),
-
- PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{}
-
- IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup),
- HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret),
-
- %% Calculate [sender]_handshake_traffic_secret
- {Messages1, _} = HHistory1,
- ClientHSTrafficSecret =
- tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages1)),
- ServerHSTrafficSecret =
- tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages1)),
-
- %% Calculate traffic keys
- #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite),
- {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret),
- {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret),
-
- %% Update pending connection state
- PendingRead0 = ssl_record:pending_connection_state(ConnectionStates1, read),
- PendingWrite0 = ssl_record:pending_connection_state(ConnectionStates1, write),
-
- PendingRead = update_conn_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV),
- PendingWrite = update_conn_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV),
-
- %% Update pending and copy to current (activate)
- %% All subsequent handshake messages are encrypted
- %% ([sender]_handshake_traffic_secret)
- ConnectionStates2 = ConnectionStates1#{current_read => PendingRead,
- current_write => PendingWrite,
- pending_read => PendingRead,
- pending_write => PendingWrite},
-
- %% Create Certificate
- Certificate = tls_handshake_1_3:certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server),
-
- %% Encode Certificate
- {_CertificateBin, _ConnectionStates2, HHistory2} =
- tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates1, HHistory1),
-
- %% Create CertificateVerify
-
- {Messages2, _} = HHistory2,
-
- %% Use selected signature_alg from here, HKDF only used for key_schedule
- CertificateVerify = tls_handshake_1_3:certificate_verify(OwnCert, CertPrivateKey, SignatureScheme,
- Messages2, server),
- io:format("### CertificateVerify: ~p~n", [CertificateVerify]),
-
- %% Encode CertificateVerify
-
- %% Send Certificate, CertifricateVerify
-
- %% Send finished
-
- %% Next record/Next event
-
- ConnectionStates1.
-
- %% K_send = handshake ???
- %% (Send EncryptedExtensions)
- %% ([Send CertificateRequest])
- %% [Send Certificate + CertificateVerify]
- %% Send Finished
- %% K_send = application ???
-
- %% Will be called implicitly
- %% {Record, State} = Connection:next_record(State2#state{session = Session}),
- %% Connection:next_event(wait_flight2, Record, State, Actions),
- %% OR
- %% Connection:next_event(WAIT_EOED, Record, State, Actions)
-
-
-get_server_private_key(#key_share_server_hello{server_share = ServerShare}) ->
- get_private_key(ServerShare).
-
-get_private_key(#key_share_entry{
- key_exchange = #'ECPrivateKey'{} = PrivateKey}) ->
- PrivateKey;
-get_private_key(#key_share_entry{
- key_exchange =
- {_, PrivateKey}}) ->
- PrivateKey.
-
-
-%% X25519, X448
-calculate_shared_secret(OthersKey, MyKey, Group)
- when is_binary(OthersKey) andalso is_binary(MyKey) andalso
- (Group =:= x25519 orelse Group =:= x448)->
- crypto:compute_key(ecdh, OthersKey, MyKey, Group);
-%% FFDHE
-calculate_shared_secret(OthersKey, MyKey, Group)
- when is_binary(OthersKey) andalso is_binary(MyKey) ->
- Params = #'DHParameter'{prime = P} = ssl_dh_groups:dh_params(Group),
- S = public_key:compute_key(OthersKey, MyKey, Params),
- Size = byte_size(binary:encode_unsigned(P)),
- ssl_cipher:add_zero_padding(S, Size);
-%% ECDHE
-calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group)
- when is_binary(OthersKey) ->
- Point = #'ECPoint'{point = OthersKey},
- public_key:compute_key(Point, MyKey).
-
-
-update_conn_state(ConnectionState = #{security_parameters := SecurityParameters0},
- HandshakeSecret, Key, IV) ->
- %% Store secret
- SecurityParameters = SecurityParameters0#security_parameters{
- master_secret = HandshakeSecret},
- ConnectionState#{security_parameters => SecurityParameters,
- cipher_state => cipher_init(Key, IV)}.
-
-
-cipher_init(Key, IV) ->
- #cipher_state{key = Key, iv = IV, tag_len = 16}.
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index cbe9550e70..2c8910cefa 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -27,6 +27,7 @@
-include("tls_handshake_1_3.hrl").
-include("ssl_alert.hrl").
+-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -42,6 +43,8 @@
certificate_verify/5,
server_hello/4]).
+-export([do_negotiated/2]).
+
%%====================================================================
%% Create handshake messages
%%====================================================================
@@ -326,7 +329,6 @@ handle_client_hello(#client_hello{cipher_suites = ClientCiphers,
%% Generate server_share
KeyShare = ssl_cipher:generate_server_share(Group),
-
_Ret = #{cipher => Cipher,
group => Group,
sign_alg => SelectedSignAlg,
@@ -356,6 +358,177 @@ handle_client_hello(#client_hello{cipher_suites = ClientCiphers,
end.
+do_negotiated(#{client_share := ClientKey,
+ group := SelectedGroup,
+ sign_alg := SignatureScheme
+ } = Map,
+ #{connection_states := ConnectionStates0,
+ session_id := SessionId,
+ own_certificate := OwnCert,
+ cert_db := CertDbHandle,
+ cert_db_ref := CertDbRef,
+ ssl_options := SslOpts,
+ key_share := KeyShare,
+ tls_handshake_history := HHistory0,
+ transport_cb := Transport,
+ socket := Socket,
+ private_key := CertPrivateKey}) ->
+ {Ref,Maybe} = maybe(),
+
+ try
+ %% Create server_hello
+ %% Extensions: supported_versions, key_share, (pre_shared_key)
+ ServerHello = server_hello(SessionId, KeyShare, ConnectionStates0, Map),
+
+ %% Update handshake_history (done in encode!)
+ %% Encode handshake
+ {BinMsg, ConnectionStates1, HHistory1} =
+ tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0),
+ %% Send server_hello
+ tls_connection:send(Transport, Socket, BinMsg),
+ log_handshake(SslOpts, ServerHello),
+ log_tls_record(SslOpts, BinMsg),
+ ConnectionStates2 = calculate_security_parameters(ClientKey, SelectedGroup, KeyShare,
+ HHistory1, ConnectionStates1),
+ %% Create Certificate
+ Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server),
+
+ %% Encode Certificate
+ {_, _ConnectionStates3, HHistory2} =
+ tls_connection:encode_handshake(Certificate, {3,4}, ConnectionStates2, HHistory1),
+ %% log_handshake(SslOpts, Certificate),
+
+ %% Create CertificateVerify
+ {Messages, _} = HHistory2,
+
+ %% Use selected signature_alg from here, HKDF only used for key_schedule
+ CertificateVerify =
+ tls_handshake_1_3:certificate_verify(OwnCert, CertPrivateKey, SignatureScheme,
+ Messages, server),
+ io:format("### CertificateVerify: ~p~n", [CertificateVerify]),
+
+ %% Encode CertificateVerify
+
+ %% Send Certificate, CertifricateVerify
+
+ %% Send finished
+
+ %% Next record/Next event
+
+ Maybe(not_implemented())
+
+
+ catch
+ {Ref, {state_not_implemented, negotiated}} ->
+ %% TODO
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, "state not implemented")
+ end.
+
+
+%% TODO: Remove this function!
+not_implemented() ->
+ {error, negotiated}.
+
+
+log_handshake(SslOpts, Message) ->
+ Msg = #{direction => outbound,
+ protocol => 'handshake',
+ message => Message},
+ ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}).
+
+
+log_tls_record(SslOpts, BinMsg) ->
+ Report = #{direction => outbound,
+ protocol => 'tls_record',
+ message => BinMsg},
+ ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}).
+
+
+calculate_security_parameters(ClientKey, SelectedGroup, KeyShare, HHistory, ConnectionStates) ->
+ #{security_parameters := SecParamsR} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ #security_parameters{prf_algorithm = HKDFAlgo,
+ cipher_suite = CipherSuite} = SecParamsR,
+
+ %% Calculate handshake_secret
+ EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, <<>>}),
+
+ PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{}
+
+ IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup),
+ HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret),
+
+ %% Calculate [sender]_handshake_traffic_secret
+ {Messages, _} = HHistory,
+ ClientHSTrafficSecret =
+ tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)),
+ ServerHSTrafficSecret =
+ tls_v1:server_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)),
+
+ %% Calculate traffic keys
+ #{cipher := Cipher} = ssl_cipher_format:suite_definition(CipherSuite),
+ {ReadKey, ReadIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ClientHSTrafficSecret),
+ {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, ServerHSTrafficSecret),
+
+ %% Update pending connection state
+ PendingRead0 = ssl_record:pending_connection_state(ConnectionStates, read),
+ PendingWrite0 = ssl_record:pending_connection_state(ConnectionStates, write),
+
+ PendingRead = update_conn_state(PendingRead0, HandshakeSecret, ReadKey, ReadIV),
+ PendingWrite = update_conn_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV),
+
+ %% Update pending and copy to current (activate)
+ %% All subsequent handshake messages are encrypted
+ %% ([sender]_handshake_traffic_secret)
+ #{current_read => PendingRead,
+ current_write => PendingWrite,
+ pending_read => PendingRead,
+ pending_write => PendingWrite}.
+
+
+get_server_private_key(#key_share_server_hello{server_share = ServerShare}) ->
+ get_private_key(ServerShare).
+
+get_private_key(#key_share_entry{
+ key_exchange = #'ECPrivateKey'{} = PrivateKey}) ->
+ PrivateKey;
+get_private_key(#key_share_entry{
+ key_exchange =
+ {_, PrivateKey}}) ->
+ PrivateKey.
+
+%% X25519, X448
+calculate_shared_secret(OthersKey, MyKey, Group)
+ when is_binary(OthersKey) andalso is_binary(MyKey) andalso
+ (Group =:= x25519 orelse Group =:= x448)->
+ crypto:compute_key(ecdh, OthersKey, MyKey, Group);
+%% FFDHE
+calculate_shared_secret(OthersKey, MyKey, Group)
+ when is_binary(OthersKey) andalso is_binary(MyKey) ->
+ Params = #'DHParameter'{prime = P} = ssl_dh_groups:dh_params(Group),
+ S = public_key:compute_key(OthersKey, MyKey, Params),
+ Size = byte_size(binary:encode_unsigned(P)),
+ ssl_cipher:add_zero_padding(S, Size);
+%% ECDHE
+calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group)
+ when is_binary(OthersKey) ->
+ Point = #'ECPoint'{point = OthersKey},
+ public_key:compute_key(Point, MyKey).
+
+
+update_conn_state(ConnectionState = #{security_parameters := SecurityParameters0},
+ HandshakeSecret, Key, IV) ->
+ %% Store secret
+ SecurityParameters = SecurityParameters0#security_parameters{
+ master_secret = HandshakeSecret},
+ ConnectionState#{security_parameters => SecurityParameters,
+ cipher_state => cipher_init(Key, IV)}.
+
+
+cipher_init(Key, IV) ->
+ #cipher_state{key = Key, iv = IV, tag_len = 16}.
+
+
%% If there is no overlap between the received
%% "supported_groups" and the groups supported by the server, then the
%% server MUST abort the handshake with a "handshake_failure" or an