File 1545-ssl-Adjust-max_handshake_size-for-SLH_DSA.patch of Package erlang
From 3a93b8c79e3da493fe4f361e60fa18a2c06b1b1d Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Mon, 24 Nov 2025 13:55:38 +0100
Subject: [PATCH 5/5] ssl: Adjust max_handshake_size for SLH_DSA
Correct doc and fix whitespace errors and long lines
---
lib/ssl/src/ssl.erl | 16 ++++---
lib/ssl/src/ssl_config.erl | 93 ++++++++++++++++++++++++--------------
2 files changed, 69 insertions(+), 40 deletions(-)
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 3e099902b9..d0840815f4 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -660,13 +660,13 @@ Options common to both client and server side.
The `keep_secrets` functionality is disabled (`false`) by default.
If set to legacy value `true` keylog information can be retrieved from the connection
- using connection_information/2.
+ using connection_information/2.
Added in OTP 23.2.
> #### Note {: .info }
> Note that having to ask the connection has some drawbacks
- > as for instance you can not get keylog information for
+ > as for instance you can not get keylog information for
> failed connections, and other keylog items have
> to be retrieved in a polling manner and are not correctly
> formatted for key_updates.
@@ -692,7 +692,8 @@ Options common to both client and server side.
Used to limit the size of valid TLS handshake packets to avoid DoS
attacks.
- Integer (24 bits, unsigned). Defaults to `256*1024`.
+ Integer (24 bits, unsigned). Defaults to `256*1024` before OTP-26 or if SLH-DSA algorithms
+ are configured, otherwise the default is `256*1024`/2.
- **`{hibernate_after, HibernateTimeout}`** - Hibernate inactive connection processes.
@@ -723,9 +724,10 @@ Options common to both client and server side.
{ciphers, cipher_suites()} |
{signature_algs, signature_algs()} |
{signature_algs_cert, [sign_scheme()]} |
- {keep_secrets, KeepSecrets:: boolean() |
- {keylog_hs, fun((Info::keylog_info()) -> any())} |
- {keylog, fun((Info::keylog_info()) -> any())}} |
+ {keep_secrets,
+ KeepSecrets:: boolean() |
+ {keylog_hs, fun((Info::keylog_info()) -> any())} |
+ {keylog, fun((Info::keylog_info()) -> any())}} |
{max_handshake_size, HandshakeSize::pos_integer()} |
{versions, [protocol_version()]} |
{log_level, Level::logger:level() | none | all} |
@@ -734,7 +736,7 @@ Options common to both client and server side.
{sender_spawn_opts, SpawnOpts::[erlang:spawn_opt_option()]}.
--doc(#{group =>
+-doc(#{group =>
<<"Client and Server Options">>}).
-doc """
Common certificate related options to both client and server.
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index ac5c4481a2..41d13f7080 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -27,11 +27,11 @@
-include("ssl_internal.hrl").
-include("ssl_connection.hrl").
--include_lib("public_key/include/public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
-define(DEFAULT_MAX_SESSION_CACHE, 1000).
-define(TWO_HOURS, 7200).
--define(SEVEN_DAYS, 604800).
+-define(SEVEN_DAYS, 604800).
%% Connection parameter configuration
-export([init/2,
@@ -175,22 +175,25 @@ group_pairs(Pairs) ->
dsa => []
}).
-group_pairs([#{private_key := #'ECPrivateKey'{parameters = {namedCurve, ?'id-Ed25519'}}} = Pair | Rest],
+group_pairs([#{private_key :=
+ #'ECPrivateKey'{parameters = {namedCurve, ?'id-Ed25519'}}} = Pair | Rest],
#{eddsa := EDDSA} = Group) ->
group_pairs(Rest, Group#{eddsa => [Pair | EDDSA]});
-group_pairs([#{private_key := #'ECPrivateKey'{parameters = {namedCurve, ?'id-Ed448'}}} = Pair | Rest],
+group_pairs([#{private_key :=
+ #'ECPrivateKey'{parameters = {namedCurve, ?'id-Ed448'}}} = Pair | Rest],
#{eddsa := EDDSA} = Group) ->
group_pairs(Rest, Group#{eddsa => [Pair | EDDSA]});
group_pairs([#{private_key := #'ECPrivateKey'{}} = Pair | Rest], #{ecdsa := ECDSA} = Group) ->
group_pairs(Rest, Group#{ecdsa => [Pair | ECDSA]});
-group_pairs([#{private_key := {#'RSAPrivateKey'{}, #'RSASSA-PSS-params'{}}} = Pair | Rest],
+group_pairs([#{private_key := {#'RSAPrivateKey'{}, #'RSASSA-PSS-params'{}}} = Pair | Rest],
#{rsa_pss_pss := RSAPSS} = Group) ->
group_pairs(Rest, Group#{rsa_pss_pss => [Pair | RSAPSS]});
group_pairs([#{private_key := #'RSAPrivateKey'{}} = Pair | Rest], #{rsa := RSA} = Group) ->
group_pairs(Rest, Group#{rsa => [Pair | RSA]});
group_pairs([#{private_key := #'ML-DSAPrivateKey'{}} = Pair | Rest], #{mldsa := MLDSA} = Group) ->
group_pairs(Rest, Group#{mldsa => [Pair | MLDSA]});
-group_pairs([#{private_key := #'SLH-DSAPrivateKey'{}} = Pair | Rest], #{slhdsa := SLHDSA} = Group) ->
+group_pairs([#{private_key :=
+ #'SLH-DSAPrivateKey'{}} = Pair | Rest], #{slhdsa := SLHDSA} = Group) ->
group_pairs(Rest, Group#{slhdsa => [Pair | SLHDSA]});
group_pairs([#{private_key := #'DSAPrivateKey'{}} = Pair | Rest], #{dsa := DSA} = Group) ->
group_pairs(Rest, Group#{dsa => [Pair | DSA]});
@@ -337,10 +340,11 @@ private_key(#'PrivateKeyInfo'{privateKeyAlgorithm =
Algorithm == ?'id-slh-dsa-shake-256f';
Algorithm == ?'id-slh-dsa-shake-256s' ->
public_key:der_decode('SLH-DSA-PrivateKey', DerKey);
-private_key(#'PrivateKeyInfo'{privateKeyAlgorithm =
- #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?'id-ecPublicKey',
- parameters = {asn1_OPENTYPE, Parameters}},
- privateKey = Key}) ->
+private_key(#'PrivateKeyInfo'{
+ privateKeyAlgorithm =
+ #'PrivateKeyInfo_privateKeyAlgorithm'{algorithm = ?'id-ecPublicKey',
+ parameters = {asn1_OPENTYPE, Parameters}},
+ privateKey = Key}) ->
ECKey = public_key:der_decode('ECPrivateKey', iolist_to_binary(Key)),
ECParameters = public_key:der_decode('EcpkParameters', Parameters),
ECKey#'ECPrivateKey'{parameters = ECParameters};
@@ -476,7 +480,7 @@ dh_file(DbHandle, DHParamFile) ->
end
catch
_:Reason ->
- file_error(DHParamFile, {dhfile, Reason})
+ file_error(DHParamFile, {dhfile, Reason})
end.
%%====================================================================
@@ -815,7 +819,8 @@ opt_certs(UserOpts, #{log_level := LogLevel, versions := Versions} = Opts0, Env)
{old, [CertKey]} ->
opt_old_certs(UserOpts, CertKey, Opts0, Env);
{Where, CKs0} when is_list(CKs0) ->
- warn_override(Where, UserOpts, certs_keys, [cert,certfile,key,keyfile,password], LogLevel),
+ warn_override(Where, UserOpts, certs_keys,
+ [cert,certfile,key,keyfile,password], LogLevel),
CKs = lists:foldl(fun(CK0, Acc) ->
CK = check_legacy_cert_key(Versions, CK0, #{}, LogLevel),
case maps:size(CK) =:= 0 of
@@ -839,7 +844,7 @@ opt_old_certs(UserOpts, CertKeys, #{log_level := LogLevel, versions := Versions}
check_legacy_cert_key(Versions, UserOpts, CertKeys0, LogLevel) ->
CertKeys1 = handle_legacy_cert_opt(UserOpts, CertKeys0, LogLevel),
- CertKeys = handle_legacy_key_opt(Versions, UserOpts, CertKeys1),
+ CertKeys = handle_legacy_key_opt(Versions, UserOpts, CertKeys1),
handle_possible_legacy_key_password(UserOpts, CertKeys).
handle_legacy_cert_opt(UserOpts, CertKeys, LogLevel) ->
@@ -1136,7 +1141,7 @@ valid_signature_algs(AlgCertSchemes0, #{versions := Versions} = Opts, UserOpts,
{old, Algs} ->
{Algs, AlgCertSchemes0}
end.
-
+
opt_signature_algs_not_valid(UserOpts, #{versions := Versions} = Opts0) ->
Opts =
case get_opt_list(signature_algs, undefined, UserOpts, Opts0) of
@@ -1167,7 +1172,8 @@ opt_alpn(UserOpts, #{versions := Versions} = Opts, #{role := server}) ->
{Where, NPA} = get_opt_list(next_protocols_advertised, undefined, UserOpts, Opts),
validate_protocols(is_list(NPA), next_protocols_advertised, NPA),
- assert_version_dep(is_list(NPA), next_protocols_advertised, Versions, ['tlsv1','tlsv1.1','tlsv1.2']),
+ assert_version_dep(is_list(NPA), next_protocols_advertised, Versions,
+ ['tlsv1','tlsv1.1','tlsv1.2']),
assert_client_only(alpn_advertised_protocols, UserOpts),
assert_client_only(client_preferred_next_protocols, UserOpts),
@@ -1203,10 +1209,11 @@ make_next_protocol_selector({Precedence, PrefProtcol} = V) ->
make_next_protocol_selector({Precedence, PrefProtcol, ?NO_PROTOCOL});
make_next_protocol_selector({Precedence, AllProtocols, DefP} = V) ->
option_error(not is_list(AllProtocols), client_preferred_next_protocols, V),
- option_error(not (is_binary(DefP) andalso byte_size(DefP) < 256), client_preferred_next_protocols, V),
+ option_error(not (is_binary(DefP) andalso byte_size(DefP) < 256),
+ client_preferred_next_protocols, V),
validate_protocols(true, client_preferred_next_protocols, AllProtocols),
case Precedence of
- client ->
+ client ->
fun(Advertised) ->
Search = fun(P) -> lists:member(P, Advertised) end,
case lists:search(Search, AllProtocols) of
@@ -1398,7 +1405,7 @@ handle_user_lookup(UserOpts, #{versions := Versions} = Opts) ->
{_, Lookup} ->
Lookup
end.
-
+
opt_supported_groups(UserOpts, #{versions := TlsVsns} = Opts, Env) ->
SG = case get_opt_list(supported_groups, undefined, UserOpts, Opts) of
@@ -1421,7 +1428,7 @@ opt_supported_groups(UserOpts, #{versions := TlsVsns} = Opts, Env) ->
{old, CPS0} -> CPS0;
{_, CPS0} -> handle_cipher_option(CPS0, TlsVsns)
end,
-
+
ECCS = try assert_version_dep(eccs, TlsVsns, ['tlsv1.2', 'tlsv1.1', 'tlsv1']) of
_ ->
case get_opt_list(eccs, undefined, UserOpts, Opts) of
@@ -1468,11 +1475,30 @@ opt_crl(UserOpts, Opts, _Env) ->
opt_handshake(UserOpts, Opts, _Env) ->
{_, HS} = get_opt_of(handshake, [hello, full], full, UserOpts, Opts),
- {_, MHSS} = get_opt_int(max_handshake_size, 1, ?MAX_UNIT24, ?DEFAULT_MAX_HANDSHAKE_SIZE,
+ DefaultMaxHS = default_max_hs(Opts),
+
+ {_, MHSS} = get_opt_int(max_handshake_size, 1, ?MAX_UNIT24 , DefaultMaxHS,
UserOpts, Opts),
Opts#{handshake => HS, max_handshake_size => MHSS}.
+default_max_hs(#{signature_algs:= undefined}) ->
+ ?DEFAULT_MAX_EARLY_DATA_SIZE;
+default_max_hs(#{signature_algs:= Algs}) ->
+ %%% In OTP-26 max handshake_size was lowered by half for most
+ %%% handshakes would fit that size and OpenSSL had a lower default
+ Set = sets:intersection(sets:from_list(Algs, [{version, 2}]),
+ sets:from_list(tls_v1:slh_dsa_schemes(),
+ [{version, 2}])),
+ case sets:is_empty(Set) of
+ true ->
+ ?DEFAULT_MAX_HANDSHAKE_SIZE;
+ false ->
+ %% SLH_DSA creates fairly big handshake sizes so raise limit back
+ %% if these algorithms are supported,
+ ?DEFAULT_MAX_HANDSHAKE_SIZE * 2
+ end.
+
opt_use_srtp(UserOpts, #{protocol := Protocol} = Opts, _Env) ->
UseSRTP = case get_opt_map(use_srtp, undefined, UserOpts, Opts) of
{old, UseSRTP0} ->
@@ -1788,7 +1814,8 @@ validate_server_cert_opts(#{handshake := hello}, _) ->
%% This verification should be done only when handshake := full, as options
%% to fulfill the requirement can be supplied at that time.
ok;
-validate_server_cert_opts(#{certs_keys := [_|_]=CertsKeys, ciphers := CPHS, versions := Versions}, _) ->
+validate_server_cert_opts(#{certs_keys := [_|_]=CertsKeys,
+ ciphers := CPHS, versions := Versions}, _) ->
validate_certs_or_anon_ciphers(CertsKeys, CPHS, Versions);
validate_server_cert_opts(#{ciphers := CPHS, versions := Versions}, _) ->
validate_anon_ciphers(CPHS, Versions).
@@ -1872,7 +1899,7 @@ binary_cipher_suites([?TLS_1_3], []) ->
%% not require explicit configuration TLS-1.3
%% only mode.
default_binary_suites(exclusive, ?TLS_1_3);
-binary_cipher_suites([Version| _], []) ->
+binary_cipher_suites([Version| _], []) ->
%% Defaults to all supported suites that does
%% not require explicit configuration
default_binary_suites(default, Version);
@@ -1928,9 +1955,9 @@ tuple_to_map({Kex, Cipher, Mac, Prf}) ->
prf => Prf}.
%% Backwards compatible
-tuple_to_map_mac(aes_128_gcm, _) ->
+tuple_to_map_mac(aes_128_gcm, _) ->
aead;
-tuple_to_map_mac(aes_256_gcm, _) ->
+tuple_to_map_mac(aes_256_gcm, _) ->
aead;
tuple_to_map_mac(chacha20_poly1305, _) ->
aead;
@@ -1974,20 +2001,20 @@ unambiguous_path(Value) ->
validate_filename(UP, cacertfile).
%% Assert that basic options are on the format {Key, Value}
-%% with a few exceptions and phase out log_alert
+%% with a few exceptions and phase out log_alert
handle_option_format([], Acc) ->
lists:reverse(Acc);
handle_option_format([{log_alert, Bool} | Rest], Acc) when is_boolean(Bool) ->
case proplists:get_value(log_level, Acc ++ Rest, undefined) of
undefined ->
- handle_option_format(Rest, [{log_level,
+ handle_option_format(Rest, [{log_level,
map_log_level(Bool)} | Acc]);
_ ->
handle_option_format(Rest, Acc)
end;
handle_option_format([{Key,_} = Opt | Rest], Acc) when is_atom(Key) ->
handle_option_format(Rest, [Opt | Acc]);
-%% Handle exceptions
+%% Handle exceptions
handle_option_format([{raw,_,_,_} = Opt | Rest], Acc) ->
handle_option_format(Rest, [Opt | Acc]);
handle_option_format([inet = Opt | Rest], Acc) ->
@@ -2074,11 +2101,11 @@ connection_cb(tls) ->
connection_cb(dtls) ->
dtls_gen_connection.
-handle_possible_version_change([Version|_], [Version|_] = VersionOpt, OrigSSLOpts, _) ->
+handle_possible_version_change([Version|_], [Version|_] = VersionOpt, OrigSSLOpts, _) ->
filter_for_versions(VersionOpt, OrigSSLOpts);
handle_possible_version_change(_, [], OrigSSLOpts, _) ->
OrigSSLOpts;
-handle_possible_version_change(_, VersionsOpt, #{ciphers := Suites} = OrigSSLOpts, Record) ->
+handle_possible_version_change(_, VersionsOpt, #{ciphers := Suites} = OrigSSLOpts, Record) ->
FallbackSuites = ciphers_for_version(VersionsOpt, Suites, Record),
filter_for_versions(VersionsOpt, OrigSSLOpts#{ciphers => FallbackSuites}).
@@ -2167,7 +2194,7 @@ validate_cert_keys_pems(#{certs_keys := CertKeys}, PemCacheName) ->
case handle_key_file(CertKey, PemCacheName) of
ok ->
true;
- {error, Reason} ->
+ {error, Reason} ->
#{keyfile := KeyFile} = CertKey,
option_error(keyfile, {binary_to_list(KeyFile), Reason})
end
@@ -2177,7 +2204,7 @@ validate_cert_keys_pems(#{certs_keys := CertKeys}, PemCacheName) ->
validate_cert_keys_pems(_, _) ->
%% Needs validation when called from listen, to
%% provide early failure.
- true.
+ true.
validate_cacerts_pem(#{cacertfile := Cacertfile}, PemCacheName) ->
case do_handle_cert_file(Cacertfile, PemCacheName) of
@@ -2195,7 +2222,7 @@ validate_dh_pem(#{dhfile := DHFile}, PemCacheName) ->
true;
{error, Reason} ->
option_error(dhfile, Reason)
- end;
+ end;
validate_dh_pem(_, _) ->
true.
@@ -2207,7 +2234,7 @@ handle_cert_file(_,_) ->
do_handle_cert_file(File, PemCacheName) ->
case file:read_file(File) of
- {ok, Pem} ->
+ {ok, Pem} ->
Entries = public_key:pem_decode(Pem),
case lists:keyfind('Certificate', 1, Entries) of
false ->
--
2.51.0