Mercurial > prosody-hg
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 }; |
