Commit 615e7ad9ab for strongswan.org

commit 615e7ad9abc05f17efecf49a83cea4ae9e3359a5
Author: Arthur Chan <arthur.chan@adalogics.com>
Date:   Wed Feb 11 09:57:43 2026 +0000

    oss-fuzz: Add new fuzzer targeting libtls

    Closes strongswan/strongswan#3004

    Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
    Co-authored-by: Tobias Brunner <tobias@strongswan.org>

diff --git a/fuzz/.gitignore b/fuzz/.gitignore
index d01163707b..35fa553414 100644
--- a/fuzz/.gitignore
+++ b/fuzz/.gitignore
@@ -11,4 +11,5 @@ fuzz_ocsp_rsp_def
 fuzz_pa_tnc
 fuzz_pb_tnc
 fuzz_radius
+fuzz_tls
 fuzz_vici
diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am
index d5aa1d6c76..f13677af6e 100644
--- a/fuzz/Makefile.am
+++ b/fuzz/Makefile.am
@@ -5,6 +5,7 @@ AM_CPPFLAGS = @CPPFLAGS@ \
 	-I$(top_srcdir)/src/libimcv \
 	-I$(top_srcdir)/src/libtncif \
 	-I$(top_srcdir)/src/libtpmtss \
+	-I$(top_srcdir)/src/libtls \
 	-I$(top_srcdir)/src/libtnccs \
 	-I$(top_srcdir)/src/libtnccs/plugins/tnccs_20 \
 	-I$(top_srcdir)/src/libradius
@@ -47,6 +48,10 @@ radius_ldflags = \
 	$(top_builddir)/src/libradius/.libs/libradius.a \
 	$(fuzz_ldflags)

+tls_ldflags = \
+	$(top_builddir)/src/libtls/.libs/libtls.a \
+	$(fuzz_ldflags)
+
 # fuzzers that use crypto plugins in a significant way
 fuzzers_with_plugins = \
 	fuzz_certs fuzz_crls fuzz_ocsp_req fuzz_ocsp_rsp
@@ -55,7 +60,7 @@ fuzzers_with_def = $(fuzzers_with_plugins:%=%_def)
 fuzzers_with_cus = $(fuzzers_with_plugins:%=%_cus)

 fuzzers_no_plugins = \
-	fuzz_ids fuzz_ike fuzz_pa_tnc fuzz_pb_tnc fuzz_radius fuzz_vici
+	fuzz_ids fuzz_ike fuzz_pa_tnc fuzz_pb_tnc fuzz_radius fuzz_tls fuzz_vici

 ALL_FUZZERS=$(fuzzers_with_def) $(fuzzers_with_cus) $(fuzzers_no_plugins)

@@ -102,6 +107,9 @@ fuzz_ike: fuzz_ike.c ${libfuzzer}
 fuzz_radius: fuzz_radius.c ${libfuzzer}
 	$(CC) $(fuzz_cppflags_def) $(CFLAGS) -o $@ $< $(radius_ldflags)

+fuzz_tls: fuzz_tls.c ${libfuzzer}
+	$(CC) $(fuzz_cppflags_def) $(CFLAGS) -o $@ $< $(tls_ldflags)
+
 fuzz_vici: fuzz_vici.c ${libfuzzer}
 	$(CC) $(AM_CPPFLAGS) $(CFLAGS) -o $@ $< $(charon_ldflags)

diff --git a/fuzz/fuzz_tls.c b/fuzz/fuzz_tls.c
new file mode 100644
index 0000000000..329e2cc93c
--- /dev/null
+++ b/fuzz/fuzz_tls.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2026 Arthur SC Chan
+ *
+ * Copyright (C) secunet Security Networks AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <library.h>
+#include <tls.h>
+#include <credentials/sets/mem_cred.h>
+
+/**
+ * ECDSA256 private key (same as used in test_socket.c)
+ */
+static chunk_t ecdsa256 = chunk_from_chars(
+	0x30,0x77,0x02,0x01,0x01,0x04,0x20,0x2d,0x01,0x7e,0x5b,0x4a,0x7d,0x78,0xe9,0x23,
+	0xeb,0xb2,0xac,0x4c,0xf1,0x28,0x3b,0xfa,0x1d,0xa9,0x08,0x5c,0xd0,0x60,0x2a,0xa6,
+	0x54,0xd3,0x94,0xd4,0x05,0xa1,0x04,0xa0,0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,
+	0x03,0x01,0x07,0xa1,0x44,0x03,0x42,0x00,0x04,0x15,0x9c,0xbe,0xdb,0x54,0xa6,0xe7,
+	0x7f,0x76,0x05,0xa6,0x9d,0xf3,0x41,0x38,0x43,0x98,0xe9,0x0b,0x2b,0x8b,0x02,0xb4,
+	0x04,0x9b,0x61,0x84,0x65,0x63,0x3b,0x08,0xb2,0x4b,0x1e,0xd0,0x32,0x20,0xe9,0xfc,
+	0x62,0xa7,0xd0,0x71,0x9e,0xe9,0xf9,0x2d,0x91,0xb8,0xf2,0xa3,0x4d,0x8a,0x78,0xb2,
+	0x0b,0xfb,0x59,0x7c,0x40,0xbd,0xaf,0xa2,0x07
+);
+
+/**
+ * TLS certificate for ECDSA256 key
+ * pki --self --in ecdsa256.key --dn "C=CH, O=strongSwan, CN=tls-fuzz-ecdsa" --san server.test
+ */
+static chunk_t ecdsa256_crt = chunk_from_chars(
+	0x30,0x82,0x01,0x87,0x30,0x82,0x01,0x2C,0xA0,0x03,0x02,0x01,0x02,0x02,0x08,0x3D,
+	0x6D,0x48,0xA3,0x1F,0x8A,0x62,0x3E,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,
+	0x04,0x03,0x02,0x30,0x3B,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,
+	0x43,0x48,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x73,0x74,0x72,
+	0x6F,0x6E,0x67,0x53,0x77,0x61,0x6E,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x03,
+	0x13,0x0E,0x74,0x6C,0x73,0x2D,0x66,0x75,0x7A,0x7A,0x2D,0x65,0x63,0x64,0x73,0x61,
+	0x30,0x1E,0x17,0x0D,0x32,0x36,0x30,0x35,0x32,0x39,0x30,0x38,0x33,0x38,0x33,0x36,
+	0x5A,0x17,0x0D,0x32,0x39,0x30,0x35,0x32,0x38,0x30,0x38,0x33,0x38,0x33,0x36,0x5A,
+	0x30,0x3B,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x48,0x31,
+	0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x73,0x74,0x72,0x6F,0x6E,0x67,
+	0x53,0x77,0x61,0x6E,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x03,0x13,0x0E,0x74,
+	0x6C,0x73,0x2D,0x66,0x75,0x7A,0x7A,0x2D,0x65,0x63,0x64,0x73,0x61,0x30,0x59,0x30,
+	0x13,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x08,0x2A,0x86,0x48,0xCE,
+	0x3D,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0x15,0x9C,0xBE,0xDB,0x54,0xA6,0xE7,0x7F,
+	0x76,0x05,0xA6,0x9D,0xF3,0x41,0x38,0x43,0x98,0xE9,0x0B,0x2B,0x8B,0x02,0xB4,0x04,
+	0x9B,0x61,0x84,0x65,0x63,0x3B,0x08,0xB2,0x4B,0x1E,0xD0,0x32,0x20,0xE9,0xFC,0x62,
+	0xA7,0xD0,0x71,0x9E,0xE9,0xF9,0x2D,0x91,0xB8,0xF2,0xA3,0x4D,0x8A,0x78,0xB2,0x0B,
+	0xFB,0x59,0x7C,0x40,0xBD,0xAF,0xA2,0x07,0xA3,0x1A,0x30,0x18,0x30,0x16,0x06,0x03,
+	0x55,0x1D,0x11,0x04,0x0F,0x30,0x0D,0x82,0x0B,0x73,0x65,0x72,0x76,0x65,0x72,0x2E,
+	0x74,0x65,0x73,0x74,0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,
+	0x03,0x49,0x00,0x30,0x46,0x02,0x21,0x00,0xCB,0xD9,0x0D,0xC9,0x5C,0x68,0xF8,0x9D,
+	0x69,0x69,0xB3,0x73,0x33,0xE7,0x29,0x08,0x63,0x7D,0xDB,0x12,0x8E,0xB5,0x5F,0xBF,
+	0xEC,0xE7,0x81,0x95,0xC4,0x3F,0x7E,0xFA,0x02,0x21,0x00,0xB7,0x79,0xDA,0x0F,0xD1,
+	0x42,0x9C,0xC2,0xD1,0x32,0x03,0x2D,0xE5,0x55,0x22,0xD8,0x09,0xAC,0xA9,0x1B,0xA1,
+	0x0D,0x31,0xB8,0x24,0xEE,0xE3,0xE0,0xBF,0xBD,0x18,0xA5,
+);
+
+/**
+ * Credential set
+ */
+static mem_cred_t *creds;
+
+/**
+ * Load the test certificate
+ */
+static void setup_cert()
+{
+	private_key_t *key;
+	certificate_t *cert;
+
+	creds = mem_cred_create();
+	lib->credmgr->add_set(lib->credmgr, &creds->set);
+
+	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+							 BUILD_BLOB, ecdsa256, BUILD_END);
+	if (key)
+	{
+		creds->add_key(creds, key);
+	}
+
+	cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+							  BUILD_BLOB, ecdsa256_crt, BUILD_END);
+	if (cert)
+	{
+		creds->add_cert(creds, TRUE, cert);
+	}
+}
+
+/**
+ * Minimal TLS application stub for fuzzing
+ */
+typedef struct {
+	tls_application_t public;
+} fuzz_tls_application_t;
+
+static status_t app_process(tls_application_t *this, bio_reader_t *reader)
+{
+	/* Consume application data without processing */
+	return NEED_MORE;
+}
+
+static status_t app_build(tls_application_t *this, bio_writer_t *writer)
+{
+	/* No application data to send */
+	return INVALID_STATE;
+}
+
+static void app_destroy(tls_application_t *this)
+{
+	free(this);
+}
+
+static tls_application_t *create_tls_application()
+{
+	fuzz_tls_application_t *app;
+
+	INIT(app,
+		.public = {
+			.process = app_process,
+			.build = app_build,
+			.destroy = app_destroy,
+		},
+	);
+	return &app->public;
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+	dbg_default_set_level(-1);
+	library_init(NULL, "fuzz_tls");
+	if (!lib->plugins->load(lib->plugins, PLUGINS))
+	{
+		return 1;
+	}
+	setup_cert();
+	return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
+{
+	tls_t *tls;
+	identification_t *server_id, *peer_id;
+	tls_application_t *app;
+	status_t status;
+	char out[TLS_MAX_FRAGMENT_LEN + 2048];
+	size_t outlen, msglen;
+	bool is_server;
+
+	/* Need one role byte plus minimum TLS record header (5 bytes) */
+	if (len < 6)
+	{
+		return 0;
+	}
+
+	/* First byte selects role: input is then either a request (server) or
+	 * response (client) */
+	is_server = buf[0] & 1;
+	buf++;
+	len--;
+
+	server_id = identification_create_from_string("server.test");
+	peer_id = identification_create_from_string("peer.test");
+	app = create_tls_application();
+
+	tls = tls_create(is_server, server_id, peer_id, TLS_PURPOSE_GENERIC,
+					 app, NULL, TLS_FLAG_ENCRYPTION_OPTIONAL);
+	tls->set_version(tls, TLS_SUPPORTED_MIN, TLS_SUPPORTED_MAX);
+
+	/* Client must emit ClientHello first so input is interpreted as the
+	 * server's reply */
+	if (!is_server)
+	{
+		do
+		{
+			outlen = sizeof(out);
+			status = tls->build(tls, out, &outlen, &msglen);
+		}
+		while (status == NEED_MORE);
+	}
+
+	status = tls->process(tls, (void*)buf, len);
+
+	/* Drive build() naturally based on process() status */
+	if (status == NEED_MORE)
+	{
+		do
+		{
+			outlen = sizeof(out);
+			status = tls->build(tls, out, &outlen, &msglen);
+		}
+		while (status == NEED_MORE);
+	}
+
+	tls->destroy(tls);
+	server_id->destroy(server_id);
+	peer_id->destroy(peer_id);
+	return 0;
+}