From 332716f059f291eba836fb46071a9b3e718f43c0 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Wed, 15 Aug 2012 10:52:39 +0200 Subject: ssl: Add Signature Algorithms hello extension from TLS 1.2 This is also avoids triggering some bugs in OpenSSL. --- lib/ssl/src/ssl_cipher.erl | 6 ++- lib/ssl/src/ssl_connection.erl | 16 ++++--- lib/ssl/src/ssl_handshake.erl | 85 ++++++++++++++++++++++++++++------- lib/ssl/src/ssl_handshake.hrl | 15 ++++++- lib/ssl/test/ssl_to_openssl_SUITE.erl | 13 ------ 5 files changed, 95 insertions(+), 40 deletions(-) (limited to 'lib') diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 86b09b3f32..358972f522 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -606,7 +606,11 @@ hash_size(md5) -> hash_size(sha) -> 20; hash_size(sha256) -> - 32. + 32; +hash_size(sha384) -> + 48; +hash_size(sha512) -> + 64. %% RFC 5246: 6.2.3.2. CBC Block Cipher %% diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index ad02c6c0cf..c09e07018d 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1606,18 +1606,18 @@ handle_server_key( ?UINT16(GLen), G/binary, ?UINT16(YLen), ServerPublicDhKey/binary>>), - - case verify_dh_params(Version, Signed, Hash, PubKeyInfo) of + + case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of true -> dh_master_secret(P, G, ServerPublicDhKey, undefined, State); false -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params({3, Minor}, Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) +verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) when Minor >= 3 -> - public_key:verify({digest, Hashes}, sha, Signed, PubKey); -verify_dh_params(_Version, Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> + public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey); +verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> @@ -1625,8 +1625,10 @@ verify_dh_params(_Version, Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParam _ -> false end; -verify_dh_params(_Version, Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> - public_key:verify({digest, Hash}, sha, Signed, {PublicKey, PublicKeyParams}). +verify_dh_params(_Version, Signed, Hash, undefined, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, sha, Signed, {PublicKey, PublicKeyParams}); +verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}). dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> PMpint = mpint_binary(Prime), diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 5a14841949..d096bc347d 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -78,7 +78,8 @@ client_hello(Host, Port, ConnectionStates, compression_methods = ssl_record:compressions(), random = SecParams#security_parameters.client_random, renegotiation_info = - renegotiation_info(client, ConnectionStates, Renegotiation) + renegotiation_info(client, ConnectionStates, Renegotiation), + hash_signs = default_hash_signs() }. %%-------------------------------------------------------------------- @@ -121,10 +122,11 @@ hello_request() -> %%-------------------------------------------------------------------- hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, - session_id = SessionId, renegotiation_info = Info}, + session_id = SessionId, renegotiation_info = Info, + hash_signs = _HashSigns}, #ssl_options{secure_renegotiate = SecureRenegotation}, ConnectionStates0, Renegotiation) -> - +%%TODO: select hash and signature algorigthm case ssl_record:is_acceptable_version(Version) of true -> case handle_renegotiation_info(client, Info, ConnectionStates0, @@ -143,10 +145,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, hello(#client_hello{client_version = ClientVersion, random = Random, cipher_suites = CipherSuites, - renegotiation_info = Info} = Hello, + renegotiation_info = Info, + hash_signs = _HashSigns} = Hello, #ssl_options{versions = Versions, secure_renegotiate = SecureRenegotation} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> +%% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), case ssl_record:is_acceptable_version(Version) of true -> @@ -830,16 +834,19 @@ dec_hs(_Version, ?CLIENT_HELLO, <>) -> - - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions), - undefined), + HelloExtensions = dec_hello_extensions(Extensions), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #client_hello{ client_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suites = from_2bytes(CipherSuites), compression_methods = Comp_methods, - renegotiation_info = RenegotiationInfo + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns }; dec_hs(_Version, ?SERVER_HELLO, <>) -> - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []), - undefined), + HelloExtensions = dec_hello_extensions(Extensions, []), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #server_hello{ server_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = RenegotiationInfo}; + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}; dec_hs(_Version, ?CERTIFICATE, <>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; @@ -952,6 +964,15 @@ dec_hello_extensions(<>, Acc) -> + SignAlgoListLen = Len - 2, + <> = ExtData, + HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || + <> <= SignAlgoList], + dec_hello_extensions(Rest, [{hash_signs, + #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]); + %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. dec_hello_extensions(<>, Acc) -> @@ -993,14 +1014,19 @@ enc_hs(#client_hello{client_version = {Major, Minor}, session_id = SessionID, cipher_suites = CipherSuites, compression_methods = CompMethods, - renegotiation_info = RenegotiationInfo}, _Version) -> + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions = hello_extensions(RenegotiationInfo), - ExtensionsBin = enc_hello_extensions(Extensions), + Extensions0 = hello_extensions(RenegotiationInfo), + Extensions1 = if + Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); + true -> Extensions0 + end, + ExtensionsBin = enc_hello_extensions(Extensions1), {?CLIENT_HELLO, < SignLen = byte_size(Sign), <>. -%% Renegotiation info, only current extension +hello_extensions(undefined) -> + []; +%% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> []; hello_extensions(#renegotiation_info{} = Info) -> + [Info]; +hello_extensions(#hash_sign_algos{} = Info) -> [Info]. enc_hello_extensions(Extensions) -> @@ -1105,7 +1135,14 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> InfoLen = byte_size(Info), Len = InfoLen +1, - enc_hello_extensions(Rest, <>). + enc_hello_extensions(Rest, <>); + +enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> + SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || + {Hash, Sign} <- HashSignAlgos >>, + ListLen = byte_size(SignAlgoList), + Len = ListLen + 2, + enc_hello_extensions(Rest, <>). from_3bytes(Bin3) -> @@ -1230,3 +1267,17 @@ certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> Hashes -> true; _ -> false end. + +-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). +-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). + +-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)). + +default_hash_signs() -> + #hash_sign_algos{hash_sign_algos = + [?TLSEXT_SIGALG(sha512), + ?TLSEXT_SIGALG(sha384), + ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha), + ?TLSEXT_SIGALG_DSA(sha), + ?TLSEXT_SIGALG_RSA(md5)]}. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index abe2fa5261..9967a265c1 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -97,7 +97,8 @@ session_id, % opaque SessionID<0..32> cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). -record(server_hello, { @@ -106,7 +107,8 @@ session_id, % opaque SessionID<0..32> cipher_suite, % cipher_suites compression_method, % compression_method - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -224,6 +226,15 @@ renegotiated_connection }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Signature Algorithms RFC 5746 section 7.4.1.4.1. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(SIGNATURE_ALGORITHMS_EXT, 13). + +-record(hash_sign_algos, { + hash_sign_algos + }). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index ec35c42773..ce481919f2 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1170,17 +1170,6 @@ version_flag('tlsv1.2') -> " -tls1_2 "; version_flag(sslv3) -> " -ssl3 ". - -check_sane_openssl_tls_client_renegotaite(Config) -> - case proplists:get_value(name, ?config(tc_group_properties, Config)) of - Version when Version == 'tlsv1.2'; Version == 'tlsv1.1' -> - case os:cmd("openssl version") of - "OpenSSL 1.0.1 " ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - _ -> - Config - end - end. check_sane_openssl_renegotaite(Config) -> case os:cmd("openssl version") of @@ -1188,8 +1177,6 @@ check_sane_openssl_renegotaite(Config) -> {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 0.9.7" ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 1.0.1" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; _ -> Config end. -- cgit v1.2.3