aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/tls_connection.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/tls_connection.erl')
-rw-r--r--lib/ssl/src/tls_connection.erl164
1 files changed, 95 insertions, 69 deletions
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 246fecf34a..0415ea6ecc 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -76,7 +76,8 @@
negotiated_version, % tls_version()
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
- hashsign_algorithm, % atom as defined by cipher_suite
+ hashsign_algorithm = {undefined, undefined},
+ cert_hashsign_algorithm,
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
private_key, % PKIX: #'RSAPrivateKey'{}
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
@@ -89,7 +90,6 @@
cert_db_ref, % ref()
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
- log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
start_or_recv_from, % "gen_fsm From"
timer, % start_or_recv_timer
@@ -367,6 +367,7 @@ hello(#hello_request{}, #state{role = client} = State0) ->
next_state(hello, hello, Record, State);
hello(#server_hello{cipher_suite = CipherSuite,
+ hash_signs = HashSign,
compression_method = Compression} = Hello,
#state{session = #session{session_id = OldId},
connection_states = ConnectionStates0,
@@ -389,9 +390,10 @@ hello(#server_hello{cipher_suite = CipherSuite,
_ ->
NextProtocol
end,
-
+
State = State0#state{key_algorithm = KeyAlgorithm,
- hashsign_algorithm = default_hashsign(Version, KeyAlgorithm),
+ hashsign_algorithm =
+ negotiated_hashsign(HashSign, KeyAlgorithm, Version),
negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret,
@@ -407,22 +409,28 @@ hello(#server_hello{cipher_suite = CipherSuite,
end
end;
-hello(Hello = #client_hello{client_version = ClientVersion},
+hello(Hello = #client_hello{client_version = ClientVersion,
+ hash_signs = HashSigns},
State = #state{connection_states = ConnectionStates0,
port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
+
+ HashSign = tls_handshake:select_hashsign(HashSigns, Cert),
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
- {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise,
+ {Version, {Type, #session{cipher_suite = CipherSuite} = Session}, ConnectionStates, ProtocolsToAdvertise,
EcPointFormats, EllipticCurves} ->
+ {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
+ NH = negotiated_hashsign(HashSign, KeyAlgorithm, Version),
do_server_hello(Type, ProtocolsToAdvertise,
EcPointFormats, EllipticCurves,
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
session = Session,
+ hashsign_algorithm = NH,
client_ecc = {EllipticCurves, EcPointFormats}});
#alert{} = Alert ->
handle_own_alert(Alert, ClientVersion, hello, State)
@@ -527,7 +535,7 @@ certify(#certificate{} = Cert,
Opts#ssl_options.verify,
Opts#ssl_options.verify_fun, Role) of
{PeerCert, PublicKeyInfo} ->
- handle_peer_cert(PeerCert, PublicKeyInfo,
+ handle_peer_cert(Role, PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false});
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State)
@@ -553,9 +561,11 @@ certify(#server_key_exchange{} = Msg,
#state{role = client, key_algorithm = rsa} = State) ->
handle_unexpected_message(Msg, certify_server_keyexchange, State);
-certify(#certificate_request{}, State0) ->
+certify(#certificate_request{hashsign_algorithms = HashSigns},
+ #state{session = #session{own_certificate = Cert}} = State0) ->
+ HashSign = tls_handshake:select_hashsign(HashSigns, Cert),
{Record, State} = next_record(State0#state{client_certificate_requested = true}),
- next_state(certify, certify, Record, State);
+ next_state(certify, certify, Record, State#state{cert_hashsign_algorithm = HashSign});
%% PSK and RSA_PSK might bypass the Server-Key-Exchange
certify(#server_hello_done{},
@@ -758,21 +768,18 @@ cipher(#hello_request{}, State0) ->
cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign},
#state{role = server,
- public_key_info = PublicKeyInfo,
+ public_key_info = {Algo, _, _} =PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
- hashsign_algorithm = ConnectionHashSign,
tls_handshake_history = Handshake
} = State0) ->
- HashSign = case CertHashSign of
- {_, _} -> CertHashSign;
- _ -> ConnectionHashSign
- end,
+
+ HashSign = tls_handshake:select_cert_hashsign(CertHashSign, Algo, Version),
case tls_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, HashSign, MasterSecret, Handshake) of
valid ->
{Record, State} = next_record(State0),
- next_state(cipher, cipher, Record, State);
+ next_state(cipher, cipher, Record, State#state{cert_hashsign_algorithm = HashSign});
#alert{} = Alert ->
handle_own_alert(Alert, Version, cipher, State0)
end;
@@ -978,7 +985,7 @@ handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protoc
handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
{reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
-handle_sync_event({set_opts, Opts0}, _From, StateName,
+handle_sync_event({set_opts, Opts0}, _From, StateName0,
#state{socket_options = Opts1,
socket = Socket,
transport_cb = Transport,
@@ -987,11 +994,12 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
- {reply, Reply, StateName, State1, get_timeout(State1)};
+ {reply, Reply, StateName0, State1, get_timeout(State1)};
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
{Record, State2} = next_record_if_active(State1),
- case next_state(StateName, StateName, Record, State2) of
+ %% Note: Renogotiation may cause StateName0 =/= StateName
+ case next_state(StateName0, StateName0, Record, State2) of
{next_state, StateName, State, Timeout} ->
{reply, Reply, StateName, State, Timeout};
{stop, Reason, State} ->
@@ -999,13 +1007,14 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
end;
Buffer =:= <<>> ->
%% Active once already set
- {reply, Reply, StateName, State1, get_timeout(State1)};
+ {reply, Reply, StateName0, State1, get_timeout(State1)};
true ->
case read_application_data(<<>>, State1) of
Stop = {stop,_,_} ->
Stop;
{Record, State2} ->
- case next_state(StateName, StateName, Record, State2) of
+ %% Note: Renogotiation may cause StateName0 =/= StateName
+ case next_state(StateName0, StateName0, Record, State2) of
{next_state, StateName, State, Timeout} ->
{reply, Reply, StateName, State, Timeout};
{stop, Reason, State} ->
@@ -1238,9 +1247,9 @@ ssl_init(SslOpts, Role) ->
{ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
init_manager_name(false) ->
- put(ssl_manager, ssl_manager);
+ put(ssl_manager, ssl_manager:manager_name(normal));
init_manager_name(true) ->
- put(ssl_manager, ssl_manager_dist).
+ put(ssl_manager, ssl_manager:manager_name(dist)).
init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
@@ -1368,25 +1377,34 @@ sync_send_all_state_event(FsmPid, Event) ->
{error, closed}
end.
-%% We do currently not support cipher suites that use fixed DH.
-%% If we want to implement that we should add a code
-%% here to extract DH parameters form cert.
-handle_peer_cert(PeerCert, PublicKeyInfo,
- #state{session = Session} = State0) ->
+handle_peer_cert(Role, PeerCert, PublicKeyInfo,
+ #state{session = #session{cipher_suite = CipherSuite} = Session} = State0) ->
State1 = State0#state{session =
Session#session{peer_certificate = PeerCert},
public_key_info = PublicKeyInfo},
- State2 = case PublicKeyInfo of
- {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams} ->
- ECDHKey = public_key:generate_key(PublicKeyParams),
- State3 = State1#state{diffie_hellman_keys = ECDHKey},
- ec_dh_master_secret(ECDHKey, PublicKey, State3);
-
- _ -> State1
- end,
+ {KeyAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite),
+ State2 = handle_peer_cert_key(Role, PeerCert, PublicKeyInfo, KeyAlg, State1),
+
{Record, State} = next_record(State2),
next_state(certify, certify, Record, State).
+handle_peer_cert_key(client, _,
+ {?'id-ecPublicKey', #'ECPoint'{point = _ECPoint} = PublicKey, PublicKeyParams},
+ KeyAlg, State) when KeyAlg == ecdh_rsa;
+ KeyAlg == ecdh_ecdsa ->
+ ECDHKey = public_key:generate_key(PublicKeyParams),
+ ec_dh_master_secret(ECDHKey, PublicKey, State#state{diffie_hellman_keys = ECDHKey});
+
+%% We do currently not support cipher suites that use fixed DH.
+%% If we want to implement that the following clause can be used
+%% to extract DH parameters form cert.
+%% handle_peer_cert_key(client, _PeerCert, {?dhpublicnumber, PublicKey, PublicKeyParams}, {_,SignAlg},
+%% #state{diffie_hellman_keys = {_, MyPrivatKey}} = State) when SignAlg == dh_rsa;
+%% SignAlg == dh_dss ->
+%% dh_master_secret(PublicKeyParams, PublicKey, MyPrivatKey, State);
+handle_peer_cert_key(_, _, _, _, State) ->
+ State.
+
certify_client(#state{client_certificate_requested = true, role = client,
connection_states = ConnectionStates0,
transport_cb = Transport,
@@ -1413,10 +1431,9 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
private_key = PrivateKey,
session = #session{master_secret = MasterSecret,
own_certificate = OwnCert},
- hashsign_algorithm = HashSign,
+ cert_hashsign_algorithm = HashSign,
tls_handshake_history = Handshake0} = State) ->
- %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this.
case tls_handshake:client_certificate_verify(OwnCert, MasterSecret,
Version, HashSign, PrivateKey, Handshake0) of
#certificate_verify{} = Verified ->
@@ -1559,8 +1576,7 @@ server_hello(ServerHello, #state{transport_cb = Transport,
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
tls_handshake_history = Handshake1,
- key_algorithm = KeyAlgorithm,
- hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}.
+ key_algorithm = KeyAlgorithm}.
server_hello_done(#state{transport_cb = Transport,
socket = Socket,
@@ -1936,7 +1952,7 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
negotiated_version = Version,
socket = Socket,
transport_cb = Transport} = State) ->
- Msg = tls_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef),
+ Msg = tls_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef, Version),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
@@ -2013,12 +2029,13 @@ handle_server_key(#server_key_exchange{exchange_keys = Keys},
#state{key_algorithm = KeyAlg,
negotiated_version = Version} = State) ->
Params = tls_handshake:decode_server_key(Keys, KeyAlg, Version),
- HashSign = connection_hashsign(Params#server_key_params.hashsign, State),
- case HashSign of
- {_, SignAlgo} when SignAlgo == anon; SignAlgo == ecdh_anon ->
- server_master_secret(Params#server_key_params.params, State);
- _ ->
- verify_server_key(Params, HashSign, State)
+ HashSign = negotiated_hashsign(Params#server_key_params.hashsign, KeyAlg, Version),
+ case is_anonymous(KeyAlg) of
+ true ->
+ server_master_secret(Params#server_key_params.params,
+ State#state{hashsign_algorithm = HashSign});
+ false ->
+ verify_server_key(Params, HashSign, State#state{hashsign_algorithm = HashSign})
end.
verify_server_key(#server_key_params{params = Params,
@@ -2458,7 +2475,7 @@ do_format_reply(list, _,_, Data) ->
binary_to_list(Data).
header(0, <<>>) ->
- [];
+ <<>>;
header(_, <<>>) ->
[];
header(0, Binary) ->
@@ -2677,7 +2694,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
tls_cipher_texts = [],
user_application = {Monitor, User},
user_data_buffer = <<>>,
- log_alert = true,
session_cache_cb = SessionCacheCb,
renegotiation = {false, first},
start_or_recv_from = undefined,
@@ -2778,12 +2794,11 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{socket = Socket, transport_cb = Transport,
- start_or_recv_from = From, host = Host,
+ #state{socket = Socket, transport_cb = Transport, ssl_options = SslOpts, start_or_recv_from = From, host = Host,
port = Port, session = Session, user_application = {_Mon, Pid},
- log_alert = Log, role = Role, socket_options = Opts} = State) ->
+ role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
- log_alert(Log, StateName, Alert),
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
@@ -2793,21 +2808,21 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
{stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{log_alert = Log, renegotiation = {true, internal}} = State) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
{stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{log_alert = Log, renegotiation = {true, From}} = State0) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
gen_fsm:reply(From, {error, renegotiation_rejected}),
{Record, State} = next_record(State0),
next_state(StateName, connection, Record, State);
handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
- #state{log_alert = Log} = State0) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
{Record, State} = next_record(State0),
next_state(StateName, StateName, Record, State).
@@ -2845,7 +2860,7 @@ handle_own_alert(Alert, Version, StateName,
#state{transport_cb = Transport,
socket = Socket,
connection_states = ConnectionStates,
- log_alert = Log} = State) ->
+ ssl_options = SslOpts} = State) ->
try %% Try to tell the other side
{BinMsg, _} =
encode_alert(Alert, Version, ConnectionStates),
@@ -2855,7 +2870,7 @@ handle_own_alert(Alert, Version, StateName,
ignore
end,
try %% Try to tell the local user
- log_alert(Log, StateName, Alert),
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
@@ -2996,11 +3011,6 @@ get_pending_connection_state_prf(CStates, Direction) ->
CS = tls_record:pending_connection_state(CStates, Direction),
CS#connection_state.security_parameters#security_parameters.prf_algorithm.
-connection_hashsign(HashSign = {_, _}, _State) ->
- HashSign;
-connection_hashsign(_, #state{hashsign_algorithm = HashSign}) ->
- HashSign.
-
%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms
%% If the client does not send the signature_algorithms extension, the
%% server MUST do the following:
@@ -3015,12 +3025,18 @@ connection_hashsign(_, #state{hashsign_algorithm = HashSign}) ->
%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
+negotiated_hashsign(undefined, Algo, Version) ->
+ default_hashsign(Version, Algo);
+negotiated_hashsign(HashSign = {_, _}, _, _) ->
+ HashSign.
+
default_hashsign(_Version = {Major, Minor}, KeyExchange)
- when Major == 3 andalso Minor >= 3 andalso
+ when Major >= 3 andalso Minor >= 3 andalso
(KeyExchange == rsa orelse
KeyExchange == dhe_rsa orelse
KeyExchange == dh_rsa orelse
KeyExchange == ecdhe_rsa orelse
+ KeyExchange == ecdh_rsa orelse
KeyExchange == srp_rsa) ->
{sha, rsa};
default_hashsign(_Version, KeyExchange)
@@ -3028,12 +3044,12 @@ default_hashsign(_Version, KeyExchange)
KeyExchange == dhe_rsa;
KeyExchange == dh_rsa;
KeyExchange == ecdhe_rsa;
+ KeyExchange == ecdh_rsa;
KeyExchange == srp_rsa ->
{md5sha, rsa};
default_hashsign(_Version, KeyExchange)
when KeyExchange == ecdhe_ecdsa;
- KeyExchange == ecdh_ecdsa;
- KeyExchange == ecdh_rsa ->
+ KeyExchange == ecdh_ecdsa ->
{sha, ecdsa};
default_hashsign(_Version, KeyExchange)
when KeyExchange == dhe_dss;
@@ -3082,3 +3098,13 @@ select_curve(#state{client_ecc = {[Curve|_], _}}) ->
{namedCurve, Curve};
select_curve(_) ->
{namedCurve, ?secp256k1}.
+
+is_anonymous(Algo) when Algo == dh_anon;
+ Algo == ecdh_anon;
+ Algo == psk;
+ Algo == dhe_psk;
+ Algo == rsa_psk;
+ Algo == srp_anon ->
+ true;
+is_anonymous(_) ->
+ false.