aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/diameter/src/base/diameter_service.erl13
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl208
-rw-r--r--lib/ssl/src/ssl_connection.erl112
-rw-r--r--lib/ssl/src/ssl_handshake.erl188
-rw-r--r--lib/ssl/src/ssl_handshake.hrl9
5 files changed, 347 insertions, 183 deletions
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index c0fccd8080..b5584ca0d0 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -2060,9 +2060,15 @@ request_cb({eval, RC, F}, App, Mask, T, TC, Fs, Pkt) ->
%% protocol_error/5
protocol_error(RC, {_, OH, OR}, TPid, Fs, Pkt) ->
- #diameter_packet{avps = Avps} = Pkt,
+ #diameter_packet{avps = Avps, errors = Es} = Pkt,
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Fs, Pkt).
+ reply(answer_message({OH, OR, RC}, Avps),
+ ?BASE,
+ TPid,
+ Fs,
+ Pkt#diameter_packet{errors = [RC | Es]}).
+%% Note that reply/5 may set the result code once more. It's set in
+%% answer_message/2 in case reply/5 doesn't.
%% protocol_error/4
@@ -2175,7 +2181,8 @@ is_loop(Code, Vid, OH, Avps) ->
%%
%% Send a locally originating reply.
-%% Skip the setting of Result-Code and Failed-AVP's below.
+%% Skip the setting of Result-Code and Failed-AVP's below. This is
+%% currently undocumented.
reply([Msg], Dict, TPid, Fs, Pkt)
when is_list(Msg);
is_tuple(Msg) ->
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index b41d1a6f5c..b03a9ce4d1 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -40,6 +40,7 @@
send_nok/1,
send_eval/1,
send_bad_answer/1,
+ send_protocol_error/1,
send_arbitrary/1,
send_unknown/1,
send_unknown_mandatory/1,
@@ -48,6 +49,9 @@
send_unsupported_app/1,
send_error_bit/1,
send_unsupported_version/1,
+ send_invalid_avp_bits/1,
+ send_invalid_avp_length/1,
+ send_invalid_reject/1,
send_long/1,
send_nopeer/1,
send_noapp/1,
@@ -113,12 +117,15 @@
%% Sequence mask for End-to-End and Hop-by-Hop identifiers.
-define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits
-%% Run tests cases in different encoding variants. Send outgoing
-%% messages as lists or records.
+%% How to construct messages, as record or list.
-define(ENCODINGS, [list, record]).
-%% Identifers for client connections.
--define(CONNECTIONS, [c1,c2,c3]).
+%% How to send answers, in a diameter_packet or not.
+-define(CONTAINERS, [pkt, msg]).
+
+%% Send over multiple connections that are mapped onto
+%% [{E,P} || E <- ?ENCODINGS, P <- ?CONTAINERS].
+-define(CONNECTIONS, [c0,c1,c2,c3]).
%% Not really what we should be setting unless the message is sent in
%% the common application but diameter doesn't care.
@@ -162,6 +169,8 @@
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED').
-define(UNABLE_TO_DELIVER,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER').
+-define(INVALID_AVP_LENGTH,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_LENGTH').
-define(EVENT_RECORD,
?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD').
@@ -174,6 +183,8 @@
?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
-define(BAD_ANSWER,
?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_BAD_ANSWER').
+-define(USER_MOVED,
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
-define(A, list_to_atom).
-define(L, atom_to_list).
@@ -187,14 +198,17 @@ suite() ->
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, ?util:name([E,C]), P} || E <- ?ENCODINGS,
- C <- ?CONNECTIONS,
- P <- [[], [parallel]]]
+ ++ [{group, ?util:name([R,C,A]), P} || R <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ A <- ?ENCODINGS,
+ P <- [[], [parallel]]]
++ [remove_transports, stop_services, stop].
groups() ->
Ts = tc(),
- [{?util:name([E,C]), [], Ts} || E <- ?ENCODINGS, C <- ?CONNECTIONS].
+ [{?util:name([R,C,A]), [], Ts} || R <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ A <- ?ENCODINGS].
init_per_group(Name, Config) ->
[{group, Name} | Config].
@@ -215,6 +229,7 @@ tc() ->
send_nok,
send_eval,
send_bad_answer,
+ send_protocol_error,
send_arbitrary,
send_unknown,
send_unknown_mandatory,
@@ -223,6 +238,9 @@ tc() ->
send_unsupported_app,
send_error_bit,
send_unsupported_version,
+ send_invalid_avp_bits,
+ send_invalid_avp_length,
+ send_invalid_reject,
send_long,
send_nopeer,
send_noapp,
@@ -265,7 +283,9 @@ start_services(_Config) ->
add_transports(Config) ->
LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]),
- Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C}]) || C <- ?CONNECTIONS],
+ Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C},
+ {capabilities, [osi(C)]}])
+ || C <- ?CONNECTIONS],
?util:write_priv(Config, "transport", [LRef | Cs]).
remove_transports(Config) ->
@@ -283,11 +303,15 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
io:format("connection: ~p -> ~p~n", [DH,OH]),
ok.
+osi(Id) ->
+ [$c,N] = atom_to_list(Id),
+ {'Origin-State-Id', N - $0}.
+
%% ===========================================================================
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -297,13 +321,14 @@ result_codes(_Config) ->
?INVALID_HDR_BITS,
?INVALID_AVP_BITS,
?AVP_UNSUPPORTED,
- ?UNSUPPORTED_VERSION}.
+ ?UNSUPPORTED_VERSION,
+ ?INVALID_AVP_LENGTH}.
%% Send an ACR and expect success.
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
-
+
#diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
= call(Config, Req).
@@ -311,7 +336,7 @@ send_ok(Config) ->
send_nok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 0}],
-
+
#'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
= call(Config, Req).
@@ -331,6 +356,15 @@ send_bad_answer(Config) ->
{'Accounting-Record-Number', 2}],
{error, timeout} = call(Config, Req).
+%% Send an ACR that the server callback answers explicitly with a
+%% protocol error.
+send_protocol_error(Config) ->
+ Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
+ {'Accounting-Record-Number', 4}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
+ = call(Config, Req).
+
%% Send an ASR with an arbitrary AVP and expect success and the same
%% AVP in the reply.
send_arbitrary(Config) ->
@@ -398,6 +432,29 @@ send_unsupported_version(Config) ->
#diameter_base_STA{'Result-Code' = ?UNSUPPORTED_VERSION}
= call(Config, Req).
+%% Send a request containing an incorrect AVP length.
+send_invalid_avp_bits(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
+ = call(Config, Req).
+
+%% Send a request containing an AVP length that doesn't match the
+%% AVP's type.
+send_invalid_avp_length(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+
+ #'diameter_base_STA'{'Result-Code' = ?INVALID_AVP_LENGTH}
+ = call(Config, Req).
+
+%% Send a request containing 5xxx errors that the server rejects with
+%% 3xxx.
+send_invalid_reject(Config) ->
+ Req = ['STR', {'Termination-Cause', ?USER_MOVED}],
+
+ #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
+ = call(Config, Req).
+
%% Send something long that will be fragmented by TCP.
send_long(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
@@ -572,17 +629,38 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- [Encoding, Client] = ?util:name(proplists:get_value(group, Config)),
+ [Encoding, C, E] = ?util:name(proplists:get_value(group, Config)),
diameter:call(?CLIENT,
dict(Req),
- req(Req, Encoding),
- [{extra, [Name, Client]} | Opts]).
+ msg(Req, Encoding),
+ [{extra, [Name, client(E,C)]} | Opts]).
+
+client(E, C) ->
+ list_to_atom([$c, $0 + 2*codec(E) + container(C)]).
+
+client(N) ->
+ {codec(N bsr 1), container(N rem 2)}.
+
+codec(record) -> 0;
+codec(list) -> 1;
+codec(0) -> record;
+codec(1) -> list.
-req(['ACR' = H | T], record) ->
+%% Here we're just mapping booleans but the readable atoms are part of
+%% (constructed) group names, so it's good that they're readable.
+
+container(pkt) -> 0;
+container(msg) -> 1;
+container(0) -> pkt;
+container(1) -> msg.
+
+msg([H|T], record)
+ when H == 'ACR';
+ H == 'ACA' ->
?ACCT:'#new-'(?ACCT:msg2rec(H), T);
-req([H|T], record) ->
+msg([H|T], record) ->
?BASE:'#new-'(?BASE:msg2rec(H), T);
-req(T, _) ->
+msg(T, _) ->
T.
dict(['ACR' | _]) ->
@@ -662,6 +740,40 @@ prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _, _) ->
log(#diameter_packet{} = P, T) ->
io:format("~p: ~p~n", [T,P]).
+%% prepare/3
+
+prepare(Pkt, Caps, send_invalid_avp_bits) ->
+ Req = prepare(Pkt, Caps),
+ %% Last AVP in our STR is Termination-Cause of type Unsigned32:
+ %% set its length improperly.
+ #diameter_packet{header = #diameter_header{length = L},
+ bin = B}
+ = E
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ Offset = L - 7, %% to AVP Length
+ <<H:Offset/binary, 12:24/integer, T:4/binary>> = B,
+ E#diameter_packet{bin = <<H/binary, 13:24/integer, T/binary>>};
+
+prepare(Pkt, Caps, N)
+ when N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Req = prepare(Pkt, Caps),
+ %% Second last AVP in our STR is Auth-Application-Id of type
+ %% Unsigned32: Send a value of length 8.
+ #diameter_packet{header = #diameter_header{length = L},
+ bin = B0}
+ = E
+ = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ Offset = L - 7 - 12, %% to AVP Length
+ <<H0:Offset/binary, 12:24/integer, T:16/binary>> = B0,
+ <<V, L:24/integer, H/binary>> = H0, %% assert
+ E#diameter_packet{bin = <<V,
+ (L+4):24/integer,
+ H/binary,
+ 16:24/integer,
+ 0:32/integer,
+ T/binary>>};
+
prepare(Pkt, Caps, send_unsupported) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
@@ -693,6 +805,8 @@ prepare(Pkt, Caps, send_anything) ->
prepare(Pkt, Caps, _Name) ->
prepare(Pkt, Caps).
+%% prepare/2
+
prepare(#diameter_packet{msg = Req}, Caps)
when is_record(Req, diameter_base_accounting_ACR);
'ACR' == hd(Req) ->
@@ -757,10 +871,17 @@ handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, _Id, {Pid, Ref}) ->
Pid ! {Ref, Pkt}.
answer(Pkt, Req, _Peer, Name) ->
- #diameter_packet{header = H, msg = Rec, errors = []} = Pkt,
+ #diameter_packet{header = H, msg = Rec, errors = Es} = Pkt,
ApplId = app(Req, Name),
#diameter_header{application_id = ApplId} = H, %% assert
-
+ answer(Rec, Es, Name).
+
+answer(Rec, [_|_], N)
+ when N == send_invalid_avp_bits;
+ N == send_invalid_avp_length;
+ N == send_invalid_reject ->
+ Rec;
+answer(Rec, [], _) ->
Rec.
app(_, send_unsupported_app) ->
@@ -786,7 +907,17 @@ handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) ->
{V,B} = ?CLIENT_MASK,
V = EI bsr B, %% assert
V = HI bsr B, %%
- request(M, Caps).
+ #diameter_caps{origin_state_id = {_,[N]}} = Caps,
+ answer(client(N), request(M, Caps)).
+
+answer(T, {Tag, Action, Post}) ->
+ {Tag, answer(T, Action), Post};
+answer({E,C}, {reply, Ans}) ->
+ answer(C, {reply, msg(Ans, E)});
+answer(pkt, {reply, Ans}) ->
+ {reply, #diameter_packet{msg = Ans}};
+answer(_, T) ->
+ T.
%% send_nok
request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
@@ -806,7 +937,7 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}],
- {reply, #diameter_packet{header = #diameter_header{is_error = true},%% not
+ {reply, #diameter_packet{header = #diameter_header{is_error = true},%% NOT
msg = Ans}};
%% send_eval
@@ -836,15 +967,28 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}]};
+%% send_protocol_error
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}],
+ {reply, Ans};
+
request(#diameter_base_ASR{'Session-Id' = SId,
'AVP' = Avps},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
- {reply, #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'Session-Id' = SId,
- 'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'AVP' = Avps}};
+ {reply, ['ASA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'AVP', Avps}]};
+
+%% send_invalid_reject
+request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED}, _Caps) ->
+ {protocol_error, ?TOO_BUSY};
%% send_noreply
request(#diameter_base_STR{'Termination-Cause' = T},
@@ -867,10 +1011,10 @@ request(#diameter_base_STR{'Destination-Host'= [H]},
request(#diameter_base_STR{'Session-Id' = SId},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
- {reply, #diameter_base_STA{'Result-Code' = ?SUCCESS,
- 'Session-Id' = SId,
- 'Origin-Host' = OH,
- 'Origin-Realm' = OR}};
+ {reply, ['STA', {'Result-Code', ?SUCCESS},
+ {'Session-Id', SId},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}]};
%% send_error
request(#diameter_base_RAR{}, _Caps) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index cde13069b5..94f76e0606 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1628,78 +1628,49 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
ssl_record:set_server_verify_data(current_write, Data, ConnectionStates).
-handle_server_key(#server_key_exchange{params =
- #server_dh_params{dh_p = P,
- dh_g = G,
- dh_y = ServerPublicDhKey},
- signed_params = <<>>},
- #state{key_algorithm = dh_anon} = State) ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
-
-handle_server_key(
- #server_key_exchange{params =
- #server_dh_params{dh_p = P,
- dh_g = G,
- dh_y = ServerPublicDhKey},
- signed_params = Signed,
- hashsign = HashSign},
- #state{negotiated_version = Version,
- public_key_info = PubKeyInfo,
- connection_states = ConnectionStates} = State) ->
-
- PLen = size(P),
- GLen = size(G),
- YLen = size(ServerPublicDhKey),
- HashAlgo = connection_hash_algo(HashSign, State),
+handle_server_key(#server_key_exchange{exchange_keys = Keys},
+ #state{key_algorithm = KeyAlg,
+ negotiated_version = Version} = State) ->
+ Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version),
+ HashSign = connection_hashsign(Params#server_key_params.hashsign, State),
+ case HashSign of
+ {_, anon} ->
+ server_master_secret(Params#server_key_params.params, State);
+ _ ->
+ verify_server_key(Params, HashSign, State)
+ end.
- ConnectionState =
+verify_server_key(#server_key_params{params = Params,
+ params_bin = EncParams,
+ signature = Signature},
+ HashSign = {HashAlgo, _},
+ #state{negotiated_version = Version,
+ public_key_info = PubKeyInfo,
+ connection_states = ConnectionStates} = State) ->
+ ConnectionState =
ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
Hash = ssl_handshake:server_key_exchange_hash(HashAlgo,
- <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen),
- ServerPublicDhKey/binary>>),
-
- case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ EncParams/binary>>),
+ case ssl_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of
true ->
- dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
+ server_master_secret(Params, State);
false ->
?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams})
- when Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey);
-verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) ->
- case public_key:decrypt_public(Signed, PubKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes ->
- true;
- _ ->
- false
- end;
-verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) ->
- public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}).
-
-dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
- PMpint = mpint_binary(Prime),
- GMpint = mpint_binary(Base),
- Keys = {_, PrivateDhKey} =
- crypto:dh_generate_key([PMpint,GMpint]),
- dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
+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(PMpint, GMpint, PublicDhKey, PrivateDhKey,
- #state{session = Session,
- negotiated_version = Version, role = Role,
- connection_states = ConnectionStates0} = State) ->
- PremasterSecret =
- crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
- [PMpint, GMpint]),
+master_from_premaster_secret(PremasterSecret,
+ #state{session = Session,
+ negotiated_version = Version, role = Role,
+ connection_states = ConnectionStates0} = State) ->
case ssl_handshake:master_secret(Version, PremasterSecret,
ConnectionStates0, Role) of
{MasterSecret, ConnectionStates} ->
@@ -1711,6 +1682,19 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
Alert
end.
+dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
+ PMpint = mpint_binary(Prime),
+ GMpint = mpint_binary(Base),
+ Keys = {_, PrivateDhKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
+
+dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) ->
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
+ [PMpint, GMpint]),
+ master_from_premaster_secret(PremasterSecret, State).
+
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,
@@ -2485,10 +2469,10 @@ get_pending_connection_state_prf(CStates, Direction) ->
CS = ssl_record:pending_connection_state(CStates, Direction),
CS#connection_state.security_parameters#security_parameters.prf_algorithm.
-connection_hash_algo({HashAlgo, _}, _State) ->
- HashAlgo;
-connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) ->
- HashAlgo.
+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
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index db21dac942..1929370991 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -32,10 +32,10 @@
-export([master_secret/4, client_hello/8, server_hello/5, hello/4,
hello_request/0, certify/7, certificate/4,
- client_certificate_verify/6, certificate_verify/6,
+ client_certificate_verify/6, certificate_verify/6, verify_signature/5,
certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
finished/5, verify_connection/6, get_tls_handshake/3,
- decode_client_key/3, server_hello_done/0,
+ decode_client_key/3, decode_server_key/3, server_hello_done/0,
encode_handshake/2, init_handshake_history/0, update_handshake_history/2,
decrypt_premaster_secret/2, prf/5, next_protocol/1]).
@@ -320,25 +320,36 @@ client_certificate_verify(OwnCert, MasterSecret, Version,
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of
+certificate_verify(Signature, PublicKeyInfo, Version,
+ HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) ->
+ Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
+ case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of
true ->
valid;
_ ->
- ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
- end;
-certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version,
- {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) ->
- Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake),
- case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of
- true ->
- valid;
- false ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
+%%--------------------------------------------------------------------
+-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(),
+ public_key_info()) -> true | false.
+%%
+%% Description: Checks that a public_key signature is valid.
+%%--------------------------------------------------------------------
+verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) ->
+ true;
+verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams})
+ when Minor >= 3 ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey);
+verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) ->
+ case public_key:decrypt_public(Signature, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash -> true;
+ _ -> false
+ end;
+verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
+
%%--------------------------------------------------------------------
-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) ->
@@ -382,31 +393,33 @@ key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
- {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) ->
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
<<?UINT32(_), GBin/binary>> = crypto:mpint(G),
- PLen = byte_size(PBin),
- GLen = byte_size(GBin),
- YLen = byte_size(PublicKey),
ServerDHParams = #server_dh_params{dh_p = PBin,
dh_g = GBin, dh_y = PublicKey},
+ enc_server_key_exchange(Version, ServerDHParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey).
+enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
+ ClientRandom, ServerRandom, PrivateKey) ->
+ EncParams = enc_server_key(Params),
case HashAlgo of
null ->
- #server_key_exchange{params = ServerDHParams,
- signed_params = <<>>,
- hashsign = {null, anon}};
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {null, anon},
+ signature = <<>>};
_ ->
Hash =
server_key_exchange_hash(HashAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), PBin/binary,
- ?UINT16(GLen), GBin/binary,
- ?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
- #server_key_exchange{params = ServerDHParams,
- signed_params = Signed,
- hashsign = {HashAlgo, SignAlgo}}
+ ServerRandom/binary,
+ EncParams/binary>>),
+ Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey),
+ #server_key_params{params = Params,
+ params_bin = EncParams,
+ hashsign = {HashAlgo, SignAlgo},
+ signature = Signature}
end.
%%--------------------------------------------------------------------
@@ -523,6 +536,15 @@ decode_client_key(ClientKey, Type, Version) ->
dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
+-spec decode_server_key(binary(), key_algo(), tls_version()) ->
+ #server_key_params{}.
+%%
+%% Description: Decode server_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_server_key(ServerKey, Type, Version) ->
+ dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
-spec init_handshake_history() -> tls_handshake_history().
%%
@@ -975,31 +997,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
next_protocol_negotiation = NextProtocolNegotiation};
dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-
-dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = <<>>, hashsign = {null, anon}};
-dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?BYTE(HashAlgo), ?BYTE(SignAlgo),
- ?UINT16(Len), Sig:Len/binary>>)
- when Major == 3, Minor >= 3 ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = Sig,
- hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}};
-dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- ?UINT16(Len), Sig:Len/binary>>) ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
- dh_y = Y},
- signed_params = Sig, hashsign = undefined};
+dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) ->
+ #server_key_exchange{exchange_keys = Keys};
dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary,
@@ -1039,6 +1038,42 @@ dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
#client_diffie_hellman_public{dh_public = DH_Y}.
+dec_ske_params(Len, Keys, Version) ->
+ <<Params:Len/bytes, Signature/binary>> = Keys,
+ dec_ske_signature(Params, Signature, Version).
+
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(0)>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, <<>>};
+dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo),
+ ?UINT16(Len), Signature:Len/binary>>, {Major, Minor})
+ when Major == 3, Minor >= 3 ->
+ HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)},
+ {Params, HashSign, Signature};
+dec_ske_signature(Params, <<>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(0)>>, _) ->
+ {Params, {null, anon}, <<>>};
+dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) ->
+ {Params, undefined, Signature};
+dec_ske_signature(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+dec_server_key(<<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) ->
+ Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(_, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
dec_hello_extensions(<<>>) ->
[];
dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
@@ -1156,18 +1191,12 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
ASN1Certs = certs_from_list(ASN1CertList),
ACLen = erlang:iolist_size(ASN1Certs),
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
-enc_hs(#server_key_exchange{params = #server_dh_params{
- dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams, hashsign = HashSign}, Version) ->
- PLen = byte_size(P),
- GLen = byte_size(G),
- YLen = byte_size(Y),
- Signature = enc_sign(HashSign, SignedParams, Version),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
- ?UINT16(GLen), G/binary,
- ?UINT16(YLen), Y/binary,
- Signature/binary>>
- };
+enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) ->
+ {?SERVER_KEY_EXCHANGE, Keys};
+enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign,
+ signature = Signature}, Version) ->
+ EncSign = enc_sign(HashSign, Signature, Version),
+ {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>};
enc_hs(#certificate_request{certificate_types = CertTypes,
hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos},
certificate_authorities = CertAuths},
@@ -1211,6 +1240,14 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
<<?UINT16(Len), DHPublic/binary>>.
+enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) ->
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>.
+
+enc_sign({_, anon}, _Sign, _Version) ->
+ <<>>;
enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor})
when Major == 3, Minor >= 3->
SignLen = byte_size(Signature),
@@ -1328,8 +1365,8 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) ->
digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 ->
public_key:sign({digest, Hash}, HashAlgo, Key);
-digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) ->
- public_key:sign({digest, Hash}, sha, Key);
+digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key);
digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
[{rsa_pad, rsa_pkcs1_padding}]).
@@ -1378,19 +1415,6 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
{unknown, {SslState, UserState}}
end.
-certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, sha, Signature, PublicKey);
-certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor})
- when Major == 3, Minor >= 3 ->
- public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey);
-certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) ->
- case public_key:decrypt_public(Signature, PublicKey,
- [{rsa_pad, rsa_pkcs1_padding}]) of
- Hashes -> true;
- _ -> false
- end.
-
-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 9af6511d68..2414d5b666 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -141,9 +141,14 @@
}).
-record(server_key_exchange, {
+ exchange_keys
+ }).
+
+-record(server_key_params, {
params, %% #server_rsa_params{} | #server_dh_params{}
- signed_params, %% #signature{}
- hashsign %% term(atom(), atom())
+ params_bin,
+ hashsign, %% term(atom(), atom())
+ signature %% #signature{}
}).
%% enum { anonymous, rsa, dsa } SignatureAlgorithm;