diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 112 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 188 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.hrl | 9 |
3 files changed, 161 insertions, 148 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index cde13069b5..94f76e0606 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1628,78 +1628,49 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> ssl_record:set_server_verify_data(current_write, Data, ConnectionStates). -handle_server_key(#server_key_exchange{params = - #server_dh_params{dh_p = P, - dh_g = G, - dh_y = ServerPublicDhKey}, - signed_params = <<>>}, - #state{key_algorithm = dh_anon} = State) -> - dh_master_secret(P, G, ServerPublicDhKey, undefined, State); - -handle_server_key( - #server_key_exchange{params = - #server_dh_params{dh_p = P, - dh_g = G, - dh_y = ServerPublicDhKey}, - signed_params = Signed, - hashsign = HashSign}, - #state{negotiated_version = Version, - public_key_info = PubKeyInfo, - connection_states = ConnectionStates} = State) -> - - PLen = size(P), - GLen = size(G), - YLen = size(ServerPublicDhKey), - HashAlgo = connection_hash_algo(HashSign, State), +handle_server_key(#server_key_exchange{exchange_keys = Keys}, + #state{key_algorithm = KeyAlg, + negotiated_version = Version} = State) -> + Params = ssl_handshake:decode_server_key(Keys, KeyAlg, Version), + HashSign = connection_hashsign(Params#server_key_params.hashsign, State), + case HashSign of + {_, anon} -> + server_master_secret(Params#server_key_params.params, State); + _ -> + verify_server_key(Params, HashSign, State) + end. - ConnectionState = +verify_server_key(#server_key_params{params = Params, + params_bin = EncParams, + signature = Signature}, + HashSign = {HashAlgo, _}, + #state{negotiated_version = Version, + public_key_info = PubKeyInfo, + connection_states = ConnectionStates} = 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(HashAlgo, - <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), P/binary, - ?UINT16(GLen), G/binary, - ?UINT16(YLen), - ServerPublicDhKey/binary>>), - - case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of + <<ClientRandom/binary, + ServerRandom/binary, + EncParams/binary>>), + case ssl_handshake:verify_signature(Version, Hash, HashSign, Signature, PubKeyInfo) of true -> - dh_master_secret(P, G, ServerPublicDhKey, undefined, State); + server_master_secret(Params, State); false -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) - when Minor >= 3 -> - 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 -> - true; - _ -> - false - end; -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), - GMpint = mpint_binary(Base), - Keys = {_, PrivateDhKey} = - crypto:dh_generate_key([PMpint,GMpint]), - dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); +server_master_secret(#server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, + State) -> + dh_master_secret(P, G, ServerPublicDhKey, undefined, State). -dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, - #state{session = Session, - negotiated_version = Version, role = Role, - connection_states = ConnectionStates0} = State) -> - PremasterSecret = - crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, - [PMpint, GMpint]), +master_from_premaster_secret(PremasterSecret, + #state{session = Session, + negotiated_version = Version, role = Role, + connection_states = ConnectionStates0} = State) -> case ssl_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, Role) of {MasterSecret, ConnectionStates} -> @@ -1711,6 +1682,19 @@ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, Alert end. +dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> + PMpint = mpint_binary(Prime), + GMpint = mpint_binary(Base), + Keys = {_, PrivateDhKey} = + crypto:dh_generate_key([PMpint,GMpint]), + dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + +dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State) -> + PremasterSecret = + crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, + [PMpint, GMpint]), + master_from_premaster_secret(PremasterSecret, State). + cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), next_state_connection(cipher, ack_connection(State#state{session = Session, @@ -2485,10 +2469,10 @@ get_pending_connection_state_prf(CStates, Direction) -> CS = ssl_record:pending_connection_state(CStates, Direction), CS#connection_state.security_parameters#security_parameters.prf_algorithm. -connection_hash_algo({HashAlgo, _}, _State) -> - HashAlgo; -connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) -> - HashAlgo. +connection_hashsign(HashSign = {_, _}, _State) -> + HashSign; +connection_hashsign(_, #state{hashsign_algorithm = HashSign}) -> + HashSign. %% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms %% If the client does not send the signature_algorithms extension, the diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index db21dac942..1929370991 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -32,10 +32,10 @@ -export([master_secret/4, client_hello/8, server_hello/5, hello/4, hello_request/0, certify/7, certificate/4, - client_certificate_verify/6, certificate_verify/6, + client_certificate_verify/6, certificate_verify/6, verify_signature/5, certificate_request/3, key_exchange/3, server_key_exchange_hash/2, finished/5, verify_connection/6, get_tls_handshake/3, - decode_client_key/3, server_hello_done/0, + decode_client_key/3, decode_server_key/3, server_hello_done/0, encode_handshake/2, init_handshake_history/0, update_handshake_history/2, decrypt_premaster_secret/2, prf/5, next_protocol/1]). @@ -320,25 +320,36 @@ client_certificate_verify(OwnCert, MasterSecret, Version, %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -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 +certificate_verify(Signature, PublicKeyInfo, Version, + HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) -> + Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case verify_signature(Version, Hash, HashSign, Signature, PublicKeyInfo) of true -> valid; _ -> - ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) - end; -certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, - {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> - Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), - case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of - true -> - valid; - false -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. +%%-------------------------------------------------------------------- +-spec verify_signature(tls_version(), binary(), {term(), term()}, binary(), + public_key_info()) -> true | false. +%% +%% Description: Checks that a public_key signature is valid. +%%-------------------------------------------------------------------- +verify_signature(_Version, _Hash, {_HashAlgo, anon}, _Signature, _) -> + true; +verify_signature({3, Minor}, Hash, {HashAlgo, rsa}, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) + when Minor >= 3 -> + public_key:verify({digest, Hash}, HashAlgo, Signature, PubKey); +verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, _PubKeyParams}) -> + case public_key:decrypt_public(Signature, PubKey, + [{rsa_pad, rsa_pkcs1_padding}]) of + Hash -> true; + _ -> false + end; +verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}). + %%-------------------------------------------------------------------- -spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) -> @@ -382,31 +393,33 @@ key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, #'DHParameter'{prime = P, base = G}, - {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) -> + HashSign, ClientRandom, ServerRandom, PrivateKey}) -> <<?UINT32(_), PBin/binary>> = crypto:mpint(P), <<?UINT32(_), GBin/binary>> = crypto:mpint(G), - PLen = byte_size(PBin), - GLen = byte_size(GBin), - YLen = byte_size(PublicKey), ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, + enc_server_key_exchange(Version, ServerDHParams, HashSign, + ClientRandom, ServerRandom, PrivateKey). +enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo}, + ClientRandom, ServerRandom, PrivateKey) -> + EncParams = enc_server_key(Params), case HashAlgo of null -> - #server_key_exchange{params = ServerDHParams, - signed_params = <<>>, - hashsign = {null, anon}}; + #server_key_params{params = Params, + params_bin = EncParams, + hashsign = {null, anon}, + signature = <<>>}; _ -> Hash = server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), PBin/binary, - ?UINT16(GLen), GBin/binary, - ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey), - #server_key_exchange{params = ServerDHParams, - signed_params = Signed, - hashsign = {HashAlgo, SignAlgo}} + ServerRandom/binary, + EncParams/binary>>), + Signature = digitally_signed(Version, Hash, HashAlgo, PrivateKey), + #server_key_params{params = Params, + params_bin = EncParams, + hashsign = {HashAlgo, SignAlgo}, + signature = Signature} end. %%-------------------------------------------------------------------- @@ -523,6 +536,15 @@ decode_client_key(ClientKey, Type, Version) -> dec_client_key(ClientKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- +-spec decode_server_key(binary(), key_algo(), tls_version()) -> + #server_key_params{}. +%% +%% Description: Decode server_key data and return appropriate type +%%-------------------------------------------------------------------- +decode_server_key(ServerKey, Type, Version) -> + dec_server_key(ServerKey, key_exchange_alg(Type), Version). + +%%-------------------------------------------------------------------- -spec init_handshake_history() -> tls_handshake_history(). %% @@ -975,31 +997,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, next_protocol_negotiation = NextProtocolNegotiation}; dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; - -dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?UINT16(0)>>) -> %% 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 = <<>>, hashsign = {null, anon}}; -dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?BYTE(HashAlgo), ?BYTE(SignAlgo), - ?UINT16(Len), Sig:Len/binary>>) - when Major == 3, Minor >= 3 -> - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, - dh_y = Y}, - signed_params = Sig, - hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}}; -dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - ?UINT16(Len), Sig:Len/binary>>) -> - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, - dh_y = Y}, - signed_params = Sig, hashsign = undefined}; +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, Keys) -> + #server_key_exchange{exchange_keys = Keys}; dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary, @@ -1039,6 +1038,42 @@ dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> #client_diffie_hellman_public{dh_public = DH_Y}. +dec_ske_params(Len, Keys, Version) -> + <<Params:Len/bytes, Signature/binary>> = Keys, + dec_ske_signature(Params, Signature, Version). + +dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(0)>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, + {Params, HashSign, <<>>}; +dec_ske_signature(Params, <<?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(Len), Signature:Len/binary>>, {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}, + {Params, HashSign, Signature}; +dec_ske_signature(Params, <<>>, _) -> + {Params, {null, anon}, <<>>}; +dec_ske_signature(Params, <<?UINT16(0)>>, _) -> + {Params, {null, anon}, <<>>}; +dec_ske_signature(Params, <<?UINT16(Len), Signature:Len/binary>>, _) -> + {Params, undefined, Signature}; +dec_ske_signature(_, _, _) -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). + +dec_server_key(<<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct, + ?KEY_EXCHANGE_DIFFIE_HELLMAN, Version) -> + Params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}, + {BinMsg, HashSign, Signature} = dec_ske_params(PLen + GLen + YLen + 6, KeyStruct, Version), + #server_key_params{params = Params, + params_bin = BinMsg, + hashsign = HashSign, + signature = Signature}; +dec_server_key(_, _, _) -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). + dec_hello_extensions(<<>>) -> []; dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> @@ -1156,18 +1191,12 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> ASN1Certs = certs_from_list(ASN1CertList), ACLen = erlang:iolist_size(ASN1Certs), {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; -enc_hs(#server_key_exchange{params = #server_dh_params{ - dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams, hashsign = HashSign}, Version) -> - PLen = byte_size(P), - GLen = byte_size(G), - YLen = byte_size(Y), - Signature = enc_sign(HashSign, SignedParams, Version), - {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary, - ?UINT16(GLen), G/binary, - ?UINT16(YLen), Y/binary, - Signature/binary>> - }; +enc_hs(#server_key_exchange{exchange_keys = Keys}, _Version) -> + {?SERVER_KEY_EXCHANGE, Keys}; +enc_hs(#server_key_params{params_bin = Keys, hashsign = HashSign, + signature = Signature}, Version) -> + EncSign = enc_sign(HashSign, Signature, Version), + {?SERVER_KEY_EXCHANGE, <<Keys/binary, EncSign/binary>>}; enc_hs(#certificate_request{certificate_types = CertTypes, hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, certificate_authorities = CertAuths}, @@ -1211,6 +1240,14 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), <<?UINT16(Len), DHPublic/binary>>. +enc_server_key(#server_dh_params{dh_p = P, dh_g = G, dh_y = Y}) -> + PLen = byte_size(P), + GLen = byte_size(G), + YLen = byte_size(Y), + <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>. + +enc_sign({_, anon}, _Sign, _Version) -> + <<>>; enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor}) when Major == 3, Minor >= 3-> SignLen = byte_size(Signature), @@ -1328,8 +1365,8 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) -> digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 -> public_key:sign({digest, Hash}, HashAlgo, Key); -digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) -> - public_key:sign({digest, Hash}, sha, Key); +digitally_signed(_Version, Hash, HashAlgo, #'DSAPrivateKey'{} = Key) -> + public_key:sign({digest, Hash}, HashAlgo, Key); digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, [{rsa_pad, rsa_pkcs1_padding}]). @@ -1378,19 +1415,6 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, {SslState, UserState}} end. -certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor}) - when Major == 3, Minor >= 3 -> - public_key:verify({digest, Hashes}, sha, Signature, PublicKey); -certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor}) - when Major == 3, Minor >= 3 -> - public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey); -certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> - case public_key:decrypt_public(Signature, PublicKey, - [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> true; - _ -> false - end. - -define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). -define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 9af6511d68..2414d5b666 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -141,9 +141,14 @@ }). -record(server_key_exchange, { + exchange_keys + }). + +-record(server_key_params, { params, %% #server_rsa_params{} | #server_dh_params{} - signed_params, %% #signature{} - hashsign %% term(atom(), atom()) + params_bin, + hashsign, %% term(atom(), atom()) + signature %% #signature{} }). %% enum { anonymous, rsa, dsa } SignatureAlgorithm; |