aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/ssl.appup.src7
-rw-r--r--lib/ssl/src/ssl.erl148
-rw-r--r--lib/ssl/src/ssl_alert.erl60
-rw-r--r--lib/ssl/src/ssl_certificate.erl53
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl2
-rw-r--r--lib/ssl/src/ssl_cipher.erl528
-rw-r--r--lib/ssl/src/ssl_cipher.hrl15
-rw-r--r--lib/ssl/src/ssl_connection.erl1590
-rw-r--r--lib/ssl/src/ssl_handshake.erl530
-rw-r--r--lib/ssl/src/ssl_handshake.hrl28
-rw-r--r--lib/ssl/src/ssl_internal.hrl15
-rw-r--r--lib/ssl/src/ssl_record.erl169
-rw-r--r--lib/ssl/src/ssl_record.hrl23
-rw-r--r--lib/ssl/src/ssl_ssl3.erl18
-rw-r--r--lib/ssl/src/ssl_tls1.erl20
15 files changed, 2060 insertions, 1146 deletions
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 3aea2428c9..e8ae6846aa 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -8,7 +8,9 @@
{"3.10.4", [{restart_application, ssl}]},
{"3.10.5", [{restart_application, ssl}]},
{"3.10.6", [{restart_application, ssl}]},
- {"3.10.7", [{restart_application, ssl}]}
+ {"3.10.7", [{restart_application, ssl}]},
+ {"3.10.8", [{restart_application, ssl}]},
+ {"3.10.9", [{restart_application, ssl}]}
],
[
{"3.10", [{restart_application, ssl}]},
@@ -18,6 +20,7 @@
{"3.10.4", [{restart_application, ssl}]},
{"3.10.5", [{restart_application, ssl}]},
{"3.10.6", [{restart_application, ssl}]},
- {"3.10.7", [{restart_application, ssl}]}
+ {"3.10.8", [{restart_application, ssl}]},
+ {"3.10.9", [{restart_application, ssl}]}
]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index de74c91505..95cd92ee60 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -29,13 +29,15 @@
connect/3, connect/2, connect/4, connection_info/1,
controlling_process/2, listen/2, pid/1, peername/1, recv/2, recv/3,
send/2, getopts/2, setopts/2, seed/1, sockname/1, peercert/1,
- peercert/2, version/0, versions/0, session_info/1, format_error/1]).
+ peercert/2, version/0, versions/0, session_info/1, format_error/1,
+ renegotiate/1]).
%% Should be deprecated as soon as old ssl is removed
%%-deprecated({pid, 1, next_major_release}).
-include("ssl_int.hrl").
-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -145,24 +147,28 @@ transport_accept(ListenSocket) ->
transport_accept(ListenSocket, infinity).
transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}},
- fd = new_ssl} = SslSocket, Timeout) ->
+ fd = new_ssl}, Timeout) ->
%% The setopt could have been invoked on the listen socket
%% and options should be inherited.
EmOptions = emulated_options(),
{ok, InetValues} = inet:getopts(ListenSocket, EmOptions),
- {CbModule,_,_} = CbInfo,
- {ok, Socket} = CbModule:accept(ListenSocket, Timeout),
- inet:setopts(Socket, internal_inet_values()),
- {ok, Port} = inet:port(Socket),
- case ssl_connection_sup:start_child([server, "localhost", Port, Socket,
- {SslOpts, socket_options(InetValues)}, self(),
- CbInfo]) of
- {ok, Pid} ->
- CbModule:controlling_process(Socket, Pid),
- {ok, SslSocket#sslsocket{pid = Pid}};
- {error, Reason} ->
- {error, Reason}
+ ok = inet:setopts(ListenSocket, internal_inet_values()),
+ {CbModule,_,_, _} = CbInfo,
+ case CbModule:accept(ListenSocket, Timeout) of
+ {ok, Socket} ->
+ ok = inet:setopts(ListenSocket, InetValues),
+ {ok, Port} = inet:port(Socket),
+ ConnArgs = [server, "localhost", Port, Socket,
+ {SslOpts, socket_options(InetValues)}, self(), CbInfo],
+ case ssl_connection_sup:start_child(ConnArgs) of
+ {ok, Pid} ->
+ ssl_connection:socket_control(Socket, Pid, CbModule);
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
end;
transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
@@ -180,22 +186,9 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
ssl_accept(ListenSocket) ->
ssl_accept(ListenSocket, infinity).
-ssl_accept(#sslsocket{pid = Pid, fd = new_ssl}, Timeout) ->
- gen_fsm:send_event(Pid, socket_control),
- try gen_fsm:sync_send_all_state_event(Pid, started, Timeout) of
- connected ->
- ok;
- {error, _} = Error ->
- Error
- catch
- exit:{noproc, _} ->
- {error, closed};
- exit:{timeout, _} ->
- {error, timeout};
- exit:{normal, _} ->
- {error, closed}
- end;
-
+ssl_accept(#sslsocket{fd = new_ssl} = Socket, Timeout) ->
+ ssl_connection:handshake(Socket, Timeout);
+
ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity);
@@ -211,7 +204,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
try handle_options(SslOptions ++ InetValues, server) of
{ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} ->
{ok, Port} = inet:port(Socket),
- ssl_connection:accept(Port, Socket,
+ ssl_connection:ssl_accept(Port, Socket,
{SslOpts, EmOpts},
self(), CbInfo, Timeout)
catch
@@ -223,7 +216,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
%%
%% Description: Close a ssl connection
%%--------------------------------------------------------------------
-close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}) ->
+close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}) ->
CbMod:close(ListenSocket);
close(#sslsocket{pid = Pid, fd = new_ssl}) ->
ssl_connection:close(Pid);
@@ -232,7 +225,7 @@ close(Socket = #sslsocket{}) ->
ssl_broker:close(Socket).
%%--------------------------------------------------------------------
-%% Function: send(Socket, Data) -> ok
+%% Function: send(Socket, Data) -> ok | {error, Reason}
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
@@ -382,7 +375,7 @@ setopts(#sslsocket{} = Socket, Options) ->
%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
-shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}, How) ->
+shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) ->
CbMod:shutdown(ListenSocket, How);
shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) ->
ssl_connection:shutdown(Pid, How).
@@ -436,6 +429,10 @@ versions() ->
AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+
+renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:renegotiation(Pid).
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -452,7 +449,7 @@ do_new_connect(Address, Port,
#config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts,
emulated=EmOpts,inet_ssl=SocketOpts},
Timeout) ->
- {CbModule, _, _} = CbInfo,
+ {CbModule, _, _, _} = CbInfo,
try CbModule:connect(Address, Port, SocketOpts, Timeout) of
{ok, Socket} ->
ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts},
@@ -474,7 +471,7 @@ old_connect(Address, Port, Options, Timeout) ->
new_listen(Port, Options0) ->
try
{ok, Config} = handle_options(Options0, server),
- #config{cb={CbModule, _, _},inet_user=Options} = Config,
+ #config{cb={CbModule, _, _, _},inet_user=Options} = Config,
case CbModule:listen(Port, Options) of
{ok, ListenSocket} ->
{ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
@@ -509,6 +506,9 @@ handle_options(Opts0, Role) ->
end
end,
+ UserFailIfNoPeerCert = validate_option(fail_if_no_peer_cert,
+ proplists:get_value(fail_if_no_peer_cert, Opts, false)),
+
{Verify, FailIfNoPeerCert, CaCertDefault} =
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
@@ -521,9 +521,7 @@ handle_options(Opts0, Role) ->
verify_none ->
{verify_none, false, ca_cert_default(verify_none, Role)};
verify_peer ->
- {verify_peer, proplists:get_value(fail_if_no_peer_cert,
- Opts, false),
- ca_cert_default(verify_peer, Role)};
+ {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, Role)};
Value ->
throw({error, {eoptions, {verify, Value}}})
end,
@@ -534,28 +532,32 @@ handle_options(Opts0, Role) ->
versions = handle_option(versions, Opts, []),
verify = validate_option(verify, Verify),
verify_fun = handle_option(verify_fun, Opts, VerifyFun),
- fail_if_no_peer_cert = validate_option(fail_if_no_peer_cert,
- FailIfNoPeerCert),
+ fail_if_no_peer_cert = FailIfNoPeerCert,
verify_client_once = handle_option(verify_client_once, Opts, false),
+ validate_extensions_fun = handle_option(validate_extensions_fun, Opts, undefined),
depth = handle_option(depth, Opts, 1),
certfile = CertFile,
keyfile = handle_option(keyfile, Opts, CertFile),
key = handle_option(key, Opts, undefined),
password = handle_option(password, Opts, ""),
cacertfile = handle_option(cacertfile, Opts, CaCertDefault),
+ dhfile = handle_option(dhfile, Opts, undefined),
ciphers = handle_option(ciphers, Opts, []),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
+ secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
+ renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, [])
},
- CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}),
- SslOptions = [versions, verify, verify_fun,
+ CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
+ SslOptions = [versions, verify, verify_fun, validate_extensions_fun,
+ fail_if_no_peer_cert, verify_client_once,
depth, certfile, keyfile,
- key, password, cacertfile, ciphers,
+ key, password, cacertfile, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cd_info],
+ cb_info, renegotiate_at, secure_renegotiate],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -585,6 +587,9 @@ validate_option(fail_if_no_peer_cert, Value)
validate_option(verify_client_once, Value)
when Value == true; Value == false ->
Value;
+
+validate_option(validate_extensions_fun, Value) when Value == undefined; is_function(Value) ->
+ Value;
validate_option(depth, Value) when is_integer(Value),
Value >= 0, Value =< 255->
Value;
@@ -605,11 +610,17 @@ validate_option(cacertfile, undefined) ->
"";
validate_option(cacertfile, Value) when is_list(Value), Value =/= "" ->
Value;
+validate_option(dhfile, undefined = Value) ->
+ Value;
+validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
+ Value;
validate_option(ciphers, Value) when is_list(Value) ->
Version = ssl_record:highest_protocol_version([]),
try cipher_suites(Version, Value)
catch
exit:_ ->
+ throw({error, {eoptions, {ciphers, Value}}});
+ error:_->
throw({error, {eoptions, {ciphers, Value}}})
end;
validate_option(reuse_session, Value) when is_function(Value) ->
@@ -617,6 +628,13 @@ validate_option(reuse_session, Value) when is_function(Value) ->
validate_option(reuse_sessions, Value) when Value == true;
Value == false ->
Value;
+
+validate_option(secure_renegotiate, Value) when Value == true;
+ Value == false ->
+ Value;
+validate_option(renegotiate_at, Value) when is_integer(Value) ->
+ min(Value, ?DEFAULT_RENEGOTIATE_AT);
+
validate_option(debug, Value) when is_list(Value); Value == true ->
Value;
validate_option(Opt, Value) ->
@@ -628,7 +646,7 @@ validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
validate_versions(Rest, Versions);
-validate_versions(Ver, Versions) ->
+validate_versions([Ver| _], Versions) ->
throw({error, {eoptions, {Ver, {versions, Versions}}}}).
validate_inet_option(mode, Value)
@@ -722,24 +740,34 @@ cipher_suites(Version, Ciphers0) ->
format_error({error, Reason}) ->
format_error(Reason);
+format_error(Reason) when is_list(Reason) ->
+ Reason;
format_error(closed) ->
- "Connection closed for the operation in question.";
+ "The connection is closed";
+format_error(ecacertfile) ->
+ "Own CA certificate file is invalid.";
+format_error(ecertfile) ->
+ "Own certificate file is invalid.";
+format_error(ekeyfile) ->
+ "Own private key file is invalid.";
+format_error(esslaccept) ->
+ "Server SSL handshake procedure between client and server failed.";
+format_error(esslconnect) ->
+ "Client SSL handshake procedure between client and server failed.";
+format_error({eoptions, Options}) ->
+ lists:flatten(io_lib:format("Error in options list: ~p~n", [Options]));
+
+%%%%%%%%%%%% START OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
format_error(ebadsocket) ->
"Connection not found (internal error).";
format_error(ebadstate) ->
"Connection not in connect state (internal error).";
format_error(ebrokertype) ->
"Wrong broker type (internal error).";
-format_error(ecacertfile) ->
- "Own CA certificate file is invalid.";
-format_error(ecertfile) ->
- "Own certificate file is invalid.";
format_error(echaintoolong) ->
"The chain of certificates provided by peer is too long.";
format_error(ecipher) ->
"Own list of specified ciphers is invalid.";
-format_error(ekeyfile) ->
- "Own private key file is invalid.";
format_error(ekeymismatch) ->
"Own private key does not match own certificate.";
format_error(enoissuercert) ->
@@ -765,10 +793,6 @@ format_error(epeercertinvalid) ->
"Certificate provided by peer is invalid.";
format_error(eselfsignedcert) ->
"Certificate provided by peer is self signed.";
-format_error(esslaccept) ->
- "Server SSL handshake procedure between client and server failed.";
-format_error(esslconnect) ->
- "Client SSL handshake procedure between client and server failed.";
format_error(esslerrssl) ->
"SSL protocol failure. Typically because of a fatal alert from peer.";
format_error(ewantconnect) ->
@@ -787,6 +811,9 @@ format_error({badcast, _Cast}) ->
format_error({badinfo, _Info}) ->
"Call not recognized for current mode (active or passive) and state "
"of socket.";
+
+%%%%%%%%%%%%%%%%%% END OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
format_error(Error) ->
case (catch inet:format_error(Error)) of
"unkknown POSIX" ++ _ ->
@@ -798,7 +825,7 @@ format_error(Error) ->
end.
no_format(Error) ->
- io_lib:format("No format string for error: \"~p\" available.", [Error]).
+ lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
%% Start old ssl port program if needed.
ensure_old_ssl_started() ->
@@ -832,6 +859,11 @@ version() ->
Vsns
end,
{ok, {SSLVsn, CompVsn, LibVsn}}.
+
+min(N,M) when N < M ->
+ N;
+min(_, M) ->
+ M.
%% Only used to remove exit messages from old ssl
%% First is a nonsense clause to provide some
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index d3f9c833f1..db9a883654 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -38,10 +38,6 @@ reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) ->
esslconnect;
reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) ->
esslaccept;
-reason_code(#alert{description = ?CERTIFICATE_EXPIRED}, _) ->
- epeercertexpired;
-reason_code(#alert{level = ?FATAL}, _) ->
- esslerrssl;
reason_code(#alert{description = Description}, _) ->
description_txt(Description).
@@ -55,51 +51,51 @@ level_txt(?FATAL) ->
"Fatal error:".
description_txt(?CLOSE_NOTIFY) ->
- "close_notify";
+ "close notify";
description_txt(?UNEXPECTED_MESSAGE) ->
- "unexpected_message";
+ "unexpected message";
description_txt(?BAD_RECORD_MAC) ->
- "bad_record_mac";
+ "bad record mac";
description_txt(?DECRYPTION_FAILED) ->
- "decryption_failed";
+ "decryption failed";
description_txt(?RECORD_OVERFLOW) ->
- "record_overflow";
+ "record overflow";
description_txt(?DECOMPRESSION_FAILURE) ->
- "decompression_failure";
+ "decompression failure";
description_txt(?HANDSHAKE_FAILURE) ->
- "handshake_failure";
+ "handshake failure";
description_txt(?BAD_CERTIFICATE) ->
- "bad_certificate";
+ "bad certificate";
description_txt(?UNSUPPORTED_CERTIFICATE) ->
- "unsupported_certificate";
+ "unsupported certificate";
description_txt(?CERTIFICATE_REVOKED) ->
- "certificate_revoked";
+ "certificate revoked";
description_txt(?CERTIFICATE_EXPIRED) ->
- "certificate_expired";
+ "certificate expired";
description_txt(?CERTIFICATE_UNKNOWN) ->
- "certificate_unknown";
+ "certificate unknown";
description_txt(?ILLEGAL_PARAMETER) ->
- "illegal_parameter";
+ "illegal parameter";
description_txt(?UNKNOWN_CA) ->
- "unknown_ca";
+ "unknown ca";
description_txt(?ACCESS_DENIED) ->
- "access_denied";
+ "access denied";
description_txt(?DECODE_ERROR) ->
- "decode_error";
+ "decode error";
description_txt(?DECRYPT_ERROR) ->
- "decrypt_error";
+ "decrypt error";
description_txt(?EXPORT_RESTRICTION) ->
- "export_restriction";
+ "export restriction";
description_txt(?PROTOCOL_VERSION) ->
- "protocol_version";
+ "protocol version";
description_txt(?INSUFFICIENT_SECURITY) ->
- "insufficient_security";
+ "insufficient security";
description_txt(?INTERNAL_ERROR) ->
- "internal_error";
+ "internal error";
description_txt(?USER_CANCELED) ->
- "user_canceled";
+ "user canceled";
description_txt(?NO_RENEGOTIATION) ->
- "no_renegotiation".
+ "no renegotiation".
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index d97b61a5ce..686e90a70c 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -29,10 +29,12 @@
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_debug.hrl").
+-include_lib("public_key/include/public_key.hrl").
-export([trusted_cert_and_path/3,
certificate_chain/2,
- file_to_certificats/1]).
+ file_to_certificats/1,
+ validate_extensions/6]).
%%====================================================================
%% Internal application API
@@ -87,6 +89,30 @@ file_to_certificats(File) ->
{ok, List} = ssl_manager:cache_pem_file(File),
[Bin || {cert, Bin, not_encrypted} <- List].
+
+%% Validates ssl/tls specific extensions
+validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) ->
+ {UnknownExtensions, ValidationState, AccErr};
+
+validate_extensions([#'Extension'{extnID = ?'id-ce-extKeyUsage',
+ extnValue = KeyUse,
+ critical = true} | Rest],
+ ValidationState, UnknownExtensions, Verify, AccErr0, Role) ->
+ case is_valid_extkey_usage(KeyUse, Role) of
+ true ->
+ validate_extensions(Rest, ValidationState, UnknownExtensions,
+ Verify, AccErr0, Role);
+ false ->
+ AccErr =
+ not_valid_extension({bad_cert, invalid_ext_key_usage}, Verify, AccErr0),
+ validate_extensions(Rest, ValidationState, UnknownExtensions, Verify, AccErr, Role)
+ end;
+
+validate_extensions([Extension | Rest], ValidationState, UnknownExtensions,
+ Verify, AccErr, Role) ->
+ validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions],
+ Verify, AccErr, Role).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -154,3 +180,18 @@ not_valid(Alert, true, _) ->
throw(Alert);
not_valid(_, false, {ErlCert, Path}) ->
{ErlCert, Path, [{bad_cert, unknown_ca}]}.
+
+is_valid_extkey_usage(KeyUse, client) ->
+ %% Client wants to verify server
+ is_valid_key_usage(KeyUse,?'id-kp-serverAuth');
+is_valid_extkey_usage(KeyUse, server) ->
+ %% Server wants to verify client
+ is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
+
+is_valid_key_usage(KeyUse, Use) ->
+ lists:member(Use, KeyUse).
+
+not_valid_extension(Error, true, _) ->
+ throw(Error);
+not_valid_extension(Error, false, AccErrors) ->
+ [Error | AccErrors].
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index adae92530a..b8c3c6f6b7 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -159,6 +159,8 @@ issuer_candidate(no_candidate) ->
case ets:first(Db) of
'$end_of_table' ->
no_more_candidates;
+ {file, _} = Key ->
+ issuer_candidate(Key);
Key ->
[Cert] = lookup(Key, Db),
{Key, Cert}
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 3d3d11b7f3..ef4b450d68 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -91,10 +91,10 @@ cipher(?DES, CipherState, Mac, Fragment) ->
block_cipher(fun(Key, IV, T) ->
crypto:des_cbc_encrypt(Key, IV, T)
end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?DES40, CipherState, Mac, Fragment) ->
- block_cipher(fun(Key, IV, T) ->
- crypto:des_cbc_encrypt(Key, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
+%% cipher(?DES40, CipherState, Mac, Fragment) ->
+%% block_cipher(fun(Key, IV, T) ->
+%% crypto:des_cbc_encrypt(Key, IV, T)
+%% end, block_size(des_cbc), CipherState, Mac, Fragment);
cipher(?'3DES', CipherState, Mac, Fragment) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
@@ -104,15 +104,15 @@ cipher(?AES, CipherState, Mac, Fragment) ->
crypto:aes_cbc_128_encrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_encrypt(Key, IV, T)
- end, block_size(aes_128_cbc), CipherState, Mac, Fragment);
+ end, block_size(aes_128_cbc), CipherState, Mac, Fragment).
%% cipher(?IDEA, CipherState, Mac, Fragment) ->
%% block_cipher(fun(Key, IV, T) ->
%% crypto:idea_cbc_encrypt(Key, IV, T)
%% end, block_size(idea_cbc), CipherState, Mac, Fragment);
-cipher(?RC2, CipherState, Mac, Fragment) ->
- block_cipher(fun(Key, IV, T) ->
- crypto:rc2_40_cbc_encrypt(Key, IV, T)
- end, block_size(rc2_cbc_40), CipherState, Mac, Fragment).
+%% cipher(?RC2, CipherState, Mac, Fragment) ->
+%% block_cipher(fun(Key, IV, T) ->
+%% crypto:rc2_40_cbc_encrypt(Key, IV, T)
+%% end, block_size(rc2_cbc_40), CipherState, Mac, Fragment).
block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
Mac, Fragment) ->
@@ -157,10 +157,10 @@ decipher(?DES, HashSz, CipherState, Fragment) ->
block_decipher(fun(Key, IV, T) ->
crypto:des_cbc_decrypt(Key, IV, T)
end, CipherState, HashSz, Fragment);
-decipher(?DES40, HashSz, CipherState, Fragment) ->
- block_decipher(fun(Key, IV, T) ->
- crypto:des_cbc_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment);
+%% decipher(?DES40, HashSz, CipherState, Fragment) ->
+%% block_decipher(fun(Key, IV, T) ->
+%% crypto:des_cbc_decrypt(Key, IV, T)
+%% end, CipherState, HashSz, Fragment);
decipher(?'3DES', HashSz, CipherState, Fragment) ->
block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_decrypt(K1, K2, K3, IV, T)
@@ -170,15 +170,15 @@ decipher(?AES, HashSz, CipherState, Fragment) ->
crypto:aes_cbc_128_decrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment);
+ end, CipherState, HashSz, Fragment).
%% decipher(?IDEA, HashSz, CipherState, Fragment) ->
%% block_decipher(fun(Key, IV, T) ->
%% crypto:idea_cbc_decrypt(Key, IV, T)
%% end, CipherState, HashSz, Fragment);
-decipher(?RC2, HashSz, CipherState, Fragment) ->
- block_decipher(fun(Key, IV, T) ->
- crypto:rc2_40_cbc_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment).
+%% decipher(?RC2, HashSz, CipherState, Fragment) ->
+%% block_decipher(fun(Key, IV, T) ->
+%% crypto:rc2_40_cbc_decrypt(Key, IV, T)
+%% end, CipherState, HashSz, Fragment).
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
HashSz, Fragment) ->
@@ -223,34 +223,34 @@ suites({3, N}) when N == 1; N == 2 ->
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite>
-%% Note: since idea is unsupported on the openssl version used by
-%% crypto (as of OTP R12B), we've commented away the idea stuff
+%% Note: Currently not supported suites are commented away.
+%% They should be supported or removed in the future.
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
{null, null, null, ignore};
-suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
- {rsa, null, md5, ignore};
-suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
- {rsa, null, sha, ignore};
-suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> % ok
+%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
+%% {rsa, null, md5, ignore};
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha, ignore};
+suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
{rsa, rc4_128, md5, no_export};
-suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> % ok
+suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
{rsa, rc4_128, sha, no_export};
-%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> % unsupported
+%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
%% {rsa, idea_cbc, sha, no_export};
-suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> % ok
+suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
{rsa, des_cbc, sha, no_export};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
{rsa, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_DSS_WITH_DES_CBC_SHA) ->
- {dh_dss, des_cbc, sha, no_export};
-suite_definition(?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dh_dss, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_RSA_WITH_DES_CBC_SHA) ->
- {dh_rsa, des_cbc, sha, no_export};
-suite_definition(?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dh_rsa, '3des_ede_cbc', sha, no_export};
+%% suite_definition(?TLS_DH_DSS_WITH_DES_CBC_SHA) ->
+%% {dh_dss, des_cbc, sha, no_export};
+%% suite_definition(?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA) ->
+%% {dh_dss, '3des_ede_cbc', sha, no_export};
+%% suite_definition(?TLS_DH_RSA_WITH_DES_CBC_SHA) ->
+%% {dh_rsa, des_cbc, sha, no_export};
+%% suite_definition(?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA) ->
+%% {dh_rsa, '3des_ede_cbc', sha, no_export};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
{dhe_dss, des_cbc, sha, no_export};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
@@ -259,103 +259,103 @@ suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
{dhe_rsa, des_cbc, sha, no_export};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
{dhe_rsa, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
- {dh_anon, rc4_128, md5, no_export};
-suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des40_cbc, sha, no_export};
-suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha, no_export};
+%% suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
+%% {dh_anon, rc4_128, md5, no_export};
+%% suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
+%% {dh_anon, des40_cbc, sha, no_export};
+%% suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
+%% {dh_anon, '3des_ede_cbc', sha, no_export};
%%% TSL V1.1 AES suites
-suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> % ok
+suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
{rsa, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_DSS_WITH_AES_128_CBC_SHA) ->
- {dh_dss, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_RSA_WITH_AES_128_CBC_SHA) ->
- {dh_rsa, aes_128_cbc, sha, ignore};
+%% suite_definition(?TLS_DH_DSS_WITH_AES_128_CBC_SHA) ->
+%% {dh_dss, aes_128_cbc, sha, ignore};
+%% suite_definition(?TLS_DH_RSA_WITH_AES_128_CBC_SHA) ->
+%% {dh_rsa, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
{dhe_dss, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
{dhe_rsa, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> % ok
+%% suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
+%% {dh_anon, aes_128_cbc, sha, ignore};
+suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
{rsa, aes_256_cbc, sha, ignore};
-suite_definition(?TLS_DH_DSS_WITH_AES_256_CBC_SHA) ->
- {dh_dss, aes_256_cbc, sha, ignore};
-suite_definition(?TLS_DH_RSA_WITH_AES_256_CBC_SHA) ->
- {dh_rsa, aes_256_cbc, sha, ignore};
+%% suite_definition(?TLS_DH_DSS_WITH_AES_256_CBC_SHA) ->
+%% {dh_dss, aes_256_cbc, sha, ignore};
+%% suite_definition(?TLS_DH_RSA_WITH_AES_256_CBC_SHA) ->
+%% {dh_rsa, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
{dhe_dss, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha, ignore};
-suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha, ignore};
+ {dhe_rsa, aes_256_cbc, sha, ignore}.
+%% suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
+%% {dh_anon, aes_256_cbc, sha, ignore};
%% TSL V1.1 KRB SUITES
-suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) ->
- {krb5, des_cbc, sha, ignore};
-suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) ->
- {krb5, '3des_ede_cbc', sha, ignore};
-suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) ->
- {krb5, rc4_128, sha, ignore};
+%% suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) ->
+%% {krb5, des_cbc, sha, ignore};
+%% suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) ->
+%% {krb5, '3des_ede_cbc', sha, ignore};
+%% suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) ->
+%% {krb5, rc4_128, sha, ignore};
%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_SHA) ->
%% {krb5, idea_cbc, sha, ignore};
-suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) ->
- {krb5, des_cbc, md5, ignore};
-suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) ->
- {krb5, '3des_ede_cbc', md5, ignore};
-suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) ->
- {krb5, rc4_128, md5, ignore};
+%% suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) ->
+%% {krb5, des_cbc, md5, ignore};
+%% suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) ->
+%% {krb5, '3des_ede_cbc', md5, ignore};
+%% suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) ->
+%% {krb5, rc4_128, md5, ignore};
%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_MD5) ->
%% {krb5, idea_cbc, md5, ignore};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
- {rsa, rc4_56, md5, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
- {rsa, rc2_cbc_56, md5, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
- {rsa, des_cbc, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
- {rsa, rc4_56, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
- {dhe_dss, rc4_56, sha, export};
-suite_definition(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
- {dhe_dss, rc4_128, sha, export};
+%% suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
+%% {rsa, rc4_56, md5, export};
+%% suite_definition(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
+%% {rsa, rc2_cbc_56, md5, export};
+%% suite_definition(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
+%% {rsa, des_cbc, sha, export};
+%% suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
+%% {dhe_dss, des_cbc, sha, export};
+%% suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
+%% {rsa, rc4_56, sha, export};
+%% suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
+%% {dhe_dss, rc4_56, sha, export};
+%% suite_definition(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
+%% {dhe_dss, rc4_128, sha, export};
%% Export suites TLS 1.0 OR SSLv3-only servers.
-suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA) ->
- {krb5_export, des40_cbc, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA) ->
- {krb5_export, rc2_cbc_40, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_SHA) ->
- {krb5_export, des40_cbc, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5) ->
- {krb5_export, des40_cbc, md5, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5) ->
- {krb5_export, rc2_cbc_40, md5, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_MD5) ->
- {krb5_export, rc2_cbc_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> % ok
- {rsa, rc4_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) -> % ok
- {rsa, rc2_cbc_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_dss, des40_cbc, sha, export};
-suite_definition(?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- {dhe_dss, des40_cbc, sha, export};
-suite_definition(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {dhe_rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5) ->
- {dh_anon, rc4_40, md5, export};
-suite_definition(?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_anon, des40_cbc, sha, export}.
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA) ->
+%% {krb5_export, des40_cbc, sha, export};
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA) ->
+%% {krb5_export, rc2_cbc_40, sha, export};
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_SHA) ->
+%% {krb5_export, des40_cbc, sha, export};
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5) ->
+%% {krb5_export, des40_cbc, md5, export};
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5) ->
+%% {krb5_export, rc2_cbc_40, md5, export};
+%% suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_MD5) ->
+%% {krb5_export, rc2_cbc_40, md5, export};
+%% suite_definition(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) ->
+%% {rsa, rc4_40, md5, export};
+%% suite_definition(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) ->
+%% {rsa, rc2_cbc_40, md5, export};
+%% suite_definition(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {rsa, des40_cbc, sha, export};
+%% suite_definition(?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {dh_dss, des40_cbc, sha, export};
+%% suite_definition(?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {dh_rsa, des40_cbc, sha, export};
+%% suite_definition(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {dhe_dss, des40_cbc, sha, export};
+%% suite_definition(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {dhe_rsa, des40_cbc, sha, export};
+%% suite_definition(?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5) ->
+%% {dh_anon, rc4_40, md5, export};
+%% suite_definition(?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA) ->
+%% {dh_anon, des40_cbc, sha, export}.
%% TLS v1.1 suites
suite({rsa, null, md5, ignore}) ->
@@ -372,14 +372,14 @@ suite({rsa, des_cbc, sha, no_export}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
suite({rsa, '3des_ede_cbc', sha, no_export}) ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dh_dss, des_cbc, sha, no_export}) ->
- ?TLS_DH_DSS_WITH_DES_CBC_SHA;
-suite({dh_dss, '3des_ede_cbc', sha, no_export}) ->
- ?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA;
-suite({dh_rsa, des_cbc, sha, no_export}) ->
- ?TLS_DH_RSA_WITH_DES_CBC_SHA;
-suite({dh_rsa, '3des_ede_cbc', sha, no_export}) ->
- ?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA;
+%% suite({dh_dss, des_cbc, sha, no_export}) ->
+%% ?TLS_DH_DSS_WITH_DES_CBC_SHA;
+%% suite({dh_dss, '3des_ede_cbc', sha, no_export}) ->
+%% ?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA;
+%% suite({dh_rsa, des_cbc, sha, no_export}) ->
+%% ?TLS_DH_RSA_WITH_DES_CBC_SHA;
+%% suite({dh_rsa, '3des_ede_cbc', sha, no_export}) ->
+%% ?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA;
suite({dhe_dss, des_cbc, sha, no_export}) ->
?TLS_DHE_DSS_WITH_DES_CBC_SHA;
suite({dhe_dss, '3des_ede_cbc', sha, no_export}) ->
@@ -388,108 +388,108 @@ suite({dhe_rsa, des_cbc, sha, no_export}) ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
suite({dhe_rsa, '3des_ede_cbc', sha, no_export}) ->
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dh_anon, rc4_128, md5, no_export}) ->
- ?TLS_DH_anon_WITH_RC4_128_MD5;
-suite({dh_anon, des40_cbc, sha, no_export}) ->
- ?TLS_DH_anon_WITH_DES_CBC_SHA;
-suite({dh_anon, '3des_ede_cbc', sha, no_export}) ->
- ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+%% suite({dh_anon, rc4_128, md5, no_export}) ->
+%% ?TLS_DH_anon_WITH_RC4_128_MD5;
+%% suite({dh_anon, des40_cbc, sha, no_export}) ->
+%% ?TLS_DH_anon_WITH_DES_CBC_SHA;
+%% suite({dh_anon, '3des_ede_cbc', sha, no_export}) ->
+%% ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
%%% TSL V1.1 AES suites
suite({rsa, aes_128_cbc, sha, ignore}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-suite({dh_dss, aes_128_cbc, sha, ignore}) ->
- ?TLS_DH_DSS_WITH_AES_128_CBC_SHA;
-suite({dh_rsa, aes_128_cbc, sha, ignore}) ->
- ?TLS_DH_RSA_WITH_AES_128_CBC_SHA;
-suite({dhe_dss, aes_128_cbc, sha, ignore}) ->
- ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
+%% suite({dh_dss, aes_128_cbc, sha, ignore}) ->
+%% ?TLS_DH_DSS_WITH_AES_128_CBC_SHA;
+%% suite({dh_rsa, aes_128_cbc, sha, ignore}) ->
+%% ?TLS_DH_RSA_WITH_AES_128_CBC_SHA;
+%% suite({dhe_dss, aes_128_cbc, sha, ignore}) ->
+%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
suite({dhe_rsa, aes_128_cbc, sha, ignore}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-suite({dh_anon, aes_128_cbc, sha, ignore}) ->
- ?TLS_DH_anon_WITH_AES_128_CBC_SHA;
+%% suite({dh_anon, aes_128_cbc, sha, ignore}) ->
+%% ?TLS_DH_anon_WITH_AES_128_CBC_SHA;
suite({rsa, aes_256_cbc, sha, ignore}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA;
-suite({dh_dss, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_DSS_WITH_AES_256_CBC_SHA;
-suite({dh_rsa, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_RSA_WITH_AES_256_CBC_SHA;
+%% suite({dh_dss, aes_256_cbc, sha, ignore}) ->
+%% ?TLS_DH_DSS_WITH_AES_256_CBC_SHA;
+%% suite({dh_rsa, aes_256_cbc, sha, ignore}) ->
+%% ?TLS_DH_RSA_WITH_AES_256_CBC_SHA;
suite({dhe_dss, aes_256_cbc, sha, ignore}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
suite({dhe_rsa, aes_256_cbc, sha, ignore}) ->
- ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-suite({dh_anon, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA;
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA.
+%% suite({dh_anon, aes_256_cbc, sha, ignore}) ->
+%% ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
%% TSL V1.1 KRB SUITES
-suite({krb5, des_cbc, sha, ignore}) ->
- ?TLS_KRB5_WITH_DES_CBC_SHA;
-suite({krb5_cbc, '3des_ede_cbc', sha, ignore}) ->
- ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA;
-suite({krb5, rc4_128, sha, ignore}) ->
- ?TLS_KRB5_WITH_RC4_128_SHA;
+%% suite({krb5, des_cbc, sha, ignore}) ->
+%% ?TLS_KRB5_WITH_DES_CBC_SHA;
+%% suite({krb5_cbc, '3des_ede_cbc', sha, ignore}) ->
+%% ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA;
+%% suite({krb5, rc4_128, sha, ignore}) ->
+%% ?TLS_KRB5_WITH_RC4_128_SHA;
%% suite({krb5_cbc, idea_cbc, sha, ignore}) ->
%% ?TLS_KRB5_WITH_IDEA_CBC_SHA;
-suite({krb5_cbc, md5, ignore}) ->
- ?TLS_KRB5_WITH_DES_CBC_MD5;
-suite({krb5_ede_cbc, des_cbc, md5, ignore}) ->
- ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5;
-suite({krb5_128, rc4_128, md5, ignore}) ->
- ?TLS_KRB5_WITH_RC4_128_MD5;
+%% suite({krb5_cbc, md5, ignore}) ->
+%% ?TLS_KRB5_WITH_DES_CBC_MD5;
+%% suite({krb5_ede_cbc, des_cbc, md5, ignore}) ->
+%% ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5;
+%% suite({krb5_128, rc4_128, md5, ignore}) ->
+%% ?TLS_KRB5_WITH_RC4_128_MD5;
%% suite({krb5, idea_cbc, md5, ignore}) ->
%% ?TLS_KRB5_WITH_IDEA_CBC_MD5;
%% Export suites TLS 1.0 OR SSLv3-only servers.
-suite({rsa, rc4_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
-suite({rsa, rc2_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({rsa, des40_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({rsa, rc4_56, md5, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
-suite({rsa, rc2_cbc_56, md5, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
-suite({rsa, des_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
-suite({dhe_dss, des_cbc, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
-suite({rsa, rc4_56, sha, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
-suite({dhe_dss, rc4_56, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
-suite({dhe_dss, rc4_128, sha, export}) ->
- ?TLS_DHE_DSS_WITH_RC4_128_SHA;
-suite({krb5_export, des40_cbc, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA;
-suite({krb5_export, rc2_cbc_40, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA;
-suite({krb5_export, rc4_cbc_40, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC4_40_SHA;
-suite({krb5_export, des40_cbc, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5;
-suite({krb5_export, rc2_cbc_40, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({krb5_export, rc4_cbc_40, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC4_40_MD5;
-suite({rsa_export, rc4_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
-suite({rsa_export, rc2_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({rsa_export, des40_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_dss_export, des40_cbc, sha, export}) ->
- ?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_rsa_export, des40_cbc, sha, export}) ->
- ?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dhe_dss_export, des40_cbc, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
-suite({dhe_rsa_export, des40_cbc, sha, export}) ->
- ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_anon_export, rc4_40, md5, export}) ->
- ?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5;
-suite({dh_anon_export, des40_cbc, sha, export}) ->
- ?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA.
+%% suite({rsa, rc4_40, md5, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
+%% suite({rsa, rc2_cbc_40, md5, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
+%% suite({rsa, des40_cbc, sha, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({rsa, rc4_56, md5, export}) ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
+%% suite({rsa, rc2_cbc_56, md5, export}) ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
+%% suite({rsa, des_cbc, sha, export}) ->
+%% ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
+%% suite({dhe_dss, des_cbc, sha, export}) ->
+%% ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
+%% suite({rsa, rc4_56, sha, export}) ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
+%% suite({dhe_dss, rc4_56, sha, export}) ->
+%% ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
+%% suite({dhe_dss, rc4_128, sha, export}) ->
+%% ?TLS_DHE_DSS_WITH_RC4_128_SHA;
+%% suite({krb5_export, des40_cbc, sha, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA;
+%% suite({krb5_export, rc2_cbc_40, sha, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA;
+%% suite({krb5_export, rc4_cbc_40, sha, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_RC4_40_SHA;
+%% suite({krb5_export, des40_cbc, md5, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5;
+%% suite({krb5_export, rc2_cbc_40, md5, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5;
+%% suite({krb5_export, rc4_cbc_40, md5, export}) ->
+%% ?TLS_KRB5_EXPORT_WITH_RC4_40_MD5;
+%% suite({rsa_export, rc4_cbc_40, md5, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
+%% suite({rsa_export, rc2_cbc_40, md5, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
+%% suite({rsa_export, des40_cbc, sha, export}) ->
+%% ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({dh_dss_export, des40_cbc, sha, export}) ->
+%% ?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({dh_rsa_export, des40_cbc, sha, export}) ->
+%% ?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({dhe_dss_export, des40_cbc, sha, export}) ->
+%% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({dhe_rsa_export, des40_cbc, sha, export}) ->
+%% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% suite({dh_anon_export, rc4_40, md5, export}) ->
+%% ?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5;
+%% suite({dh_anon_export, des40_cbc, sha, export}) ->
+%% ?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA.
%% translate constants <-> openssl-strings
@@ -524,35 +524,35 @@ openssl_suite("RC4-SHA") ->
openssl_suite("RC4-MD5") ->
?TLS_RSA_WITH_RC4_128_MD5;
%% TODO: Do we want to support this?
-openssl_suite("EXP1024-RC4-MD5") ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
-openssl_suite("EXP1024-RC2-CBC-MD5") ->
- ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
-openssl_suite("EXP1024-DES-CBC-SHA") ->
- ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
-openssl_suite("EXP1024-DHE-DSS-DES-CBC-SHA") ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
-openssl_suite("EXP1024-RC4-SHA") ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
-openssl_suite("EXP1024-DHE-DSS-RC4-SHA") ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
-openssl_suite("DHE-DSS-RC4-SHA") ->
- ?TLS_DHE_DSS_WITH_RC4_128_SHA;
+%% openssl_suite("EXP1024-RC4-MD5") ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
+%% openssl_suite("EXP1024-RC2-CBC-MD5") ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
+%% openssl_suite("EXP1024-DES-CBC-SHA") ->
+%% ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
+%% openssl_suite("EXP1024-DHE-DSS-DES-CBC-SHA") ->
+%% ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
+%% openssl_suite("EXP1024-RC4-SHA") ->
+%% ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
+%% openssl_suite("EXP1024-DHE-DSS-RC4-SHA") ->
+%% ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
+%% openssl_suite("DHE-DSS-RC4-SHA") ->
+%% ?TLS_DHE_DSS_WITH_RC4_128_SHA;
openssl_suite("EDH-RSA-DES-CBC-SHA") ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
openssl_suite("DES-CBC-SHA") ->
- ?TLS_RSA_WITH_DES_CBC_SHA;
-openssl_suite("EXP-EDH-RSA-DES-CBC-SHA") ->
- ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-EDH-DSS-DES-CBC-SHA") ->
- ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-DES-CBC-SHA") ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-RC2-CBC-MD5") ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-openssl_suite("EXP-RC4-MD5") ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5.
+ ?TLS_RSA_WITH_DES_CBC_SHA.
+%% openssl_suite("EXP-EDH-RSA-DES-CBC-SHA") ->
+%% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% openssl_suite("EXP-EDH-DSS-DES-CBC-SHA") ->
+%% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
+%% openssl_suite("EXP-DES-CBC-SHA") ->
+%% ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
+%% openssl_suite("EXP-RC2-CBC-MD5") ->
+%% ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
+%% openssl_suite("EXP-RC4-MD5") ->
+%% ?TLS_RSA_EXPORT_WITH_RC4_40_MD5.
openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
"DHE-RSA-AES256-SHA";
@@ -582,31 +582,31 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
"EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
"DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-EDH-RSA-DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-EDH-DSS-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) ->
- "EXP-RC2-CBC-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) ->
- "EXP-RC4-MD5";
-
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
- "EXP1024-RC4-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
- "EXP1024-RC2-CBC-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
- "EXP1024-DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
- "EXP1024-DHE-DSS-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
- "EXP1024-RC4-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
- "EXP1024-DHE-DSS-RC4-SHA";
-openssl_suite_name(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
- "DHE-DSS-RC4-SHA";
+%% openssl_suite_name(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
+%% "EXP-EDH-RSA-DES-CBC-SHA";
+%% openssl_suite_name(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
+%% "EXP-EDH-DSS-DES-CBC-SHA";
+%% openssl_suite_name(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
+%% "EXP-DES-CBC-SHA";
+%% openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) ->
+%% "EXP-RC2-CBC-MD5";
+%% openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) ->
+%% "EXP-RC4-MD5";
+
+%% openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
+%% "EXP1024-RC4-MD5";
+%% openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
+%% "EXP1024-RC2-CBC-MD5";
+%% openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
+%% "EXP1024-DES-CBC-SHA";
+%% openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
+%% "EXP1024-DHE-DSS-DES-CBC-SHA";
+%% openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
+%% "EXP1024-RC4-SHA";
+%% openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
+%% "EXP1024-DHE-DSS-RC4-SHA";
+%% openssl_suite_name(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
+%% "DHE-DSS-RC4-SHA";
%% No oppenssl name
openssl_suite_name(Cipher) ->
@@ -621,15 +621,15 @@ bulk_cipher_algorithm(null) ->
%% Not supported yet
%% bulk_cipher_algorithm(idea_cbc) ->
%% ?IDEA;
-bulk_cipher_algorithm(Cipher) when Cipher == rc2_cbc_40;
- Cipher == rc2_cbc_56 ->
- ?RC2;
+%% bulk_cipher_algorithm(Cipher) when Cipher == rc2_cbc_40;
+%% Cipher == rc2_cbc_56 ->
+%% ?RC2;
bulk_cipher_algorithm(Cipher) when Cipher == rc4_40;
Cipher == rc4_56;
Cipher == rc4_128 ->
?RC4;
-bulk_cipher_algorithm(des40_cbc) ->
- ?DES40;
+%% bulk_cipher_algorithm(des40_cbc) ->
+%% ?DES40;
bulk_cipher_algorithm(des_cbc) ->
?DES;
bulk_cipher_algorithm('3des_ede_cbc') ->
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index 4304c501b7..d282cbd780 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -250,4 +250,9 @@
-define(TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, <<?BYTE(16#00), ?BYTE(16#65)>>).
-define(TLS_DHE_DSS_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#66)>>).
+%% RFC 5746 - Not a real ciphersuite used to signal empty "renegotiation_info" extension
+%% to avoid handshake failure from old servers that do not ignore
+%% hello extension data as they should.
+-define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>).
+
-endif. % -ifdef(ssl_cipher).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 4847fd278d..a4eaf03086 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -39,17 +39,17 @@
-include_lib("public_key/include/public_key.hrl").
%% Internal application API
--export([send/2, send/3, recv/3, connect/7, accept/6, close/1, shutdown/2,
+-export([send/2, send/3, recv/3, connect/7, ssl_accept/6, handshake/2,
+ socket_control/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1,
- sockname/1, peername/1]).
+ peer_certificate/1, sockname/1, peername/1, renegotiation/1]).
%% Called by ssl_connection_sup
-export([start_link/7]).
%% gen_fsm callbacks
--export([init/1, hello/2, certify/2, cipher/2, connection/2, connection/3, abbreviated/2,
- handle_event/3,
+-export([init/1, hello/2, certify/2, cipher/2, connection/2,
+ abbreviated/2, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-record(state, {
@@ -58,6 +58,7 @@
transport_cb, % atom() - callback module
data_tag, % atom() - ex tcp.
close_tag, % atom() - ex tcp_closed
+ error_tag, % atom() - ex tcp_error
host, % string() | ipadress()
port, % integer()
socket, % socket()
@@ -78,57 +79,94 @@
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
- private_key, % PKIX: 'RSAPrivateKey'
- diffie_hellman_params, %
+ private_key, % PKIX: #'RSAPrivateKey'{}
+ diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
+ diffie_hellman_keys, % {PublicKey, PrivateKey}
premaster_secret, %
cert_db_ref, % ets_table()
from, % term(), where to reply
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
%% tls_buffer, % Keeps a lookahead one packet if available
- log_alert % boolan()
+ log_alert, % boolean()
+ renegotiation, % {boolean(), From | internal | peer}
+ recv_during_renegotiation, %boolean()
+ send_queue % queue()
}).
+-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
+ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
+ base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
+
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function:
+%% Function: send(Pid, Data) -> ok | {error, Reason}
%%
-%% Description:
+%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
send(Pid, Data) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
send(Pid, Data, Timeout) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason}
%%
-%% Description:
+%% Description: Receives data when active = false
%%--------------------------------------------------------------------
-recv(Pid, Length, Timeout) -> % TODO: Prio with renegotiate?
+recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length}, Timeout).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: : connect(Host, Port, Socket, Options,
+%% User, CbInfo, Timeout) -> {ok, Socket}
%%
-%% Description:
+%% Description: Connect to a ssl server.
%%--------------------------------------------------------------------
connect(Host, Port, Socket, Options, User, CbInfo, Timeout) ->
start_fsm(client, Host, Port, Socket, Options, User, CbInfo,
Timeout).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: accept(Port, Socket, Opts, User,
+%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason}
%%
-%% Description:
+%% Description: Performs accept on a ssl listen socket. e.i. performs
+%% ssl handshake.
%%--------------------------------------------------------------------
-accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
+ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
start_fsm(server, "localhost", Port, Socket, Opts, User,
CbInfo, Timeout).
+
%%--------------------------------------------------------------------
-%% Function:
+%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason}
+%%
+%% Description: Starts ssl handshake.
+%%--------------------------------------------------------------------
+handshake(#sslsocket{pid = Pid}, Timeout) ->
+ case sync_send_all_state_event(Pid, start, Timeout) of
+ connected ->
+ ok;
+ Error ->
+ Error
+ end.
+%--------------------------------------------------------------------
+%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason}
+%%
+%% Description: Set the ssl process to own the accept socket
+%%--------------------------------------------------------------------
+socket_control(Socket, Pid, CbModule) ->
+ case CbModule:controlling_process(Socket, Pid) of
+ ok ->
+ {ok, sslsocket(Pid)};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: close() -> ok
%%
-%% Description:
+%% Description: Close a ssl connection
%%--------------------------------------------------------------------
close(ConnectionPid) ->
case sync_send_all_state_event(ConnectionPid, close) of
@@ -139,76 +177,85 @@ close(ConnectionPid) ->
end.
%%--------------------------------------------------------------------
-%% Function:
+%% Function: shutdown(Socket, How) -> ok | {error, Reason}
%%
-%% Description:
+%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
shutdown(ConnectionPid, How) ->
sync_send_all_state_event(ConnectionPid, {shutdown, How}).
-
%%--------------------------------------------------------------------
-%% Function:
+%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason}
%%
-%% Description:
+%% Description: Changes process that receives the messages when active = true
+%% or once.
%%--------------------------------------------------------------------
new_user(ConnectionPid, User) ->
sync_send_all_state_event(ConnectionPid, {new_user, User}).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason}
%%
-%% Description:
+%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
sockname(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, sockname).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason}
%%
-%% Description:
+%% Description: Same as inet:peername/1
%%--------------------------------------------------------------------
peername(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peername).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason}
%%
-%% Description:
+%% Description: Same as inet:getopts/2
%%--------------------------------------------------------------------
get_opts({ListenSocket, {_SslOpts, SockOpts}, _}, OptTags) ->
get_socket_opts(ListenSocket, OptTags, SockOpts, []);
get_opts(ConnectionPid, OptTags) ->
sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: setopts(Socket, Options) -> ok | {error, Reason}
%%
-%% Description:
+%% Description: Same as inet:setopts/2
%%--------------------------------------------------------------------
set_opts(ConnectionPid, Options) ->
sync_send_all_state_event(ConnectionPid, {set_opts, Options}).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} |
+%% {error, Reason}
%%
-%% Description:
+%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
info(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, info).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason}
%%
-%% Description:
+%% Description: Returns info about the ssl session
%%--------------------------------------------------------------------
session_info(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, session_info).
%%--------------------------------------------------------------------
-%% Function:
+%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason}
%%
-%% Description:
+%% Description: Returns the peer cert
%%--------------------------------------------------------------------
peer_certificate(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peer_certificate).
+%%--------------------------------------------------------------------
+%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason}
+%%
+%% Description: Starts a renegotiation of the ssl session.
+%%--------------------------------------------------------------------
+renegotiation(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, renegotiate).
+
%%====================================================================
%% ssl_connection_sup API
%%====================================================================
@@ -224,7 +271,6 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options,
User, CbInfo], []).
-
%%====================================================================
%% gen_fsm callbacks
%%====================================================================
@@ -243,54 +289,63 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
Hashes0 = ssl_handshake:init_hashes(),
try ssl_init(SSLOpts, Role) of
- {ok, Ref, CacheRef, OwnCert, Key} ->
+ {ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
State = State0#state{tls_handshake_hashes = Hashes0,
own_cert = OwnCert,
cert_db_ref = Ref,
session_cache = CacheRef,
- private_key = Key},
+ private_key = Key,
+ diffie_hellman_params = DHParams},
{ok, hello, State}
catch
throw:Error ->
{stop, Error}
end.
-
+
%%--------------------------------------------------------------------
%% Function:
%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
%% {next_state, NextStateName,
%% NextState, Timeout} |
%% {stop, Reason, NewState}
-%% Description:There should be one instance of this function for each possible
-%% state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:send_event/2, the instance of this function with the same name as
-%% the current state name StateName is called to handle the event. It is also
-%% called if a timeout occurs.
+%%
+%% Description:There should be one instance of this function for each
+%% possible state name. Whenever a gen_fsm receives an event sent
+%% using gen_fsm:send_event/2, the instance of this function with the
+%% same name as the current state name StateName is called to handle
+%% the event. It is also called if a timeout occurs.
+%%
%%--------------------------------------------------------------------
-hello(socket_control, #state{host = Host, port = Port, role = client,
- ssl_options = SslOpts,
- transport_cb = Transport, socket = Socket,
- connection_states = ConnectionStates}
+hello(start, #state{host = Host, port = Port, role = client,
+ ssl_options = SslOpts,
+ transport_cb = Transport, socket = Socket,
+ connection_states = ConnectionStates,
+ renegotiation = {Renegotiation, _}}
= State0) ->
- Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, SslOpts),
+ Hello = ssl_handshake:client_hello(Host, Port,
+ ConnectionStates, SslOpts, Renegotiation),
+
Version = Hello#client_hello.client_version,
Hashes0 = ssl_handshake:init_hashes(),
{BinMsg, CS2, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates, Hashes0),
Transport:send(Socket, BinMsg),
- State = State0#state{connection_states = CS2,
+ State1 = State0#state{connection_states = CS2,
negotiated_version = Version, %% Requested version
session =
#session{session_id = Hello#client_hello.session_id,
is_resumable = false},
tls_handshake_hashes = Hashes1},
- {next_state, hello, next_record(State)};
+ {Record, State} = next_record(State1),
+ next_state(hello, Record, State);
-hello(socket_control, #state{role = server} = State) ->
- {next_state, hello, next_record(State)};
+hello(start, #state{role = server} = State0) ->
+ {Record, State} = next_record(State0),
+ next_state(hello, Record, State);
-hello(hello, #state{role = client} = State) ->
- {next_state, hello, State};
+hello(#hello_request{}, #state{role = client} = State0) ->
+ {Record, State} = next_record(State0),
+ next_state(hello, Record, State);
hello(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression} = Hello,
@@ -299,53 +354,60 @@ hello(#server_hello{cipher_suite = CipherSuite,
role = client,
negotiated_version = ReqVersion,
host = Host, port = Port,
+ renegotiation = {Renegotiation, _},
+ ssl_options = SslOptions,
session_cache = Cache,
session_cache_cb = CacheCb} = State0) ->
- {Version, NewId, ConnectionStates1} =
- ssl_handshake:hello(Hello, ConnectionStates0),
-
- {KeyAlgorithm, _, _, _} =
- ssl_cipher:suite_definition(CipherSuite),
-
- PremasterSecret = make_premaster_secret(ReqVersion),
-
- State = State0#state{key_algorithm = KeyAlgorithm,
- negotiated_version = Version,
- connection_states = ConnectionStates1,
- premaster_secret = PremasterSecret},
- case ssl_session:is_new(OldId, NewId) of
- true ->
- Session = Session0#session{session_id = NewId,
- cipher_suite = CipherSuite,
+ case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
+ {Version, NewId, ConnectionStates1} ->
+ {KeyAlgorithm, _, _, _} =
+ ssl_cipher:suite_definition(CipherSuite),
+
+ PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
+
+ State1 = State0#state{key_algorithm = KeyAlgorithm,
+ negotiated_version = Version,
+ connection_states = ConnectionStates1,
+ premaster_secret = PremasterSecret},
+
+ case ssl_session:is_new(OldId, NewId) of
+ true ->
+ Session = Session0#session{session_id = NewId,
+ cipher_suite = CipherSuite,
compression_method = Compression},
- {next_state, certify,
- next_record(State#state{session = Session})};
- false ->
- Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}),
- case ssl_handshake:master_secret(Version, Session,
- ConnectionStates1, client) of
- {_, ConnectionStates2} ->
- {next_state, abbreviated,
- next_record(State#state{
- connection_states = ConnectionStates2,
- session = Session})};
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State),
- {stop, normal, State}
- end
+ {Record, State} = next_record(State1#state{session = Session}),
+ next_state(certify, Record, State);
+ false ->
+ Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}),
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates1, client) of
+ {_, ConnectionStates2} ->
+ {Record, State} =
+ next_record(State1#state{
+ connection_states = ConnectionStates2,
+ session = Session}),
+ next_state(abbreviated, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, hello, State1),
+ {stop, normal, State1}
+ end
+ end;
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ReqVersion, hello, State0),
+ {stop, normal, State0}
end;
hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
port = Port, session = Session0,
- session_cache = Cache,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
- case ssl_handshake:hello(Hello, {Port, SslOpts,
- Session0, Cache, CacheCb,
- ConnectionStates0}) of
+ case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
+ ConnectionStates0}, Renegotiation) of
{Version, {Type, Session}, ConnectionStates} ->
do_server_hello(Type, State#state{connection_states =
ConnectionStates,
@@ -354,52 +416,59 @@ hello(Hello = #client_hello{client_version = ClientVersion},
#alert{} = Alert ->
handle_own_alert(Alert, ClientVersion, hello, State),
{stop, normal, State}
- end.
+ end;
-abbreviated(socket_control, #state{role = server} = State) ->
- {next_state, abbreviated, State};
-abbreviated(hello, State) ->
- {next_state, certify, State};
+hello(Msg, State) ->
+ handle_unexpected_message(Msg, hello, State).
-abbreviated(Finished = #finished{},
+abbreviated(#hello_request{}, State0) ->
+ {Record, State} = next_record(State0),
+ next_state(hello, Record, State);
+
+abbreviated(Finished = #finished{verify_data = Data},
#state{role = server,
negotiated_version = Version,
tls_handshake_hashes = Hashes,
session = #session{master_secret = MasterSecret},
- from = From} = State) ->
+ connection_states = ConnectionStates0} =
+ State) ->
case ssl_handshake:verify_connection(Version, Finished, client,
MasterSecret, Hashes) of
- verified ->
- gen_fsm:reply(From, connected),
- {next_state, connection, next_record_if_active(State)};
- #alert{} = Alert ->
+ verified ->
+ ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0),
+ next_state_connection(abbreviated,
+ ack_connection(State#state{connection_states = ConnectionStates}));
+ #alert{} = Alert ->
handle_own_alert(Alert, Version, abbreviated, State),
{stop, normal, State}
end;
-abbreviated(Finished = #finished{},
+abbreviated(Finished = #finished{verify_data = Data},
#state{role = client, tls_handshake_hashes = Hashes0,
session = #session{master_secret = MasterSecret},
- from = From,
- negotiated_version = Version} = State) ->
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State) ->
case ssl_handshake:verify_connection(Version, Finished, server,
MasterSecret, Hashes0) of
verified ->
- {ConnectionStates, Hashes} = finalize_client_handshake(State),
- gen_fsm:reply(From, connected),
- {next_state, connection,
- next_record_if_active(State#state{tls_handshake_hashes = Hashes,
- connection_states =
- ConnectionStates})};
+ ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0),
+ {ConnectionStates, Hashes} =
+ finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated),
+ next_state_connection(abbreviated,
+ ack_connection(State#state{tls_handshake_hashes = Hashes,
+ connection_states =
+ ConnectionStates}));
#alert{} = Alert ->
handle_own_alert(Alert, Version, abbreviated, State),
{stop, normal, State}
- end.
+ end;
-certify(socket_control, #state{role = server} = State) ->
- {next_state, certify, State};
-certify(hello, State) ->
- {next_state, certify, State};
+abbreviated(Msg, State) ->
+ handle_unexpected_message(Msg, abbreviated, State).
+
+certify(#hello_request{}, State0) ->
+ {Record, State} = next_record(State0),
+ next_state(hello, Record, State);
certify(#certificate{asn1_certificates = []},
#state{role = server, negotiated_version = Version,
@@ -414,53 +483,72 @@ certify(#certificate{asn1_certificates = []},
#state{role = server,
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = false}} =
- State) ->
- {next_state, certify, next_record(State#state{client_certificate_requested = false})};
+ State0) ->
+ {Record, State} = next_record(State0#state{client_certificate_requested = false}),
+ next_state(certify, Record, State);
certify(#certificate{} = Cert,
- #state{session = Session,
- negotiated_version = Version,
+ #state{negotiated_version = Version,
+ role = Role,
cert_db_ref = CertDbRef,
- ssl_options = Opts} = State0) ->
+ ssl_options = Opts} = State) ->
case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
- Opts#ssl_options.verify_fun) of
+ Opts#ssl_options.verify_fun,
+ Opts#ssl_options.validate_extensions_fun, Role) of
{PeerCert, PublicKeyInfo} ->
- State = State0#state{session =
- Session#session{peer_certificate = PeerCert},
- public_key_info = PublicKeyInfo,
- client_certificate_requested = false
- },
- {next_state, certify, next_record(State)};
+ handle_peer_cert(PeerCert, PublicKeyInfo,
+ State#state{client_certificate_requested = false});
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify_certificate, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify_certificate, State),
+ {stop, normal, State}
end;
certify(#server_key_exchange{} = KeyExchangeMsg,
- #state{role = client,
- key_algorithm = Alg} = State)
- when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; Alg == krb5 ->
- NewState = handle_server_key(KeyExchangeMsg, State),
- {next_state, certify, NewState};
+ #state{role = client, negotiated_version = Version,
+ key_algorithm = Alg} = State0)
+ when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 ->
+ case handle_server_key(KeyExchangeMsg, State0) of
+ #state{} = State1 ->
+ {Record, State} = next_record(State1),
+ next_state(certify, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify_server_keyexchange,
+ State0),
+ {stop, normal, State0}
+ end;
certify(#server_key_exchange{},
State = #state{role = client, negotiated_version = Version,
key_algorithm = Alg})
- when Alg == rsa; Alg == dh_dss; Alg == dh_rsa ->
+ when Alg == rsa; Alg == dh_dss; Alg == dh_rsa ->
Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
handle_own_alert(Alert, Version, certify_server_key_exchange, State),
{stop, normal, State};
-certify(KeyExchangeMsg = #server_key_exchange{}, State =
- #state{role = server}) ->
- NewState = handle_clinet_key(KeyExchangeMsg, State),
- {next_state, cipher, NewState};
+certify(#certificate_request{}, State0) ->
+ {Record, State} = next_record(State0#state{client_certificate_requested = true}),
+ next_state(certify, Record, State);
-certify(#certificate_request{}, State) ->
- NewState = State#state{client_certificate_requested = true},
- {next_state, certify, next_record(NewState)};
+%% Master secret was determined with help of server-key exchange msg
+certify(#server_hello_done{},
+ #state{session = #session{master_secret = MasterSecret} = Session,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ premaster_secret = undefined,
+ role = client} = State0) ->
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates0, client) of
+ {MasterSecret, ConnectionStates1} ->
+ State = State0#state{connection_states = ConnectionStates1},
+ client_certify_and_key_exchange(State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_server_hello_done, State0),
+ {stop, normal, State0}
+ end;
+%% Master secret is calculated from premaster_secret
certify(#server_hello_done{},
#state{session = Session0,
connection_states = ConnectionStates0,
@@ -487,7 +575,8 @@ certify(#client_key_exchange{},
negotiated_version = Version}) ->
%% We expect a certificate here
Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, certify_server_waiting_certificate, State),
+ handle_own_alert(Alert, Version,
+ certify_server_waiting_certificate, State),
{stop, normal, State};
@@ -504,9 +593,10 @@ certify(#client_key_exchange{exchange_keys
ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
Session = Session0#session{master_secret = MasterSecret},
- State = State0#state{connection_states = ConnectionStates,
+ State1 = State0#state{connection_states = ConnectionStates,
session = Session},
- {next_state, cipher, next_record(State)};
+ {Record, State} = next_record(State1),
+ next_state(cipher, Record, State);
#alert{} = Alert ->
handle_own_alert(Alert, Version,
certify_client_key_exchange, State0),
@@ -517,12 +607,46 @@ certify(#client_key_exchange{exchange_keys
handle_own_alert(Alert, Version, certify_client_key_exchange,
State0),
{stop, normal, State0}
- end.
+ end;
-cipher(socket_control, #state{role = server} = State) ->
- {next_state, cipher, State};
-cipher(hello, State) ->
- {next_state, cipher, State};
+certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{
+ dh_public = ClientPublicDhKey}},
+ #state{negotiated_version = Version,
+ diffie_hellman_params = #'DHParameter'{prime = P,
+ base = G},
+ diffie_hellman_keys = {_, ServerDhPrivateKey},
+ role = Role,
+ session = Session,
+ connection_states = ConnectionStates0} = State0) ->
+
+ PMpint = crypto:mpint(P),
+ GMpint = crypto:mpint(G),
+ PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey),
+ ServerDhPrivateKey,
+ [PMpint, GMpint]),
+
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, Role) of
+ {MasterSecret, ConnectionStates} ->
+ State1 = State0#state{session =
+ Session#session{master_secret
+ = MasterSecret},
+ connection_states = ConnectionStates},
+
+ {Record, State} = next_record(State1),
+ next_state(cipher, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_client_key_exchange, State0),
+ {stop, normal, State0}
+ end;
+
+certify(Msg, State) ->
+ handle_unexpected_message(Msg, certify, State).
+
+cipher(#hello_request{}, State0) ->
+ {Record, State} = next_record(State0),
+ next_state(hello, Record, State);
cipher(#certificate_verify{signature = Signature},
#state{role = server,
@@ -531,104 +655,83 @@ cipher(#certificate_verify{signature = Signature},
session = #session{master_secret = MasterSecret},
key_algorithm = Algorithm,
tls_handshake_hashes = Hashes
- } = State) ->
+ } = State0) ->
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
Version, MasterSecret,
Algorithm, Hashes) of
valid ->
- {next_state, cipher, next_record(State)};
+ {Record, State} = next_record(State0),
+ next_state(cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, cipher, State0),
+ {stop, normal, State0}
end;
-cipher(#finished{} = Finished,
- State = #state{from = From,
- negotiated_version = Version,
- host = Host,
- port = Port,
- role = Role,
- session = #session{master_secret = MasterSecret}
- = Session0,
- tls_handshake_hashes = Hashes}) ->
-
+cipher(#finished{verify_data = Data} = Finished,
+ #state{negotiated_version = Version,
+ host = Host,
+ port = Port,
+ role = Role,
+ session = #session{master_secret = MasterSecret}
+ = Session0,
+ tls_handshake_hashes = Hashes0,
+ connection_states = ConnectionStates0} = State) ->
+
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
- MasterSecret, Hashes) of
+ MasterSecret, Hashes0) of
verified ->
- gen_fsm:reply(From, connected),
Session = register_session(Role, Host, Port, Session0),
case Role of
client ->
- {next_state, connection,
- next_record_if_active(State#state{session = Session})};
+ ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
+ next_state_connection(cipher, ack_connection(State#state{session = Session,
+ connection_states = ConnectionStates}));
server ->
- {NewConnectionStates, NewHashes} =
- finalize_server_handshake(State#state{
- session = Session}),
- NewState =
- State#state{connection_states = NewConnectionStates,
- session = Session,
- tls_handshake_hashes = NewHashes},
- {next_state, connection, next_record_if_active(NewState)}
+ ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
+ {ConnectionStates, Hashes} =
+ finalize_handshake(State#state{
+ connection_states = ConnectionStates1,
+ session = Session}, cipher),
+ next_state_connection(cipher, ack_connection(State#state{connection_states =
+ ConnectionStates,
+ session = Session,
+ tls_handshake_hashes =
+ Hashes}))
end;
#alert{} = Alert ->
handle_own_alert(Alert, Version, cipher, State),
{stop, normal, State}
- end.
+ end;
-connection(socket_control, #state{role = server} = State) ->
- {next_state, connection, State};
-connection(hello, State = #state{host = Host, port = Port,
- socket = Socket,
- ssl_options = SslOpts,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0,
- tls_handshake_hashes = Hashes0}) ->
+cipher(Msg, State) ->
+ handle_unexpected_message(Msg, cipher, State).
+
+connection(#hello_request{}, #state{host = Host, port = Port,
+ socket = Socket,
+ ssl_options = SslOpts,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _},
+ tls_handshake_hashes = Hashes0} = State0) ->
+
Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates0, SslOpts),
+ ConnectionStates0, SslOpts, Renegotiation),
+
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
- {next_state, hello, State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1}}.
-
-%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}|
-%% {stop, Reason, Reply, NewState}
-%% Description: There should be one instance of this function for each
-%% possible state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:sync_send_event/2,3, the instance of this function with the same
-%% name as the current state name StateName is called to handle the event.
-%%--------------------------------------------------------------------
-connection({application_data, Data0}, _From,
- State = #state{socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0}) ->
- %% We should look into having a worker process to do this to
- %% parallize send and receive decoding and not block the receiver
- %% if sending is overloading the socket.
- try
- Data = encode_packet(Data0, State#state.socket_options),
- {Msgs, ConnectionStates1} = encode_data(Data, Version, ConnectionStates0),
- Result = Transport:send(Socket, Msgs),
- {reply, Result,
- connection, State#state{connection_states = ConnectionStates1}}
-
- catch throw:Error ->
- {reply, Error, connection, State}
- end.
-
+ {Record, State} = next_record(State0#state{connection_states =
+ ConnectionStates1,
+ tls_handshake_hashes = Hashes1}),
+ next_state(hello, Record, State);
+connection(#client_hello{} = Hello, #state{role = server} = State) ->
+ hello(Hello, State);
+
+connection(Msg, State) ->
+ handle_unexpected_message(Msg, connection, State).
%%--------------------------------------------------------------------
%% Function:
%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
@@ -640,84 +743,8 @@ connection({application_data, Data0}, _From,
%% gen_fsm:send_all_state_event/2, this function is called to handle
%% the event.
%%--------------------------------------------------------------------
-handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
- StateName,
- State = #state{key_algorithm = KeyAlg,
- tls_handshake_buffer = Buf0,
- negotiated_version = Version}) ->
- Handle =
- fun({Packet, Raw}, {next_state, SName, AS=#state{tls_handshake_hashes=Hs0}}) ->
- Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, AS#state{tls_handshake_hashes=Hs1});
- (_, StopState) -> StopState
- end,
- try
- {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
- Start = {next_state, StateName, State#state{tls_handshake_buffer = Buf}},
- lists:foldl(Handle, Start, Packets)
- catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State),
- {stop, normal, State}
- end;
-
-handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data},
- StateName, State0) ->
- case application_data(Data, State0) of
- Stop = {stop,_,_} ->
- Stop;
- State ->
- {next_state, StateName, State}
- end;
-
-handle_event(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
- _ChangeCipher,
- StateName,
- State = #state{connection_states = ConnectionStates0}) ->
- ?DBG_TERM(_ChangeCipher),
- ConnectionStates1 =
- ssl_record:activate_pending_connection_state(ConnectionStates0, read),
- {next_state, StateName,
- next_record(State#state{connection_states = ConnectionStates1})};
-
-handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) ->
- Alerts = decode_alerts(Data),
- ?DBG_TERM(Alerts),
- [alert_event(A) || A <- Alerts],
- {next_state, StateName, State};
-
-handle_event(#alert{level = ?FATAL} = Alert, connection,
- #state{from = From, user_application = {_Mon, Pid}, log_alert = Log,
- host = Host, port = Port, session = Session,
- role = Role, socket_options = Opts} = State) ->
- invalidate_session(Role, Host, Port, Session),
- log_alert(Log, connection, Alert),
- alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
- {stop, normal, State};
-handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- connection, #state{from = From,
- role = Role,
- user_application = {_Mon, Pid},
- socket_options = Opts} = State) ->
- alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
- {stop, normal, State};
-
-handle_event(#alert{level = ?FATAL} = Alert, StateName,
- #state{from = From, host = Host, port = Port, session = Session,
- log_alert = Log, role = Role} = State) ->
- invalidate_session(Role, Host, Port, Session),
- log_alert(Log, StateName, Alert),
- alert_user(From, Alert, Role),
- {stop, normal, State};
-handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- _, #state{from = From, role = Role} = State) ->
- alert_user(From, Alert, Role),
- {stop, normal, State};
-handle_event(#alert{level = ?WARNING} = Alert, StateName,
- #state{log_alert = Log} = State) ->
- log_alert(Log, StateName, Alert),
-%%TODO: Could be user_canceled or no_negotiation should the latter be
- %% treated as fatal?!
- {next_state, StateName, next_record(State)}.
+handle_event(_Event, StateName, State) ->
+ {next_state, StateName, State}.
%%--------------------------------------------------------------------
%% Function:
@@ -734,39 +761,81 @@ handle_event(#alert{level = ?WARNING} = Alert, StateName,
%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
%% the event.
%%--------------------------------------------------------------------
-handle_sync_event(started, From, StateName, State) ->
+handle_sync_event({application_data, Data0}, From, connection,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ send_queue = SendQueue,
+ socket_options = SockOpts,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}}
+ = State) ->
+ %% We should look into having a worker process to do this to
+ %% parallize send and receive decoding and not block the receiver
+ %% if sending is overloading the socket.
+ try
+ Data = encode_packet(Data0, SockOpts),
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ {reply, Result,
+ connection, State#state{connection_states = ConnectionStates}};
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData}, SendQueue),
+ renegotiation = {true, internal}})
+ end
+ catch throw:Error ->
+ {reply, Error, connection, State}
+ end;
+handle_sync_event({application_data, Data}, From, StateName,
+ #state{send_queue = Queue} = State) ->
+ %% In renegotiation priorities handshake, send data when handshake is finished
+ {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}};
+
+handle_sync_event(start, From, hello, State) ->
+ hello(start, State#state{from = From});
+
+%% The two clauses below could happen if a server upgrades a socket in
+%% active mode. Note that in this case we are lucky that
+%% controlling_process has been evalueated before receiving handshake
+%% messages from client. The server should put the socket in passive
+%% mode before telling the client that it is willing to upgrade
+%% and before calling ssl:ssl_accept/2. These clauses are
+%% here to make sure it is the users problem and not owers if
+%% they upgrade a active socket.
+handle_sync_event(start, _, connection, State) ->
+ {reply, connected, connection, State};
+handle_sync_event(start, From, StateName, State) ->
{next_state, StateName, State#state{from = From}};
-handle_sync_event(close, From, _StateName, State) ->
- {stop, normal, ok, State#state{from = From}};
+handle_sync_event(close, _, _StateName, State) ->
+ {stop, normal, ok, State};
-handle_sync_event({shutdown, How}, From, StateName,
+handle_sync_event({shutdown, How}, _, StateName,
#state{transport_cb = CbModule,
socket = Socket} = State) ->
case CbModule:shutdown(Socket, How) of
ok ->
{reply, ok, StateName, State};
Error ->
- {stop, normal, Error, State#state{from = From}}
+ {stop, normal, Error, State}
end;
-%% TODO: men vad g�r next_record om det �r t.ex. renegotiate? kanske
-%% inte bra... t�l att t�nkas p�!
-handle_sync_event({recv, N}, From, StateName,
- State0 = #state{user_data_buffer = Buffer}) ->
- State1 = State0#state{bytes_to_read = N, from = From},
- case Buffer of
- <<>> ->
- State = next_record(State1),
- {next_state, StateName, State};
- _ ->
- case application_data(<<>>, State1) of
- Stop = {stop, _, _} ->
- Stop;
- State ->
- {next_state, StateName, State}
- end
- end;
+handle_sync_event({recv, N}, From, connection = StateName, State0) ->
+ passive_receive(State0#state{bytes_to_read = N, from = From}, StateName);
+
+%% Doing renegotiate wait with handling request until renegotiate is
+%% finished. Will be handled by next_state_connection/2.
+handle_sync_event({recv, N}, From, StateName, State) ->
+ {next_state, StateName, State#state{bytes_to_read = N, from = From,
+ recv_during_renegotiation = true}};
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
@@ -801,7 +870,13 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
{reply, ok, StateName, State1};
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
- {reply, ok, StateName, next_record_if_active(State1)};
+ {Record, State2} = next_record_if_active(State1),
+ case next_state(StateName, Record, State2) of
+ {next_state, StateName, State} ->
+ {reply, ok, StateName, State};
+ {stop, Reason, State} ->
+ {stop, Reason, State}
+ end;
Buffer =:= <<>> ->
%% Active once already set
{reply, ok, StateName, State1};
@@ -809,10 +884,21 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
case application_data(<<>>, State1) of
Stop = {stop,_,_} ->
Stop;
- State ->
- {reply, ok, StateName, State}
+ {Record, State2} ->
+ case next_state(StateName, Record, State2) of
+ {next_state, StateName, State} ->
+ {reply, ok, StateName, State};
+ {stop, Reason, State} ->
+ {stop, Reason, State}
+ end
end
- end;
+ end;
+
+handle_sync_event(renegotiate, From, connection, State) ->
+ renegotiate(State#state{renegotiation = {true, From}});
+
+handle_sync_event(renegotiate, _, StateName, State) ->
+ {reply, {error, already_renegotiating}, StateName, State};
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
@@ -834,7 +920,6 @@ handle_sync_event(peer_certificate, _, StateName,
= State) ->
{reply, {ok, Cert}, StateName, State}.
-
%%--------------------------------------------------------------------
%% Function:
%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
@@ -847,50 +932,17 @@ handle_sync_event(peer_certificate, _, StateName,
%%--------------------------------------------------------------------
%% raw data from TCP, unpack records
-handle_info({Protocol, _, Data}, StateName, State =
+handle_info({Protocol, _, Data}, StateName,
#state{data_tag = Protocol,
- negotiated_version = Version,
- tls_record_buffer = Buf0,
- tls_cipher_texts = CT0}) ->
- case ssl_record:get_tls_records(Data, Buf0) of
- {Records, Buf1} ->
- CT1 = CT0 ++ Records,
- {next_state, StateName,
- next_record(State#state{tls_record_buffer = Buf1,
- tls_cipher_texts = CT1})};
+ negotiated_version = Version} = State0) ->
+ case next_tls_record(Data, State0) of
+ {Record, State} ->
+ next_state(StateName, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, StateName, State0),
+ {stop, normal, State0}
end;
-%% %% This is the code for {packet,ssl} removed because it was slower
-%% %% than handling it in erlang.
-%% handle_info(Data = #ssl_tls{}, StateName,
-%% State = #state{tls_buffer = Buffer,
-%% socket = Socket,
-%% connection_states = ConnectionStates0}) ->
-%% case Buffer of
-%% buffer ->
-%% {next_state, StateName, State#state{tls_buffer = [Data]}};
-%% continue ->
-%% inet:setopts(Socket, [{active,once}]),
-%% {Plain, ConnectionStates} =
-%% ssl_record:decode_cipher_text(Data, ConnectionStates0),
-%% gen_fsm:send_all_state_event(self(), Plain),
-%% {next_state, StateName,
-%% State#state{tls_buffer = buffer,
-%% connection_states = ConnectionStates}};
-%% List when is_list(List) ->
-%% {next_state, StateName,
-%% State#state{tls_buffer = Buffer ++ [Data]}}
-%% end;
-
-%% handle_info(CloseMsg = {_, Socket}, StateName0,
-%% #state{socket = Socket,tls_buffer = [Msg]} = State0) ->
-%% %% Hmm we have a ssl_tls msg buffered, handle that first
-%% %% and it proberbly is a close alert
-%% {next_state, StateName0, State0#state{tls_buffer=[Msg,{ssl_close,CloseMsg}]}};
-
handle_info({CloseTag, Socket}, _StateName,
#state{socket = Socket, close_tag = CloseTag,
negotiated_version = Version, host = Host,
@@ -909,13 +961,28 @@ handle_info({CloseTag, Socket}, _StateName,
?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Role),
{stop, normal, State};
+handle_info({ErrorTag, Socket, econnaborted}, StateName,
+ #state{socket = Socket, from = User, role = Role,
+ error_tag = ErrorTag} = State) when StateName =/= connection ->
+ alert_user(User, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role),
+ {stop, normal, State};
+
+handle_info({ErrorTag, Socket, Reason}, _,
+ #state{socket = Socket, from = User,
+ role = Role, error_tag = ErrorTag} = State) ->
+ Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]),
+ error_logger:info_report(Report),
+ alert_user(User, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role),
+ {stop, normal, State};
+
handle_info({'DOWN', MonitorRef, _, _, _}, _,
State = #state{user_application={MonitorRef,_Pid}}) ->
{stop, normal, State};
-handle_info(A, StateName, State) ->
- io:format("SSL: Bad info (state ~w): ~w\n", [StateName, A]),
- {stop, bad_info, State}.
+handle_info(Msg, StateName, State) ->
+ Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
+ error_logger:info_report(Report),
+ {next_state, StateName, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, StateName, State) -> void()
@@ -924,17 +991,25 @@ handle_info(A, StateName, State) ->
%% necessary cleaning up. When it returns, the gen_fsm terminates with
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
-terminate(_Reason, connection, _S=#state{negotiated_version = Version,
+terminate(_Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
- socket = Socket}) ->
+ socket = Socket, send_queue = SendQueue,
+ renegotiation = Renegotiate}) ->
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate),
{BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY),
Version, ConnectionStates),
Transport:send(Socket, BinAlert),
+ workaround_transport_delivery_problems(Socket, Transport),
Transport:close(Socket);
-terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, socket = Socket}) ->
- Transport:close(Socket),
- ok.
+terminate(_Reason, _StateName, #state{transport_cb = Transport,
+ socket = Socket, send_queue = SendQueue,
+ renegotiation = Renegotiate}) ->
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate),
+ workaround_transport_delivery_problems(Socket, Transport),
+ Transport:close(Socket).
%%--------------------------------------------------------------------
%% Function:
@@ -947,18 +1022,21 @@ code_change(_OldVsn, StateName, State, _Extra) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_} = CbInfo,
+start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
case ssl_connection_sup:start_child([Role, Host, Port, Socket,
Opts, User, CbInfo]) of
{ok, Pid} ->
- CbModule:controlling_process(Socket, Pid),
- send_event(Pid, socket_control),
- case sync_send_all_state_event(Pid, started, Timeout) of
- connected ->
- {ok, sslsocket(Pid)};
- {error, Reason} ->
- {error, Reason}
+ case socket_control(Socket, Pid, CbModule) of
+ {ok, SslSocket} ->
+ case handshake(SslSocket, Timeout) of
+ ok ->
+ {ok, SslSocket};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
end;
{error, Reason} ->
{error, Reason}
@@ -969,8 +1047,8 @@ ssl_init(SslOpts, Role) ->
PrivateKey =
init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- ?DBG_TERM(PrivateKey),
- {ok, CertDbRef, CacheRef, OwnCert, PrivateKey}.
+ DHParams = init_diffie_hellman(SslOpts#ssl_options.dhfile, Role),
+ {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}.
init_certificates(#ssl_options{cacertfile = CACertFile,
certfile = CertFile}, Role) ->
@@ -1005,12 +1083,14 @@ init_certificates(CertDbRef, CacheRef, CertFile, server) ->
catch
_E:{badmatch, _R={error,_}} ->
Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, CertFile, erlang:get_stacktrace()]),
+ [?LINE, _E,_R, CertFile,
+ erlang:get_stacktrace()]),
error_logger:error_report(Report),
throw(ecertfile);
_E:_R ->
Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, CertFile, erlang:get_stacktrace()]),
+ [?LINE, _E,_R, CertFile,
+ erlang:get_stacktrace()]),
error_logger:error_report(Report),
throw(ecertfile)
end.
@@ -1021,47 +1101,43 @@ init_private_key(undefined, KeyFile, Password, _) ->
try
{ok, List} = ssl_manager:cache_pem_file(KeyFile),
[Der] = [Der || Der = {PKey, _ , _} <- List,
- PKey =:= rsa_private_key orelse PKey =:= dsa_private_key],
+ PKey =:= rsa_private_key orelse
+ PKey =:= dsa_private_key],
{ok, Decoded} = public_key:decode_private_key(Der,Password),
Decoded
catch
_E:{badmatch, _R={error,_}} ->
Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, KeyFile, erlang:get_stacktrace()]),
+ [?LINE, _E,_R, KeyFile,
+ erlang:get_stacktrace()]),
error_logger:error_report(Report),
throw(ekeyfile);
_E:_R ->
Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, KeyFile, erlang:get_stacktrace()]),
+ [?LINE, _E,_R, KeyFile,
+ erlang:get_stacktrace()]),
error_logger:error_report(Report),
throw(ekeyfile)
end;
init_private_key(PrivateKey, _, _,_) ->
PrivateKey.
-send_event(FsmPid, Event) ->
- gen_fsm:send_event(FsmPid, Event).
-
-sync_send_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_event(FsmPid, Event, Timeout) of
- Reply ->
- Reply
- catch
- exit:{noproc, _} ->
- {error, closed};
- exit:{timeout, _} ->
- {error, timeout};
- exit:{normal, _} ->
- {error, closed}
+init_diffie_hellman(_, client) ->
+ undefined;
+init_diffie_hellman(undefined, _) ->
+ ?DEFAULT_DIFFIE_HELLMAN_PARAMS;
+init_diffie_hellman(DHParamFile, server) ->
+ {ok, List} = ssl_manager:cache_pem_file(DHParamFile),
+ case [Der || Der = {dh_params, _ , _} <- List] of
+ [Der] ->
+ {ok, Decoded} = public_key:decode_dhparams(Der),
+ Decoded;
+ [] ->
+ ?DEFAULT_DIFFIE_HELLMAN_PARAMS
end.
-
-
-send_all_state_event(FsmPid, Event) ->
- gen_fsm:send_all_state_event(FsmPid, Event).
-
sync_send_all_state_event(FsmPid, Event) ->
- sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT).
+ sync_send_all_state_event(FsmPid, Event, infinity).
sync_send_all_state_event(FsmPid, Event, Timeout) ->
try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
@@ -1074,9 +1150,16 @@ sync_send_all_state_event(FsmPid, Event, Timeout) ->
{error, closed}
end.
-%% Events: #alert{}
-alert_event(Alert) ->
- send_all_state_event(self(), Alert).
+%% 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) ->
+ State1 = State0#state{session =
+ Session#session{peer_certificate = PeerCert},
+ public_key_info = PublicKeyInfo},
+ {Record, State} = next_record(State1),
+ next_state(certify, Record, State).
certify_client(#state{client_certificate_requested = true, role = client,
connection_states = ConnectionStates0,
@@ -1111,9 +1194,8 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
ignore -> %% No key or cert or fixed_diffie_hellman
State;
Verified ->
- SigAlg = ssl_handshake:sig_alg(KeyAlg),
{BinVerified, ConnectionStates1, Hashes1} =
- encode_handshake(Verified, SigAlg, Version,
+ encode_handshake(Verified, KeyAlg, Version,
ConnectionStates0, Hashes0),
Transport:send(Socket, BinVerified),
State#state{connection_states = ConnectionStates1,
@@ -1124,29 +1206,34 @@ verify_client_cert(#state{client_certificate_requested = false} = State) ->
do_server_hello(Type, #state{negotiated_version = Version,
session = Session,
- connection_states = ConnectionStates0}
+ connection_states = ConnectionStates0,
+ renegotiation = {Renegotiation, _}}
= State0) when is_atom(Type) ->
ServerHello =
ssl_handshake:server_hello(Session#session.session_id, Version,
- ConnectionStates0),
- State = server_hello(ServerHello, State0),
+ ConnectionStates0, Renegotiation),
+ State1 = server_hello(ServerHello, State0),
case Type of
new ->
- do_server_hello(ServerHello, State);
+ do_server_hello(ServerHello, State1);
resumed ->
+ ConnectionStates1 = State1#state.connection_states,
case ssl_handshake:master_secret(Version, Session,
- ConnectionStates0, server) of
- {_, ConnectionStates1} ->
- State1 = State#state{connection_states=ConnectionStates1,
- session = Session},
- {ConnectionStates, Hashes} = finalize_server_handshake(State1),
- Resumed = State1#state{connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes},
- {next_state, abbreviated, next_record(Resumed)};
+ ConnectionStates1, server) of
+ {_, ConnectionStates2} ->
+ State2 = State1#state{connection_states=ConnectionStates2,
+ session = Session},
+ {ConnectionStates, Hashes} =
+ finalize_handshake(State2, abbreviated),
+ State3 = State2#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hashes},
+ {Record, State} = next_record(State3),
+ next_state(abbreviated, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, hello, State1),
+ {stop, normal, State1}
end
end;
@@ -1157,12 +1244,13 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite,
negotiated_version = Version} = State0) ->
try server_certify_and_key_exchange(State0) of
#state{} = State1 ->
- State = server_hello_done(State1),
+ State2 = server_hello_done(State1),
Session =
Session0#session{session_id = SessionId,
cipher_suite = CipherSuite,
compression_method = Compression},
- {next_state, certify, State#state{session = Session}}
+ {Record, State} = next_record(State2#state{session = Session}),
+ next_state(certify, Record, State)
catch
#alert{} = Alert ->
handle_own_alert(Alert, Version, hello, State0),
@@ -1173,16 +1261,16 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0) ->
try do_client_certify_and_key_exchange(State0) of
State1 = #state{} ->
- {ConnectionStates, Hashes} = finalize_client_handshake(State1),
- State = State1#state{connection_states = ConnectionStates,
+ {ConnectionStates, Hashes} = finalize_handshake(State1, certify),
+ State2 = State1#state{connection_states = ConnectionStates,
%% Reinitialize
client_certificate_requested = false,
tls_handshake_hashes = Hashes},
- {next_state, cipher, next_record(State)}
-
+ {Record, State} = next_record(State2),
+ next_state(cipher, Record, State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify_foo, State0),
+ handle_own_alert(Alert, Version, client_certify_and_key_exchange, State0),
{stop, normal, State0}
end.
@@ -1215,17 +1303,16 @@ server_hello_done(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes} = State0) ->
+ tls_handshake_hashes = Hashes} = State) ->
HelloDone = ssl_handshake:server_hello_done(),
-
+
{BinHelloDone, NewConnectionStates, NewHashes} =
encode_handshake(HelloDone, Version, ConnectionStates, Hashes),
Transport:send(Socket, BinHelloDone),
- State = State0#state{connection_states = NewConnectionStates,
- tls_handshake_hashes = NewHashes},
- next_record(State).
-
+ State#state{connection_states = NewConnectionStates,
+ tls_handshake_hashes = NewHashes}.
+
certify_server(#state{transport_cb = Transport,
socket = Socket,
negotiated_version = Version,
@@ -1252,14 +1339,16 @@ key_exchange(#state{role = server, key_algorithm = Algo} = State)
Algo == dh_rsa ->
State;
-key_exchange(#state{role = server, key_algorithm = rsa_export} = State) ->
+%% Remove or uncomment when we decide if to support export cipher suites
+%%key_exchange(#state{role = server, key_algorithm = rsa_export} = State) ->
%% TODO when the public key in the server certificate is
%% less than or equal to 512 bits in length dont send key_exchange
%% but do it otherwise
- State;
+%% State;
key_exchange(#state{role = server, key_algorithm = Algo,
diffie_hellman_params = Params,
+ private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
tls_handshake_hashes = Hashes0,
@@ -1270,26 +1359,28 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Algo == dhe_dss_export;
Algo == dhe_rsa;
Algo == dhe_rsa_export ->
- Msg = ssl_handshake:key_exchange(server, Params),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
- Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
-key_exchange(#state{role = server, key_algorithm = dh_anon,
- connection_states = ConnectionStates0,
- negotiated_version = Version,
- tls_handshake_hashes = Hashes0,
- socket = Socket,
- transport_cb = Transport
- } = State) ->
- Msg = ssl_handshake:key_exchange(server, anonymous),
- {BinMsg, ConnectionStates1, Hashes1} =
+ Keys = public_key:gen_key(Params),
+ 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, {dh, Keys, Params,
+ Algo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
+ State#state{connection_states = ConnectionStates,
+ diffie_hellman_keys = Keys,
tls_handshake_hashes = Hashes1};
+
+
+%% key_algorithm = dh_anon is not supported. Should be by default disabled
+%% if support is implemented and then we need a key_exchange clause for it
+%% here.
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
@@ -1309,23 +1400,24 @@ key_exchange(#state{role = client,
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
- public_key_info = PublicKeyInfo,
negotiated_version = Version,
- diffie_hellman_params = Params,
- own_cert = Cert,
+ diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
tls_handshake_hashes = Hashes0} = State)
when Algorithm == dhe_dss;
Algorithm == dhe_dss_export;
Algorithm == dhe_rsa;
Algorithm == dhe_rsa_export ->
- Msg = dh_key_exchange(Cert, Params, PublicKeyInfo),
+ Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
tls_handshake_hashes = Hashes1}.
+%% key_algorithm = dh_rsa | dh_dss are not supported. If we want to
+%% support it we need a key_exchange clause for it here.
+
rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
@@ -1334,17 +1426,22 @@ rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
ssl_handshake:key_exchange(client,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
-
rsa_key_exchange(_, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
-dh_key_exchange(OwnCert, Params, PublicKeyInfo) ->
- case public_key:pkix_is_fixed_dh_cert(OwnCert) of
- true ->
- ssl_handshake:key_exchange(client, fixed_diffie_hellman);
- false ->
- ssl_handshake:key_exchange(client, {dh, Params, PublicKeyInfo})
- end.
+%% Uncomment if we decide to support cipher suites with key_algorithm
+%% dh_rsa and dh_dss. Could also be removed if we decide support for
+%% this will not be needed. Not supported by openssl!
+%% dh_key_exchange(OwnCert, DhKeys, true) ->
+%% case public_key:pkix_is_fixed_dh_cert(OwnCert) of
+%% true ->
+%% ssl_handshake:key_exchange(client, fixed_diffie_hellman);
+%% false ->
+%% {DhPubKey, _} = DhKeys,
+%% ssl_handshake:key_exchange(client, {dh, DhPubKey})
+%% end;
+%% dh_key_exchange(_, {DhPubKey, _}, false) ->
+%% ssl_handshake:key_exchange(client, {dh, DhPubKey}).
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
@@ -1364,48 +1461,105 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
State) ->
State.
-finalize_client_handshake(#state{connection_states = ConnectionStates0}
- = State) ->
- ConnectionStates1 =
- cipher_protocol(State#state{connection_states =
- ConnectionStates0}),
- ConnectionStates2 =
- ssl_record:activate_pending_connection_state(ConnectionStates1,
+finalize_handshake(State, StateName) ->
+ ConnectionStates0 = cipher_protocol(State),
+ ConnectionStates =
+ ssl_record:activate_pending_connection_state(ConnectionStates0,
write),
- finished(State#state{connection_states = ConnectionStates2}).
+ finished(State#state{connection_states = ConnectionStates}, StateName).
-
-finalize_server_handshake(State) ->
- ConnectionStates0 = cipher_protocol(State),
- ConnectionStates =
- ssl_record:activate_pending_connection_state(ConnectionStates0, write),
- finished(State#state{connection_states = ConnectionStates}).
-
-cipher_protocol(#state{connection_states = ConnectionStates,
+cipher_protocol(#state{connection_states = ConnectionStates0,
socket = Socket,
negotiated_version = Version,
transport_cb = Transport}) ->
- {BinChangeCipher, NewConnectionStates} =
- encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates),
+ {BinChangeCipher, ConnectionStates} =
+ encode_change_cipher(#change_cipher_spec{},
+ Version, ConnectionStates0),
Transport:send(Socket, BinChangeCipher),
- NewConnectionStates.
+ ConnectionStates.
finished(#state{role = Role, socket = Socket, negotiated_version = Version,
transport_cb = Transport,
session = Session,
- connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes}) ->
+ connection_states = ConnectionStates0,
+ tls_handshake_hashes = Hashes0}, StateName) ->
MasterSecret = Session#session.master_secret,
- Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes),
- {BinFinished, NewConnectionStates, NewHashes} =
- encode_handshake(Finished, Version, ConnectionStates, Hashes),
+ Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0),
+ ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName),
+ {BinFinished, ConnectionStates, Hashes} =
+ encode_handshake(Finished, Version, ConnectionStates1, Hashes0),
Transport:send(Socket, BinFinished),
- {NewConnectionStates, NewHashes}.
+ {ConnectionStates, Hashes}.
+
+save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) ->
+ ssl_record:set_client_verify_data(current_write, Data, ConnectionStates);
+save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, cipher) ->
+ ssl_record:set_server_verify_data(current_both, Data, ConnectionStates);
+save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
+ ssl_record:set_client_verify_data(current_both, Data, ConnectionStates);
+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 = Signed},
+ #state{session = Session, negotiated_version = Version, role = Role,
+ public_key_info = PubKeyInfo,
+ key_algorithm = KeyAlgo,
+ connection_states = ConnectionStates0} = State) ->
+
+ PLen = size(P),
+ GLen = size(G),
+ YLen = size(ServerPublicDhKey),
+
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates0, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), P/binary,
+ ?UINT16(GLen), G/binary,
+ ?UINT16(YLen),
+ ServerPublicDhKey/binary>>),
+
+ case verify_dh_params(Signed, Hash, PubKeyInfo) of
+ true ->
+ PMpint = mpint_binary(P),
+ GMpint = mpint_binary(G),
+ Keys = {_, ClientDhPrivateKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(ServerPublicDhKey),
+ ClientDhPrivateKey, [PMpint, GMpint]),
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, Role) of
+ {MasterSecret, ConnectionStates} ->
+ State#state{diffie_hellman_keys = Keys,
+ session =
+ Session#session{master_secret
+ = MasterSecret},
+ connection_states = ConnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+ false ->
+ ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)
+ end.
-handle_server_key(_KeyExchangeMsg, State) ->
- State.
-handle_clinet_key(_KeyExchangeMsg, State) ->
- State.
+verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) ->
+ case public_key:decrypt_public(Signed, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash ->
+ true;
+ _ ->
+ false
+ end.
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
?DBG_TERM(Alert),
@@ -1442,8 +1596,8 @@ encode_size_packet(Bin, Size, Max) ->
false -> <<Len:Size, Bin/binary>>
end.
-encode_data(Data, Version, ConnectionStates) ->
- ssl_record:encode_data(Data, Version, ConnectionStates).
+encode_data(Data, Version, ConnectionStates, RenegotiateAt) ->
+ ssl_record:encode_data(Data, Version, ConnectionStates, RenegotiateAt).
decode_alerts(Bin) ->
decode_alerts(Bin, []).
@@ -1454,6 +1608,20 @@ decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
decode_alerts(<<>>, Acc) ->
lists:reverse(Acc, []).
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ {Record, State} = next_record(State0),
+ next_state(StateName, Record, State);
+ _ ->
+ case application_data(<<>>, State0) of
+ Stop = {stop, _, _} ->
+ Stop;
+ {Record, State} ->
+ next_state(StateName, Record, State)
+ end
+ end.
+
application_data(Data, #state{user_application = {_Mon, Pid},
socket_options = SOpts,
bytes_to_read = BytesToRead,
@@ -1475,12 +1643,12 @@ application_data(Data, #state{user_application = {_Mon, Pid},
socket_options = SocketOpt
},
if
- SocketOpt#socket_options.active =:= false ->
- State; %% Passive mode, wait for active once or recv
- Buffer =:= <<>> -> %% Active and empty, get more data
- next_record(State);
- true -> %% We have more data
- application_data(<<>>, State)
+ SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->
+ %% Passive mode, wait for active once or recv
+ %% Active and empty, get more data
+ next_record_if_active(State);
+ true -> %% We have more data
+ application_data(<<>>, State)
end;
{error,_Reason} -> %% Invalid packet in packet mode
deliver_packet_error(SOpts, Buffer1, Pid, From),
@@ -1504,39 +1672,82 @@ get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
end;
get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
PacketOpts = [{packet_size, Size}],
- case erlang:decode_packet(Type, Buffer, PacketOpts) of
+ case decode_packet(Type, Buffer, PacketOpts) of
{more, _} ->
{ok, <<>>, Buffer};
Decoded ->
Decoded
end.
-deliver_app_data(SO = #socket_options{active=once}, Data, Pid, From) ->
- send_or_reply(once, Pid, From, format_reply(SO, Data)),
- SO#socket_options{active=false};
-deliver_app_data(SO= #socket_options{active=Active}, Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_reply(SO, Data)),
- SO.
+decode_packet({http, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph, Buffer, PacketOpts);
+decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph_bin, Buffer, PacketOpts);
+decode_packet(Type, Buffer, PacketOpts) ->
+ erlang:decode_packet(Type, Buffer, PacketOpts).
+
+%% Just like with gen_tcp sockets, an ssl socket that has been configured with
+%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
+%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
+%% represent the current state as follows:
+%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
+%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
+%% Note that if the user has explicitly configured the socket to expect
+%% HTTP headers using the {packet, httph} option, we don't do any automatic
+%% switching of states.
+deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(SOpts, Data)),
+ SO = case Data of
+ {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
+ ((Type =:= http) or (Type =:= http_bin)) ->
+ SOpts#socket_options{packet={Type, headers}};
+ http_eoh when tuple_size(Type) =:= 2 ->
+ % End of headers - expect another Request/Response line
+ {Type1, headers} = Type,
+ SOpts#socket_options{packet=Type1};
+ _ ->
+ SOpts
+ end,
+ case Active of
+ once ->
+ SO#socket_options{active=false};
+ _ ->
+ SO
+ end.
-format_reply(#socket_options{active=false, mode=Mode, header=Header}, Data) ->
- {ok, format_reply(Mode, Header, Data)};
-format_reply(#socket_options{active=_, mode=Mode, header=Header}, Data) ->
- {ssl, sslsocket(), format_reply(Mode, Header, Data)}.
+format_reply(#socket_options{active = false, mode = Mode, packet = Packet,
+ header = Header}, Data) ->
+ {ok, format_reply(Mode, Packet, Header, Data)};
+format_reply(#socket_options{active = _, mode = Mode, packet = Packet,
+ header = Header}, Data) ->
+ {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}.
-deliver_packet_error(SO= #socket_options{active=Active}, Data, Pid, From) ->
+deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) ->
send_or_reply(Active, Pid, From, format_packet_error(SO, Data)).
-format_packet_error(#socket_options{active=false, mode=Mode}, Data) ->
- {error, {invalid_packet, format_reply(Mode, raw, Data)}};
-format_packet_error(#socket_options{active=_, mode=Mode}, Data) ->
- {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, Data)}}.
-
-format_reply(list, _, Data) -> binary_to_list(Data);
-format_reply(binary, 0, Data) -> Data;
-format_reply(binary, raw, Data) -> Data;
-format_reply(binary, N, Data) -> % Header mode
- <<Header:N/binary, Rest/binary>> = Data,
- [binary_to_list(Header), Rest].
+format_packet_error(#socket_options{active = false, mode = Mode}, Data) ->
+ {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}};
+format_packet_error(#socket_options{active = _, mode = Mode}, Data) ->
+ {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}.
+
+format_reply(binary, _, N, Data) when N > 0 -> % Header mode
+ header(N, Data);
+format_reply(binary, _, _, Data) -> Data;
+format_reply(list, Packet, _, Data) when is_integer(Packet); Packet == raw ->
+ binary_to_list(Data);
+format_reply(list, _,_, Data) ->
+ Data.
+
+header(0, <<>>) ->
+ <<>>;
+header(_, <<>>) ->
+ [];
+header(0, Binary) ->
+ Binary;
+header(N, Binary) ->
+ <<?BYTE(ByteN), NewBinary/binary>> = Binary,
+ [ByteN | header(N-1, NewBinary)].
%% tcp_closed
send_or_reply(false, _Pid, undefined, _Data) ->
@@ -1557,50 +1768,137 @@ opposite_role(server) ->
send_user(Pid, Msg) ->
Pid ! Msg.
-%% %% This is the code for {packet,ssl} removed because it was slower
-%% %% than handling it in erlang.
-%% next_record(#state{socket = Socket,
-%% tls_buffer = [Msg|Rest],
-%% connection_states = ConnectionStates0} = State) ->
-%% Buffer =
-%% case Rest of
-%% [] ->
-%% inet:setopts(Socket, [{active,once}]),
-%% buffer;
-%% _ -> Rest
-%% end,
-%% case Msg of
-%% #ssl_tls{} ->
-%% {Plain, ConnectionStates} =
-%% ssl_record:decode_cipher_text(Msg, ConnectionStates0),
-%% gen_fsm:send_all_state_event(self(), Plain),
-%% State#state{tls_buffer=Buffer, connection_states = ConnectionStates};
-%% {ssl_close, Msg} ->
-%% self() ! Msg,
-%% State#state{tls_buffer=Buffer}
-%% end;
-%% next_record(#state{socket = Socket, tls_buffer = undefined} = State) ->
-%% inet:setopts(Socket, [{active,once}]),
-%% State#state{tls_buffer=continue};
-%% next_record(State) ->
-%% State#state{tls_buffer=continue}.
+next_state(Next, no_record, State) ->
+ {next_state, Next, State};
+
+next_state(Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
+ Alerts = decode_alerts(EncAlerts),
+ handle_alerts(Alerts, {next_state, Next, State});
+
+next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ State0 = #state{key_algorithm = KeyAlg,
+ tls_handshake_buffer = Buf0,
+ negotiated_version = Version}) ->
+ Handle =
+ fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_hashes(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0,
+ renegotiation = {true, peer}});
+ ({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Already in negotiation so it will be ignored!
+ ?MODULE:SName(Packet, State);
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ Hs0 = ssl_handshake:init_hashes(),
+ Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ renegotiation = {true, peer}});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
+ Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
+ (_, StopState) -> StopState
+ end,
+ try
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
+ Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}},
+ lists:foldl(Handle, Start, Packets)
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State0),
+ {stop, normal, State0}
+ end;
+
+next_state(StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) ->
+ case application_data(Data, State0) of
+ Stop = {stop,_,_} ->
+ Stop;
+ {Record, State} ->
+ next_state(StateName, Record, State)
+ end;
+next_state(StateName, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
+ _ChangeCipher,
+ #state{connection_states = ConnectionStates0} = State0) ->
+ ?DBG_TERM(_ChangeCipher),
+ ConnectionStates1 =
+ ssl_record:activate_pending_connection_state(ConnectionStates0, read),
+ {Record, State} = next_record(State0#state{connection_states = ConnectionStates1}),
+ next_state(StateName, Record, State);
+next_state(StateName, #ssl_tls{type = _Unknown}, State0) ->
+ %% Ignore unknown type
+ {Record, State} = next_record(State0),
+ next_state(StateName, Record, State).
+
+next_tls_record(Data, #state{tls_record_buffer = Buf0,
+ tls_cipher_texts = CT0} = State0) ->
+ case ssl_record:get_tls_records(Data, Buf0) of
+ {Records, Buf1} ->
+ CT1 = CT0 ++ Records,
+ next_record(State0#state{tls_record_buffer = Buf1,
+ tls_cipher_texts = CT1});
+ #alert{} = Alert ->
+ Alert
+ end.
next_record(#state{tls_cipher_texts = [], socket = Socket} = State) ->
inet:setopts(Socket, [{active,once}]),
- State;
+ {no_record, State};
next_record(#state{tls_cipher_texts = [CT | Rest],
connection_states = ConnStates0} = State) ->
{Plain, ConnStates} = ssl_record:decode_cipher_text(CT, ConnStates0),
- gen_fsm:send_all_state_event(self(), Plain),
- State#state{tls_cipher_texts = Rest, connection_states = ConnStates}.
+ {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}}.
next_record_if_active(State =
#state{socket_options =
#socket_options{active = false}}) ->
- State;
+ {no_record ,State};
+
next_record_if_active(State) ->
next_record(State).
+next_state_connection(StateName, #state{send_queue = Queue0,
+ negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}
+ } = State) ->
+ %% Send queued up data
+ case queue:out(Queue0) of
+ {{value, {From, Data}}, Queue} ->
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ gen_fsm:reply(From, Result),
+ next_state_connection(StateName,
+ State#state{connection_states = ConnectionStates,
+ send_queue = Queue});
+ %% This is unlikely to happen. User configuration of the
+ %% undocumented test option renegotiation_at can make it more likely.
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData}, Queue),
+ renegotiation = {true, internal}})
+ end;
+ {empty, Queue0} ->
+ next_state_is_connection(State)
+ end.
+
+next_state_is_connection(State =
+ #state{recv_during_renegotiation = true, socket_options =
+ #socket_options{active = false}}) ->
+ passive_receive(State#state{recv_during_renegotiation = false}, connection);
+
+next_state_is_connection(State0) ->
+ {Record, State} = next_record_if_active(State0),
+ next_state(connection, Record, State).
+
register_session(_, _, _, #session{is_resumable = true} = Session) ->
Session; %% Already registered
register_session(client, Host, Port, Session0) ->
@@ -1618,7 +1916,7 @@ invalidate_session(server, _, Port, Session) ->
ssl_manager:invalidate_session(Port, Session).
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
- {CbModule, DataTag, CloseTag}) ->
+ {CbModule, DataTag, CloseTag, ErrorTag}) ->
ConnectionStates = ssl_record:init_connection_states(Role),
SessionCacheCb = case application:get_env(ssl, session_cb) of
@@ -1638,6 +1936,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
transport_cb = CbModule,
data_tag = DataTag,
close_tag = CloseTag,
+ error_tag = ErrorTag,
role = Role,
host = Host,
port = Port,
@@ -1650,7 +1949,10 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
bytes_to_read = 0,
user_data_buffer = <<>>,
log_alert = true,
- session_cache_cb = SessionCacheCb
+ session_cache_cb = SessionCacheCb,
+ renegotiation = {false, first},
+ recv_during_renegotiation = false,
+ send_queue = queue:new()
}.
sslsocket(Pid) ->
@@ -1665,8 +1967,12 @@ get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) ->
get_socket_opts(Socket, Tags, SockOpts,
[{mode, SockOpts#socket_options.mode} | Acc]);
get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) ->
- get_socket_opts(Socket, Tags, SockOpts,
- [{packet, SockOpts#socket_options.packet} | Acc]);
+ case SockOpts#socket_options.packet of
+ {Type, headers} ->
+ get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]);
+ Type ->
+ get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc])
+ end;
get_socket_opts(Socket, [header | Tags], SockOpts, Acc) ->
get_socket_opts(Socket, Tags, SockOpts,
[{header, SockOpts#socket_options.header} | Acc]);
@@ -1688,7 +1994,8 @@ set_socket_opts(Socket, [], SockOpts, Other) ->
inet:setopts(Socket, Other),
SockOpts;
set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) ->
- set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other);
+ set_socket_opts(Socket, Opts,
+ SockOpts#socket_options{mode = Mode}, Other);
set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
@@ -1701,10 +2008,71 @@ set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) ->
set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) ->
set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]).
+handle_alerts([], Result) ->
+ Result;
+handle_alerts(_, {stop, _, _} = Stop) ->
+ %% If it is a fatal alert immediately close
+ Stop;
+handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+ handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
+
+handle_alert(#alert{level = ?FATAL} = Alert, connection,
+ #state{from = From, user_application = {_Mon, Pid},
+ log_alert = Log,
+ host = Host, port = Port, session = Session,
+ role = Role, socket_options = Opts} = State) ->
+ invalidate_session(Role, Host, Port, Session),
+ log_alert(Log, connection, Alert),
+ alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
+ {stop, normal, State};
+
+handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
+ connection, #state{from = From,
+ role = Role,
+ user_application = {_Mon, Pid},
+ socket_options = Opts} = State) ->
+ alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
+ {stop, normal, State};
+
+handle_alert(#alert{level = ?FATAL} = Alert, StateName,
+ #state{from = From, host = Host, port = Port, session = Session,
+ log_alert = Log, role = Role} = State) ->
+ invalidate_session(Role, Host, Port, Session),
+ log_alert(Log, StateName, Alert),
+ alert_user(From, Alert, Role),
+ {stop, normal, State};
+handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
+ _, #state{from = From, role = Role} = State) ->
+ alert_user(From, Alert, Role),
+ {stop, normal, State};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{log_alert = Log, renegotiation = {true, internal}, from = From,
+ role = Role} = State) ->
+ log_alert(Log, StateName, Alert),
+ alert_user(From, Alert, Role),
+ {stop, normal, State};
+
+handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{log_alert = Log, renegotiation = {true, From}} = State0) ->
+ log_alert(Log, StateName, Alert),
+ gen_fsm:reply(From, {error, renegotiation_rejected}),
+ {Record, State} = next_record(State0),
+ next_state(connection, Record, State);
+
+handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
+ #state{log_alert = Log} = State0) ->
+ log_alert(Log, StateName, Alert),
+ {Record, State} = next_record(State0),
+ next_state(StateName, Record, State).
+
alert_user(From, Alert, Role) ->
alert_user(false, no_pid, From, Alert, Role).
alert_user(false = Active, Pid, From, Alert, Role) ->
+ %% If there is an outstanding ssl_accept | recv
+ %% From will be defined and send_or_reply will
+ %% send the appropriate error message.
ReasonCode = ssl_alert:reason_code(Alert, Role),
send_or_reply(Active, Pid, From, {error, ReasonCode});
alert_user(Active, Pid, From, Alert, Role) ->
@@ -1732,7 +2100,7 @@ handle_own_alert(Alert, Version, StateName,
log_alert = Log}) ->
try %% Try to tell the other side
{BinMsg, _} =
- encode_alert(Alert, Version, ConnectionStates),
+ encode_alert(Alert, Version, ConnectionStates),
Transport:send(Socket, BinMsg)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
@@ -1744,6 +2112,70 @@ handle_own_alert(Alert, Version, StateName,
ok
end.
-make_premaster_secret({MajVer, MinVer}) ->
+handle_unexpected_message(_Msg, StateName, #state{negotiated_version = Version} = State) ->
+ Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
+ handle_own_alert(Alert, Version, StateName, State),
+ {stop, normal, State}.
+
+make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa;
+ Alg == dh_dss;
+ Alg == dh_rsa ->
Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
- <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>.
+ <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
+make_premaster_secret(_, _) ->
+ undefined.
+
+mpint_binary(Binary) ->
+ Size = byte_size(Binary),
+ <<?UINT32(Size), Binary/binary>>.
+
+
+ack_connection(#state{renegotiation = {true, Initiater}} = State)
+ when Initiater == internal;
+ Initiater == peer ->
+ State#state{renegotiation = undefined};
+ack_connection(#state{renegotiation = {true, From}} = State) ->
+ gen_fsm:reply(From, ok),
+ State#state{renegotiation = undefined};
+ack_connection(#state{renegotiation = {false, first},
+ from = From} = State) when From =/= undefined ->
+ gen_fsm:reply(From, connected),
+ State#state{renegotiation = undefined};
+ack_connection(State) ->
+ State.
+
+renegotiate(#state{role = client} = State) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_hashes(),
+ connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+renegotiate(#state{role = server,
+ socket = Socket,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State0) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined),
+ Hs0 = ssl_handshake:init_hashes(),
+ {BinMsg, ConnectionStates} =
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ {Record, State} = next_record(State0#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hs0}),
+ next_state(hello, Record, State).
+
+notify_senders(SendQueue) ->
+ lists:foreach(fun({From, _}) ->
+ gen_fsm:reply(From, {error, closed})
+ end, queue:to_list(SendQueue)).
+
+notify_renegotiater({true, From}) when not is_atom(From) ->
+ gen_fsm:reply(From, {error, closed});
+notify_renegotiater(_) ->
+ ok.
+
+workaround_transport_delivery_problems(Socket, Transport) ->
+ inet:setopts(Socket, [{active, false}]),
+ Transport:shutdown(Socket, write),
+ Transport:recv(Socket, 0).
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 8c598135ca..54938e0fbc 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,11 +31,11 @@
-include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/4, server_hello/3, hello/2,
- certify/5, certificate/3,
+-export([master_secret/4, client_hello/5, server_hello/4, hello/4,
+ hello_request/0, certify/7, certificate/3,
client_certificate_verify/6,
certificate_verify/6, certificate_request/2,
- key_exchange/2, finished/4,
+ key_exchange/2, server_key_exchange_hash/2, finished/4,
verify_connection/5,
get_tls_handshake/4,
server_hello_done/0, sig_alg/1,
@@ -57,7 +57,7 @@
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
ciphers = Ciphers}
- = SslOpts) ->
+ = SslOpts, Renegotiation) ->
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
@@ -70,22 +70,25 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
#client_hello{session_id = Id,
client_version = Version,
- cipher_suites = Ciphers,
+ cipher_suites = cipher_suites(Ciphers, Renegotiation),
compression_methods = ssl_record:compressions(),
- random = SecParams#security_parameters.client_random
+ random = SecParams#security_parameters.client_random,
+ renegotiation_info =
+ renegotiation_info(client, ConnectionStates, Renegotiation)
}.
%%--------------------------------------------------------------------
-%% Function: server_hello(Host, Port, SessionId,
-%% Version, ConnectionStates) -> #server_hello{}
+%% Function: server_hello(SessionId, Version,
+%% ConnectionStates, Renegotiation) -> #server_hello{}
%% SessionId
%% Version
-%% ConnectionStates
+%% ConnectionStates
+%% Renegotiation
%%
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates) ->
+server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
#server_hello{server_version = Version,
@@ -93,31 +96,56 @@ server_hello(SessionId, Version, ConnectionStates) ->
compression_method =
SecParams#security_parameters.compression_algorithm,
random = SecParams#security_parameters.server_random,
- session_id = SessionId
+ session_id = SessionId,
+ renegotiation_info =
+ renegotiation_info(server, ConnectionStates, Renegotiation)
}.
%%--------------------------------------------------------------------
-%% Function: hello(Hello, Info) ->
+%% Function: hello_request() -> #hello_request{}
+%%
+%% Description: Creates a hello request message sent by server to
+%% trigger renegotiation.
+%%--------------------------------------------------------------------
+hello_request() ->
+ #hello_request{}.
+
+%%--------------------------------------------------------------------
+%% Function: hello(Hello, Info, Renegotiation) ->
%% {Version, Id, NewConnectionStates} |
%% #alert{}
%%
%% Hello = #client_hello{} | #server_hello{}
-%% Info = ConnectionStates | {Port, Session, ConnectionStates}
+%% Info = ConnectionStates | {Port, #ssl_options{}, Session,
+%% Cahce, CahceCb, ConnectionStates}
%% ConnectionStates = #connection_states{}
+%% Renegotiation = boolean()
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
- session_id = SessionId}, ConnectionStates) ->
- NewConnectionStates =
- hello_pending_connection_states(client, CipherSuite, Random,
- Compression, ConnectionStates),
- {Version, SessionId, NewConnectionStates};
-
-hello(#client_hello{client_version = ClientVersion, random = Random} = Hello,
- {Port, #ssl_options{versions = Versions} = SslOpts,
- Session0, Cache, CacheCb, ConnectionStates0}) ->
+ session_id = SessionId, renegotiation_info = Info},
+ #ssl_options{secure_renegotiate = SecureRenegotation},
+ ConnectionStates0, Renegotiation) ->
+
+ case handle_renegotiation_info(client, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation, []) of
+ {ok, ConnectionStates1} ->
+ ConnectionStates =
+ hello_pending_connection_states(client, CipherSuite, Random,
+ Compression, ConnectionStates1),
+ {Version, SessionId, ConnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+
+hello(#client_hello{client_version = ClientVersion, random = Random,
+ cipher_suites = CipherSuites,
+ renegotiation_info = Info} = Hello,
+ #ssl_options{versions = Versions,
+ secure_renegotiate = SecureRenegotation} = SslOpts,
+ {Port, Session0, Cache, CacheCb, ConnectionStates0}, Renegotiation) ->
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
@@ -129,13 +157,20 @@ hello(#client_hello{client_version = ClientVersion, random = Random} = Hello,
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- ConnectionStates =
- hello_pending_connection_states(server,
- CipherSuite,
- Random,
- Compression,
- ConnectionStates0),
- {Version, {Type, Session}, ConnectionStates}
+ case handle_renegotiation_info(server, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation,
+ CipherSuites) of
+ {ok, ConnectionStates1} ->
+ ConnectionStates =
+ hello_pending_connection_states(server,
+ CipherSuite,
+ Random,
+ Compression,
+ ConnectionStates1),
+ {Version, {Type, Session}, ConnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end
end;
false ->
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
@@ -152,10 +187,25 @@ hello(#client_hello{client_version = ClientVersion, random = Random} = Hello,
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
- MaxPathLen, Verify, VerifyFun) ->
+ MaxPathLen, Verify, VerifyFun, ValidateFun, Role) ->
[PeerCert | _] = ASN1Certs,
VerifyBool = verify_bool(Verify),
-
+
+ ValidateExtensionFun =
+ case ValidateFun of
+ undefined ->
+ fun(Extensions, ValidationState, Verify0, AccError) ->
+ ssl_certificate:validate_extensions(Extensions, ValidationState,
+ [], Verify0, AccError, Role)
+ end;
+ Fun ->
+ fun(Extensions, ValidationState, Verify0, AccError) ->
+ {NewExtensions, NewValidationState, NewAccError}
+ = ssl_certificate:validate_extensions(Extensions, ValidationState,
+ [], Verify0, AccError, Role),
+ Fun(NewExtensions, NewValidationState, Verify0, NewAccError)
+ end
+ end,
try
%% Allow missing root_cert and check that with VerifyFun
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of
@@ -165,6 +215,8 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
[{max_path_length,
MaxPathLen},
{verify, VerifyBool},
+ {validate_extensions_fun,
+ ValidateExtensionFun},
{acc_errors,
VerifyErrors}]),
case Result of
@@ -248,8 +300,10 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm,
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
certificate_verify(Signature, {_, PublicKey, _}, Version,
- MasterSecret, Algorithm, {_, Hashes0})
- when Algorithm =:= rsa; Algorithm =:= dh_rsa; Algorithm =:= dhe_rsa ->
+ MasterSecret, Algorithm, {_, Hashes0})
+ when Algorithm == rsa;
+ Algorithm == dh_rsa;
+ Algorithm == dhe_rsa ->
Hashes = calc_certificate_verify(Version, MasterSecret,
Algorithm, Hashes0),
case public_key:decrypt_public(Signature, PublicKey,
@@ -291,38 +345,40 @@ key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) ->
EncPremasterSecret =
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-key_exchange(client, fixed_diffie_hellman) ->
- #client_key_exchange{exchange_keys =
- #client_diffie_hellman_public{
- dh_public = <<>>
- }};
-key_exchange(client, {dh, PublicKey}) ->
- Len = byte_size(PublicKey),
+
+%% Uncomment if dh_rsa and dh_dss cipher suites should
+%% be supported.
+%% key_exchange(client, fixed_diffie_hellman) ->
+%% #client_key_exchange{exchange_keys =
+%% #client_diffie_hellman_public{
+%% dh_public = <<>>
+%% }};
+key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
- exchange_keys = #client_diffie_hellman_public{
- dh_public = <<?UINT16(Len), PublicKey/binary>>}
+ exchange_keys = #client_diffie_hellman_public{
+ dh_public = PublicKey}
};
-%% key_exchange(server, {{?'dhpublicnumber', _PublicKey,
-%% #'DomainParameters'{p = P, g = G, y = Y},
-%% SignAlgorithm, ClientRandom, ServerRandom}}) ->
-%% ServerDHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
-%% PLen = byte_size(P),
-%% GLen = byte_size(G),
-%% YLen = byte_size(Y),
-%% Hash = server_key_exchange_hash(SignAlgorithm, <<ClientRandom/binary,
-%% ServerRandom/binary,
-%% ?UINT16(PLen), P/binary,
-%% ?UINT16(GLen), G/binary,
-%% ?UINT16(YLen), Y/binary>>),
-%% Signed = digitally_signed(Hash, PrivateKey),
-%% #server_key_exchange{
-%% params = ServerDHParams,
-%% signed_params = Signed
-%% };
-key_exchange(_, _) ->
- %%TODO : Real imp
- #server_key_exchange{}.
+key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
+ #'DHParameter'{prime = P, base = G},
+ KeyAlgo, 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},
+
+ Hash =
+ server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), PBin/binary,
+ ?UINT16(GLen), GBin/binary,
+ ?UINT16(YLen), PublicKey/binary>>),
+ Signed = digitally_signed(Hash, PrivateKey),
+ #server_key_exchange{params = ServerDHParams,
+ signed_params = Signed}.
%%--------------------------------------------------------------------
%% Function: master_secret(Version, Session/PremasterSecret,
@@ -417,7 +473,8 @@ server_hello_done() ->
%%
%% encode a handshake packet to binary
%%--------------------------------------------------------------------
-encode_handshake(Package, Version, SigAlg) ->
+encode_handshake(Package, Version, KeyAlg) ->
+ SigAlg = sig_alg(KeyAlg),
{MsgType, Bin} = enc_hs(Package, Version, SigAlg),
Len = byte_size(Bin),
[MsgType, ?uint24(Len), Bin].
@@ -437,32 +494,16 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) ->
get_tls_handshake_aux(list_to_binary([Buffer, Data]),
KeyAlg, Version, []).
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>,
- KeyAlg, Version, Acc) ->
+get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+ Body:Length/binary,Rest/binary>>, KeyAlg,
+ Version, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body, KeyAlg, Version),
+ H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version),
get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]);
get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) ->
{lists:reverse(Acc), Data}.
%%--------------------------------------------------------------------
-%% Function: sig_alg(atom()) -> integer()
-%%
-%% Description: Convert from key exchange as atom to signature
-%% algorithm as a ?SIGNATURE_... constant
-%%--------------------------------------------------------------------
-
-sig_alg(dh_anon) ->
- ?SIGNATURE_ANONYMOUS;
-sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa ->
- ?SIGNATURE_RSA;
-sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss ->
- ?SIGNATURE_DSA;
-sig_alg(_) ->
- ?NULL.
-
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
verify_bool(verify_peer) ->
@@ -510,7 +551,109 @@ select_session(Hello, Port, Session, Version,
false ->
{resumed, CacheCb:lookup(Cache, {Port, SessionId})}
end.
-
+
+
+cipher_suites(Suites, false) ->
+ [?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
+cipher_suites(Suites, true) ->
+ Suites.
+
+renegotiation_info(client, _, false) ->
+ #renegotiation_info{renegotiated_connection = undefined};
+renegotiation_info(server, ConnectionStates, false) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ #renegotiation_info{renegotiated_connection = ?byte(0)};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+renegotiation_info(client, ConnectionStates, true) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ Data = CS#connection_state.client_verify_data,
+ #renegotiation_info{renegotiated_connection = Data};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end;
+
+renegotiation_info(server, ConnectionStates, true) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case CS#connection_state.secure_renegotiation of
+ true ->
+ CData = CS#connection_state.client_verify_data,
+ SData =CS#connection_state.server_verify_data,
+ #renegotiation_info{renegotiated_connection = <<CData/binary, SData/binary>>};
+ false ->
+ #renegotiation_info{renegotiated_connection = undefined}
+ end.
+
+handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
+ ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+
+handle_renegotiation_info(server, undefined, ConnectionStates, _, _, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
+ false ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
+ end;
+
+handle_renegotiation_info(_, undefined, ConnectionStates, false, _, _) ->
+ {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
+
+handle_renegotiation_info(client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
+ ConnectionStates, true, _, _) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ CData = CS#connection_state.client_verify_data,
+ SData = CS#connection_state.server_verify_data,
+ case <<CData/binary, SData/binary>> == ClientServerVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end;
+handle_renegotiation_info(server, #renegotiation_info{renegotiated_connection = ClientVerify},
+ ConnectionStates, true, _, CipherSuites) ->
+
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ false ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ Data = CS#connection_state.client_verify_data,
+ case Data == ClientVerify of
+ true ->
+ {ok, ConnectionStates};
+ false ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end
+ end;
+
+handle_renegotiation_info(client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
+ handle_renegotiation_info(ConnectionStates, SecureRenegotation);
+
+handle_renegotiation_info(server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
+ case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
+ true ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ false ->
+ handle_renegotiation_info(ConnectionStates, SecureRenegotation)
+ end.
+
+handle_renegotiation_info(ConnectionStates, SecureRenegotation) ->
+ CS = ssl_record:current_connection_state(ConnectionStates, read),
+ case {SecureRenegotation, CS#connection_state.secure_renegotiation} of
+ {_, true} ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ {true, false} ->
+ ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION);
+ {false, false} ->
+ {ok, ConnectionStates}
+ end.
+
%% Update pending connection states with parameters exchanged via
%% hello messages
%% NOTE : Role is the role of the receiver of the hello message
@@ -621,46 +764,69 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
random = ssl_ssl2:client_random(ChallengeData, CDLength),
session_id = 0,
cipher_suites = from_3bytes(CipherSuites),
- compression_methods = [?NULL]
+ compression_methods = [?NULL],
+ renegotiation_info = undefined
};
dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- _FutureCompatData/binary>>,
+ Extensions/binary>>,
_, _) ->
+
+ RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
+ undefined),
#client_hello{
client_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suites = from_2bytes(CipherSuites),
- compression_methods = Comp_methods
+ compression_methods = Comp_methods,
+ renegotiation_info = RenegotiationInfo
};
+
dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method)>>, _, _) ->
+ Cipher_suite:2/binary, ?BYTE(Comp_method)>>, _, _) ->
#server_hello{
server_version = {Major,Minor},
random = Random,
session_id = Session_ID,
cipher_suite = Cipher_suite,
- compression_method = Comp_method
- };
+ compression_method = Comp_method,
+ renegotiation_info = undefined};
+
+dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
+ ?BYTE(SID_length), Session_ID:SID_length/binary,
+ Cipher_suite:2/binary, ?BYTE(Comp_method),
+ ?UINT16(ExtLen), Extensions:ExtLen/binary>>, _, _) ->
+
+ RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
+ undefined),
+ #server_hello{
+ server_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suite = Cipher_suite,
+ compression_method = Comp_method,
+ renegotiation_info = RenegotiationInfo};
dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
- ?UINT16(ExpLen), Exp:ExpLen/binary,
- Sig/binary>>,
- ?KEY_EXCHANGE_RSA, _) ->
- #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
- rsa_exponent = Exp},
- signed_params = Sig};
+%% Uncomment if support for export ciphers is added.
+%% dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
+%% ?UINT16(ExpLen), Exp:ExpLen/binary,
+%% ?UINT16(_), Sig/binary>>,
+%% ?KEY_EXCHANGE_RSA, _) ->
+%% #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
+%% rsa_exponent = Exp},
+%% signed_params = Sig};
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
- Sig/binary>>,
+ ?UINT16(_), Sig/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y},
+ #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
+ dh_y = Y},
signed_params = Sig};
dec_hs(?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
@@ -672,23 +838,51 @@ dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
#server_hello_done{};
dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, rsa, {3, 0}) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
#client_key_exchange{exchange_keys = PreSecret};
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, rsa, _) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA, _) ->
PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
#client_key_exchange{exchange_keys = PreSecret};
dec_hs(?CLIENT_KEY_EXCHANGE, <<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
%% TODO: Should check whether the cert already contains a suitable DH-key (7.4.7.2)
throw(?ALERT_REC(?FATAL, implicit_public_value_encoding));
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YCLen), DH_YC:DH_YCLen/binary>>,
+dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #client_diffie_hellman_public{dh_public = DH_YC};
+ #client_key_exchange{exchange_keys =
+ #client_diffie_hellman_public{dh_public = DH_Y}};
dec_hs(?FINISHED, VerifyData, _, _) ->
#finished{verify_data = VerifyData};
dec_hs(_, _, _, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+dec_hello_extensions(<<>>) ->
+ [];
+dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
+ dec_hello_extensions(Extensions, []);
+dec_hello_extensions(_) ->
+ [].
+
+dec_hello_extensions(<<>>, Acc) ->
+ Acc;
+dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binary, Rest/binary>>, Acc) ->
+ RenegotiateInfo = case Len of
+ 1 -> % Initial handshake
+ Info; % should be <<0>> will be matched in handle_renegotiation_info
+ _ ->
+ VerifyLen = Len - 1,
+ <<?BYTE(VerifyLen), VerifyInfo/binary>> = Info,
+ VerifyInfo
+ end,
+ dec_hello_extensions(Rest, [{renegotiation_info,
+ #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
+dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len, Rest/binary>>, Acc) ->
+ dec_hello_extensions(Rest, Acc);
+%% Need this clause?
+dec_hello_extensions(_, Acc) ->
+ Acc.
+
encrypted_premaster_secret(Secret, RSAPublicKey) ->
try
PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
@@ -725,54 +919,62 @@ certs_from_list(ACList) ->
enc_hs(#hello_request{}, _Version, _) ->
{?HELLO_REQUEST, <<>>};
-enc_hs(#client_hello{
- client_version = {Major, Minor},
- random = Random,
- session_id = SessionID,
- cipher_suites = CipherSuites,
- compression_methods = CompMethods}, _Version, _) ->
+enc_hs(#client_hello{client_version = {Major, Minor},
+ random = Random,
+ session_id = SessionID,
+ cipher_suites = CipherSuites,
+ compression_methods = CompMethods,
+ renegotiation_info = RenegotiationInfo}, _Version, _) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
+ Extensions = hello_extensions(RenegotiationInfo),
+ ExtensionsBin = enc_hello_extensions(Extensions),
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
?UINT16(CsLength), BinCipherSuites/binary,
- ?BYTE(CmLength), BinCompMethods/binary>>};
-enc_hs(#server_hello{
- server_version = {Major, Minor},
- random = Random,
- session_id = Session_ID,
- cipher_suite = Cipher_suite,
- compression_method = Comp_method}, _Version, _) ->
+ ?BYTE(CmLength), BinCompMethods/binary, ExtensionsBin/binary>>};
+
+enc_hs(#server_hello{server_version = {Major, Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suite = Cipher_suite,
+ compression_method = Comp_method,
+ renegotiation_info = RenegotiationInfo}, _Version, _) ->
SID_length = byte_size(Session_ID),
+ Extensions = hello_extensions(RenegotiationInfo),
+ ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
- Cipher_suite/binary, ?BYTE(Comp_method)>>};
+ Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
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_rsa_params{rsa_modulus = Mod,
- rsa_exponent = Exp},
- signed_params = SignedParams}, _Version, _) ->
- ModLen = byte_size(Mod),
- ExpLen = byte_size(Exp),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod/binary,
- ?UINT16(ExpLen), Exp/binary,
- SignedParams/binary>>
- };
+%% Uncomment if support for export ciphers is added.
+%% enc_hs(#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
+%% rsa_exponent = Exp},
+%% signed_params = SignedParams}, _Version, _) ->
+%% ModLen = byte_size(Mod),
+%% ExpLen = byte_size(Exp),
+%% SignedLen = byte_size(SignedParams),
+%% {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary,
+%% ?UINT16(ExpLen), Exp/binary,
+%% ?UINT16(SignedLen), SignedParams/binary>>
+%% };
enc_hs(#server_key_exchange{params = #server_dh_params{
dh_p = P, dh_g = G, dh_y = Y},
signed_params = SignedParams}, _Version, _) ->
PLen = byte_size(P),
GLen = byte_size(G),
YLen = byte_size(Y),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- SignedParams/binary>>
+ SignedLen = byte_size(SignedParams),
+ {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
+ ?UINT16(GLen), G/binary,
+ ?UINT16(YLen), Y/binary,
+ ?UINT16(SignedLen), SignedParams/binary>>
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
@@ -806,6 +1008,29 @@ enc_bin_sig(BinSig) ->
Size = byte_size(BinSig),
<<?UINT16(Size), BinSig/binary>>.
+%% Renegotiation info, only current extension
+hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
+ [];
+hello_extensions(#renegotiation_info{} = Info) ->
+ [Info].
+
+enc_hello_extensions(Extensions) ->
+ enc_hello_extensions(Extensions, <<>>).
+enc_hello_extensions([], <<>>) ->
+ <<>>;
+enc_hello_extensions([], Acc) ->
+ Size = byte_size(Acc),
+ <<?UINT16(Size), Acc/binary>>;
+
+enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = Info} | Rest], Acc) ->
+ Len = byte_size(Info),
+ enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info/binary, Acc/binary>>);
+
+enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) ->
+ InfoLen = byte_size(Info),
+ Len = InfoLen +1,
+ enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>).
+
init_hashes() ->
T = {crypto:md5_init(), crypto:sha_init()},
{T, T}.
@@ -927,13 +1152,40 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes)
when N == 1; N == 2 ->
ssl_tls1:certificate_verify(Algorithm, Hashes).
-%% server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
-%% Algorithm == dh_rsa;
-%% Algorithm == dhe_rsa ->
-%% MD5 = crypto:md5_final(Value),
-%% SHA = crypto:sha_final(Value),
-%% <<MD5/binary, SHA/binary>>;
+server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
+ Algorithm == dh_rsa;
+ Algorithm == dhe_rsa ->
+ MD5Context = crypto:md5_init(),
+ NewMD5Context = crypto:md5_update(MD5Context, Value),
+ MD5 = crypto:md5_final(NewMD5Context),
+
+ SHAContext = crypto:sha_init(),
+ NewSHAContext = crypto:sha_update(SHAContext, Value),
+ SHA = crypto:sha_final(NewSHAContext),
+
+ <<MD5/binary, SHA/binary>>;
+
+server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
+ Algorithm == dhe_dss ->
+
+ SHAContext = crypto:sha_init(),
+ NewSHAContext = crypto:sha_update(SHAContext, Value),
+ crypto:sha_final(NewSHAContext).
-%% server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
-%% Algorithm == dhe_dss ->
-%% crypto:sha_final(Value).
+
+sig_alg(dh_anon) ->
+ ?SIGNATURE_ANONYMOUS;
+sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa ->
+ ?SIGNATURE_RSA;
+sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss ->
+ ?SIGNATURE_DSA;
+sig_alg(_) ->
+ ?NULL.
+
+key_exchange_alg(rsa) ->
+ ?KEY_EXCHANGE_RSA;
+key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
+ Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN;
+key_exchange_alg(_) ->
+ ?NULL.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index b2bdfa0934..74fba3786c 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -38,7 +38,8 @@
-define(NUM_OF_SESSION_ID_BYTES, 32). % TSL 1.1 & SSL 3
-define(NUM_OF_PREMASTERSECRET_BYTES, 48).
-
+-define(DEFAULT_DIFFIE_HELLMAN_GENERATOR, 2).
+-define(DEFAULT_DIFFIE_HELLMAN_PRIME, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Handsake protocol - RFC 4346 section 7.4
@@ -80,7 +81,8 @@
random,
session_id, % opaque SessionID<0..32>
cipher_suites, % cipher_suites<2..2^16-1>
- compression_methods % compression_methods<1..2^8-1>
+ compression_methods, % compression_methods<1..2^8-1>,
+ renegotiation_info
}).
-record(server_hello, {
@@ -88,7 +90,8 @@
random,
session_id, % opaque SessionID<0..32>
cipher_suite, % cipher_suites
- compression_method % compression_method
+ compression_method, % compression_method
+ renegotiation_info
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -194,6 +197,15 @@
verify_data %opaque verify_data[12]
}).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Renegotiation info RFC 5746 section 3.2
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(RENEGOTIATION_EXT, 16#ff01).
+
+-record(renegotiation_info,{
+ renegotiated_connection
+ }).
+
-endif. % -ifdef(ssl_handshake).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 23a5c93452..fdc0c33750 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -57,12 +57,15 @@
verify_fun, % fun(CertVerifyErrors) -> boolean()
fail_if_no_peer_cert, % boolean()
verify_client_once, % boolean()
+ %% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError}
+ validate_extensions_fun,
depth, % integer()
certfile, % file()
keyfile, % file()
key, %
password, %
cacertfile, % file()
+ dhfile, % file()
ciphers, %
%% Local policy for the server if it want's to reuse the session
%% or not. Defaluts to allways returning true.
@@ -71,6 +74,8 @@
%% If false sessions will never be reused, if true they
%% will be reused if possible.
reuse_sessions, % boolean()
+ renegotiate_at,
+ secure_renegotiate,
debug %
}).
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 37a3d1b639..f9f915f13d 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -38,14 +38,17 @@
set_mac_secret/4,
set_master_secret/2,
activate_pending_connection_state/2,
- set_pending_cipher_state/4]).
+ set_pending_cipher_state/4,
+ set_renegotiation_flag/2,
+ set_client_verify_data/3,
+ set_server_verify_data/3]).
%% Handling of incoming data
-export([get_tls_records/2]).
%% Encoding records
-export([encode_handshake/3, encode_alert_record/3,
- encode_change_cipher_spec/2, encode_data/3]).
+ encode_change_cipher_spec/2, encode_data/4]).
%% Decoding
-export([decode_cipher_text/2]).
@@ -175,6 +178,98 @@ set_master_secret(MasterSecret,
master_secret = MasterSecret}},
States#connection_states{pending_read = Read1, pending_write = Write1}.
+%%--------------------------------------------------------------------
+%% Function: set_renegotiation_flag(Flag, States) ->
+%% #connection_states{}
+%% Flag = boolean()
+%% States = #connection_states{}
+%%
+%% Set master_secret in pending connection states
+%%--------------------------------------------------------------------
+set_renegotiation_flag(Flag, #connection_states{
+ current_read = CurrentRead0,
+ current_write = CurrentWrite0,
+ pending_read = PendingRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{secure_renegotiation = Flag},
+ CurrentWrite = CurrentWrite0#connection_state{secure_renegotiation = Flag},
+ PendingRead = PendingRead0#connection_state{secure_renegotiation = Flag},
+ PendingWrite = PendingWrite0#connection_state{secure_renegotiation = Flag},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite,
+ pending_read = PendingRead,
+ pending_write = PendingWrite}.
+
+%%--------------------------------------------------------------------
+%% Function: set_client_verify_data(State, Data, States) ->
+%% #connection_states{}
+%% State = atom()
+%% Data = binary()
+%% States = #connection_states{}
+%%
+%% Set verify data in connection states.
+%%--------------------------------------------------------------------
+set_client_verify_data(current_read, Data,
+ #connection_states{current_read = CurrentRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
+ PendingWrite = PendingWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ pending_write = PendingWrite};
+set_client_verify_data(current_write, Data,
+ #connection_states{pending_read = PendingRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ PendingRead = PendingRead0#connection_state{client_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{pending_read = PendingRead,
+ current_write = CurrentWrite};
+set_client_verify_data(current_both, Data,
+ #connection_states{current_read = CurrentRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite}.
+
+%%--------------------------------------------------------------------
+%% Function: set_server_verify_data(State, Data, States) ->
+%% #connection_states{}
+%% State = atom()
+%% Data = binary()
+%% States = #connection_states{}
+%%
+%% Set verify data in pending connection states.
+%%--------------------------------------------------------------------
+set_server_verify_data(current_write, Data,
+ #connection_states{pending_read = PendingRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ PendingRead = PendingRead0#connection_state{server_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{pending_read = PendingRead,
+ current_write = CurrentWrite};
+
+set_server_verify_data(current_read, Data,
+ #connection_states{current_read = CurrentRead0,
+ pending_write = PendingWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
+ PendingWrite = PendingWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ pending_write = PendingWrite};
+
+set_server_verify_data(current_both, Data,
+ #connection_states{current_read = CurrentRead0,
+ current_write = CurrentWrite0}
+ = ConnectionStates) ->
+ CurrentRead = CurrentRead0#connection_state{server_verify_data = Data},
+ CurrentWrite = CurrentWrite0#connection_state{server_verify_data = Data},
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite}.
%%--------------------------------------------------------------------
%% Function: activate_pending_connection_state(States, Type) ->
@@ -191,7 +286,9 @@ activate_pending_connection_state(States =
NewCurrent = Pending#connection_state{sequence_number = 0},
SecParams = Pending#connection_state.security_parameters,
ConnectionEnd = SecParams#security_parameters.connection_end,
- NewPending = empty_connection_state(ConnectionEnd),
+ EmptyPending = empty_connection_state(ConnectionEnd),
+ SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
+ NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
States#connection_states{current_read = NewCurrent,
pending_read = NewPending
};
@@ -202,7 +299,9 @@ activate_pending_connection_state(States =
NewCurrent = Pending#connection_state{sequence_number = 0},
SecParams = Pending#connection_state.security_parameters,
ConnectionEnd = SecParams#security_parameters.connection_end,
- NewPending = empty_connection_state(ConnectionEnd),
+ EmptyPending = empty_connection_state(ConnectionEnd),
+ SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
+ NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
States#connection_states{current_write = NewCurrent,
pending_write = NewPending
}.
@@ -474,19 +573,31 @@ split_bin(Bin, ChunkSize, Acc) ->
lists:reverse(Acc, [Bin])
end.
-encode_data(Frag, Version, ConnectionStates)
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt)
when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
- encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates);
-encode_data(Frag, Version, ConnectionStates) ->
+ case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of
+ {renegotiate, Data} ->
+ {[], Data, ConnectionStates};
+ {Msg, CS} ->
+ {Msg, [], CS}
+ end;
+
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) ->
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
- {CS1, Acc} =
- lists:foldl(fun(B, {CS0, Acc}) ->
- {ET, CS1} =
- encode_plain_text(?APPLICATION_DATA,
- Version, B, CS0),
- {CS1, [ET | Acc]}
- end, {ConnectionStates, []}, Data),
- {lists:reverse(Acc), CS1}.
+ encode_data(Data, Version, ConnectionStates, RenegotiateAt);
+
+encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) ->
+ {ConnectionStates, EncodedMsg, NotEncdedData} =
+ lists:foldl(fun(B, {CS0, Encoded, Rest}) ->
+ case encode_plain_text(?APPLICATION_DATA,
+ Version, B, CS0, RenegotiateAt) of
+ {renegotiate, NotEnc} ->
+ {CS0, Encoded, [NotEnc | Rest]};
+ {Enc, CS1} ->
+ {CS1, [Enc | Encoded], Rest}
+ end
+ end, {ConnectionStates0, [], []}, Data),
+ {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}.
encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
@@ -499,6 +610,16 @@ encode_alert_record(#alert{level = Level, description = Description},
encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
+encode_plain_text(Type, Version, Data, ConnectionStates, RenegotiateAt) ->
+ #connection_states{current_write =
+ #connection_state{sequence_number = Num}} = ConnectionStates,
+ case renegotiate(Num, RenegotiateAt) of
+ false ->
+ encode_plain_text(Type, Version, Data, ConnectionStates);
+ true ->
+ {renegotiate, Data}
+ end.
+
encode_plain_text(Type, Version, Data, ConnectionStates) ->
#connection_states{current_write=#connection_state{
compression_state=CompS0,
@@ -511,6 +632,11 @@ encode_plain_text(Type, Version, Data, ConnectionStates) ->
CTBin = encode_tls_cipher_text(Type, Version, CipherText),
{CTBin, ConnectionStates#connection_states{current_write = CS2}}.
+renegotiate(N, M) when N < M->
+ false;
+renegotiate(_,_) ->
+ true.
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
@@ -529,9 +655,6 @@ cipher(Type, Version, Fragment, CS0) ->
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
-decipher(TLS=#ssl_tls{type = ?CHANGE_CIPHER_SPEC}, CS) ->
- %% These are never encrypted
- {TLS, CS};
decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
SP = CS0#connection_state.security_parameters,
BCA = SP#security_parameters.bulk_cipher_algorithm, % eller Cipher?
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 7370e0f0b3..5fb0070b91 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -60,9 +60,20 @@
compression_state,
cipher_state,
mac_secret,
- sequence_number
+ sequence_number,
+ %% RFC 5746
+ secure_renegotiation,
+ client_verify_data,
+ server_verify_data
}).
+-define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
+%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
+%% We will renegotiate a little before so that there will be sequence numbers left
+%% for the rehandshake and a little data.
+-define(MARGIN, 100).
+-define(DEFAULT_RENEGOTIATE_AT, ?MAX_SEQENCE_NUMBER - ?MARGIN).
+
%% ConnectionEnd
-define(SERVER, 0).
-define(CLIENT, 1).
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index ab29ac64df..df809ce275 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -182,13 +182,13 @@ setup_keys(export, MasterSecret, ServerRandom, ClientRandom,
suites() ->
[
%% TODO: uncomment when supported
- %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
%%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index e0013c48ac..ce9a135168 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -136,13 +136,13 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
suites() ->
[
%% TODO: uncomment when supported
- %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ %%?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
%%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?