aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_handshake.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_handshake.erl')
-rw-r--r--lib/ssl/src/ssl_handshake.erl164
1 files changed, 89 insertions, 75 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 3811906d77..add5147fb4 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -31,32 +31,34 @@
-include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/6, server_hello/4, hello/4,
+-export([master_secret/4, client_hello/5, server_hello/4, hello/4,
hello_request/0, certify/7, certificate/3,
client_certificate_verify/6,
certificate_verify/6, certificate_request/2,
key_exchange/2, server_key_exchange_hash/2, finished/4,
verify_connection/5,
- get_tls_handshake/4,
+ get_tls_handshake/2, decode_client_key/3,
server_hello_done/0, sig_alg/1,
encode_handshake/3, init_hashes/0,
update_hashes/2, decrypt_premaster_secret/2]).
--type tls_handshake() :: #client_hello{} | #server_hello{} | #server_hello_done{} |
-#certificate{} | #client_key_exchange{} | #finished{} | #certificate_verify{}.
+-type tls_handshake() :: #client_hello{} | #server_hello{} |
+ #server_hello_done{} | #certificate{} | #certificate_request{} |
+ #client_key_exchange{} | #finished{} | #certificate_verify{} |
+ #hello_request{}.
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(host(), port_num(), #connection_states{},
- #ssl_options{}, binary(), boolean()) -> #client_hello{}.
+ #ssl_options{}, boolean()) -> #client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
ciphers = UserSuites}
- = SslOpts, Cert, Renegotiation) ->
+ = SslOpts, Renegotiation) ->
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
@@ -64,7 +66,7 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
- Ciphers = available_suites(Cert, UserSuites, Version),
+ Ciphers = available_suites(UserSuites, Version),
Id = ssl_manager:client_session_id(Host, Port, SslOpts),
@@ -110,7 +112,7 @@ hello_request() ->
#connection_states{} | {port_num(), #session{}, cache_ref(),
atom(), #connection_states{}, binary()},
boolean()) -> {tls_version(), session_id(), #connection_states{}}|
- {tls_version(), {resumed | new, session_id()},
+ {tls_version(), {resumed | new, #session{}},
#connection_states{}} | #alert{}.
%%
%% Description: Handles a recieved hello message
@@ -201,18 +203,15 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
end
end,
try
- %% Allow missing root_cert and check that with VerifyFun
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of
- {TrustedErlCert, CertPath, VerifyErrors} ->
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef) of
+ {TrustedErlCert, CertPath} ->
Result = public_key:pkix_path_validation(TrustedErlCert,
CertPath,
[{max_path_length,
MaxPathLen},
{verify, VerifyBool},
{validate_extensions_fun,
- ValidateExtensionFun},
- {acc_errors,
- VerifyErrors}]),
+ ValidateExtensionFun}]),
case Result of
{error, Reason} ->
path_validation_alert(Reason, Verify);
@@ -232,7 +231,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
end.
%%--------------------------------------------------------------------
--spec certificate(der_cert(), term(), client | server) -> #certificate{}.
+-spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}.
%%
%% Description: Creates a certificate message.
%%--------------------------------------------------------------------
@@ -260,8 +259,8 @@ certificate(OwnCert, CertDbRef, server) ->
%%--------------------------------------------------------------------
-spec client_certificate_verify(undefined | der_cert(), binary(),
tls_version(), key_algo(), private_key(),
- {binary(), binary()}) ->
- #certificate_verify{} | ignore.
+ {{binary(), binary()},{binary(), binary()}}) ->
+ #certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
%%--------------------------------------------------------------------
@@ -283,9 +282,9 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm,
end.
%%--------------------------------------------------------------------
--spec certificate_verify(binary(), public_key_info(), tls_version(),
- binary(), key_algo(),
- {binary(), binary()}) -> valid | #alert{}.
+%% -spec certificate_verify(binary(), public_key_info(), tls_version(),
+%% binary(), key_algo(),
+%% {_, {binary(), binary()}}) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
@@ -304,9 +303,15 @@ certificate_verify(Signature, {_, PublicKey, _}, Version,
end;
certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version,
MasterSecret, dhe_dss = Algorithm, {_, Hashes0}) ->
- Hashes = calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
- public_key:verify_signature(Hashes, sha, Signature, PublicKey, PublicKeyParams).
+ Hashes = calc_certificate_verify(Version, MasterSecret,
+ Algorithm, Hashes0),
+ case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of
+ true ->
+ valid;
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
+ end.
+
%%--------------------------------------------------------------------
-spec certificate_request(#connection_states{}, certdb_ref()) ->
@@ -329,7 +334,7 @@ certificate_request(ConnectionStates, CertDbRef) ->
-spec key_exchange(client | server,
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
- {dh, binary(), #'DHParameter'{}, key_algo(),
+ {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(),
binary(), binary(), private_key()}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
@@ -406,7 +411,7 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
end.
%%--------------------------------------------------------------------
--spec finished(tls_version(), client | server, binary(), {binary(), binary()}) ->
+-spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) ->
#finished{}.
%%
%% Description: Creates a handshake finished message
@@ -417,7 +422,7 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
%%--------------------------------------------------------------------
-spec verify_connection(tls_version(), #finished{}, client | server, binary(),
- {binary(), binary()}) -> verified | #alert{}.
+ {_, {binary(), binary()}}) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
@@ -442,7 +447,7 @@ server_hello_done() ->
#server_hello_done{}.
%%--------------------------------------------------------------------
--spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> binary().
+-spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> iolist().
%%
%% Description: Encode a handshake packet to binary
%%--------------------------------------------------------------------
@@ -453,29 +458,36 @@ encode_handshake(Package, Version, KeyAlg) ->
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
--spec get_tls_handshake(binary(), binary(), key_algo(), tls_version()) ->
- {[tls_handshake()], [binary()], binary()}.
+-spec get_tls_handshake(binary(), binary() | iolist()) ->
+ {[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-get_tls_handshake(Data, <<>>, KeyAlg, Version) ->
- get_tls_handshake_aux(Data, KeyAlg, Version, []);
-get_tls_handshake(Data, Buffer, KeyAlg, Version) ->
- get_tls_handshake_aux(list_to_binary([Buffer, Data]),
- KeyAlg, Version, []).
+get_tls_handshake(Data, <<>>) ->
+ get_tls_handshake_aux(Data, []);
+get_tls_handshake(Data, Buffer) ->
+ get_tls_handshake_aux(list_to_binary([Buffer, Data]), []).
+
+%%--------------------------------------------------------------------
+-spec decode_client_key(binary(), key_algo(), tls_version()) ->
+ #encrypted_premaster_secret{} | #client_diffie_hellman_public{}.
+%%
+%% Description: Decode client_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_client_key(ClientKey, Type, Version) ->
+ dec_client_key(ClientKey, key_exchange_alg(Type), Version).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
- Body:Length/binary,Rest/binary>>, KeyAlg,
- Version, Acc) ->
+ Body:Length/binary,Rest/binary>>, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version),
- get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]);
-get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) ->
+ H = dec_hs(Type, Body),
+ get_tls_handshake_aux(Rest, [{H,Raw} | Acc]);
+get_tls_handshake_aux(Data, Acc) ->
{lists:reverse(Acc), Data}.
verify_bool(verify_peer) ->
@@ -495,6 +507,8 @@ path_validation_alert({bad_cert, unknown_critical_extension}, _) ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
path_validation_alert({bad_cert, cert_revoked}, _) ->
?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
+path_validation_alert({bad_cert, unknown_ca}, _) ->
+ ?ALERT_REC(?FATAL, ?UNKNOWN_CA);
path_validation_alert(_, _) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
@@ -518,13 +532,16 @@ select_session(Hello, Port, Session, Version,
{resumed, CacheCb:lookup(Cache, {Port, SessionId})}
end.
-available_suites(Cert, UserSuites, Version) ->
+available_suites(UserSuites, Version) ->
case UserSuites of
[] ->
- ssl_cipher:filter(Cert, ssl_cipher:suites(Version));
+ ssl_cipher:suites(Version);
_ ->
- ssl_cipher:filter(Cert, UserSuites)
+ UserSuites
end.
+
+available_suites(ServerCert, UserSuites, Version) ->
+ ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)).
cipher_suites(Suites, false) ->
[?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
@@ -718,7 +735,7 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(?HELLO_REQUEST, <<>>, _, _) ->
+dec_hs(?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
@@ -728,8 +745,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>,
- _, _) ->
+ ChallengeData:CDLength/binary>>) ->
?DBG_HEX(CipherSuites),
?DBG_HEX(CipherSuites),
#client_hello{client_version = {Major, Minor},
@@ -743,8 +759,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>,
- _, _) ->
+ Extensions/binary>>) ->
RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
undefined),
@@ -759,7 +774,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method)>>, _, _) ->
+ Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
#server_hello{
server_version = {Major,Minor},
random = Random,
@@ -771,7 +786,7 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method),
- ?UINT16(ExtLen), Extensions:ExtLen/binary>>, _, _) ->
+ ?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
undefined),
@@ -782,44 +797,42 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo};
-dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) ->
+dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
- ?UINT16(Len), Sig:Len/binary>>,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ ?UINT16(Len), Sig:Len/binary>>) ->
#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,
- ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>, _, _) ->
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
-dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
+dec_hs(?SERVER_HELLO_DONE, <<>>) ->
#server_hello_done{};
-dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)->
+dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)->
#certificate_verify{signature = Signature};
-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>>,
- ?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, _) ->
- throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #client_key_exchange{exchange_keys =
- #client_diffie_hellman_public{dh_public = DH_Y}};
-dec_hs(?FINISHED, VerifyData, _, _) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+ #client_key_exchange{exchange_keys = PKEPMS};
+dec_hs(?FINISHED, VerifyData) ->
#finished{verify_data = VerifyData};
-dec_hs(_, _, _, _) ->
+dec_hs(_, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
+dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ #client_diffie_hellman_public{dh_public = DH_Y}.
+
dec_hello_extensions(<<>>) ->
[];
dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
@@ -1036,9 +1049,10 @@ certificate_authorities(CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
- Subj = public_key:pkix_transform(OTPSubj, encode),
- {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj),
- DNEncodedBin = iolist_to_binary(DNEncoded),
+ DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
+ %%Subj = public_key:pkix_transform(OTPSubj, encode),
+ %% {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj),
+ %% DNEncodedBin = iolist_to_binary(DNEncoded),
DNEncodedLen = byte_size(DNEncodedBin),
<<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
end,
@@ -1062,7 +1076,7 @@ digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hash, Key,
[{rsa_pad, rsa_pkcs1_padding}]);
digitally_signed(Hash, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(none, Hash, Key).
+ public_key:sign(Hash, none, Key).
calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
@@ -1114,7 +1128,7 @@ sig_alg(_) ->
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
- Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
+ Alg == dh_dss; Alg == dh_rsa ->
?KEY_EXCHANGE_DIFFIE_HELLMAN;
key_exchange_alg(_) ->
?NULL.