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.erl618
1 files changed, 189 insertions, 429 deletions
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 173b9611c6..018c8befe0 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2013. 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
@@ -15,54 +15,40 @@
%% under the License.
%%
%% %CopyrightEnd%
-%%
-%%
%%----------------------------------------------------------------------
-%% Purpose: Help functions for handling the SSL-Record protocol
-%%
+%% Purpose: Handle TLS/SSL/DTLS record protocol. Note epoch is only
+%% used by DTLS but handled here so we can avoid code duplication.
%%----------------------------------------------------------------------
-module(ssl_record).
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--include("ssl_alert.hrl").
--include("ssl_handshake.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_alert.hrl").
%% Connection state handling
--export([init_connection_states/1,
- current_connection_state/2, pending_connection_state/2,
- update_security_params/3,
+-export([init_connection_states/1,
+ current_connection_state/2, pending_connection_state/2,
+ activate_pending_connection_state/2,
+ set_security_params/3,
set_mac_secret/4,
- set_master_secret/2,
- activate_pending_connection_state/2,
+ set_master_secret/2,
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]).
-%% Decoding
--export([decode_cipher_text/2]).
-
-%% Misc.
--export([protocol_version/1, lowest_protocol_version/2,
- highest_protocol_version/1, supported_protocol_versions/0,
- is_acceptable_version/1]).
-
--export([compressions/0]).
+%% Compression
+-export([compress/3, uncompress/3, compressions/0]).
--compile(inline).
-
--define(INITIAL_BYTES, 5).
+%% Payload encryption/decryption
+-export([cipher/4, decipher/3, is_correct_mac/2]).
%%====================================================================
%% Internal application API
@@ -72,7 +58,7 @@
-spec init_connection_states(client | server) -> #connection_states{}.
%%
%% Description: Creates a connection_states record with appropriate
-%% values for the initial SSL connection setup.
+%% values for the initial SSL connection setup.
%%--------------------------------------------------------------------
init_connection_states(Role) ->
ConnectionEnd = record_protocol_role(Role),
@@ -90,7 +76,7 @@ init_connection_states(Role) ->
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the current conection state.
-%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
current_connection_state(#connection_states{current_read = Current},
read) ->
Current;
@@ -100,38 +86,77 @@ current_connection_state(#connection_states{current_write = Current},
%%--------------------------------------------------------------------
-spec pending_connection_state(#connection_states{}, read | write) ->
- #connection_state{}.
+ term().
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the pending conection state.
-%%--------------------------------------------------------------------
-pending_connection_state(#connection_states{pending_read = Pending},
+%%--------------------------------------------------------------------
+pending_connection_state(#connection_states{pending_read = Pending},
read) ->
Pending;
pending_connection_state(#connection_states{pending_write = Pending},
write) ->
Pending.
+
%%--------------------------------------------------------------------
--spec update_security_params(#security_parameters{}, #security_parameters{},
+-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.
+%%--------------------------------------------------------------------
+activate_pending_connection_state(States =
+ #connection_states{current_read = Current,
+ pending_read = Pending},
+ read) ->
+ NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
+ sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ 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
+ };
+
+activate_pending_connection_state(States =
+ #connection_states{current_write = Current,
+ pending_write = Pending},
+ write) ->
+ NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
+ sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ 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
+ }.
+
+
+%%--------------------------------------------------------------------
+-spec set_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.
-%%--------------------------------------------------------------------
-update_security_params(ReadParams, WriteParams, States =
+%%--------------------------------------------------------------------
+set_security_params(ReadParams, WriteParams, States =
#connection_states{pending_read = Read,
- pending_write = Write}) ->
+ pending_write = Write}) ->
States#connection_states{pending_read =
- Read#connection_state{security_parameters =
+ Read#connection_state{security_parameters =
ReadParams},
- pending_write =
- Write#connection_state{security_parameters =
+ pending_write =
+ Write#connection_state{security_parameters =
WriteParams}
}.
%%--------------------------------------------------------------------
--spec set_mac_secret(binary(), binary(), client | server,
- #connection_states{}) -> #connection_states{}.
+-spec set_mac_secret(binary(), binary(), client | server,
+ #connection_states{}) -> #connection_states{}.
%%
%% Description: update the mac_secret field in pending connection states
%%--------------------------------------------------------------------
@@ -156,7 +181,7 @@ set_mac_secret(ReadMacSecret, WriteMacSecret,
%%--------------------------------------------------------------------
set_master_secret(MasterSecret,
States = #connection_states{pending_read = Read,
- pending_write = Write}) ->
+ pending_write = Write}) ->
ReadSecPar = Read#connection_state.security_parameters,
Read1 = Read#connection_state{
security_parameters = ReadSecPar#security_parameters{
@@ -176,15 +201,15 @@ set_renegotiation_flag(Flag, #connection_states{
current_read = CurrentRead0,
current_write = CurrentWrite0,
pending_read = PendingRead0,
- pending_write = PendingWrite0}
+ 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,
+ ConnectionStates#connection_states{current_read = CurrentRead,
+ current_write = CurrentWrite,
+ pending_read = PendingRead,
pending_write = PendingWrite}.
%%--------------------------------------------------------------------
@@ -192,27 +217,27 @@ set_renegotiation_flag(Flag, #connection_states{
binary(), #connection_states{})->
#connection_states{}.
%%
-%% Description: Set verify data in connection states.
+%% Description: Set verify data in connection states.
%%--------------------------------------------------------------------
-set_client_verify_data(current_read, Data,
+set_client_verify_data(current_read, Data,
#connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ 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,
+set_client_verify_data(current_write, Data,
#connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ 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,
+set_client_verify_data(current_both, Data,
#connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ current_write = CurrentWrite0}
= ConnectionStates) ->
CurrentRead = CurrentRead0#connection_state{client_verify_data = Data},
CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
@@ -225,66 +250,32 @@ set_client_verify_data(current_both, Data,
%%
%% Description: Set verify data in pending connection states.
%%--------------------------------------------------------------------
-set_server_verify_data(current_write, Data,
+set_server_verify_data(current_write, Data,
#connection_states{pending_read = PendingRead0,
- current_write = CurrentWrite0}
+ 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,
+set_server_verify_data(current_read, Data,
#connection_states{current_read = CurrentRead0,
- pending_write = PendingWrite0}
+ 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,
+set_server_verify_data(current_both, Data,
#connection_states{current_read = CurrentRead0,
- current_write = CurrentWrite0}
+ 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.
-%%--------------------------------------------------------------------
-activate_pending_connection_state(States =
- #connection_states{pending_read = Pending},
- read) ->
- NewCurrent = Pending#connection_state{sequence_number = 0},
- SecParams = Pending#connection_state.security_parameters,
- ConnectionEnd = SecParams#security_parameters.connection_end,
- 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
- };
-
-activate_pending_connection_state(States =
- #connection_states{pending_write = Pending},
- write) ->
- NewCurrent = Pending#connection_state{sequence_number = 0},
- SecParams = Pending#connection_state.security_parameters,
- ConnectionEnd = SecParams#security_parameters.connection_end,
- 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
- }.
-
%%--------------------------------------------------------------------
-spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
#cipher_state{}, client | server) ->
@@ -306,227 +297,6 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
pending_read = Read#connection_state{cipher_state = ServerState},
pending_write = Write#connection_state{cipher_state = ClientState}}.
-%%--------------------------------------------------------------------
--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, <<>>) ->
- get_tls_records_aux(Data, []);
-get_tls_records(Data, Buffer) ->
- get_tls_records_aux(list_to_binary([Buffer, Data]), []).
-
-get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length),
- Data:Length/binary, Rest/binary>>, Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary,
- Rest/binary>>, Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
- ?UINT16(Length), Data:Length/binary, Rest/binary>>,
- Acc) ->
- get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
-%% Matches an ssl v2 client hello message.
-%% The server must be able to receive such messages, from clients that
-%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
-get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
- Acc) ->
- case Data0 of
- <<?BYTE(?CLIENT_HELLO), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>> ->
- Length = Length0-1,
- <<?BYTE(_), Data1:Length/binary>> = Data0,
- Data = <<?BYTE(?CLIENT_HELLO), ?UINT24(Length), Data1/binary>>,
- get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
- version = {MajVer, MinVer},
- fragment = Data} | Acc]);
- _ ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
-
- end;
-
-get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
- ?UINT16(Length), _/binary>>,
- _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
-get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
- when Length0 > ?MAX_CIPHER_TEXT_LENGTH ->
- ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
-
-get_tls_records_aux(Data, Acc) ->
- case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of
- true ->
- {lists:reverse(Acc), Data};
- false ->
- ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE)
- end.
-%%--------------------------------------------------------------------
--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.
-%%--------------------------------------------------------------------
-protocol_version('tlsv1.2') ->
- {3, 3};
-protocol_version('tlsv1.1') ->
- {3, 2};
-protocol_version(tlsv1) ->
- {3, 1};
-protocol_version(sslv3) ->
- {3, 0};
-protocol_version(sslv2) -> %% Backwards compatibility
- {2, 0};
-protocol_version({3, 3}) ->
- 'tlsv1.2';
-protocol_version({3, 2}) ->
- 'tlsv1.1';
-protocol_version({3, 1}) ->
- tlsv1;
-protocol_version({3, 0}) ->
- sslv3.
-%%--------------------------------------------------------------------
--spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
-%%
-%% Description: Lowes protocol version of two given versions
-%%--------------------------------------------------------------------
-lowest_protocol_version(Version = {M, N}, {M, O}) when N < O ->
- Version;
-lowest_protocol_version({M, _},
- Version = {M, _}) ->
- Version;
-lowest_protocol_version(Version = {M,_},
- {N, _}) when M < N ->
- Version;
-lowest_protocol_version(_,Version) ->
- Version.
-%%--------------------------------------------------------------------
--spec highest_protocol_version([tls_version()]) -> tls_version().
-%%
-%% Description: Highest protocol version present in a list
-%%--------------------------------------------------------------------
-highest_protocol_version([]) ->
- highest_protocol_version();
-highest_protocol_version(Versions) ->
- [Ver | Vers] = Versions,
- highest_protocol_version(Ver, Vers).
-
-highest_protocol_version(Version, []) ->
- Version;
-highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M > O ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M > N ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(_, [Version | Rest]) ->
- highest_protocol_version(Version, Rest).
-
-%%--------------------------------------------------------------------
--spec supported_protocol_versions() -> [tls_version()].
-%%
-%% Description: Protocol versions supported
-%%--------------------------------------------------------------------
-supported_protocol_versions() ->
- Fun = fun(Version) ->
- protocol_version(Version)
- end,
- case application:get_env(ssl, protocol_version) of
- undefined ->
- lists:map(Fun, supported_protocol_versions([]));
- {ok, []} ->
- lists:map(Fun, supported_protocol_versions([]));
- {ok, Vsns} when is_list(Vsns) ->
- Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
- supported_protocol_versions(Versions);
- {ok, Vsn} ->
- Versions = lists:filter(fun is_acceptable_version/1, [Fun(Vsn)]),
- supported_protocol_versions(Versions)
- end.
-
-supported_protocol_versions([]) ->
- Vsns = case sufficient_tlsv1_2_crypto_support() of
- true ->
- ?ALL_SUPPORTED_VERSIONS;
- false ->
- ?MIN_SUPPORTED_VERSIONS
- end,
- application:set_env(ssl, protocol_version, Vsns),
- Vsns;
-
-supported_protocol_versions([_|_] = Vsns) ->
- Vsns.
-
-%%--------------------------------------------------------------------
--spec is_acceptable_version(tls_version()) -> boolean().
-%%
-%% Description: ssl version 2 is not acceptable security risks are too big.
-%%--------------------------------------------------------------------
-is_acceptable_version({N,_})
- when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
- true;
-is_acceptable_version(_) ->
- false.
-
-%%--------------------------------------------------------------------
--spec compressions() -> [binary()].
-%%
-%% Description: return a list of compressions supported (currently none)
-%%--------------------------------------------------------------------
-compressions() ->
- [?byte(?NULL)].
-
-%%--------------------------------------------------------------------
--spec decode_cipher_text(#ssl_tls{}, #connection_states{}) ->
- {#ssl_tls{}, #connection_states{}}| #alert{}.
-%%
-%% Description: Decode cipher text
-%%--------------------------------------------------------------------
-decode_cipher_text(CipherText, ConnnectionStates0) ->
- ReadState0 = ConnnectionStates0#connection_states.current_read,
- #connection_state{compression_state = CompressionS0,
- security_parameters = SecParams} = ReadState0,
- CompressAlg = SecParams#security_parameters.compression_algorithm,
- 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.
-%%--------------------------------------------------------------------
--spec encode_data(binary(), tls_version(), #connection_states{}) ->
- {iolist(), #connection_states{}}.
-%%
-%% Description: Encodes data to send on the ssl-socket.
-%%--------------------------------------------------------------------
-encode_data(Frag, Version,
- #connection_states{current_write = #connection_state{
- security_parameters =
- #security_parameters{bulk_cipher_algorithm = BCA}}} =
- ConnectionStates) ->
- Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
- encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
%%--------------------------------------------------------------------
-spec encode_handshake(iolist(), tls_version(), #connection_states{}) ->
@@ -558,31 +328,71 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
-%%% Internal functions
+-spec encode_data(binary(), tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes data to send on the ssl-socket.
%%--------------------------------------------------------------------
-encode_iolist(Type, Data, Version, ConnectionStates0) ->
- {ConnectionStates, EncodedMsg} =
- lists:foldl(fun(Text, {CS0, Encoded}) ->
- {Enc, CS1} = encode_plain_text(Type, Version, Text, CS0),
- {CS1, [Enc | Encoded]}
- end, {ConnectionStates0, []}, Data),
- {lists:reverse(EncodedMsg), ConnectionStates}.
+encode_data(Frag, Version,
+ #connection_states{current_write = #connection_state{
+ security_parameters =
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ ConnectionStates) ->
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
+ encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
-highest_protocol_version() ->
- highest_protocol_version(supported_protocol_versions()).
+uncompress(?NULL, Data, CS) ->
+ {Data, CS}.
-initial_connection_state(ConnectionEnd) ->
- #connection_state{security_parameters =
- initial_security_params(ConnectionEnd),
- sequence_number = 0
- }.
+compress(?NULL, Data, CS) ->
+ {Data, CS}.
-initial_security_params(ConnectionEnd) ->
- SecParams = #security_parameters{connection_end = ConnectionEnd,
- compression_algorithm = ?NULL},
- ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL,
- SecParams).
+%%--------------------------------------------------------------------
+-spec compressions() -> [binary()].
+%%
+%% Description: return a list of compressions supported (currently none)
+%%--------------------------------------------------------------------
+compressions() ->
+ [?byte(?NULL)].
+
+%%--------------------------------------------------------------------
+-spec cipher(tls_version(), iolist(), #connection_state{}, MacHash::binary()) ->
+ {CipherFragment::binary(), #connection_state{}}.
+%%
+%% Description: Payload encryption
+%%--------------------------------------------------------------------
+cipher(Version, Fragment,
+ #connection_state{cipher_state = CipherS0,
+ security_parameters=
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo}
+ } = WriteState0, MacHash) ->
+ {CipherFragment, CipherS1} =
+ ssl_cipher:cipher(BulkCipherAlgo, CipherS0, MacHash, Fragment, Version),
+ {CipherFragment, WriteState0#connection_state{cipher_state = CipherS1}}.
+%%--------------------------------------------------------------------
+-spec decipher(tls_version(), binary(), #connection_state{}) -> {binary(), binary(), #connection_state{}}.
+%%
+%% Description: Payload decryption
+%%--------------------------------------------------------------------
+decipher(Version, CipherFragment,
+ #connection_state{security_parameters =
+ #security_parameters{bulk_cipher_algorithm =
+ BulkCipherAlgo,
+ hash_size = HashSz},
+ cipher_state = CipherS0
+ } = ReadState) ->
+ case ssl_cipher:decipher(BulkCipherAlgo, HashSz, CipherS0, CipherFragment, Version) of
+ {PlainFragment, Mac, CipherS1} ->
+ CS1 = ReadState#connection_state{cipher_state = CipherS1},
+ {PlainFragment, Mac, CS1};
+ #alert{} = Alert ->
+ Alert
+ end.
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
empty_connection_state(ConnectionEnd) ->
SecParams = empty_security_params(ConnectionEnd),
#connection_state{security_parameters = SecParams}.
@@ -599,14 +409,52 @@ random() ->
Random_28_bytes = crypto:rand_bytes(28),
<<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
+dtls_next_epoch(#connection_state{epoch = undefined}) -> %% SSL/TLS
+ undefined;
+dtls_next_epoch(#connection_state{epoch = Epoch}) -> %% DTLS
+ Epoch + 1.
+
+is_correct_mac(Mac, Mac) ->
+ true;
+is_correct_mac(_M,_H) ->
+ false.
+
record_protocol_role(client) ->
?CLIENT;
record_protocol_role(server) ->
?SERVER.
-%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are not vulnerable to this attack.
-split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when BCA =/= ?RC4 andalso ({3, 1} == Version orelse
- {3, 0} == Version) ->
+initial_connection_state(ConnectionEnd) ->
+ #connection_state{security_parameters =
+ initial_security_params(ConnectionEnd),
+ sequence_number = 0
+ }.
+
+initial_security_params(ConnectionEnd) ->
+ SecParams = #security_parameters{connection_end = ConnectionEnd,
+ compression_algorithm = ?NULL},
+ ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, SecParams).
+
+
+encode_plain_text(Type, Version, Data, ConnectionStates) ->
+ RecordCB = protocol_module(Version),
+ RecordCB:encode_plain_text(Type, Version, Data, ConnectionStates).
+
+encode_iolist(Type, Data, Version, ConnectionStates0) ->
+ RecordCB = protocol_module(Version),
+ {ConnectionStates, EncodedMsg} =
+ lists:foldl(fun(Text, {CS0, Encoded}) ->
+ {Enc, CS1} =
+ RecordCB:encode_plain_text(Type, Version, Text, CS0),
+ {CS1, [Enc | Encoded]}
+ end, {ConnectionStates0, []}, Data),
+ {lists:reverse(EncodedMsg), ConnectionStates}.
+
+%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
+%% not vulnerable to this attack.
+split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when
+ BCA =/= ?RC4 andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
do_split_bin(Rest, ChunkSize, [[FirstByte]]);
split_bin(Bin, ChunkSize, _, _) ->
do_split_bin(Bin, ChunkSize, []).
@@ -621,95 +469,7 @@ do_split_bin(Bin, ChunkSize, Acc) ->
lists:reverse(Acc, [Bin])
end.
-encode_plain_text(Type, Version, Data, ConnectionStates) ->
- #connection_states{current_write=#connection_state{
- compression_state=CompS0,
- security_parameters=
- #security_parameters{compression_algorithm=CompAlg}
- }=CS0} = ConnectionStates,
- {Comp, CompS1} = compress(CompAlg, Data, CompS0),
- CS1 = CS0#connection_state{compression_state = CompS1},
- {CipherText, CS2} = cipher(Type, Version, Comp, CS1),
- CTBin = encode_tls_cipher_text(Type, Version, CipherText),
- {CTBin, ConnectionStates#connection_states{current_write = CS2}}.
-
-encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
- Length = erlang:iolist_size(Fragment),
- [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
-
-cipher(Type, Version, Fragment, CS0) ->
- Length = erlang:iolist_size(Fragment),
- {MacHash, CS1=#connection_state{cipher_state = CipherS0,
- security_parameters=
- #security_parameters{bulk_cipher_algorithm =
- BCA}
- }} =
- hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version),
- 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,
- HashSz = SP#security_parameters.hash_size,
- CipherS0 = CS0#connection_state.cipher_state,
- 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,
- fragment = _Fragment}, CS) ->
- {Data, CS}.
-
-compress(?NULL, Data, CS) ->
- {Data, CS}.
-
-hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
- mac_secret = MacSecret,
- security_parameters =
- SecPars} = CS0,
- Type, Version, Length, Fragment) ->
- Hash = mac_hash(Version,
- SecPars#security_parameters.mac_algorithm,
- MacSecret, SeqNo, Type,
- Length, Fragment),
- {Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
-
-is_correct_mac(Mac, Mac) ->
- true;
-is_correct_mac(_M,_H) ->
- false.
-
-mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
- _Length, _Fragment) ->
- <<>>;
-mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
- ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
-mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
- when N =:= 1; N =:= 2; N =:= 3 ->
- ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
- Length, Fragment).
-
-sufficient_tlsv1_2_crypto_support() ->
- Data = "Sampl",
- Data2 = "e #1",
- Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39,
- 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>,
- try
- crypto:sha256_mac(Key, lists:flatten([Data, Data2])),
- true
- catch _:_ -> false
- end.
+protocol_module({3, _}) ->
+ tls_record;
+protocol_module({254, _}) ->
+ dtls_record.