diff options
Diffstat (limited to 'lib/ssl/src/tls_record.erl')
-rw-r--r-- | lib/ssl/src/tls_record.erl | 133 |
1 files changed, 79 insertions, 54 deletions
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 38022030ee..a5c550a429 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -30,9 +30,10 @@ -include("ssl_alert.hrl"). -include("tls_handshake.hrl"). -include("ssl_cipher.hrl"). +-include_lib("kernel/include/logger.hrl"). %% Handling of incoming data --export([get_tls_records/3, init_connection_states/2]). +-export([get_tls_records/4, init_connection_states/2]). %% Encoding TLS records -export([encode_handshake/3, encode_alert_record/3, @@ -40,13 +41,16 @@ -export([encode_plain_text/4]). %% Decoding --export([decode_cipher_text/3]). +-export([decode_cipher_text/4]). + +%% Logging helper +-export([build_tls_record/1]). %% Protocol version handling -export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, highest_protocol_version/2, is_higher/2, supported_protocol_versions/0, - is_acceptable_version/1, is_acceptable_version/2, hello_version/2]). + is_acceptable_version/1, is_acceptable_version/2, hello_version/1]). -export_type([tls_version/0, tls_atom_version/0]). @@ -76,8 +80,10 @@ init_connection_states(Role, BeastMitigation) -> %%-------------------------------------------------------------------- -spec get_tls_records( - binary(), [tls_version()] | tls_version(), - Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}) -> + binary(), + [tls_version()] | tls_version(), + Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}, + #ssl_options{}) -> {Records :: [#ssl_tls{}], Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} | #alert{}. @@ -86,11 +92,10 @@ init_connection_states(Role, BeastMitigation) -> %% Description: Given old buffer and new data from TCP, packs up a records %% data %%-------------------------------------------------------------------- - -get_tls_records(Data, Versions, Buffer) when is_binary(Buffer) -> - parse_tls_records(Versions, {[Data],byte_size(Data),[]}, undefined); -get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}) -> - parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, Hdr). +get_tls_records(Data, Versions, Buffer, SslOpts) when is_binary(Buffer) -> + parse_tls_records(Versions, {[Data],byte_size(Data),[]}, SslOpts, undefined); +get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}, SslOpts) -> + parse_tls_records(Versions, {Front,Size + byte_size(Data),[Data|Rear]}, SslOpts, Hdr). %%==================================================================== %% Encoding @@ -102,6 +107,8 @@ get_tls_records(Data, Versions, {Hdr, {Front,Size,Rear}}) -> % %% Description: Encodes a handshake message to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_handshake(Frag, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_handshake(Frag, ConnectionStates); encode_handshake(Frag, Version, #{current_write := #{beast_mitigation := BeastMitigation, @@ -122,6 +129,8 @@ encode_handshake(Frag, Version, %% %% Description: Encodes an alert message to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_alert_record(Alert, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_alert_record(Alert, ConnectionStates); encode_alert_record(#alert{level = Level, description = Description}, Version, ConnectionStates) -> encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>, @@ -142,6 +151,8 @@ encode_change_cipher_spec(Version, ConnectionStates) -> %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- +encode_data(Data, {3, 4}, ConnectionStates) -> + tls_record_1_3:encode_data(Data, ConnectionStates); encode_data(Data, Version, #{current_write := #{beast_mitigation := BeastMitigation, security_parameters := @@ -155,12 +166,14 @@ encode_data(Data, Version, %%==================================================================== %%-------------------------------------------------------------------- --spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states(), boolean()) -> +-spec decode_cipher_text(tls_version(), #ssl_tls{}, ssl_record:connection_states(), boolean()) -> {#ssl_tls{}, ssl_record:connection_states()}| #alert{}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- -decode_cipher_text(CipherText, +decode_cipher_text({3,4}, CipherTextRecord, ConnectionStates, _) -> + tls_record_1_3:decode_cipher_text(CipherTextRecord, ConnectionStates); +decode_cipher_text(_, CipherTextRecord, #{current_read := #{sequence_number := Seq, security_parameters := @@ -170,7 +183,7 @@ decode_cipher_text(CipherText, } } = ConnectionStates0, _) -> SeqBin = <<?UINT64(Seq)>>, - #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherText, + #ssl_tls{type = Type, version = {MajVer,MinVer} = Version, fragment = Fragment} = CipherTextRecord, StartAdditionalData = <<SeqBin/binary, ?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer)>>, CipherS = ssl_record:nonce_seed(BulkCipherAlgo, SeqBin, CipherS0), case ssl_record:decipher_aead( @@ -187,17 +200,17 @@ decode_cipher_text(CipherText, cipher_state => CipherS, sequence_number => Seq + 1, compression_state => CompressionS}}, - {CipherText#ssl_tls{fragment = Plain}, ConnectionStates}; + {CipherTextRecord#ssl_tls{fragment = Plain}, ConnectionStates}; #alert{} = Alert -> Alert end; -decode_cipher_text(#ssl_tls{version = Version, - fragment = CipherFragment} = CipherText, +decode_cipher_text(_, #ssl_tls{version = Version, + fragment = CipherFragment} = CipherTextRecord, #{current_read := ReadState0} = ConnnectionStates0, PaddingCheck) -> case ssl_record:decipher(Version, CipherFragment, ReadState0, PaddingCheck) of {PlainFragment, Mac, ReadState1} -> - MacHash = ssl_cipher:calc_mac_hash(CipherText#ssl_tls.type, Version, PlainFragment, ReadState1), + MacHash = ssl_cipher:calc_mac_hash(CipherTextRecord#ssl_tls.type, Version, PlainFragment, ReadState1), case ssl_record:is_correct_mac(Mac, MacHash) of true -> #{sequence_number := Seq, @@ -210,7 +223,7 @@ decode_cipher_text(#ssl_tls{version = Version, ConnnectionStates0#{current_read => ReadState1#{sequence_number => Seq + 1, compression_state => CompressionS1}}, - {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; + {CipherTextRecord#ssl_tls{fragment = Plain}, ConnnectionStates}; false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; @@ -229,6 +242,8 @@ decode_cipher_text(#ssl_tls{version = Version, %% Description: Creates a protocol version record from a version atom %% or vice versa. %%-------------------------------------------------------------------- +protocol_version('tlsv1.3') -> + {3, 4}; protocol_version('tlsv1.2') -> {3, 3}; protocol_version('tlsv1.1') -> @@ -239,6 +254,8 @@ protocol_version(sslv3) -> {3, 0}; protocol_version(sslv2) -> %% Backwards compatibility {2, 0}; +protocol_version({3, 4}) -> + 'tlsv1.3'; protocol_version({3, 3}) -> 'tlsv1.2'; protocol_version({3, 2}) -> @@ -372,10 +389,10 @@ is_acceptable_version({N,_} = Version, Versions) is_acceptable_version(_,_) -> false. --spec hello_version(tls_version(), [tls_version()]) -> tls_version(). -hello_version(Version, _) when Version >= {3, 3} -> - Version; -hello_version(_, Versions) -> +-spec hello_version([tls_version()]) -> tls_version(). +hello_version([Highest|_]) when Highest >= {3,3} -> + Highest; +hello_version(Versions) -> lowest_protocol_version(Versions). %%-------------------------------------------------------------------- @@ -394,84 +411,91 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> server_verify_data => undefined }. +%% Used by logging to recreate the received bytes +build_tls_record(#ssl_tls{type = Type, version = {MajVer, MinVer}, fragment = Fragment}) -> + Length = byte_size(Fragment), + <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),?UINT16(Length), Fragment/binary>>. -parse_tls_records(Versions, Q, undefined) -> - decode_tls_records(Versions, Q, [], undefined, undefined, undefined); -parse_tls_records(Versions, Q, #ssl_tls{type = Type, version = Version, fragment = Length}) -> - decode_tls_records(Versions, Q, [], Type, Version, Length). + +parse_tls_records(Versions, Q, SslOpts, undefined) -> + decode_tls_records(Versions, Q, SslOpts, [], undefined, undefined, undefined); +parse_tls_records(Versions, Q, SslOpts, #ssl_tls{type = Type, version = Version, fragment = Length}) -> + decode_tls_records(Versions, Q, SslOpts, [], Type, Version, Length). %% Generic code path -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, undefined, _Version, _Length) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, undefined, _Version, _Length) -> if 5 =< Size -> {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(5, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length); 3 =< Size -> {<<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(3, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined); 1 =< Size -> {<<?BYTE(Type)>>, Q} = binary_from_front(1, Q0), - validate_tls_records_type(Versions, Q, Acc, Type, undefined, undefined); + validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, undefined, undefined); true -> - validate_tls_records_type(Versions, Q0, Acc, undefined, undefined, undefined) + validate_tls_records_type(Versions, Q0, SslOpts, Acc, undefined, undefined, undefined) end; -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, undefined, _Length) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, undefined, _Length) -> if 4 =< Size -> {<<?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Length)>>, Q} = binary_from_front(4, Q0), - validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, Length); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, Length); 2 =< Size -> {<<?BYTE(MajVer),?BYTE(MinVer)>>, Q} = binary_from_front(2, Q0), - validate_tls_record_version(Versions, Q, Acc, Type, {MajVer,MinVer}, undefined); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, {MajVer,MinVer}, undefined); true -> - validate_tls_record_version(Versions, Q0, Acc, Type, undefined, undefined) + validate_tls_record_version(Versions, Q0, SslOpts, Acc, Type, undefined, undefined) end; -decode_tls_records(Versions, {_,Size,_} = Q0, Acc, Type, Version, undefined) -> +decode_tls_records(Versions, {_,Size,_} = Q0, SslOpts, Acc, Type, Version, undefined) -> if 2 =< Size -> {<<?UINT16(Length)>>, Q} = binary_from_front(2, Q0), - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); true -> - validate_tls_record_length(Versions, Q0, Acc, Type, Version, undefined) + validate_tls_record_length(Versions, Q0, SslOpts, Acc, Type, Version, undefined) end; -decode_tls_records(Versions, Q, Acc, Type, Version, Length) -> - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length). +decode_tls_records(Versions, Q, SslOpts, Acc, Type, Version, Length) -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length). -validate_tls_records_type(_Versions, Q, Acc, undefined, _Version, _Length) -> +validate_tls_records_type(_Versions, Q, _SslOpts, Acc, undefined, _Version, _Length) -> {lists:reverse(Acc), {undefined, Q}}; -validate_tls_records_type(Versions, Q, Acc, Type, Version, Length) -> +validate_tls_records_type(Versions, Q, SslOpts, Acc, Type, Version, Length) -> if ?KNOWN_RECORD_TYPE(Type) -> - validate_tls_record_version(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length); true -> %% Not ?KNOWN_RECORD_TYPE(Type) ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE) end. -validate_tls_record_version(_Versions, Q, Acc, Type, undefined, _Length) -> +validate_tls_record_version(_Versions, Q, _SslOpts, Acc, Type, undefined, _Length) -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = undefined, fragment = undefined}, Q}}; -validate_tls_record_version(Versions, Q, Acc, Type, Version, Length) -> - if - is_list(Versions) -> +validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) -> + case Versions of + _ when is_list(Versions) -> case is_acceptable_version(Version, Versions) of true -> - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); false -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; - Version =:= Versions -> + {3, 4} when Version =:= {3, 3} -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); + Version -> %% Exact version match - validate_tls_record_length(Versions, Q, Acc, Type, Version, Length); - true -> + validate_tls_record_length(Versions, Q, SslOpts, Acc, Type, Version, Length); + _ -> ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end. -validate_tls_record_length(_Versions, Q, Acc, Type, Version, undefined) -> +validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}}; -validate_tls_record_length(Versions, {_,Size0,_} = Q0, Acc, Type, Version, Length) -> +validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) -> if Length =< ?MAX_CIPHER_TEXT_LENGTH -> if @@ -479,7 +503,8 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, Acc, Type, Version, Lengt %% Complete record {Fragment, Q} = binary_from_front(Length, Q0), Record = #ssl_tls{type = Type, version = Version, fragment = Fragment}, - decode_tls_records(Versions, Q, [Record|Acc], undefined, undefined, undefined); + ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record), + decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined); true -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = Version, fragment = Length}, Q0}} |