diff options
Diffstat (limited to 'lib/ssl/src')
-rw-r--r-- | lib/ssl/src/ssl.appup.src | 7 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 26 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate_db.erl | 2 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 679 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 172 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.hrl | 13 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 12 | ||||
-rw-r--r-- | lib/ssl/src/ssl_record.erl | 64 | ||||
-rw-r--r-- | lib/ssl/src/ssl_record.hrl | 17 | ||||
-rw-r--r-- | lib/ssl/src/ssl_ssl3.erl | 18 | ||||
-rw-r--r-- | lib/ssl/src/ssl_tls1.erl | 20 |
11 files changed, 698 insertions, 332 deletions
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 755c3b51b5..fdda65021d 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -7,7 +7,9 @@ {"3.10.3", [{restart_application, ssl}]}, {"3.10.4", [{restart_application, ssl}]}, {"3.10.5", [{restart_application, ssl}]}, - {"3.10.6", [{restart_application, ssl}]} + {"3.10.6", [{restart_application, ssl}]}, + {"3.10.7", [{restart_application, ssl}]}, + {"3.10.8", [{restart_application, ssl}]} ], [ {"3.10", [{restart_application, ssl}]}, @@ -16,6 +18,7 @@ {"3.10.3", [{restart_application, ssl}]}, {"3.10.4", [{restart_application, ssl}]}, {"3.10.5", [{restart_application, ssl}]}, - {"3.10.6", [{restart_application, ssl}]} + {"3.10.6", [{restart_application, ssl}]}, + {"3.10.8", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index de74c91505..87a0939897 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 @@ -436,6 +438,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 %%%-------------------------------------------------------------------- @@ -543,19 +549,21 @@ handle_options(Opts0, Role) -> 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), + 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, depth, certfile, keyfile, - key, password, cacertfile, ciphers, + key, password, cacertfile, dhfile, ciphers, debug, reuse_session, reuse_sessions, ssl_imp, - cd_info], + cd_info, renegotiate_at], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -605,6 +613,10 @@ 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) @@ -617,6 +629,9 @@ validate_option(reuse_session, Value) when is_function(Value) -> validate_option(reuse_sessions, 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) -> @@ -832,6 +847,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_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_connection.erl b/lib/ssl/src/ssl_connection.erl index bbffa1e564..a78b0257f5 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -41,15 +41,14 @@ %% Internal application API -export([send/2, send/3, recv/3, connect/7, accept/6, 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, connection/3, + abbreviated/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -record(state, { @@ -78,17 +77,25 @@ 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() + 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 %%==================================================================== @@ -99,15 +106,17 @@ %% Description: %%-------------------------------------------------------------------- send(Pid, Data) -> - sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity). + sync_send_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_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, + Timeout). %%-------------------------------------------------------------------- %% Function: %% %% Description: %%-------------------------------------------------------------------- -recv(Pid, Length, Timeout) -> % TODO: Prio with renegotiate? +recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- %% Function: @@ -209,6 +218,14 @@ session_info(ConnectionPid) -> peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). +%%-------------------------------------------------------------------- +%% Function: +%% +%% Description: +%%-------------------------------------------------------------------- +renegotiation(ConnectionPid) -> + send_all_state_event(ConnectionPid, renegotiate). + %%==================================================================== %% ssl_connection_sup API %%==================================================================== @@ -224,7 +241,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,12 +259,13 @@ 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 -> @@ -261,18 +278,20 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, %% {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} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, SslOpts), + Hello = ssl_handshake:client_hello(Host, Port, + ConnectionStates, SslOpts), Version = Hello#client_hello.client_version, Hashes0 = ssl_handshake:init_hashes(), {BinMsg, CS2, Hashes1} = @@ -289,7 +308,7 @@ hello(socket_control, #state{host = Host, port = Port, role = client, hello(socket_control, #state{role = server} = State) -> {next_state, hello, next_record(State)}; -hello(hello, #state{role = client} = State) -> +hello(#hello_request{}, #state{role = client} = State) -> {next_state, hello, State}; hello(#server_hello{cipher_suite = CipherSuite, @@ -301,13 +320,14 @@ hello(#server_hello{cipher_suite = CipherSuite, host = Host, port = Port, 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), + PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), State = State0#state{key_algorithm = KeyAlgorithm, negotiated_version = Version, @@ -358,21 +378,20 @@ hello(Hello = #client_hello{client_version = ClientVersion}, abbreviated(socket_control, #state{role = server} = State) -> {next_state, abbreviated, State}; -abbreviated(hello, State) -> +abbreviated(#hello_request{}, State) -> {next_state, certify, State}; abbreviated(Finished = #finished{}, #state{role = server, negotiated_version = Version, tls_handshake_hashes = Hashes, - session = #session{master_secret = MasterSecret}, - from = From} = State) -> + session = #session{master_secret = MasterSecret}} = 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 -> + ack_connection(State), + next_state_connection(State); + #alert{} = Alert -> handle_own_alert(Alert, Version, abbreviated, State), {stop, normal, State} end; @@ -380,17 +399,15 @@ abbreviated(Finished = #finished{}, abbreviated(Finished = #finished{}, #state{role = client, tls_handshake_hashes = Hashes0, session = #session{master_secret = MasterSecret}, - from = From, negotiated_version = Version} = 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})}; + ack_connection(State), + next_state_connection(State#state{tls_handshake_hashes = Hashes, + connection_states = + ConnectionStates}); #alert{} = Alert -> handle_own_alert(Alert, Version, abbreviated, State), {stop, normal, State} @@ -398,7 +415,7 @@ abbreviated(Finished = #finished{}, certify(socket_control, #state{role = server} = State) -> {next_state, certify, State}; -certify(hello, State) -> +certify(#hello_request{}, State) -> {next_state, certify, State}; certify(#certificate{asn1_certificates = []}, @@ -415,34 +432,35 @@ certify(#certificate{asn1_certificates = []}, 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})}; + {next_state, certify, + next_record(State#state{client_certificate_requested = false})}; certify(#certificate{} = Cert, - #state{session = Session, - negotiated_version = Version, + #state{negotiated_version = Version, 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 {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); #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{} = State -> + {next_state, certify, next_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, @@ -452,15 +470,30 @@ certify(#server_key_exchange{}, 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{}, 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 +520,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}; @@ -517,11 +551,41 @@ certify(#client_key_exchange{exchange_keys handle_own_alert(Alert, Version, certify_client_key_exchange, State0), {stop, normal, State0} + end; + +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} -> + State = State0#state{session = + Session#session{master_secret + = MasterSecret}, + connection_states = ConnectionStates}, + {next_state, cipher, next_record(State)}; + #alert{} = Alert -> + handle_own_alert(Alert, Version, + certify_client_key_exchange, State0), + {stop, normal, State0} end. cipher(socket_control, #state{role = server} = State) -> {next_state, cipher, State}; -cipher(hello, State) -> +cipher(#hello_request{}, State) -> {next_state, cipher, State}; cipher(#certificate_verify{signature = Signature}, @@ -543,8 +607,7 @@ cipher(#certificate_verify{signature = Signature}, end; cipher(#finished{} = Finished, - State = #state{from = From, - negotiated_version = Version, + State = #state{negotiated_version = Version, host = Host, port = Port, role = Role, @@ -556,12 +619,11 @@ cipher(#finished{} = Finished, opposite_role(Role), MasterSecret, Hashes) of verified -> - gen_fsm:reply(From, connected), + ack_connection(State), Session = register_session(Role, Host, Port, Session0), case Role of client -> - {next_state, connection, - next_record_if_active(State#state{session = Session})}; + next_state_connection(State#state{session = Session}); server -> {NewConnectionStates, NewHashes} = finalize_server_handshake(State#state{ @@ -570,7 +632,7 @@ cipher(#finished{} = Finished, State#state{connection_states = NewConnectionStates, session = Session, tls_handshake_hashes = NewHashes}, - {next_state, connection, next_record_if_active(NewState)} + next_state_connection(NewState) end; #alert{} = Alert -> handle_own_alert(Alert, Version, cipher, State), @@ -579,7 +641,7 @@ cipher(#finished{} = Finished, connection(socket_control, #state{role = server} = State) -> {next_state, connection, State}; -connection(hello, State = #state{host = Host, port = Port, +connection(#hello_request{}, State = #state{host = Host, port = Port, socket = Socket, ssl_options = SslOpts, negotiated_version = Version, @@ -592,42 +654,12 @@ connection(hello, State = #state{host = Host, port = Port, {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. + {next_state, hello, next_record(State#state{connection_states = + ConnectionStates1, + tls_handshake_hashes = Hashes1, + renegotiation = true})}; +connection(#client_hello{} = Hello, #state{role = server} = State) -> + hello(Hello, State). %%-------------------------------------------------------------------- %% Function: @@ -642,22 +674,23 @@ connection({application_data, Data0}, _From, %%-------------------------------------------------------------------- handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, - State = #state{key_algorithm = KeyAlg, - tls_handshake_buffer = Buf0, - negotiated_version = Version}) -> + State0 = #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}); + fun({Packet, Raw}, {next_state, SName, + AS=#state{tls_handshake_hashes=Hs0}}) -> (_, 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}}, + {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} + handle_own_alert(Alert, Version, StateName, State0), + {stop, normal, State0} end; handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, @@ -686,7 +719,8 @@ handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) -> {next_state, StateName, State}; handle_event(#alert{level = ?FATAL} = Alert, connection, - #state{from = From, user_application = {_Mon, Pid}, log_alert = Log, + #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), @@ -717,8 +751,13 @@ handle_event(#alert{level = ?WARNING} = Alert, StateName, 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)}. + {next_state, StateName, next_record(State)}; +handle_event(renegotiate, connection, State) -> + renegotiate(State); +handle_event(renegotiate, StateName, State) -> + %% Already in renegotiate ignore + {next_state, StateName, State}. %%-------------------------------------------------------------------- %% Function: %% handle_sync_event(Event, From, StateName, @@ -734,6 +773,42 @@ 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({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)}) + 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(started, From, StateName, State) -> {next_state, StateName, State#state{from = From}}; @@ -750,23 +825,14 @@ handle_sync_event({shutdown, How}, From, StateName, {stop, normal, Error, State#state{from = From}} 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/1. +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, _}}) -> @@ -834,7 +900,6 @@ handle_sync_event(peer_certificate, _, StateName, = State) -> {reply, {ok, Cert}, StateName, State}. - %%-------------------------------------------------------------------- %% Function: %% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}| @@ -932,7 +997,8 @@ terminate(_Reason, connection, _S=#state{negotiated_version = Version, Version, ConnectionStates), Transport:send(Socket, BinAlert), Transport:close(Socket); -terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, socket = Socket}) -> +terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, + socket = Socket}) -> Transport:close(Socket), ok. @@ -969,8 +1035,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 +1071,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,40 +1089,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_event(FsmPid, Event) -> + gen_fsm:send_event(FsmPid, Event). send_all_state_event(FsmPid, Event) -> @@ -1078,6 +1149,44 @@ sync_send_all_state_event(FsmPid, Event, Timeout) -> alert_event(Alert) -> send_all_state_event(self(), Alert). + +%% TODO: This clause is not yet supported. Do we need +%% to support it, not supported by openssl! +handle_peer_cert(PeerCert, {Algorithm, _, _} = PublicKeyInfo, + #state{negotiated_version = Version, + session = Session} = + State0) when Algorithm == dh_rsa; + Algorithm == dh_dss -> + case public_key:pkix_is_fixed_dh_cert(PeerCert) of + true -> + %% TODO: extract DH-params from cert and save + %% Keys and SharedSecret in state. + %% {P, G, ServerPublicDhKey}= extract ..... + %% Keys = {ClientDhPublicKey, _} = + %% public_key:gen_key(#'DHParameter'{prime = P, base = G}), + %%SharedSecret = dh_shared_secret(ServerPublicDhKey, + %% ClientDhPublicKey, + %% [P, G]), + %% ssl_handshake:master_secret ... + State = State0#state{session = + Session#session{peer_certificate + = PeerCert}, + public_key_info = PublicKeyInfo}, + {next_state, certify, next_record(State)}; + + false -> + Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), + handle_own_alert(Alert, Version, certify_certificate, State0), + {stop, normal, State0} + end; + +handle_peer_cert(PeerCert, PublicKeyInfo, + #state{session = Session} = State0) -> + State = State0#state{session = + Session#session{peer_certificate = PeerCert}, + public_key_info = PublicKeyInfo}, + {next_state, certify, next_record(State)}. + certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, @@ -1111,9 +1220,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, @@ -1140,8 +1248,10 @@ do_server_hello(Type, #state{negotiated_version = Version, {_, ConnectionStates1} -> State1 = State#state{connection_states=ConnectionStates1, session = Session}, - {ConnectionStates, Hashes} = finalize_server_handshake(State1), - Resumed = State1#state{connection_states = ConnectionStates, + {ConnectionStates, Hashes} = + finalize_server_handshake(State1), + Resumed = State1#state{connection_states = + ConnectionStates, tls_handshake_hashes = Hashes}, {next_state, abbreviated, next_record(Resumed)}; #alert{} = Alert -> @@ -1260,6 +1370,7 @@ key_exchange(#state{role = server, key_algorithm = rsa_export} = 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 +1381,38 @@ 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}; + +%% TODO: Not yet supported should be by default disabled when supported. +%% 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} = +%% encode_handshake(Msg, Version, ConnectionStates0, Hashes0), +%% Transport:send(Socket, BinMsg), +%% State#state{connection_states = ConnectionStates1, +%% tls_handshake_hashes = Hashes1}; key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1309,17 +1432,33 @@ 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_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + client_certificate_requested = ClientCertReq, + own_cert = OwnCert, + diffie_hellman_keys = DhKeys, + socket = Socket, transport_cb = Transport, + tls_handshake_hashes = Hashes0} = State) + when Algorithm == dh_dss; + Algorithm == dh_rsa -> + Msg = dh_key_exchange(OwnCert, DhKeys, ClientCertReq), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), @@ -1334,17 +1473,19 @@ 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) -> +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 -> - ssl_handshake:key_exchange(client, {dh, Params, PublicKeyInfo}) - end. + {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, @@ -1378,7 +1519,8 @@ finalize_client_handshake(#state{connection_states = ConnectionStates0} finalize_server_handshake(State) -> ConnectionStates0 = cipher_protocol(State), ConnectionStates = - ssl_record:activate_pending_connection_state(ConnectionStates0, write), + ssl_record:activate_pending_connection_state(ConnectionStates0, + write), finished(State#state{connection_states = ConnectionStates}). cipher_protocol(#state{connection_states = ConnectionStates, @@ -1386,7 +1528,8 @@ cipher_protocol(#state{connection_states = ConnectionStates, negotiated_version = Version, transport_cb = Transport}) -> {BinChangeCipher, NewConnectionStates} = - encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates), + encode_change_cipher(#change_cipher_spec{}, + Version, ConnectionStates), Transport:send(Socket, BinChangeCipher), NewConnectionStates. @@ -1402,10 +1545,69 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, Transport:send(Socket, BinFinished), {NewConnectionStates, NewHashes}. -handle_server_key(_KeyExchangeMsg, State) -> - State. -handle_clinet_key(_KeyExchangeMsg, State) -> - State. +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_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), @@ -1429,12 +1631,10 @@ encode_handshake(HandshakeRec, SigAlg, Version, ConnectionStates0, Hashes0) -> encode_packet(Data, #socket_options{packet=Packet}) -> case Packet of - 0 -> Data; 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1); 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1); 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1); - _ -> - throw({error, {badarg, {eoptions, {packet, Packet}}}}) + _ -> Data end. encode_size_packet(Bin, Size, Max) -> @@ -1444,8 +1644,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, []). @@ -1456,6 +1656,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 + <<>> -> + State = next_record(State0), + {next_state, StateName, State}; + _ -> + case application_data(<<>>, State0) of + Stop = {stop, _, _} -> + Stop; + State -> + {next_state, StateName, State} + end + end. + application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SOpts, bytes_to_read = BytesToRead, @@ -1596,13 +1810,56 @@ next_record(#state{tls_cipher_texts = [CT | Rest], gen_fsm:send_all_state_event(self(), Plain), State#state{tls_cipher_texts = Rest, connection_states = ConnStates}. + next_record_if_active(State = #state{socket_options = #socket_options{active = false}}) -> State; + next_record_if_active(State) -> next_record(State). +next_state_connection(#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(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})}) + 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(State) -> + {next_state, connection, next_record_if_active(State)}. + + register_session(_, _, _, #session{is_resumable = true} = Session) -> Session; %% Already registered register_session(client, Host, Port, Session0) -> @@ -1652,7 +1909,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, + recv_during_renegotiation = false, + send_queue = queue:new() }. sslsocket(Pid) -> @@ -1690,7 +1950,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); @@ -1732,12 +1993,28 @@ handle_own_alert(Alert, Version, StateName, role = Role, connection_states = ConnectionStates, log_alert = Log}) -> - {BinMsg, _} = - encode_alert(Alert, Version, ConnectionStates), - Transport:send(Socket, BinMsg), - log_alert(Log, StateName, Alert), - alert_user(User, Alert, Role). + try %% Try to tell the other side + {BinMsg, _} = + encode_alert(Alert, Version, ConnectionStates), + Transport:send(Socket, BinMsg) + catch _:_ -> %% Can crash if we are in a uninitialized state + ignore + end, + try %% Try to tell the local user + log_alert(Log, StateName, Alert), + alert_user(User, Alert, Role) + catch _:_ -> + ok + end. -make_premaster_secret({MajVer, MinVer}) -> +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>>. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 8c598135ca..ff78375b9f 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/4, server_hello/3, hello/2, + hello_request/0, certify/5, 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, @@ -97,6 +97,15 @@ server_hello(SessionId, Version, ConnectionStates) -> }. %%-------------------------------------------------------------------- +%% 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) -> %% {Version, Id, NewConnectionStates} | %% #alert{} @@ -248,8 +257,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, @@ -296,30 +307,32 @@ key_exchange(client, fixed_diffie_hellman) -> #client_diffie_hellman_public{ dh_public = <<>> }}; -key_exchange(client, {dh, PublicKey}) -> - Len = byte_size(PublicKey), +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(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}; key_exchange(_, _) -> %%TODO : Real imp #server_key_exchange{}. @@ -417,7 +430,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 +451,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_excahange_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) -> @@ -649,8 +647,8 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, 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>>, + ?UINT16(ExpLen), Exp:ExpLen/binary, + ?UINT16(_), Sig/binary>>, ?KEY_EXCHANGE_RSA, _) -> #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod, rsa_exponent = Exp}, @@ -658,9 +656,10 @@ dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary, 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,18 +671,20 @@ 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(_, _, _, _) -> @@ -756,12 +757,13 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) -> {?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, _) -> + signed_params = SignedParams}, _Version, _) -> ModLen = byte_size(Mod), ExpLen = byte_size(Exp), - {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod/binary, + SignedLen = byte_size(SignedParams), + {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary, ?UINT16(ExpLen), Exp/binary, - SignedParams/binary>> + ?UINT16(SignedLen), SignedParams/binary>> }; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, @@ -769,10 +771,11 @@ enc_hs(#server_key_exchange{params = #server_dh_params{ 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}, @@ -927,13 +930,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). + + +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. -%% server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss; -%% Algorithm == dhe_dss -> -%% crypto:sha_final(Value). +key_excahange_alg(rsa) -> + ?KEY_EXCHANGE_RSA; +key_excahange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; + Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> + ?KEY_EXCHANGE_DIFFIE_HELLMAN; +key_excahange_alg(_) -> + ?NULL. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index b2bdfa0934..889d39f2af 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 diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 23a5c93452..ab24c28b2f 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% %% @@ -63,6 +63,7 @@ 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 +72,7 @@ %% If false sessions will never be reused, if true they %% will be reused if possible. reuse_sessions, % boolean() + renegotiate_at, debug % }). diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 37a3d1b639..da48f049f6 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% %% @@ -45,7 +45,7 @@ %% 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]). @@ -474,19 +474,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 +511,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 +533,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 +556,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..362b7039d4 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% %% @@ -63,6 +63,13 @@ sequence_number }). +-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? |