[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