1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19
20-module(tls_record_1_3).
21
22-include("tls_record.hrl").
23-include("tls_record_1_3.hrl").
24-include("ssl_internal.hrl").
25-include("ssl_alert.hrl").
26-include("ssl_cipher.hrl").
27
28%% Encoding
29-export([encode_handshake/2, encode_alert_record/2,
30	 encode_data/2]).
31-export([encode_plain_text/3]).
32
33%% Decoding
34-export([decode_cipher_text/2]).
35
36%%====================================================================
37%% Encoding
38%%====================================================================
39
40%%--------------------------------------------------------------------
41-spec encode_handshake(iolist(), ssl_record:connection_states()) ->
42			      {iolist(), ssl_record:connection_states()}.
43%
44%% Description: Encodes a handshake message to send on the tls-1.3-socket.
45%%--------------------------------------------------------------------
46encode_handshake(Frag, ConnectionStates) ->
47    case iolist_size(Frag) of
48	N  when N > ?MAX_PLAIN_TEXT_LENGTH ->
49            %% TODO: Consider padding here
50	    Data = tls_record:split_iovec(Frag),
51	    encode_iolist(?HANDSHAKE, Data, ConnectionStates);
52	_  ->
53	    encode_plain_text(?HANDSHAKE, Frag, ConnectionStates)
54    end.
55
56%%--------------------------------------------------------------------
57-spec encode_alert_record(#alert{}, ssl_record:connection_states()) ->
58				 {iolist(), ssl_record:connection_states()}.
59%%
60%% Description: Encodes an alert message to send on the ssl-socket.
61%%--------------------------------------------------------------------
62encode_alert_record(#alert{level = Level, description = Description},
63                    ConnectionStates) ->
64    encode_plain_text(?ALERT, <<?BYTE(Level), ?BYTE(Description)>>,
65		      ConnectionStates).
66%%--------------------------------------------------------------------
67-spec encode_data(iolist(), ssl_record:connection_states()) ->
68			 {iolist(), ssl_record:connection_states()}.
69%%
70%% Description: Encodes data to send on the ssl-socket.
71%%--------------------------------------------------------------------
72encode_data(Frag, ConnectionStates) ->
73    Data = tls_record:split_iovec(Frag),
74    encode_iolist(?APPLICATION_DATA, Data, ConnectionStates).
75
76encode_plain_text(Type, Data0, #{current_write := Write0} = ConnectionStates) ->
77    PadLen = 0, %% TODO where to specify PadLen?
78    Data = inner_plaintext(Type, Data0, PadLen),
79    CipherFragment = encode_plain_text(Data, Write0),
80    {CipherText, Write} = encode_tls_cipher_text(CipherFragment, Write0),
81    {CipherText, ConnectionStates#{current_write => Write}}.
82
83encode_iolist(Type, Data, ConnectionStates0) ->
84    {ConnectionStates, EncodedMsg} =
85        lists:foldl(fun(Text, {CS0, Encoded}) ->
86			    {Enc, CS1} =
87				encode_plain_text(Type, Text, CS0),
88			    {CS1, [Enc | Encoded]}
89		    end, {ConnectionStates0, []}, Data),
90    {lists:reverse(EncodedMsg), ConnectionStates}.
91
92%%====================================================================
93%% Decoding
94%%====================================================================
95
96%%--------------------------------------------------------------------
97-spec decode_cipher_text(#ssl_tls{}, ssl_record:connection_states()) ->
98				{#ssl_tls{}, ssl_record:connection_states()}| #alert{}.
99%%
100%% Description: Decode cipher text, use legacy type ssl_tls instead of tls_cipher_text
101%% in decoding context so that we can reuse the code from erlier versions.
102%%--------------------------------------------------------------------
103decode_cipher_text(#ssl_tls{type = ?OPAQUE_TYPE,
104                            version = ?LEGACY_VERSION,
105                            fragment = CipherFragment},
106		   #{current_read :=
107			 #{sequence_number := Seq,
108                           cipher_state := #cipher_state{key = Key,
109                                                         iv = IV,
110                                                         tag_len = TagLen},
111			   security_parameters :=
112			       #security_parameters{
113				  cipher_type = ?AEAD,
114                                  bulk_cipher_algorithm =
115                                      BulkCipherAlgo}
116			  } = ReadState0} = ConnectionStates0) ->
117    case decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) of
118	#alert{} = Alert ->
119	    Alert;
120	PlainFragment ->
121	    ConnectionStates =
122                ConnectionStates0#{current_read =>
123                                       ReadState0#{sequence_number => Seq + 1}},
124	    {decode_inner_plaintext(PlainFragment), ConnectionStates}
125    end;
126
127
128%% RFC8446 - TLS 1.3 (OpenSSL compatibility)
129%% Handle unencrypted Alerts from openssl s_client when server's
130%% connection states are already stepped into traffic encryption.
131%% (E.g. openssl s_client receives a CertificateRequest with
132%% a signature_algorithms_cert extension that does not contain
133%% the signature algorithm of the client's certificate.)
134decode_cipher_text(#ssl_tls{type = ?ALERT,
135                            version = ?LEGACY_VERSION,
136                            fragment = <<2,47>>},
137		   ConnectionStates0) ->
138    {#ssl_tls{type = ?ALERT,
139              version = {3,4}, %% Internally use real version
140              fragment = <<2,47>>}, ConnectionStates0};
141%% TLS 1.3 server can receive a User Cancelled Alert when handshake is
142%% paused and then cancelled on the client side.
143decode_cipher_text(#ssl_tls{type = ?ALERT,
144                            version = ?LEGACY_VERSION,
145                            fragment = <<2,90>>},
146		   ConnectionStates0) ->
147    {#ssl_tls{type = ?ALERT,
148              version = {3,4}, %% Internally use real version
149              fragment = <<2,90>>}, ConnectionStates0};
150%% RFC8446 - TLS 1.3
151%% D.4.  Middlebox Compatibility Mode
152%%    -  If not offering early data, the client sends a dummy
153%%       change_cipher_spec record (see the third paragraph of Section 5)
154%%       immediately before its second flight.  This may either be before
155%%       its second ClientHello or before its encrypted handshake flight.
156%%       If offering early data, the record is placed immediately after the
157%%       first ClientHello.
158decode_cipher_text(#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
159                            version = ?LEGACY_VERSION,
160                            fragment = <<1>>},
161		   ConnectionStates0) ->
162    {#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
163              version = {3,4}, %% Internally use real version
164              fragment = <<1>>}, ConnectionStates0};
165decode_cipher_text(#ssl_tls{type = Type,
166                            version = ?LEGACY_VERSION,
167                            fragment = CipherFragment},
168		   #{current_read :=
169			 #{security_parameters :=
170			       #security_parameters{
171                                  cipher_suite = ?TLS_NULL_WITH_NULL_NULL}
172			  }} = ConnnectionStates0) ->
173    {#ssl_tls{type = Type,
174              version = {3,4}, %% Internally use real version
175              fragment = CipherFragment}, ConnnectionStates0};
176decode_cipher_text(#ssl_tls{type = Type}, _) ->
177    %% Version mismatch is already asserted
178    ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, {record_type_mismatch, Type}).
179
180%%--------------------------------------------------------------------
181%%% Internal functions
182%%--------------------------------------------------------------------
183inner_plaintext(Type, Data, Length) ->
184    #inner_plaintext{
185       content = Data,
186       type = Type,
187       zeros = zero_padding(Length)
188      }.
189zero_padding(Length)->
190    binary:copy(<<?BYTE(0)>>, Length).
191
192encode_plain_text(#inner_plaintext{
193                     content = Data,
194                     type = Type,
195                     zeros = Zeros
196                    }, #{cipher_state := #cipher_state{key= Key,
197                                                       iv = IV,
198                                                       tag_len = TagLen},
199                         sequence_number := Seq,
200                         security_parameters :=
201                             #security_parameters{
202                                cipher_type = ?AEAD,
203                                bulk_cipher_algorithm = BulkCipherAlgo}
204                        }) ->
205    PlainText = [Data, Type, Zeros],
206    Encoded = cipher_aead(PlainText, BulkCipherAlgo, Key, Seq, IV, TagLen),
207    #tls_cipher_text{opaque_type = 23,  %% 23 (application_data) for outward compatibility
208                     legacy_version = {3,3},
209                     encoded_record = Encoded};
210encode_plain_text(#inner_plaintext{
211                     content = Data,
212                     type = Type
213                    }, #{security_parameters :=
214                             #security_parameters{
215                                cipher_suite = ?TLS_NULL_WITH_NULL_NULL}
216                        }) ->
217    %% RFC8446 - 5.1.  Record Layer
218    %% When record protection has not yet been engaged, TLSPlaintext
219    %% structures are written directly onto the wire.
220    #tls_cipher_text{opaque_type = Type,
221                      legacy_version = {3,3},
222                      encoded_record = Data};
223
224encode_plain_text(_, CS) ->
225    exit({cs, CS}).
226
227additional_data(Length) ->
228    <<?BYTE(?OPAQUE_TYPE), ?BYTE(3), ?BYTE(3),?UINT16(Length)>>.
229
230%% The per-record nonce for the AEAD construction is formed as
231%% follows:
232%%
233%% 1.  The 64-bit record sequence number is encoded in network byte
234%%     order and padded to the left with zeros to iv_length.
235%%
236%% 2.  The padded sequence number is XORed with either the static
237%%     client_write_iv or server_write_iv (depending on the role).
238%%
239%% The resulting quantity (of length iv_length) is used as the
240%% per-record nonce.
241nonce(Seq, IV) ->
242    Padding = binary:copy(<<0>>, byte_size(IV) - 8),
243    crypto:exor(<<Padding/binary,?UINT64(Seq)>>, IV).
244
245cipher_aead(Fragment, BulkCipherAlgo, Key, Seq, IV, TagLen) ->
246    AAD = additional_data(erlang:iolist_size(Fragment) + TagLen),
247    Nonce = nonce(Seq, IV),
248    {Content, CipherTag} =
249        ssl_cipher:aead_encrypt(BulkCipherAlgo, Key, Nonce, Fragment, AAD, TagLen),
250    <<Content/binary, CipherTag/binary>>.
251
252encode_tls_cipher_text(#tls_cipher_text{opaque_type = Type,
253                                        legacy_version = {MajVer, MinVer},
254                                        encoded_record = Encoded}, #{sequence_number := Seq} = Write) ->
255    Length = erlang:iolist_size(Encoded),
256    {[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Encoded],
257     Write#{sequence_number => Seq +1}}.
258
259decipher_aead(CipherFragment, BulkCipherAlgo, Key, Seq, IV, TagLen) ->
260    try
261        AAD = additional_data(erlang:iolist_size(CipherFragment)),
262        Nonce = nonce(Seq, IV),
263        {CipherText, CipherTag} = aead_ciphertext_split(CipherFragment, TagLen),
264	case ssl_cipher:aead_decrypt(BulkCipherAlgo, Key, Nonce, CipherText, CipherTag, AAD) of
265	    Content when is_binary(Content) ->
266		Content;
267	    _ ->
268                ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed)
269	end
270    catch
271	_:_ ->
272            ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC, decryption_failed)
273    end.
274
275
276aead_ciphertext_split(CipherTextFragment, TagLen)
277  when is_binary(CipherTextFragment) ->
278    CipherLen = erlang:byte_size(CipherTextFragment) - TagLen,
279    <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> = CipherTextFragment,
280    {CipherText, CipherTag};
281aead_ciphertext_split(CipherTextFragment, TagLen)
282  when is_list(CipherTextFragment) ->
283    CipherLen = erlang:iolist_size(CipherTextFragment) - TagLen,
284    <<CipherText:CipherLen/bytes, CipherTag:TagLen/bytes>> =
285        erlang:iolist_to_binary(CipherTextFragment),
286    {CipherText, CipherTag}.
287
288decode_inner_plaintext(PlainText) ->
289    case binary:last(PlainText) of
290        0 ->
291            decode_inner_plaintext(init_binary(PlainText));
292        Type when Type =:= ?APPLICATION_DATA orelse
293                  Type =:= ?HANDSHAKE orelse
294                  Type =:= ?ALERT ->
295            #ssl_tls{type = Type,
296                     version = {3,4}, %% Internally use real version
297                     fragment = init_binary(PlainText)};
298        _Else ->
299            ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE, empty_alert)
300    end.
301
302init_binary(B) ->
303    {Init, _} =
304        split_binary(B, byte_size(B) - 1),
305    Init.
306