aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_record.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_record.erl')
-rw-r--r--lib/ssl/src/ssl_record.erl268
1 files changed, 177 insertions, 91 deletions
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index da48f049f6..acd0d49c19 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -29,6 +29,7 @@
-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_handshake.hrl").
+-include("ssl_cipher.hrl").
-include("ssl_debug.hrl").
%% Connection state handling
@@ -38,7 +39,10 @@
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]).
@@ -62,10 +66,9 @@
%%====================================================================
%% Internal application API
%%====================================================================
+
%%--------------------------------------------------------------------
-%% Function: init_connection_states(Role) -> #connection_states{}
-%% Role = client | server
-%% Random = binary()
+-spec init_connection_states(client | server) -> #connection_states{}.
%%
%% Description: Creates a connection_states record with appropriate
%% values for the initial SSL connection setup.
@@ -81,9 +84,8 @@ init_connection_states(Role) ->
}.
%%--------------------------------------------------------------------
-%% Function: current_connection_state(States, Type) -> #connection_state{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec current_connection_state(#connection_states{}, read | write) ->
+ #connection_state{}.
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the current conection state.
@@ -96,9 +98,8 @@ current_connection_state(#connection_states{current_write = Current},
Current.
%%--------------------------------------------------------------------
-%% Function: pending_connection_state(States, Type) -> #connection_state{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec pending_connection_state(#connection_states{}, read | write) ->
+ #connection_state{}.
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the pending conection state.
@@ -111,14 +112,11 @@ pending_connection_state(#connection_states{pending_write = Pending},
Pending.
%%--------------------------------------------------------------------
-%% Function: update_security_params(Params, States) ->
-%% #connection_states{}
-%% Params = #security_parameters{}
-%% States = #connection_states{}
+-spec update_security_params(#security_parameters{}, #security_parameters{},
+ #connection_states{}) -> #connection_states{}.
%%
%% Description: Creates a new instance of the connection_states record
-%% where the pending states gets its security parameters
-%% updated to <Params>.
+%% where the pending states gets its security parameters updated.
%%--------------------------------------------------------------------
update_security_params(ReadParams, WriteParams, States =
#connection_states{pending_read = Read,
@@ -131,14 +129,10 @@ update_security_params(ReadParams, WriteParams, States =
WriteParams}
}.
%%--------------------------------------------------------------------
-%% Function: set_mac_secret(ClientWriteMacSecret,
-%% ServerWriteMacSecret, Role, States) ->
-%% #connection_states{}
-%% MacSecret = binary()
-%% States = #connection_states{}
-%% Role = server | client
+-spec set_mac_secret(binary(), binary(), client | server,
+ #connection_states{}) -> #connection_states{}.
%%
-%% update the mac_secret field in pending connection states
+%% Description: update the mac_secret field in pending connection states
%%--------------------------------------------------------------------
set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) ->
set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States);
@@ -155,12 +149,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret,
%%--------------------------------------------------------------------
-%% Function: set_master_secret(MasterSecret, States) ->
-%% #connection_states{}
-%% MacSecret =
-%% States = #connection_states{}
+-spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
%%
-%% Set master_secret in pending connection states
+%% Description: Set master_secret in pending connection states
%%--------------------------------------------------------------------
set_master_secret(MasterSecret,
States = #connection_states{pending_read = Read,
@@ -175,12 +166,94 @@ set_master_secret(MasterSecret,
master_secret = MasterSecret}},
States#connection_states{pending_read = Read1, pending_write = Write1}.
+%%--------------------------------------------------------------------
+-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
+%%
+%% Description: Set secure_renegotiation 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: activate_pending_connection_state(States, Type) ->
-%% #connection_states{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec set_client_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
+%%
+%% Description: 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}.
+%%--------------------------------------------------------------------
+-spec set_server_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
+%%
+%% Description: 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}.
+
+%%--------------------------------------------------------------------
+-spec activate_pending_connection_state(#connection_states{}, read | write) ->
+ #connection_states{}.
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending state of <Type> has been activated.
@@ -191,7 +264,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,17 +277,17 @@ 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
}.
%%--------------------------------------------------------------------
-%% Function: set_pending_cipher_state(States, ClientState,
-%% ServerState, Role) ->
-%% #connection_states{}
-%% ClientState = ServerState = #cipher_state{}
-%% States = #connection_states{}
+-spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
+ #cipher_state{}, client | server) ->
+ #connection_states{}.
%%
%% Description: Set the cipher state in the specified pending connection state.
%%--------------------------------------------------------------------
@@ -231,12 +306,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
pending_write = Write#connection_state{cipher_state = ClientState}}.
%%--------------------------------------------------------------------
-%% Function: get_tls_record(Data, Buffer) -> Result
-%% Result = {[#tls_compressed{}], NewBuffer}
-%% Data = Buffer = NewBuffer = binary()
-%%
-%% Description: given old buffer and new data from TCP, packs up a records
-%% and returns it as a list of #tls_compressed, also returns leftover
+-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
+%%
+%% Description: Given old buffer and new data from TCP, packs up a records
+%% and returns it as a list of tls_compressed binaries also returns leftover
%% data
%%--------------------------------------------------------------------
get_tls_records(Data, <<>>) ->
@@ -299,8 +372,8 @@ get_tls_records_aux(Data, Acc) ->
{lists:reverse(Acc), Data}.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Version) -> #protocol_version{}
-%% Version = atom()
+-spec protocol_version(tls_atom_version() | tls_version()) ->
+ tls_version() | tls_atom_version().
%%
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
@@ -311,19 +384,16 @@ protocol_version(tlsv1) ->
{3, 1};
protocol_version(sslv3) ->
{3, 0};
-protocol_version(sslv2) ->
+protocol_version(sslv2) -> %% Backwards compatibility
{2, 0};
protocol_version({3, 2}) ->
'tlsv1.1';
protocol_version({3, 1}) ->
tlsv1;
protocol_version({3, 0}) ->
- sslv3;
-protocol_version({2, 0}) ->
- sslv2.
+ sslv3.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Version1, Version2) -> #protocol_version{}
-%% Version1 = Version2 = #protocol_version{}
+-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
%%
%% Description: Lowes protocol version of two given versions
%%--------------------------------------------------------------------
@@ -338,8 +408,7 @@ lowest_protocol_version(Version = {M,_},
lowest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Versions) -> #protocol_version{}
-%% Versions = [#protocol_version{}]
+-spec highest_protocol_version([tls_version()]) -> tls_version().
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
@@ -361,14 +430,13 @@ highest_protocol_version(_, [Version | Rest]) ->
highest_protocol_version(Version, Rest).
%%--------------------------------------------------------------------
-%% Function: supported_protocol_versions() -> Versions
-%% Versions = [#protocol_version{}]
-%%
+-spec supported_protocol_versions() -> [tls_version()].
+%%
%% Description: Protocol versions supported
%%--------------------------------------------------------------------
supported_protocol_versions() ->
Fun = fun(Version) ->
- protocol_version(Version)
+ protocol_version(Version)
end,
case application:get_env(ssl, protocol_version) of
undefined ->
@@ -376,14 +444,20 @@ supported_protocol_versions() ->
{ok, []} ->
lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
{ok, Vsns} when is_list(Vsns) ->
- lists:map(Fun, Vsns);
+ Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
+ supported_protocol_versions(Versions);
{ok, Vsn} ->
- [Fun(Vsn)]
+ Versions = lists:filter(fun is_acceptable_version/1, [Fun(Vsn)]),
+ supported_protocol_versions(Versions)
end.
+supported_protocol_versions([]) ->
+ ?DEFAULT_SUPPORTED_VERSIONS;
+supported_protocol_versions([_|_] = Vsns) ->
+ Vsns.
+
%%--------------------------------------------------------------------
-%% Function: is_acceptable_version(Version) -> true | false
-%% Version = #protocol_version{}
+-spec is_acceptable_version(tls_version()) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%--------------------------------------------------------------------
@@ -394,7 +468,7 @@ is_acceptable_version(_) ->
false.
%%--------------------------------------------------------------------
-%% Function: compressions() -> binary()
+-spec compressions() -> [binary()].
%%
%% Description: return a list of compressions supported (currently none)
%%--------------------------------------------------------------------
@@ -402,8 +476,8 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
-%% Function: decode_cipher_text(CipherText, ConnectionStates0) ->
-%% {Plain, ConnectionStates}
+-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) ->
+ {#ssl_tls{}, #connection_states{}}| #alert{}.
%%
%% Description: Decode cipher text
%%--------------------------------------------------------------------
@@ -412,13 +486,17 @@ decode_cipher_text(CipherText, ConnnectionStates0) ->
#connection_state{compression_state = CompressionS0,
security_parameters = SecParams} = ReadState0,
CompressAlg = SecParams#security_parameters.compression_algorithm,
- {Compressed, ReadState1} = decipher(CipherText, ReadState0),
- {Plain, CompressionS1} = uncompress(CompressAlg,
- Compressed, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
- {Plain, ConnnectionStates}.
+ case decipher(CipherText, ReadState0) of
+ {Compressed, ReadState1} ->
+ {Plain, CompressionS1} = uncompress(CompressAlg,
+ Compressed, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ compression_state = CompressionS1}},
+ {Plain, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -433,12 +511,10 @@ initial_connection_state(ConnectionEnd) ->
}.
initial_security_params(ConnectionEnd) ->
- #security_parameters{connection_end = ConnectionEnd,
- bulk_cipher_algorithm = ?NULL,
- mac_algorithm = ?NULL,
- compression_algorithm = ?NULL,
- cipher_type = ?NULL
- }.
+ SecParams = #security_parameters{connection_end = ConnectionEnd,
+ compression_algorithm = ?NULL},
+ ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL,
+ SecParams).
empty_connection_state(ConnectionEnd) ->
SecParams = empty_security_params(ConnectionEnd),
@@ -544,29 +620,37 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
cipher(Type, Version, Fragment, CS0) ->
Length = erlang:iolist_size(Fragment),
- {Hash, CS1=#connection_state{cipher_state = CipherS0,
+ {MacHash, CS1=#connection_state{cipher_state = CipherS0,
security_parameters=
#security_parameters{bulk_cipher_algorithm =
BCA}
}} =
hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
?DBG_HEX(Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, Hash, Fragment),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment),
?DBG_HEX(Ciphered),
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
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?
+ BCA = SP#security_parameters.bulk_cipher_algorithm,
HashSz = SP#security_parameters.hash_size,
CipherS0 = CS0#connection_state.cipher_state,
- {T, Mac, CipherS1} = ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment),
- CS1 = CS0#connection_state{cipher_state = CipherS1},
- TLength = size(T),
- {Hash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, Fragment),
- ok = check_hash(Hash, Mac),
- {TLS#ssl_tls{fragment = T}, CS2}.
+ case ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment, Version) of
+ {T, Mac, CipherS1} ->
+ CS1 = CS0#connection_state{cipher_state = CipherS1},
+ TLength = size(T),
+ {MacHash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, T),
+ case is_correct_mac(Mac, MacHash) of
+ true ->
+ {TLS#ssl_tls{fragment = T}, CS2};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
uncompress(?NULL, Data = #ssl_tls{type = _Type,
version = _Version,
@@ -587,10 +671,12 @@ hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
Length, Fragment),
{Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
-check_hash(_, _) ->
- ok. %% TODO check this
+is_correct_mac(Mac, Mac) ->
+ true;
+is_correct_mac(_M,_H) ->
+ false.
-mac_hash(?NULL, {_,_}, _MacSecret, _SeqNo, _Type,
+mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
_Length, _Fragment) ->
<<>>;
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->