PROGRAMS{noinst}=dtlsclient dtlsserver
ENDIF
+ IF[{- !$disabled{"ech"} -}]
+ PROGRAMS{noinst}=echconfiglist_parser
+ ENDIF
+
SOURCE[asn1]=asn1.c driver.c fuzz_rand.c
INCLUDE[asn1]=../include {- $ex_inc -}
DEPEND[asn1]=../libcrypto ../libssl {- $ex_lib -}
INCLUDE[dtlsserver]=../include {- $ex_inc -}
DEPEND[dtlsserver]=../libcrypto ../libssl {- $ex_lib -}
+ SOURCE[echconfiglist_parser]=echconfiglist_parser.c driver.c
+ INCLUDE[echconfiglist_parser]=../include {- $ex_inc -}
+ DEPEND[echconfiglist_parser]=../libcrypto ../libssl {- $ex_lib -}
+
SOURCE[pem]=pem.c driver.c
INCLUDE[pem]=../include {- $ex_inc -}
DEPEND[pem]=../libcrypto.a {- $ex_lib -}
PROGRAMS{noinst}=dtlsclient-test dtlsserver-test
ENDIF
+ IF[{- !$disabled{"ech"} -}]
+ PROGRAMS{noinst}=echconfiglist_parser-test
+ ENDIF
+
SOURCE[asn1-test]=asn1.c $FUZZTESTSRC fuzz_rand.c
INCLUDE[asn1-test]=../include ../test/mfail
DEPEND[asn1-test]=../libcrypto.a ../libssl.a
INCLUDE[dtlsserver-test]=../include ../test/mfail
DEPEND[dtlsserver-test]=../libcrypto.a ../libssl.a
+ SOURCE[echconfiglist_parser-test]=echconfiglist_parser.c $FUZZTESTSRC
+ INCLUDE[echconfiglist_parser-test]=../include ../test/mfail
+ DEPEND[echconfiglist_parser-test]=../libcrypto.a ../libssl.a
+
SOURCE[pem-test]=pem.c $FUZZTESTSRC
INCLUDE[pem-test]=../include ../test/mfail
DEPEND[pem-test]=../libcrypto.a
--- /dev/null
+/*
+ * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+#include <limits.h>
+#include <openssl/ech.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/e_os2.h>
+#include <openssl/byteorder.h>
+#include "fuzzer.h"
+
+static void parse_one(const uint8_t *buf, int len)
+{
+ OSSL_ECHSTORE *es;
+ BIO *in;
+
+ es = OSSL_ECHSTORE_new(NULL, NULL);
+ if (es == NULL)
+ return;
+
+ in = BIO_new_mem_buf(buf, len);
+ if (in == NULL) {
+ OSSL_ECHSTORE_free(es);
+ return;
+ }
+
+ OSSL_ECHSTORE_read_echconfiglist(es, in);
+
+ OSSL_ECHSTORE_free(es);
+ BIO_free(in);
+}
+
+int FuzzerInitialize(int *argc, char ***argv)
+{
+ return 1;
+}
+
+int FuzzerTestOneInput(const uint8_t *buf, size_t len)
+{
+ uint8_t *fixed_buf = NULL;
+ int bio_len;
+ uint16_t outer_len, inner_len;
+
+ if (len > INT_MAX)
+ return 0;
+ bio_len = (int)len;
+
+ /* Target raw without any fixup */
+ parse_one(buf, bio_len);
+
+ /*
+ * ech_decode_and_flatten has a strict size check:
+ * OSSL_ECH_MIN_ECHCONFIG_LEN = 32
+ * OSSL_ECH_MAX_ECHCONFIG_LEN = 1500
+ */
+ if (len < OSSL_ECH_MIN_ECHCONFIG_LEN || len >= OSSL_ECH_MAX_ECHCONFIG_LEN)
+ goto end;
+ outer_len = (uint16_t)(len - 2);
+ inner_len = (uint16_t)(len - 6);
+
+ fixed_buf = OPENSSL_memdup(buf, len);
+ if (fixed_buf == NULL)
+ goto end;
+
+ /* Fix up to pass initial checks*/
+ OPENSSL_store_u16_be(fixed_buf, outer_len);
+ OPENSSL_store_u16_be(fixed_buf + 2, OSSL_ECH_RFC9849_VERSION);
+ OPENSSL_store_u16_be(fixed_buf + 4, inner_len);
+
+ parse_one(fixed_buf, bio_len);
+
+end:
+ OPENSSL_free(fixed_buf);
+ ERR_clear_error();
+
+ return 0;
+}
+
+void FuzzerCleanup(void)
+{
+}
--- /dev/null
+#!/usr/bin/env perl
+# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use OpenSSL::Test qw/:DEFAULT srctop_file/;
+use OpenSSL::Test::Utils;
+
+my $fuzzer = "echconfiglist_parser";
+setup("test_fuzz_${fuzzer}");
+
+plan skip_all => "This test requires ech support"
+ if disabled("ech");
+
+plan tests => 2; # one more due to below require_ok(...)
+
+require_ok(srctop_file('test','recipes','fuzz.pl'));
+
+fuzz_ok($fuzzer);