aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorPéter Dimitrov <[email protected]>2018-11-16 11:39:51 +0100
committerPéter Dimitrov <[email protected]>2018-11-20 09:55:54 +0100
commitf2ec822db072c3366effc93688e6def9742d8c5b (patch)
tree09c22e0928243c528c45ecb89683d5c21cd2e1e4 /lib
parentf995d04a0575cdd110a96741bc733eb95d063113 (diff)
downloadotp-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.erl29
-rw-r--r--lib/ssl/src/ssl_handshake.erl42
-rw-r--r--lib/ssl/src/tls_connection.erl1
-rw-r--r--lib/ssl/src/tls_connection_1_3.erl131
-rw-r--r--lib/ssl/src/tls_handshake.erl2
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl332
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}.