From b9a31f24053c84d9a7ffa4281bc11f47b3be5905 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 18 Jun 2013 12:30:38 +0200 Subject: ssl: DTLS record handling Also refactor so that TLS and DTLS can have common functions when possible. --- lib/ssl/src/Makefile | 7 +- lib/ssl/src/dtls_record.erl | 51 ++--- lib/ssl/src/dtls_record.hrl | 10 - lib/ssl/src/dtls_v1.erl | 2 +- lib/ssl/src/ssl.app.src | 8 +- lib/ssl/src/ssl_cipher.erl | 4 +- lib/ssl/src/ssl_handshake.erl | 12 +- lib/ssl/src/ssl_record.erl | 89 +++++++++ lib/ssl/src/ssl_record.hrl | 7 + lib/ssl/src/ssl_ssl2.erl | 37 ---- lib/ssl/src/ssl_ssl3.erl | 203 -------------------- lib/ssl/src/ssl_tls1.erl | 432 ------------------------------------------ lib/ssl/src/ssl_v2.erl | 37 ++++ lib/ssl/src/ssl_v3.erl | 203 ++++++++++++++++++++ lib/ssl/src/tls_handshake.erl | 19 +- lib/ssl/src/tls_record.erl | 31 +-- lib/ssl/src/tls_record.hrl | 7 - lib/ssl/src/tls_v1.erl | 432 ++++++++++++++++++++++++++++++++++++++++++ 18 files changed, 822 insertions(+), 769 deletions(-) create mode 100644 lib/ssl/src/ssl_record.erl delete mode 100644 lib/ssl/src/ssl_ssl2.erl delete mode 100644 lib/ssl/src/ssl_ssl3.erl delete mode 100644 lib/ssl/src/ssl_tls1.erl create mode 100644 lib/ssl/src/ssl_v2.erl create mode 100644 lib/ssl/src/ssl_v3.erl create mode 100644 lib/ssl/src/tls_v1.erl diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index a467b3f608..a5af451244 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -65,9 +65,10 @@ MODULES= \ ssl_socket \ tls_record \ dtls_record \ - ssl_ssl2 \ - ssl_ssl3 \ - ssl_tls1 \ + ssl_record \ + ssl_v2 \ + ssl_v3 \ + tls_v1 \ ssl_tls_dist_proxy INTERNAL_HRL_FILES = \ diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 98c60f599c..daadae0725 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -30,7 +30,7 @@ %% Misc. -export([protocol_version/1, lowest_protocol_version/2, highest_protocol_version/1, supported_protocol_versions/0, - is_acceptable_version/2]). + is_acceptable_version/2, cipher/4, decipher/2]). %%-------------------------------------------------------------------- -spec init_connection_state_seq(tls_version(), #connection_states{}) -> @@ -71,25 +71,18 @@ current_connection_state_epoch(#connection_states{current_write = Current}, %% Description: Returns the instance of the connection_state record %% that is defined by the Epoch. %%-------------------------------------------------------------------- -connection_state_by_epoch(#connection_states{previous_read = CS}, Epoch, read) - when CS#connection_state.epoch == Epoch -> - CS; connection_state_by_epoch(#connection_states{current_read = CS}, Epoch, read) when CS#connection_state.epoch == Epoch -> CS; connection_state_by_epoch(#connection_states{pending_read = CS}, Epoch, read) when CS#connection_state.epoch == Epoch -> CS; -connection_state_by_epoch(#connection_states{previous_write = CS}, Epoch, write) - when CS#connection_state.epoch == Epoch -> - CS; connection_state_by_epoch(#connection_states{current_write = CS}, Epoch, write) when CS#connection_state.epoch == Epoch -> CS; connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write) when CS#connection_state.epoch == Epoch -> CS. - %%-------------------------------------------------------------------- -spec set_connection_state_by_epoch(#connection_states{}, #connection_state{}, read | write) -> ok. @@ -97,12 +90,6 @@ connection_state_by_epoch(#connection_states{pending_write = CS}, Epoch, write) %% Description: Returns the instance of the connection_state record %% that is defined by the Epoch. %%-------------------------------------------------------------------- -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{previous_read = CS}, - NewCS = #connection_state{epoch = Epoch}, read) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{previous_read = NewCS}; - set_connection_state_by_epoch(ConnectionStates0 = #connection_states{current_read = CS}, NewCS = #connection_state{epoch = Epoch}, read) @@ -115,12 +102,6 @@ set_connection_state_by_epoch(ConnectionStates0 = when CS#connection_state.epoch == Epoch -> ConnectionStates0#connection_states{pending_read = NewCS}; -set_connection_state_by_epoch(ConnectionStates0 = - #connection_states{previous_write = CS}, - NewCS = #connection_state{epoch = Epoch}, write) - when CS#connection_state.epoch == Epoch -> - ConnectionStates0#connection_states{previous_write = NewCS}; - set_connection_state_by_epoch(ConnectionStates0 = #connection_states{current_write = CS}, NewCS = #connection_state{epoch = Epoch}, write) @@ -133,7 +114,6 @@ set_connection_state_by_epoch(ConnectionStates0 = when CS#connection_state.epoch == Epoch -> ConnectionStates0#connection_states{pending_write = NewCS}. - %%-------------------------------------------------------------------- -spec protocol_version(tls_atom_version() | tls_version()) -> tls_version() | tls_atom_version(). @@ -283,20 +263,21 @@ supported_connection_protocol_versions([]) -> is_acceptable_version(Version, Versions) -> lists:member(Version, Versions). -%%-------------------------------------------------------------------- --spec clear_previous_epoch(#connection_states{}) -> - #connection_states{}. -%% -%% Description: Advance to min_read_epoch to the current read epoch. -%%-------------------------------------------------------------------- -clear_previous_epoch(States = - #connection_states{current_read = Current}) -> - States#connection_states{min_read_epoch = Current#connection_state.epoch}. - +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={254, _}, - epoch = Epoch, sequence = SeqNo, + epoch = Epoch, record_seq = SeqNo, fragment=Fragment}, CS0) -> SP = CS0#connection_state.security_parameters, BCA = SP#security_parameters.bulk_cipher_algorithm, @@ -307,7 +288,7 @@ decipher(TLS=#ssl_tls{type=Type, version=Version={254, _}, CS1 = CS0#connection_state{cipher_state = CipherS1}, TLength = size(T), MacHash = hash_with_seqno(CS1, Type, Version, Epoch, SeqNo, TLength, T), - case is_correct_mac(Mac, MacHash) of + case ssl_record:is_correct_mac(Mac, MacHash) of true -> {TLS#ssl_tls{fragment = T}, CS1}; false -> @@ -338,3 +319,7 @@ hash_and_bump_seqno(#connection_state{epoch = Epoch, 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/dtls_record.hrl b/lib/ssl/src/dtls_record.hrl index c50550cc28..b72c14c2d7 100644 --- a/lib/ssl/src/dtls_record.hrl +++ b/lib/ssl/src/dtls_record.hrl @@ -30,16 +30,6 @@ -define(INITIAL_BYTES, 5). --record(connection_states, { - min_read_epoch, - previous_read, - current_read, - pending_read, - previous_write, - current_write, - pending_write - }). - %% Used to handle tls_plain_text, tls_compressed and tls_cipher_text -record(ssl_tls, { diff --git a/lib/ssl/src/dtls_v1.erl b/lib/ssl/src/dtls_v1.erl index 930998b460..a9fcf575af 100644 --- a/lib/ssl/src/dtls_v1.erl +++ b/lib/ssl/src/dtls_v1.erl @@ -25,7 +25,7 @@ -spec suites(Minor:: 253|255) -> [cipher_suite()]. suites(Minor) -> - tls_v1:suites(corresponding_minor_tls_version(Minor)); + tls_v1:suites(corresponding_minor_tls_version(Minor)). mac_hash(Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 0a2ae92e9d..677f5fdd07 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -20,14 +20,14 @@ inet_tls_dist, ssl_tls_dist_proxy, ssl_dist_sup, - ssl_tls1, - ssl_ssl3, - ssl_ssl2, + tls_v1, + ssl_v3, + ssl_v2, ssl_session, ssl_session_cache_api, ssl_session_cache, ssl_socket, - %%ssl_record, + ssl_record, ssl_manager, ssl_handshake, ssl_connection_sup, diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 09aad8e414..d958b74e9f 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -195,9 +195,9 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, %% Description: Returns a list of supported cipher suites. %%-------------------------------------------------------------------- suites({3, 0}) -> - ssl_ssl3:suites(); + ssl_v3:suites(); suites({3, N}) -> - ssl_tls1:suites(N). + tls_v1:suites(N). %%-------------------------------------------------------------------- -spec anonymous_suites() -> [cipher_suite()]. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 0f5d5def48..5084c46571 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -575,6 +575,7 @@ encode_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | R encode_hello_extensions(Rest, <>); encode_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) -> + EllipticCurveList = << <<(tls_v1:oid_to_enum(X)):16>> || X <- EllipticCurves>>, ListLen = byte_size(EllipticCurveList), Len = ListLen + 2, @@ -752,7 +753,7 @@ dec_server_key(<> = KeyStruct, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) -> - Params = #server_ecdh_params{curve = {namedCurve, ssl_tls1:enum_to_oid(CurveID)}, + Params = #server_ecdh_params{curve = {namedCurve, tls_v1:enum_to_oid(CurveID)}, public = ECPoint}, {BinMsg, HashSign, Signature} = dec_server_key_params(PointLen + 4, KeyStruct, Version), #server_key_params{params = Params, @@ -1445,10 +1446,11 @@ dec_hello_extensions(<> = ExtData, EllipticCurves = [tls_v1:enum_to_oid(X) || <> <= EllipticCurveList], - dec_hello_extensions(Rest, Acc#hello_extensions{elliptic_curves = - #elliptic_curves{elliptic_curve_list = - EllipticCurves}}); -dec_hello_extensions(<>, Acc) -> %%ECPointFormatListLen = Len - 1, <> = ExtData, diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl new file mode 100644 index 0000000000..ac56e3ab29 --- /dev/null +++ b/lib/ssl/src/ssl_record.erl @@ -0,0 +1,89 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(ssl_record). + +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). + +-export([empty_connection_state/1, activate_pending_connection_state/2, is_correct_mac/2]). + +empty_connection_state(ConnectionEnd) -> + SecParams = empty_security_params(ConnectionEnd), + #connection_state{security_parameters = SecParams}. + +empty_security_params(ConnectionEnd = ?CLIENT) -> + #security_parameters{connection_end = ConnectionEnd, + client_random = random()}; +empty_security_params(ConnectionEnd = ?SERVER) -> + #security_parameters{connection_end = ConnectionEnd, + server_random = random()}. +random() -> + Secs_since_1970 = calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) - 62167219200, + Random_28_bytes = crypto:rand_bytes(28), + <>. + +%%-------------------------------------------------------------------- +-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 has been activated. +%%-------------------------------------------------------------------- +activate_pending_connection_state(States = + #connection_states{current_read = Current, + pending_read = Pending}, + read) -> + %% Next epoch is a noop for SSL/TLS only uesed by DTLS + NewCurrent = Pending#connection_state{epoch = connection_state_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) -> + %% Next epoch is a noop for SSL/TLS only uesed by DTLS + NewCurrent = Pending#connection_state{epoch = connection_state_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 + }. + +connection_state_next_epoch(#connection_state{epoch = undefined}) -> + undefined; +connection_state_next_epoch(State) -> + State#connection_state.epoch + 1. + +is_correct_mac(Mac, Mac) -> + true; +is_correct_mac(_M,_H) -> + false. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index 8a6a211553..34893ce699 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -42,6 +42,13 @@ server_verify_data }). +-record(connection_states, { + current_read, + pending_read, + current_write, + pending_write + }). + -record(security_parameters, { cipher_suite, connection_end, diff --git a/lib/ssl/src/ssl_ssl2.erl b/lib/ssl/src/ssl_ssl2.erl deleted file mode 100644 index a9ab6a2678..0000000000 --- a/lib/ssl/src/ssl_ssl2.erl +++ /dev/null @@ -1,37 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2011. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%---------------------------------------------------------------------- -%% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher -%% will send an sslv2 hello. -%%---------------------------------------------------------------------- - --module(ssl_ssl2). - --export([client_random/2]). - -client_random(ChallengeData, 32) -> - ChallengeData; -client_random(ChallengeData, N) when N > 32 -> - <> = ChallengeData, - NewChallengeData; -client_random(ChallengeData, N) -> - Pad = list_to_binary(lists:duplicate(N, 0)), - <>. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl deleted file mode 100644 index 013c27ebb5..0000000000 --- a/lib/ssl/src/ssl_ssl3.erl +++ /dev/null @@ -1,203 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%---------------------------------------------------------------------- -%% Purpose: Handles sslv3 encryption. -%%---------------------------------------------------------------------- - --module(ssl_ssl3). - --include("ssl_cipher.hrl"). --include("ssl_internal.hrl"). --include("ssl_record.hrl"). % MD5 and SHA - --export([master_secret/3, finished/3, certificate_verify/3, - mac_hash/6, setup_keys/7, - suites/0]). --compile(inline). - -%%==================================================================== -%% Internal application API -%%==================================================================== - --spec master_secret(binary(), binary(), binary()) -> binary(). - -master_secret(PremasterSecret, ClientRandom, ServerRandom) -> - %% draft-ietf-tls-ssl-version3-00 - 6.2.2 - %% key_block = - %% MD5(master_secret + SHA(`A' + master_secret + - %% ServerHello.random + - %% ClientHello.random)) + - %% MD5(master_secret + SHA(`BB' + master_secret + - %% ServerHello.random + - %% ClientHello.random)) + - %% MD5(master_secret + SHA(`CCC' + master_secret + - %% ServerHello.random + - %% ClientHello.random)) + [...]; - Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), - Block. - --spec finished(client | server, binary(), [binary()]) -> binary(). - -finished(Role, MasterSecret, Handshake) -> - %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished - %% struct { - %% opaque md5_hash[16]; - %% opaque sha_hash[20]; - %% } Finished; - %% - %% md5_hash MD5(master_secret + pad2 + - %% MD5(handshake_messages + Sender + - %% master_secret + pad1)); - %% sha_hash SHA(master_secret + pad2 + - %% SHA(handshake_messages + Sender + - %% master_secret + pad1)); - Sender = get_sender(Role), - MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake), - SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake), - <>. - --spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary(). - -certificate_verify(md5sha, MasterSecret, Handshake) -> - %% md5_hash - %% MD5(master_secret + pad_2 + - %% MD5(handshake_messages + master_secret + pad_1)); - %% sha_hash - %% SHA(master_secret + pad_2 + - %% SHA(handshake_messages + master_secret + pad_1)); - - MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake), - SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake), - <>; - -certificate_verify(sha, MasterSecret, Handshake) -> - %% sha_hash - %% SHA(master_secret + pad_2 + - %% SHA(handshake_messages + master_secret + pad_1)); - - handshake_hash(?SHA, MasterSecret, undefined, Handshake). - --spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). - -mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> - %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 - %% hash(MAC_write_secret + pad_2 + - %% hash(MAC_write_secret + pad_1 + seq_num + - %% SSLCompressed.type + SSLCompressed.length + - %% SSLCompressed.fragment)); - Mac = mac_hash(Method, Mac_write_secret, - [<>, Fragment]), - Mac. - --spec setup_keys(binary(), binary(), binary(), - integer(), integer(), term(), integer()) -> - {binary(), binary(), binary(), - binary(), binary(), binary()}. - -setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> - KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, - 2*(HS+KML+IVS)), - %% draft-ietf-tls-ssl-version3-00 - 6.2.2 - %% The key_block is partitioned as follows. - %% client_write_MAC_secret[CipherSpec.hash_size] - %% server_write_MAC_secret[CipherSpec.hash_size] - %% client_write_key[CipherSpec.key_material] - %% server_write_key[CipherSpec.key_material] - %% client_write_IV[CipherSpec.IV_size] /* non-export ciphers */ - %% server_write_IV[CipherSpec.IV_size] /* non-export ciphers */ - <> = KeyBlock, - {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}. - --spec suites() -> [cipher_suite()]. - -suites() -> - [ - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - ?TLS_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_RSA_WITH_IDEA_CBC_SHA, - ?TLS_RSA_WITH_RC4_128_SHA, - ?TLS_RSA_WITH_RC4_128_MD5, - ?TLS_RSA_WITH_DES_CBC_SHA - ]. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- - -hash(?MD5, Data) -> - crypto:hash(md5, Data); -hash(?SHA, Data) -> - crypto:hash(sha, Data). - -%%pad_1(?NULL) -> -%% ""; -pad_1(?MD5) -> - <<"666666666666666666666666666666666666666666666666">>; -pad_1(?SHA) -> - <<"6666666666666666666666666666666666666666">>. -%%pad_2(?NULL) -> -%% ""; -pad_2(?MD5) -> - <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" - "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>; -pad_2(?SHA) -> - <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" - "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>. - -mac_hash(?NULL, _Secret, _Data) -> - <<>>; -mac_hash(Method, Secret, Data) -> - InnerHash = hash(Method, [Secret, pad_1(Method), Data]), - hash(Method, [Secret, pad_2(Method), InnerHash]). - -handshake_hash(Method, MasterSecret, undefined, Handshake) -> - InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]), - hash(Method, [MasterSecret, pad_2(Method), InnerHash]); -handshake_hash(Method, MasterSecret, Sender, Handshake) -> - InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]), - hash(Method, [MasterSecret, pad_2(Method), InnerHash]). - -get_sender(client) -> "CLNT"; -get_sender(server) -> "SRVR". - -generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) -> - gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom], - WantedLength, 0, $A, 1, []). - -gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len -> - <> = list_to_binary(lists:reverse(Acc)), - Block; -gen(Secret, All, Wanted, Len, C, N, Acc) -> - Prefix = lists:duplicate(N, C), - SHA = crypto:hash(sha, [Prefix, All]), - MD5 = crypto:hash(md5, [Secret, SHA]), - gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]). diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl deleted file mode 100644 index 8ab66d0627..0000000000 --- a/lib/ssl/src/ssl_tls1.erl +++ /dev/null @@ -1,432 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%---------------------------------------------------------------------- -%% Purpose: Handles tls1 encryption. -%%---------------------------------------------------------------------- - --module(ssl_tls1). - --include("ssl_cipher.hrl"). --include("ssl_internal.hrl"). --include("ssl_record.hrl"). - --export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, - setup_keys/8, suites/1, prf/5, - ecc_curves/1, oid_to_enum/1, enum_to_oid/1]). - -%%==================================================================== -%% Internal application API -%%==================================================================== - --spec master_secret(integer(), binary(), binary(), binary()) -> binary(). - -master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> - %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret, - %% "master secret", ClientHello.random + - %% ServerHello.random)[0..47]; - - prf(PrfAlgo, PreMasterSecret, <<"master secret">>, - [ClientRandom, ServerRandom], 48). - --spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary(). - -finished(Role, Version, PrfAlgo, MasterSecret, Handshake) - when Version == 1; Version == 2; PrfAlgo == ?MD5SHA -> - %% RFC 2246 & 4346 - 7.4.9. Finished - %% struct { - %% opaque verify_data[12]; - %% } Finished; - %% - %% verify_data - %% PRF(master_secret, finished_label, MD5(handshake_messages) + - %% SHA-1(handshake_messages)) [0..11]; - MD5 = crypto:hash(md5, Handshake), - SHA = crypto:hash(sha, Handshake), - prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); - -finished(Role, Version, PrfAlgo, MasterSecret, Handshake) - when Version == 3 -> - %% RFC 5246 - 7.4.9. Finished - %% struct { - %% opaque verify_data[12]; - %% } Finished; - %% - %% verify_data - %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11]; - Hash = crypto:hash(mac_algo(PrfAlgo), Handshake), - prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12). - --spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). - -certificate_verify(md5sha, _Version, Handshake) -> - MD5 = crypto:hash(md5, Handshake), - SHA = crypto:hash(sha, Handshake), - <>; - -certificate_verify(HashAlgo, _Version, Handshake) -> - crypto:hash(HashAlgo, Handshake). - --spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(), - integer(), integer()) -> {binary(), binary(), binary(), - binary(), binary(), binary()}. - -setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, - KeyMatLen, IVSize) - when Version == 1 -> - %% RFC 2246 - 6.3. Key calculation - %% key_block = PRF(SecurityParameters.master_secret, - %% "key expansion", - %% SecurityParameters.server_random + - %% SecurityParameters.client_random); - %% Then the key_block is partitioned as follows: - %% client_write_MAC_secret[SecurityParameters.hash_size] - %% server_write_MAC_secret[SecurityParameters.hash_size] - %% client_write_key[SecurityParameters.key_material_length] - %% server_write_key[SecurityParameters.key_material_length] - %% client_write_IV[SecurityParameters.IV_size] - %% server_write_IV[SecurityParameters.IV_size] - WantedLength = 2 * (HashSize + KeyMatLen + IVSize), - KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", - [ServerRandom, ClientRandom], WantedLength), - <> = KeyBlock, - {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}; - -%% TLS v1.1 -setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, - KeyMatLen, IVSize) - when Version == 2 -> - %% RFC 4346 - 6.3. Key calculation - %% key_block = PRF(SecurityParameters.master_secret, - %% "key expansion", - %% SecurityParameters.server_random + - %% SecurityParameters.client_random); - %% Then the key_block is partitioned as follows: - %% client_write_MAC_secret[SecurityParameters.hash_size] - %% server_write_MAC_secret[SecurityParameters.hash_size] - %% client_write_key[SecurityParameters.key_material_length] - %% server_write_key[SecurityParameters.key_material_length] - %% - %% RFC 4346 is incomplete, the client and server IVs have to - %% be generated just like for TLS 1.0 - WantedLength = 2 * (HashSize + KeyMatLen + IVSize), - KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", - [ServerRandom, ClientRandom], WantedLength), - <> = KeyBlock, - {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}; - -%% TLS v1.2 -setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, - KeyMatLen, IVSize) - when Version == 3 -> - %% RFC 5246 - 6.3. Key calculation - %% key_block = PRF(SecurityParameters.master_secret, - %% "key expansion", - %% SecurityParameters.server_random + - %% SecurityParameters.client_random); - %% Then the key_block is partitioned as follows: - %% client_write_MAC_secret[SecurityParameters.hash_size] - %% server_write_MAC_secret[SecurityParameters.hash_size] - %% client_write_key[SecurityParameters.key_material_length] - %% server_write_key[SecurityParameters.key_material_length] - %% client_write_IV[SecurityParameters.fixed_iv_length] - %% server_write_IV[SecurityParameters.fixed_iv_length] - WantedLength = 2 * (HashSize + KeyMatLen + IVSize), - KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion", - [ServerRandom, ClientRandom], WantedLength), - <> = KeyBlock, - {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}. - --spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), - integer(), binary()) -> binary(). - -mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, - Length, Fragment) -> - %% RFC 2246 & 4346 - 6.2.3.1. - %% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + - %% TLSCompressed.version + TLSCompressed.length + - %% TLSCompressed.fragment)); - Mac = hmac_hash(Method, Mac_write_secret, - [<>, - Fragment]), - Mac. - --spec suites(1|2|3) -> [cipher_suite()]. - -suites(Minor) when Minor == 1; Minor == 2-> - case sufficent_ec_support() of - true -> - all_suites(Minor); - false -> - no_ec_suites(Minor) - end; - -suites(Minor) when Minor == 3 -> - case sufficent_ec_support() of - true -> - all_suites(3) ++ all_suites(2); - false -> - no_ec_suites(3) ++ no_ec_suites(2) - end. - -all_suites(Minor) when Minor == 1; Minor == 2-> - [ - ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, - ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, - ?TLS_RSA_WITH_AES_256_CBC_SHA, - - ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, - - ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, - ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, - ?TLS_RSA_WITH_AES_128_CBC_SHA, - - ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, - ?TLS_RSA_WITH_RC4_128_SHA, - ?TLS_RSA_WITH_RC4_128_MD5, - ?TLS_DHE_RSA_WITH_DES_CBC_SHA, - ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, - ?TLS_ECDH_RSA_WITH_RC4_128_SHA, - - ?TLS_RSA_WITH_DES_CBC_SHA - ]; -all_suites(3) -> - [ - ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, - ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, - ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, - ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, - - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, - ?TLS_RSA_WITH_AES_256_CBC_SHA256, - - ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, - ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, - - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, - ?TLS_RSA_WITH_AES_128_CBC_SHA256 - ]. - -no_ec_suites(Minor) when Minor == 1; Minor == 2-> - [ - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - ?TLS_RSA_WITH_AES_256_CBC_SHA, - ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, - ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, - ?TLS_RSA_WITH_AES_128_CBC_SHA, - ?TLS_RSA_WITH_RC4_128_SHA, - ?TLS_RSA_WITH_RC4_128_MD5, - ?TLS_DHE_RSA_WITH_DES_CBC_SHA, - ?TLS_RSA_WITH_DES_CBC_SHA - ]; -no_ec_suites(3) -> - [ - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, - ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, - ?TLS_RSA_WITH_AES_256_CBC_SHA256, - ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, - ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, - ?TLS_RSA_WITH_AES_128_CBC_SHA256 - ]. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- -%%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%% -hmac_hash(?NULL, _, _) -> - <<>>; -hmac_hash(Alg, Key, Value) -> - crypto:hmac(mac_algo(Alg), Key, Value). - -mac_algo(?MD5) -> md5; -mac_algo(?SHA) -> sha; -mac_algo(?SHA256) -> sha256; -mac_algo(?SHA384) -> sha384; -mac_algo(?SHA512) -> sha512. - -% First, we define a data expansion function, P_hash(secret, data) that -% uses a single hash function to expand a secret and seed into an -% arbitrary quantity of output: -%% P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + -%% HMAC_hash(secret, A(2) + seed) + -%% HMAC_hash(secret, A(3) + seed) + ... - -p_hash(Secret, Seed, WantedLength, Method) -> - p_hash(Secret, Seed, WantedLength, Method, 0, []). - -p_hash(_Secret, _Seed, WantedLength, _Method, _N, []) - when WantedLength =< 0 -> - []; -p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc]) - when WantedLength =< 0 -> - Keep = byte_size(Last) + WantedLength, - <> = Last, - list_to_binary(lists:reverse(Acc, [B])); -p_hash(Secret, Seed, WantedLength, Method, N, Acc) -> - N1 = N+1, - Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]), - p_hash(Secret, Seed, WantedLength - byte_size(Bin), Method, N1, [Bin|Acc]). - - -%% ... Where A(0) = seed -%% A(i) = HMAC_hash(secret, A(i-1)) -%% a(0, _Secret, Seed, _Method) -> -%% Seed. -%% a(N, Secret, Seed, Method) -> -%% hmac_hash(Method, Secret, a(N-1, Secret, Seed, Method)). -a(0, _Secret, Seed, _Method) -> - Seed; -a(N, Secret, Seed0, Method) -> - Seed = hmac_hash(Method, Secret, Seed0), - a(N-1, Secret, Seed, Method). - -split_secret(BinSecret) -> - %% L_S = length in bytes of secret; - %% L_S1 = L_S2 = ceil(L_S / 2); - %% The secret is partitioned into two halves (with the possibility of - %% one shared byte) as described above, S1 taking the first L_S1 bytes, - %% and S2 the last L_S2 bytes. - Length = byte_size(BinSecret), - Div = Length div 2, - EvenLength = Length - Div, - <> = BinSecret, - <<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret, - {Secret1, Secret2}. - -prf(?MD5SHA, Secret, Label, Seed, WantedLength) -> - %% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR - %% P_SHA-1(S2, label + seed); - {S1, S2} = split_secret(Secret), - LS = list_to_binary([Label, Seed]), - crypto:exor(p_hash(S1, LS, WantedLength, ?MD5), - p_hash(S2, LS, WantedLength, ?SHA)); - -prf(MAC, Secret, Label, Seed, WantedLength) -> - %% PRF(secret, label, seed) = P_SHA256(secret, label + seed); - LS = list_to_binary([Label, Seed]), - p_hash(Secret, LS, WantedLength, MAC). - -%%%% Misc help functions %%%% - -finished_label(client) -> - <<"client finished">>; -finished_label(server) -> - <<"server finished">>. - -%% list ECC curves in prefered order -ecc_curves(_Minor) -> - [?sect571r1,?sect571k1,?secp521r1,?sect409k1,?sect409r1, - ?secp384r1,?sect283k1,?sect283r1,?secp256k1,?secp256r1, - ?sect239k1,?sect233k1,?sect233r1,?secp224k1,?secp224r1, - ?sect193r1,?sect193r2,?secp192k1,?secp192r1,?sect163k1, - ?sect163r1,?sect163r2,?secp160k1,?secp160r1,?secp160r2]. - -%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) -oid_to_enum(?sect163k1) -> 1; -oid_to_enum(?sect163r1) -> 2; -oid_to_enum(?sect163r2) -> 3; -oid_to_enum(?sect193r1) -> 4; -oid_to_enum(?sect193r2) -> 5; -oid_to_enum(?sect233k1) -> 6; -oid_to_enum(?sect233r1) -> 7; -oid_to_enum(?sect239k1) -> 8; -oid_to_enum(?sect283k1) -> 9; -oid_to_enum(?sect283r1) -> 10; -oid_to_enum(?sect409k1) -> 11; -oid_to_enum(?sect409r1) -> 12; -oid_to_enum(?sect571k1) -> 13; -oid_to_enum(?sect571r1) -> 14; -oid_to_enum(?secp160k1) -> 15; -oid_to_enum(?secp160r1) -> 16; -oid_to_enum(?secp160r2) -> 17; -oid_to_enum(?secp192k1) -> 18; -oid_to_enum(?secp192r1) -> 19; -oid_to_enum(?secp224k1) -> 20; -oid_to_enum(?secp224r1) -> 21; -oid_to_enum(?secp256k1) -> 22; -oid_to_enum(?secp256r1) -> 23; -oid_to_enum(?secp384r1) -> 24; -oid_to_enum(?secp521r1) -> 25. - -enum_to_oid(1) -> ?sect163k1; -enum_to_oid(2) -> ?sect163r1; -enum_to_oid(3) -> ?sect163r2; -enum_to_oid(4) -> ?sect193r1; -enum_to_oid(5) -> ?sect193r2; -enum_to_oid(6) -> ?sect233k1; -enum_to_oid(7) -> ?sect233r1; -enum_to_oid(8) -> ?sect239k1; -enum_to_oid(9) -> ?sect283k1; -enum_to_oid(10) -> ?sect283r1; -enum_to_oid(11) -> ?sect409k1; -enum_to_oid(12) -> ?sect409r1; -enum_to_oid(13) -> ?sect571k1; -enum_to_oid(14) -> ?sect571r1; -enum_to_oid(15) -> ?secp160k1; -enum_to_oid(16) -> ?secp160r1; -enum_to_oid(17) -> ?secp160r2; -enum_to_oid(18) -> ?secp192k1; -enum_to_oid(19) -> ?secp192r1; -enum_to_oid(20) -> ?secp224k1; -enum_to_oid(21) -> ?secp224r1; -enum_to_oid(22) -> ?secp256k1; -enum_to_oid(23) -> ?secp256r1; -enum_to_oid(24) -> ?secp384r1; -enum_to_oid(25) -> ?secp521r1. - -sufficent_ec_support() -> - CryptoSupport = crypto:supports(), - proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)). diff --git a/lib/ssl/src/ssl_v2.erl b/lib/ssl/src/ssl_v2.erl new file mode 100644 index 0000000000..07876366f1 --- /dev/null +++ b/lib/ssl/src/ssl_v2.erl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher +%% will send an sslv2 hello. +%%---------------------------------------------------------------------- + +-module(ssl_v2). + +-export([client_random/2]). + +client_random(ChallengeData, 32) -> + ChallengeData; +client_random(ChallengeData, N) when N > 32 -> + <> = ChallengeData, + NewChallengeData; +client_random(ChallengeData, N) -> + Pad = list_to_binary(lists:duplicate(N, 0)), + <>. diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl new file mode 100644 index 0000000000..d477b3df81 --- /dev/null +++ b/lib/ssl/src/ssl_v3.erl @@ -0,0 +1,203 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Handles sslv3 encryption. +%%---------------------------------------------------------------------- + +-module(ssl_v3). + +-include("ssl_cipher.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). % MD5 and SHA + +-export([master_secret/3, finished/3, certificate_verify/3, + mac_hash/6, setup_keys/7, + suites/0]). +-compile(inline). + +%%==================================================================== +%% Internal application API +%%==================================================================== + +-spec master_secret(binary(), binary(), binary()) -> binary(). + +master_secret(PremasterSecret, ClientRandom, ServerRandom) -> + %% draft-ietf-tls-ssl-version3-00 - 6.2.2 + %% key_block = + %% MD5(master_secret + SHA(`A' + master_secret + + %% ServerHello.random + + %% ClientHello.random)) + + %% MD5(master_secret + SHA(`BB' + master_secret + + %% ServerHello.random + + %% ClientHello.random)) + + %% MD5(master_secret + SHA(`CCC' + master_secret + + %% ServerHello.random + + %% ClientHello.random)) + [...]; + Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), + Block. + +-spec finished(client | server, binary(), [binary()]) -> binary(). + +finished(Role, MasterSecret, Handshake) -> + %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished + %% struct { + %% opaque md5_hash[16]; + %% opaque sha_hash[20]; + %% } Finished; + %% + %% md5_hash MD5(master_secret + pad2 + + %% MD5(handshake_messages + Sender + + %% master_secret + pad1)); + %% sha_hash SHA(master_secret + pad2 + + %% SHA(handshake_messages + Sender + + %% master_secret + pad1)); + Sender = get_sender(Role), + MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake), + <>. + +-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary(). + +certificate_verify(md5sha, MasterSecret, Handshake) -> + %% md5_hash + %% MD5(master_secret + pad_2 + + %% MD5(handshake_messages + master_secret + pad_1)); + %% sha_hash + %% SHA(master_secret + pad_2 + + %% SHA(handshake_messages + master_secret + pad_1)); + + MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake), + <>; + +certificate_verify(sha, MasterSecret, Handshake) -> + %% sha_hash + %% SHA(master_secret + pad_2 + + %% SHA(handshake_messages + master_secret + pad_1)); + + handshake_hash(?SHA, MasterSecret, undefined, Handshake). + +-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). + +mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> + %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 + %% hash(MAC_write_secret + pad_2 + + %% hash(MAC_write_secret + pad_1 + seq_num + + %% SSLCompressed.type + SSLCompressed.length + + %% SSLCompressed.fragment)); + Mac = mac_hash(Method, Mac_write_secret, + [<>, Fragment]), + Mac. + +-spec setup_keys(binary(), binary(), binary(), + integer(), integer(), term(), integer()) -> + {binary(), binary(), binary(), + binary(), binary(), binary()}. + +setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> + KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, + 2*(HS+KML+IVS)), + %% draft-ietf-tls-ssl-version3-00 - 6.2.2 + %% The key_block is partitioned as follows. + %% client_write_MAC_secret[CipherSpec.hash_size] + %% server_write_MAC_secret[CipherSpec.hash_size] + %% client_write_key[CipherSpec.key_material] + %% server_write_key[CipherSpec.key_material] + %% client_write_IV[CipherSpec.IV_size] /* non-export ciphers */ + %% server_write_IV[CipherSpec.IV_size] /* non-export ciphers */ + <> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}. + +-spec suites() -> [cipher_suite()]. + +suites() -> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA, + %%?TLS_RSA_WITH_IDEA_CBC_SHA, + ?TLS_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_MD5, + ?TLS_RSA_WITH_DES_CBC_SHA + ]. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +hash(?MD5, Data) -> + crypto:hash(md5, Data); +hash(?SHA, Data) -> + crypto:hash(sha, Data). + +%%pad_1(?NULL) -> +%% ""; +pad_1(?MD5) -> + <<"666666666666666666666666666666666666666666666666">>; +pad_1(?SHA) -> + <<"6666666666666666666666666666666666666666">>. +%%pad_2(?NULL) -> +%% ""; +pad_2(?MD5) -> + <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" + "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>; +pad_2(?SHA) -> + <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\" + "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>. + +mac_hash(?NULL, _Secret, _Data) -> + <<>>; +mac_hash(Method, Secret, Data) -> + InnerHash = hash(Method, [Secret, pad_1(Method), Data]), + hash(Method, [Secret, pad_2(Method), InnerHash]). + +handshake_hash(Method, MasterSecret, undefined, Handshake) -> + InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]), + hash(Method, [MasterSecret, pad_2(Method), InnerHash]); +handshake_hash(Method, MasterSecret, Sender, Handshake) -> + InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]), + hash(Method, [MasterSecret, pad_2(Method), InnerHash]). + +get_sender(client) -> "CLNT"; +get_sender(server) -> "SRVR". + +generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) -> + gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom], + WantedLength, 0, $A, 1, []). + +gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len -> + <> = list_to_binary(lists:reverse(Acc)), + Block; +gen(Secret, All, Wanted, Len, C, N, Acc) -> + Prefix = lists:duplicate(N, C), + SHA = crypto:hash(sha, [Prefix, All]), + MD5 = crypto:hash(md5, [Secret, SHA]), + gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]). diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 066590afb1..fef1464c64 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -33,7 +33,7 @@ -include_lib("public_key/include/public_key.hrl"). -export([client_hello/8, server_hello/4, hello/4, - get_tls_handshake/3, encode_handshake/2, + get_tls_handshake/3, encode_handshake/2, decode_handshake/3, init_handshake_history/0, update_handshake_history/2]). %%==================================================================== @@ -197,17 +197,22 @@ update_handshake_history(Handshake, % special-case SSL2 client hello update_handshake_history({Handshake0, _Prev}, Data) -> {[Data|Handshake0], Handshake0}. -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- + get_tls_handshake_aux(Version, <>, Acc) -> Raw = <>, - H = decode_handshake(Version, Type, Body), - get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]); + Handshake = decode_handshake(Version, Type, Body), + get_tls_handshake_aux(Version, Rest, [{Handshake,Raw} | Acc]); get_tls_handshake_aux(_Version, Data, Acc) -> {lists:reverse(Acc), Data}. +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +decode_handshake(_, ?HELLO_REQUEST, <<>>) -> + #hello_request{}; + %% Client hello v2. %% 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. @@ -217,7 +222,7 @@ decode_handshake(_Version, ?CLIENT_HELLO, <>) -> #client_hello{client_version = {Major, Minor}, - random = ssl_ssl2:client_random(ChallengeData, CDLength), + random = ssl_v2:client_random(ChallengeData, CDLength), session_id = 0, cipher_suites = ssl_handshake:decode_suites('3_bytes', CipherSuites), compression_methods = [?NULL], diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 1409a04763..9ab512f334 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -77,7 +77,7 @@ init_connection_states(Role) -> ConnectionEnd = record_protocol_role(Role), Current = initial_connection_state(ConnectionEnd), - Pending = empty_connection_state(ConnectionEnd), + Pending = ssl_record:empty_connection_state(ConnectionEnd), #connection_states{current_read = Current, pending_read = Pending, current_write = Current, @@ -265,7 +265,7 @@ activate_pending_connection_state(States = NewCurrent = Pending#connection_state{sequence_number = 0}, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - EmptyPending = empty_connection_state(ConnectionEnd), + EmptyPending = ssl_record:empty_connection_state(ConnectionEnd), SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_read = NewCurrent, @@ -278,7 +278,7 @@ activate_pending_connection_state(States = NewCurrent = Pending#connection_state{sequence_number = 0}, SecParams = Pending#connection_state.security_parameters, ConnectionEnd = SecParams#security_parameters.connection_end, - EmptyPending = empty_connection_state(ConnectionEnd), + EmptyPending = ssl_record:empty_connection_state(ConnectionEnd), SecureRenegotation = NewCurrent#connection_state.secure_renegotiation, NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation}, States#connection_states{current_write = NewCurrent, @@ -591,21 +591,6 @@ initial_security_params(ConnectionEnd) -> ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL, SecParams). -empty_connection_state(ConnectionEnd) -> - SecParams = empty_security_params(ConnectionEnd), - #connection_state{security_parameters = SecParams}. - -empty_security_params(ConnectionEnd = ?CLIENT) -> - #security_parameters{connection_end = ConnectionEnd, - client_random = random()}; -empty_security_params(ConnectionEnd = ?SERVER) -> - #security_parameters{connection_end = ConnectionEnd, - server_random = random()}. -random() -> - Secs_since_1970 = calendar:datetime_to_gregorian_seconds( - calendar:universal_time()) - 62167219200, - Random_28_bytes = crypto:rand_bytes(28), - <>. record_protocol_role(client) -> ?CLIENT; @@ -667,7 +652,7 @@ decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) -> 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 + case ssl_record:is_correct_mac(Mac, MacHash) of true -> {TLS#ssl_tls{fragment = T}, CS2}; false -> @@ -696,19 +681,15 @@ hash_and_bump_seqno(#connection_state{sequence_number = SeqNo, 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); + ssl_v3: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, + tls_v1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, Length, Fragment). sufficient_tlsv1_2_crypto_support() -> diff --git a/lib/ssl/src/tls_record.hrl b/lib/ssl/src/tls_record.hrl index 0e3552bafa..0be1e35458 100644 --- a/lib/ssl/src/tls_record.hrl +++ b/lib/ssl/src/tls_record.hrl @@ -27,13 +27,6 @@ -define(tls_record, true). -include("ssl_record.hrl"). %% Common TLS and DTLS records and Constantes --record(connection_states, { - current_read, - pending_read, - current_write, - pending_write - }). - %% Used to handle tls_plain_text, tls_compressed and tls_cipher_text -record(ssl_tls, { diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl new file mode 100644 index 0000000000..2395e98642 --- /dev/null +++ b/lib/ssl/src/tls_v1.erl @@ -0,0 +1,432 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Handles tls1 encryption. +%%---------------------------------------------------------------------- + +-module(tls_v1). + +-include("ssl_cipher.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). + +-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, + setup_keys/8, suites/1, prf/5, + ecc_curves/1, oid_to_enum/1, enum_to_oid/1]). + +%%==================================================================== +%% Internal application API +%%==================================================================== + +-spec master_secret(integer(), binary(), binary(), binary()) -> binary(). + +master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> + %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret, + %% "master secret", ClientHello.random + + %% ServerHello.random)[0..47]; + + prf(PrfAlgo, PreMasterSecret, <<"master secret">>, + [ClientRandom, ServerRandom], 48). + +-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary(). + +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 1; Version == 2; PrfAlgo == ?MD5SHA -> + %% RFC 2246 & 4346 - 7.4.9. Finished + %% struct { + %% opaque verify_data[12]; + %% } Finished; + %% + %% verify_data + %% PRF(master_secret, finished_label, MD5(handshake_messages) + + %% SHA-1(handshake_messages)) [0..11]; + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), + prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); + +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 3 -> + %% RFC 5246 - 7.4.9. Finished + %% struct { + %% opaque verify_data[12]; + %% } Finished; + %% + %% verify_data + %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11]; + Hash = crypto:hash(mac_algo(PrfAlgo), Handshake), + prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12). + +-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). + +certificate_verify(md5sha, _Version, Handshake) -> + MD5 = crypto:hash(md5, Handshake), + SHA = crypto:hash(sha, Handshake), + <>; + +certificate_verify(HashAlgo, _Version, Handshake) -> + crypto:hash(HashAlgo, Handshake). + +-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 1 -> + %% RFC 2246 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% client_write_IV[SecurityParameters.IV_size] + %% server_write_IV[SecurityParameters.IV_size] + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}; + +%% TLS v1.1 +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 2 -> + %% RFC 4346 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% + %% RFC 4346 is incomplete, the client and server IVs have to + %% be generated just like for TLS 1.0 + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}; + +%% TLS v1.2 +setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 3 -> + %% RFC 5246 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% client_write_IV[SecurityParameters.fixed_iv_length] + %% server_write_IV[SecurityParameters.fixed_iv_length] + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}. + +-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), + integer(), binary()) -> binary(). + +mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, + Length, Fragment) -> + %% RFC 2246 & 4346 - 6.2.3.1. + %% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + + %% TLSCompressed.version + TLSCompressed.length + + %% TLSCompressed.fragment)); + Mac = hmac_hash(Method, Mac_write_secret, + [<>, + Fragment]), + Mac. + +-spec suites(1|2|3) -> [cipher_suite()]. + +suites(Minor) when Minor == 1; Minor == 2-> + case sufficent_ec_support() of + true -> + all_suites(Minor); + false -> + no_ec_suites(Minor) + end; + +suites(Minor) when Minor == 3 -> + case sufficent_ec_support() of + true -> + all_suites(3) ++ all_suites(2); + false -> + no_ec_suites(3) ++ no_ec_suites(2) + end. + +all_suites(Minor) when Minor == 1; Minor == 2-> + [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + ?TLS_RSA_WITH_AES_256_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA, + + ?TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDHE_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_MD5, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, + ?TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + ?TLS_ECDH_RSA_WITH_RC4_128_SHA, + + ?TLS_RSA_WITH_DES_CBC_SHA + ]; +all_suites(3) -> + [ + ?TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + ?TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA256, + + ?TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + ?TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_WITH_AES_128_CBC_SHA256 + ]. + +no_ec_suites(Minor) when Minor == 1; Minor == 2-> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + ?TLS_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA, + ?TLS_RSA_WITH_RC4_128_SHA, + ?TLS_RSA_WITH_RC4_128_MD5, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, + ?TLS_RSA_WITH_DES_CBC_SHA + ]; +no_ec_suites(3) -> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_WITH_AES_128_CBC_SHA256 + ]. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +%%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%% +hmac_hash(?NULL, _, _) -> + <<>>; +hmac_hash(Alg, Key, Value) -> + crypto:hmac(mac_algo(Alg), Key, Value). + +mac_algo(?MD5) -> md5; +mac_algo(?SHA) -> sha; +mac_algo(?SHA256) -> sha256; +mac_algo(?SHA384) -> sha384; +mac_algo(?SHA512) -> sha512. + +% First, we define a data expansion function, P_hash(secret, data) that +% uses a single hash function to expand a secret and seed into an +% arbitrary quantity of output: +%% P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + +%% HMAC_hash(secret, A(2) + seed) + +%% HMAC_hash(secret, A(3) + seed) + ... + +p_hash(Secret, Seed, WantedLength, Method) -> + p_hash(Secret, Seed, WantedLength, Method, 0, []). + +p_hash(_Secret, _Seed, WantedLength, _Method, _N, []) + when WantedLength =< 0 -> + []; +p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc]) + when WantedLength =< 0 -> + Keep = byte_size(Last) + WantedLength, + <> = Last, + list_to_binary(lists:reverse(Acc, [B])); +p_hash(Secret, Seed, WantedLength, Method, N, Acc) -> + N1 = N+1, + Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]), + p_hash(Secret, Seed, WantedLength - byte_size(Bin), Method, N1, [Bin|Acc]). + + +%% ... Where A(0) = seed +%% A(i) = HMAC_hash(secret, A(i-1)) +%% a(0, _Secret, Seed, _Method) -> +%% Seed. +%% a(N, Secret, Seed, Method) -> +%% hmac_hash(Method, Secret, a(N-1, Secret, Seed, Method)). +a(0, _Secret, Seed, _Method) -> + Seed; +a(N, Secret, Seed0, Method) -> + Seed = hmac_hash(Method, Secret, Seed0), + a(N-1, Secret, Seed, Method). + +split_secret(BinSecret) -> + %% L_S = length in bytes of secret; + %% L_S1 = L_S2 = ceil(L_S / 2); + %% The secret is partitioned into two halves (with the possibility of + %% one shared byte) as described above, S1 taking the first L_S1 bytes, + %% and S2 the last L_S2 bytes. + Length = byte_size(BinSecret), + Div = Length div 2, + EvenLength = Length - Div, + <> = BinSecret, + <<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret, + {Secret1, Secret2}. + +prf(?MD5SHA, Secret, Label, Seed, WantedLength) -> + %% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR + %% P_SHA-1(S2, label + seed); + {S1, S2} = split_secret(Secret), + LS = list_to_binary([Label, Seed]), + crypto:exor(p_hash(S1, LS, WantedLength, ?MD5), + p_hash(S2, LS, WantedLength, ?SHA)); + +prf(MAC, Secret, Label, Seed, WantedLength) -> + %% PRF(secret, label, seed) = P_SHA256(secret, label + seed); + LS = list_to_binary([Label, Seed]), + p_hash(Secret, LS, WantedLength, MAC). + +%%%% Misc help functions %%%% + +finished_label(client) -> + <<"client finished">>; +finished_label(server) -> + <<"server finished">>. + +%% list ECC curves in prefered order +ecc_curves(_Minor) -> + [?sect571r1,?sect571k1,?secp521r1,?sect409k1,?sect409r1, + ?secp384r1,?sect283k1,?sect283r1,?secp256k1,?secp256r1, + ?sect239k1,?sect233k1,?sect233r1,?secp224k1,?secp224r1, + ?sect193r1,?sect193r2,?secp192k1,?secp192r1,?sect163k1, + ?sect163r1,?sect163r2,?secp160k1,?secp160r1,?secp160r2]. + +%% ECC curves from draft-ietf-tls-ecc-12.txt (Oct. 17, 2005) +oid_to_enum(?sect163k1) -> 1; +oid_to_enum(?sect163r1) -> 2; +oid_to_enum(?sect163r2) -> 3; +oid_to_enum(?sect193r1) -> 4; +oid_to_enum(?sect193r2) -> 5; +oid_to_enum(?sect233k1) -> 6; +oid_to_enum(?sect233r1) -> 7; +oid_to_enum(?sect239k1) -> 8; +oid_to_enum(?sect283k1) -> 9; +oid_to_enum(?sect283r1) -> 10; +oid_to_enum(?sect409k1) -> 11; +oid_to_enum(?sect409r1) -> 12; +oid_to_enum(?sect571k1) -> 13; +oid_to_enum(?sect571r1) -> 14; +oid_to_enum(?secp160k1) -> 15; +oid_to_enum(?secp160r1) -> 16; +oid_to_enum(?secp160r2) -> 17; +oid_to_enum(?secp192k1) -> 18; +oid_to_enum(?secp192r1) -> 19; +oid_to_enum(?secp224k1) -> 20; +oid_to_enum(?secp224r1) -> 21; +oid_to_enum(?secp256k1) -> 22; +oid_to_enum(?secp256r1) -> 23; +oid_to_enum(?secp384r1) -> 24; +oid_to_enum(?secp521r1) -> 25. + +enum_to_oid(1) -> ?sect163k1; +enum_to_oid(2) -> ?sect163r1; +enum_to_oid(3) -> ?sect163r2; +enum_to_oid(4) -> ?sect193r1; +enum_to_oid(5) -> ?sect193r2; +enum_to_oid(6) -> ?sect233k1; +enum_to_oid(7) -> ?sect233r1; +enum_to_oid(8) -> ?sect239k1; +enum_to_oid(9) -> ?sect283k1; +enum_to_oid(10) -> ?sect283r1; +enum_to_oid(11) -> ?sect409k1; +enum_to_oid(12) -> ?sect409r1; +enum_to_oid(13) -> ?sect571k1; +enum_to_oid(14) -> ?sect571r1; +enum_to_oid(15) -> ?secp160k1; +enum_to_oid(16) -> ?secp160r1; +enum_to_oid(17) -> ?secp160r2; +enum_to_oid(18) -> ?secp192k1; +enum_to_oid(19) -> ?secp192r1; +enum_to_oid(20) -> ?secp224k1; +enum_to_oid(21) -> ?secp224r1; +enum_to_oid(22) -> ?secp256k1; +enum_to_oid(23) -> ?secp256r1; +enum_to_oid(24) -> ?secp384r1; +enum_to_oid(25) -> ?secp521r1. + +sufficent_ec_support() -> + CryptoSupport = crypto:supports(), + proplists:get_bool(ecdh, proplists:get_value(public_keys, CryptoSupport)). -- cgit v1.2.3