From 0bcbe96a1a68fef87efc43719853edcee2f58720 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Sun, 8 Apr 2012 03:39:52 +0200 Subject: ssl: Make signature handling version dependant TLS 1.2 introduces changes on how signatures are calculate and encoded. This makes the signature handling version aware --- lib/ssl/src/ssl_cipher.erl | 5 +- lib/ssl/src/ssl_connection.erl | 21 +++++--- lib/ssl/src/ssl_handshake.erl | 113 ++++++++++++++++++++++------------------- lib/ssl/src/ssl_handshake.hrl | 6 ++- lib/ssl/src/ssl_ssl3.erl | 2 +- lib/ssl/src/ssl_tls1.erl | 4 +- 6 files changed, 85 insertions(+), 66 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 9e1fbe20f4..8b6d5ea4ea 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -35,7 +35,8 @@ -export([security_parameters/3, suite_definition/1, decipher/5, cipher/5, suite/1, suites/1, anonymous_suites/0, - openssl_suite/1, openssl_suite_name/1, filter/2]). + openssl_suite/1, openssl_suite_name/1, filter/2, + hash_algorithm/1, sign_algorithm/1]). -compile(inline). @@ -608,7 +609,7 @@ hash_algorithm(sha512) -> ?SHA512; hash_algorithm(?NULL) -> null; hash_algorithm(?MD5) -> md5; hash_algorithm(?SHA) -> sha; -%%hash_algorithm(?SHA224) -> sha224; +hash_algorithm(?SHA224) -> sha224; hash_algorithm(?SHA256) -> sha256; hash_algorithm(?SHA384) -> sha384; hash_algorithm(?SHA512) -> sha512. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 0d3efae5f4..19847575e5 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -77,6 +77,7 @@ supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite + hashsign_algorithm, % atom as defined by cipher_suite public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} private_key, % PKIX: #'RSAPrivateKey'{} diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side @@ -380,6 +381,7 @@ hello(#server_hello{cipher_suite = CipherSuite, PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), State = State0#state{key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, connection_states = ConnectionStates, premaster_secret = PremasterSecret}, @@ -643,10 +645,11 @@ cipher(#certificate_verify{signature = Signature}, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, + hashsign_algorithm = HashSign, tls_handshake_history = Handshake } = State0) -> case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, Handshake) of + Version, HashSign, MasterSecret, Handshake) of valid -> {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); @@ -1247,10 +1250,11 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, private_key = PrivateKey, session = #session{master_secret = MasterSecret, own_certificate = OwnCert}, + hashsign_algorithm = HashSign, tls_handshake_history = Handshake0} = State) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, PrivateKey, Handshake0) of + Version, HashSign, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> {BinVerified, ConnectionStates, Handshake} = encode_handshake(Verified, Version, @@ -1391,7 +1395,8 @@ server_hello(ServerHello, #state{transport_cb = Transport, Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, tls_handshake_history = Handshake1, - key_algorithm = KeyAlgorithm}. + key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}. server_hello_done(#state{transport_cb = Transport, socket = Socket, @@ -1433,6 +1438,7 @@ certify_server(#state{transport_cb = Transport, key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; key_exchange(#state{role = server, key_algorithm = Algo, + hashsign_algorithm = {HashAlgo, _}, diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, @@ -1451,7 +1457,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, - Algo, ClientRandom, + HashAlgo, ClientRandom, ServerRandom, PrivateKey}), {BinMsg, ConnectionStates, Handshake} = @@ -1577,22 +1583,23 @@ handle_server_key( #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, - signed_params = Signed}, + signed_params = Signed, + hashsign = HashSign}, #state{negotiated_version = Version, public_key_info = PubKeyInfo, - key_algorithm = KeyAlgo, connection_states = ConnectionStates} = State) -> PLen = size(P), GLen = size(G), YLen = size(ServerPublicDhKey), + HashAlgo = connection_hash_algo(HashSign, State), ConnectionState = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo, + Hash = ssl_handshake:server_key_exchange_hash(HashAlgo, < %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), - tls_version(), private_key(), + tls_version(), term(), private_key(), tls_handshake_history()) -> #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- -client_certificate_verify(undefined, _, _, _, _) -> +client_certificate_verify(undefined, _, _, _, _, _) -> ignore; -client_certificate_verify(_, _, _, undefined, _) -> +client_certificate_verify(_, _, _, _, undefined, _) -> ignore; client_certificate_verify(OwnCert, MasterSecret, Version, + {HashAlgo, SignAlgo}, 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), Handshake), - Signed = digitally_signed(Hashes, PrivateKey), - #certificate_verify{signature = Signed} + false -> + Hashes = + calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey), + #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}} end. %%-------------------------------------------------------------------- --spec certificate_verify(binary(), public_key_info(), tls_version(), +-spec certificate_verify(binary(), public_key_info(), tls_version(), term(), binary(), tls_handshake_history()) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. @@ -296,18 +296,25 @@ certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor}) certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> case public_key:decrypt_public(Signature, PublicKey, [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> + Hashes -> true; + _ -> false + end. + +certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of + true -> valid; _ -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end; -certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, - MasterSecret, {_, Handshake}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Handshake), +certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of - true -> - valid; + true -> + valid; false -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. @@ -324,9 +331,11 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), + HashSigns = hashsign_algorithms(CipherSuite), Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, + hashsign_algorithms = HashSigns, certificate_authorities = Authorities }. @@ -334,7 +343,7 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> -spec key_exchange(client | server, tls_version(), {premaster_secret, binary(), public_key_info()} | {dh, binary()} | - {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(), + {dh, {binary(), binary()}, #'DHParameter'{}, hash_algo(), binary(), binary(), private_key()}) -> #client_key_exchange{} | #server_key_exchange{}. %% @@ -351,9 +360,9 @@ key_exchange(client, _Version, {dh, <>}) -> dh_public = PublicKey} }; -key_exchange(server, _Version, {dh, {<>, _}, +key_exchange(server, Version, {dh, {<>, _}, #'DHParameter'{prime = P, base = G}, - KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) -> + HashAlgo, ClientRandom, ServerRandom, PrivateKey}) -> <> = crypto:mpint(P), <> = crypto:mpint(G), PLen = byte_size(PBin), @@ -362,20 +371,22 @@ key_exchange(server, _Version, {dh, {<>, _}, ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, - case KeyAlgo of - dh_anon -> + case HashAlgo of + null -> #server_key_exchange{params = ServerDHParams, - signed_params = <<>>}; + signed_params = <<>>, + hashsign = {null, anon}}; _ -> Hash = - server_key_exchange_hash(KeyAlgo, <>), - Signed = digitally_signed(Hash, PrivateKey), + Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey), #server_key_exchange{params = ServerDHParams, - signed_params = Signed} + signed_params = Signed, + hashsign = {HashAlgo, digitally_signed_alg(PrivateKey)}} end. %%-------------------------------------------------------------------- @@ -527,6 +538,7 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> [{rsa_pad, rsa_pkcs1_padding}]) catch _:_ -> + io:format("decrypt_premaster_secret error"), throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR)) end. @@ -535,14 +547,13 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> %% %% Description: Calculate server key exchange hash %%-------------------------------------------------------------------- -server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; - Algorithm == dhe_rsa -> +server_key_exchange_hash(md5sha, Value) -> MD5 = crypto:md5(Value), - SHA = crypto:sha(Value), + SHA = crypto:sha(Value), <>; -server_key_exchange_hash(dhe_dss, Value) -> - crypto:sha(Value). +server_key_exchange_hash(Hash, Value) -> + crypto:hash(Hash, Value). %%-------------------------------------------------------------------- -spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) -> @@ -878,14 +889,14 @@ dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <>) -> %% May happen if key_algorithm is dh_anon #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = <<>>}; + signed_params = <<>>, hashsign = {null, anon}}; dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <>) -> #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = Sig}; + signed_params = Sig, hashsign = undefined}; dec_hs(_Version, ?CERTIFICATE_REQUEST, <>) -> @@ -894,7 +905,7 @@ dec_hs(_Version, ?CERTIFICATE_REQUEST, dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) -> #server_hello_done{}; dec_hs(_Version, ?CERTIFICATE_VERIFY,<>)-> - #certificate_verify{signature = Signature}; + #certificate_verify{hashsign_algorithm = {unknown, unknown}, signature = Signature}; dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) -> #client_key_exchange{exchange_keys = PKEPMS}; dec_hs(_Version, ?FINISHED, VerifyData) -> @@ -1005,15 +1016,15 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> {?CERTIFICATE, <>}; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams}, _Version) -> + signed_params = SignedParams, hashsign = HashSign}, Version) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), - SignedLen = byte_size(SignedParams), + Signature = enc_sign(HashSign, SignedParams, Version), {?SERVER_KEY_EXCHANGE, <> + Signature/binary>> }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, @@ -1028,8 +1039,8 @@ enc_hs(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)}; -enc_hs(#certificate_verify{signature = BinSig}, _) -> - EncSig = enc_bin_sig(BinSig), +enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) -> + EncSig = enc_sign(HashSign, BinSig, Version), {?CERTIFICATE_VERIFY, EncSig}; enc_hs(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. @@ -1043,9 +1054,9 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), <>. -enc_bin_sig(BinSig) -> - Size = byte_size(BinSig), - <>. +enc_sign(_HashSign, Sign, _Version) -> + SignLen = byte_size(Sign), + <>. %% Renegotiation info, only current extension hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> @@ -1125,7 +1136,7 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) -> [Cert | Acc]; (_, Acc) -> Acc - end, + end, ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle). @@ -1135,10 +1146,11 @@ digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) -> public_key:sign({digest, Hash}, sha, Key); digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, - [{rsa_pad, rsa_pkcs1_padding}]); -digitally_signed(Hash, #'DSAPrivateKey'{} = Key) -> - public_key:sign(Hash, none, Key). - + [{rsa_pad, rsa_pkcs1_padding}]). + +digitally_signed_alg(#'RSAPrivateKey'{} = _Key) -> rsa; +digitally_signed_alg(#'DSAPrivateKey'{} = _Key) -> dsa. + calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); @@ -1182,8 +1194,3 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, UserState} -> {unknown, {SslState, UserState}} end. - -alg_oid(#'RSAPrivateKey'{}) -> - ?'rsaEncryption'; -alg_oid(#'DSAPrivateKey'{}) -> - ?'id-dsa'. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 20e498ea2e..abe2fa5261 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -32,6 +32,7 @@ -type public_key_params() :: #'Dss-Parms'{} | term(). -type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. -type tls_handshake_history() :: {[binary()], [binary()]}. +-type hash_algo() :: atom(). %% Signature algorithms -define(ANON, 0). @@ -136,7 +137,8 @@ -record(server_key_exchange, { params, %% #server_rsa_params{} | #server_dh_params{} - signed_params %% #signature{} + signed_params, %% #signature{} + hashsign %% term(atom(), atom()) }). %% enum { anonymous, rsa, dsa } SignatureAlgorithm; @@ -166,6 +168,7 @@ -record(certificate_request, { certificate_types, %ClientCertificateType <1..2^8-1> + hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>; certificate_authorities %DistinguishedName <0..2^16-1> }). @@ -200,6 +203,7 @@ %%% Certificate verify - RFC 4346 section 7.4.8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(certificate_verify, { + hashsign_algorithm, signature % binary() }). diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index bb368fe5d0..a11c5b8c0c 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -76,7 +76,7 @@ finished(Role, MasterSecret, Handshake) -> -spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', MasterSecret, Handshake) -> +certificate_verify(md5sha, MasterSecret, Handshake) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index e6e55048a4..d62ea6e5a4 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -28,7 +28,7 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). --export([master_secret/4, finished/5, certificate_verify/2, mac_hash/7, +-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, setup_keys/8, suites/1, prf/5]). %%==================================================================== @@ -75,7 +75,7 @@ finished(Role, Version, PrfAlgo, MasterSecret, Handshake) -spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', Handshake) -> +certificate_verify(md5sha, _Version, Handshake) -> MD5 = crypto:md5(Handshake), SHA = crypto:sha(Handshake), <>; -- cgit v1.2.3