MySQL 26.10.0
Source Code Documentation
dh_ecdh_config.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2018, 2026, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is designed to work with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have either included with
14 the program or referenced in the documentation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24*/
25
26#ifndef DH_KEYS_INCLUDED
27#define DH_KEYS_INCLUDED
28
29#ifdef MYSQL_SERVER
30#include "my_dbug.h"
31#endif /* MYSQL_SERVER */
32
33#include <openssl/dh.h>
34#include <openssl/err.h>
35#include <openssl/evp.h>
36#include <openssl/ssl.h>
37
38#include <algorithm>
39#include <string>
40#include <string_view>
41#include <vector>
42
43namespace {
44[[maybe_unused]] constexpr bool tls_pqc_sign_supported() {
45#if OPENSSL_VERSION_NUMBER >= 0x30500000L
46 return true;
47#else
48 return false;
49#endif
50}
51
52/**
53 Set DH paramenter for given SSL context
54
55 @param [in] ctx SSL context
56
57 @return status of operation
58 @retval false Success
59 @retval true Failure
60*/
61[[maybe_unused]] bool set_dh(SSL_CTX *ctx) {
62 int security_level = 2;
63 security_level = SSL_CTX_get_security_level(ctx);
64#ifdef MYSQL_SERVER
65 assert(security_level <= 5);
66#endif /* MYSQL_SERVER */
67 if (security_level < 2) security_level = 2;
68#ifdef MYSQL_SERVER
69 DBUG_EXECUTE_IF("crypto_policy_3", security_level = 3;);
70#endif /* MYSQL_SERVER */
71
72#if OPENSSL_VERSION_NUMBER >= 0x30000000L
73 if (SSL_CTX_set_dh_auto(ctx, 1) == 0) {
74 return true;
75 }
76#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
77
78 DH *dh = nullptr;
79 switch (security_level) {
80 case 1:
81 [[fallthrough]];
82 case 2:
83 dh = DH_new_by_nid(NID_ffdhe2048);
84 break;
85 case 3:
86 dh = DH_new_by_nid(NID_ffdhe3072);
87 break;
88 case 4:
89 dh = DH_new_by_nid(NID_ffdhe8192);
90 break;
91 case 5:
92 /* there is no RFC7919 approved prime for sec level 5 */
93 [[fallthrough]];
94 default:
95 break;
96 };
97 if (SSL_CTX_set_tmp_dh(ctx, dh) == 0) {
98 if (dh) DH_free(dh);
99 return true;
100 }
101 DH_free(dh);
102
103#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
104 return false;
105}
106
107/**
108 Filter a user-configured TLS key exchange list against the internal
109 allowlist and optional PQC-only restriction. Duplicate groups are ignored.
110
111 @param [in] tls_kex Colon-delimited TLS key exchange group list
112 @param [in] force_pqc Require only PQC-capable groups
113 @param [out] filtered Sanitized TLS key exchange group list
114
115 @returns status of operation
116 @retval false Success
117 @retval true Rejected group supplied or no allowlisted groups remain
118*/
119bool sanitize_tls_kex_list(const char *tls_kex, bool force_pqc,
120 std::string *filtered) {
121 filtered->clear();
122 if (tls_kex == nullptr || tls_kex[0] == '\0') return false;
123
124 static constexpr const char *classic_groups[] = {
125#ifdef NID_X25519
126 "X25519",
127#endif
128 "secp384r1", "secp256r1", "secp521r1"};
129#if OPENSSL_VERSION_NUMBER >= 0x30500000L
130 static constexpr const char *pqc_groups[] = {
131 "X25519MLKEM768", "secp384r1MLKEM1024", "secp256r1MLKEM768", "MLKEM512",
132 "MLKEM768"};
133#endif
134
135 auto is_allowed = [](std::string_view group, bool &is_pqc_capable) {
136#if OPENSSL_VERSION_NUMBER >= 0x30500000L
137 for (const char *candidate : pqc_groups) {
138 if (group == candidate) {
139 is_pqc_capable = true;
140 return true;
141 }
142 }
143#endif
144 is_pqc_capable = false;
145 for (const char *candidate : classic_groups) {
146 if (group == candidate) return true;
147 }
148 return false;
149 };
150
151 std::string groups(tls_kex);
152 std::vector<std::string_view> seen_groups;
153 size_t begin = 0;
154 while (begin <= groups.length()) {
155 const size_t end = groups.find(':', begin);
156 const std::string_view token{
157 groups.data() + begin,
158 end == std::string::npos ? groups.length() - begin : end - begin};
159
160 bool is_pqc_capable;
161 if (token.empty() || !is_allowed(token, is_pqc_capable) ||
162 (force_pqc && !is_pqc_capable))
163 return true;
164
165 if (std::find(seen_groups.begin(), seen_groups.end(), token) ==
166 seen_groups.end()) {
167 seen_groups.push_back(token);
168 if (!filtered->empty()) filtered->append(":");
169 filtered->append(token);
170 }
171
172 if (end == std::string::npos) break;
173 begin = end + 1;
174 }
175
176 return false;
177}
178
179/**
180 Set TLS supported group details
181
182 @param [in] ctx SSL Context
183 @param force_pqc Force Post-Quantum Crypto KEX algorithms
184 @param tls_kex Explicit TLS key exchange group list
185
186 @returns status of operation
187 @retval false Success
188 @retval true Error
189*/
190[[maybe_unused]] bool set_ecdh(SSL_CTX *ctx, bool force_pqc = false,
191 const char *tls_kex = nullptr) {
192 std::string filtered_tls_kex;
193 if (sanitize_tls_kex_list(tls_kex, force_pqc, &filtered_tls_kex)) return true;
194
195 if (!filtered_tls_kex.empty()) {
196#if OPENSSL_VERSION_NUMBER >= 0x30500000L
197 if (SSL_CTX_set1_groups_list(
198 ctx, const_cast<char *>(filtered_tls_kex.c_str())) == 0)
199 return true;
200#else /* OPENSSL_VERSION_NUMBER >= 0x30500000L */
201 auto group_name_to_nid = [](const std::string &group) {
202#ifdef NID_X25519
203 if (group == "X25519") return NID_X25519;
204#endif
205 if (group == "secp384r1") return NID_secp384r1;
206 if (group == "secp256r1") return NID_X9_62_prime256v1;
207 if (group == "secp521r1") return NID_secp521r1;
208 return NID_undef;
209 };
210
211 std::vector<int> groups;
212 groups.reserve(4);
213 size_t begin = 0;
214 while (begin <= filtered_tls_kex.length()) {
215 const size_t end = filtered_tls_kex.find(':', begin);
216 const std::string token =
217 end == std::string::npos
218 ? filtered_tls_kex.substr(begin)
219 : filtered_tls_kex.substr(begin, end - begin);
220 const int nid = group_name_to_nid(token);
221 if (nid != NID_undef) groups.push_back(nid);
222 if (end == std::string::npos) break;
223 begin = end + 1;
224 }
225
226 if (groups.empty()) return true;
227
228 if (SSL_CTX_set1_groups(ctx, groups.data(),
229 static_cast<int>(groups.size())) == 0)
230 return true;
231#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */
232
233 return false;
234 }
235
236#if OPENSSL_VERSION_NUMBER >= 0x30500000L
237 /* Add support for Post-Quantum Cryptography (PQC) TLS 1.3
238 * key exchange algorithms (introduced with OpenSSL >= 3.5.0).
239 *
240 * The configuration must work for both standard and FIPS mode
241 * (FIPS provider does not implement PQC yet), so all entries
242 * here are prefixed with ? to auto-ignore missing implementations.
243 *
244 * It must also work with TLS 1.2, i.e. PQC is currently optional,
245 * we do not enforce strict compliance yet to make the server work
246 * with legacy clients.
247 * At some point, we may enforce strict PQC mode compliance, phasing
248 * out the legacy, less secure protocols.
249 *
250 * The cryptographic groups list below (used for key exchange) favors
251 * hybrid PQC groups (vs pure PQC) for better compatibility and
252 * performance, with fallback to classical groups for legacy clients.
253 * The '/' tuple separator keeps the classical fallback below the PQC
254 * tuple, even when a TLS 1.3 client predicts only a classical key share. */
255 static constexpr char force_pqc_groups[] =
256 "X25519MLKEM768:" /* hybrid X25519 + ML-KEM-768 */
257 "secp384r1MLKEM1024:" /* hybrid P-384 + ML-KEM-1024 */
258 "secp256r1MLKEM768:" /* hybrid P-256 + ML-KEM-768 */
259 "MLKEM512:MLKEM768"; /* pure ML-KEM */
260 static constexpr char default_groups[] =
261 "?X25519MLKEM768:" /* hybrid X25519 + ML-KEM-768 (if available) */
262 "?secp384r1MLKEM1024:" /* hybrid P-384 + ML-KEM-1024 */
263 "?secp256r1MLKEM768:" /* hybrid P-256 + ML-KEM-768 */
264 "?MLKEM512:?MLKEM768/" /* pure ML-KEM */
265#ifdef NID_X25519
266 "?X25519:" /* classical groups as fallback */
267#endif
268 "?secp384r1:"
269 "?secp256r1:"
270 "?secp521r1";
271 static constexpr char fallback_groups[] =
272#ifdef NID_X25519
273 "?X25519:"
274#endif
275 "?secp384r1:"
276 "?secp256r1:"
277 "?secp521r1";
278
279 const char *const selected_groups =
280 force_pqc ? force_pqc_groups : default_groups;
281 const char *const selected_fallback_groups =
282 force_pqc ? nullptr : fallback_groups;
283
284 if (SSL_CTX_set1_groups_list(ctx, const_cast<char *>(selected_groups)) == 0) {
285 if (selected_fallback_groups != nullptr) {
286 ERR_clear_error();
287 if (SSL_CTX_set1_groups_list(
288 ctx, const_cast<char *>(selected_fallback_groups)) != 0)
289 return false;
290 }
291 return true;
292 }
293#else /* OPENSSL_VERSION_NUMBER >= 0x30500000L */
294 if (force_pqc) return true;
295
296 /*
297 SSL_CTX_set1_groups() configures all supported TLS groups, not only EC
298 curves. Include FFDHE groups so DHE cipher suites remain negotiable, but
299 fall back to EC-only groups for OpenSSL builds that reject FFDHE NIDs.
300 */
301 int groups[] = {
302#ifdef NID_X25519
303 NID_X25519,
304#endif
305 NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1};
306 int group_size = sizeof(groups) / sizeof(int);
307 if (SSL_CTX_set1_groups(ctx, groups, group_size) == 0) return true;
308#endif /* OPENSSL_VERSION_NUMBER >= 0x30500000L */
309
310 return false;
311}
312
313} // namespace
314
315#endif /* DH_KEYS_INCLUDED */
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:247
#define DBUG_EXECUTE_IF(keyword, a1)
Definition: my_dbug.h:171
bool set_dh(SSL_CTX *ctx)
Set DH paramenter for given SSL context.
Definition: dh_ecdh_config.h:61
constexpr bool tls_pqc_sign_supported()
Definition: dh_ecdh_config.h:44
bool sanitize_tls_kex_list(const char *tls_kex, bool force_pqc, std::string *filtered)
Filter a user-configured TLS key exchange list against the internal allowlist and optional PQC-only r...
Definition: dh_ecdh_config.h:119
bool set_ecdh(SSL_CTX *ctx, bool force_pqc=false, const char *tls_kex=nullptr)
Set TLS supported group details.
Definition: dh_ecdh_config.h:190
Container::const_iterator find(const Container &c, Value &&value)
Definition: generic.h:40
const char * begin(const char *const c)
Definition: base64.h:44
Cursor end()
A past-the-end Cursor.
Definition: rules_table_service.cc:192
int security_level(void)
Definition: sql_authentication.cc:1291