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.erl486
1 files changed, 471 insertions, 15 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 8f4fd88d42..fa64915fd0 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
@@ -73,7 +75,6 @@
session_cache, %
session_cache_cb, %
negotiated_version, % tls_version()
- supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
hashsign_algorithm, % atom as defined by cipher_suite
@@ -81,6 +82,9 @@
private_key, % PKIX: #'RSAPrivateKey'{}
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()
@@ -472,6 +476,13 @@ abbreviated(#finished{verify_data = Data} = Finished,
handle_own_alert(Alert, Version, abbreviated, State)
end;
+%% only allowed to send next_protocol message after change cipher spec
+%% & before finished message and it is not allowed during renegotiation
+abbreviated(#next_protocol{selected_protocol = SelectedProtocol},
+ #state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
+ {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
+ next_state(abbreviated, abbreviated, Record, State);
+
abbreviated(timeout, State) ->
{ next_state, abbreviated, State, hibernate };
@@ -522,7 +533,9 @@ certify(#certificate{} = Cert,
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 ->
+ when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon;
+ 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),
@@ -539,6 +552,45 @@ certify(#certificate_request{}, State0) ->
{Record, State} = next_record(State0#state{client_certificate_requested = true}),
next_state(certify, certify, Record, State);
+%% PSK and RSA_PSK might bypass the Server-Key-Exchange
+certify(#server_hello_done{},
+ #state{session = #session{master_secret = undefined},
+ negotiated_version = Version,
+ psk_identity = PSKIdentity,
+ premaster_secret = undefined,
+ role = client,
+ key_algorithm = Alg} = State0)
+ when Alg == psk ->
+ case server_psk_master_secret(PSKIdentity, State0) of
+ #state{} = State ->
+ client_certify_and_key_exchange(State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify, State0)
+ end;
+
+certify(#server_hello_done{},
+ #state{session = #session{master_secret = undefined},
+ ssl_options = SslOpts,
+ negotiated_version = Version,
+ psk_identity = PSKIdentity,
+ premaster_secret = undefined,
+ role = client,
+ key_algorithm = Alg} = State0)
+ when Alg == rsa_psk ->
+ case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of
+ {ok, PSK} when is_binary(PSK) ->
+ PremasterSecret = make_premaster_secret(Version, rsa),
+ Len = byte_size(PSK),
+ RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>,
+ State1 = State0#state{premaster_secret = PremasterSecret},
+ State = master_from_premaster_secret(RealPMS, State1),
+ client_certify_and_key_exchange(State);
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ end;
+
%% Master secret was determined with help of server-key exchange msg
certify(#server_hello_done{},
#state{session = #session{master_secret = MasterSecret} = Session,
@@ -625,6 +677,60 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl
next_state(certify, cipher, Record, State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, certify, State0)
+ end;
+
+certify_client_key_exchange(#client_psk_identity{identity = ClientPSKIdentity},
+ #state{negotiated_version = Version} = State0) ->
+ case server_psk_master_secret(ClientPSKIdentity, 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;
+
+certify_client_key_exchange(#client_dhe_psk_identity{
+ identity = ClientPSKIdentity,
+ dh_public = ClientPublicDhKey},
+ #state{negotiated_version = Version,
+ diffie_hellman_params = #'DHParameter'{prime = P,
+ base = G},
+ diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) ->
+ case dhe_psk_master_secret(ClientPSKIdentity, crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, 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;
+
+certify_client_key_exchange(#client_rsa_psk_identity{
+ identity = PskIdentity,
+ exchange_keys =
+ #encrypted_premaster_secret{premaster_secret= EncPMS}},
+ #state{negotiated_version = Version,
+ private_key = Key} = State0) ->
+ PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key),
+ case server_rsa_psk_master_secret(PskIdentity, PremasterSecret, 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;
+
+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.
%%--------------------------------------------------------------------
@@ -656,11 +762,10 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
handle_own_alert(Alert, Version, cipher, State0)
end;
-% client must send a next protocol message if we are expecting it
+%% client must send a next protocol message if we are expecting it
cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
next_protocol = undefined, negotiated_version = Version} = State0) ->
- handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0),
- {stop, normal, State0};
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
cipher(#finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
@@ -682,8 +787,8 @@ cipher(#finished{verify_data = Data} = Finished,
handle_own_alert(Alert, Version, cipher, State)
end;
-% only allowed to send next_protocol message after change cipher spec
-% & before finished message and it is not allowed during renegotiation
+%% only allowed to send next_protocol message after change cipher spec
+%% & before finished message and it is not allowed during renegotiation
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
{Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
@@ -1146,7 +1251,9 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, client) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
+ %% Ignoring potential proxy-certificates see:
+ %% http://dev.globus.org/wiki/Security/ProxyFileFormat
+ [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert}
catch _Error:_Reason ->
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined}
@@ -1154,7 +1261,7 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CertFile, server) ->
try
- [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
+ [OwnCert|_] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
_:Reason ->
@@ -1439,7 +1546,8 @@ server_hello_done(#state{transport_cb = Transport,
State#state{connection_states = ConnectionStates,
tls_handshake_history = Handshake}.
-certify_server(#state{key_algorithm = dh_anon} = State) ->
+certify_server(#state{key_algorithm = Algo} = State)
+ when Algo == dh_anon; Algo == psk; Algo == dhe_psk; Algo == srp_anon ->
State;
certify_server(#state{transport_cb = Transport,
@@ -1494,6 +1602,128 @@ key_exchange(#state{role = server, key_algorithm = Algo,
diffie_hellman_keys = Keys,
tls_handshake_history = Handshake};
+key_exchange(#state{role = server, key_algorithm = psk,
+ ssl_options = #ssl_options{psk_identity = undefined}} = State) ->
+ State;
+key_exchange(#state{role = server, key_algorithm = psk,
+ ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ hashsign_algorithm = HashSignAlgo,
+ private_key = PrivateKey,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_history = Handshake0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State) ->
+ 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, {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
+
+key_exchange(#state{role = server, key_algorithm = dhe_psk,
+ ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ hashsign_algorithm = HashSignAlgo,
+ diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params,
+ private_key = PrivateKey,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_history = Handshake0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State) ->
+ Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]),
+ 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, {dhe_psk, PskIdentityHint, Keys, Params,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ diffie_hellman_keys = Keys,
+ tls_handshake_history = Handshake};
+
+key_exchange(#state{role = server, key_algorithm = rsa_psk,
+ ssl_options = #ssl_options{psk_identity = undefined}} = State) ->
+ State;
+key_exchange(#state{role = server, key_algorithm = rsa_psk,
+ ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ hashsign_algorithm = HashSignAlgo,
+ private_key = PrivateKey,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_history = Handshake0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State) ->
+ 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, {psk, PskIdentityHint,
+ HashSignAlgo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ 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,
@@ -1523,6 +1753,68 @@ 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,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ key_algorithm = psk,
+ negotiated_version = Version,
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = ssl_handshake:key_exchange(client, Version, {psk, SslOpts#ssl_options.psk_identity}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
+
+key_exchange(#state{role = client,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ key_algorithm = dhe_psk,
+ negotiated_version = Version,
+ diffie_hellman_keys = {DhPubKey, _},
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = ssl_handshake:key_exchange(client, Version, {dhe_psk, SslOpts#ssl_options.psk_identity, DhPubKey}),
+ {BinMsg, ConnectionStates, Handshake} =
+ encode_handshake(Msg, Version, ConnectionStates0, Handshake0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates,
+ tls_handshake_history = Handshake};
+
+key_exchange(#state{role = client,
+ ssl_options = SslOpts,
+ connection_states = ConnectionStates0,
+ key_algorithm = rsa_psk,
+ public_key_info = PublicKeyInfo,
+ negotiated_version = Version,
+ premaster_secret = PremasterSecret,
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_history = Handshake0} = State) ->
+ Msg = rsa_psk_key_exchange(Version, SslOpts#ssl_options.psk_identity, PremasterSecret, PublicKeyInfo),
+ {BinMsg, ConnectionStates, Handshake} =
+ 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 = {ClientPubKey, _},
+ 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, ClientPubKey}),
+ {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, _, _})
@@ -1541,6 +1833,22 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
rsa_key_exchange(_, _, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
+rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+ when Algorithm == ?rsaEncryption;
+ Algorithm == ?md2WithRSAEncryption;
+ Algorithm == ?md5WithRSAEncryption;
+ Algorithm == ?sha1WithRSAEncryption;
+ Algorithm == ?sha224WithRSAEncryption;
+ Algorithm == ?sha256WithRSAEncryption;
+ Algorithm == ?sha384WithRSAEncryption;
+ Algorithm == ?sha512WithRSAEncryption
+ ->
+ ssl_handshake:key_exchange(client, Version,
+ {psk_premaster_secret, PskIdentity, PremasterSecret,
+ PublicKeyInfo});
+rsa_psk_key_exchange(_, _, _, _) ->
+ throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
+
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
cert_db = CertDbHandle,
@@ -1659,7 +1967,23 @@ verify_server_key(#server_key_params{params = Params,
server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey},
State) ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State).
+ dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
+
+server_master_secret(#server_psk_params{
+ hint = IdentityHint},
+ State) ->
+ %% store for later use
+ State#state{psk_identity = IdentityHint};
+
+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);
+
+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,
@@ -1689,6 +2013,131 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) ->
[PMpint, GMpint]),
master_from_premaster_secret(PremasterSecret, State).
+handle_psk_identity(_PSKIdentity, LookupFun)
+ when LookupFun == undefined ->
+ error;
+handle_psk_identity(PSKIdentity, {Fun, UserState}) ->
+ Fun(psk, PSKIdentity, UserState).
+
+server_psk_master_secret(ClientPSKIdentity,
+ #state{ssl_options = SslOpts} = State) ->
+ case handle_psk_identity(ClientPSKIdentity, SslOpts#ssl_options.user_lookup_fun) of
+ {ok, PSK} when is_binary(PSK) ->
+ Len = byte_size(PSK),
+ PremasterSecret = <<?UINT16(Len), 0:(Len*8), ?UINT16(Len), PSK/binary>>,
+ master_from_premaster_secret(PremasterSecret, State);
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ end.
+
+dhe_psk_master_secret(PSKIdentity, Prime, Base, PublicDhKey, undefined, State) ->
+ PMpint = mpint_binary(Prime),
+ GMpint = mpint_binary(Base),
+ Keys = {_, PrivateDhKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey,
+ State#state{diffie_hellman_keys = Keys});
+
+dhe_psk_master_secret(PSKIdentity, PMpint, GMpint, PublicDhKey, PrivateDhKey,
+ #state{ssl_options = SslOpts} = State) ->
+ case handle_psk_identity(PSKIdentity, SslOpts#ssl_options.user_lookup_fun) of
+ {ok, PSK} when is_binary(PSK) ->
+ DHSecret =
+ crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
+ [PMpint, GMpint]),
+ DHLen = erlang:byte_size(DHSecret),
+ Len = erlang:byte_size(PSK),
+ PremasterSecret = <<?UINT16(DHLen), DHSecret/binary, ?UINT16(Len), PSK/binary>>,
+ master_from_premaster_secret(PremasterSecret, State);
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)
+ end.
+
+server_rsa_psk_master_secret(PskIdentity, PremasterSecret,
+ #state{ssl_options = SslOpts} = State) ->
+ case handle_psk_identity(PskIdentity, SslOpts#ssl_options.user_lookup_fun) of
+ {ok, PSK} when is_binary(PSK) ->
+ Len = byte_size(PSK),
+ RealPMS = <<?UINT16(48), PremasterSecret/binary, ?UINT16(Len), PSK/binary>>,
+ master_from_premaster_secret(RealPMS, State);
+ #alert{} = Alert ->
+ Alert;
+ _ ->
+ ?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) ->
+ case crypto:srp_generate_key(Verifier, Generator, Prime, '6a') of
+ error ->
+ generate_srp_server_keys(SrpParams, N+1);
+ Keys ->
+ Keys
+ end.
+
+generate_srp_client_keys(_Generator, _Prime, 10) ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+generate_srp_client_keys(Generator, Prime, N) ->
+
+ case crypto:srp_generate_key(Generator, Prime, '6a') of
+ error ->
+ generate_srp_client_keys(Generator, Prime, N+1);
+ Keys ->
+ Keys
+ end.
+
+handle_srp_identity(Username, {Fun, UserState}) ->
+ case Fun(srp, Username, UserState) of
+ {ok, {SRPParams, Salt, DerivedKey}}
+ when is_atom(SRPParams), is_binary(Salt), is_binary(DerivedKey) ->
+ {Generator, Prime} = ssl_srp_primes:get_srp_params(SRPParams),
+ Verifier = crypto:mod_exp_prime(Generator, DerivedKey, 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, ClientPub, State = #state{srp_keys = {ServerPub, ServerPriv}}) ->
+ case crypto:srp_compute_key(Verifier, Prime, ClientPub, ServerPub, ServerPriv, '6a') of
+ error ->
+ ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER);
+ PremasterSecret ->
+ master_from_premaster_secret(PremasterSecret, State)
+ end.
+
+client_srp_master_secret(_Generator, _Prime, _Salt, _ServerPub, #alert{} = Alert, _State) ->
+ Alert;
+client_srp_master_secret(Generator, Prime, Salt, ServerPub, undefined, State) ->
+ Keys = generate_srp_client_keys(Generator, Prime, 0),
+ client_srp_master_secret(Generator, Prime, Salt, ServerPub, Keys, State#state{srp_keys = Keys});
+
+client_srp_master_secret(Generator, Prime, Salt, ServerPub, {ClientPub, ClientPriv},
+ #state{ssl_options = SslOpts} = State) ->
+ case ssl_srp_primes:check_srp_params(Generator, Prime) of
+ ok ->
+ {Username, Password} = SslOpts#ssl_options.srp_identity,
+ DerivedKey = crypto:sha([Salt, crypto:sha([Username, <<$:>>, Password])]),
+
+ case crypto:srp_compute_key(DerivedKey, Prime, Generator, ClientPub, ClientPriv, ServerPub, '6a') 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,
@@ -2488,19 +2937,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 ->
+ when KeyExchange == dh_anon;
+ KeyExchange == psk;
+ KeyExchange == dhe_psk;
+ KeyExchange == rsa_psk;
+ KeyExchange == srp_anon ->
{null, anon}.
start_or_recv_cancel_timer(infinity, _RecvFrom) ->