From fefc1cce202b4e3a44f29e3f7fdb425b85d9c12b Mon Sep 17 00:00:00 2001 From: Olivier Houchard Date: Thu, 26 Jun 2025 13:06:18 +0200 Subject: [PATCH] MEDIUM: ssl: Add support for ktls on TLS 1.3 with AWS-LC AWS-LC added a new API in AWS-LC 1.54 that allows the user to retrieve the keys for TLS 1.3 connections with SSL_get_read_traffic_secret(), so use it to be able to use ktls with TLS 1.3 too. --- src/ssl_sock.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 1a7473cb2..cd90bce48 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -6032,6 +6032,44 @@ static int ssl_remove_xprt(struct connection *conn, void *xprt_ctx, void *toremo } #ifdef HA_USE_KTLS +#if defined(OPENSSL_IS_AWSLC) && AWSLC_API_VERSION >= 34 & !defined(HA_DISABLE_AWSLC_TLS13) +static int ssl_sock_hkdf_expand_label(struct ssl_sock_ctx *ctx, uint8_t *secret, size_t secret_len, uint8_t *label, size_t label_len, uint8_t *out, size_t out_len) +{ + SSL *ssl = ctx->ssl; + /* 2 for length, 1 for the string length, label_len + 6 ("tls13 ") + 1 for the empty context string. */ + int buf_len = 2 + 1 + label_len + 6 + 1; + /* + * As we only labels we use are "salt" or "key", consider label_len + * can only be 4 max. + */ + unsigned char buf[2 + 1 + 4 + 6 + 1]; + const EVP_MD *digest; + uint16_t len = htons(out_len); + + digest = SSL_CIPHER_get_handshake_digest(SSL_get_current_cipher(ssl)); + + BUG_ON(label_len > 4); + if (digest == NULL) + return -1; + /* + * The label looks like : + * 2 bytes for the length (in big engian) + * 1 byte for the label length, and then the string + * 1 byte for the context length, and then the context (we have no + * context so it will always be 0 + */ + memcpy(buf, &len, sizeof(len)); + len = label_len + 6; + memcpy(&buf[2], &len, 1); + memcpy(&buf[3], "tls13 ", 6); + memcpy(&buf[9], label, label_len); + len = 0; + memcpy(&buf[9 + label_len], &len, 1); + + return (HKDF_expand(out, out_len, digest, secret, secret_len, buf, buf_len) == 1 ? 0 : - 1); +} +#endif + #if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) static void ssl_sock_setup_ktls(struct ssl_sock_ctx *ctx) { @@ -6093,6 +6131,20 @@ static void ssl_sock_setup_ktls(struct ssl_sock_ctx *ctx) case TLS_1_2_VERSION: is_tls_12 = 1; break; +#if defined(OPENSSL_IS_AWSLC) && AWSLC_API_VERSION >= 34 & !defined(HA_DISABLE_AWSLC_TLS13) + /* + * For now only AWS-LC >= 1.54 supports the API to export + * TLS 1.3 secrets. + * Unfortunately, AWSLC_API_VERSION hasn't been bumped when + * those were added, so use the latest one. It will match + * AWS-LC 1.52 and 1.53 too, but there's not much we + * can do about it, one can define HA_DISABLE_AWSLC_TLS13 + * to build with AWS-LC 1.52 or 1.53. + */ + case TLS_1_3_VERSION: + is_tls_12 = 0; + break; +#endif default: ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; return; @@ -6213,6 +6265,88 @@ static void ssl_sock_setup_ktls(struct ssl_sock_ctx *ctx) goto out; } ctx->flags |= SSL_SOCK_F_KTLS_SEND | SSL_SOCK_F_KTLS_RECV; + } else { +#if defined(OPENSSL_IS_AWSLC) && AWSLC_API_VERSION >= 34 & !defined(HA_DISABLE_AWSLC_TLS13) + unsigned char iv[12]; + size_t len; + + /* + * 12 is the maximum size for iv + salt currently supported + * in Linux, if a new cipher is added that expands that, + * then the buffer should be expanded. + */ + BUG_ON(iv_size + salt_size > sizeof(iv)); + if (SSL_get_read_traffic_secret(ctx->ssl, NULL, &len) != 1) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + /* + * We may have to increase buf size if a new cipher is + * added and its secret size is too big. + */ + BUG_ON(len > sizeof(buf)); + + if (SSL_get_read_traffic_secret(ctx->ssl, buf, &len) != 1) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + + if (ssl_sock_hkdf_expand_label(ctx, buf, len, (unsigned char *)"key", 3, &info.buf[iv_size], key_size) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + if (ssl_sock_hkdf_expand_label(ctx, buf, len, (unsigned char *)"iv", 2, iv, iv_size + salt_size) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + /* + * iv contains both what should go to "iv" and "salt", + * salt first, then iv. + */ + memcpy(&info.buf[0], iv + salt_size, iv_size); + memcpy(&info.buf[iv_size + key_size], iv, salt_size); + seq = SSL_get_read_sequence(ssl); + seq = my_htonll(seq); + memcpy(&info.buf[iv_size + key_size + salt_size], &seq, seq_size); + if (ktls_set_key(ctx, &info, info_size, 0) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + if (SSL_get_write_traffic_secret(ctx->ssl, NULL, &len) != 1) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + BUG_ON(len > sizeof(buf)); + + if (SSL_get_write_traffic_secret(ctx->ssl, buf, &len) != 1) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + + if (ssl_sock_hkdf_expand_label(ctx, buf, len, (unsigned char *)"key", 3, &info.buf[iv_size], key_size) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + if (ssl_sock_hkdf_expand_label(ctx, buf, len, (unsigned char *)"iv", 2, iv, iv_size + salt_size) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + /* + * iv contains both what should go to "iv" and "salt", + * salt first, then iv. + */ + memcpy(&info.buf[0], iv + salt_size, iv_size); + memcpy(&info.buf[iv_size + key_size], iv, salt_size); + seq = SSL_get_write_sequence(ssl); + seq = my_htonll(seq); + memcpy(&info.buf[iv_size + key_size + salt_size], &seq, seq_size); + if (ktls_set_key(ctx, &info, info_size, 1) != 0) { + ctx->flags &= ~SSL_SOCK_F_KTLS_ENABLED; + goto out; + } + ctx->flags |= SSL_SOCK_F_KTLS_RECV | SSL_SOCK_F_KTLS_SEND; +#endif + } out: return;