diff options
author | Ingela Anderton Andin <ingela@erlang.org> | 2010-03-25 15:50:24 +0000 |
---|---|---|
committer | Erlang/OTP <otp@erlang.org> | 2010-03-25 15:50:24 +0000 |
commit | 995f42028bfbb939572840b7a0a38c1c39ba05fa (patch) | |
tree | 1e5cbe880c65cea0da1677cea2c46d3dcbface9f /lib | |
parent | 8b34c68c916219f7c93e6cc9bb6b5f42e4bc66e6 (diff) | |
download | otp-995f42028bfbb939572840b7a0a38c1c39ba05fa.tar.gz otp-995f42028bfbb939572840b7a0a38c1c39ba05fa.tar.bz2 otp-995f42028bfbb939572840b7a0a38c1c39ba05fa.zip |
OTP-7046 Support for Diffie-Hellman keyexchange
Diffstat (limited to 'lib')
-rw-r--r-- | lib/public_key/asn1/Makefile | 14 | ||||
-rw-r--r-- | lib/public_key/asn1/OTP-PUB-KEY.set.asn | 1 | ||||
-rw-r--r-- | lib/public_key/asn1/PKCS-3.asn1 | 21 | ||||
-rw-r--r-- | lib/public_key/src/pubkey_crypto.erl | 59 | ||||
-rw-r--r-- | lib/public_key/src/public_key.erl | 38 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 7 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 439 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 159 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.hrl | 13 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 1 | ||||
-rw-r--r-- | lib/ssl/src/ssl_ssl3.erl | 18 | ||||
-rw-r--r-- | lib/ssl/src/ssl_tls1.erl | 20 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 36 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem | 5 |
14 files changed, 573 insertions, 258 deletions
diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index fbea701be9..94abec083c 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 2008-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 2008-2010. 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% # @@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) ASN_TOP = OTP-PUB-KEY ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ - PKIXAttributeCertificate OTP-PKIX + PKIXAttributeCertificate PKCS-1 PKCS-3 OTP-PKIX ASN_ASNS = $(ASN_MODULES:%=%.asn1) ASN_ERLS = $(ASN_TOP).erl ASN_HRLS = $(ASN_TOP).hrl @@ -110,4 +110,6 @@ OTP-PUB-KEY.asn1db: PKIX1Algorithms88.asn1 \ PKIX1Explicit88.asn1 \ PKIX1Implicit88.asn1 \ PKIXAttributeCertificate.asn1 \ + PKCS-1.asn1\ + PKCS-3.asn1\ OTP-PKIX.asn1 diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn index 2f9ccd6b0e..5c76d13115 100644 --- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn +++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn @@ -4,4 +4,5 @@ PKIX1Implicit88.asn1 PKIXAttributeCertificate.asn1 PKIX1Algorithms88.asn1 PKCS-1.asn1 +PKCS-3.asn1 DSS.asn1 diff --git a/lib/public_key/asn1/PKCS-3.asn1 b/lib/public_key/asn1/PKCS-3.asn1 new file mode 100644 index 0000000000..64180b3a85 --- /dev/null +++ b/lib/public_key/asn1/PKCS-3.asn1 @@ -0,0 +1,21 @@ +PKCS-3 { + iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) 3 +} + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +pkcs-3 OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) 3 } + +dhKeyAgreement OBJECT IDENTIFIER ::= { pkcs-3 1 } + +DHParameter ::= SEQUENCE { + prime INTEGER, -- p + base INTEGER, -- g + privateValueLength INTEGER OPTIONAL } + +END
\ No newline at end of file diff --git a/lib/public_key/src/pubkey_crypto.erl b/lib/public_key/src/pubkey_crypto.erl index fe4e97fcc5..4ab655e977 100644 --- a/lib/public_key/src/pubkey_crypto.erl +++ b/lib/public_key/src/pubkey_crypto.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2008-2010. 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% %% @@ -26,7 +26,7 @@ -export([encrypt_public/3, decrypt_private/3, encrypt_private/3, decrypt_public/3, - sign/2, sign/3, verify/5]). + sign/2, sign/3, verify/5, gen_key/2]). -define(UINT32(X), X:32/unsigned-big-integer). @@ -44,10 +44,14 @@ %% %% Description: Public key encrypts PlainText. %%-------------------------------------------------------------------- -encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E},Padding) -> - crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],Padding); -encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E},Padding) -> - crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],Padding). +encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E}, + Padding) -> + crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)], + Padding); +encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E}, + Padding) -> + crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)], + Padding). encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N, publicExponent = E, @@ -67,15 +71,20 @@ encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N, %% Description: Uses private key to decrypt public key encrypted data. %%-------------------------------------------------------------------- decrypt_private(CipherText, - #'RSAPrivateKey'{modulus = N,publicExponent = E,privateExponent = D}, + #'RSAPrivateKey'{modulus = N,publicExponent = E, + privateExponent = D}, Padding) -> crypto:rsa_private_decrypt(CipherText, - [crypto:mpint(E), crypto:mpint(N),crypto:mpint(D)], - Padding). -decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E}, Padding) -> - crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], Padding); -decrypt_public(CipherText, #'RSAPrivateKey'{modulus = N, publicExponent = E}, Padding) -> - crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], Padding). + [crypto:mpint(E), crypto:mpint(N), + crypto:mpint(D)], Padding). +decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E}, + Padding) -> + crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], + Padding); +decrypt_public(CipherText, #'RSAPrivateKey'{modulus = N, publicExponent = E}, + Padding) -> + crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)], + Padding). %%-------------------------------------------------------------------- %% Function: sign(PlainText, Key) -> @@ -125,10 +134,24 @@ verify(sha, PlainText, Signature, Key, #'Dss-Parms'{p = P, q = Q, g = G}) -> [crypto:mpint(P), crypto:mpint(Q), crypto:mpint(G), crypto:mpint(Key)]). + +%%-------------------------------------------------------------------- +%% Function: gen_key(Type, Params) -> +%% Type = diffie_hellman +%% Params = [P,G] | [Y, P, G] +%% Description: Generates keys. +%% ----------------------------------------------------------------- +gen_key(diffie_hellman, [Y, P, G]) -> + crypto:dh_generate_key(crypto:mpint(Y), [crypto:mpint(P), + crypto:mpint(G)]); +gen_key(diffie_hellman, [P, G]) -> + crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]). + +%%% TODO: Support rsa, dss key_gen + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - sized_binary(Binary) when is_binary(Binary) -> Size = size(Binary), <<?UINT32(Size), Binary/binary>>; diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 52c695523f..9a90ffe888 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -23,11 +23,10 @@ -include("public_key.hrl"). --export([decode_private_key/1, decode_private_key/2, +-export([decode_private_key/1, decode_private_key/2, decode_dhparams/1, decrypt_private/2, decrypt_private/3, encrypt_public/2, encrypt_public/3, decrypt_public/2, decrypt_public/3, - encrypt_private/2, encrypt_private/3, - sign/2, sign/3, + encrypt_private/2, encrypt_private/3, gen_key/1, sign/2, sign/3, verify_signature/3, verify_signature/4, verify_signature/5, pem_to_der/1, pem_to_der/2, pkix_decode_cert/2, pkix_encode_cert/1, pkix_transform/2, @@ -62,6 +61,21 @@ decode_private_key(KeyInfo = {dsa_private_key, _, _}, Password) -> DerEncoded = pubkey_pem:decode_key(KeyInfo, Password), 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded). + +%%-------------------------------------------------------------------- +%% Function: decode_dhparams(DhParamInfo) -> +%% {ok, DhParams} | {error, Reason} +%% +%% DhParamsInfo = {Type, der_bin(), ChipherInfo} - as returned from +%% pem_to_der/[1,2] for DH parameters. +%% Type = dh_params +%% ChipherInfo = opaque() | no_encryption +%% +%% Description: Decodes an asn1 der encoded DH parameters. +%%-------------------------------------------------------------------- +decode_dhparams({dh_params, DerEncoded, not_encrypted}) -> + 'OTP-PUB-KEY':decode('DHParameter', DerEncoded). + %%-------------------------------------------------------------------- %% Function: decrypt_private(CipherText, Key) -> %% decrypt_private(CipherText, Key, Options) -> PlainTex @@ -109,6 +123,18 @@ encrypt_private(PlainText, Key, Options) -> pubkey_crypto:encrypt_private(PlainText, Key, Padding). %%-------------------------------------------------------------------- +%% Function: gen_key(Params) -> Keys +%% +%% Params = #'DomainParameters'{} - Currently only supported option +%% Keys = {PublicDHKey = integer(), PrivateDHKey = integer()} +%% +%% Description: Generates keys. Currently supports Diffie-Hellman keys. +%%-------------------------------------------------------------------- +gen_key(#'DHParameter'{prime = P, base = G}) when is_integer(P), + is_integer(G) -> + pubkey_crypto:gen_key(diffie_hellman, [P, G]). + +%%-------------------------------------------------------------------- %% Function: pem_to_der(CertSource) -> %% pem_to_der(CertSource, Password) -> {ok, [Entry]} | %% {error, Reason} @@ -116,7 +142,6 @@ encrypt_private(PlainText, Key, Options) -> %% CertSource = File | CertData %% CertData = binary() %% File = path() -%% Password = string() %% Entry = {entry_type(), der_bin(), ChipherInfo} %% ChipherInfo = opague() | no_encryption %% der_bin() = binary() @@ -127,7 +152,9 @@ encrypt_private(PlainText, Key, Options) -> %% entries as asn1 der encoded entities. Currently supported entry %% types are certificates, certificate requests, rsa private keys and %% dsa private keys. In the case of a key entry ChipherInfo will be -%% used by decode_private_key/2 if the key is protected by a password. +%% private keys and Diffie Hellam parameters .In the case of a key +%% entry ChipherInfo will be used by decode_private_key/2 if the key +%% is protected by a password. %%-------------------------------------------------------------------- pem_to_der(CertSource) -> pem_to_der(CertSource, no_passwd). @@ -136,7 +163,6 @@ pem_to_der(File, Password) when is_list(File) -> pubkey_pem:read_file(File, Password); pem_to_der(PemBin, Password) when is_binary(PemBin) -> pubkey_pem:decode(PemBin, Password). - %%-------------------------------------------------------------------- %% Function: pkix_decode_cert(BerCert, Type) -> {ok, Cert} | {error, Reason} %% diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index cb678e3f53..87a0939897 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -549,6 +549,7 @@ handle_options(Opts0, Role) -> key = handle_option(key, Opts, undefined), password = handle_option(password, Opts, ""), cacertfile = handle_option(cacertfile, Opts, CaCertDefault), + dhfile = handle_option(dhfile, Opts, undefined), ciphers = handle_option(ciphers, Opts, []), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), @@ -560,7 +561,7 @@ handle_options(Opts0, Role) -> CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}), SslOptions = [versions, verify, verify_fun, depth, certfile, keyfile, - key, password, cacertfile, ciphers, + key, password, cacertfile, dhfile, ciphers, debug, reuse_session, reuse_sessions, ssl_imp, cd_info, renegotiate_at], @@ -612,6 +613,10 @@ validate_option(cacertfile, undefined) -> ""; validate_option(cacertfile, Value) when is_list(Value), Value =/= "" -> Value; +validate_option(dhfile, undefined = Value) -> + Value; +validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> + Value; validate_option(ciphers, Value) when is_list(Value) -> Version = ssl_record:highest_protocol_version([]), try cipher_suites(Version, Value) diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 3ede21d377..a78b0257f5 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -47,8 +47,8 @@ -export([start_link/7]). %% gen_fsm callbacks --export([init/1, hello/2, certify/2, cipher/2, connection/2, abbreviated/2, - handle_event/3, +-export([init/1, hello/2, certify/2, cipher/2, connection/2, connection/3, + abbreviated/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -record(state, { @@ -77,8 +77,9 @@ client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} - private_key, % PKIX: 'RSAPrivateKey' - diffie_hellman_params, % + private_key, % PKIX: #'RSAPrivateKey'{} + diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side + diffie_hellman_keys, % {PublicKey, PrivateKey} premaster_secret, % cert_db_ref, % ets_table() from, % term(), where to reply @@ -91,6 +92,10 @@ send_queue % queue() }). +-define(DEFAULT_DIFFIE_HELLMAN_PARAMS, + #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, + base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). + %%==================================================================== %% Internal application API %%==================================================================== @@ -101,9 +106,11 @@ %% Description: %%-------------------------------------------------------------------- send(Pid, Data) -> - sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity). + sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, + infinity). send(Pid, Data, Timeout) -> - sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout). + sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, + Timeout). %%-------------------------------------------------------------------- %% Function: %% @@ -234,7 +241,6 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options, User, CbInfo], []). - %%==================================================================== %% gen_fsm callbacks %%==================================================================== @@ -253,12 +259,13 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, Hashes0 = ssl_handshake:init_hashes(), try ssl_init(SSLOpts, Role) of - {ok, Ref, CacheRef, OwnCert, Key} -> + {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> State = State0#state{tls_handshake_hashes = Hashes0, own_cert = OwnCert, cert_db_ref = Ref, session_cache = CacheRef, - private_key = Key}, + private_key = Key, + diffie_hellman_params = DHParams}, {ok, hello, State} catch throw:Error -> @@ -271,18 +278,20 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options, %% {next_state, NextStateName, %% NextState, Timeout} | %% {stop, Reason, NewState} -%% Description:There should be one instance of this function for each possible -%% state name. Whenever a gen_fsm receives an event sent using -%% gen_fsm:send_event/2, the instance of this function with the same name as -%% the current state name StateName is called to handle the event. It is also -%% called if a timeout occurs. +%% +%% Description:There should be one instance of this function for each +%% possible state name. Whenever a gen_fsm receives an event sent +%% using gen_fsm:send_event/2, the instance of this function with the +%% same name as the current state name StateName is called to handle +%% the event. It is also called if a timeout occurs. %%-------------------------------------------------------------------- hello(socket_control, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, SslOpts), + Hello = ssl_handshake:client_hello(Host, Port, + ConnectionStates, SslOpts), Version = Hello#client_hello.client_version, Hashes0 = ssl_handshake:init_hashes(), {BinMsg, CS2, Hashes1} = @@ -311,13 +320,14 @@ hello(#server_hello{cipher_suite = CipherSuite, host = Host, port = Port, session_cache = Cache, session_cache_cb = CacheCb} = State0) -> + {Version, NewId, ConnectionStates1} = ssl_handshake:hello(Hello, ConnectionStates0), {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), - PremasterSecret = make_premaster_secret(ReqVersion), + PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), State = State0#state{key_algorithm = KeyAlgorithm, negotiated_version = Version, @@ -422,34 +432,35 @@ certify(#certificate{asn1_certificates = []}, ssl_options = #ssl_options{verify = verify_peer, fail_if_no_peer_cert = false}} = State) -> - {next_state, certify, next_record(State#state{client_certificate_requested = false})}; + {next_state, certify, + next_record(State#state{client_certificate_requested = false})}; certify(#certificate{} = Cert, - #state{session = Session, - negotiated_version = Version, + #state{negotiated_version = Version, cert_db_ref = CertDbRef, - ssl_options = Opts} = State0) -> + ssl_options = Opts} = State) -> case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, Opts#ssl_options.verify_fun) of {PeerCert, PublicKeyInfo} -> - State = State0#state{session = - Session#session{peer_certificate = PeerCert}, - public_key_info = PublicKeyInfo, - client_certificate_requested = false - }, - {next_state, certify, next_record(State)}; + handle_peer_cert(PeerCert, PublicKeyInfo, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify_certificate, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify_certificate, State), + {stop, normal, State} end; certify(#server_key_exchange{} = KeyExchangeMsg, - #state{role = client, - key_algorithm = Alg} = State) - when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; Alg == krb5 -> - NewState = handle_server_key(KeyExchangeMsg, State), - {next_state, certify, NewState}; + #state{role = client, negotiated_version = Version, + key_algorithm = Alg} = State0) + when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 -> + case handle_server_key(KeyExchangeMsg, State0) of + #state{} = State -> + {next_state, certify, next_record(State)}; + #alert{} = Alert -> + handle_own_alert(Alert, Version, certify_server_keyexchange, + State0), + {stop, normal, State0} + end; certify(#server_key_exchange{}, State = #state{role = client, negotiated_version = Version, @@ -459,15 +470,30 @@ certify(#server_key_exchange{}, handle_own_alert(Alert, Version, certify_server_key_exchange, State), {stop, normal, State}; -certify(KeyExchangeMsg = #server_key_exchange{}, State = - #state{role = server}) -> - NewState = handle_clinet_key(KeyExchangeMsg, State), - {next_state, cipher, NewState}; - certify(#certificate_request{}, State) -> NewState = State#state{client_certificate_requested = true}, {next_state, certify, next_record(NewState)}; + +%% Master secret was determined with help of server-key exchange msg +certify(#server_hello_done{}, + #state{session = #session{master_secret = MasterSecret} = Session, + connection_states = ConnectionStates0, + negotiated_version = Version, + premaster_secret = undefined, + role = client} = State0) -> + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, client) of + {MasterSecret, ConnectionStates1} -> + State = State0#state{connection_states = ConnectionStates1}, + client_certify_and_key_exchange(State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, + certify_server_hello_done, State0), + {stop, normal, State0} + end; + +%% Master secret is calculated from premaster_secret certify(#server_hello_done{}, #state{session = Session0, connection_states = ConnectionStates0, @@ -494,7 +520,8 @@ certify(#client_key_exchange{}, negotiated_version = Version}) -> %% We expect a certificate here Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE), - handle_own_alert(Alert, Version, certify_server_waiting_certificate, State), + handle_own_alert(Alert, Version, + certify_server_waiting_certificate, State), {stop, normal, State}; @@ -524,6 +551,36 @@ certify(#client_key_exchange{exchange_keys handle_own_alert(Alert, Version, certify_client_key_exchange, State0), {stop, normal, State0} + end; + +certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ + dh_public = ClientPublicDhKey}}, + #state{negotiated_version = Version, + diffie_hellman_params = #'DHParameter'{prime = P, + base = G}, + diffie_hellman_keys = {_, ServerDhPrivateKey}, + role = Role, + session = Session, + connection_states = ConnectionStates0} = State0) -> + + PMpint = crypto:mpint(P), + GMpint = crypto:mpint(G), + PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey), + ServerDhPrivateKey, + [PMpint, GMpint]), + + case ssl_handshake:master_secret(Version, PremasterSecret, + ConnectionStates0, Role) of + {MasterSecret, ConnectionStates} -> + State = State0#state{session = + Session#session{master_secret + = MasterSecret}, + connection_states = ConnectionStates}, + {next_state, cipher, next_record(State)}; + #alert{} = Alert -> + handle_own_alert(Alert, Version, + certify_client_key_exchange, State0), + {stop, normal, State0} end. cipher(socket_control, #state{role = server} = State) -> @@ -621,28 +678,15 @@ handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, tls_handshake_buffer = Buf0, negotiated_version = Version}) -> Handle = - 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}); - ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> - %% This message should not be included in handshake - %% message hashes. If allready in negotiation 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, - renegotiation = true}); - ({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}); + fun({Packet, Raw}, {next_state, SName, + AS=#state{tls_handshake_hashes=Hs0}}) -> (_, StopState) -> StopState end, try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version), - Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}}, + {Packets, Buf} = + ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version), + Start = {next_state, StateName, + State#state{tls_handshake_buffer = Buf}}, lists:foldl(Handle, Start, Packets) catch throw:#alert{} = Alert -> handle_own_alert(Alert, Version, StateName, State0), @@ -675,7 +719,8 @@ handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) -> {next_state, StateName, State}; handle_event(#alert{level = ?FATAL} = Alert, connection, - #state{from = From, user_application = {_Mon, Pid}, log_alert = Log, + #state{from = From, user_application = {_Mon, Pid}, + log_alert = Log, host = Host, port = Port, session = Session, role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), @@ -952,7 +997,8 @@ terminate(_Reason, connection, _S=#state{negotiated_version = Version, Version, ConnectionStates), Transport:send(Socket, BinAlert), Transport:close(Socket); -terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, socket = Socket}) -> +terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, + socket = Socket}) -> Transport:close(Socket), ok. @@ -989,8 +1035,8 @@ ssl_init(SslOpts, Role) -> PrivateKey = init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - ?DBG_TERM(PrivateKey), - {ok, CertDbRef, CacheRef, OwnCert, PrivateKey}. + DHParams = init_diffie_hellman(SslOpts#ssl_options.dhfile, Role), + {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}. init_certificates(#ssl_options{cacertfile = CACertFile, certfile = CertFile}, Role) -> @@ -1025,12 +1071,14 @@ init_certificates(CertDbRef, CacheRef, CertFile, server) -> catch _E:{badmatch, _R={error,_}} -> Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, CertFile, erlang:get_stacktrace()]), + [?LINE, _E,_R, CertFile, + erlang:get_stacktrace()]), error_logger:error_report(Report), throw(ecertfile); _E:_R -> Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, CertFile, erlang:get_stacktrace()]), + [?LINE, _E,_R, CertFile, + erlang:get_stacktrace()]), error_logger:error_report(Report), throw(ecertfile) end. @@ -1041,24 +1089,41 @@ init_private_key(undefined, KeyFile, Password, _) -> try {ok, List} = ssl_manager:cache_pem_file(KeyFile), [Der] = [Der || Der = {PKey, _ , _} <- List, - PKey =:= rsa_private_key orelse PKey =:= dsa_private_key], + PKey =:= rsa_private_key orelse + PKey =:= dsa_private_key], {ok, Decoded} = public_key:decode_private_key(Der,Password), Decoded catch _E:{badmatch, _R={error,_}} -> Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, KeyFile, erlang:get_stacktrace()]), + [?LINE, _E,_R, KeyFile, + erlang:get_stacktrace()]), error_logger:error_report(Report), throw(ekeyfile); _E:_R -> Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n", - [?LINE, _E,_R, KeyFile, erlang:get_stacktrace()]), + [?LINE, _E,_R, KeyFile, + erlang:get_stacktrace()]), error_logger:error_report(Report), throw(ekeyfile) end; init_private_key(PrivateKey, _, _,_) -> PrivateKey. +init_diffie_hellman(_, client) -> + undefined; +init_diffie_hellman(undefined, _) -> + ?DEFAULT_DIFFIE_HELLMAN_PARAMS; +init_diffie_hellman(DHParamFile, server) -> + {ok, List} = ssl_manager:cache_pem_file(DHParamFile), + case [Der || Der = {dh_params, _ , _} <- List] of + [Der] -> + {ok, Decoded} = public_key:decode_dhparams(Der), + Decoded; + [] -> + ?DEFAULT_DIFFIE_HELLMAN_PARAMS + end. + send_event(FsmPid, Event) -> gen_fsm:send_event(FsmPid, Event). @@ -1084,6 +1149,44 @@ sync_send_all_state_event(FsmPid, Event, Timeout) -> alert_event(Alert) -> send_all_state_event(self(), Alert). + +%% TODO: This clause is not yet supported. Do we need +%% to support it, not supported by openssl! +handle_peer_cert(PeerCert, {Algorithm, _, _} = PublicKeyInfo, + #state{negotiated_version = Version, + session = Session} = + State0) when Algorithm == dh_rsa; + Algorithm == dh_dss -> + case public_key:pkix_is_fixed_dh_cert(PeerCert) of + true -> + %% TODO: extract DH-params from cert and save + %% Keys and SharedSecret in state. + %% {P, G, ServerPublicDhKey}= extract ..... + %% Keys = {ClientDhPublicKey, _} = + %% public_key:gen_key(#'DHParameter'{prime = P, base = G}), + %%SharedSecret = dh_shared_secret(ServerPublicDhKey, + %% ClientDhPublicKey, + %% [P, G]), + %% ssl_handshake:master_secret ... + State = State0#state{session = + Session#session{peer_certificate + = PeerCert}, + public_key_info = PublicKeyInfo}, + {next_state, certify, next_record(State)}; + + false -> + Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), + handle_own_alert(Alert, Version, certify_certificate, State0), + {stop, normal, State0} + end; + +handle_peer_cert(PeerCert, PublicKeyInfo, + #state{session = Session} = State0) -> + State = State0#state{session = + Session#session{peer_certificate = PeerCert}, + public_key_info = PublicKeyInfo}, + {next_state, certify, next_record(State)}. + certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, @@ -1117,9 +1220,8 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, ignore -> %% No key or cert or fixed_diffie_hellman State; Verified -> - SigAlg = ssl_handshake:sig_alg(KeyAlg), {BinVerified, ConnectionStates1, Hashes1} = - encode_handshake(Verified, SigAlg, Version, + encode_handshake(Verified, KeyAlg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinVerified), State#state{connection_states = ConnectionStates1, @@ -1146,8 +1248,10 @@ do_server_hello(Type, #state{negotiated_version = Version, {_, ConnectionStates1} -> State1 = State#state{connection_states=ConnectionStates1, session = Session}, - {ConnectionStates, Hashes} = finalize_server_handshake(State1), - Resumed = State1#state{connection_states = ConnectionStates, + {ConnectionStates, Hashes} = + finalize_server_handshake(State1), + Resumed = State1#state{connection_states = + ConnectionStates, tls_handshake_hashes = Hashes}, {next_state, abbreviated, next_record(Resumed)}; #alert{} = Alert -> @@ -1266,6 +1370,7 @@ key_exchange(#state{role = server, key_algorithm = rsa_export} = State) -> key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_params = Params, + private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, tls_handshake_hashes = Hashes0, @@ -1276,26 +1381,38 @@ key_exchange(#state{role = server, key_algorithm = Algo, Algo == dhe_dss_export; Algo == dhe_rsa; Algo == dhe_rsa_export -> - Msg = ssl_handshake:key_exchange(server, Params), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), - Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; -key_exchange(#state{role = server, key_algorithm = dh_anon, - connection_states = ConnectionStates0, - negotiated_version = Version, - tls_handshake_hashes = Hashes0, - socket = Socket, - transport_cb = Transport - } = State) -> - Msg = ssl_handshake:key_exchange(server, anonymous), - {BinMsg, ConnectionStates1, Hashes1} = + Keys = public_key:gen_key(Params), + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params, + Algo, ClientRandom, + ServerRandom, + PrivateKey}), + {BinMsg, ConnectionStates, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, + State#state{connection_states = ConnectionStates, + diffie_hellman_keys = Keys, tls_handshake_hashes = Hashes1}; + +%% TODO: Not yet supported should be by default disabled when supported. +%% key_exchange(#state{role = server, key_algorithm = dh_anon, +%% connection_states = ConnectionStates0, +%% negotiated_version = Version, +%% tls_handshake_hashes = Hashes0, +%% socket = Socket, +%% transport_cb = Transport +%% } = State) -> +%% Msg = ssl_handshake:key_exchange(server, anonymous), +%% {BinMsg, ConnectionStates1, Hashes1} = +%% encode_handshake(Msg, Version, ConnectionStates0, Hashes0), +%% Transport:send(Socket, BinMsg), +%% State#state{connection_states = ConnectionStates1, +%% tls_handshake_hashes = Hashes1}; key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1315,17 +1432,33 @@ key_exchange(#state{role = client, key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = Algorithm, - public_key_info = PublicKeyInfo, negotiated_version = Version, - diffie_hellman_params = Params, - own_cert = Cert, + diffie_hellman_keys = {DhPubKey, _}, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; Algorithm == dhe_dss_export; Algorithm == dhe_rsa; Algorithm == dhe_rsa_export -> - Msg = dh_key_exchange(Cert, Params, PublicKeyInfo), + Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), + {BinMsg, ConnectionStates1, Hashes1} = + encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + Transport:send(Socket, BinMsg), + State#state{connection_states = ConnectionStates1, + tls_handshake_hashes = Hashes1}; + +key_exchange(#state{role = client, + connection_states = ConnectionStates0, + key_algorithm = Algorithm, + negotiated_version = Version, + client_certificate_requested = ClientCertReq, + own_cert = OwnCert, + diffie_hellman_keys = DhKeys, + socket = Socket, transport_cb = Transport, + tls_handshake_hashes = Hashes0} = State) + when Algorithm == dh_dss; + Algorithm == dh_rsa -> + Msg = dh_key_exchange(OwnCert, DhKeys, ClientCertReq), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), @@ -1340,17 +1473,19 @@ rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) ssl_handshake:key_exchange(client, {premaster_secret, PremasterSecret, PublicKeyInfo}); - rsa_key_exchange(_, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). -dh_key_exchange(OwnCert, Params, PublicKeyInfo) -> +dh_key_exchange(OwnCert, DhKeys, true) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> ssl_handshake:key_exchange(client, fixed_diffie_hellman); false -> - ssl_handshake:key_exchange(client, {dh, Params, PublicKeyInfo}) - end. + {DhPubKey, _} = DhKeys, + ssl_handshake:key_exchange(client, {dh, DhPubKey}) + end; +dh_key_exchange(_, {DhPubKey, _}, false) -> + ssl_handshake:key_exchange(client, {dh, DhPubKey}). request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, @@ -1384,7 +1519,8 @@ finalize_client_handshake(#state{connection_states = ConnectionStates0} finalize_server_handshake(State) -> ConnectionStates0 = cipher_protocol(State), ConnectionStates = - ssl_record:activate_pending_connection_state(ConnectionStates0, write), + ssl_record:activate_pending_connection_state(ConnectionStates0, + write), finished(State#state{connection_states = ConnectionStates}). cipher_protocol(#state{connection_states = ConnectionStates, @@ -1392,7 +1528,8 @@ cipher_protocol(#state{connection_states = ConnectionStates, negotiated_version = Version, transport_cb = Transport}) -> {BinChangeCipher, NewConnectionStates} = - encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates), + encode_change_cipher(#change_cipher_spec{}, + Version, ConnectionStates), Transport:send(Socket, BinChangeCipher), NewConnectionStates. @@ -1408,10 +1545,69 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, Transport:send(Socket, BinFinished), {NewConnectionStates, NewHashes}. -handle_server_key(_KeyExchangeMsg, State) -> - State. -handle_clinet_key(_KeyExchangeMsg, State) -> - State. +handle_server_key( + #server_key_exchange{params = + #server_dh_params{dh_p = P, + dh_g = G, + dh_y = ServerPublicDhKey}, + signed_params = Signed}, + #state{session = Session, negotiated_version = Version, role = Role, + public_key_info = PubKeyInfo, + key_algorithm = KeyAlgo, + connection_states = ConnectionStates0} = State) -> + + PLen = size(P), + GLen = size(G), + YLen = size(ServerPublicDhKey), + + ConnectionState = + ssl_record:pending_connection_state(ConnectionStates0, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo, + <<ClientRandom/binary, + ServerRandom/binary, + ?UINT16(PLen), P/binary, + ?UINT16(GLen), G/binary, + ?UINT16(YLen), + ServerPublicDhKey/binary>>), + + case verify_dh_params(Signed, Hash, PubKeyInfo) of + true -> + PMpint = mpint_binary(P), + GMpint = mpint_binary(G), + Keys = {_, ClientDhPrivateKey} = + crypto:dh_generate_key([PMpint,GMpint]), + PremasterSecret = + crypto:dh_compute_key(mpint_binary(ServerPublicDhKey), + ClientDhPrivateKey, [PMpint, GMpint]), + case ssl_handshake:master_secret(Version, PremasterSecret, + ConnectionStates0, Role) of + {MasterSecret, ConnectionStates} -> + State#state{diffie_hellman_keys = Keys, + session = + Session#session{master_secret + = MasterSecret}, + connection_states = ConnectionStates}; + #alert{} = Alert -> + Alert + end; + false -> + ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE) + end. + +%%handle_clinet_key(_KeyExchangeMsg, State) -> +%% State. + +verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) -> + case public_key:decrypt_public(Signed, PubKey, + [{rsa_pad, rsa_pkcs1_padding}]) of + Hash -> + true; + _ -> + false + end. encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ?DBG_TERM(Alert), @@ -1754,7 +1950,8 @@ set_socket_opts(Socket, [], SockOpts, Other) -> inet:setopts(Socket, Other), SockOpts; set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) -> - set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other); + set_socket_opts(Socket, Opts, + SockOpts#socket_options{mode = Mode}, Other); set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) -> set_socket_opts(Socket, Opts, SockOpts#socket_options{packet = Packet}, Other); @@ -1810,34 +2007,14 @@ handle_own_alert(Alert, Version, StateName, ok end. -make_premaster_secret({MajVer, MinVer}) -> +make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa; + Alg == dh_dss; + Alg == dh_rsa -> Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), - <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>. + <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>; +make_premaster_secret(_, _) -> + undefined. - -ack_connection(#state{renegotiation = true}) -> - ok; -ack_connection(#state{renegotiation = false, from = From}) -> - gen_fsm:reply(From, connected). - - -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}); -renegotiate(#state{role = server, - socket = Socket, - transport_cb = Transport, - negotiated_version = Version, - connection_states = ConnectionStates0} = State) -> - HelloRequest = ssl_handshake:hello_request(), - Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined), - Hs0 = ssl_handshake:init_hashes(), - {BinMsg, ConnectionStates} = - ssl_record:encode_handshake(Frag, Version, ConnectionStates0), - Transport:send(Socket, BinMsg), - {next_state, hello, next_record(State#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hs0, - renegotiation = true})}. +mpint_binary(Binary) -> + Size = byte_size(Binary), + <<?UINT32(Size), Binary/binary>>. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 4e1c495fda..ff78375b9f 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -35,7 +35,7 @@ hello_request/0, certify/5, certificate/3, client_certificate_verify/6, certificate_verify/6, certificate_request/2, - key_exchange/2, finished/4, + key_exchange/2, server_key_exchange_hash/2, finished/4, verify_connection/5, get_tls_handshake/4, server_hello_done/0, sig_alg/1, @@ -257,8 +257,10 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- certificate_verify(Signature, {_, PublicKey, _}, Version, - MasterSecret, Algorithm, {_, Hashes0}) - when Algorithm =:= rsa; Algorithm =:= dh_rsa; Algorithm =:= dhe_rsa -> + MasterSecret, Algorithm, {_, Hashes0}) + when Algorithm == rsa; + Algorithm == dh_rsa; + Algorithm == dhe_rsa -> Hashes = calc_certificate_verify(Version, MasterSecret, Algorithm, Hashes0), case public_key:decrypt_public(Signature, PublicKey, @@ -305,30 +307,32 @@ key_exchange(client, fixed_diffie_hellman) -> #client_diffie_hellman_public{ dh_public = <<>> }}; -key_exchange(client, {dh, PublicKey}) -> - Len = byte_size(PublicKey), +key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> #client_key_exchange{ - exchange_keys = #client_diffie_hellman_public{ - dh_public = <<?UINT16(Len), PublicKey/binary>>} + exchange_keys = #client_diffie_hellman_public{ + dh_public = PublicKey} }; -%% key_exchange(server, {{?'dhpublicnumber', _PublicKey, -%% #'DomainParameters'{p = P, g = G, y = Y}, -%% SignAlgorithm, ClientRandom, ServerRandom}}) -> -%% ServerDHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}, -%% PLen = byte_size(P), -%% GLen = byte_size(G), -%% YLen = byte_size(Y), -%% Hash = server_key_exchange_hash(SignAlgorithm, <<ClientRandom/binary, -%% ServerRandom/binary, -%% ?UINT16(PLen), P/binary, -%% ?UINT16(GLen), G/binary, -%% ?UINT16(YLen), Y/binary>>), -%% Signed = digitally_signed(Hash, PrivateKey), -%% #server_key_exchange{ -%% params = ServerDHParams, -%% signed_params = Signed -%% }; +key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _}, + #'DHParameter'{prime = P, base = G}, + KeyAlgo, 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}, + + Hash = + server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, + ServerRandom/binary, + ?UINT16(PLen), PBin/binary, + ?UINT16(GLen), GBin/binary, + ?UINT16(YLen), PublicKey/binary>>), + Signed = digitally_signed(Hash, PrivateKey), + #server_key_exchange{params = ServerDHParams, + signed_params = Signed}; key_exchange(_, _) -> %%TODO : Real imp #server_key_exchange{}. @@ -426,7 +430,8 @@ server_hello_done() -> %% %% encode a handshake packet to binary %%-------------------------------------------------------------------- -encode_handshake(Package, Version, SigAlg) -> +encode_handshake(Package, Version, KeyAlg) -> + SigAlg = sig_alg(KeyAlg), {MsgType, Bin} = enc_hs(Package, Version, SigAlg), Len = byte_size(Bin), [MsgType, ?uint24(Len), Bin]. @@ -446,32 +451,16 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) -> get_tls_handshake_aux(list_to_binary([Buffer, Data]), KeyAlg, Version, []). -get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, - KeyAlg, Version, Acc) -> +get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), + Body:Length/binary,Rest/binary>>, KeyAlg, + Version, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - H = dec_hs(Type, Body, KeyAlg, Version), + H = dec_hs(Type, Body, key_excahange_alg(KeyAlg), Version), get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]); get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> {lists:reverse(Acc), Data}. %%-------------------------------------------------------------------- -%% Function: sig_alg(atom()) -> integer() -%% -%% Description: Convert from key exchange as atom to signature -%% algorithm as a ?SIGNATURE_... constant -%%-------------------------------------------------------------------- - -sig_alg(dh_anon) -> - ?SIGNATURE_ANONYMOUS; -sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa -> - ?SIGNATURE_RSA; -sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss -> - ?SIGNATURE_DSA; -sig_alg(_) -> - ?NULL. - - -%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- verify_bool(verify_peer) -> @@ -658,8 +647,8 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary, - ?UINT16(ExpLen), Exp:ExpLen/binary, - Sig/binary>>, + ?UINT16(ExpLen), Exp:ExpLen/binary, + ?UINT16(_), Sig/binary>>, ?KEY_EXCHANGE_RSA, _) -> #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod, rsa_exponent = Exp}, @@ -667,9 +656,10 @@ dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary, dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, - Sig/binary>>, + ?UINT16(_), Sig/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, + #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, + dh_y = Y}, signed_params = Sig}; dec_hs(?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, @@ -681,18 +671,20 @@ dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) -> #server_hello_done{}; dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)-> #certificate_verify{signature = Signature}; -dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, rsa, {3, 0}) -> +dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS}, #client_key_exchange{exchange_keys = PreSecret}; -dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, rsa, _) -> +dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, + ?KEY_EXCHANGE_RSA, _) -> PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS}, #client_key_exchange{exchange_keys = PreSecret}; dec_hs(?CLIENT_KEY_EXCHANGE, <<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> %% TODO: Should check whether the cert already contains a suitable DH-key (7.4.7.2) throw(?ALERT_REC(?FATAL, implicit_public_value_encoding)); -dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YCLen), DH_YC:DH_YCLen/binary>>, +dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) -> - #client_diffie_hellman_public{dh_public = DH_YC}; + #client_key_exchange{exchange_keys = + #client_diffie_hellman_public{dh_public = DH_Y}}; dec_hs(?FINISHED, VerifyData, _, _) -> #finished{verify_data = VerifyData}; dec_hs(_, _, _, _) -> @@ -765,12 +757,13 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) -> {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; enc_hs(#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod, rsa_exponent = Exp}, - signed_params = SignedParams}, _Version, _) -> + signed_params = SignedParams}, _Version, _) -> ModLen = byte_size(Mod), ExpLen = byte_size(Exp), - {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod/binary, + SignedLen = byte_size(SignedParams), + {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary, ?UINT16(ExpLen), Exp/binary, - SignedParams/binary>> + ?UINT16(SignedLen), SignedParams/binary>> }; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, @@ -778,10 +771,11 @@ enc_hs(#server_key_exchange{params = #server_dh_params{ PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), - {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, - ?UINT16(GLen), G:GLen/binary, - ?UINT16(YLen), Y:YLen/binary, - SignedParams/binary>> + SignedLen = byte_size(SignedParams), + {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary, + ?UINT16(GLen), G/binary, + ?UINT16(YLen), Y/binary, + ?UINT16(SignedLen), SignedParams/binary>> }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, @@ -936,13 +930,40 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes) when N == 1; N == 2 -> ssl_tls1:certificate_verify(Algorithm, Hashes). -%% server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; -%% Algorithm == dh_rsa; -%% Algorithm == dhe_rsa -> -%% MD5 = crypto:md5_final(Value), -%% SHA = crypto:sha_final(Value), -%% <<MD5/binary, SHA/binary>>; +server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; + Algorithm == dh_rsa; + Algorithm == dhe_rsa -> + MD5Context = crypto:md5_init(), + NewMD5Context = crypto:md5_update(MD5Context, Value), + MD5 = crypto:md5_final(NewMD5Context), + + SHAContext = crypto:sha_init(), + NewSHAContext = crypto:sha_update(SHAContext, Value), + SHA = crypto:sha_final(NewSHAContext), + + <<MD5/binary, SHA/binary>>; + +server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss; + Algorithm == dhe_dss -> + + SHAContext = crypto:sha_init(), + NewSHAContext = crypto:sha_update(SHAContext, Value), + crypto:sha_final(NewSHAContext). + + +sig_alg(dh_anon) -> + ?SIGNATURE_ANONYMOUS; +sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa -> + ?SIGNATURE_RSA; +sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss -> + ?SIGNATURE_DSA; +sig_alg(_) -> + ?NULL. -%% server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss; -%% Algorithm == dhe_dss -> -%% crypto:sha_final(Value). +key_excahange_alg(rsa) -> + ?KEY_EXCHANGE_RSA; +key_excahange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; + Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> + ?KEY_EXCHANGE_DIFFIE_HELLMAN; +key_excahange_alg(_) -> + ?NULL. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index b2bdfa0934..889d39f2af 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -38,7 +38,8 @@ -define(NUM_OF_SESSION_ID_BYTES, 32). % TSL 1.1 & SSL 3 -define(NUM_OF_PREMASTERSECRET_BYTES, 48). - +-define(DEFAULT_DIFFIE_HELLMAN_GENERATOR, 2). +-define(DEFAULT_DIFFIE_HELLMAN_PRIME, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Handsake protocol - RFC 4346 section 7.4 diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index edb8d9741d..ab24c28b2f 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -63,6 +63,7 @@ key, % password, % cacertfile, % file() + dhfile, % file() ciphers, % %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index ab29ac64df..df809ce275 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -182,13 +182,13 @@ setup_keys(export, MasterSecret, ServerRandom, ClientRandom, suites() -> [ %% TODO: uncomment when supported - %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA, - %% ?TLS_DHE_RSA_WITH_3DES_EDE_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_RSA_WITH_AES_128_CBC_SHA, %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this? diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index e0013c48ac..ce9a135168 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. 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% %% @@ -136,13 +136,13 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, suites() -> [ %% TODO: uncomment when supported - %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, - %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, - %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA, - %% ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?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_RSA_WITH_AES_128_CBC_SHA, %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this? diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index c79cfdea3f..8dc987e3ff 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -155,7 +155,7 @@ all(suite) -> upgrade, upgrade_with_timeout, tcp_connect, ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, shutdown_write, shutdown_both, shutdown_error, ciphers, - send_close, + send_close, dh_params, server_verify_peer_passive, server_verify_peer_active, server_verify_peer_active_once, server_verify_none_passive, server_verify_none_active, @@ -239,7 +239,7 @@ controlling_process(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, controlling_process_result, [self(), ClientMsg]}}, @@ -675,6 +675,38 @@ send_close(Config) when is_list(Config) -> gen_tcp:close(TcpS), {error, _} = ssl:send(SslS, "Hello world"), ssl_test_lib:close(Server). +%%-------------------------------------------------------------------- +dh_params(doc) -> + ["Test to specify DH-params file in server."]; + +dh_params(suite) -> + []; + +dh_params(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + DataDir = ?config(data_dir, Config), + DHParamFile = filename:join(DataDir, "dHParam.pem"), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, [{dhfile, DHParamFile} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, + [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- upgrade(doc) -> diff --git a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem b/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem new file mode 100644 index 0000000000..feb581da30 --- /dev/null +++ b/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem @@ -0,0 +1,5 @@ +-----BEGIN DH PARAMETERS----- +MIGHAoGBAMY5VmCZ22ZEy/KO8kjt94PH7ZtSG0Z0zitlMlvd4VsNkDzXsVeu+wkH +FGDC3h3vgv6iwXGCbmrSOVk/FPZbzLhwZ8aLnkUFOBbOvVvb1JptQwOt8mf+eScG +M2gGBktheQV5Nf1IrzOctG7VGt+neiqb/Y86uYCcDdL+M8++0qnLAgEC +-----END DH PARAMETERS----- |