Mercurial > prosody-hg
changeset 14221:565debd9c37a
Merge 13.0->trunk
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Fri, 12 Jun 2026 13:05:10 +0100 |
| parents | 2ec517d8d43e (current diff) 8f34af6a3821 (diff) |
| children | ad7f7b5f4792 |
| files | core/certmanager.lua core/features.lua util/human/io.lua util/prosodyctl/check.lua |
| diffstat | 7 files changed, 550 insertions(+), 84 deletions(-) [+] |
line wrap: on
line diff
--- a/core/certmanager.lua Sun Jun 07 20:32:55 2026 +0200 +++ b/core/certmanager.lua Fri Jun 12 13:05:10 2026 +0100 @@ -12,6 +12,8 @@ local tls = require "prosody.net.tls_luasec"; local stat = require "lfs".attributes; +local tlsref = require "prosody.util.tlsref"; + local x509 = require "prosody.util.x509"; local lfs = require "lfs"; @@ -216,84 +218,6 @@ dane = tls.features.capabilities.dane and configmanager.get("*", "use_dane") ~= false and { "no_ee_namechecks" }; } --- https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.1 -local ffdhe2048 = [[ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz -+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a -87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 -YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi -7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD -ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== ------END DH PARAMETERS----- -]] - -local mozilla_ssl_configs = { - -- https://wiki.mozilla.org/Security/Server_Side_TLS - -- Version 5.7 as of 2023-07-09 - modern = { - protocol = "tlsv1_3"; - options = { cipher_server_preference = false }; - ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; - intermediate = { - protocol = "tlsv1_2+"; - dhparam = ffdhe2048; - options = { cipher_server_preference = false }; - ciphers = { - "ECDHE-ECDSA-AES128-GCM-SHA256"; - "ECDHE-RSA-AES128-GCM-SHA256"; - "ECDHE-ECDSA-AES256-GCM-SHA384"; - "ECDHE-RSA-AES256-GCM-SHA384"; - "ECDHE-ECDSA-CHACHA20-POLY1305"; - "ECDHE-RSA-CHACHA20-POLY1305"; - "DHE-RSA-AES128-GCM-SHA256"; - "DHE-RSA-AES256-GCM-SHA384"; - "DHE-RSA-CHACHA20-POLY1305"; - }; - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; - old = { - protocol = "tlsv1+"; - dhparam = nil; -- openssl dhparam 1024 - options = { cipher_server_preference = true }; - ciphers = { - "ECDHE-ECDSA-AES128-GCM-SHA256"; - "ECDHE-RSA-AES128-GCM-SHA256"; - "ECDHE-ECDSA-AES256-GCM-SHA384"; - "ECDHE-RSA-AES256-GCM-SHA384"; - "ECDHE-ECDSA-CHACHA20-POLY1305"; - "ECDHE-RSA-CHACHA20-POLY1305"; - "DHE-RSA-AES128-GCM-SHA256"; - "DHE-RSA-AES256-GCM-SHA384"; - "DHE-RSA-CHACHA20-POLY1305"; - "ECDHE-ECDSA-AES128-SHA256"; - "ECDHE-RSA-AES128-SHA256"; - "ECDHE-ECDSA-AES128-SHA"; - "ECDHE-RSA-AES128-SHA"; - "ECDHE-ECDSA-AES256-SHA384"; - "ECDHE-RSA-AES256-SHA384"; - "ECDHE-ECDSA-AES256-SHA"; - "ECDHE-RSA-AES256-SHA"; - "DHE-RSA-AES128-SHA256"; - "DHE-RSA-AES256-SHA256"; - "AES128-GCM-SHA256"; - "AES256-GCM-SHA384"; - "AES128-SHA256"; - "AES256-SHA256"; - "AES128-SHA"; - "AES256-SHA"; - "DES-CBC3-SHA"; - }; - curveslist = { "X25519"; "prime256v1"; "secp384r1" }; - ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; - }; -}; - - if tls.features.curves then for i = #core_defaults.curveslist, 1, -1 do if not tls.features.curves[ core_defaults.curveslist[i] ] then @@ -321,12 +245,33 @@ -- We can't read the password interactively when daemonized password = function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end; }); - local profile = configmanager.get("*", "tls_profile") or "intermediate"; - if mozilla_ssl_configs[profile] then - cfg:apply(mozilla_ssl_configs[profile]); - elseif profile ~= "legacy" then - log("error", "Invalid value for 'tls_profile': expected one of \"modern\", \"intermediate\" (default), \"old\" or \"legacy\" but got %q", profile); - return nil, "Invalid configuration, 'tls_profile' had an unknown value."; + local profile_version = configmanager.get("*", "tls_profile_version"); + local profile_name = configmanager.get("*", "tls_profile"); + + if profile_name ~= "legacy" then + local tls_profile, tls_profile_err = tlsref.get_profile(profile_version, profile_name); + if not tls_profile then + if tls_profile_err == "unknown-version" then + log( + "error", + "Invalid value %q for 'tls_profile_version': expected one of: \"%s\" or \"latest\"", + profile_version, + tlsref.get_valid_versions():concat("\", \"") + ); + return nil, "Invalid configuration, 'tls_profile_version' had an unknown value."; + elseif tls_profile_err == "unknown-profile" then + log( + "error", + "Invalid value %q for 'tls_profile': expected one of \"%s\" or \"legacy\" (default: %q)", + profile_name, + tlsref.get_valid_profile_names(profile_version):concat("\", \""), + tlsref.get_default_profile_name(profile_version) + ); + return nil, "Invalid configuration, 'tls_profile' had an unknown value."; + end + else + cfg:apply(tls_profile); + end end cfg:apply(global_ssl_config);
--- a/core/features.lua Sun Jun 07 20:32:55 2026 +0200 +++ b/core/features.lua Fri Jun 12 13:05:10 2026 +0100 @@ -39,5 +39,8 @@ -- SIGUSR1 and 2 events "signal-events"; + + -- util.human.io.simple_version_compare + "simple-version-compare"; }; };
--- a/spec/util_human_io_spec.lua Sun Jun 07 20:32:55 2026 +0200 +++ b/spec/util_human_io_spec.lua Fri Jun 12 13:05:10 2026 +0100 @@ -100,6 +100,79 @@ return assert.is_nil(human_io.parse_duration_lax("two weeks"), "\"2 weeks\" -> nil"); end); end); + + describe("simple_version_compare", function () + local version_compare = human_io.simple_version_compare; + + local function cmp_version(a, b, exp) + assert.equal(exp, version_compare(a, b), ("%q < %q"):format(a, b)); + end + + it("orders versions with differing major numbers", function () + cmp_version("1.0", "2.0", true); + cmp_version("2.0", "1.0", false); + end); + + it("orders versions with differing minor numbers", function () + cmp_version("1.0", "1.1", true); + cmp_version("1.1", "1.0", false); + end); + + it("treats a shorter version as less than a longer one sharing the same prefix", function () + cmp_version("1.0", "1.0.1", true); + cmp_version("1.0.1", "1.0", false); + + cmp_version("1", "1.0", true); + cmp_version("1", "1.1", true); + + cmp_version("1.0.0", "1.0.0.1", true); + end); + + it("compares components left-to-right regardless of how many components there are", function () + cmp_version("1.0.1", "1.1", true); + cmp_version("1.1", "1.0.1", false); + + cmp_version("1.0.1", "1.1.0", true); + cmp_version("1.1.0", "1.0.1", false); + + cmp_version("1.0.0.1", "1.0.1", true); + end); + + it("never considers a version less than itself", function () + cmp_version("0", "0", false); + cmp_version("1.0", "1.0", false); + cmp_version("1.2.3", "1.2.3", false); + cmp_version("foo", "foo", false); + end); + + it("compares components numerically", function () + cmp_version("1.9", "1.10", true); + cmp_version("1.10", "1.9", false); + + cmp_version("1.9.9", "1.10.0", true); + + cmp_version("2.0", "10.0", true); + cmp_version("10.0", "2.0", false); + + cmp_version("1.2", "1.10", true); + + cmp_version("1.99", "1.100", true); + cmp_version("1.100", "1.99", false); + end); + + it("handles leading zeros in numeric components", function () + cmp_version("1.02", "1.10", true); + cmp_version("1.10", "1.02", false); + end); + + it("treats an empty or non-numeric string as a version with no components", function () + cmp_version("", "1.0", true); + cmp_version("1.0", "", false); + cmp_version("foo", "1.0", true); + cmp_version("1.0", "foo", false); + cmp_version("", "", false); + end); + end); end);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spec/util_tlsref_spec.lua Fri Jun 12 13:05:10 2026 +0100 @@ -0,0 +1,168 @@ +local tlsref = require "prosody.util.tlsref"; + +describe("util.tlsref", function() + local version_compare = require "prosody.util.human.io".simple_version_compare; + + describe("default version", function() + it("is not greater than latest version", function () + local default_version = tlsref.get_default_version(); + local latest_version = tlsref.get_latest_version(); + assert.equal(false, version_compare(latest_version, default_version)); + end); + + it("is a known version", function () + assert.is_table(tlsref.get_profile(tlsref.get_default_version())); + end); + end); + + describe("latest version", function () + it("is a known version", function () + assert.is_table(tlsref.get_profile("latest")); + end); + + it("is the first entry of get_valid_versions()", function () + local versions = tlsref.get_valid_versions(); + assert.equal(tlsref.get_latest_version(), versions[1]); + end); + end); + + describe("get_valid_versions()", function () + it("returns versions in descending order", function () + local versions = tlsref.get_valid_versions(); + assert.is_true(#versions >= 1); + for i = 1, #versions-1 do + -- versions[i] must sort before versions[i+1] + assert.equal(false, version_compare(versions[i], versions[i+1])); + end + end); + + it("contains the default version", function () + local found = false; + for _, v in ipairs(tlsref.get_valid_versions()) do + if v == tlsref.get_default_version() then found = true; end + end + assert.is_true(found); + end); + end); + + describe("get_valid_profile_names()", function () + it("returns an error for unknown versions", function () + local names, err = tlsref.get_valid_profile_names("0.0"); + assert.is_nil(names); + assert.equal("unknown-version", err); + end); + + it("uses the default version when none is given", function () + assert.same( + tlsref.get_valid_profile_names(tlsref.get_default_version()), + tlsref.get_valid_profile_names() + ); + end); + + it("accepts 'latest' as a version", function () + assert.same( + tlsref.get_valid_profile_names(tlsref.get_latest_version()), + tlsref.get_valid_profile_names("latest") + ); + end); + + it("lists the most-preferred profile first", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + local names = tlsref.get_valid_profile_names(version); + assert.equal("modern", names[1], + ("most-preferred profile should be first for version %s"):format(version)); + end + end); + end); + + describe("get_profile()", function () + it("returns the default profile of the default version when called with no arguments", function () + assert.equal( + tlsref.get_profile(tlsref.get_default_version(), tlsref.get_default_profile_name()), + tlsref.get_profile() + ); + end); + + it("resolves 'latest' to the latest version", function () + assert.equal( + tlsref.get_profile(tlsref.get_latest_version()), + tlsref.get_profile("latest") + ); + end); + + it("returns an error for unknown versions", function () + local profile, err = tlsref.get_profile("0.0"); + assert.is_nil(profile); + assert.equal("unknown-version", err); + end); + + it("returns an error for unknown profile names", function () + local profile, err = tlsref.get_profile(nil, "no-such-profile"); + assert.is_nil(profile); + assert.equal("unknown-profile", err); + end); + + it("returns an error for profiles absent from a given version", function () + -- 'old' was dropped from the 6.0 guidelines + local profile, err = tlsref.get_profile("6.0", "old"); + assert.is_nil(profile); + assert.equal("unknown-profile", err); + end); + + it("resolves every advertised version/profile combination", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile, err = tlsref.get_profile(version, name); + assert.is_table(profile, + ("get_profile(%q, %q) failed: %s"):format(version, name, err)); + end + end + end); + end); + + describe("get_default_profile_name()", function () + it("returns an error for unknown versions", function () + local name, err = tlsref.get_default_profile_name("0.0"); + assert.is_nil(name); + assert.equal("unknown-version", err); + end); + + it("accepts 'latest' as a version", function () + assert.is_string(tlsref.get_default_profile_name("latest")); + end); + + it("returns a profile name valid for every version", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + local name = tlsref.get_default_profile_name(version); + assert.is_string(name); + assert.is_table(tlsref.get_profile(version, name), + ("default profile %q missing from version %s"):format(name, version)); + end + end); + end); + + describe("profile contents", function () + it("every profile specifies a TLS protocol version", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile = tlsref.get_profile(version, name); + assert.is_string(profile.protocol); + assert.truthy(profile.protocol:match("^tlsv1"), + ("unexpected protocol %q in %s/%s"):format(profile.protocol, version, name)); + end + end + end); + + it("every profile specifies TLS 1.3 ciphersuites and curves", function () + for _, version in ipairs(tlsref.get_valid_versions()) do + for _, name in ipairs(tlsref.get_valid_profile_names(version)) do + local profile = tlsref.get_profile(version, name); + assert.is_table(profile.ciphersuites); + assert.is_true(#profile.ciphersuites > 0); + assert.is_table(profile.curveslist); + assert.is_true(#profile.curveslist > 0); + end + end + end); + end); +end);
--- a/util/human/io.lua Sun Jun 07 20:32:55 2026 +0200 +++ b/util/human/io.lua Fri Jun 12 13:05:10 2026 +0100 @@ -235,6 +235,29 @@ return tonumber(n) * ( multipliers_lax[m] or 1 ); end +-- Compares simple version strings (numeric only, not semver compatible) +-- (can be passed to table.sort) +local function simple_version_compare(a, b) + local a_f, a_s, a_part = a:gmatch("%d+"); + local b_f, b_s, b_part = b:gmatch("%d+"); + + a_part = a_f(a_s, a_part); + b_part = b_f(b_s, b_part); + + while a_part and b_part do + local an, bn = tonumber(a_part), tonumber(b_part); + if an ~= bn then + return an < bn; + end + a_part = a_f(a_s, a_part); + b_part = b_f(b_s, b_part); + end + + -- return true (a is lower) if b is exhausted + -- otherwise, they are equal or b is longer/greater (-> return false) + return b_part ~= nil; +end + return { getchar = getchar; getline = getline; @@ -250,4 +273,5 @@ table = new_table; parse_duration = parse_duration; parse_duration_lax = parse_duration_lax; + simple_version_compare = simple_version_compare; };
--- a/util/prosodyctl/check.lua Sun Jun 07 20:32:55 2026 +0200 +++ b/util/prosodyctl/check.lua Fri Jun 12 13:05:10 2026 +0100 @@ -465,6 +465,7 @@ "statistics_interval", "tcp_keepalives", "tls_profile", + "tls_profile_version", "trusted_proxies", "umask", "use_dane", @@ -733,6 +734,26 @@ end do + local tlsref = require "prosody.util.tlsref"; + local tls_profile_name = configmanager.get("*", "tls_profile"); + local tls_profile_version = configmanager.get("*", "tls_profile_version"); + local profile, err = tlsref.get_profile(tls_profile_version, tls_profile_name); + if not profile then + print(""); + print(" TLS profile selection:"); + if err == "unknown-version" then + print((" tls_profile_version is set to an unknown value (%q)"):format(tls_profile_version)); + print((" Valid values: \"%s\""):format(tlsref.get_valid_versions():concat("\", \""))); + elseif err == "unknown-profile" then + print((" tls_profile is set to an unknown value (%q)"):format(tls_profile_name)); + print((" Valid values: \"%s\""):format(tlsref.get_valid_profile_names():concat("\", \""))); + else + print((" Unknown error loading the configured profile: %s"):format(err)); + end + end + end + + do local registration_enabled_hosts = {}; for host in enabled_hosts() do local host_modules, component = modulemanager.get_modules_for_host(host);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/tlsref.lua Fri Jun 12 13:05:10 2026 +0100 @@ -0,0 +1,232 @@ +local array = require "prosody.util.array"; +local it = require "prosody.util.iterators"; +local version_compare = require "prosody.util.human.io".simple_version_compare; + +local latest_version = "6.0" +local default_version = "5.7"; +local default_profile = "intermediate"; -- should exist in all profiles + +-- https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.1 +local ffdhe2048 = [[ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- +]]; + +local profiles = { + -- https://wiki.mozilla.org/Security/Server_Side_TLS + ["6.0"] = { + -- Version 6.0 as of 2026-04-08 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; + + ["5.8"] = { + -- Version 5.8 as of 2026-04-08 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + old = { + protocol = "tlsv1+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = true }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "ECDHE-ECDSA-AES128-SHA256"; + "ECDHE-RSA-AES128-SHA256"; + "ECDHE-ECDSA-AES128-SHA"; + "ECDHE-RSA-AES128-SHA"; + "ECDHE-ECDSA-AES256-SHA384"; + "ECDHE-RSA-AES256-SHA384"; + "ECDHE-ECDSA-AES256-SHA"; + "ECDHE-RSA-AES256-SHA"; + "AES128-GCM-SHA256"; + "AES256-GCM-SHA384"; + "AES128-SHA256"; + "AES256-SHA256"; + "AES128-SHA"; + "AES256-SHA"; + "DES-CBC3-SHA"; + }; + curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; + + ["5.7"] = { + -- Version 5.7 as of 2023-07-09 + modern = { + protocol = "tlsv1_3"; + options = { cipher_server_preference = false }; + ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + intermediate = { + protocol = "tlsv1_2+"; + dhparam = ffdhe2048; + options = { cipher_server_preference = false }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "DHE-RSA-AES128-GCM-SHA256"; + "DHE-RSA-AES256-GCM-SHA384"; + "DHE-RSA-CHACHA20-POLY1305"; + }; + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + old = { + protocol = "tlsv1+"; + dhparam = nil; -- openssl dhparam 1024 + options = { cipher_server_preference = true }; + ciphers = { + "ECDHE-ECDSA-AES128-GCM-SHA256"; + "ECDHE-RSA-AES128-GCM-SHA256"; + "ECDHE-ECDSA-AES256-GCM-SHA384"; + "ECDHE-RSA-AES256-GCM-SHA384"; + "ECDHE-ECDSA-CHACHA20-POLY1305"; + "ECDHE-RSA-CHACHA20-POLY1305"; + "DHE-RSA-AES128-GCM-SHA256"; + "DHE-RSA-AES256-GCM-SHA384"; + "DHE-RSA-CHACHA20-POLY1305"; + "ECDHE-ECDSA-AES128-SHA256"; + "ECDHE-RSA-AES128-SHA256"; + "ECDHE-ECDSA-AES128-SHA"; + "ECDHE-RSA-AES128-SHA"; + "ECDHE-ECDSA-AES256-SHA384"; + "ECDHE-RSA-AES256-SHA384"; + "ECDHE-ECDSA-AES256-SHA"; + "ECDHE-RSA-AES256-SHA"; + "DHE-RSA-AES128-SHA256"; + "DHE-RSA-AES256-SHA256"; + "AES128-GCM-SHA256"; + "AES256-GCM-SHA384"; + "AES128-SHA256"; + "AES256-SHA256"; + "AES128-SHA"; + "AES256-SHA"; + "DES-CBC3-SHA"; + }; + curveslist = { "X25519"; "prime256v1"; "secp384r1" }; + ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" }; + }; + }; +}; + +local function get_valid_versions() + return array.collect(it.keys(profiles)):sort(version_compare):reverse(); +end + +local pref_order = { modern = 100, intermediate = 50, old = 0 }; + +local function sort_profile_by_pref(a, b) + local pref_a, pref_b = pref_order[a] or 0, pref_order[b] or 0; + return pref_a > pref_b; -- Best first +end + +local function get_valid_profile_names(version) + if version == "latest" then + version = latest_version; + end + local version_profiles = profiles[version or default_version]; + if not version_profiles then + return nil, "unknown-version"; + end + return array.collect(it.keys(version_profiles)):sort(sort_profile_by_pref); +end + +local function get_profile(version, profile_name) + if version == "latest" then + version = latest_version; + end + local version_profiles = profiles[version or default_version]; + if not version_profiles then + return nil, "unknown-version"; + end + local profile = version_profiles[profile_name or default_profile]; + if not profile then + return nil, "unknown-profile"; + end + return profile; +end + +local function get_default_version() + return default_version; +end + +local function get_latest_version() + return latest_version; +end + +local function get_default_profile_name(version) + -- Default profile name is currently the same across all versions, + -- but can't guarantee this in the future - ensure valid version + if version ~= "latest" and not profiles[version or default_version] then + return nil, "unknown-version"; + end + return default_profile; +end + +return { + profiles = profiles; + get_default_version = get_default_version; + get_latest_version = get_latest_version; + get_default_profile_name = get_default_profile_name; + get_valid_versions = get_valid_versions; + get_valid_profile_names = get_valid_profile_names; + get_profile = get_profile; +};
