From 571133751287d93598dc90fe90b58ab4580f4836 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Fri, 15 Jun 2012 17:35:58 +0200 Subject: ssl: Calculate handshake hash only when needed TLS/SSL version before 1.2 always used a MD5/SHA combination for the handshake hashes. With TLS 1.2 the default hash is SHA256 and it is possible to negotiate a different hash. This change delays the calculation of the handshake hashes until they are really needed. At that point the hash to use should be known. For now MD5/SHA is still hard coded. --- lib/ssl/src/ssl_connection.erl | 186 +++++++++++++++++++++-------------------- lib/ssl/src/ssl_handshake.erl | 93 ++++++++++----------- lib/ssl/src/ssl_handshake.hrl | 3 +- lib/ssl/src/ssl_ssl3.erl | 49 ++++------- lib/ssl/src/ssl_tls1.erl | 25 +++--- 5 files changed, 166 insertions(+), 190 deletions(-) (limited to 'lib/ssl') diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index c57930e821..965b396353 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -67,8 +67,7 @@ tls_packets = [], % Not yet handled decode ssl/tls packets. tls_record_buffer, % binary() buffer of incomplete records tls_handshake_buffer, % binary() buffer of incomplete handshakes - %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) - tls_handshake_hashes, % see above + tls_handshake_history, % tls_handshake_history() tls_cipher_texts, % list() received but not deciphered yet cert_db, % session, % #session{} from ssl_handshake.hrl @@ -301,12 +300,13 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), - Hashes0 = ssl_handshake:init_hashes(), + Handshake = ssl_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_init(SSLOpts0, Role) of {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> Session = State0#state.session, - State = State0#state{tls_handshake_hashes = Hashes0, + State = State0#state{ + tls_handshake_history = Handshake, session = Session#session{own_certificate = OwnCert, time_stamp = TimeStamp}, file_ref_db = FileRefHandle, @@ -344,15 +344,15 @@ hello(start, #state{host = Host, port = Port, role = client, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - Hashes0 = ssl_handshake:init_hashes(), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Hello, Version, ConnectionStates0, Hashes0), + Handshake0 = ssl_handshake:init_handshake_history(), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = ConnectionStates, - negotiated_version = Version, %% Requested version at this point + negotiated_version = Version, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_hashes = Hashes}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State1), next_state(hello, hello, Record, State); @@ -431,12 +431,13 @@ abbreviated(#hello_request{}, State0) -> abbreviated(#finished{verify_data = Data} = Finished, #state{role = server, negotiated_version = Version, - tls_handshake_hashes = Hashes, + tls_handshake_history = Handshake, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State) -> +%%CHECKME case ssl_handshake:verify_connection(Version, Finished, client, - MasterSecret, Hashes) of + MasterSecret, Handshake) of verified -> ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), next_state_connection(abbreviated, @@ -447,18 +448,18 @@ abbreviated(#finished{verify_data = Data} = Finished, end; abbreviated(#finished{verify_data = Data} = Finished, - #state{role = client, tls_handshake_hashes = Hashes0, + #state{role = client, tls_handshake_history = Handshake0, session = #session{master_secret = MasterSecret}, negotiated_version = Version, connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, server, - MasterSecret, Hashes0) of + MasterSecret, Handshake0) of verified -> ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated), next_state_connection(abbreviated, - ack_connection(State#state{tls_handshake_hashes = Hashes, + ack_connection(State#state{tls_handshake_history = Handshake, connection_states = ConnectionStates})); #alert{} = Alert -> @@ -641,10 +642,10 @@ cipher(#certificate_verify{signature = Signature}, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, - tls_handshake_hashes = Hashes + tls_handshake_history = Handshake } = State0) -> case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, Hashes) of + Version, MasterSecret, Handshake) of valid -> {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); @@ -660,10 +661,10 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), - MasterSecret, Hashes0) of + MasterSecret, Handshake0) of verified -> Session = register_session(Role, Host, Port, Session0), cipher_role(Role, Data, Session, State); @@ -691,17 +692,17 @@ connection(#hello_request{}, #state{host = Host, port = Port, transport_cb = Transport, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, - tls_handshake_hashes = Hashes0} = State0) -> + tls_handshake_history = Handshake0} = State0) -> Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Hello, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = - ConnectionStates, + ConnectionStates, session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_hashes = Hashes}), + tls_handshake_history = Handshake}), next_state(connection, hello, Record, State); connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> %% Mitigate Computational DoS attack @@ -1224,13 +1225,13 @@ certify_client(#state{client_certificate_requested = true, role = client, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}, socket = Socket, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), - {BinCert, ConnectionStates, Hashes} = - encode_handshake(Certificate, Version, ConnectionStates0, Hashes0), + {BinCert, ConnectionStates, Handshake} = + encode_handshake(Certificate, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCert), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}; + tls_handshake_history = Handshake}; certify_client(#state{client_certificate_requested = false} = State) -> State. @@ -1242,17 +1243,17 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, private_key = PrivateKey, session = #session{master_secret = MasterSecret, own_certificate = OwnCert}, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, PrivateKey, Hashes0) of + Version, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> - {BinVerified, ConnectionStates, Hashes} = + {BinVerified, ConnectionStates, Handshake} = encode_handshake(Verified, Version, - ConnectionStates0, Hashes0), + ConnectionStates0, Handshake0), Transport:send(Socket, BinVerified), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}; + tls_handshake_history = Handshake}; ignore -> State; #alert{} = Alert -> @@ -1308,11 +1309,11 @@ resumed_server_hello(#state{session = Session, {_, ConnectionStates1} -> State1 = State0#state{connection_states = ConnectionStates1, session = Session}, - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State1, abbreviated), State2 = State1#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State2), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> @@ -1351,11 +1352,11 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of State1 = #state{} -> - {ConnectionStates, Hashes} = finalize_handshake(State1, certify), + {ConnectionStates, Handshake} = finalize_handshake(State1, certify), State2 = State1#state{connection_states = ConnectionStates, %% Reinitialize client_certificate_requested = false, - tls_handshake_hashes = Hashes}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State2), next_state(certify, cipher, Record, State) catch @@ -1378,29 +1379,29 @@ server_hello(ServerHello, #state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> CipherSuite = ServerHello#server_hello.cipher_suite, {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates1, Handshake1} = + encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1, + tls_handshake_history = Handshake1, key_algorithm = KeyAlgorithm}. server_hello_done(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> HelloDone = ssl_handshake:server_hello_done(), - {BinHelloDone, ConnectionStates, Hashes} = - encode_handshake(HelloDone, Version, ConnectionStates0, Hashes0), + {BinHelloDone, ConnectionStates, Handshake} = + encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinHelloDone), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}. + tls_handshake_history = Handshake}. certify_server(#state{key_algorithm = dh_anon} = State) -> State; @@ -1409,17 +1410,17 @@ certify_server(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> - {BinCertMsg, ConnectionStates, Hashes} = - encode_handshake(CertMsg, Version, ConnectionStates0, Hashes0), + {BinCertMsg, ConnectionStates, Handshake} = + encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCertMsg), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes + tls_handshake_history = Handshake }; Alert = #alert{} -> throw(Alert) @@ -1432,7 +1433,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, socket = Socket, transport_cb = Transport } = State) @@ -1449,12 +1450,12 @@ key_exchange(#state{role = server, key_algorithm = Algo, Algo, ClientRandom, ServerRandom, PrivateKey}), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, diffie_hellman_keys = Keys, - tls_handshake_hashes = Hashes}; + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1463,36 +1464,36 @@ key_exchange(#state{role = client, negotiated_version = Version, premaster_secret = PremasterSecret, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}; + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = Algorithm, negotiated_version = Version, diffie_hellman_keys = {DhPubKey, _}, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) + tls_handshake_history = Handshake0} = State) when Algorithm == dhe_dss; Algorithm == dhe_rsa; Algorithm == dh_anon -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}. + tls_handshake_history = Handshake}. rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) when Algorithm == ?rsaEncryption; Algorithm == ?md2WithRSAEncryption; Algorithm == ?md5WithRSAEncryption; Algorithm == ?sha1WithRSAEncryption -> - ssl_handshake:key_exchange(client, + ssl_handshake:key_exchange(client, {premaster_secret, PremasterSecret, PublicKeyInfo}); rsa_key_exchange(_, _) -> @@ -1502,17 +1503,17 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), - {BinMsg, ConnectionStates, Hashes} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{client_certificate_requested = true, connection_states = ConnectionStates, - tls_handshake_hashes = Hashes}; + tls_handshake_history = Handshake}; request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State) -> State. @@ -1538,14 +1539,14 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, transport_cb = Transport, session = Session, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0}, StateName) -> + tls_handshake_history = Handshake0}, StateName) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0), + Finished = ssl_handshake:finished(Version, Role, MasterSecret, Handshake0), ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName), - {BinFinished, ConnectionStates, Hashes} = - encode_handshake(Finished, Version, ConnectionStates1, Hashes0), + {BinFinished, ConnectionStates, Handshake} = + encode_handshake(Finished, Version, ConnectionStates1, Handshake0), Transport:send(Socket, BinFinished), - {ConnectionStates, Hashes}. + {ConnectionStates, Handshake}. save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) -> ssl_record:set_client_verify_data(current_write, Data, ConnectionStates); @@ -1641,26 +1642,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1, session = Session}, cipher), next_state_connection(cipher, ack_connection(State#state{connection_states = ConnectionStates, session = Session, - tls_handshake_hashes = - Hashes})). + tls_handshake_history = + Handshake})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ssl_record:encode_alert_record(Alert, Version, ConnectionStates). encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> ssl_record:encode_change_cipher_spec(Version, ConnectionStates). -encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) -> +encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) -> Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), - Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag), + Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag), {E, ConnectionStates1} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), - {E, ConnectionStates1, Hashes1}. + {E, ConnectionStates1, Handshake1}. encode_packet(Data, #socket_options{packet=Packet}) -> case Packet of @@ -1913,21 +1914,22 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> %% This message should not be included in handshake %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + Hs0 = ssl_handshake:init_handshake_history(), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, renegotiation = {true, peer}}); ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> %% This message should not be included in handshake %% message hashes. Already in negotiation so it will be ignored! ?MODULE:SName(Packet, State); ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Hs0 = ssl_handshake:init_hashes(), - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, + Version = Packet#client_hello.client_version, + Hs0 = ssl_handshake:init_handshake_history(), + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); (_, StopState) -> StopState end, try @@ -2011,7 +2013,7 @@ next_state_connection(StateName, #state{send_queue = Queue0, next_state_is_connection(StateName, State) end. -%% In next_state_is_connection/1: clear tls_handshake_hashes, +%% In next_state_is_connection/1: clear tls_handshake, %% premaster_secret and public_key_info (only needed during handshake) %% to reduce memory foot print of a connection. next_state_is_connection(_, State = @@ -2020,13 +2022,13 @@ next_state_is_connection(_, State = #socket_options{active = false}}) when RecvFrom =/= undefined -> passive_receive(State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}, connection); + tls_handshake_history = ssl_handshake:init_handshake_history()}, connection); next_state_is_connection(StateName, State0) -> {Record, State} = next_record_if_active(State0), next_state(StateName, connection, Record, State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}). + tls_handshake_history = ssl_handshake:init_handshake_history()}). register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> Session = Session0#session{is_resumable = true}, @@ -2307,8 +2309,8 @@ ack_connection(State) -> renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested %% the renegotiation - Hs0 = ssl_handshake:init_hashes(), - connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0}); + Hs0 = ssl_handshake:init_handshake_history(), + connection(#hello_request{}, State#state{tls_handshake_history = Hs0}); renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, @@ -2316,13 +2318,13 @@ renegotiate(#state{role = server, connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), Frag = ssl_handshake:encode_handshake(HelloRequest, Version), - Hs0 = ssl_handshake:init_hashes(), + Hs0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hs0}), + tls_handshake_history = Hs0}), next_state(connection, hello, Record, State#state{allow_renegotiate = true}). notify_senders(SendQueue) -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 06d45966c1..cf42129534 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -36,7 +36,7 @@ certificate_request/3, key_exchange/2, server_key_exchange_hash/2, finished/4, verify_connection/5, get_tls_handshake/2, decode_client_key/3, server_hello_done/0, - encode_handshake/2, init_hashes/0, update_hashes/2, + encode_handshake/2, init_handshake_history/0, update_handshake_history/2, decrypt_premaster_secret/2, prf/5]). -export([dec_hello_extensions/2]). @@ -258,7 +258,7 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) -> %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), tls_version(), private_key(), - {{binary(), binary()},{binary(), binary()}}) -> + tls_handshake_history()) -> #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. @@ -268,28 +268,28 @@ client_certificate_verify(undefined, _, _, _, _) -> client_certificate_verify(_, _, _, undefined, _) -> ignore; client_certificate_verify(OwnCert, MasterSecret, Version, - PrivateKey, {Hashes0, _}) -> + PrivateKey, {Handshake, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE); false -> Hashes = calc_certificate_verify(Version, MasterSecret, - alg_oid(PrivateKey), Hashes0), + alg_oid(PrivateKey), Handshake), Signed = digitally_signed(Hashes, PrivateKey), #certificate_verify{signature = Signed} end. %%-------------------------------------------------------------------- -spec certificate_verify(binary(), public_key_info(), tls_version(), - binary(), {_, {binary(), binary()}}) -> valid | #alert{}. + binary(), tls_handshake_history()) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version, - MasterSecret, {_, Hashes0}) -> + MasterSecret, {_, Handshake}) -> Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), + Algorithm, Handshake), case public_key:decrypt_public(Signature, PublicKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> @@ -298,9 +298,9 @@ certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Versi ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end; certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, - MasterSecret, {_, Hashes0}) -> + MasterSecret, {_, Handshake}) -> Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), + Algorithm, Handshake), case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of true -> valid; @@ -416,26 +416,26 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- --spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) -> +-spec finished(tls_version(), client | server, binary(), tls_handshake_history()) -> #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- -finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes +finished(Version, Role, MasterSecret, {Handshake, _}) -> % use the current hashes #finished{verify_data = - calc_finished(Version, Role, MasterSecret, Hashes)}. + calc_finished(Version, Role, MasterSecret, Handshake)}. %%-------------------------------------------------------------------- -spec verify_connection(tls_version(), #finished{}, client | server, binary(), - {_, {binary(), binary()}}) -> verified | #alert{}. + tls_handshake_history()) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. %%------------------------------------------------------------------- verify_connection(Version, #finished{verify_data = Data}, - Role, MasterSecret, {_, {MD5, SHA}}) -> + Role, MasterSecret, {_, Handshake}) -> %% use the previous hashes - case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of + case calc_finished(Version, Role, MasterSecret, Handshake) of Data -> verified; _ -> @@ -482,39 +482,34 @@ decode_client_key(ClientKey, Type, Version) -> dec_client_key(ClientKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- --spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}. +-spec init_handshake_history() -> tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) init functions to -%% initalize the hash context. +%% Description: Initialize the empty handshake history buffer. %%-------------------------------------------------------------------- -init_hashes() -> - T = {crypto:md5_init(), crypto:sha_init()}, - {T, T}. +init_handshake_history() -> + {[], []}. %%-------------------------------------------------------------------- --spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) -> - {{binary(), binary()}, {binary(), binary()}}. +-spec update_handshake_history(tls_handshake_history(), Data ::term()) -> + tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) update functions to -%% update the hash context with Data. +%% Description: Update the handshake history buffer with Data. %%-------------------------------------------------------------------- -update_hashes(Hashes, % special-case SSL2 client hello - <>) -> - update_hashes(Hashes, - <>); -update_hashes({{MD50, SHA0}, _Prev}, Data) -> - {MD51, SHA1} = {crypto:md5_update(MD50, Data), - crypto:sha_update(SHA0, Data)}, - {{MD51, SHA1}, {MD50, SHA0}}. +update_handshake_history(Handshake, % special-case SSL2 client hello + <>) -> + update_handshake_history(Handshake, + <>); +update_handshake_history({Handshake0, _Prev}, Data) -> + {[Data|Handshake0], Handshake0}. %%-------------------------------------------------------------------- -spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary(). @@ -1139,17 +1134,17 @@ setup_keys({3,1}, MasterSecret, ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KML, IVS). -calc_finished({3, 0}, Role, MasterSecret, Hashes) -> - ssl_ssl3:finished(Role, MasterSecret, Hashes); -calc_finished({3, N}, Role, MasterSecret, Hashes) +calc_finished({3, 0}, Role, MasterSecret, Handshake) -> + ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake)); +calc_finished({3, N}, Role, MasterSecret, Handshake) when N == 1; N == 2 -> - ssl_tls1:finished(Role, MasterSecret, Hashes). + ssl_tls1:finished(Role, MasterSecret, lists:reverse(Handshake)). -calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) -> - ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes); -calc_certificate_verify({3, N}, _, Algorithm, Hashes) +calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Handshake) -> + ssl_ssl3:certificate_verify(Algorithm, MasterSecret, lists:reverse(Handshake)); +calc_certificate_verify({3, N}, _, Algorithm, Handshake) when N == 1; N == 2 -> - ssl_tls1:certificate_verify(Algorithm, Hashes). + ssl_tls1:certificate_verify(Algorithm, lists:reverse(Handshake)). key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index fb0ebac7d1..8510def2fd 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -31,6 +31,7 @@ -type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. -type public_key_params() :: #'Dss-Parms'{} | term(). -type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. +-type tls_handshake_history() :: {[binary()], [binary()]}. -record(session, { session_id, diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index f2926b2d2f..2b4ae1539f 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), Block. --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, MasterSecret, Handshake) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { %% opaque md5_hash[16]; @@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(handshake_messages + Sender + %% master_secret + pad1)); Sender = get_sender(Role), - MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake), <>. --spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary(). +-spec certificate_verify(OID::tuple(), binary(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> +certificate_verify(?'rsaEncryption', MasterSecret, Handshake) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); @@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake), <>; -certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) -> +certificate_verify(?'id-dsa', MasterSecret, Handshake) -> %% sha_hash %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - handshake_hash(?SHA, MasterSecret, undefined, SHAHash). + + handshake_hash(?SHA, MasterSecret, undefined, Handshake). -spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). @@ -157,16 +158,6 @@ hash(?MD5, Data) -> hash(?SHA, Data) -> crypto:sha(Data). -hash_update(?MD5, Context, Data) -> - crypto:md5_update(Context, Data); -hash_update(?SHA, Context, Data) -> - crypto:sha_update(Context, Data). - -hash_final(?MD5, Context) -> - crypto:md5_final(Context); -hash_final(?SHA, Context) -> - crypto:sha_final(Context). - %%pad_1(?NULL) -> %% ""; pad_1(?MD5) -> @@ -189,19 +180,11 @@ mac_hash(Method, Secret, Data) -> InnerHash = hash(Method, [Secret, pad_1(Method), Data]), hash(Method, [Secret, pad_2(Method), InnerHash]). -handshake_hash(Method, HandshakeHash, Extra) -> - HSH = hash_update(Method, HandshakeHash, Extra), - hash_final(Method, HSH). - -handshake_hash(Method, MasterSecret, undefined, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [MasterSecret, pad_1(Method)]), +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, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [Sender, MasterSecret, pad_1(Method)]), +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"; diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index c8aae34892..d64a8f815d 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -44,9 +44,9 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> prf(PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, MasterSecret, Handshake) -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { %% opaque verify_data[12]; @@ -55,19 +55,19 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% verify_data %% PRF(master_secret, finished_label, MD5(handshake_messages) + %% SHA-1(handshake_messages)) [0..11]; - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). --spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary(). +-spec certificate_verify(OID::tuple(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) -> - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), +certificate_verify(?'rsaEncryption', Handshake) -> + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), <>; -certificate_verify(?'id-dsa', {_, SHAHash}) -> - hash_final(?SHA, SHAHash). +certificate_verify(?'id-dsa', Handshake) -> + crypto:sha(Handshake). -spec setup_keys(binary(), binary(), binary(), integer(), integer(), integer()) -> {binary(), binary(), binary(), @@ -228,8 +228,3 @@ finished_label(client) -> <<"client finished">>; finished_label(server) -> <<"server finished">>. - -hash_final(?MD5, Conntext) -> - crypto:md5_final(Conntext); -hash_final(?SHA, Conntext) -> - crypto:sha_final(Conntext). -- cgit v1.2.3