diff options
author | Péter Dimitrov <[email protected]> | 2018-11-16 11:39:51 +0100 |
---|---|---|
committer | Péter Dimitrov <[email protected]> | 2018-11-20 09:55:54 +0100 |
commit | f2ec822db072c3366effc93688e6def9742d8c5b (patch) | |
tree | 09c22e0928243c528c45ecb89683d5c21cd2e1e4 /lib | |
parent | f995d04a0575cdd110a96741bc733eb95d063113 (diff) | |
download | otp-f2ec822db072c3366effc93688e6def9742d8c5b.tar.gz otp-f2ec822db072c3366effc93688e6def9742d8c5b.tar.bz2 otp-f2ec822db072c3366effc93688e6def9742d8c5b.zip |
ssl: Improve the "start" and "negotiated" states
This change adds the capability to the TLS 1.3 server to process
ClientHello messages and answer with ServerHello.
Change-Id: I13f6cfac932574300338e7301c6162252a591c70
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ssl/src/ssl_cipher.erl | 29 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 42 | ||||
-rw-r--r-- | lib/ssl/src/tls_connection.erl | 1 | ||||
-rw-r--r-- | lib/ssl/src/tls_connection_1_3.erl | 131 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake.erl | 2 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 332 |
6 files changed, 430 insertions, 107 deletions
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 18109741cc..c4b8e2172a 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -34,7 +34,7 @@ -include("tls_handshake_1_3.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([security_parameters/2, security_parameters/3, +-export([security_parameters/2, security_parameters/3, security_parameters_1_3/3, cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/5, aead_decrypt/6, suites/1, all_suites/1, crypto_support_filters/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1, @@ -47,7 +47,7 @@ scheme_to_components/1, hash_size/1]). %% RFC 8446 TLS 1.3 --export([generate_client_shares/1]). +-export([generate_client_shares/1, generate_server_share/1]). -compile(inline). @@ -88,6 +88,24 @@ security_parameters(Version, CipherSuite, SecParams) -> prf_algorithm = prf_algorithm(PrfHashAlg, Version), hash_size = hash_size(Hash)}. +security_parameters_1_3(SecParams, ClientRandom, CipherSuite) -> + #{cipher := Cipher, + mac := Hash, + prf := PrfHashAlg} = ssl_cipher_format:suite_definition(CipherSuite), + SecParams#security_parameters{ + client_random = ClientRandom, + cipher_suite = CipherSuite, + bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), + cipher_type = type(Cipher), + key_size = effective_key_bits(Cipher), + expanded_key_material_length = expanded_key_material(Cipher), + key_material_length = key_material(Cipher), + iv_size = iv_size(Cipher), + mac_algorithm = mac_algorithm(Hash), + prf_algorithm =prf_algorithm(PrfHashAlg, {3,4}), + hash_size = hash_size(Hash), + compression_algorithm = 0}. + %%-------------------------------------------------------------------- -spec cipher_init(cipher_enum(), binary(), binary()) -> #cipher_state{}. %% @@ -1192,6 +1210,13 @@ filter_keyuse_suites(Use, KeyUse, CipherSuits, Suites) -> CipherSuits -- Suites end. +generate_server_share(Group) -> + Key = generate_key_exchange(Group), + #key_share_server_hello{ + server_share = #key_share_entry{ + group = Group, + key_exchange = Key + }}. generate_client_shares([]) -> #key_share_client_hello{client_shares = []}; diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 0b2ecfc981..417e5d9eb6 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -76,9 +76,11 @@ handle_client_hello_extensions/9, %% Returns server hello extensions handle_server_hello_extensions/9, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, - select_hashsign_algs/3, empty_extensions/2 + select_hashsign_algs/3, empty_extensions/2, add_server_share/2 ]). +-export([get_cert_params/1]). + %%==================================================================== %% Create handshake messages %%==================================================================== @@ -1137,25 +1139,31 @@ maybe_add_key_share(HelloExtensions, undefined) -> maybe_add_key_share(HelloExtensions, KeyShare) -> #key_share_client_hello{client_shares = ClientShares0} = KeyShare, %% Keep only public keys - Fun = fun(#key_share_entry{ - group = Group, - key_exchange = - #'ECPrivateKey'{publicKey = PublicKey}}) -> - #key_share_entry{ - group = Group, - key_exchange = PublicKey}; - (#key_share_entry{ - group = Group, - key_exchange = - {PublicKey, _}}) -> - #key_share_entry{ - group = Group, - key_exchange = PublicKey} - end, - ClientShares = lists:map(Fun, ClientShares0), + ClientShares = lists:map(fun kse_remove_private_key/1, ClientShares0), HelloExtensions#{key_share => #key_share_client_hello{ client_shares = ClientShares}}. +add_server_share(Extensions, KeyShare) -> + #key_share_server_hello{server_share = ServerShare0} = KeyShare, + %% Keep only public keys + ServerShare = kse_remove_private_key(ServerShare0), + Extensions#{key_share => #key_share_server_hello{ + server_share = ServerShare}}. + +kse_remove_private_key(#key_share_entry{ + group = Group, + key_exchange = + #'ECPrivateKey'{publicKey = PublicKey}}) -> + #key_share_entry{ + group = Group, + key_exchange = PublicKey}; +kse_remove_private_key(#key_share_entry{ + group = Group, + key_exchange = + {PublicKey, _}}) -> + #key_share_entry{ + group = Group, + key_exchange = PublicKey}. signature_algs_ext(undefined) -> undefined; diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index be262c48e8..5e6ba652f0 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -88,6 +88,7 @@ %% gen_statem callbacks -export([callback_mode/0, terminate/3, code_change/4, format_status/2]). +-export([encode_handshake/4]). -define(DIST_CNTRL_SPAWN_OPTS, [{priority, max}]). diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index c8732e7847..04bcea1e1b 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -104,56 +104,97 @@ -include("ssl_alert.hrl"). -include("ssl_connection.hrl"). +-include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). --export([hello/4]). --export([gen_handshake/4]). +%% gen_statem helper functions +-export([start/4, + negotiated/4 + ]). -hello(internal, {common_client_hello, Type, ServerHelloExt}, State, Connection) -> - do_server_hello(Type, ServerHelloExt, State, Connection). +start(internal, + #client_hello{} = Hello, + #state{connection_states = _ConnectionStates0, + ssl_options = #ssl_options{ciphers = _ServerCiphers, + signature_algs = _ServerSignAlgs, + signature_algs_cert = _SignatureSchemes, %% TODO: Check?? + supported_groups = _ServerGroups0, + versions = _Versions} = SslOpts, + session = #session{own_certificate = Cert}} = State0, + _Module) -> + + Env = #{cert => Cert}, + case tls_handshake_1_3:handle_client_hello(Hello, SslOpts, Env) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, start, State0); + M -> + %% update connection_states with cipher + State = update_state(State0, M), + {next_state, negotiated, State, [{next_event, internal, M}]} + + end. + +%% TODO: move these functions +update_state(#state{connection_states = ConnectionStates0, + session = Session} = State, + #{client_random := ClientRandom, + cipher := Cipher, + key_share := KeyShare, + session_id := SessionId}) -> + #{security_parameters := SecParamsR0} = PendingRead = + maps:get(pending_read, ConnectionStates0), + #{security_parameters := SecParamsW0} = PendingWrite = + maps:get(pending_write, ConnectionStates0), + SecParamsR = ssl_cipher:security_parameters_1_3(SecParamsR0, ClientRandom, Cipher), + SecParamsW = ssl_cipher:security_parameters_1_3(SecParamsW0, ClientRandom, Cipher), + ConnectionStates = + ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, + pending_write => PendingWrite#{security_parameters => SecParamsW}}, + State#state{connection_states = ConnectionStates, + key_share = KeyShare, + session = Session#session{session_id = SessionId}}. + + +negotiated(internal, + Map, + #state{connection_states = ConnectionStates0, + session = #session{session_id = SessionId}, + ssl_options = #ssl_options{} = SslOpts, + key_share = KeyShare, + tls_handshake_history = HHistory0, + transport_cb = Transport, + socket = Socket}, _Module) -> + + %% Create server_hello + %% Extensions: supported_versions, key_share, (pre_shared_key) + ServerHello = tls_handshake_1_3:server_hello(SessionId, KeyShare, + ConnectionStates0, Map), + + %% Update handshake_history (done in encode!) + %% Encode handshake + {BinMsg, _ConnectionStates, _HHistory} = + tls_connection:encode_handshake(ServerHello, {3,4}, ConnectionStates0, HHistory0), + %% Send server_hello + tls_connection:send(Transport, Socket, BinMsg), + Report = #{direction => outbound, + protocol => 'tls_record', + message => BinMsg}, + Msg = #{direction => outbound, + protocol => 'handshake', + message => ServerHello}, + ssl_logger:debug(SslOpts#ssl_options.log_level, Msg, #{domain => [otp,ssl,handshake]}), + ssl_logger:debug(SslOpts#ssl_options.log_level, Report, #{domain => [otp,ssl,tls_record]}), + ok. + + %% K_send = handshake ??? + %% (Send EncryptedExtensions) + %% ([Send CertificateRequest]) + %% [Send Certificate + CertificateVerify] + %% Send Finished + %% K_send = application ??? -do_server_hello(Type, #{next_protocol_negotiation := _NextProtocols} = - _ServerHelloExt, - #state{negotiated_version = _Version, - session = #session{session_id = _SessId}, - connection_states = _ConnectionStates0, - ssl_options = #ssl_options{versions = [_HighestVersion|_]}} - = State0, _Connection) when is_atom(Type) -> -%% NEGOTIATED -%% | Send ServerHello -%% | K_send = handshake -%% | Send EncryptedExtensions -%% | [Send CertificateRequest] -%% Can send | [Send Certificate + CertificateVerify] -%% app data | Send Finished -%% after --> | K_send = application -%% here +--------+--------+ -%% No 0-RTT | | 0-RTT -%% | | -%% K_recv = handshake | | K_recv = early data -%% [Skip decrypt errors] | +------> WAIT_EOED -+ -%% | | Recv | | Recv EndOfEarlyData -%% | | early data | | K_recv = handshake -%% | +------------+ | -%% | | -%% +> WAIT_FLIGHT2 <--------+ %% Will be called implicitly %% {Record, State} = Connection:next_record(State2#state{session = Session}), %% Connection:next_event(wait_flight2, Record, State, Actions), %% OR %% Connection:next_event(WAIT_EOED, Record, State, Actions) - {next_state, wait_flight2, State0, []}. - %% TODO: Add new states to tls_connection! - %% State0. - - -gen_handshake(StateName, Type, Event, - #state{negotiated_version = Version} = State) -> - try tls_connection_1_3:StateName(Type, Event, State, ?MODULE) of - Result -> - Result - catch - _:_ -> - ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, - malformed_handshake_data), - Version, StateName, State) - end. diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 19535a2fcb..5aca4bf8c8 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -263,8 +263,6 @@ get_tls_handshake(Version, Data, Buffer, Options) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -handle_client_hello(Version = {3,4}, ClientHello, SslOpts, Info, Renegotiation) -> - tls_handshake_1_3:handle_client_hello(Version, ClientHello, SslOpts, Info, Renegotiation); handle_client_hello(Version, #client_hello{session_id = SugesstedId, cipher_suites = CipherSuites, diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 5c7630cde4..f381e038cf 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -28,13 +28,45 @@ -include("tls_handshake_1_3.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). +-include("ssl_record.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Encode -export([encode_handshake/1, decode_handshake/2]). %% Handshake --export([handle_client_hello/5]). +-export([handle_client_hello/3]). + +%% Create handshake messages +-export([server_hello/4]). + +%%==================================================================== +%% Create handshake messages +%%==================================================================== + +server_hello(SessionId, KeyShare, ConnectionStates, _Map) -> + #{security_parameters := SecParams} = + ssl_record:pending_connection_state(ConnectionStates, read), + Extensions = server_hello_extensions(KeyShare), + #server_hello{server_version = {3,3}, %% legacy_version + cipher_suite = SecParams#security_parameters.cipher_suite, + compression_method = + SecParams#security_parameters.compression_algorithm, + random = SecParams#security_parameters.server_random, + session_id = SessionId, + extensions = Extensions + }. + +server_hello_extensions(KeyShare) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions = #{server_hello_selected_version => SupportedVersions}, + ssl_handshake:add_server_share(Extensions, KeyShare). + + + +%%==================================================================== +%% Encode handshake +%%==================================================================== encode_handshake(#certificate_request_1_3{ certificate_request_context = Context, @@ -68,6 +100,11 @@ encode_handshake(#key_update{request_update = Update}) -> encode_handshake(HandshakeMsg) -> ssl_handshake:encode_handshake(HandshakeMsg, {3,4}). + +%%==================================================================== +%% Decode handshake +%%==================================================================== + decode_handshake(?CERTIFICATE_REQUEST, <<?BYTE(0), ?UINT16(Size), EncExts:Size/binary>>) -> Exts = decode_extensions(EncExts, certificate_request), #certificate_request_1_3{ @@ -156,45 +193,258 @@ extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions)]. -handle_client_hello(Version, - #client_hello{session_id = _SugesstedId, - cipher_suites = _CipherSuites, - compression_methods = _Compressions, - random = _Random, - extensions = _HelloExt}, - #ssl_options{versions = Versions, - signature_algs = _SupportedHashSigns, - eccs = _SupportedECCs, - honor_ecc_order = _ECCOrder} = _SslOpts, - {_Port, _Session0, _Cache, _CacheCb, _ConnectionStates0, _Cert, _}, - _Renegotiation) -> - case tls_record:is_acceptable_version(Version, Versions) of - true -> - %% Get supported_groups - %% SupportedGroups = maps:get(elliptic_curves, HelloExt, undefined), - %% Get KeyShareClientHello - - %% Validate supported_groups + KeyShareClientHello - %% IF valid THEN - %% IF supported_groups IS empty send HelloRetryRequest - %% ELSE continue - %% ELSE - %% send Alert - %% ClientHashSigns = maps:get(signature_algs, HelloExt, undefined), - %% ClientSignatureSchemes = maps:get(signature_algs_cert, HelloExt, undefined), - - %% Implement session handling. - - %% Select curve - - %% Sessions cannot be resumed by ClientHello - - %% Select cipher_suite - %% Select hash_sign - - %% Handle extensions - ok; - false -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) +%%==================================================================== +%% Handle handshake messages +%%==================================================================== + +handle_client_hello(#client_hello{cipher_suites = ClientCiphers, + random = Random, + session_id = SessionId, + extensions = Extensions} = _Hello, + #ssl_options{ciphers = ServerCiphers, + signature_algs = ServerSignAlgs, + signature_algs_cert = _SignatureSchemes, %% TODO: Check?? + supported_groups = ServerGroups0} = _SslOpts, + Env) -> + + Cert = maps:get(cert, Env, undefined), + + ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), + ClientGroups = get_supported_groups(ClientGroups0), + ServerGroups = get_supported_groups(ServerGroups0), + + ClientShares0 = maps:get(key_share, Extensions, undefined), + ClientShares = get_key_shares(ClientShares0), + + ClientSignAlgs = get_signature_scheme_list( + maps:get(signature_algs, Extensions, undefined)), + ClientSignAlgsCert = get_signature_scheme_list( + maps:get(signature_algs_cert, Extensions, undefined)), + + %% TODO: use library function if it exists + %% Init the maybe "monad" + {Ref,Maybe} = maybe(), + + try + %% If the server does not select a PSK, then the server independently selects a + %% cipher suite, an (EC)DHE group and key share for key establishment, + %% and a signature algorithm/certificate pair to authenticate itself to + %% the client. + Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), + Group = Maybe(select_server_group(ServerGroups, ClientGroups)), + Maybe(validate_key_share(ClientGroups, ClientShares)), + _ClientPubKey = Maybe(get_client_public_key(Group, ClientShares)), + + %% Handle certificate + {PublicKeyAlgo, SignAlgo} = get_certificate_params(Cert), + + %% Check if client supports signature algorithm of server certificate + Maybe(check_cert_sign_algo(SignAlgo, ClientSignAlgs, ClientSignAlgsCert)), + + %% Check if server supports + SelectedSignAlg = Maybe(select_sign_algo(PublicKeyAlgo, ClientSignAlgs, ServerSignAlgs)), + + %% Generate server_share + KeyShare = ssl_cipher:generate_server_share(Group), + + _Ret = #{cipher => Cipher, + group => Group, + sign_alg => SelectedSignAlg, + %% client_share => ClientPubKey, + key_share => KeyShare, + client_random => Random, + session_id => SessionId} + + %% TODO: + %% - session handling + %% - handle extensions: ALPN + %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats) + + catch + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, {client_hello_retry_request, _Group0}} -> + %% TODO + exit({client_hello_retry_request, not_implemented}); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +%% If there is no overlap between the received +%% "supported_groups" and the groups supported by the server, then the +%% server MUST abort the handshake with a "handshake_failure" or an +%% "insufficient_security" alert. +select_server_group(_, []) -> + {error, {insufficient_security, no_suitable_groups}}; +select_server_group(ServerGroups, [C|ClientGroups]) -> + case lists:member(C, ServerGroups) of + true -> + {ok, C}; + false -> + select_server_group(ServerGroups, ClientGroups) end. + +%% RFC 8446 - 4.2.8. Key Share +%% This vector MAY be empty if the client is requesting a +%% HelloRetryRequest. Each KeyShareEntry value MUST correspond to a +%% group offered in the "supported_groups" extension and MUST appear in +%% the same order. However, the values MAY be a non-contiguous subset +%% of the "supported_groups" extension and MAY omit the most preferred +%% groups. +%% +%% Clients can offer as many KeyShareEntry values as the number of +%% supported groups it is offering, each representing a single set of +%% key exchange parameters. +%% +%% Clients MUST NOT offer multiple KeyShareEntry values +%% for the same group. Clients MUST NOT offer any KeyShareEntry values +%% for groups not listed in the client's "supported_groups" extension. +%% Servers MAY check for violations of these rules and abort the +%% handshake with an "illegal_parameter" alert if one is violated. +validate_key_share(_ ,[]) -> + ok; +validate_key_share([], _) -> + {error, illegal_parameter}; +validate_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> + validate_key_share(ClientGroups, ClientShares); +validate_key_share([_|ClientGroups], [_|_] = ClientShares) -> + validate_key_share(ClientGroups, ClientShares). + + +get_client_public_key(Group, ClientShares) -> + case lists:keysearch(Group, 2, ClientShares) of + {value, {_, _, ClientPublicKey}} -> + {ok, ClientPublicKey}; + false -> + %% ClientHelloRetryRequest + {error, {client_hello_retry_request, Group}} + end. + +select_cipher_suite([], _) -> + {error, no_suitable_cipher}; +select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> + case lists:member(Cipher, ServerCiphers) of + true -> + {ok, Cipher}; + false -> + select_cipher_suite(ClientCiphers, ServerCiphers) + end. + +%% RFC 8446 (TLS 1.3) +%% TLS 1.3 provides two extensions for indicating which signature +%% algorithms may be used in digital signatures. The +%% "signature_algorithms_cert" extension applies to signatures in +%% certificates and the "signature_algorithms" extension, which +%% originally appeared in TLS 1.2, applies to signatures in +%% CertificateVerify messages. +%% +%% If no "signature_algorithms_cert" extension is +%% present, then the "signature_algorithms" extension also applies to +%% signatures appearing in certificates. +check_cert_sign_algo(SignAlgo, ClientSignAlgs, undefined) -> + maybe_lists_member(SignAlgo, ClientSignAlgs, + {insufficient_security, no_suitable_signature_algorithm}); +check_cert_sign_algo(SignAlgo, _, ClientSignAlgsCert) -> + maybe_lists_member(SignAlgo, ClientSignAlgsCert, + {insufficient_security, no_suitable_signature_algorithm}). + + +%% DSA keys are not supported by TLS 1.3 +select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> + {error, {insufficient_security, no_suitable_public_key}}; +%% TODO: Implement check for ellipctic curves! +select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> + {_, S, _} = ssl_cipher:scheme_to_components(C), + case PublicKeyAlgo =:= rsa andalso + ((S =:= rsa_pkcs1) orelse (S =:= rsa_pss_rsae) orelse (S =:= rsa_pss_pss)) andalso + lists:member(C, ServerSignAlgs) of + true -> + {ok, C}; + false -> + select_sign_algo(PublicKeyAlgo, ClientSignAlgs, ServerSignAlgs) + end. + + +maybe_lists_member(Elem, List, Error) -> + case lists:member(Elem, List) of + true -> + ok; + false -> + {error, Error} + end. + +%% TODO: test with ecdsa, rsa_pss_rsae, rsa_pss_pss +get_certificate_params(Cert) -> + {SignAlgo0, _Param, PublicKeyAlgo0} = ssl_handshake:get_cert_params(Cert), + SignAlgo = public_key:pkix_sign_types(SignAlgo0), + PublicKeyAlgo = public_key_algo(PublicKeyAlgo0), + Scheme = sign_algo_to_scheme(SignAlgo), + {PublicKeyAlgo, Scheme}. + +sign_algo_to_scheme({Hash0, Sign0}) -> + SupportedSchemes = tls_v1:default_signature_schemes({3,4}), + Hash = case Hash0 of + sha -> + sha1; + H -> + H + end, + Sign = case Sign0 of + rsa -> + rsa_pkcs1; + S -> + S + end, + sign_algo_to_scheme(Hash, Sign, SupportedSchemes). +%% +sign_algo_to_scheme(_, _, []) -> + not_found; +sign_algo_to_scheme(H, S, [Scheme|T]) -> + {Hash, Sign, _Curve} = ssl_cipher:scheme_to_components(Scheme), + case H =:= Hash andalso S =:= Sign of + true -> + Scheme; + false -> + sign_algo_to_scheme(H, S, T) + end. + + +%% Note: copied from ssl_handshake +public_key_algo(?rsaEncryption) -> + rsa; +public_key_algo(?'id-ecPublicKey') -> + ecdsa; +public_key_algo(?'id-dsa') -> + dsa. + +get_signature_scheme_list(undefined) -> + undefined; +get_signature_scheme_list(#signature_algorithms_cert{ + signature_scheme_list = ClientSignatureSchemes}) -> + ClientSignatureSchemes; +get_signature_scheme_list(#signature_algorithms{ + signature_scheme_list = ClientSignatureSchemes}) -> + ClientSignatureSchemes. + +get_supported_groups(#supported_groups{supported_groups = Groups}) -> + Groups. + +get_key_shares(#key_share_client_hello{client_shares = ClientShares}) -> + ClientShares. + +maybe() -> + Ref = erlang:make_ref(), + Ok = fun(ok) -> ok; + ({ok,R}) -> R; + ({error,Reason}) -> + throw({Ref,Reason}) + end, + {Ref,Ok}. |