comparison util/tlsref.lua @ 14218:926f25af2ffe 13.0

util.tlsref: New util library to encapsulate the Mozilla/TLSRef recommendations Previously we embedded this data directly into core.certmanager. This splits it out, for various benefits: - Removes data from the business logic (the config parsing is complex enough as it is) - Allows easier testing and tracking of the data (this commit adds various consistency checks, so that we can have more confidence in future updates to the data not breaking stuff) This library also supports multiple versions of the recommendations. Previously we picked a single version, and put that into certmanager. But this meant we had to make choices for our users, and one of the choices is between advancing security standards and ensuring we don't break connectivity for people doing minor upgrades (deterring people from performing minor upgrades would have a security impact too). This library supports the concept of a "default" and a "latest" version, so we are now free to add new versions into stable releases, and admins can pick between compatibility and security.
author Matthew Wild <mwild1@gmail.com>
date Fri, 12 Jun 2026 13:01:56 +0100
parents
children ad7f7b5f4792
comparison
equal deleted inserted replaced
14217:7eb0e47418ab 14218:926f25af2ffe
1 local array = require "prosody.util.array";
2 local it = require "prosody.util.iterators";
3 local version_compare = require "prosody.util.human.io".simple_version_compare;
4
5 local latest_version = "6.0"
6 local default_version = "5.7";
7 local default_profile = "intermediate"; -- should exist in all profiles
8
9 -- https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.1
10 local ffdhe2048 = [[
11 -----BEGIN DH PARAMETERS-----
12 MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
13 +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
14 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
15 YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
16 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
17 ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
18 -----END DH PARAMETERS-----
19 ]];
20
21 local profiles = {
22 -- https://wiki.mozilla.org/Security/Server_Side_TLS
23 ["6.0"] = {
24 -- Version 6.0 as of 2026-04-08
25 modern = {
26 protocol = "tlsv1_3";
27 options = { cipher_server_preference = false };
28 ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these
29 curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" };
30 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
31 };
32 intermediate = {
33 protocol = "tlsv1_2+";
34 dhparam = ffdhe2048;
35 options = { cipher_server_preference = false };
36 ciphers = {
37 "ECDHE-ECDSA-AES128-GCM-SHA256";
38 "ECDHE-RSA-AES128-GCM-SHA256";
39 "ECDHE-ECDSA-AES256-GCM-SHA384";
40 "ECDHE-RSA-AES256-GCM-SHA384";
41 "ECDHE-ECDSA-CHACHA20-POLY1305";
42 "ECDHE-RSA-CHACHA20-POLY1305";
43 };
44 curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" };
45 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
46 };
47 };
48
49 ["5.8"] = {
50 -- Version 5.8 as of 2026-04-08
51 modern = {
52 protocol = "tlsv1_3";
53 options = { cipher_server_preference = false };
54 ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these
55 curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" };
56 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
57 };
58 intermediate = {
59 protocol = "tlsv1_2+";
60 dhparam = ffdhe2048;
61 options = { cipher_server_preference = false };
62 ciphers = {
63 "ECDHE-ECDSA-AES128-GCM-SHA256";
64 "ECDHE-RSA-AES128-GCM-SHA256";
65 "ECDHE-ECDSA-AES256-GCM-SHA384";
66 "ECDHE-RSA-AES256-GCM-SHA384";
67 "ECDHE-ECDSA-CHACHA20-POLY1305";
68 "ECDHE-RSA-CHACHA20-POLY1305";
69 };
70 curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" };
71 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
72 };
73 old = {
74 protocol = "tlsv1+";
75 dhparam = ffdhe2048;
76 options = { cipher_server_preference = true };
77 ciphers = {
78 "ECDHE-ECDSA-AES128-GCM-SHA256";
79 "ECDHE-RSA-AES128-GCM-SHA256";
80 "ECDHE-ECDSA-AES256-GCM-SHA384";
81 "ECDHE-RSA-AES256-GCM-SHA384";
82 "ECDHE-ECDSA-CHACHA20-POLY1305";
83 "ECDHE-RSA-CHACHA20-POLY1305";
84 "ECDHE-ECDSA-AES128-SHA256";
85 "ECDHE-RSA-AES128-SHA256";
86 "ECDHE-ECDSA-AES128-SHA";
87 "ECDHE-RSA-AES128-SHA";
88 "ECDHE-ECDSA-AES256-SHA384";
89 "ECDHE-RSA-AES256-SHA384";
90 "ECDHE-ECDSA-AES256-SHA";
91 "ECDHE-RSA-AES256-SHA";
92 "AES128-GCM-SHA256";
93 "AES256-GCM-SHA384";
94 "AES128-SHA256";
95 "AES256-SHA256";
96 "AES128-SHA";
97 "AES256-SHA";
98 "DES-CBC3-SHA";
99 };
100 curveslist = { "X25519MLKEM768"; "X25519"; "prime256v1"; "secp384r1" };
101 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
102 };
103 };
104
105 ["5.7"] = {
106 -- Version 5.7 as of 2023-07-09
107 modern = {
108 protocol = "tlsv1_3";
109 options = { cipher_server_preference = false };
110 ciphers = "DEFAULT"; -- TLS 1.3 uses 'ciphersuites' rather than these
111 curveslist = { "X25519"; "prime256v1"; "secp384r1" };
112 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
113 };
114 intermediate = {
115 protocol = "tlsv1_2+";
116 dhparam = ffdhe2048;
117 options = { cipher_server_preference = false };
118 ciphers = {
119 "ECDHE-ECDSA-AES128-GCM-SHA256";
120 "ECDHE-RSA-AES128-GCM-SHA256";
121 "ECDHE-ECDSA-AES256-GCM-SHA384";
122 "ECDHE-RSA-AES256-GCM-SHA384";
123 "ECDHE-ECDSA-CHACHA20-POLY1305";
124 "ECDHE-RSA-CHACHA20-POLY1305";
125 "DHE-RSA-AES128-GCM-SHA256";
126 "DHE-RSA-AES256-GCM-SHA384";
127 "DHE-RSA-CHACHA20-POLY1305";
128 };
129 curveslist = { "X25519"; "prime256v1"; "secp384r1" };
130 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
131 };
132 old = {
133 protocol = "tlsv1+";
134 dhparam = nil; -- openssl dhparam 1024
135 options = { cipher_server_preference = true };
136 ciphers = {
137 "ECDHE-ECDSA-AES128-GCM-SHA256";
138 "ECDHE-RSA-AES128-GCM-SHA256";
139 "ECDHE-ECDSA-AES256-GCM-SHA384";
140 "ECDHE-RSA-AES256-GCM-SHA384";
141 "ECDHE-ECDSA-CHACHA20-POLY1305";
142 "ECDHE-RSA-CHACHA20-POLY1305";
143 "DHE-RSA-AES128-GCM-SHA256";
144 "DHE-RSA-AES256-GCM-SHA384";
145 "DHE-RSA-CHACHA20-POLY1305";
146 "ECDHE-ECDSA-AES128-SHA256";
147 "ECDHE-RSA-AES128-SHA256";
148 "ECDHE-ECDSA-AES128-SHA";
149 "ECDHE-RSA-AES128-SHA";
150 "ECDHE-ECDSA-AES256-SHA384";
151 "ECDHE-RSA-AES256-SHA384";
152 "ECDHE-ECDSA-AES256-SHA";
153 "ECDHE-RSA-AES256-SHA";
154 "DHE-RSA-AES128-SHA256";
155 "DHE-RSA-AES256-SHA256";
156 "AES128-GCM-SHA256";
157 "AES256-GCM-SHA384";
158 "AES128-SHA256";
159 "AES256-SHA256";
160 "AES128-SHA";
161 "AES256-SHA";
162 "DES-CBC3-SHA";
163 };
164 curveslist = { "X25519"; "prime256v1"; "secp384r1" };
165 ciphersuites = { "TLS_AES_128_GCM_SHA256"; "TLS_AES_256_GCM_SHA384"; "TLS_CHACHA20_POLY1305_SHA256" };
166 };
167 };
168 };
169
170 local function get_valid_versions()
171 return array.collect(it.keys(profiles)):sort(version_compare):reverse();
172 end
173
174 local pref_order = { modern = 100, intermediate = 50, old = 0 };
175
176 local function sort_profile_by_pref(a, b)
177 local pref_a, pref_b = pref_order[a] or 0, pref_order[b] or 0;
178 return pref_a > pref_b; -- Best first
179 end
180
181 local function get_valid_profile_names(version)
182 if version == "latest" then
183 version = latest_version;
184 end
185 local version_profiles = profiles[version or default_version];
186 if not version_profiles then
187 return nil, "unknown-version";
188 end
189 return array.collect(it.keys(version_profiles)):sort(sort_profile_by_pref);
190 end
191
192 local function get_profile(version, profile_name)
193 if version == "latest" then
194 version = latest_version;
195 end
196 local version_profiles = profiles[version or default_version];
197 if not version_profiles then
198 return nil, "unknown-version";
199 end
200 local profile = version_profiles[profile_name or default_profile];
201 if not profile then
202 return nil, "unknown-profile";
203 end
204 return profile;
205 end
206
207 local function get_default_version()
208 return default_version;
209 end
210
211 local function get_latest_version()
212 return latest_version;
213 end
214
215 local function get_default_profile_name(version)
216 -- Default profile name is currently the same across all versions,
217 -- but can't guarantee this in the future - ensure valid version
218 if version ~= "latest" and not profiles[version or default_version] then
219 return nil, "unknown-version";
220 end
221 return default_profile;
222 end
223
224 return {
225 profiles = profiles;
226 get_default_version = get_default_version;
227 get_latest_version = get_latest_version;
228 get_default_profile_name = get_default_profile_name;
229 get_valid_versions = get_valid_versions;
230 get_valid_profile_names = get_valid_profile_names;
231 get_profile = get_profile;
232 };