[RFC PATCH v1 09/13] hostapd: Add DecoyAuth configuration

Jeff Hansen x at jeffhansen.com
Fri Sep 11 12:20:03 PDT 2026


Add the DecoyAuth build option and optional-by-default runtime mode.
Require H2E for DecoyAuth exchanges. Expose cache and worker support as
independent build options and include interpolation cache configuration
for SAE access points.

Signed-off-by: Jeff Hansen <x at jeffhansen.com>
---
 hostapd/Makefile      | 13 +++++++++++++
 hostapd/config_file.c | 34 ++++++++++++++++++++++++++++++++++
 hostapd/defconfig     | 11 +++++++++++
 hostapd/hostapd.conf  | 22 ++++++++++++++++++++++
 src/ap/ap_config.c    | 10 ++++++++++
 src/ap/ap_config.h    |  3 +++
 6 files changed, 93 insertions(+)

diff --git a/hostapd/Makefile b/hostapd/Makefile
index 20cfe2773..c3ec4afd4 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -296,6 +296,19 @@ endif
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
 OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_DECOYAUTH
+CFLAGS += -DCONFIG_SAE_DECOYAUTH
+ifndef CONFIG_NATIVE_WINDOWS
+ifdef CONFIG_SAE_DECOYAUTH_THREADS
+CFLAGS += -DCONFIG_SAE_DECOYAUTH_THREADS
+LIBS += -pthread
+LIBS_s += -pthread
+endif
+ifdef CONFIG_SAE_DECOYAUTH_CACHE
+CFLAGS += -DCONFIG_SAE_DECOYAUTH_CACHE
+endif
+endif
+endif
 ifdef CONFIG_SAE_PK
 CFLAGS += -DCONFIG_SAE_PK
 NEED_BASE64=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 9ced2c860..0188db70b 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4673,6 +4673,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->sae_confirm_immediate = atoi(pos);
 	} else if (os_strcmp(buf, "sae_pwe") == 0) {
 		bss->sae_pwe = atoi(pos);
+#ifdef CONFIG_SAE_DECOYAUTH
+	} else if (os_strcmp(buf, "decoyauth") == 0) {
+		bss->decoyauth = atoi(pos);
+		if (bss->decoyauth < SAE_DECOYAUTH_DISABLED ||
+		    bss->decoyauth > SAE_DECOYAUTH_REQUIRED) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid decoyauth value %d (expected 0..2)",
+				   line, bss->decoyauth);
+			return 1;
+		}
+#ifdef CONFIG_SAE_DECOYAUTH_CACHE
+	} else if (os_strcmp(buf, "decoyauth_cache_dir") == 0) {
+		if (pos[0] != '/') {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: decoyauth_cache_dir must be an absolute path",
+				   line);
+			return 1;
+		}
+		os_free(bss->decoyauth_cache_dir);
+		bss->decoyauth_cache_dir = os_strdup(pos);
+		if (!bss->decoyauth_cache_dir)
+			return 1;
+	} else if (os_strcmp(buf, "decoyauth_cache_entries") == 0) {
+		int entries = atoi(pos);
+
+		if (entries < 0 || entries > SAE_MAX_PASSWORDS) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid decoyauth_cache_entries value %d (expected 0..%d)",
+				   line, entries, SAE_MAX_PASSWORDS);
+			return 1;
+		}
+		bss->decoyauth_cache_entries = entries;
+#endif /* CONFIG_SAE_DECOYAUTH_CACHE */
+#endif /* CONFIG_SAE_DECOYAUTH */
 	} else if (os_strcmp(buf, "sae_accept_h2e_without_use") == 0) {
 		bss->sae_accept_h2e_without_use = atoi(pos);
 	} else if (os_strcmp(buf, "sae_pw_id_num") == 0) {
diff --git a/hostapd/defconfig b/hostapd/defconfig
index cecfc7c8a..d04859a65 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -164,6 +164,17 @@ CONFIG_IPV6=y
 # Simultaneous Authentication of Equals (SAE), WPA3-Personal
 #CONFIG_SAE=y
 
+# Experimental DecoyAuth extension for SAE multi-password authentication
+#CONFIG_SAE_DECOYAUTH=y
+
+# Cache DecoyAuth interpolation matrices on persistent storage. This requires
+# CONFIG_SAE_DECOYAUTH and is not supported on native Windows builds.
+#CONFIG_SAE_DECOYAUTH_CACHE=y
+
+# Use worker threads for DecoyAuth candidate and interpolation preparation.
+# This requires CONFIG_SAE_DECOYAUTH and is not supported on native Windows.
+#CONFIG_SAE_DECOYAUTH_THREADS=y
+
 # SAE Public Key, WPA3-Personal
 #CONFIG_SAE_PK=y
 
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index ee7c71df3..0418136e1 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -2298,6 +2298,28 @@ own_ip_addr=127.0.0.1
 # regardless of the sae_pwe parameter value.
 #sae_pwe=0
 
+# DecoyAuth mode for SAE authentication
+# 0 = disabled (use standard SAE)
+# 1 = optional (default; use DecoyAuth with capable stations and standard SAE
+#     with the first matching password for legacy stations)
+# 2 = required (reject stations that do not negotiate DecoyAuth)
+# DecoyAuth requires H2E and supports MLO. Password identifiers, SAE-PK, mesh,
+# and SAE offload are not supported. Optional mode uses standard SAE fallback
+# when H2E or DecoyAuth is unavailable.
+#decoyauth=1
+
+# Optional persistent cache for the password-derived DecoyAuth interpolation
+# matrix. The directory and its files are restricted to the hostapd user because
+# the matrix is sensitive credential-derived material. Matrix entries use a
+# fixed-width binary representation and are read directly from the filesystem;
+# they are not expanded into a resident BIGNUM matrix.
+# The default is /tmp/hostapd-decoyauth.
+#decoyauth_cache_dir=/tmp/hostapd-decoyauth
+
+# Number of most-recent interpolation matrices retained in the directory.
+# 0 disables the cache. The default is 10.
+#decoyauth_cache_entries=10
+
 # Accept SAE association when the STA advertises SAE H2E support in the RSNXE
 # but authenticated using the hunting-and-pecking loop (i.e., did not use H2E).
 # By default (0), hostapd rejects such an association as a potential downgrade
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 6616b0e82..0ceb3992a 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -126,6 +126,15 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 
 	bss->anti_clogging_threshold = 5;
 	bss->sae_sync = 3;
+#ifdef CONFIG_SAE_DECOYAUTH
+	bss->decoyauth = SAE_DECOYAUTH_OPTIONAL;
+#ifdef CONFIG_SAE_DECOYAUTH_CACHE
+	bss->decoyauth_cache_dir = os_strdup("/tmp/hostapd-decoyauth");
+	bss->decoyauth_cache_entries = 10;
+#endif /* CONFIG_SAE_DECOYAUTH_CACHE */
+#else /* CONFIG_SAE_DECOYAUTH */
+	bss->decoyauth = SAE_DECOYAUTH_DISABLED;
+#endif /* CONFIG_SAE_DECOYAUTH */
 
 	bss->gas_frag_limit = 1400;
 
@@ -865,6 +874,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 	os_free(conf->rsn_preauth_interfaces);
 	os_free(conf->ctrl_interface);
 	os_free(conf->config_id);
+	os_free(conf->decoyauth_cache_dir);
 	os_free(conf->ca_cert);
 	os_free(conf->server_cert);
 	os_free(conf->server_cert2);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 660fd8098..56e5d45af 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -670,6 +670,9 @@ struct hostapd_bss_config {
 	int sae_require_mfp;
 	int sae_confirm_immediate;
 	enum sae_pwe sae_pwe;
+	int decoyauth;
+	char *decoyauth_cache_dir;
+	unsigned int decoyauth_cache_entries;
 	/* Accept an SAE association even when the STA advertises SAE H2E
 	 * support in the RSNXE but authenticated using hunting-and-pecking.
 	 * This is a workaround for WPA3-Personal Compatibility Mode and
-- 
2.53.0




More information about the Hostap mailing list