aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_connection.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_connection.erl')
-rw-r--r--lib/ssl/src/ssl_connection.erl167
1 files changed, 161 insertions, 6 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index e3ddce85e1..e752b70d18 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -34,6 +34,8 @@
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
+-include("ssl_srp.hrl").
+-include("ssl_srp_primes.hrl").
-include_lib("public_key/include/public_key.hrl").
%% Internal application API
@@ -81,6 +83,8 @@
diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
diffie_hellman_keys, % {PublicKey, PrivateKey}
psk_identity, % binary() - server psk identity hint
+ srp_params, % #srp_user{}
+ srp_keys, % {PublicKey, PrivateKey}
premaster_secret, %
file_ref_db, % ets()
cert_db_ref, % ref()
@@ -530,7 +534,8 @@ certify(#server_key_exchange{} = KeyExchangeMsg,
#state{role = client, negotiated_version = Version,
key_algorithm = Alg} = State0)
when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon;
- Alg == psk; Alg == dhe_psk; Alg == rsa_psk ->
+ Alg == psk; Alg == dhe_psk; Alg == rsa_psk;
+ Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->
case handle_server_key(KeyExchangeMsg, State0) of
#state{} = State1 ->
{Record, State} = next_record(State1),
@@ -712,6 +717,20 @@ certify_client_key_exchange(#client_rsa_psk_identity{
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State0)
+ end;
+
+certify_client_key_exchange(#client_srp_public{srp_a = ClientPublicKey},
+ #state{negotiated_version = Version,
+ srp_params =
+ #srp_user{prime = Prime,
+ verifier = Verifier}
+ } = State0) ->
+ case server_srp_master_secret(Verifier, Prime, ClientPublicKey, State0) of
+ #state{} = State1 ->
+ {Record, State} = next_record(State1),
+ next_state(certify, cipher, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify, State0)
end.
%%--------------------------------------------------------------------
@@ -1667,6 +1686,44 @@ key_exchange(#state{role = server, key_algorithm = rsa_psk,
State#state{connection_states = ConnectionStates,
tls_handshake_history = Handshake};
+key_exchange(#state{role = server, key_algorithm = Algo,
+ ssl_options = #ssl_options{user_lookup_fun = LookupFun},
+ hashsign_algorithm = HashSignAlgo,
+ session = #session{srp_username = Username},
+ private_key = PrivateKey,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_history = Handshake0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State)
+ when Algo == srp_dss;
+ Algo == srp_rsa;
+ Algo == srp_anon ->
+ SrpParams = handle_srp_identity(Username, LookupFun),
+ Keys = case generate_srp_server_keys(SrpParams, 0) of
+ Alert = #alert{} ->
+ throw(Alert);
+ Keys0 = {_,_} ->
+ Keys0
+ end,
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates0, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ Msg = ssl_handshake:key_exchange(server, Version, {srp, Keys, SrpParams,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ srp_params = SrpParams,
+ srp_keys = Keys,
+ tls_handshake_history = Handshake};
+
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = rsa,
@@ -1741,6 +1798,23 @@ key_exchange(#state{role = client,
encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
+
+key_exchange(#state{role = client,
+ connection_states = ConnectionStates0,
+ key_algorithm = Algorithm,
+ negotiated_version = Version,
+ srp_keys = {ClntPubKey, _},
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_history = Handshake0} = State)
+ when Algorithm == srp_dss;
+ Algorithm == srp_rsa;
+ Algorithm == srp_anon ->
+ Msg = ssl_handshake:key_exchange(client, Version, {srp, ClntPubKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
tls_handshake_history = Handshake}.
rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
@@ -1905,7 +1979,11 @@ server_master_secret(#server_dhe_psk_params{
hint = IdentityHint,
dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}},
State) ->
- dhe_psk_master_secret(IdentityHint, P, G, ServerPublicDhKey, undefined, State).
+ dhe_psk_master_secret(IdentityHint, P, G, ServerPublicDhKey, undefined, State);
+
+server_master_secret(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
+ State) ->
+ client_srp_master_secret(G, N, S, B, undefined, State).
master_from_premaster_secret(PremasterSecret,
#state{session = Session,
@@ -1992,6 +2070,79 @@ server_rsa_psk_master_secret(PskIdentity, PremasterSecret,
?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
end.
+generate_srp_server_keys(_SrpParams, 10) ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+generate_srp_server_keys(SrpParams =
+ #srp_user{generator = Generator, prime = Prime,
+ verifier = Verifier}, N) ->
+ Private = ssl:random_bytes(32),
+ Multiplier = crypto:srp6a_multiplier(Generator, Prime),
+ case crypto:srp_value_B(Multiplier, Verifier, Generator, Private, Prime) of
+ error ->
+ generate_srp_server_keys(SrpParams, N+1);
+ Public -> {Public, Private}
+ end.
+
+generate_srp_client_keys(_Generator, _Prime, 10) ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+generate_srp_client_keys(Generator, Prime, N) ->
+ Private = ssl:random_bytes(32),
+ case crypto:srp_mod_exp(Generator, Private, Prime) of
+ error ->
+ generate_srp_client_keys(Generator, Prime, N+1);
+ Public -> {Public, Private}
+ end.
+
+handle_srp_identity(Username, {Fun, UserState}) ->
+ case Fun(srp, Username, UserState) of
+ {ok, {SRPParams, Salt, UserPassHash}}
+ when is_atom(SRPParams), is_binary(Salt), is_binary(UserPassHash) ->
+ {Generator, Prime} = ssl_srp_primes:get_srp_params(SRPParams),
+ Verifier = crypto:srp_mod_exp(Generator, UserPassHash, Prime),
+ #srp_user{generator = Generator, prime = Prime,
+ salt = Salt, verifier = Verifier};
+ #alert{} = Alert ->
+ throw(Alert);
+ _ ->
+ throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER))
+ end.
+
+server_srp_master_secret(Verifier, Prime, ClntPub, State = #state{srp_keys = {SrvrPub, SrvrPriv}}) ->
+ U = crypto:srp6_value_u(ClntPub, SrvrPub, Prime),
+ case crypto:srp_server_secret(Verifier, SrvrPriv, U, ClntPub, Prime) of
+ error ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ PremasterSecret ->
+ master_from_premaster_secret(PremasterSecret, State)
+ end.
+
+client_srp_master_secret(_Generator, _Prime, _Salt, _SrvrPub, #alert{} = Alert, _State) ->
+ Alert;
+client_srp_master_secret(Generator, Prime, Salt, SrvrPub, undefined, State) ->
+ Keys = generate_srp_client_keys(Generator, Prime, 0),
+ client_srp_master_secret(Generator, Prime, Salt, SrvrPub, Keys, State#state{srp_keys = Keys});
+
+client_srp_master_secret(Generator, Prime, Salt, SrvrPub, {ClntPub, ClntPriv},
+ #state{ssl_options = SslOpts} = State) ->
+ case ssl_srp_primes:check_srp_params(Generator, Prime) of
+ ok ->
+ {Username, Password} = SslOpts#ssl_options.srp_identity,
+ UserPassHash = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]),
+
+ Multiplier = crypto:srp6a_multiplier(Generator, Prime),
+ U = crypto:srp6_value_u(ClntPub, SrvrPub, Prime),
+ case crypto:srp_client_secret(ClntPriv, U, SrvrPub, Multiplier,
+ Generator, UserPassHash, Prime) of
+ error ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ PremasterSecret ->
+
+ master_from_premaster_secret(PremasterSecret, State)
+ end;
+ _ ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ end.
+
cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
next_state_connection(cipher, ack_connection(State#state{session = Session,
@@ -2791,22 +2942,26 @@ default_hashsign(_Version = {Major, Minor}, KeyExchange)
when Major == 3 andalso Minor >= 3 andalso
(KeyExchange == rsa orelse
KeyExchange == dhe_rsa orelse
- KeyExchange == dh_rsa) ->
+ KeyExchange == dh_rsa orelse
+ KeyExchange == srp_rsa) ->
{sha, rsa};
default_hashsign(_Version, KeyExchange)
when KeyExchange == rsa;
KeyExchange == dhe_rsa;
- KeyExchange == dh_rsa ->
+ KeyExchange == dh_rsa;
+ KeyExchange == srp_rsa ->
{md5sha, rsa};
default_hashsign(_Version, KeyExchange)
when KeyExchange == dhe_dss;
- KeyExchange == dh_dss ->
+ KeyExchange == dh_dss;
+ KeyExchange == srp_dss ->
{sha, dsa};
default_hashsign(_Version, KeyExchange)
when KeyExchange == dh_anon;
KeyExchange == psk;
KeyExchange == dhe_psk;
- KeyExchange == rsa_psk ->
+ KeyExchange == rsa_psk;
+ KeyExchange == srp_anon ->
{null, anon}.
start_or_recv_cancel_timer(infinity, _RecvFrom) ->