From 810c34a7991f2b6edd5e9f41e3c667958a5b2bc8 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 10 Sep 2013 17:43:40 +0200 Subject: ssl: Refactor handshake and record handling --- lib/ssl/src/dtls_handshake.erl | 10 ++-- lib/ssl/src/dtls_record.erl | 123 ++++++++++++++--------------------------- lib/ssl/src/ssl_cipher.erl | 2 +- lib/ssl/src/ssl_record.erl | 38 ++++++++++++- lib/ssl/src/tls_handshake.erl | 2 - lib/ssl/src/tls_record.erl | 82 ++++++++++----------------- 6 files changed, 113 insertions(+), 144 deletions(-) diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index d0f9649f9f..d898da65fb 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -87,11 +87,6 @@ hello(Address, Port, {reply, HelloVerifyRequest} end. -address_to_bin({A,B,C,D}, Port) -> - <<0:80,16#ffff:16,A,B,C,D,Port:16>>; -address_to_bin({A,B,C,D,E,F,G,H}, Port) -> - <>. - %%-------------------------------------------------------------------- encode_handshake(Package, Version, MsgSeq, Mss) -> {MsgType, Bin} = enc_hs(Package, Version), @@ -426,3 +421,8 @@ decode_handshake(_Version, ?HELLO_VERIFY_REQUEST, < ssl_handshake:decode_handshake(Version, Tag, Msg). + +address_to_bin({A,B,C,D}, Port) -> + <<0:80,16#ffff:16,A,B,C,D,Port:16>>; +address_to_bin({A,B,C,D,E,F,G,H}, Port) -> + <>. diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index f667458a10..7959c4d860 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -40,8 +40,9 @@ %% Protocol version handling -export([protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, supported_protocol_versions/0, - is_acceptable_version/2, cipher/4, decipher/2]). + is_acceptable_version/2]). +%% DTLS Epoch handling -export([init_connection_state_seq/2, current_connection_state_epoch/2, set_connection_state_by_epoch/3, connection_state_by_epoch/3]). @@ -114,35 +115,44 @@ get_dtls_records_aux(Data, Acc) -> end. encode_plain_text(Type, Version, Data, - #connection_state{ - compression_state = CompS0, - epoch = Epoch, - sequence_number = Seq, - security_parameters= - #security_parameters{compression_algorithm = CompAlg} - }= CS0) -> + #connection_states{current_write=#connection_state{ + epoch = Epoch, + sequence_number = Seq, + compression_state=CompS0, + security_parameters= + #security_parameters{compression_algorithm=CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record: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, Epoch, Seq, CipherText), - {CTBin, CS2}. - -decode_cipher_text(CipherText, ConnnectionStates0) -> - ReadState0 = ConnnectionStates0#connection_states.current_read, - #connection_state{compression_state = CompressionS0, - security_parameters = SecParams} = ReadState0, + WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + MacHash = calc_mac_hash(WriteState1, Type, Version, Epoch, Seq, Comp), + {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash), + CipherText = encode_tls_cipher_text(Type, Version, Epoch, Seq, CipherFragment), + {CipherText, ConnectionStates#connection_states{current_write = + WriteState#connection_state{sequence_number = Seq +1}}}. + +decode_cipher_text(#ssl_tls{type = Type, version = Version, + epoch = Epoch, + record_seq = Seq, + fragment = CipherFragment} = CipherText, + #connection_states{current_read = + #connection_state{compression_state = CompressionS0, + security_parameters = SecParams} = ReadState0} + = ConnnectionStates0) -> CompressAlg = SecParams#security_parameters.compression_algorithm, - case decipher(CipherText, ReadState0) of - {Compressed, ReadState1} -> - {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg, - Compressed, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - compression_state = CompressionS1}}, - {Plain, ConnnectionStates}; - #alert{} = Alert -> - Alert - end. + {PlainFragment, Mac, ReadState1} = ssl_record:decipher(dtls_v1:corresponding_tls_version(Version), + CipherFragment, ReadState0), + MacHash = calc_mac_hash(Type, Version, Epoch, Seq, PlainFragment, ReadState1), + case ssl_record:is_correct_mac(Mac, MacHash) of + true -> + {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg, + PlainFragment, CompressionS0), + ConnnectionStates = ConnnectionStates0#connection_states{ + current_read = ReadState1#connection_state{ + compression_state = CompressionS1}}, + {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end. %%-------------------------------------------------------------------- -spec protocol_version(tls_atom_version() | tls_version()) -> @@ -323,62 +333,13 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Epoch, Seq, Fragment) -> [<>, Fragment]. -cipher(Type, Version, Fragment, CS0) -> +calc_mac_hash(#connection_state{mac_secret = MacSecret, + security_parameters = #security_parameters{mac_algorithm = MacAlg}}, + Type, Version, Epoch, SeqNo, Fragment) -> 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={254, _}, - epoch = Epoch, record_seq = SeqNo, - 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 = hash_with_seqno(CS1, Type, Version, Epoch, SeqNo, TLength, T), - case ssl_record:is_correct_mac(Mac, MacHash) of - true -> - {TLS#ssl_tls{fragment = T}, CS1}; - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - #alert{} = Alert -> - Alert - end. - -hash_with_seqno(#connection_state{mac_secret = MacSecret, - security_parameters = - SecPars}, - Type, Version = {254, _}, - Epoch, SeqNo, Length, Fragment) -> - mac_hash(Version, - SecPars#security_parameters.mac_algorithm, - MacSecret, (Epoch bsl 48) + SeqNo, Type, + mac_hash(Version, MacAlg, MacSecret, (Epoch bsl 48) + SeqNo, Type, Length, Fragment). -hash_and_bump_seqno(#connection_state{epoch = Epoch, - sequence_number = SeqNo, - mac_secret = MacSecret, - security_parameters = - SecPars} = CS0, - Type, Version = {254, _}, Length, Fragment) -> - Hash = mac_hash(Version, - SecPars#security_parameters.mac_algorithm, - MacSecret, (Epoch bsl 48) + SeqNo, Type, - Length, Fragment), - {Hash, CS0#connection_state{sequence_number = SeqNo+1}}. - mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> dtls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, Length, Fragment). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index e6ed0d8626..b2077c662a 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -72,7 +72,7 @@ security_parameters(Version, CipherSuite, SecParams) -> hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- --spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) -> +-spec cipher(cipher_enum(), #cipher_state{}, binary(), iolist(), tls_version()) -> {binary(), #cipher_state{}}. %% %% Description: Encrypts the data and the MAC using chipher described diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 50a45dc16b..018c8befe0 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -47,7 +47,8 @@ %% Compression -export([compress/3, uncompress/3, compressions/0]). --export([is_correct_mac/2]). +%% Payload encryption/decryption +-export([cipher/4, decipher/3, is_correct_mac/2]). %%==================================================================== %% Internal application API @@ -355,6 +356,41 @@ 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) -> diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 262f2d929f..f783bacff6 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -26,10 +26,8 @@ -include("tls_handshake.hrl"). -include("tls_record.hrl"). --include("ssl_cipher.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). --include("ssl_srp.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([client_hello/8, server_hello/4, hello/4, diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 54cf8d0b80..88107557a0 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -121,17 +121,20 @@ get_tls_records_aux(Data, Acc) -> ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE) 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, +encode_plain_text(Type, Version, Data, + #connection_states{current_write = + #connection_state{ + sequence_number = Seq, + compression_state=CompS0, + security_parameters= + #security_parameters{compression_algorithm=CompAlg} + }= WriteState0} = ConnectionStates) -> {Comp, CompS1} = ssl_record:compress(CompAlg, Data, CompS0), - CS1 = CS0#connection_state{compression_state = CompS1}, - {CipherFragment, CS2} = cipher(Type, Version, Comp, CS1), - CTBin = encode_tls_cipher_text(Type, Version, CipherFragment), - {CTBin, ConnectionStates#connection_states{current_write = CS2}}. + WriteState1 = WriteState0#connection_state{compression_state = CompS1}, + MacHash = calc_mac_hash(Type, Version, Comp, WriteState1), + {CipherFragment, WriteState} = ssl_record:cipher(Version, Comp, WriteState1, MacHash), + CipherText = encode_tls_cipher_text(Type, Version, CipherFragment), + {CipherText, ConnectionStates#connection_states{current_write = WriteState#connection_state{sequence_number = Seq +1}}}. %%-------------------------------------------------------------------- -spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> @@ -143,19 +146,23 @@ decode_cipher_text(#ssl_tls{type = Type, version = Version, fragment = CipherFragment} = CipherText, ConnnectionStates0) -> ReadState0 = ConnnectionStates0#connection_states.current_read, #connection_state{compression_state = CompressionS0, + sequence_number = Seq, security_parameters = SecParams} = ReadState0, CompressAlg = SecParams#security_parameters.compression_algorithm, - case decipher(Type, Version, CipherFragment, ReadState0) of - {PlainFragment, ReadState1} -> - {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg, - PlainFragment, CompressionS0), - ConnnectionStates = ConnnectionStates0#connection_states{ - current_read = ReadState1#connection_state{ - compression_state = CompressionS1}}, - {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; - #alert{} = Alert -> - Alert - end. + {PlainFragment, Mac, ReadState1} = ssl_record:decipher(Version, CipherFragment, ReadState0), + MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState1), + case ssl_record:is_correct_mac(Mac, MacHash) of + true -> + {Plain, CompressionS1} = ssl_record:uncompress(CompressAlg, + PlainFragment, CompressionS0), + ConnnectionStates = ConnnectionStates0#connection_states{ + current_read = ReadState1#connection_state{ + sequence_number = Seq + 1, + compression_state = CompressionS1}}, + {CipherText#ssl_tls{fragment = Plain}, ConnnectionStates}; + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end. %%-------------------------------------------------------------------- -spec protocol_version(tls_atom_version() | tls_version()) -> @@ -280,39 +287,6 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) -> Length = erlang:iolist_size(Fragment), [<>, Fragment]. -cipher(Type, Version, Fragment, - #connection_state{cipher_state = CipherS0, - sequence_number = SeqNo, - security_parameters= - #security_parameters{bulk_cipher_algorithm = - BCA} - } = WriteState0) -> - MacHash = calc_mac_hash(Type, Version, Fragment, WriteState0), - {CipherFragment, CipherS1} = - ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version), - WriteState = WriteState0#connection_state{cipher_state=CipherS1}, - {CipherFragment, WriteState#connection_state{sequence_number = SeqNo+1}}. - -decipher(Type, Version, CipherFragment, - #connection_state{sequence_number = SeqNo} = ReadState) -> - SP = ReadState#connection_state.security_parameters, - BCA = SP#security_parameters.bulk_cipher_algorithm, - HashSz = SP#security_parameters.hash_size, - CipherS0 = ReadState#connection_state.cipher_state, - case ssl_cipher:decipher(BCA, HashSz, CipherS0, CipherFragment, Version) of - {PlainFragment, Mac, CipherS1} -> - CS1 = ReadState#connection_state{cipher_state = CipherS1}, - MacHash = calc_mac_hash(Type, Version, PlainFragment, ReadState), - case ssl_record:is_correct_mac(Mac, MacHash) of - true -> - {PlainFragment, - CS1#connection_state{sequence_number = SeqNo+1}}; - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - #alert{} = Alert -> - Alert - end. mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type, _Length, _Fragment) -> -- cgit v1.2.3