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.erl551
1 files changed, 472 insertions, 79 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 1929370991..e358cbe9bb 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -28,9 +28,10 @@
-include("ssl_cipher.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
+-include("ssl_srp.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/8, server_hello/5, hello/4,
+-export([master_secret/4, client_hello/8, server_hello/7, hello/4,
hello_request/0, certify/7, certificate/4,
client_certificate_verify/6, certificate_verify/6, verify_signature/5,
certificate_request/3, key_exchange/3, server_key_exchange_hash/2,
@@ -46,6 +47,8 @@
#client_key_exchange{} | #finished{} | #certificate_verify{} |
#hello_request{} | #next_protocol{}.
+-define(NAMED_CURVE_TYPE, 3).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -61,14 +64,12 @@ client_hello(Host, Port, ConnectionStates,
ciphers = UserSuites
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
-
- Fun = fun(Version) ->
- ssl_record:protocol_version(Version)
- end,
- Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
+ Version = ssl_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
Ciphers = available_suites(UserSuites, Version),
+ SRP = srp_user(SslOpts),
+ {EcPointFormats, EllipticCurves} = default_ecc_extensions(Version),
Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
@@ -80,7 +81,10 @@ client_hello(Host, Port, ConnectionStates,
renegotiation_info =
renegotiation_info(client, ConnectionStates, Renegotiation),
+ srp = SRP,
hash_signs = default_hash_signs(),
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation =
encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, Renegotiation)
}.
@@ -97,11 +101,14 @@ encode_protocols_advertised_on_server(Protocols) ->
%%--------------------------------------------------------------------
-spec server_hello(session_id(), tls_version(), #connection_states{},
- boolean(), [binary()] | undefined) -> #server_hello{}.
+ boolean(), [binary()] | undefined,
+ #ec_point_formats{} | undefined,
+ #elliptic_curves{} | undefined) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
-server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdvertisedOnServer) ->
+server_hello(SessionId, Version, ConnectionStates, Renegotiation,
+ ProtocolsAdvertisedOnServer, EcPointFormats, EllipticCurves) ->
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
#server_hello{server_version = Version,
@@ -112,6 +119,8 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation, ProtocolsAdver
session_id = SessionId,
renegotiation_info =
renegotiation_info(server, ConnectionStates, Renegotiation),
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation = encode_protocols_advertised_on_server(ProtocolsAdvertisedOnServer)
}.
@@ -130,7 +139,8 @@ hello_request() ->
atom(), #connection_states{}, binary()},
boolean()) ->
{tls_version(), session_id(), #connection_states{}, binary() | undefined}|
- {tls_version(), {resumed | new, #session{}}, #connection_states{}, list(binary()) | undefined} |
+ {tls_version(), {resumed | new, #session{}}, #connection_states{}, [binary()] | undefined,
+ [oid()] | undefined, [oid()] | undefined} |
#alert{}.
%%
%% Description: Handles a recieved hello message
@@ -139,10 +149,11 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
session_id = SessionId, renegotiation_info = Info,
hash_signs = _HashSigns} = Hello,
- #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector,
+ versions = SupportedVersions},
ConnectionStates0, Renegotiation) ->
%%TODO: select hash and signature algorigthm
- case ssl_record:is_acceptable_version(Version) of
+ case ssl_record:is_acceptable_version(Version, SupportedVersions) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
Renegotiation, SecureRenegotation, []) of
@@ -163,42 +174,27 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
-hello(#client_hello{client_version = ClientVersion, random = Random,
- cipher_suites = CipherSuites,
- renegotiation_info = Info} = Hello,
- #ssl_options{versions = Versions,
- secure_renegotiate = SecureRenegotation} = SslOpts,
+hello(#client_hello{client_version = ClientVersion} = Hello,
+ #ssl_options{versions = Versions} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
-%% TODO: select hash and signature algorithm
+ %% TODO: select hash and signature algorithm
Version = select_version(ClientVersion, Versions),
- case ssl_record:is_acceptable_version(Version) of
+ case ssl_record:is_acceptable_version(Version, Versions) of
true ->
- {Type, #session{cipher_suite = CipherSuite,
- compression_method = Compression} = Session}
+ %% TODO: need to take supported Curves into Account when selecting the CipherSuite....
+ %% if whe have an ECDSA cert with an unsupported curve, we need to drop ECDSA ciphers
+ {Type, #session{cipher_suite = CipherSuite} = Session1}
= select_session(Hello, Port, Session0, Version,
SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
_ ->
- case handle_renegotiation_info(server, Info, ConnectionStates0,
- Renegotiation, SecureRenegotation,
- CipherSuites) of
- {ok, ConnectionStates1} ->
- ConnectionStates =
- hello_pending_connection_states(server,
- Version,
- CipherSuite,
- Random,
- Compression,
- ConnectionStates1),
- case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
- #alert{} = Alert ->
- Alert;
- ProtocolsToAdvertise ->
- {Version, {Type, Session}, ConnectionStates, ProtocolsToAdvertise}
- end;
- #alert{} = Alert ->
+ try handle_hello_extensions(Hello, Version, SslOpts, Session1, ConnectionStates0, Renegotiation) of
+ {Session, ConnectionStates, ProtocolsToAdvertise, ECPointFormats, EllipticCurves} ->
+ {Version, {Type, Session}, ConnectionStates,
+ ProtocolsToAdvertise, ECPointFormats, EllipticCurves}
+ catch throw:Alert ->
Alert
end
end;
@@ -348,9 +344,10 @@ verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey,
_ -> false
end;
verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams});
+verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) ->
public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}).
-
%%--------------------------------------------------------------------
-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) ->
#certificate_request{}.
@@ -375,6 +372,11 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) ->
{premaster_secret, binary(), public_key_info()} |
{dh, binary()} |
{dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()},
+ binary(), binary(), private_key()} |
+ {ecdh, #'ECPrivateKey'{}} |
+ {psk, binary()} |
+ {dhe_psk, binary(), binary()} |
+ {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()},
binary(), binary(), private_key()}) ->
#client_key_exchange{} | #server_key_exchange{}.
%%
@@ -385,20 +387,84 @@ key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) ->
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
+key_exchange(client, _Version, {dh, PublicKey}) ->
#client_key_exchange{
exchange_keys = #client_diffie_hellman_public{
dh_public = PublicKey}
};
-key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
- #'DHParameter'{prime = P, base = G},
- HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
- <<?UINT32(_), PBin/binary>> = crypto:mpint(P),
- <<?UINT32(_), GBin/binary>> = crypto:mpint(G),
- ServerDHParams = #server_dh_params{dh_p = PBin,
- dh_g = GBin, dh_y = PublicKey},
+key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) ->
+ #client_key_exchange{
+ exchange_keys = #client_ec_diffie_hellman_public{
+ dh_public = ECPublicKey}
+ };
+
+key_exchange(client, _Version, {psk, Identity}) ->
+ #client_key_exchange{
+ exchange_keys = #client_psk_identity{
+ identity = Identity}
+ };
+
+key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) ->
+ #client_key_exchange{
+ exchange_keys = #client_dhe_psk_identity{
+ identity = Identity,
+ dh_public = PublicKey}
+ };
+
+key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) ->
+ EncPremasterSecret =
+ encrypted_premaster_secret(Secret, PublicKey),
+ #client_key_exchange{
+ exchange_keys = #client_rsa_psk_identity{
+ identity = PskIdentity,
+ exchange_keys = EncPremasterSecret}};
+
+key_exchange(client, _Version, {srp, PublicKey}) ->
+ #client_key_exchange{
+ exchange_keys = #client_srp_public{
+ srp_a = PublicKey}
+ };
+
+key_exchange(server, Version, {dh, {PublicKey, _},
+ #'DHParameter'{prime = P, base = G},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerDHParams = #server_dh_params{dh_p = int_to_bin(P),
+ dh_g = int_to_bin(G), dh_y = PublicKey},
enc_server_key_exchange(Version, ServerDHParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey},
+ parameters = ECCurve}, HashSign, ClientRandom, ServerRandom,
+ PrivateKey}) ->
+ ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey},
+ enc_server_key_exchange(Version, ServerECParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {psk, PskIdentityHint,
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerPSKParams = #server_psk_params{hint = PskIdentityHint},
+ enc_server_key_exchange(Version, ServerPSKParams, HashSign,
+ ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _},
+ #'DHParameter'{prime = P, base = G},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerEDHPSKParams = #server_dhe_psk_params{
+ hint = PskIdentityHint,
+ dh_params = #server_dh_params{dh_p = int_to_bin(P),
+ dh_g = int_to_bin(G), dh_y = PublicKey}
+ },
+ enc_server_key_exchange(Version, ServerEDHPSKParams,
+ HashSign, ClientRandom, ServerRandom, PrivateKey);
+
+key_exchange(server, Version, {srp, {PublicKey, _},
+ #srp_user{generator = Generator, prime = Prime,
+ salt = Salt},
+ HashSign, ClientRandom, ServerRandom, PrivateKey}) ->
+ ServerSRPParams = #server_srp_params{srp_n = Prime, srp_g = Generator,
+ srp_s = Salt, srp_b = PublicKey},
+ enc_server_key_exchange(Version, ServerSRPParams, HashSign,
ClientRandom, ServerRandom, PrivateKey).
enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},
@@ -528,7 +594,13 @@ get_tls_handshake(Version, Data, Buffer) ->
%%--------------------------------------------------------------------
-spec decode_client_key(binary(), key_algo(), tls_version()) ->
- #encrypted_premaster_secret{} | #client_diffie_hellman_public{}.
+ #encrypted_premaster_secret{}
+ | #client_diffie_hellman_public{}
+ | #client_ec_diffie_hellman_public{}
+ | #client_psk_identity{}
+ | #client_dhe_psk_identity{}
+ | #client_rsa_psk_identity{}
+ | #client_srp_public{}.
%%
%% Description: Decode client_key data and return appropriate type
%%--------------------------------------------------------------------
@@ -595,8 +667,8 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) ->
%% Description: Calculate server key exchange hash
%%--------------------------------------------------------------------
server_key_exchange_hash(md5sha, Value) ->
- MD5 = crypto:md5(Value),
- SHA = crypto:sha(Value),
+ MD5 = crypto:hash(md5, Value),
+ SHA = crypto:hash(sha, Value),
<<MD5/binary, SHA/binary>>;
server_key_exchange_hash(Hash, Value) ->
@@ -680,6 +752,11 @@ cipher_suites(Suites, false) ->
cipher_suites(Suites, true) ->
Suites.
+srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
+ #srp{username = UserName};
+srp_user(_) ->
+ undefined.
+
renegotiation_info(client, _, false) ->
#renegotiation_info{renegotiated_connection = undefined};
renegotiation_info(server, ConnectionStates, false) ->
@@ -762,6 +839,36 @@ select_next_protocol(Protocols, NextProtocolSelector) ->
Protocol
end.
+default_ecc_extensions(Version) ->
+ case proplists:get_bool(ec, crypto:algorithms()) of
+ true ->
+ EcPointFormats = #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]},
+ EllipticCurves = #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)},
+ {EcPointFormats, EllipticCurves};
+ _ ->
+ {undefined, undefined}
+ end.
+
+handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0) ->
+ case proplists:get_bool(ec, crypto:algorithms()) of
+ true ->
+ EcPointFormats1 = handle_ecc_point_fmt_extension(EcPointFormats0),
+ EllipticCurves1 = handle_ecc_curves_extension(Version, EllipticCurves0),
+ {EcPointFormats1, EllipticCurves1};
+ _ ->
+ {undefined, undefined}
+ end.
+
+handle_ecc_point_fmt_extension(undefined) ->
+ undefined;
+handle_ecc_point_fmt_extension(_) ->
+ #ec_point_formats{ec_point_format_list = [?ECPOINT_UNCOMPRESSED]}.
+
+handle_ecc_curves_extension(Version, undefined) ->
+ undefined;
+handle_ecc_curves_extension(Version, _) ->
+ #elliptic_curves{elliptic_curve_list = ssl_tls1:ecc_curves(Version)}.
+
handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -869,11 +976,7 @@ hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
}.
select_version(ClientVersion, Versions) ->
- Fun = fun(Version) ->
- ssl_record:protocol_version(Version)
- end,
- ServerVersion = ssl_record:highest_protocol_version(lists:map(Fun,
- Versions)),
+ ServerVersion = ssl_record:highest_protocol_version(Versions),
ssl_record:lowest_protocol_version(ClientVersion, ServerVersion).
select_cipher_suite([], _) ->
@@ -948,7 +1051,10 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
DecodedExtensions = dec_hello_extensions(Extensions),
RenegotiationInfo = proplists:get_value(renegotiation_info, DecodedExtensions, undefined),
+ SRP = proplists:get_value(srp, DecodedExtensions, undefined),
HashSigns = proplists:get_value(hash_signs, DecodedExtensions, undefined),
+ EllipticCurves = proplists:get_value(elliptic_curves, DecodedExtensions,
+ undefined),
NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, DecodedExtensions, undefined),
#client_hello{
@@ -958,7 +1064,9 @@ dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suites = from_2bytes(CipherSuites),
compression_methods = Comp_methods,
renegotiation_info = RenegotiationInfo,
+ srp = SRP,
hash_signs = HashSigns,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation = NextProtocolNegotiation
};
@@ -972,7 +1080,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
renegotiation_info = undefined,
- hash_signs = undefined};
+ hash_signs = undefined,
+ elliptic_curves = undefined};
dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
@@ -984,6 +1093,8 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
undefined),
HashSigns = proplists:get_value(hash_signs, HelloExtensions,
undefined),
+ EllipticCurves = proplists:get_value(elliptic_curves, HelloExtensions,
+ undefined),
NextProtocolNegotiation = proplists:get_value(next_protocol_negotiation, HelloExtensions, undefined),
#server_hello{
@@ -994,6 +1105,7 @@ dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo,
hash_signs = HashSigns,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation = NextProtocolNegotiation};
dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
@@ -1036,7 +1148,28 @@ 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}.
+ #client_diffie_hellman_public{dh_public = DH_Y};
+dec_client_key(<<>>, ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
+ throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
+dec_client_key(<<?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, _) ->
+ #client_ec_diffie_hellman_public{dh_public = DH_Y};
+dec_client_key(<<?UINT16(Len), Id:Len/binary>>,
+ ?KEY_EXCHANGE_PSK, _) ->
+ #client_psk_identity{identity = Id};
+dec_client_key(<<?UINT16(Len), Id:Len/binary,
+ ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_DHE_PSK, _) ->
+ #client_dhe_psk_identity{identity = Id, dh_public = DH_Y};
+dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA_PSK, {3, 0}) ->
+ #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
+dec_client_key(<<?UINT16(Len), Id:Len/binary, ?UINT16(_), PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA_PSK, _) ->
+ #client_rsa_psk_identity{identity = Id, exchange_keys = #encrypted_premaster_secret{premaster_secret = PKEPMS}};
+dec_client_key(<<?UINT16(ALen), A:ALen/binary>>,
+ ?KEY_EXCHANGE_SRP, _) ->
+ #client_srp_public{srp_a = A}.
dec_ske_params(Len, Keys, Version) ->
<<Params:Len/bytes, Signature/binary>> = Keys,
@@ -1071,6 +1204,54 @@ dec_server_key(<<?UINT16(PLen), P:PLen/binary,
params_bin = BinMsg,
hashsign = HashSign,
signature = Signature};
+%% ECParameters with named_curve
+%% TODO: explicit curve
+dec_server_key(<<?BYTE(?NAMED_CURVE), ?UINT16(CurveID),
+ ?BYTE(PointLen), ECPoint:PointLen/binary,
+ _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN, Version) ->
+ Params = #server_ecdh_params{curve = {namedCurve, ssl_tls1:enum_to_oid(CurveID)},
+ public = ECPoint},
+ {BinMsg, HashSign, Signature} = dec_ske_params(PointLen + 4, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary>> = KeyStruct,
+ KeyExchange, Version)
+ when KeyExchange == ?KEY_EXCHANGE_PSK; KeyExchange == ?KEY_EXCHANGE_RSA_PSK ->
+ Params = #server_psk_params{
+ hint = PskIdentityHint},
+ {BinMsg, HashSign, Signature} = dec_ske_params(Len + 2, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,
+ ?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_DHE_PSK, Version) ->
+ DHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+ Params = #server_dhe_psk_params{
+ hint = IdentityHint,
+ dh_params = DHParams},
+ {BinMsg, HashSign, Signature} = dec_ske_params(Len + PLen + GLen + YLen + 8, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
+dec_server_key(<<?UINT16(NLen), N:NLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?BYTE(SLen), S:SLen/binary,
+ ?UINT16(BLen), B:BLen/binary, _/binary>> = KeyStruct,
+ ?KEY_EXCHANGE_SRP, Version) ->
+ Params = #server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B},
+ {BinMsg, HashSign, Signature} = dec_ske_params(NLen + GLen + SLen + BLen + 7, KeyStruct, Version),
+ #server_key_params{params = Params,
+ params_bin = BinMsg,
+ hashsign = HashSign,
+ signature = Signature};
dec_server_key(_, _, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
@@ -1098,6 +1279,11 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar
dec_hello_extensions(Rest, [{renegotiation_info,
#renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]);
+dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc)
+ when Len == SRPLen + 2 ->
+ dec_hello_extensions(Rest, [{srp,
+ #srp{username = SRP}} | Acc]);
+
dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
ExtData:Len/binary, Rest/binary>>, Acc) ->
SignAlgoListLen = Len - 2,
@@ -1107,6 +1293,22 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len),
dec_hello_extensions(Rest, [{hash_signs,
#hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]);
+dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ EllipticCurveListLen = Len - 2,
+ <<?UINT16(EllipticCurveListLen), EllipticCurveList/binary>> = ExtData,
+ EllipticCurves = [ssl_tls1:enum_to_oid(X) || <<X:16>> <= EllipticCurveList],
+ dec_hello_extensions(Rest, [{elliptic_curves,
+ #elliptic_curves{elliptic_curve_list = EllipticCurves}} | Acc]);
+
+dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ ECPointFormatListLen = Len - 1,
+ <<?BYTE(ECPointFormatListLen), ECPointFormatList/binary>> = ExtData,
+ ECPointFormats = binary_to_list(ECPointFormatList),
+ dec_hello_extensions(Rest, [{ec_point_formats,
+ #ec_point_formats{ec_point_format_list = ECPointFormats}} | Acc]);
+
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
@@ -1155,14 +1357,19 @@ enc_hs(#client_hello{client_version = {Major, Minor},
cipher_suites = CipherSuites,
compression_methods = CompMethods,
renegotiation_info = RenegotiationInfo,
+ srp = SRP,
hash_signs = HashSigns,
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- Extensions0 = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
+ Extensions0 = hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation)
+ ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EcPointFormats)
+ ++ ec_hello_extensions(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites), EllipticCurves),
Extensions1 = if
Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns);
true -> Extensions0
@@ -1177,16 +1384,21 @@ enc_hs(#client_hello{client_version = {Major, Minor},
enc_hs(#server_hello{server_version = {Major, Minor},
random = Random,
session_id = Session_ID,
- cipher_suite = Cipher_suite,
+ cipher_suite = CipherSuite,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo,
+ ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves,
next_protocol_negotiation = NextProtocolNegotiation}, _Version) ->
SID_length = byte_size(Session_ID),
- Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation),
+ CipherSuites = [ssl_cipher:suite_definition(CipherSuite)],
+ Extensions = hello_extensions(RenegotiationInfo, NextProtocolNegotiation)
+ ++ ec_hello_extensions(CipherSuites, EcPointFormats)
+ ++ ec_hello_extensions(CipherSuites, EllipticCurves),
ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
- Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
+ CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
ASN1Certs = certs_from_list(ASN1CertList),
ACLen = erlang:iolist_size(ASN1Certs),
@@ -1238,13 +1450,64 @@ enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) ->
<<?UINT16(PKEPMSLen), PKEPMS/binary>>;
enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
Len = byte_size(DHPublic),
- <<?UINT16(Len), DHPublic/binary>>.
+ <<?UINT16(Len), DHPublic/binary>>;
+enc_cke(#client_ec_diffie_hellman_public{dh_public = DHPublic}, _) ->
+ Len = byte_size(DHPublic),
+ <<?BYTE(Len), DHPublic/binary>>;
+enc_cke(#client_psk_identity{identity = undefined}, _) ->
+ Id = <<"psk_identity">>,
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary>>;
+enc_cke(#client_psk_identity{identity = Id}, _) ->
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary>>;
+enc_cke(Identity = #client_dhe_psk_identity{identity = undefined}, Version) ->
+ enc_cke(Identity#client_dhe_psk_identity{identity = <<"psk_identity">>}, Version);
+enc_cke(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic}, _) ->
+ Len = byte_size(Id),
+ DHLen = byte_size(DHPublic),
+ <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>;
+enc_cke(Identity = #client_rsa_psk_identity{identity = undefined}, Version) ->
+ enc_cke(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version);
+enc_cke(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) ->
+ EncPMS = enc_cke(ExchangeKeys, Version),
+ Len = byte_size(Id),
+ <<?UINT16(Len), Id/binary, EncPMS/binary>>;
+enc_cke(#client_srp_public{srp_a = A}, _) ->
+ Len = byte_size(A),
+ <<?UINT16(Len), A/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>>.
+ <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
+enc_server_key(#server_ecdh_params{curve = {namedCurve, ECCurve}, public = ECPubKey}) ->
+ %%TODO: support arbitrary keys
+ KLen = size(ECPubKey),
+ <<?BYTE(?NAMED_CURVE_TYPE), ?UINT16((ssl_tls1:oid_to_enum(ECCurve))),
+ ?BYTE(KLen), ECPubKey/binary>>;
+enc_server_key(#server_psk_params{hint = PskIdentityHint}) ->
+ Len = byte_size(PskIdentityHint),
+ <<?UINT16(Len), PskIdentityHint/binary>>;
+enc_server_key(Params = #server_dhe_psk_params{hint = undefined}) ->
+ enc_server_key(Params#server_dhe_psk_params{hint = <<>>});
+enc_server_key(#server_dhe_psk_params{
+ hint = PskIdentityHint,
+ dh_params = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y}}) ->
+ Len = byte_size(PskIdentityHint),
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ <<?UINT16(Len), PskIdentityHint/binary,
+ ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>;
+enc_server_key(#server_srp_params{srp_n = N, srp_g = G, srp_s = S, srp_b = B}) ->
+ NLen = byte_size(N),
+ GLen = byte_size(G),
+ SLen = byte_size(S),
+ BLen = byte_size(B),
+ <<?UINT16(NLen), N/binary, ?UINT16(GLen), G/binary,
+ ?BYTE(SLen), S/binary, ?UINT16(BLen), B/binary>>.
enc_sign({_, anon}, _Sign, _Version) ->
<<>>;
@@ -1257,16 +1520,58 @@ enc_sign(_HashSign, Sign, _Version) ->
SignLen = byte_size(Sign),
<<?UINT16(SignLen), Sign/binary>>.
+
+ec_hello_extensions(CipherSuites, #elliptic_curves{} = Info) ->
+ case advertises_ec_ciphers(CipherSuites) of
+ true ->
+ [Info];
+ false ->
+ []
+ end;
+ec_hello_extensions(CipherSuites, #ec_point_formats{} = Info) ->
+ case advertises_ec_ciphers(CipherSuites) of
+ true ->
+ [Info];
+ false ->
+ []
+ end;
+ec_hello_extensions(_, undefined) ->
+ [].
+
hello_extensions(RenegotiationInfo, NextProtocolNegotiation) ->
hello_extensions(RenegotiationInfo) ++ next_protocol_extension(NextProtocolNegotiation).
+hello_extensions(RenegotiationInfo, SRP, NextProtocolNegotiation) ->
+ hello_extensions(RenegotiationInfo)
+ ++ hello_extensions(SRP)
+ ++ next_protocol_extension(NextProtocolNegotiation).
+
+advertises_ec_ciphers([]) ->
+ false;
+advertises_ec_ciphers([{ecdh_ecdsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdhe_ecdsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdh_rsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->
+ true;
+advertises_ec_ciphers([_| Rest]) ->
+ advertises_ec_ciphers(Rest).
+
%% Renegotiation info
hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) ->
[];
hello_extensions(#renegotiation_info{} = Info) ->
[Info];
+hello_extensions(#srp{} = Info) ->
+ [Info];
hello_extensions(#hash_sign_algos{} = Info) ->
- [Info].
+ [Info];
+hello_extensions(undefined) ->
+ [].
next_protocol_extension(undefined) ->
[];
@@ -1292,7 +1597,22 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest
InfoLen = byte_size(Info),
Len = InfoLen +1,
enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>);
-
+enc_hello_extensions([#elliptic_curves{elliptic_curve_list = EllipticCurves} | Rest], Acc) ->
+ EllipticCurveList = << <<(ssl_tls1:oid_to_enum(X)):16>> || X <- EllipticCurves>>,
+ ListLen = byte_size(EllipticCurveList),
+ Len = ListLen + 2,
+ enc_hello_extensions(Rest, <<?UINT16(?ELLIPTIC_CURVES_EXT),
+ ?UINT16(Len), ?UINT16(ListLen), EllipticCurveList/binary, Acc/binary>>);
+enc_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats} | Rest], Acc) ->
+ ECPointFormatList = list_to_binary(ECPointFormats),
+ ListLen = byte_size(ECPointFormatList),
+ Len = ListLen + 1,
+ enc_hello_extensions(Rest, <<?UINT16(?EC_POINT_FORMATS_EXT),
+ ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>);
+enc_hello_extensions([#srp{username = UserName} | Rest], Acc) ->
+ SRPLen = byte_size(UserName),
+ Len = SRPLen + 2,
+ enc_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>);
enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) ->
SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> ||
{Hash, Sign} <- HashSignAlgos >>,
@@ -1327,9 +1647,15 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
certificate_types({KeyExchange, _, _, _})
when KeyExchange == rsa;
KeyExchange == dhe_dss;
- KeyExchange == dhe_rsa ->
+ KeyExchange == dhe_rsa;
+ KeyExchange == ecdhe_rsa ->
<<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
+certificate_types({KeyExchange, _, _, _})
+ when KeyExchange == dh_ecdsa;
+ KeyExchange == dhe_ecdsa ->
+ <<?BYTE(?ECDSA_SIGN)>>;
+
certificate_types(_) ->
<<?BYTE(?RSA_SIGN)>>.
@@ -1346,9 +1672,6 @@ certificate_authorities(CertDbHandle, CertDbRef) ->
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
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,
@@ -1369,7 +1692,9 @@ 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}]).
+ [{rsa_pad, rsa_pkcs1_padding}]);
+digitally_signed(_Version, Hash, HashAlgo, Key) ->
+ public_key:sign({digest, Hash}, HashAlgo, Key).
calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
@@ -1402,6 +1727,19 @@ key_exchange_alg(rsa) ->
key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
?KEY_EXCHANGE_DIFFIE_HELLMAN;
+key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa;
+ Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa;
+ Alg == ecdh_anon ->
+ ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN;
+key_exchange_alg(psk) ->
+ ?KEY_EXCHANGE_PSK;
+key_exchange_alg(dhe_psk) ->
+ ?KEY_EXCHANGE_DHE_PSK;
+key_exchange_alg(rsa_psk) ->
+ ?KEY_EXCHANGE_RSA_PSK;
+key_exchange_alg(Alg)
+ when Alg == srp_rsa; Alg == srp_dss; Alg == srp_anon ->
+ ?KEY_EXCHANGE_SRP;
key_exchange_alg(_) ->
?NULL.
@@ -1417,15 +1755,70 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}).
-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}).
+-define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}).
--define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)).
+-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)).
default_hash_signs() ->
+ HashSigns = [?TLSEXT_SIGALG(sha512),
+ ?TLSEXT_SIGALG(sha384),
+ ?TLSEXT_SIGALG(sha256),
+ ?TLSEXT_SIGALG(sha224),
+ ?TLSEXT_SIGALG(sha),
+ ?TLSEXT_SIGALG_DSA(sha),
+ ?TLSEXT_SIGALG_RSA(md5)],
+ HasECC = proplists:get_bool(ec, crypto:algorithms()),
#hash_sign_algos{hash_sign_algos =
- [?TLSEXT_SIGALG(sha512),
- ?TLSEXT_SIGALG(sha384),
- ?TLSEXT_SIGALG(sha256),
- ?TLSEXT_SIGALG(sha224),
- ?TLSEXT_SIGALG(sha),
- ?TLSEXT_SIGALG_DSA(sha),
- ?TLSEXT_SIGALG_RSA(md5)]}.
+ lists:filter(fun({_, ecdsa}) -> HasECC;
+ (_) -> true end, HashSigns)}.
+
+handle_hello_extensions(#client_hello{random = Random,
+ cipher_suites = CipherSuites,
+ renegotiation_info = Info,
+ srp = SRP,
+ ec_point_formats = EcPointFormats0,
+ elliptic_curves = EllipticCurves0} = Hello, Version,
+ #ssl_options{secure_renegotiate = SecureRenegotation} = Opts,
+ Session0, ConnectionStates0, Renegotiation) ->
+ Session = handle_srp_extension(SRP, Session0),
+ ConnectionStates = handle_renegotiation_extension(Version, Info, Random, Session, ConnectionStates0,
+ Renegotiation, SecureRenegotation, CipherSuites),
+ ProtocolsToAdvertise = handle_next_protocol_extension(Hello, Renegotiation, Opts),
+ {EcPointFormats, EllipticCurves} = handle_ecc_extensions(Version, EcPointFormats0, EllipticCurves0),
+ %%TODO make extensions compund data structure
+ {Session, ConnectionStates, ProtocolsToAdvertise, EcPointFormats, EllipticCurves}.
+
+
+handle_renegotiation_extension(Version, Info, Random, #session{cipher_suite = CipherSuite,
+ compression_method = Compression},
+ ConnectionStates0, Renegotiation, SecureRenegotation, CipherSuites) ->
+ case handle_renegotiation_info(server, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation,
+ CipherSuites) of
+ {ok, ConnectionStates1} ->
+ hello_pending_connection_states(server,
+ Version,
+ CipherSuite,
+ Random,
+ Compression,
+ ConnectionStates1);
+ #alert{} = Alert ->
+ throw(Alert)
+ end.
+
+handle_next_protocol_extension(Hello, Renegotiation, SslOpts)->
+ case handle_next_protocol_on_server(Hello, Renegotiation, SslOpts) of
+ #alert{} = Alert ->
+ throw(Alert);
+ ProtocolsToAdvertise ->
+ ProtocolsToAdvertise
+ end.
+
+handle_srp_extension(undefined, Session) ->
+ Session;
+handle_srp_extension(#srp{username = Username}, Session) ->
+ Session#session{srp_username = Username}.
+
+int_to_bin(I) ->
+ L = (length(integer_to_list(I, 16)) + 1) div 2,
+ <<I:(L*8)>>.