blob: b5625ac56502b7df1eb8af8e58d1f0d511d5301c [file] [log] [blame]
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +02001#ifndef USE_QUIC
2#error "Must define USE_QUIC"
3#endif
4
5#ifndef USE_OPENSSL
6#error "Must define USE_OPENSSL"
7#endif
8
William Lallemand418200a2023-09-14 16:26:58 +02009#include <haproxy/openssl-compat.h>
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +020010/* Highly inspired from nginx QUIC TLS compatibilty code */
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +020011#include <openssl/kdf.h>
12
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +020013#include <haproxy/quic_conn.h>
14#include <haproxy/quic_tls.h>
15#include <haproxy/ssl_sock.h>
16#include <haproxy/trace.h>
17
18#ifndef HAVE_SSL_KEYLOG
19#error "HAVE_SSL_KEYLOG is not defined"
20#endif
21
22#define QUIC_OPENSSL_COMPAT_RECORD_SIZE 1024
23
24#define QUIC_TLS_KEY_LABEL "key"
25#define QUIC_TLS_IV_LABEL "iv"
26
27#define TRACE_SOURCE &trace_quic
28
29struct quic_tls_compat_record {
30 unsigned char type;
31 const unsigned char *payload;
32 size_t payload_len;
33 uint64_t number;
34 struct quic_tls_compat_keys *keys;
35};
36
37/* Callback used to set the local transport parameters into the TLS stack.
38 * Must be called after having been set at the QUIC connection level.
39 */
40static int qc_ssl_compat_add_tps_cb(SSL *ssl, unsigned int ext_type, unsigned int context,
41 const unsigned char **out, size_t *outlen,
42 X509 *x, size_t chainidx, int *al, void *add_arg)
43{
44 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
45
46 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
47
48 *out = qc->enc_params;
49 *outlen = qc->enc_params_len;
50
51 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
52 return 1;
53}
54
55/* Set the keylog callback used to derive TLS secrets and the callback
56 * used to pass local transport parameters to the TLS stack.
57 * Return 1 if succeeded, 0 if not.
58 */
59int quic_tls_compat_init(struct bind_conf *bind_conf, SSL_CTX *ctx)
60{
61 /* Ignore non-QUIC connections */
62 if (bind_conf->xprt != xprt_get(XPRT_QUIC))
63 return 1;
64
Frederic Lecailleec9c5c52024-01-16 10:17:27 +010065 /* This callback is already registered if the TLS keylog is activated for
66 * traffic decryption analysis.
67 */
68 if (!global_ssl.keylog)
69 SSL_CTX_set_keylog_callback(ctx, quic_tls_compat_keylog_callback);
70
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +020071 if (SSL_CTX_has_client_custom_ext(ctx, QUIC_OPENSSL_COMPAT_SSL_TP_EXT))
72 return 1;
73
74 if (!SSL_CTX_add_custom_ext(ctx, QUIC_OPENSSL_COMPAT_SSL_TP_EXT,
75 SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
76 qc_ssl_compat_add_tps_cb, NULL, NULL,
77 NULL, NULL))
78 return 0;
79
80 return 1;
81}
82
83static int quic_tls_compat_set_encryption_secret(struct quic_conn *qc,
84 struct quic_tls_compat_keys *keys,
85 enum ssl_encryption_level_t level,
86 const SSL_CIPHER *cipher,
87 const uint8_t *secret, size_t secret_len)
88{
89 int ret = 0, key_len;
90 struct quic_tls_secret *peer_secret;
91
92 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
93
94 peer_secret = &keys->secret;
95 if (sizeof(peer_secret->secret.data) < secret_len)
96 goto leave;
97
98 keys->cipher = tls_aead(cipher);
99 if (!keys->cipher)
100 goto leave;
101
102 key_len = EVP_CIPHER_key_length(keys->cipher);
103
104 peer_secret->secret.len = secret_len;
105 memcpy(peer_secret->secret.data, secret, secret_len);
106
107 peer_secret->key.len = key_len;
108 peer_secret->iv.len = QUIC_OPENSSL_COMPAT_TLS_IV_LEN;
109 if (!quic_hkdf_expand_label(tls_md(cipher),
110 peer_secret->key.data, peer_secret->key.len,
111 secret, secret_len,
112 (const unsigned char *)QUIC_TLS_KEY_LABEL,
113 sizeof(QUIC_TLS_KEY_LABEL) - 1) ||
114 !quic_hkdf_expand_label(tls_md(cipher),
115 peer_secret->iv.data, peer_secret->iv.len,
116 secret, secret_len,
117 (const unsigned char *)QUIC_TLS_IV_LABEL,
118 sizeof(QUIC_TLS_IV_LABEL) - 1))
119 goto leave;
120
121 ret = 1;
122 leave:
123 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
124 return ret;
125}
126
127/* Callback used to get the Handshake and Application level secrets from
128 * the TLS stack.
129 */
130void quic_tls_compat_keylog_callback(const SSL *ssl, const char *line)
131{
132 unsigned char ch, value;
133 const char *start, *p;
134 size_t n;
135 unsigned int write;
136 struct quic_openssl_compat *compat;
137 enum ssl_encryption_level_t level;
138 unsigned char secret[EVP_MAX_MD_SIZE];
139 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
140
141 /* Ignore non-QUIC connections */
142 if (!qc)
143 return;
144
145 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
146
147 p = line;
148 for (start = p; *p && *p != ' '; p++);
149 n = p - start;
150
151 if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE) - 1 == n &&
152 !strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_HANDSHAKE, n)) {
153 level = ssl_encryption_handshake;
154 write = 0;
155 }
156 else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE) - 1 == n &&
157 !strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_HANDSHAKE, n)) {
158 level = ssl_encryption_handshake;
159 write = 1;
160 }
161 else if (sizeof(QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION) - 1 == n &&
162 !strncmp(start, QUIC_OPENSSL_COMPAT_CLIENT_APPLICATION, n)) {
163 level = ssl_encryption_application;
164 write = 0;
165 }
166 else if (sizeof(QUIC_OPENSSL_COMPAT_SERVER_APPLICATION) - 1 == n &&
167 !strncmp(start, QUIC_OPENSSL_COMPAT_SERVER_APPLICATION, n)) {
168 level = ssl_encryption_application;
169 write = 1;
170 }
171 else
172 goto leave;
173
174 if (*p++ == '\0')
175 goto leave;
176
177 while (*p && *p != ' ')
178 p++;
179
180 if (*p++ == '\0')
181 goto leave;
182
183 for (n = 0, start = p; *p; p++) {
184 ch = *p;
185 if (ch >= '0' && ch <= '9') {
186 value = ch - '0';
187 goto next;
188 }
189
190 ch = (unsigned char) (ch | 0x20);
191 if (ch >= 'a' && ch <= 'f') {
192 value = ch - 'a' + 10;
193 goto next;
194 }
195
196 goto leave;
197
198next:
199 if ((p - start) % 2) {
200 secret[n++] += value;
201 }
202 else {
203 if (n >= EVP_MAX_MD_SIZE)
204 goto leave;
205
206 secret[n] = (value << 4);
207 }
208 }
209
210 /* Secret successfully parsed */
211 compat = &qc->openssl_compat;
212 if (write) {
213 compat->method->set_encryption_secrets((SSL *) ssl, level, NULL, secret, n);
214 compat->write_level = level;
215
216 } else {
217 const SSL_CIPHER *cipher;
218
219 cipher = SSL_get_current_cipher(ssl);
220 /* AES_128_CCM_SHA256 not supported at this time. Furthermore, this
221 * algorithm is silently disabled by the TLS stack. But it can be
222 * enabled with "ssl-default-bind-ciphersuites" setting.
223 */
224 if (SSL_CIPHER_get_id(cipher) == TLS1_3_CK_AES_128_CCM_SHA256) {
225 quic_set_tls_alert(qc, SSL_AD_HANDSHAKE_FAILURE);
226 goto leave;
227 }
228
229 compat->method->set_encryption_secrets((SSL *) ssl, level, secret, NULL, n);
230 compat->read_level = level;
231 compat->read_record = 0;
232 quic_tls_compat_set_encryption_secret(qc, &compat->keys, level,
233 cipher, secret, n);
234 }
235
236 leave:
237 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
238}
239
240static size_t quic_tls_compat_create_header(struct quic_conn *qc,
241 struct quic_tls_compat_record *rec,
242 unsigned char *out, int plain)
243{
244 unsigned char type;
245 size_t len;
246
247 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
248
249 len = rec->payload_len;
250 if (plain) {
251 type = rec->type;
252 }
253 else {
254 type = SSL3_RT_APPLICATION_DATA;
255 len += EVP_GCM_TLS_TAG_LEN;
256 }
257
258 out[0] = type;
259 out[1] = 0x03;
260 out[2] = 0x03;
261 out[3] = (len >> 8);
262 out[4] = len;
263
264 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
265 return 5;
266}
267
268static void quic_tls_compute_nonce(unsigned char *nonce, size_t len, uint64_t pn)
269{
270 nonce[len - 8] ^= (pn >> 56) & 0x3f;
271 nonce[len - 7] ^= (pn >> 48) & 0xff;
272 nonce[len - 6] ^= (pn >> 40) & 0xff;
273 nonce[len - 5] ^= (pn >> 32) & 0xff;
274 nonce[len - 4] ^= (pn >> 24) & 0xff;
275 nonce[len - 3] ^= (pn >> 16) & 0xff;
276 nonce[len - 2] ^= (pn >> 8) & 0xff;
277 nonce[len - 1] ^= pn & 0xff;
278}
279
280/* Cipher <in> buffer data into <out> with <cipher> as AEAD cipher, <s> as secret.
281 * <ad> is the buffer for the additional data.
282 */
283static int quic_tls_tls_seal(struct quic_conn *qc,
284 const EVP_CIPHER *cipher, struct quic_tls_secret *s,
285 unsigned char *out, size_t *outlen, unsigned char *nonce,
286 const unsigned char *in, size_t inlen,
287 const unsigned char *ad, size_t adlen)
288{
289 int ret = 0, wlen;
290 EVP_CIPHER_CTX *ctx;
291 int aead_nid = EVP_CIPHER_nid(cipher);
292
293 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
294 ctx = EVP_CIPHER_CTX_new();
295 if (ctx == NULL)
296 goto leave;
297
298 /* Note that the following encryption code works with NID_aes_128_ccm, but leads
299 * to an handshake failure with "bad record mac" (20) TLS alert received from
300 * the peer.
301 */
302 if (!EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) ||
303 !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, s->iv.len, NULL) ||
304 (aead_nid == NID_aes_128_ccm &&
305 !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, NULL)) ||
306 !EVP_EncryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) ||
307 (aead_nid == NID_aes_128_ccm &&
308 !EVP_EncryptUpdate(ctx, NULL, &wlen, NULL, inlen)) ||
309 !EVP_EncryptUpdate(ctx, NULL, &wlen, ad, adlen) ||
310 !EVP_EncryptUpdate(ctx, out, &wlen, in, inlen) ||
311 !EVP_EncryptFinal_ex(ctx, out + wlen, &wlen) ||
312 (aead_nid != NID_aes_128_ccm &&
313 !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, EVP_GCM_TLS_TAG_LEN, out + inlen))) {
314 goto leave;
315 }
316
317 *outlen = inlen + adlen + EVP_GCM_TLS_TAG_LEN;
318 ret = 1;
319 leave:
320 /* Safe to call EVP_CIPHER_CTX_free() with null ctx */
321 EVP_CIPHER_CTX_free(ctx);
322 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
323 return ret;
324}
325
326static int quic_tls_compat_create_record(struct quic_conn *qc,
327 enum ssl_encryption_level_t level,
328 struct quic_tls_compat_record *rec,
329 unsigned char *res)
330{
331 int ret = 0;
332 unsigned char *ad;
333 size_t adlen;
334 unsigned char *out;
335 size_t outlen;
336 struct quic_tls_secret *secret;
337 unsigned char nonce[QUIC_OPENSSL_COMPAT_TLS_IV_LEN];
338
339 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
340
341 ad = res;
342 adlen = quic_tls_compat_create_header(qc, rec, ad, 0);
343
344 out = res + adlen;
345 outlen = rec->payload_len + EVP_GCM_TLS_TAG_LEN;
346
347 secret = &rec->keys->secret;
348
349 memcpy(nonce, secret->iv.data, secret->iv.len);
350 quic_tls_compute_nonce(nonce, sizeof(nonce), rec->number);
351
352 if (!quic_tls_tls_seal(qc, rec->keys->cipher, secret, out, &outlen,
353 nonce, rec->payload, rec->payload_len, ad, adlen))
354 goto leave;
355
Frédéric Lécailleca1d8812023-12-07 21:12:02 +0100356 ret = outlen;
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200357leave:
358 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
359 return ret;
360}
361
362/* Callback use to parse TLS messages for <ssl> TLS session. */
Frédéric Lécaille5e28c3a2023-12-21 16:11:35 +0100363void quic_tls_compat_msg_callback(struct connection *conn,
364 int write_p, int version, int content_type,
365 const void *buf, size_t len, SSL *ssl)
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200366{
367 unsigned int alert;
368 enum ssl_encryption_level_t level;
369 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
Frédéric Lécaille5e28c3a2023-12-21 16:11:35 +0100370 struct quic_openssl_compat *com;
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200371
Frédéric Lécaille5e28c3a2023-12-21 16:11:35 +0100372 if (!write_p || !qc)
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200373 goto leave;
374
Frédéric Lécaille5e28c3a2023-12-21 16:11:35 +0100375 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
376
377 com = &qc->openssl_compat;
378 level = com->write_level;
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200379 switch (content_type) {
380 case SSL3_RT_HANDSHAKE:
381 com->method->add_handshake_data(ssl, level, buf, len);
382 break;
383 case SSL3_RT_ALERT:
384 if (len >= 2) {
385 alert = ((unsigned char *) buf)[1];
386 com->method->send_alert(ssl, level, alert);
387 }
388 break;
389 }
390
391 leave:
392 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
393}
394
395int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
396{
397 int ret = 0;
398 BIO *rbio, *wbio = NULL;
399 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
400
401 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
402
403 rbio = BIO_new(BIO_s_mem());
404 if (!rbio)
405 goto err;
406
407 wbio = BIO_new(BIO_s_null());
408 if (!wbio)
409 goto err;
410
411 SSL_set_bio(ssl, rbio, wbio);
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200412 /* No ealy data support */
413 SSL_set_max_early_data(ssl, 0);
414
415 qc->openssl_compat.rbio = rbio;
416 qc->openssl_compat.wbio = wbio;
417 qc->openssl_compat.method = quic_method;
Frédéric Lécaillee5c04ff2023-07-31 15:07:06 +0200418 qc->openssl_compat.read_level = ssl_encryption_initial;
419 qc->openssl_compat.write_level = ssl_encryption_initial;
Frédéric Lécaillec9c0d682023-06-02 16:15:35 +0200420 ret = 1;
421
422 leave:
423 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
424 return ret;
425 err:
426 BIO_free(rbio);
427 BIO_free(wbio);
428 goto leave;
429}
430
431enum ssl_encryption_level_t SSL_quic_read_level(const SSL *ssl)
432{
433 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
434
435 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
436 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
437 return qc->openssl_compat.read_level;
438}
439
440
441enum ssl_encryption_level_t SSL_quic_write_level(const SSL *ssl)
442{
443 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
444
445 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
446 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
447 return qc->openssl_compat.write_level;
448}
449
450int SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
451 const uint8_t *data, size_t len)
452{
453 int ret = 0;
454 BIO *rbio;
455 struct quic_tls_compat_record rec;
456 unsigned char in[QUIC_OPENSSL_COMPAT_RECORD_SIZE + 1];
457 unsigned char out[QUIC_OPENSSL_COMPAT_RECORD_SIZE + 1 +
458 SSL3_RT_HEADER_LENGTH + EVP_GCM_TLS_TAG_LEN];
459 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
460 size_t n;
461
462 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
463
464 rbio = SSL_get_rbio(ssl);
465
466 while (len) {
467 memset(&rec, 0, sizeof rec);
468 rec.type = SSL3_RT_HANDSHAKE;
469 rec.number = qc->openssl_compat.read_record++;
470 rec.keys = &qc->openssl_compat.keys;
471 if (level == ssl_encryption_initial) {
472 n = QUIC_MIN(len, (size_t)65535);
473 rec.payload = (unsigned char *)data;
474 rec.payload_len = n;
475 quic_tls_compat_create_header(qc, &rec, out, 1);
476 BIO_write(rbio, out, SSL3_RT_HEADER_LENGTH);
477 BIO_write(rbio, data, n);
478 }
479 else {
480 size_t outlen;
481 unsigned char *p = in;
482
483 n = QUIC_MIN(len, (size_t)QUIC_OPENSSL_COMPAT_RECORD_SIZE);
484 memcpy(in, data, n);
485 p += n;
486 *p++ = SSL3_RT_HANDSHAKE;
487
488 rec.payload = in;
489 rec.payload_len = p - in;
490
491 if (!rec.keys->cipher)
492 goto leave;
493
494 outlen = quic_tls_compat_create_record(qc, level, &rec, out);
495 if (!outlen)
496 goto leave;
497
498 BIO_write(rbio, out, outlen);
499 }
500
501 data += n;
502 len -= n;
503 }
504
505 ret = 1;
506 leave:
507 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
508 return ret;
509}
510
511int SSL_process_quic_post_handshake(SSL *ssl)
512{
513 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
514
515 /* Do nothing: rely on the TLS message callback to parse alert messages. */
516 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
517 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
518 return 1;
519}
520
521int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, size_t params_len)
522{
523 struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
524 /* The local transport parameters are stored into the quic_conn object.
525 * There is no need to add an intermediary to store pointers to these
526 * transport paraemters.
527 */
528 TRACE_ENTER(QUIC_EV_CONN_SSL_COMPAT, qc);
529 TRACE_LEAVE(QUIC_EV_CONN_SSL_COMPAT, qc);
530 return 1;
531}
532