[automerger skipped] P2P: Fix copying of secondary device types for P2P group client am: 803c2d6698 -s ours am: 721db55b37 -s ours am: 14f1daea76 -s ours am: 5c95f7236c -s ours am: eb972263ab -s ours
am skip reason: Change-Id I6837931dcd3be01e68dc93d79ec1f874063fcae3 with SHA-1 7382285854 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/wpa_supplicant_8/+/13066759
Change-Id: Ia991fa3235eca258a19e18ea22d8a5f71a38cbfd
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 3bde8d5..3590b09 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -261,6 +261,10 @@
ifdef CONFIG_SAE
L_CFLAGS += -DCONFIG_SAE
OBJS += src/common/sae.c
+ifdef CONFIG_SAE_PK
+L_CFLAGS += -DCONFIG_SAE_PK
+OBJS += src/common/sae_pk.c
+endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -425,7 +429,7 @@
ifdef CONFIG_EAP_SIM_COMMON
OBJS += src/eap_common/eap_sim_common.c
# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
-# replaced with another file implementating the interface specified in
+# replaced with another file implementing the interface specified in
# eap_sim_db.h.
OBJS += src/eap_server/eap_sim_db.c
NEED_FIPS186_2_PRF=y
@@ -552,6 +556,12 @@
ifdef CONFIG_DPP
L_CFLAGS += -DCONFIG_DPP
OBJS += src/common/dpp.c
+OBJS += src/common/dpp_auth.c
+OBJS += src/common/dpp_backup.c
+OBJS += src/common/dpp_crypto.c
+OBJS += src/common/dpp_pkex.c
+OBJS += src/common/dpp_reconfig.c
+OBJS += src/common/dpp_tcp.c
OBJS += src/ap/dpp_hostapd.c
OBJS += src/ap/gas_query_ap.c
NEED_AES_SIV=y
@@ -1147,6 +1157,7 @@
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.0
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.1
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.2
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.hostapd@1.3
LOCAL_SHARED_LIBRARIES += libbase libhidlbase libutils
LOCAL_STATIC_LIBRARIES += libhostapd_hidl
endif
@@ -1189,7 +1200,7 @@
LOCAL_CPPFLAGS := $(L_CPPFLAGS)
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_C_INCLUDES := $(INCLUDES)
-HIDL_INTERFACE_VERSION = 1.2
+HIDL_INTERFACE_VERSION = 1.3
LOCAL_SRC_FILES := \
hidl/$(HIDL_INTERFACE_VERSION)/hidl.cpp \
hidl/$(HIDL_INTERFACE_VERSION)/hostapd.cpp
@@ -1197,6 +1208,7 @@
android.hardware.wifi.hostapd@1.0 \
android.hardware.wifi.hostapd@1.1 \
android.hardware.wifi.hostapd@1.2 \
+ android.hardware.wifi.hostapd@1.3 \
libbase \
libhidlbase \
libutils \
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index 6c4410e..34a8a08 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -362,7 +362,7 @@
* RADIUS server functionality
- add minimal RADIUS accounting server support (hostapd-as-server);
this is mainly to enable testing coverage with hwsim scripts
- - allow authentication log to be written into SQLite databse
+ - allow authentication log to be written into SQLite database
- added option for TLS protocol testing of an EAP peer by simulating
various misbehaviors/known attacks
- MAC ACL support for testing purposes
@@ -668,7 +668,7 @@
* fixed HT Capabilities IE with nl80211 drivers
* moved generic AP functionality code into src/ap
* WPS: handle Selected Registrar as union of info from all Registrars
- * remove obsolte Prism54.org driver wrapper
+ * remove obsolete Prism54.org driver wrapper
* added internal debugging mechanism with backtrace support and memory
allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
* EAP-FAST server: piggyback Phase 2 start with the end of Phase 1
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 9c7fc5c..066bfae 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -1,10 +1,7 @@
-ifndef CC
-CC=gcc
-endif
+ALL=hostapd hostapd_cli
+CONFIG_FILE = .config
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../src/build.rules
ifdef LIBS
# If LIBS is set with some global build system defaults, clone those for
@@ -19,6 +16,9 @@
ifndef LIBS_n
LIBS_n := $(LIBS)
endif
+ifndef LIBS_s
+LIBS_s := $(LIBS)
+endif
endif
CFLAGS += $(EXTRA_CFLAGS)
@@ -27,8 +27,6 @@
export BINDIR ?= /usr/local/bin/
--include .config
-
ifndef CONFIG_NO_GITVER
# Add VERSION_STR postfix for builds from a git repository
ifeq ($(wildcard ../.git),../.git)
@@ -122,6 +120,7 @@
LIBS_c += -lbfd -ldl -liberty -lz
LIBS_h += -lbfd -ldl -liberty -lz
LIBS_n += -lbfd -ldl -liberty -lz
+LIBS_s += -lbfd -ldl -liberty -lz
endif
endif
@@ -294,6 +293,10 @@
ifdef CONFIG_SAE
CFLAGS += -DCONFIG_SAE
OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_PK
+CFLAGS += -DCONFIG_SAE_PK
+OBJS += ../src/common/sae_pk.o
+endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -445,7 +448,7 @@
ifdef CONFIG_EAP_SIM_COMMON
OBJS += ../src/eap_common/eap_sim_common.o
# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
-# replaced with another file implementating the interface specified in
+# replaced with another file implementing the interface specified in
# eap_sim_db.h.
OBJS += ../src/eap_server/eap_sim_db.o
NEED_FIPS186_2_PRF=y
@@ -572,6 +575,12 @@
ifdef CONFIG_DPP
CFLAGS += -DCONFIG_DPP
OBJS += ../src/common/dpp.o
+OBJS += ../src/common/dpp_auth.o
+OBJS += ../src/common/dpp_backup.o
+OBJS += ../src/common/dpp_crypto.o
+OBJS += ../src/common/dpp_pkex.o
+OBJS += ../src/common/dpp_reconfig.o
+OBJS += ../src/common/dpp_tcp.o
OBJS += ../src/ap/dpp_hostapd.o
OBJS += ../src/ap/gas_query_ap.o
NEED_AES_SIV=y
@@ -698,6 +707,7 @@
endif
OBJS += ../src/crypto/crypto_openssl.o
HOBJS += ../src/crypto/crypto_openssl.o
+SOBJS += ../src/crypto/crypto_openssl.o
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_openssl.o
endif
@@ -705,9 +715,11 @@
LIBS += -lcrypto
LIBS_h += -lcrypto
LIBS_n += -lcrypto
+LIBS_s += -lcrypto
ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_h += -ldl
+LIBS_s += -ldl
endif
ifndef CONFIG_TLS_DEFAULT_CIPHERS
CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
@@ -1251,60 +1263,25 @@
CFLAGS += -DCONFIG_NO_TKIP
endif
-ALL=hostapd hostapd_cli
-
-all: verify_config $(ALL)
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
-
-ifdef CONFIG_CODE_COVERAGE
-%.o: %.c
- @$(E) " CC " $<
- $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
-else
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-endif
-
-verify_config:
- @if [ ! -r .config ]; then \
- echo 'Building hostapd requires a configuration file'; \
- echo '(.config). See README for more instructions. You can'; \
- echo 'run "cp defconfig .config" to create an example'; \
- echo 'configuration.'; \
- exit 1; \
- fi
-
$(DESTDIR)$(BINDIR)/%: %
install -D $(<) $(@)
install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
-../src/drivers/build.hostapd:
- @if [ -f ../src/drivers/build.wpa_supplicant ]; then \
- $(MAKE) -C ../src/drivers clean; \
- fi
- @touch ../src/drivers/build.hostapd
+_OBJS_VAR := OBJS
+include ../src/objs.mk
-BCHECK=../src/drivers/build.hostapd
-
-hostapd: $(BCHECK) $(OBJS)
+hostapd: $(OBJS)
$(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
@$(E) " LD " $@
ifdef CONFIG_WPA_TRACE
OBJS_c += ../src/utils/trace.o
endif
+
+_OBJS_VAR := OBJS_c
+include ../src/objs.mk
+
hostapd_cli: $(OBJS_c)
$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
@$(E) " LD " $@
@@ -1339,6 +1316,35 @@
HOBJS += ../src/crypto/crypto_linux.o
endif
+SOBJS += sae_pk_gen.o
+SOBJS += ../src/utils/common.o
+SOBJS += ../src/utils/os_$(CONFIG_OS).o
+SOBJS += ../src/utils/base64.o
+SOBJS += ../src/utils/wpa_debug.o
+SOBJS += ../src/utils/wpabuf.o
+ifdef CONFIG_WPA_TRACE
+SOBJS += ../src/utils/trace.o
+endif
+SOBJS += ../src/common/ieee802_11_common.o
+SOBJS += ../src/common/sae.o
+SOBJS += ../src/common/sae_pk.o
+SOBJS += ../src/common/dragonfly.o
+SOBJS += $(AESOBJS)
+SOBJS += ../src/crypto/sha256-prf.o
+SOBJS += ../src/crypto/sha384-prf.o
+SOBJS += ../src/crypto/sha512-prf.o
+SOBJS += ../src/crypto/dh_groups.o
+SOBJS += ../src/crypto/sha256-kdf.o
+SOBJS += ../src/crypto/sha384-kdf.o
+SOBJS += ../src/crypto/sha512-kdf.o
+
+_OBJS_VAR := NOBJS
+include ../src/objs.mk
+_OBJS_VAR := HOBJS
+include ../src/objs.mk
+_OBJS_VAR := SOBJS
+include ../src/objs.mk
+
nt_password_hash: $(NOBJS)
$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
@$(E) " LD " $@
@@ -1347,15 +1353,17 @@
$(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
@$(E) " LD " $@
+sae_pk_gen: $(SOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o sae_pk_gen $(SOBJS) $(LIBS_s)
+ @$(E) " LD " $@
+
+.PHONY: lcov-html
lcov-html:
- lcov -c -d .. > lcov.info
+ lcov -c -d $(BUILDDIR) > lcov.info
genhtml lcov.info --output-directory lcov-html
-clean:
- $(MAKE) -C ../src clean
- rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw
- rm -f *.d *.gcno *.gcda *.gcov
+clean: common-clean
+ rm -f core *~ nt_password_hash hlr_auc_gw
+ rm -f sae_pk_gen
rm -f lcov.info
rm -rf lcov-html
-
--include $(OBJS:%.o=%.d)
diff --git a/hostapd/android.config b/hostapd/android.config
index cd54efc..f791daa 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -25,9 +25,13 @@
#LIBS += -L$(LIBNL)/lib
CONFIG_LIBNL20=y
+# BCM vendor extensions to nl80211
+ifeq ($(BOARD_WLAN_DEVICE),bcmdhd)
+CONFIG_DRIVER_NL80211_BRCM=y
+else
# QCA vendor extensions to nl80211
CONFIG_DRIVER_NL80211_QCA=y
-
+endif
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
#CFLAGS += -I/usr/local/include
@@ -128,6 +132,11 @@
# IEEE 802.11ac (Very High Throughput) support
CONFIG_IEEE80211AC=y
+# IEEE 802.11ax (High Efficiency) support
+ifeq ($(WIFI_FEATURE_HOSTAPD_11AX), true)
+CONFIG_IEEE80211AX=y
+endif
+
# Remove debugging code that is printing out debug messages to stdout.
# This can be used to reduce the size of the hostapd considerably if debugging
# code is not needed.
@@ -187,7 +196,7 @@
#CONFIG_FST=y
# Multiband Operation support
-# These extentions facilitate efficient use of multiple frequency bands
+# These extensions facilitate efficient use of multiple frequency bands
# available to the AP and the devices that may associate with it.
#CONFIG_MBO=y
@@ -231,3 +240,7 @@
# be completely removed in a future release.
CONFIG_WEP=y
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks.
+CONFIG_INTERWORKING=y
diff --git a/hostapd/android.hardware.wifi.hostapd.xml b/hostapd/android.hardware.wifi.hostapd.xml
index 4dc1701..c688d3e 100644
--- a/hostapd/android.hardware.wifi.hostapd.xml
+++ b/hostapd/android.hardware.wifi.hostapd.xml
@@ -2,7 +2,7 @@
<hal format="hidl">
<name>android.hardware.wifi.hostapd</name>
<transport>hwbinder</transport>
- <version>1.2</version>
+ <version>1.3</version>
<interface>
<name>IHostapd</name>
<instance>default</instance>
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index cc1855d..ce32f3c 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
@@ -340,7 +341,7 @@
struct hostapd_radius_attr *attr, *a;
attr = hostapd_parse_radius_attr(buf + 19);
if (attr == NULL) {
- wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+ wpa_printf(MSG_ERROR, "Invalid radius_accept_attr: %s",
buf + 19);
user = NULL; /* already in the BSS list */
goto failed;
@@ -942,104 +943,6 @@
}
-/* convert floats with one decimal place to value*10 int, i.e.,
- * "1.5" will return 15 */
-static int hostapd_config_read_int10(const char *value)
-{
- int i, d;
- char *pos;
-
- i = atoi(value);
- pos = os_strchr(value, '.');
- d = 0;
- if (pos) {
- pos++;
- if (*pos >= '0' && *pos <= '9')
- d = *pos - '0';
- }
-
- return i * 10 + d;
-}
-
-
-static int valid_cw(int cw)
-{
- return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
- cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
- cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
- cw == 32767);
-}
-
-
-enum {
- IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
- IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
- IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
- IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
-};
-
-static int hostapd_config_tx_queue(struct hostapd_config *conf,
- const char *name, const char *val)
-{
- int num;
- const char *pos;
- struct hostapd_tx_queue_params *queue;
-
- /* skip 'tx_queue_' prefix */
- pos = name + 9;
- if (os_strncmp(pos, "data", 4) == 0 &&
- pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
- num = pos[4] - '0';
- pos += 6;
- } else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
- os_strncmp(pos, "beacon_", 7) == 0) {
- wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
- return 0;
- } else {
- wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
- return -1;
- }
-
- if (num >= NUM_TX_QUEUES) {
- /* for backwards compatibility, do not trigger failure */
- wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
- return 0;
- }
-
- queue = &conf->tx_queue[num];
-
- if (os_strcmp(pos, "aifs") == 0) {
- queue->aifs = atoi(val);
- if (queue->aifs < 0 || queue->aifs > 255) {
- wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
- queue->aifs);
- return -1;
- }
- } else if (os_strcmp(pos, "cwmin") == 0) {
- queue->cwmin = atoi(val);
- if (!valid_cw(queue->cwmin)) {
- wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
- queue->cwmin);
- return -1;
- }
- } else if (os_strcmp(pos, "cwmax") == 0) {
- queue->cwmax = atoi(val);
- if (!valid_cw(queue->cwmax)) {
- wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
- queue->cwmax);
- return -1;
- }
- } else if (os_strcmp(pos, "burst") == 0) {
- queue->burst = hostapd_config_read_int10(val);
- } else {
- wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos);
- return -1;
- }
-
- return 0;
-}
-
-
#ifdef CONFIG_IEEE80211R_AP
static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
@@ -2290,6 +2193,35 @@
pw->vlan_id = atoi(pos2);
}
+#ifdef CONFIG_SAE_PK
+ pos2 = os_strstr(pos, "|pk=");
+ if (pos2) {
+ const char *epos;
+ char *tmp;
+
+ if (!end)
+ end = pos2;
+ pos2 += 4;
+ epos = os_strchr(pos2, '|');
+ if (epos) {
+ tmp = os_malloc(epos - pos2 + 1);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, pos2, epos - pos2);
+ tmp[epos - pos2] = '\0';
+ } else {
+ tmp = os_strdup(pos2);
+ if (!tmp)
+ goto fail;
+ }
+
+ pw->pk = sae_parse_pk(tmp);
+ str_clear_free(tmp);
+ if (!pw->pk)
+ goto fail;
+ }
+#endif /* CONFIG_SAE_PK */
+
pos2 = os_strstr(pos, "|id=");
if (pos2) {
if (!end)
@@ -2312,6 +2244,18 @@
pw->password[end - val] = '\0';
}
+#ifdef CONFIG_SAE_PK
+ if (pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ !sae_pk_valid_password(pw->password)) {
+ wpa_printf(MSG_INFO,
+ "Invalid SAE password for a SAE-PK sae_password entry");
+ goto fail;
+ }
+#endif /* CONFIG_SAE_PK */
+
pw->next = bss->sae_passwords;
bss->sae_passwords = pw;
@@ -2319,6 +2263,9 @@
fail:
str_clear_free(pw->password);
os_free(pw->identifier);
+#ifdef CONFIG_SAE_PK
+ sae_deinit_pk(pw->pk);
+#endif /* CONFIG_SAE_PK */
os_free(pw);
return -1;
}
@@ -2612,7 +2559,7 @@
} else if (os_strcmp(buf, "eap_teap_auth") == 0) {
int val = atoi(pos);
- if (val < 0 || val > 1) {
+ if (val < 0 || val > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid eap_teap_auth value",
line);
@@ -3383,7 +3330,7 @@
} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
conf->ap_table_expiration_time = atoi(pos);
} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
- if (hostapd_config_tx_queue(conf, buf, pos)) {
+ if (hostapd_config_tx_queue(conf->tx_queue, buf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
line);
return 1;
@@ -4190,6 +4137,12 @@
bss->own_ie_override = tmp;
} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
bss->sae_reflection_attack = atoi(pos);
+ } else if (os_strcmp(buf, "sae_commit_status") == 0) {
+ bss->sae_commit_status = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pk_omit") == 0) {
+ bss->sae_pk_omit = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pk_password_check_skip") == 0) {
+ bss->sae_pk_password_check_skip = atoi(pos);
} else if (os_strcmp(buf, "sae_commit_override") == 0) {
wpabuf_free(bss->sae_commit_override);
bss->sae_commit_override = wpabuf_parse_bin(pos);
@@ -4217,6 +4170,20 @@
bss->skip_prune_assoc = atoi(pos);
} else if (os_strcmp(buf, "ft_rsnxe_used") == 0) {
bss->ft_rsnxe_used = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_eapol_m3") == 0) {
+ bss->oci_freq_override_eapol_m3 = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_eapol_g1") == 0) {
+ bss->oci_freq_override_eapol_g1 = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_saquery_req") == 0) {
+ bss->oci_freq_override_saquery_req = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_saquery_resp") == 0) {
+ bss->oci_freq_override_saquery_resp = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_ft_assoc") == 0) {
+ bss->oci_freq_override_ft_assoc = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_fils_assoc") == 0) {
+ bss->oci_freq_override_fils_assoc = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_wnm_sleep") == 0) {
+ bss->oci_freq_override_wnm_sleep = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 6e8352f..30fa47f 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -47,6 +47,7 @@
#include "ap/ap_config.h"
#include "ap/ieee802_1x.h"
#include "ap/wpa_auth.h"
+#include "ap/pmksa_cache_auth.h"
#include "ap/ieee802_11.h"
#include "ap/sta_info.h"
#include "ap/wps_hostapd.h"
@@ -1428,6 +1429,8 @@
hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
dpp_test = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
+ dpp_version_override = atoi(value);
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
@@ -1476,12 +1479,31 @@
os_strcmp(cmd, "sae_pwe") == 0) {
if (hapd->started)
hostapd_setup_sae_pt(hapd->conf);
+ } else if (os_strcasecmp(cmd, "transition_disable") == 0) {
+ wpa_auth_set_transition_disable(hapd->wpa_auth,
+ hapd->conf->transition_disable);
}
#ifdef CONFIG_TESTING_OPTIONS
if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
hapd->conf->ft_rsnxe_used);
+ else if (os_strcmp(cmd, "oci_freq_override_eapol_m3") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_eapol_g1") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_ft_assoc") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_fils_assoc") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth,
+ WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC, atoi(value));
#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -1900,7 +1922,7 @@
if (ip.ip_hl != 5 || ip.ip_v != 4 ||
ntohs(ip.ip_len) > HWSIM_IP_LEN) {
wpa_printf(MSG_DEBUG,
- "test data: RX - ignore unexpect IP header");
+ "test data: RX - ignore unexpected IP header");
return;
}
@@ -2163,6 +2185,32 @@
if (hwaddr_aton(cmd, addr))
return -1;
+ if (is_broadcast_ether_addr(addr) && os_strstr(cmd, " BIGTK")) {
+ if (hapd->last_bigtk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset BIPN for BIGTK");
+
+ /* First, use a zero key to avoid any possible duplicate key
+ * avoidance in the driver. */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_bigtk_alg,
+ broadcast_ether_addr,
+ hapd->last_bigtk_key_idx, 0, 1, NULL, 0,
+ zero, hapd->last_bigtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC */
+ return hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_bigtk_alg,
+ broadcast_ether_addr,
+ hapd->last_bigtk_key_idx, 0, 1, NULL,
+ 0, hapd->last_bigtk,
+ hapd->last_bigtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT);
+ }
+
if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
if (hapd->last_igtk_alg == WPA_ALG_NONE)
return -1;
@@ -2409,6 +2457,19 @@
}
+static int hostapd_ctrl_get_pmksa_pmk(struct hostapd_data *hapd, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, addr, NULL);
+ if (!pmksa)
+ return -1;
+
+ return wpa_snprintf_hex(buf, buflen, pmksa->pmk, pmksa->pmk_len);
+}
+
+
static int hostapd_ctrl_get_pmk(struct hostapd_data *hapd, const char *cmd,
char *buf, size_t buflen)
{
@@ -2424,13 +2485,13 @@
if (!sta || !sta->wpa_sm) {
wpa_printf(MSG_DEBUG, "No STA WPA state machine for " MACSTR,
MAC2STR(addr));
- return -1;
+ return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
}
pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
- if (!pmk) {
+ if (!pmk || !pmk_len) {
wpa_printf(MSG_DEBUG, "No PMK stored for " MACSTR,
MAC2STR(addr));
- return -1;
+ return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
}
return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
@@ -3706,6 +3767,21 @@
} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
reply_len = -1;
+#ifdef CONFIG_DPP2
+ } else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
+ if (hostapd_dpp_controller_start(hapd, buf + 20) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
+ if (hostapd_dpp_controller_start(hapd, NULL) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
+ dpp_controller_stop(hapd->iface->interfaces->dpp);
+ } else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
+ if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
+ hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef RADIUS_SERVER
} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
@@ -4193,6 +4269,11 @@
#ifdef CONFIG_TESTING_OPTIONS
#ifdef CONFIG_DPP
dpp_test = DPP_TEST_DISABLED;
+#ifdef CONFIG_DPP2
+ dpp_version_override = 2;
+#else /* CONFIG_DPP2 */
+ dpp_version_override = 1;
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 2341765..11eed99 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -351,7 +351,7 @@
#CONFIG_ACS=y
# Multiband Operation support
-# These extentions facilitate efficient use of multiple frequency bands
+# These extensions facilitate efficient use of multiple frequency bands
# available to the AP and the devices that may associate with it.
#CONFIG_MBO=y
diff --git a/hostapd/hidl/1.2/hostapd.cpp b/hostapd/hidl/1.2/hostapd.cpp
deleted file mode 100644
index 537353a..0000000
--- a/hostapd/hidl/1.2/hostapd.cpp
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * hidl interface for wpa_hostapd daemon
- * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-#include <iomanip>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-
-#include "hostapd.h"
-#include "hidl_return_util.h"
-
-extern "C"
-{
-#include "utils/eloop.h"
-}
-
-// The HIDL implementation for hostapd creates a hostapd.conf dynamically for
-// each interface. This file can then be used to hook onto the normal config
-// file parsing logic in hostapd code. Helps us to avoid duplication of code
-// in the HIDL interface.
-// TOOD(b/71872409): Add unit tests for this.
-namespace {
-constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
-
-using android::base::RemoveFileIfExists;
-using android::base::StringPrintf;
-using android::base::WriteStringToFile;
-using android::hardware::wifi::hostapd::V1_2::IHostapd;
-
-std::string WriteHostapdConfig(
- const std::string& interface_name, const std::string& config)
-{
- const std::string file_path =
- StringPrintf(kConfFileNameFmt, interface_name.c_str());
- if (WriteStringToFile(
- config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
- getuid(), getgid())) {
- return file_path;
- }
- // Diagnose failure
- int error = errno;
- wpa_printf(
- MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
- file_path.c_str(), strerror(error));
- struct stat st;
- int result = stat(file_path.c_str(), &st);
- if (result == 0) {
- wpa_printf(
- MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
- st.st_uid, st.st_gid, st.st_mode);
- } else {
- wpa_printf(
- MSG_ERROR,
- "Error calling stat() on hostapd config file: %s",
- strerror(errno));
- }
- return "";
-}
-
-/*
- * Get the op_class for a channel/band
- * The logic here is based on Table E-4 in the 802.11 Specification
- */
-int getOpClassForChannel(int channel, int band, bool support11n, bool support11ac) {
- // 2GHz Band
- if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
- if (channel == 14) {
- return 82;
- }
- if (channel >= 1 && channel <= 13) {
- if (!support11n) {
- //20MHz channel
- return 81;
- }
- if (channel <= 9) {
- // HT40 with secondary channel above primary
- return 83;
- }
- // HT40 with secondary channel below primary
- return 84;
- }
- // Error
- return 0;
- }
-
- // 5GHz Band
- if ((band & IHostapd::BandMask::BAND_5_GHZ) != 0) {
- if (support11ac) {
- switch (channel) {
- case 42:
- case 58:
- case 106:
- case 122:
- case 138:
- case 155:
- // 80MHz channel
- return 128;
- case 50:
- case 114:
- // 160MHz channel
- return 129;
- }
- }
-
- if (!support11n) {
- if (channel >= 36 && channel <= 48) {
- return 115;
- }
- if (channel >= 52 && channel <= 64) {
- return 118;
- }
- if (channel >= 100 && channel <= 144) {
- return 121;
- }
- if (channel >= 149 && channel <= 161) {
- return 124;
- }
- if (channel >= 165 && channel <= 169) {
- return 125;
- }
- } else {
- switch (channel) {
- case 36:
- case 44:
- // HT40 with secondary channel above primary
- return 116;
- case 40:
- case 48:
- // HT40 with secondary channel below primary
- return 117;
- case 52:
- case 60:
- // HT40 with secondary channel above primary
- return 119;
- case 56:
- case 64:
- // HT40 with secondary channel below primary
- return 120;
- case 100:
- case 108:
- case 116:
- case 124:
- case 132:
- case 140:
- // HT40 with secondary channel above primary
- return 122;
- case 104:
- case 112:
- case 120:
- case 128:
- case 136:
- case 144:
- // HT40 with secondary channel below primary
- return 123;
- case 149:
- case 157:
- // HT40 with secondary channel above primary
- return 126;
- case 153:
- case 161:
- // HT40 with secondary channel below primary
- return 127;
- }
- }
- // Error
- return 0;
- }
-
- // 6GHz Band
- if ((band & IHostapd::BandMask::BAND_6_GHZ) != 0) {
- // Channels 1, 5. 9, 13, ...
- if ((channel & 0x03) == 0x01) {
- // 20MHz channel
- return 131;
- }
- // Channels 3, 11, 19, 27, ...
- if ((channel & 0x07) == 0x03) {
- // 40MHz channel
- return 132;
- }
- // Channels 7, 23, 39, 55, ...
- if ((channel & 0x0F) == 0x07) {
- // 80MHz channel
- return 133;
- }
- // Channels 15, 47, 69, ...
- if ((channel & 0x1F) == 0x0F) {
- // 160MHz channel
- return 134;
- }
- // Error
- return 0;
- }
-
- return 0;
-}
-
-bool validatePassphrase(int passphrase_len, int min_len, int max_len)
-{
- if (min_len != -1 && passphrase_len < min_len) return false;
- if (max_len != -1 && passphrase_len > max_len) return false;
- return true;
-}
-
-std::string CreateHostapdConfig(
- const IHostapd::IfaceParams& iface_params,
- const IHostapd::NetworkParams& nw_params)
-{
- if (nw_params.V1_0.ssid.size() >
- static_cast<uint32_t>(
- IHostapd::ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
- wpa_printf(
- MSG_ERROR, "Invalid SSID size: %zu", nw_params.V1_0.ssid.size());
- return "";
- }
-
- // SSID string
- std::stringstream ss;
- ss << std::hex;
- ss << std::setfill('0');
- for (uint8_t b : nw_params.V1_0.ssid) {
- ss << std::setw(2) << static_cast<unsigned int>(b);
- }
- const std::string ssid_as_string = ss.str();
-
- // Encryption config string
- std::string encryption_config_as_string;
- switch (nw_params.encryptionType) {
- case IHostapd::EncryptionType::NONE:
- // no security params
- break;
- case IHostapd::EncryptionType::WPA:
- if (!validatePassphrase(
- nw_params.passphrase.size(),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
- return "";
- }
- encryption_config_as_string = StringPrintf(
- "wpa=3\n"
- "wpa_pairwise=TKIP CCMP\n"
- "wpa_passphrase=%s",
- nw_params.passphrase.c_str());
- break;
- case IHostapd::EncryptionType::WPA2:
- if (!validatePassphrase(
- nw_params.passphrase.size(),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
- return "";
- }
- encryption_config_as_string = StringPrintf(
- "wpa=2\n"
- "rsn_pairwise=CCMP\n"
- "wpa_passphrase=%s",
- nw_params.passphrase.c_str());
- break;
- case IHostapd::EncryptionType::WPA3_SAE_TRANSITION:
- if (!validatePassphrase(
- nw_params.passphrase.size(),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
- static_cast<uint32_t>(IHostapd::ParamSizeLimits::
- WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
- return "";
- }
- encryption_config_as_string = StringPrintf(
- "wpa=2\n"
- "rsn_pairwise=CCMP\n"
- "wpa_key_mgmt=WPA-PSK SAE\n"
- "ieee80211w=1\n"
- "sae_require_mfp=1\n"
- "wpa_passphrase=%s\n"
- "sae_password=%s",
- nw_params.passphrase.c_str(),
- nw_params.passphrase.c_str());
- break;
- case IHostapd::EncryptionType::WPA3_SAE:
- if (!validatePassphrase(nw_params.passphrase.size(), 1, -1)) {
- return "";
- }
- encryption_config_as_string = StringPrintf(
- "wpa=2\n"
- "rsn_pairwise=CCMP\n"
- "wpa_key_mgmt=SAE\n"
- "ieee80211w=2\n"
- "sae_require_mfp=2\n"
- "sae_password=%s",
- nw_params.passphrase.c_str());
- break;
- default:
- wpa_printf(MSG_ERROR, "Unknown encryption type");
- return "";
- }
-
- unsigned int band = 0;
- band |= iface_params.channelParams.bandMask;
-
- std::string channel_config_as_string;
- bool isFirst = true;
- if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
- std::string freqList_as_string;
- for (const auto &range :
- iface_params.channelParams.acsChannelFreqRangesMhz) {
- if (!isFirst) {
- freqList_as_string += ",";
- }
- isFirst = false;
-
- if (range.start != range.end) {
- freqList_as_string +=
- StringPrintf("%d-%d", range.start, range.end);
- } else {
- freqList_as_string += StringPrintf("%d", range.start);
- }
- }
- channel_config_as_string = StringPrintf(
- "channel=0\n"
- "acs_exclude_dfs=%d\n"
- "freqlist=%s",
- iface_params.V1_1.V1_0.channelParams.acsShouldExcludeDfs,
- freqList_as_string.c_str());
- } else {
- int op_class = getOpClassForChannel(
- iface_params.V1_1.V1_0.channelParams.channel,
- band,
- iface_params.V1_1.V1_0.hwModeParams.enable80211N,
- iface_params.V1_1.V1_0.hwModeParams.enable80211AC);
- channel_config_as_string = StringPrintf(
- "channel=%d\n"
- "op_class=%d",
- iface_params.V1_1.V1_0.channelParams.channel, op_class);
- }
-
- std::string hw_mode_as_string;
- std::string ht_cap_vht_oper_chwidth_as_string;
-
- if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
- if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
- || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
- hw_mode_as_string = "hw_mode=any";
- if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
- ht_cap_vht_oper_chwidth_as_string =
- "ht_capab=[HT40+]\n"
- "vht_oper_chwidth=1";
- }
- } else {
- hw_mode_as_string = "hw_mode=g";
- }
- } else {
- if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
- || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
- hw_mode_as_string = "hw_mode=a";
- if (iface_params.V1_1.V1_0.channelParams.enableAcs) {
- ht_cap_vht_oper_chwidth_as_string =
- "ht_capab=[HT40+]\n"
- "vht_oper_chwidth=1";
- }
- } else {
- wpa_printf(MSG_ERROR, "Invalid band");
- return "";
- }
- }
-
- std::string he_params_as_string;
-#ifdef CONFIG_IEEE80211AX
- if (iface_params.hwModeParams.enable80211AX) {
- he_params_as_string = StringPrintf(
- "ieee80211ax=1\n"
- "he_su_beamformer=%d\n"
- "he_su_beamformee=%d\n"
- "he_mu_beamformer=%d\n"
- "he_twt_required=%d\n",
- iface_params.hwModeParams.enableHeSingleUserBeamformer ? 1 : 0,
- iface_params.hwModeParams.enableHeSingleUserBeamformee ? 1 : 0,
- iface_params.hwModeParams.enableHeMultiUserBeamformer ? 1 : 0,
- iface_params.hwModeParams.enableHeTargetWakeTime ? 1 : 0);
- } else {
- he_params_as_string = "ieee80211ax=0";
- }
-#endif /* CONFIG_IEEE80211AX */
-
- return StringPrintf(
- "interface=%s\n"
- "driver=nl80211\n"
- "ctrl_interface=/data/vendor/wifi/hostapd/ctrl\n"
- // ssid2 signals to hostapd that the value is not a literal value
- // for use as a SSID. In this case, we're giving it a hex
- // std::string and hostapd needs to expect that.
- "ssid2=%s\n"
- "%s\n"
- "ieee80211n=%d\n"
- "ieee80211ac=%d\n"
- "%s\n"
- "%s\n"
- "%s\n"
- "ignore_broadcast_ssid=%d\n"
- "wowlan_triggers=any\n"
- "%s\n",
- iface_params.V1_1.V1_0.ifaceName.c_str(), ssid_as_string.c_str(),
- channel_config_as_string.c_str(),
- iface_params.V1_1.V1_0.hwModeParams.enable80211N ? 1 : 0,
- iface_params.V1_1.V1_0.hwModeParams.enable80211AC ? 1 : 0,
- he_params_as_string.c_str(),
- hw_mode_as_string.c_str(), ht_cap_vht_oper_chwidth_as_string.c_str(),
- nw_params.V1_0.isHidden ? 1 : 0, encryption_config_as_string.c_str());
-}
-
-// hostapd core functions accept "C" style function pointers, so use global
-// functions to pass to the hostapd core function and store the corresponding
-// std::function methods to be invoked.
-//
-// NOTE: Using the pattern from the vendor HAL (wifi_legacy_hal.cpp).
-//
-// Callback to be invoked once setup is complete
-std::function<void(struct hostapd_data*)> on_setup_complete_internal_callback;
-void onAsyncSetupCompleteCb(void* ctx)
-{
- struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
- if (on_setup_complete_internal_callback) {
- on_setup_complete_internal_callback(iface_hapd);
- // Invalidate this callback since we don't want this firing
- // again.
- on_setup_complete_internal_callback = nullptr;
- }
-}
-} // namespace
-
-namespace android {
-namespace hardware {
-namespace wifi {
-namespace hostapd {
-namespace V1_2 {
-namespace implementation {
-using hidl_return_util::call;
-using namespace android::hardware::wifi::hostapd::V1_0;
-
-Hostapd::Hostapd(struct hapd_interfaces* interfaces) : interfaces_(interfaces)
-{}
-
-Return<void> Hostapd::addAccessPoint(
- const V1_0::IHostapd::IfaceParams& iface_params,
- const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::addAccessPointInternal, _hidl_cb, iface_params,
- nw_params);
-}
-
-Return<void> Hostapd::addAccessPoint_1_1(
- const V1_1::IHostapd::IfaceParams& iface_params,
- const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::addAccessPointInternal_1_1, _hidl_cb, iface_params,
- nw_params);
-}
-
-Return<void> Hostapd::addAccessPoint_1_2(
- const IfaceParams& iface_params, const NetworkParams& nw_params,
- addAccessPoint_1_2_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::addAccessPointInternal_1_2, _hidl_cb, iface_params,
- nw_params);
-}
-
-Return<void> Hostapd::removeAccessPoint(
- const hidl_string& iface_name, removeAccessPoint_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::removeAccessPointInternal, _hidl_cb, iface_name);
-}
-
-Return<void> Hostapd::terminate()
-{
- wpa_printf(MSG_INFO, "Terminating...");
- eloop_terminate();
- return Void();
-}
-
-Return<void> Hostapd::registerCallback(
- const sp<V1_1::IHostapdCallback>& callback, registerCallback_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::registerCallbackInternal, _hidl_cb, callback);
-}
-
-Return<void> Hostapd::forceClientDisconnect(
- const hidl_string& iface_name, const hidl_array<uint8_t, 6>& client_address,
- V1_2::Ieee80211ReasonCode reason_code, forceClientDisconnect_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::forceClientDisconnectInternal, _hidl_cb, iface_name,
- client_address, reason_code);
-}
-
-Return<void> Hostapd::setDebugParams(
- DebugLevel level, setDebugParams_cb _hidl_cb)
-{
- return call(
- this, &Hostapd::setDebugParamsInternal, _hidl_cb, level);
-}
-
-V1_0::HostapdStatus Hostapd::addAccessPointInternal(
- const V1_0::IHostapd::IfaceParams& iface_params,
- const V1_0::IHostapd::NetworkParams& nw_params)
-{
- return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
-}
-
-V1_0::HostapdStatus Hostapd::addAccessPointInternal_1_1(
- const V1_1::IHostapd::IfaceParams& iface_params,
- const V1_1::IHostapd::NetworkParams& nw_params)
-{
- return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
-}
-
-HostapdStatus Hostapd::addAccessPointInternal_1_2(
- const IfaceParams& iface_params, const NetworkParams& nw_params)
-{
- if (hostapd_get_iface(interfaces_, iface_params.V1_1.V1_0.ifaceName.c_str())) {
- wpa_printf(
- MSG_ERROR, "Interface %s already present",
- iface_params.V1_1.V1_0.ifaceName.c_str());
- return {HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
- }
- const auto conf_params = CreateHostapdConfig(iface_params, nw_params);
- if (conf_params.empty()) {
- wpa_printf(MSG_ERROR, "Failed to create config params");
- return {HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
- }
- const auto conf_file_path =
- WriteHostapdConfig(iface_params.V1_1.V1_0.ifaceName, conf_params);
- if (conf_file_path.empty()) {
- wpa_printf(MSG_ERROR, "Failed to write config file");
- return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
- }
- std::string add_iface_param_str = StringPrintf(
- "%s config=%s", iface_params.V1_1.V1_0.ifaceName.c_str(),
- conf_file_path.c_str());
- std::vector<char> add_iface_param_vec(
- add_iface_param_str.begin(), add_iface_param_str.end() + 1);
- if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
- wpa_printf(
- MSG_ERROR, "Adding interface %s failed",
- add_iface_param_str.c_str());
- return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
- }
- struct hostapd_data* iface_hapd =
- hostapd_get_iface(interfaces_, iface_params.V1_1.V1_0.ifaceName.c_str());
- WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
- // Register the setup complete callbacks
- on_setup_complete_internal_callback =
- [this](struct hostapd_data* iface_hapd) {
- wpa_printf(
- MSG_DEBUG, "AP interface setup completed - state %s",
- hostapd_state_text(iface_hapd->iface->state));
- if (iface_hapd->iface->state == HAPD_IFACE_DISABLED) {
- // Invoke the failure callback on all registered
- // clients.
- for (const auto& callback : callbacks_) {
- callback->onFailure(
- iface_hapd->conf->iface);
- }
- }
- };
- iface_hapd->setup_complete_cb = onAsyncSetupCompleteCb;
- iface_hapd->setup_complete_cb_ctx = iface_hapd;
- if (hostapd_enable_iface(iface_hapd->iface) < 0) {
- wpa_printf(
- MSG_ERROR, "Enabling interface %s failed",
- iface_params.V1_1.V1_0.ifaceName.c_str());
- return {HostapdStatusCode::FAILURE_UNKNOWN, ""};
- }
- return {HostapdStatusCode::SUCCESS, ""};
-}
-
-V1_0::HostapdStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
-{
- std::vector<char> remove_iface_param_vec(
- iface_name.begin(), iface_name.end() + 1);
- if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) <
- 0) {
- wpa_printf(
- MSG_ERROR, "Removing interface %s failed",
- iface_name.c_str());
- return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
- }
- return {V1_0::HostapdStatusCode::SUCCESS, ""};
-}
-
-V1_0::HostapdStatus Hostapd::registerCallbackInternal(
- const sp<V1_1::IHostapdCallback>& callback)
-{
- callbacks_.push_back(callback);
- return {V1_0::HostapdStatusCode::SUCCESS, ""};
-}
-
-V1_2::HostapdStatus Hostapd::forceClientDisconnectInternal(const std::string& iface_name,
- const std::array<uint8_t, 6>& client_address, V1_2::Ieee80211ReasonCode reason_code)
-{
- struct hostapd_data *hapd = hostapd_get_iface(interfaces_, iface_name.c_str());
- struct sta_info *sta;
- if (!hapd) {
- wpa_printf(MSG_ERROR, "Interface %s doesn't exist", iface_name.c_str());
- return {V1_2::HostapdStatusCode::FAILURE_IFACE_UNKNOWN, ""};
- }
- for (sta = hapd->sta_list; sta; sta = sta->next) {
- int res;
- res = memcmp(sta->addr, client_address.data(), ETH_ALEN);
- if (res == 0) {
- wpa_printf(MSG_INFO, "Force client:" MACSTR " disconnect with reason: %d",
- MAC2STR(client_address.data()), (uint16_t) reason_code);
- ap_sta_disconnect(hapd, sta, sta->addr, (uint16_t) reason_code);
- return {V1_2::HostapdStatusCode::SUCCESS, ""};
- }
- }
- return {V1_2::HostapdStatusCode::FAILURE_CLIENT_UNKNOWN, ""};
-}
-
-V1_2::HostapdStatus Hostapd::setDebugParamsInternal(DebugLevel level)
-{
- wpa_debug_level = static_cast<uint32_t>(level);
- return {V1_2::HostapdStatusCode::SUCCESS, ""};
-}
-
-} // namespace implementation
-} // namespace V1_2
-} // namespace hostapd
-} // namespace wifi
-} // namespace hardware
-} // namespace android
diff --git a/hostapd/hidl/1.2/hidl.cpp b/hostapd/hidl/1.3/hidl.cpp
similarity index 93%
rename from hostapd/hidl/1.2/hidl.cpp
rename to hostapd/hidl/1.3/hidl.cpp
index 4bde312..68e4d86 100644
--- a/hostapd/hidl/1.2/hidl.cpp
+++ b/hostapd/hidl/1.3/hidl.cpp
@@ -22,8 +22,8 @@
using android::hardware::configureRpcThreadpool;
using android::hardware::IPCThreadState;
-using android::hardware::wifi::hostapd::V1_2::IHostapd;
-using android::hardware::wifi::hostapd::V1_2::implementation::Hostapd;
+using android::hardware::wifi::hostapd::V1_3::IHostapd;
+using android::hardware::wifi::hostapd::V1_3::implementation::Hostapd;
// This file is a bridge between the hostapd code written in 'C' and the HIDL
// interface in C++. So, using "C" style static globals here!
diff --git a/hostapd/hidl/1.2/hidl.h b/hostapd/hidl/1.3/hidl.h
similarity index 100%
rename from hostapd/hidl/1.2/hidl.h
rename to hostapd/hidl/1.3/hidl.h
diff --git a/hostapd/hidl/1.2/hidl_return_util.h b/hostapd/hidl/1.3/hidl_return_util.h
similarity index 96%
rename from hostapd/hidl/1.2/hidl_return_util.h
rename to hostapd/hidl/1.3/hidl_return_util.h
index 81742f8..6d50348 100644
--- a/hostapd/hidl/1.2/hidl_return_util.h
+++ b/hostapd/hidl/1.3/hidl_return_util.h
@@ -16,7 +16,7 @@
namespace hardware {
namespace wifi {
namespace hostapd {
-namespace V1_2 {
+namespace V1_3 {
namespace implementation {
namespace hidl_return_util {
@@ -35,7 +35,7 @@
}
} // namespace hidl_return_util
} // namespace implementation
-} // namespace V1_2
+} // namespace V1_3
} // namespace hostapd
} // namespace wifi
} // namespace hardware
diff --git a/hostapd/hidl/1.3/hostapd.cpp b/hostapd/hidl/1.3/hostapd.cpp
new file mode 100644
index 0000000..ce20cea
--- /dev/null
+++ b/hostapd/hidl/1.3/hostapd.cpp
@@ -0,0 +1,943 @@
+/*
+ * hidl interface for wpa_hostapd daemon
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2018, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <linux/if_bridge.h>
+
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include "hostapd.h"
+#include "hidl_return_util.h"
+
+extern "C"
+{
+#include "common/wpa_ctrl.h"
+#include "drivers/linux_ioctl.h"
+}
+
+// The HIDL implementation for hostapd creates a hostapd.conf dynamically for
+// each interface. This file can then be used to hook onto the normal config
+// file parsing logic in hostapd code. Helps us to avoid duplication of code
+// in the HIDL interface.
+// TOOD(b/71872409): Add unit tests for this.
+namespace {
+constexpr char kConfFileNameFmt[] = "/data/vendor/wifi/hostapd/hostapd_%s.conf";
+
+using android::base::RemoveFileIfExists;
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using android::hardware::wifi::hostapd::V1_3::IHostapd;
+using android::hardware::wifi::hostapd::V1_3::Generation;
+using android::hardware::wifi::hostapd::V1_3::Bandwidth;
+
+#define MAX_PORTS 1024
+bool GetInterfacesInBridge(std::string br_name,
+ std::vector<std::string>* interfaces) {
+ android::base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (sock.get() < 0) {
+ wpa_printf(MSG_ERROR, "Failed to create sock (%s) in %s",
+ strerror(errno), __FUNCTION__);
+ return false;
+ }
+
+ struct ifreq request;
+ int i, ifindices[MAX_PORTS];
+ char if_name[IFNAMSIZ];
+ unsigned long args[3];
+
+ memset(ifindices, 0, MAX_PORTS * sizeof(int));
+
+ args[0] = BRCTL_GET_PORT_LIST;
+ args[1] = (unsigned long) ifindices;
+ args[2] = MAX_PORTS;
+
+ strlcpy(request.ifr_name, br_name.c_str(), IFNAMSIZ);
+ request.ifr_data = (char *)args;
+
+ if (ioctl(sock.get(), SIOCDEVPRIVATE, &request) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to ioctl SIOCDEVPRIVATE in %s",
+ __FUNCTION__);
+ return false;
+ }
+
+ for (i = 0; i < MAX_PORTS; i ++) {
+ memset(if_name, 0, IFNAMSIZ);
+ if (ifindices[i] == 0 || !if_indextoname(ifindices[i], if_name)) {
+ continue;
+ }
+ interfaces->push_back(if_name);
+ }
+ return true;
+}
+
+std::string WriteHostapdConfig(
+ const std::string& interface_name, const std::string& config)
+{
+ const std::string file_path =
+ StringPrintf(kConfFileNameFmt, interface_name.c_str());
+ if (WriteStringToFile(
+ config, file_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+ getuid(), getgid())) {
+ return file_path;
+ }
+ // Diagnose failure
+ int error = errno;
+ wpa_printf(
+ MSG_ERROR, "Cannot write hostapd config to %s, error: %s",
+ file_path.c_str(), strerror(error));
+ struct stat st;
+ int result = stat(file_path.c_str(), &st);
+ if (result == 0) {
+ wpa_printf(
+ MSG_ERROR, "hostapd config file uid: %d, gid: %d, mode: %d",
+ st.st_uid, st.st_gid, st.st_mode);
+ } else {
+ wpa_printf(
+ MSG_ERROR,
+ "Error calling stat() on hostapd config file: %s",
+ strerror(errno));
+ }
+ return "";
+}
+
+/*
+ * Get the op_class for a channel/band
+ * The logic here is based on Table E-4 in the 802.11 Specification
+ */
+int getOpClassForChannel(int channel, int band, bool support11n, bool support11ac) {
+ // 2GHz Band
+ if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
+ if (channel == 14) {
+ return 82;
+ }
+ if (channel >= 1 && channel <= 13) {
+ if (!support11n) {
+ //20MHz channel
+ return 81;
+ }
+ if (channel <= 9) {
+ // HT40 with secondary channel above primary
+ return 83;
+ }
+ // HT40 with secondary channel below primary
+ return 84;
+ }
+ // Error
+ return 0;
+ }
+
+ // 5GHz Band
+ if ((band & IHostapd::BandMask::BAND_5_GHZ) != 0) {
+ if (support11ac) {
+ switch (channel) {
+ case 42:
+ case 58:
+ case 106:
+ case 122:
+ case 138:
+ case 155:
+ // 80MHz channel
+ return 128;
+ case 50:
+ case 114:
+ // 160MHz channel
+ return 129;
+ }
+ }
+
+ if (!support11n) {
+ if (channel >= 36 && channel <= 48) {
+ return 115;
+ }
+ if (channel >= 52 && channel <= 64) {
+ return 118;
+ }
+ if (channel >= 100 && channel <= 144) {
+ return 121;
+ }
+ if (channel >= 149 && channel <= 161) {
+ return 124;
+ }
+ if (channel >= 165 && channel <= 169) {
+ return 125;
+ }
+ } else {
+ switch (channel) {
+ case 36:
+ case 44:
+ // HT40 with secondary channel above primary
+ return 116;
+ case 40:
+ case 48:
+ // HT40 with secondary channel below primary
+ return 117;
+ case 52:
+ case 60:
+ // HT40 with secondary channel above primary
+ return 119;
+ case 56:
+ case 64:
+ // HT40 with secondary channel below primary
+ return 120;
+ case 100:
+ case 108:
+ case 116:
+ case 124:
+ case 132:
+ case 140:
+ // HT40 with secondary channel above primary
+ return 122;
+ case 104:
+ case 112:
+ case 120:
+ case 128:
+ case 136:
+ case 144:
+ // HT40 with secondary channel below primary
+ return 123;
+ case 149:
+ case 157:
+ // HT40 with secondary channel above primary
+ return 126;
+ case 153:
+ case 161:
+ // HT40 with secondary channel below primary
+ return 127;
+ }
+ }
+ // Error
+ return 0;
+ }
+
+ // 6GHz Band
+ if ((band & IHostapd::BandMask::BAND_6_GHZ) != 0) {
+ // Channels 1, 5. 9, 13, ...
+ if ((channel & 0x03) == 0x01) {
+ // 20MHz channel
+ return 131;
+ }
+ // Channels 3, 11, 19, 27, ...
+ if ((channel & 0x07) == 0x03) {
+ // 40MHz channel
+ return 132;
+ }
+ // Channels 7, 23, 39, 55, ...
+ if ((channel & 0x0F) == 0x07) {
+ // 80MHz channel
+ return 133;
+ }
+ // Channels 15, 47, 69, ...
+ if ((channel & 0x1F) == 0x0F) {
+ // 160MHz channel
+ return 134;
+ }
+ if (channel == 2) {
+ // 20MHz channel
+ return 136;
+ }
+ // Error
+ return 0;
+ }
+
+ return 0;
+}
+
+bool validatePassphrase(int passphrase_len, int min_len, int max_len)
+{
+ if (min_len != -1 && passphrase_len < min_len) return false;
+ if (max_len != -1 && passphrase_len > max_len) return false;
+ return true;
+}
+
+std::string CreateHostapdConfig(
+ const android::hardware::wifi::hostapd::V1_3::IHostapd::IfaceParams& iface_params,
+ const android::hardware::wifi::hostapd::V1_3::IHostapd::ChannelParams& channelParams,
+ const IHostapd::NetworkParams& nw_params,
+ const std::string br_name)
+{
+ if (nw_params.V1_2.V1_0.ssid.size() >
+ static_cast<uint32_t>(
+ IHostapd::ParamSizeLimits::SSID_MAX_LEN_IN_BYTES)) {
+ wpa_printf(
+ MSG_ERROR, "Invalid SSID size: %zu", nw_params.V1_2.V1_0.ssid.size());
+ return "";
+ }
+
+ // SSID string
+ std::stringstream ss;
+ ss << std::hex;
+ ss << std::setfill('0');
+ for (uint8_t b : nw_params.V1_2.V1_0.ssid) {
+ ss << std::setw(2) << static_cast<unsigned int>(b);
+ }
+ const std::string ssid_as_string = ss.str();
+
+ // Encryption config string
+ uint32_t band = 0;
+ band |= channelParams.V1_2.bandMask;
+ bool is_6Ghz_band_only = band == static_cast<uint32_t>(IHostapd::BandMask::BAND_6_GHZ);
+ std::string encryption_config_as_string;
+ switch (nw_params.V1_2.encryptionType) {
+ case IHostapd::EncryptionType::NONE:
+ // no security params
+ break;
+ case IHostapd::EncryptionType::WPA:
+ if (!validatePassphrase(
+ nw_params.V1_2.passphrase.size(),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+ return "";
+ }
+ encryption_config_as_string = StringPrintf(
+ "wpa=3\n"
+ "wpa_pairwise=TKIP CCMP\n"
+ "wpa_passphrase=%s",
+ nw_params.V1_2.passphrase.c_str());
+ break;
+ case IHostapd::EncryptionType::WPA2:
+ if (!validatePassphrase(
+ nw_params.V1_2.passphrase.size(),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+ return "";
+ }
+ encryption_config_as_string = StringPrintf(
+ "wpa=2\n"
+ "rsn_pairwise=CCMP\n"
+ "wpa_passphrase=%s",
+ nw_params.V1_2.passphrase.c_str());
+ break;
+ case IHostapd::EncryptionType::WPA3_SAE_TRANSITION:
+ if (!validatePassphrase(
+ nw_params.V1_2.passphrase.size(),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MIN_LEN_IN_BYTES),
+ static_cast<uint32_t>(IHostapd::ParamSizeLimits::
+ WPA2_PSK_PASSPHRASE_MAX_LEN_IN_BYTES))) {
+ return "";
+ }
+ encryption_config_as_string = StringPrintf(
+ "wpa=2\n"
+ "rsn_pairwise=CCMP\n"
+ "wpa_key_mgmt=WPA-PSK SAE\n"
+ "ieee80211w=1\n"
+ "sae_require_mfp=1\n"
+ "wpa_passphrase=%s\n"
+ "sae_password=%s",
+ nw_params.V1_2.passphrase.c_str(),
+ nw_params.V1_2.passphrase.c_str());
+ break;
+ case IHostapd::EncryptionType::WPA3_SAE:
+ if (!validatePassphrase(nw_params.V1_2.passphrase.size(), 1, -1)) {
+ return "";
+ }
+ encryption_config_as_string = StringPrintf(
+ "wpa=2\n"
+ "rsn_pairwise=CCMP\n"
+ "wpa_key_mgmt=SAE\n"
+ "ieee80211w=2\n"
+ "sae_require_mfp=2\n"
+ "sae_pwe=%d\n"
+ "sae_password=%s",
+ is_6Ghz_band_only ? 1 : 2,
+ nw_params.V1_2.passphrase.c_str());
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "Unknown encryption type");
+ return "";
+ }
+
+ std::string channel_config_as_string;
+ bool isFirst = true;
+ if (channelParams.enableAcs) {
+ std::string freqList_as_string;
+ for (const auto &range :
+ channelParams.V1_2.acsChannelFreqRangesMhz) {
+ if (!isFirst) {
+ freqList_as_string += ",";
+ }
+ isFirst = false;
+
+ if (range.start != range.end) {
+ freqList_as_string +=
+ StringPrintf("%d-%d", range.start, range.end);
+ } else {
+ freqList_as_string += StringPrintf("%d", range.start);
+ }
+ }
+ channel_config_as_string = StringPrintf(
+ "channel=0\n"
+ "acs_exclude_dfs=%d\n"
+ "freqlist=%s",
+ iface_params.V1_2.V1_1.V1_0.channelParams.acsShouldExcludeDfs,
+ freqList_as_string.c_str());
+ } else {
+ int op_class = getOpClassForChannel(
+ channelParams.channel,
+ band,
+ iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211N,
+ iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211AC);
+ channel_config_as_string = StringPrintf(
+ "channel=%d\n"
+ "op_class=%d",
+ channelParams.channel, op_class);
+ }
+
+ std::string hw_mode_as_string;
+ std::string ht_cap_vht_oper_chwidth_as_string;
+
+ if ((band & IHostapd::BandMask::BAND_2_GHZ) != 0) {
+ if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
+ || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
+ hw_mode_as_string = "hw_mode=any";
+ if (iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211AC) {
+ ht_cap_vht_oper_chwidth_as_string =
+ "ht_capab=[HT40+]\n"
+ "vht_oper_chwidth=1";
+ }
+ } else {
+ hw_mode_as_string = "hw_mode=g";
+ }
+ } else {
+ if (((band & IHostapd::BandMask::BAND_5_GHZ) != 0)
+ || ((band & IHostapd::BandMask::BAND_6_GHZ) != 0)) {
+ hw_mode_as_string = "hw_mode=a";
+ if (iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211AC) {
+ ht_cap_vht_oper_chwidth_as_string =
+ "ht_capab=[HT40+]\n"
+ "vht_oper_chwidth=1";
+ }
+ } else {
+ wpa_printf(MSG_ERROR, "Invalid band");
+ return "";
+ }
+ }
+
+ std::string he_params_as_string;
+#ifdef CONFIG_IEEE80211AX
+ if (iface_params.V1_2.hwModeParams.enable80211AX) {
+ he_params_as_string = StringPrintf(
+ "ieee80211ax=1\n"
+ "he_su_beamformer=%d\n"
+ "he_su_beamformee=%d\n"
+ "he_mu_beamformer=%d\n"
+ "he_twt_required=%d\n",
+ iface_params.V1_2.hwModeParams.enableHeSingleUserBeamformer ? 1 : 0,
+ iface_params.V1_2.hwModeParams.enableHeSingleUserBeamformee ? 1 : 0,
+ iface_params.V1_2.hwModeParams.enableHeMultiUserBeamformer ? 1 : 0,
+ iface_params.V1_2.hwModeParams.enableHeTargetWakeTime ? 1 : 0);
+ } else {
+ he_params_as_string = "ieee80211ax=0";
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_INTERWORKING
+ std::string access_network_params_as_string;
+ if (nw_params.isMetered) {
+ access_network_params_as_string = StringPrintf(
+ "interworking=1\n"
+ "access_network_type=2\n"); // CHARGEABLE_PUBLIC_NETWORK
+ } else {
+ access_network_params_as_string = StringPrintf(
+ "interworking=0\n");
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ std::string bridge_as_string;
+ if (!br_name.empty()) {
+ bridge_as_string = StringPrintf("bridge=%s", br_name.c_str());
+ }
+
+ return StringPrintf(
+ "interface=%s\n"
+ "driver=nl80211\n"
+ "ctrl_interface=/data/vendor/wifi/hostapd/ctrl\n"
+ // ssid2 signals to hostapd that the value is not a literal value
+ // for use as a SSID. In this case, we're giving it a hex
+ // std::string and hostapd needs to expect that.
+ "ssid2=%s\n"
+ "%s\n"
+ "ieee80211n=%d\n"
+ "ieee80211ac=%d\n"
+ "%s\n"
+ "%s\n"
+ "%s\n"
+ "ignore_broadcast_ssid=%d\n"
+ "wowlan_triggers=any\n"
+#ifdef CONFIG_INTERWORKING
+ "%s\n"
+#endif /* CONFIG_INTERWORKING */
+ "%s\n"
+ "%s\n",
+ iface_params.V1_2.V1_1.V1_0.ifaceName.c_str(), ssid_as_string.c_str(),
+ channel_config_as_string.c_str(),
+ iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211N ? 1 : 0,
+ iface_params.V1_2.V1_1.V1_0.hwModeParams.enable80211AC ? 1 : 0,
+ he_params_as_string.c_str(),
+ hw_mode_as_string.c_str(), ht_cap_vht_oper_chwidth_as_string.c_str(),
+ nw_params.V1_2.V1_0.isHidden ? 1 : 0,
+#ifdef CONFIG_INTERWORKING
+ access_network_params_as_string.c_str(),
+#endif /* CONFIG_INTERWORKING */
+ encryption_config_as_string.c_str(),
+ bridge_as_string.c_str());
+}
+
+Generation getGeneration(hostapd_hw_modes *current_mode)
+{
+ wpa_printf(MSG_DEBUG, "getGeneration hwmode=%d, ht_enabled=%d, vht_enabled=%d",
+ current_mode->mode, current_mode->ht_capab != 0,
+ current_mode->vht_capab != 0);
+ switch (current_mode->mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return Generation::WIFI_STANDARD_LEGACY;
+ case HOSTAPD_MODE_IEEE80211G:
+ return current_mode->ht_capab == 0 ?
+ Generation::WIFI_STANDARD_LEGACY : Generation::WIFI_STANDARD_11N;
+ case HOSTAPD_MODE_IEEE80211A:
+ return current_mode->vht_capab == 0 ?
+ Generation::WIFI_STANDARD_11N : Generation::WIFI_STANDARD_11AC;
+ // TODO: b/162484222 miss HOSTAPD_MODE_IEEE80211AX definition.
+ default:
+ return Generation::WIFI_STANDARD_UNKNOWN;
+ }
+}
+
+Bandwidth getBandwidth(struct hostapd_config *iconf)
+{
+ wpa_printf(MSG_DEBUG, "getBandwidth %d, isHT=%d, isHT40=%d",
+ iconf->vht_oper_chwidth, iconf->ieee80211n,
+ iconf->secondary_channel);
+ switch (iconf->vht_oper_chwidth) {
+ case CHANWIDTH_80MHZ:
+ return Bandwidth::WIFI_BANDWIDTH_80;
+ case CHANWIDTH_80P80MHZ:
+ return Bandwidth::WIFI_BANDWIDTH_80P80;
+ break;
+ case CHANWIDTH_160MHZ:
+ return Bandwidth::WIFI_BANDWIDTH_160;
+ break;
+ case CHANWIDTH_USE_HT:
+ if (iconf->ieee80211n) {
+ return iconf->secondary_channel != 0 ?
+ Bandwidth::WIFI_BANDWIDTH_40 : Bandwidth::WIFI_BANDWIDTH_20;
+ }
+ return Bandwidth::WIFI_BANDWIDTH_20_NOHT;
+ default:
+ return Bandwidth::WIFI_BANDWIDTH_INVALID;
+ }
+}
+
+// hostapd core functions accept "C" style function pointers, so use global
+// functions to pass to the hostapd core function and store the corresponding
+// std::function methods to be invoked.
+//
+// NOTE: Using the pattern from the vendor HAL (wifi_legacy_hal.cpp).
+//
+// Callback to be invoked once setup is complete
+std::function<void(struct hostapd_data*)> on_setup_complete_internal_callback;
+void onAsyncSetupCompleteCb(void* ctx)
+{
+ struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
+ if (on_setup_complete_internal_callback) {
+ on_setup_complete_internal_callback(iface_hapd);
+ // Invalidate this callback since we don't want this firing
+ // again in single AP mode.
+ if (strlen(iface_hapd->conf->bridge) > 0) {
+ on_setup_complete_internal_callback = nullptr;
+ }
+ }
+}
+
+// Callback to be invoked on hotspot client connection/disconnection
+std::function<void(struct hostapd_data*, const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr)> on_sta_authorized_internal_callback;
+void onAsyncStaAuthorizedCb(void* ctx, const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr)
+{
+ struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
+ if (on_sta_authorized_internal_callback) {
+ on_sta_authorized_internal_callback(iface_hapd, mac_addr,
+ authorized, p2p_dev_addr);
+ }
+}
+
+std::function<void(struct hostapd_data*, int level,
+ enum wpa_msg_type type, const char *txt,
+ size_t len)> on_wpa_msg_internal_callback;
+
+void onAsyncWpaEventCb(void *ctx, int level,
+ enum wpa_msg_type type, const char *txt,
+ size_t len)
+{
+ struct hostapd_data* iface_hapd = (struct hostapd_data*)ctx;
+ if (on_wpa_msg_internal_callback) {
+ on_wpa_msg_internal_callback(iface_hapd, level,
+ type, txt, len);
+ }
+}
+
+
+} // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace hostapd {
+namespace V1_3 {
+namespace implementation {
+using hidl_return_util::call;
+using namespace android::hardware::wifi::hostapd::V1_0;
+
+Hostapd::Hostapd(struct hapd_interfaces* interfaces)
+ : interfaces_(interfaces), death_notifier_(sp<DeathNotifier>::make())
+{}
+
+Return<void> Hostapd::addAccessPoint(
+ const V1_0::IHostapd::IfaceParams& iface_params,
+ const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::addAccessPointInternal, _hidl_cb, iface_params,
+ nw_params);
+}
+
+Return<void> Hostapd::addAccessPoint_1_1(
+ const V1_1::IHostapd::IfaceParams& iface_params,
+ const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::addAccessPointInternal_1_1, _hidl_cb, iface_params,
+ nw_params);
+}
+
+Return<void> Hostapd::addAccessPoint_1_2(
+ const V1_2::IHostapd::IfaceParams& iface_params,
+ const V1_2::IHostapd::NetworkParams& nw_params,
+ addAccessPoint_1_2_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::addAccessPointInternal_1_2, _hidl_cb, iface_params,
+ nw_params);
+}
+
+Return<void> Hostapd::addAccessPoint_1_3(
+ const V1_3::IHostapd::IfaceParams& iface_params,
+ const V1_3::IHostapd::NetworkParams& nw_params,
+ addAccessPoint_1_3_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::addAccessPointInternal_1_3, _hidl_cb, iface_params,
+ nw_params);
+}
+
+Return<void> Hostapd::removeAccessPoint(
+ const hidl_string& iface_name, removeAccessPoint_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::removeAccessPointInternal, _hidl_cb, iface_name);
+}
+
+Return<void> Hostapd::terminate()
+{
+ wpa_printf(MSG_INFO, "Terminating...");
+ // Clear the callback to avoid IPCThreadState shutdown during the
+ // callback event.
+ callbacks_.clear();
+ eloop_terminate();
+ return Void();
+}
+
+Return<void> Hostapd::registerCallback(
+ const sp<V1_1::IHostapdCallback>& callback, registerCallback_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> Hostapd::registerCallback_1_3(
+ const sp<V1_3::IHostapdCallback>& callback, registerCallback_1_3_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::registerCallbackInternal_1_3, _hidl_cb, callback);
+}
+
+Return<void> Hostapd::forceClientDisconnect(
+ const hidl_string& iface_name, const hidl_array<uint8_t, 6>& client_address,
+ V1_2::Ieee80211ReasonCode reason_code, forceClientDisconnect_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::forceClientDisconnectInternal, _hidl_cb, iface_name,
+ client_address, reason_code);
+}
+
+Return<void> Hostapd::setDebugParams(
+ V1_2::DebugLevel level, setDebugParams_cb _hidl_cb)
+{
+ return call(
+ this, &Hostapd::setDebugParamsInternal, _hidl_cb, level);
+}
+
+V1_0::HostapdStatus Hostapd::addAccessPointInternal(
+ const V1_0::IHostapd::IfaceParams& iface_params,
+ const V1_0::IHostapd::NetworkParams& nw_params)
+{
+ return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+V1_0::HostapdStatus Hostapd::addAccessPointInternal_1_1(
+ const V1_1::IHostapd::IfaceParams& iface_params,
+ const V1_1::IHostapd::NetworkParams& nw_params)
+{
+ return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+V1_2::HostapdStatus Hostapd::addAccessPointInternal_1_2(
+ const V1_2::IHostapd::IfaceParams& iface_params,
+ const V1_2::IHostapd::NetworkParams& nw_params) {
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+V1_2::HostapdStatus Hostapd::addAccessPointInternal_1_3(
+ const V1_3::IHostapd::IfaceParams& iface_params,
+ const V1_3::IHostapd::NetworkParams& nw_params)
+{
+ int channelParamsListSize = iface_params.channelParamsList.size();
+ if (channelParamsListSize == 1) {
+ // Single AP
+ wpa_printf(MSG_INFO, "AddSingleAccessPoint, iface=%s",
+ iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ return addSingleAccessPoint(iface_params, iface_params.channelParamsList[0],
+ nw_params, "");
+ } else if (channelParamsListSize == 2) {
+ // Concurrent APs
+ wpa_printf(MSG_INFO, "AddDualAccessPoint, iface=%s",
+ iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ return addConcurrentAccessPoints(iface_params, nw_params);
+ }
+ return {V1_2::HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
+}
+
+V1_2::HostapdStatus Hostapd::addConcurrentAccessPoints(
+ const V1_3::IHostapd::IfaceParams& iface_params, const V1_3::IHostapd::NetworkParams& nw_params)
+{
+ int channelParamsListSize = iface_params.channelParamsList.size();
+ // Get available interfaces in bridge
+ std::vector<std::string> managed_interfaces;
+ std::string br_name = StringPrintf(
+ "%s", iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ if (!GetInterfacesInBridge(br_name, &managed_interfaces)) {
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN,
+ "Get interfaces in bridge failed."};
+ }
+ if (managed_interfaces.size() < channelParamsListSize) {
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN,
+ "Available interfaces less than requested bands"};
+ }
+ // start BSS on specified bands
+ for (std::size_t i = 0; i < channelParamsListSize; i ++) {
+ V1_3::IHostapd::IfaceParams iface_params_new = iface_params;
+ iface_params_new.V1_2.V1_1.V1_0.ifaceName = managed_interfaces[i];
+ V1_2::HostapdStatus status = addSingleAccessPoint(
+ iface_params_new, iface_params.channelParamsList[i], nw_params, br_name);
+ if (status.code != V1_2::HostapdStatusCode::SUCCESS) {
+ wpa_printf(MSG_ERROR, "Failed to addAccessPoint %s",
+ managed_interfaces[i].c_str());
+ return status;
+ }
+ }
+ // Save bridge interface info
+ br_interfaces_[br_name] = managed_interfaces;
+ return {V1_2::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_2::HostapdStatus Hostapd::addSingleAccessPoint(
+ const V1_3::IHostapd::IfaceParams& iface_params,
+ const V1_3::IHostapd::ChannelParams& channelParams,
+ const V1_3::IHostapd::NetworkParams& nw_params,
+ const std::string br_name)
+{
+ if (hostapd_get_iface(interfaces_, iface_params.V1_2.V1_1.V1_0.ifaceName.c_str())) {
+ wpa_printf(
+ MSG_ERROR, "Interface %s already present",
+ iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ return {V1_2::HostapdStatusCode::FAILURE_IFACE_EXISTS, ""};
+ }
+ const auto conf_params = CreateHostapdConfig(iface_params, channelParams, nw_params, br_name);
+ if (conf_params.empty()) {
+ wpa_printf(MSG_ERROR, "Failed to create config params");
+ return {V1_2::HostapdStatusCode::FAILURE_ARGS_INVALID, ""};
+ }
+ const auto conf_file_path =
+ WriteHostapdConfig(iface_params.V1_2.V1_1.V1_0.ifaceName, conf_params);
+ if (conf_file_path.empty()) {
+ wpa_printf(MSG_ERROR, "Failed to write config file");
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ std::string add_iface_param_str = StringPrintf(
+ "%s config=%s", iface_params.V1_2.V1_1.V1_0.ifaceName.c_str(),
+ conf_file_path.c_str());
+ std::vector<char> add_iface_param_vec(
+ add_iface_param_str.begin(), add_iface_param_str.end() + 1);
+ if (hostapd_add_iface(interfaces_, add_iface_param_vec.data()) < 0) {
+ wpa_printf(
+ MSG_ERROR, "Adding interface %s failed",
+ add_iface_param_str.c_str());
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ struct hostapd_data* iface_hapd =
+ hostapd_get_iface(interfaces_, iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ WPA_ASSERT(iface_hapd != nullptr && iface_hapd->iface != nullptr);
+ // Register the setup complete callbacks
+ on_setup_complete_internal_callback =
+ [this](struct hostapd_data* iface_hapd) {
+ wpa_printf(
+ MSG_INFO, "AP interface setup completed - state %s",
+ hostapd_state_text(iface_hapd->iface->state));
+ if (iface_hapd->iface->state == HAPD_IFACE_DISABLED) {
+ // Invoke the failure callback on all registered
+ // clients.
+ for (const auto& callback : callbacks_) {
+ callback->onFailure(
+ iface_hapd->conf->iface);
+ }
+ }
+ };
+
+ // Rgegister for new client connect/disconnect indication.
+ on_sta_authorized_internal_callback =
+ [this](struct hostapd_data* iface_hapd, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr) {
+ wpa_printf(MSG_DEBUG, "notify client " MACSTR " %s",
+ MAC2STR(mac_addr),
+ (authorized) ? "Connected" : "Disconnected");
+ for (const auto &callback : callbacks_) {
+ callback->onConnectedClientsChanged(strlen(iface_hapd->conf->bridge) > 0 ?
+ iface_hapd->conf->bridge : iface_hapd->conf->iface,
+ iface_hapd->conf->iface, mac_addr, authorized);
+ }
+ };
+
+ // Register for wpa_event which used to get channel switch event
+ on_wpa_msg_internal_callback =
+ [this](struct hostapd_data* iface_hapd, int level,
+ enum wpa_msg_type type, const char *txt,
+ size_t len) {
+ wpa_printf(MSG_DEBUG, "Receive wpa msg : %s", txt);
+ if (os_strncmp(txt, AP_EVENT_ENABLED,
+ strlen(AP_EVENT_ENABLED)) == 0 ||
+ os_strncmp(txt, WPA_EVENT_CHANNEL_SWITCH,
+ strlen(WPA_EVENT_CHANNEL_SWITCH)) == 0) {
+ for (const auto &callback : callbacks_) {
+ callback->onApInstanceInfoChanged(
+ strlen(iface_hapd->conf->bridge) > 0 ?
+ iface_hapd->conf->bridge : iface_hapd->conf->iface,
+ iface_hapd->conf->iface, iface_hapd->iface->freq,
+ getBandwidth(iface_hapd->iconf),
+ getGeneration(iface_hapd->iface->current_mode),
+ iface_hapd->own_addr);
+ }
+ }
+ };
+
+ // Setup callback
+ iface_hapd->setup_complete_cb = onAsyncSetupCompleteCb;
+ iface_hapd->setup_complete_cb_ctx = iface_hapd;
+ iface_hapd->sta_authorized_cb = onAsyncStaAuthorizedCb;
+ iface_hapd->sta_authorized_cb_ctx = iface_hapd;
+ wpa_msg_register_cb(onAsyncWpaEventCb);
+
+ if (hostapd_enable_iface(iface_hapd->iface) < 0) {
+ wpa_printf(
+ MSG_ERROR, "Enabling interface %s failed",
+ iface_params.V1_2.V1_1.V1_0.ifaceName.c_str());
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ return {V1_2::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_0::HostapdStatus Hostapd::removeAccessPointInternal(const std::string& iface_name)
+{
+ std::vector<char> remove_iface_param_vec(
+ iface_name.begin(), iface_name.end() + 1);
+ if (hostapd_remove_iface(interfaces_, remove_iface_param_vec.data()) <
+ 0) {
+ wpa_printf(
+ MSG_ERROR, "Removing interface %s failed",
+ iface_name.c_str());
+ return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ return {V1_0::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_0::HostapdStatus Hostapd::registerCallbackInternal(
+ const sp<V1_1::IHostapdCallback>& callback)
+{
+ return {V1_0::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+V1_2::HostapdStatus Hostapd::registerCallbackInternal_1_3(
+ const sp<V1_3::IHostapdCallback>& callback)
+{
+ if (!callback->linkToDeath(death_notifier_, 0)) {
+ wpa_printf(
+ MSG_ERROR,
+ "Error registering for death notification for "
+ "hostapd callback object");
+ return {V1_2::HostapdStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ callbacks_.push_back(callback);
+ return {V1_2::HostapdStatusCode::SUCCESS, ""};
+}
+
+V1_2::HostapdStatus Hostapd::forceClientDisconnectInternal(const std::string& iface_name,
+ const std::array<uint8_t, 6>& client_address, V1_2::Ieee80211ReasonCode reason_code)
+{
+ struct hostapd_data *hapd = hostapd_get_iface(interfaces_, iface_name.c_str());
+ struct sta_info *sta;
+ if (!hapd) {
+ wpa_printf(MSG_ERROR, "Interface %s doesn't exist", iface_name.c_str());
+ return {V1_2::HostapdStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+ }
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ int res;
+ res = memcmp(sta->addr, client_address.data(), ETH_ALEN);
+ if (res == 0) {
+ wpa_printf(MSG_INFO, "Force client:" MACSTR " disconnect with reason: %d",
+ MAC2STR(client_address.data()), (uint16_t) reason_code);
+ ap_sta_disconnect(hapd, sta, sta->addr, (uint16_t) reason_code);
+ return {V1_2::HostapdStatusCode::SUCCESS, ""};
+ }
+ }
+ return {V1_2::HostapdStatusCode::FAILURE_CLIENT_UNKNOWN, ""};
+}
+
+V1_2::HostapdStatus Hostapd::setDebugParamsInternal(V1_2::DebugLevel level)
+{
+ wpa_debug_level = static_cast<uint32_t>(level);
+ return {V1_2::HostapdStatusCode::SUCCESS, ""};
+}
+
+} // namespace implementation
+} // namespace V1_3
+} // namespace hostapd
+} // namespace wifi
+} // namespace hardware
+} // namespace android
diff --git a/hostapd/hidl/1.2/hostapd.h b/hostapd/hidl/1.3/hostapd.h
similarity index 61%
rename from hostapd/hidl/1.2/hostapd.h
rename to hostapd/hidl/1.3/hostapd.h
index ca6c32e..dc45932 100644
--- a/hostapd/hidl/1.2/hostapd.h
+++ b/hostapd/hidl/1.3/hostapd.h
@@ -14,23 +14,37 @@
#include <android-base/macros.h>
-#include <android/hardware/wifi/hostapd/1.2/IHostapd.h>
-#include <android/hardware/wifi/hostapd/1.1/IHostapdCallback.h>
+#include <android/hardware/wifi/hostapd/1.3/IHostapd.h>
+#include <android/hardware/wifi/hostapd/1.3/IHostapdCallback.h>
extern "C"
{
#include "utils/common.h"
+#include "utils/eloop.h"
#include "utils/includes.h"
#include "utils/wpa_debug.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
}
+class DeathNotifier : public android::hardware::hidl_death_recipient
+{
+public:
+ void serviceDied(
+ uint64_t /*cookie*/,
+ const android::wp<android::hidl::base::V1_0::IBase>
+ & /* who */) override
+ {
+ wpa_printf(MSG_ERROR, "Client died. Terminating...");
+ eloop_terminate();
+ }
+};
+
namespace android {
namespace hardware {
namespace wifi {
namespace hostapd {
-namespace V1_2 {
+namespace V1_3 {
namespace implementation {
using namespace android::hardware::wifi::hostapd::V1_0;
@@ -39,7 +53,7 @@
* object is used core for global control operations on
* hostapd.
*/
-class Hostapd : public V1_2::IHostapd
+class Hostapd : public V1_3::IHostapd
{
public:
Hostapd(hapd_interfaces* interfaces);
@@ -53,8 +67,13 @@
const V1_1::IHostapd::IfaceParams& iface_params,
const V1_0::IHostapd::NetworkParams& nw_params, addAccessPoint_cb _hidl_cb) override;
Return<void> addAccessPoint_1_2(
- const V1_2::IHostapd::IfaceParams& iface_params, const NetworkParams& nw_params,
+ const V1_2::IHostapd::IfaceParams& iface_params,
+ const V1_2::IHostapd::NetworkParams& nw_params,
addAccessPoint_1_2_cb _hidl_cb) override;
+ Return<void> addAccessPoint_1_3(
+ const V1_3::IHostapd::IfaceParams& iface_params,
+ const V1_3::IHostapd::NetworkParams& nw_params,
+ addAccessPoint_1_3_cb _hidl_cb) override;
Return<void> removeAccessPoint(
const hidl_string& iface_name,
removeAccessPoint_cb _hidl_cb) override;
@@ -62,12 +81,15 @@
Return<void> registerCallback(
const sp<V1_1::IHostapdCallback>& callback,
registerCallback_cb _hidl_cb) override;
+ Return<void> registerCallback_1_3(
+ const sp<V1_3::IHostapdCallback>& callback,
+ registerCallback_1_3_cb _hidl_cb) override;
Return<void>forceClientDisconnect(
const hidl_string& iface_name,
const hidl_array<uint8_t, 6>& client_address,
V1_2::Ieee80211ReasonCode reason_code, forceClientDisconnect_cb _hidl_cb) override;
Return<void> setDebugParams(
- DebugLevel level, setDebugParams_cb _hidl_cb) override;
+ V1_2::DebugLevel level, setDebugParams_cb _hidl_cb) override;
private:
// Corresponding worker functions for the HIDL methods.
V1_0::HostapdStatus addAccessPointInternal(
@@ -79,22 +101,39 @@
V1_2::HostapdStatus addAccessPointInternal_1_2(
const V1_2::IHostapd::IfaceParams& IfaceParams,
const V1_2::IHostapd::NetworkParams& nw_params);
+ V1_2::HostapdStatus addAccessPointInternal_1_3(
+ const V1_3::IHostapd::IfaceParams& IfaceParams,
+ const V1_3::IHostapd::NetworkParams& nw_params);
+ V1_2::HostapdStatus addSingleAccessPoint(
+ const V1_3::IHostapd::IfaceParams& IfaceParams,
+ const V1_3::IHostapd::ChannelParams& channelParams,
+ const V1_3::IHostapd::NetworkParams& nw_params,
+ std::string br_name);
+ V1_2::HostapdStatus addConcurrentAccessPoints(
+ const V1_3::IHostapd::IfaceParams& IfaceParams,
+ const V1_3::IHostapd::NetworkParams& nw_params);
V1_0::HostapdStatus removeAccessPointInternal(const std::string& iface_name);
V1_0::HostapdStatus registerCallbackInternal(
const sp<V1_1::IHostapdCallback>& callback);
+ V1_2::HostapdStatus registerCallbackInternal_1_3(
+ const sp<V1_3::IHostapdCallback>& callback);
V1_2::HostapdStatus forceClientDisconnectInternal(
const std::string& iface_name,
const std::array<uint8_t, 6>& client_address,
V1_2::Ieee80211ReasonCode reason_code);
- V1_2::HostapdStatus setDebugParamsInternal(DebugLevel level);
+ V1_2::HostapdStatus setDebugParamsInternal(V1_2::DebugLevel level);
// Raw pointer to the global structure maintained by the core.
struct hapd_interfaces* interfaces_;
// Callbacks registered.
- std::vector<sp<V1_1::IHostapdCallback>> callbacks_;
+ std::vector<sp<V1_3::IHostapdCallback>> callbacks_;
+ // Death notifier.
+ android::sp<DeathNotifier> death_notifier_;
+ // Bridge and its managed interfaces.
+ std::map<std::string, std::vector<std::string>> br_interfaces_;
DISALLOW_COPY_AND_ASSIGN(Hostapd);
};
} // namespace implementation
-} // namespace V1_2
+} // namespace V1_3
} // namespace hostapd
} // namespace wifi
} // namespace hardware
diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc
index 512ca0d..7cc45bd 100644
--- a/hostapd/hostapd.android.rc
+++ b/hostapd/hostapd.android.rc
@@ -15,6 +15,7 @@
interface android.hardware.wifi.hostapd@1.0::IHostapd default
interface android.hardware.wifi.hostapd@1.1::IHostapd default
interface android.hardware.wifi.hostapd@1.2::IHostapd default
+ interface android.hardware.wifi.hostapd@1.3::IHostapd default
class main
capabilities NET_ADMIN NET_RAW
user wifi
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 812c09a..bf89fbc 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -210,7 +210,7 @@
# Frequency list can be provided as range using hyphen ('-') or individual
# frequencies can be specified by comma (',') separated values
# Default: all frequencies allowed in selected hw_mode
-#freqlist=2437,5945,5965
+#freqlist=2437,5955,5975
#freqlist=2437,5985-6105
# Exclude DFS channels from ACS
@@ -822,11 +822,11 @@
#he_rts_threshold=0
# HE operating channel information; see matching vht_* parameters for details.
-# On the 6 GHz band the center freq calculation starts from 5.940 GHz offset.
-# For example idx=3 would result in 5955 MHz center frequency. In addition,
+# On the 6 GHz band the center freq calculation starts from 5.950 GHz offset.
+# For example idx=3 would result in 5965 MHz center frequency. In addition,
# he_oper_chwidth is ignored, and the channel width is derived from the
# configured operating class or center frequency indexes (see
-# IEEE P802.11ax/D4.3 Annex E, Table E-4).
+# IEEE P802.11ax/D6.1 Annex E, Table E-4).
#he_oper_chwidth
#he_oper_centr_freq_seg0_idx
#he_oper_centr_freq_seg1_idx
@@ -1202,7 +1202,7 @@
# should be unique across all issuing servers. In theory, this is a variable
# length field, but due to some existing implementations requiring A-ID to be
# 16 octets in length, it is strongly recommended to use that length for the
-# field to provid interoperability with deployed peer implementations. This
+# field to provide interoperability with deployed peer implementations. This
# field is configured in hex format.
#eap_fast_a_id=101112131415161718191a1b1c1d1e1f
@@ -1229,6 +1229,8 @@
# EAP-TEAP authentication type
# 0 = inner EAP (default)
# 1 = Basic-Password-Auth
+# 2 = Do not require Phase 2 authentication if client can be authenticated
+# during Phase 1
#eap_teap_auth=0
# EAP-TEAP authentication behavior when using PAC
@@ -1730,6 +1732,19 @@
# Enabling this automatically also enables ieee80211w, if not yet enabled.
# 0 = disabled (default)
# 1 = enabled
+# 2 = enabled in workaround mode - Allow STA that claims OCV capability to
+# connect even if the STA doesn't send OCI or negotiate PMF. This
+# workaround is to improve interoperability with legacy STAs which are
+# wrongly copying reserved bits of RSN capabilities from the AP's
+# RSNE into (Re)Association Request frames. When this configuration is
+# enabled, the AP considers STA is OCV capable only when the STA indicates
+# MFP capability in (Re)Association Request frames and sends OCI in
+# EAPOL-Key msg 2/4/FT Reassociation Request frame/FILS (Re)Association
+# Request frame; otherwise, the AP disables OCV for the current connection
+# with the STA. Enabling this workaround mode reduced OCV protection to
+# some extend since it allows misbehavior to go through. As such, this
+# should be enabled only if interoperability with misbehaving STAs is
+# needed.
#ocv=1
# disable_pmksa_caching: Disable PMKSA caching
@@ -1761,7 +1776,7 @@
# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In
# addition, an optional VLAN ID specification can be used to bind the station
-# to the specified VLAN whenver the specific SAE password entry is used.
+# to the specified VLAN whenever the specific SAE password entry is used.
#
# If the peer MAC address is not included or is set to the wildcard address
# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a
@@ -1776,7 +1791,8 @@
# special meaning of removing all previously added entries.
#
# sae_password uses the following encoding:
-#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>][|id=<identifier>]
+#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>]
+#[|pk=<m:ECPrivateKey-base64>][|id=<identifier>]
# Examples:
#sae_password=secret
#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
@@ -1965,7 +1981,7 @@
# Wildcard entry:
# Upon receiving a response from R0KH, it will be added to this list, so
# subsequent requests won't be broadcast. If R0KH does not reply, it will be
-# blacklisted.
+# temporarily blocked (see rkh_neg_timeout).
#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
# List of R1KHs in the same Mobility Domain
@@ -2021,7 +2037,7 @@
#ft_psk_generate_local=0
##### Neighbor table ##########################################################
-# Maximum number of entries kept in AP table (either for neigbor table or for
+# Maximum number of entries kept in AP table (either for neighbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
# removed when adding a new entry that would make the list grow over this
# limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 440664e..ad43981 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -975,7 +975,7 @@
dir = opendir(ctrl_iface_dir);
if (dir == NULL) {
printf("Control interface directory '%s' could not be "
- "openned.\n", ctrl_iface_dir);
+ "opened.\n", ctrl_iface_dir);
return;
}
@@ -1402,6 +1402,13 @@
}
+static int hostapd_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1464,6 +1471,23 @@
return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
+
+#ifdef CONFIG_DPP2
+
+static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
+}
+
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
@@ -1650,6 +1674,8 @@
"<id> = get DPP bootstrap URI" },
{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
"<id> = show DPP bootstrap information" },
+ { "dpp_bootstrap_set", hostapd_cli_cmd_dpp_bootstrap_set, NULL,
+ "<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
"peer=<id> [own=<id>] = initiate DPP bootstrapping" },
{ "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
@@ -1670,6 +1696,12 @@
"add PKEX code" },
{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
"*|<id> = remove DPP pkex information" },
+#ifdef CONFIG_DPP2
+ { "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL,
+ "own=<BI ID> iter=<count> = start DPP chirp" },
+ { "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
+ "= stop DPP chirp" },
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
"=Add/Delete/Show/Clear accept MAC ACL" },
diff --git a/hostapd/main.c b/hostapd/main.c
index 3ce8126..346df48 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -676,7 +676,10 @@
#endif /* CONFIG_ETH_P_OUI */
#ifdef CONFIG_DPP
os_memset(&dpp_conf, 0, sizeof(dpp_conf));
- /* TODO: dpp_conf.msg_ctx? */
+ dpp_conf.cb_ctx = &interfaces;
+#ifdef CONFIG_DPP2
+ dpp_conf.remove_bi = hostapd_dpp_remove_bi;
+#endif /* CONFIG_DPP2 */
interfaces.dpp = dpp_global_init(&dpp_conf);
if (!interfaces.dpp)
return -1;
@@ -916,8 +919,11 @@
!!(interfaces.iface[i]->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
hostapd_interface_deinit_free(interfaces.iface[i]);
+ interfaces.iface[i] = NULL;
}
os_free(interfaces.iface);
+ interfaces.iface = NULL;
+ interfaces.count = 0;
#ifdef CONFIG_DPP
dpp_global_deinit(interfaces.dpp);
diff --git a/hostapd/sae_pk_gen.c b/hostapd/sae_pk_gen.c
new file mode 100644
index 0000000..c31eff7
--- /dev/null
+++ b/hostapd/sae_pk_gen.c
@@ -0,0 +1,196 @@
+/*
+ * SAE-PK password/modifier generator
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "common/sae.h"
+
+
+int main(int argc, char *argv[])
+{
+ char *der = NULL;
+ size_t der_len;
+ struct crypto_ec_key *key = NULL;
+ struct wpabuf *pub = NULL;
+ u8 *data = NULL, *m;
+ size_t data_len;
+ char *b64 = NULL, *pw = NULL, *pos, *src;
+ int sec, j;
+ int ret = -1;
+ u8 hash[SAE_MAX_HASH_LEN];
+ char hash_hex[2 * SAE_MAX_HASH_LEN + 1];
+ u8 pw_base_bin[SAE_MAX_HASH_LEN];
+ u8 *dst;
+ int group;
+ size_t hash_len;
+ unsigned long long i, expected;
+ char m_hex[2 * SAE_PK_M_LEN + 1];
+ u32 sec_1b, val20;
+
+ wpa_debug_level = MSG_INFO;
+ if (os_program_init() < 0)
+ goto fail;
+
+ if (argc != 4) {
+ fprintf(stderr,
+ "usage: sae_pk_gen <DER ECPrivateKey file> <Sec:3|5> <SSID>\n");
+ goto fail;
+ }
+
+ sec = atoi(argv[2]);
+ if (sec != 3 && sec != 5) {
+ fprintf(stderr,
+ "Invalid Sec value (allowed values: 3 and 5)\n");
+ goto fail;
+ }
+ sec_1b = sec == 3;
+ expected = 1;
+ for (j = 0; j < sec; j++)
+ expected *= 256;
+
+ der = os_readfile(argv[1], &der_len);
+ if (!der) {
+ fprintf(stderr, "Could not read %s: %s\n",
+ argv[1], strerror(errno));
+ goto fail;
+ }
+
+ key = crypto_ec_key_parse_priv((u8 *) der, der_len);
+ if (!key) {
+ fprintf(stderr, "Could not parse ECPrivateKey\n");
+ goto fail;
+ }
+
+ pub = crypto_ec_key_get_subject_public_key(key);
+ if (!pub) {
+ fprintf(stderr, "Failed to build SubjectPublicKey\n");
+ goto fail;
+ }
+
+ group = crypto_ec_key_group(key);
+ switch (group) {
+ case 19:
+ hash_len = 32;
+ break;
+ case 20:
+ hash_len = 48;
+ break;
+ case 21:
+ hash_len = 64;
+ break;
+ default:
+ fprintf(stderr, "Unsupported private key group\n");
+ goto fail;
+ }
+
+ data_len = os_strlen(argv[3]) + SAE_PK_M_LEN + wpabuf_len(pub);
+ data = os_malloc(data_len);
+ if (!data) {
+ fprintf(stderr, "No memory for data buffer\n");
+ goto fail;
+ }
+ os_memcpy(data, argv[3], os_strlen(argv[3]));
+ m = data + os_strlen(argv[3]);
+ if (os_get_random(m, SAE_PK_M_LEN) < 0) {
+ fprintf(stderr, "Could not generate random Modifier M\n");
+ goto fail;
+ }
+ os_memcpy(m + SAE_PK_M_LEN, wpabuf_head(pub), wpabuf_len(pub));
+
+ fprintf(stderr, "Searching for a suitable Modifier M value\n");
+ for (i = 0;; i++) {
+ if (sae_hash(hash_len, data, data_len, hash) < 0) {
+ fprintf(stderr, "Hash failed\n");
+ goto fail;
+ }
+ if (hash[0] == 0 && hash[1] == 0) {
+ if ((hash[2] & 0xf0) == 0)
+ fprintf(stderr, "\r%3.2f%%",
+ 100.0 * (double) i / (double) expected);
+ for (j = 2; j < sec; j++) {
+ if (hash[j])
+ break;
+ }
+ if (j == sec)
+ break;
+ }
+ inc_byte_array(m, SAE_PK_M_LEN);
+ }
+
+ if (wpa_snprintf_hex(m_hex, sizeof(m_hex), m, SAE_PK_M_LEN) < 0 ||
+ wpa_snprintf_hex(hash_hex, sizeof(hash_hex), hash, hash_len) < 0)
+ goto fail;
+ fprintf(stderr, "\nFound a valid hash in %llu iterations: %s\n",
+ i + 1, hash_hex);
+
+ b64 = base64_encode(der, der_len, NULL);
+ if (!b64)
+ goto fail;
+ src = pos = b64;
+ while (*src) {
+ if (*src != '\n')
+ *pos++ = *src;
+ src++;
+ }
+ *pos = '\0';
+
+ /* Skip 8*Sec bits and add Sec_1b as the every 20th bit starting with
+ * one. */
+ os_memset(pw_base_bin, 0, sizeof(pw_base_bin));
+ dst = pw_base_bin;
+ for (j = 0; j < 8 * (int) hash_len / 20; j++) {
+ val20 = sae_pk_get_be19(hash + sec);
+ val20 |= sec_1b << 19;
+ sae_pk_buf_shift_left_19(hash + sec, hash_len - sec);
+
+ if (j & 1) {
+ *dst |= (val20 >> 16) & 0x0f;
+ dst++;
+ *dst++ = (val20 >> 8) & 0xff;
+ *dst++ = val20 & 0xff;
+ } else {
+ *dst++ = (val20 >> 12) & 0xff;
+ *dst++ = (val20 >> 4) & 0xff;
+ *dst = (val20 << 4) & 0xf0;
+ }
+ }
+ if (wpa_snprintf_hex(hash_hex, sizeof(hash_hex),
+ pw_base_bin, hash_len - sec) >= 0)
+ fprintf(stderr, "PasswordBase binary data for base32: %s",
+ hash_hex);
+
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * 3 - 5);
+ if (!pw)
+ goto fail;
+
+ printf("# SAE-PK password/M/private key for Sec=%d.\n", sec);
+ printf("sae_password=%s|pk=%s:%s\n", pw, m_hex, b64);
+ printf("# Longer passwords can be used for improved security at the cost of usability:\n");
+ for (j = 4; j <= ((int) hash_len * 8 + 5 - 8 * sec) / 19; j++) {
+ os_free(pw);
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * j - 5);
+ if (pw)
+ printf("# %s\n", pw);
+ }
+
+ ret = 0;
+fail:
+ os_free(der);
+ wpabuf_free(pub);
+ crypto_ec_key_deinit(key);
+ os_free(data);
+ os_free(b64);
+ os_free(pw);
+
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index cc2af03..4dcfe2d 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -1,28 +1,6 @@
-all: hs20-osu-client
+ALL=hs20-osu-client
-ifndef CC
-CC=gcc
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-else
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../../src/build.rules
CFLAGS += -I../../src/utils
CFLAGS += -I../../src/common
@@ -93,23 +71,11 @@
OBJS += ../../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl -lcrypto
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
hs20-osu-client: $(OBJS)
$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
@$(E) " LD " $@
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-
-clean:
- rm -f core *~ *.o *.d hs20-osu-client
- rm -f ../../src/utils/*.o
- rm -f ../../src/utils/*.d
- rm -f ../../src/common/*.o
- rm -f ../../src/common/*.d
- rm -f ../../src/crypto/*.o
- rm -f ../../src/crypto/*.d
- rm -f ../../src/wps/*.o
- rm -f ../../src/wps/*.d
-
--include $(OBJS:%.o=%.d)
+clean: common-clean
+ rm -f core *~
diff --git a/hs20/server/.gitignore b/hs20/server/.gitignore
new file mode 100644
index 0000000..fecb096
--- /dev/null
+++ b/hs20/server/.gitignore
@@ -0,0 +1 @@
+hs20_spp_server
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
index 9b73727..0cab6d6 100644
--- a/hs20/server/Makefile
+++ b/hs20/server/Makefile
@@ -1,16 +1,6 @@
-all: hs20_spp_server
+ALL=hs20_spp_server
-ifndef CC
-CC=gcc
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../../src/build.rules
CFLAGS += -I../../src
CFLAGS += -I../../src/utils
@@ -43,14 +33,10 @@
LIBS += $(shell xml2-config --libs)
OBJS += ../../src/utils/xml_libxml2.o
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
hs20_spp_server: $(OBJS)
$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
-clean:
- rm -f core *~ *.o *.d hs20_spp_server
- rm -f ../../src/utils/*.o
- rm -f ../../src/utils/*.d
- rm -f ../../src/crypto/*.o
- rm -f ../../src/crypto/*.d
-
--include $(OBJS:%.o=%.d)
+clean: common-clean
+ rm -f core *~
diff --git a/src/Makefile b/src/Makefile
index c9e84c1..6eb7f2a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -5,8 +5,8 @@
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
clean:
- for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
- rm -f *~
+ $(Q)for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
+ $(Q)rm -f *~
install:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 54e48a0..a1e9b7c 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -1,13 +1,3 @@
-all: libap.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DHOSTAPD
CFLAGS += -DNEED_AP_MLME
CFLAGS += -DCONFIG_ETH_P_OUI
@@ -67,7 +57,4 @@
wps_hostapd.o \
x_snoop.o
-libap.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5c01610..aa2ceb0 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -512,6 +512,17 @@
}
+static int is_in_freqlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ if (!iface->conf->acs_freq_list.num)
+ return 1;
+
+ return freq_range_list_includes(&iface->conf->acs_freq_list,
+ chan->freq);
+}
+
+
static void acs_survey_mode_interference_factor(
struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
{
@@ -527,6 +538,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
chan->chan, chan->freq);
@@ -651,6 +665,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
if (!chan_bw_allowed(chan, bw, 1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
@@ -1013,6 +1030,9 @@
if (!is_in_chanlist(iface, chan))
continue;
+ if (!is_in_freqlist(iface, chan))
+ continue;
+
*freq++ = chan->freq;
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 35a32a1..04535a1 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -160,6 +160,10 @@
/* Default to strict CRL checking. */
bss->check_crl_strict = 1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ bss->sae_commit_status = -1;
+#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -461,7 +465,8 @@
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
- if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf)) ||
+ if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+ !hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == 3 ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
return 0; /* PT not needed */
@@ -711,6 +716,9 @@
#ifdef CONFIG_SAE
sae_deinit_pt(tmp->pt);
#endif /* CONFIG_SAE */
+#ifdef CONFIG_SAE_PK
+ sae_deinit_pk(tmp->pk);
+#endif /* CONFIG_SAE_PK */
os_free(tmp);
}
}
@@ -1111,6 +1119,37 @@
}
+#ifdef CONFIG_SAE_PK
+static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
+{
+ struct sae_password_entry *pw;
+ bool res = false;
+
+ if (bss->ssid.wpa_passphrase &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ sae_pk_valid_password(bss->ssid.wpa_passphrase))
+ res = true;
+
+ for (pw = bss->sae_passwords; pw; pw = pw->next) {
+ if (!pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ sae_pk_valid_password(pw->password))
+ return true;
+
+ if (bss->ssid.wpa_passphrase && res && pw->pk &&
+ os_strcmp(bss->ssid.wpa_passphrase, pw->password) == 0)
+ res = false;
+ }
+
+ return res;
+}
+#endif /* CONFIG_SAE_PK */
+
+
static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
struct hostapd_config *conf,
int full_config)
@@ -1294,6 +1333,15 @@
}
#endif /* CONFIG_OCV */
+#ifdef CONFIG_SAE_PK
+ if (full_config && hostapd_sae_pk_in_use(bss) &&
+ hostapd_sae_pk_password_without_pk(bss)) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
+ return -1;
+ }
+#endif /* CONFIG_SAE_PK */
+
return 0;
}
@@ -1473,3 +1521,38 @@
return 2;
return with_id;
}
+
+
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_SAE_PK
+ struct sae_password_entry *pw;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->pk)
+ return true;
+ }
+#endif /* CONFIG_SAE_PK */
+
+ return false;
+}
+
+
+#ifdef CONFIG_SAE_PK
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
+{
+ bool with_pk = false;
+ struct sae_password_entry *pw;
+
+ if (conf->ssid.wpa_passphrase)
+ return false;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (!pw->pk)
+ return false;
+ with_pk = true;
+ }
+
+ return with_pk;
+}
+#endif /* CONFIG_SAE_PK */
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index cffa636..bada04c 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -197,15 +197,6 @@
#define NUM_TX_QUEUES 4
-
-struct hostapd_tx_queue_params {
- int aifs;
- int cwmin;
- int cwmax;
- int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
-};
-
-
#define MAX_ROAMING_CONSORTIUM_LEN 15
struct hostapd_roaming_consortium {
@@ -261,6 +252,7 @@
u8 peer_addr[ETH_ALEN];
int vlan_id;
struct sae_pt *pt;
+ struct sae_pk *pk;
};
struct dpp_controller_conf {
@@ -677,6 +669,9 @@
u8 bss_load_test_set;
struct wpabuf *own_ie_override;
int sae_reflection_attack;
+ int sae_commit_status;
+ int sae_pk_omit;
+ int sae_pk_password_check_skip;
struct wpabuf *sae_commit_override;
struct wpabuf *rsne_override_eapol;
struct wpabuf *rsnxe_override_eapol;
@@ -687,6 +682,13 @@
int no_beacon_rsnxe;
int skip_prune_assoc;
int ft_rsnxe_used;
+ unsigned int oci_freq_override_eapol_m3;
+ unsigned int oci_freq_override_eapol_g1;
+ unsigned int oci_freq_override_saquery_req;
+ unsigned int oci_freq_override_saquery_resp;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
+ unsigned int oci_freq_override_wnm_sleep;
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -1140,6 +1142,8 @@
void hostapd_set_security_params(struct hostapd_bss_config *bss,
int full_config);
int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
+bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 1f284f0..f157659 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -418,6 +418,7 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set)
{
@@ -439,6 +440,7 @@
params.vht_capabilities = vht_capab;
params.he_capab = he_capab;
params.he_capab_len = he_capab_len;
+ params.he_6ghz_capab = he_6ghz_capab;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
@@ -650,6 +652,12 @@
}
+bool hostapd_drv_nl80211(struct hostapd_data *hapd)
+{
+ return hapd->driver && os_strcmp(hapd->driver->name, "nl80211") == 0;
+}
+
+
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params)
{
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 56d1ad8..5738c1c 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -43,6 +43,7 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
@@ -80,6 +81,7 @@
u16 *flags, u8 *dfs_domain);
int hostapd_driver_commit(struct hostapd_data *hapd);
int hostapd_drv_none(struct hostapd_data *hapd);
+bool hostapd_drv_nl80211(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params);
struct wpa_scan_results * hostapd_driver_get_scan_results(
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 47ced9a..b3b33b7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -463,6 +463,9 @@
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
@@ -557,10 +560,13 @@
pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
pos = hostapd_eid_vht_operation(hapd, pos);
pos = hostapd_eid_txpower_envelope(hapd, pos);
- pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
}
#endif /* CONFIG_IEEE80211AC */
+ if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
+ hapd->iconf->ieee80211ax)
+ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
@@ -570,6 +576,7 @@
pos = hostapd_eid_he_operation(hapd, pos);
pos = hostapd_eid_spatial_reuse(hapd, pos);
pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
+ pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1161,6 +1168,9 @@
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1274,10 +1284,13 @@
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
tailpos = hostapd_eid_vht_operation(hapd, tailpos);
tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
- tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AC */
+ if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
+ hapd->iconf->ieee80211ax)
+ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
@@ -1288,6 +1301,7 @@
tailpos = hostapd_eid_he_operation(hapd, tailpos);
tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
+ tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 3c078b9..f04a00a 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -955,10 +955,13 @@
if (*skip_radar) {
*skip_radar = 0;
} else {
- if (iface->conf->vht_oper_chwidth == CHANWIDTH_USE_HT)
+ int oper_chwidth;
+
+ oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ if (oper_chwidth == CHANWIDTH_USE_HT)
break;
*skip_radar = 1;
- iface->conf->vht_oper_chwidth--;
+ hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
}
}
@@ -1028,7 +1031,7 @@
unsigned int i;
int err = 1;
struct hostapd_hw_modes *cmode = iface->current_mode;
- u8 current_vht_oper_chwidth = iface->conf->vht_oper_chwidth;
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
__func__, iface->cac_started ? "yes" : "no",
@@ -1089,8 +1092,8 @@
"freq=%d chan=%d sec_chan=%d", channel->freq,
channel->chan, secondary_channel);
- new_vht_oper_chwidth = iface->conf->vht_oper_chwidth;
- iface->conf->vht_oper_chwidth = current_vht_oper_chwidth;
+ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
/* Setup CSA request */
os_memset(&csa_settings, 0, sizeof(csa_settings));
@@ -1130,7 +1133,7 @@
iface->freq = channel->freq;
iface->conf->channel = channel->chan;
iface->conf->secondary_channel = secondary_channel;
- iface->conf->vht_oper_chwidth = new_vht_oper_chwidth;
+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
oper_centr_freq_seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index c86f01b..6772a87 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -26,6 +26,13 @@
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
+#ifdef CONFIG_DPP2
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+ struct dpp_authentication *auth,
+ struct dpp_config_obj *conf);
+#endif /* CONFIG_DPP2 */
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -59,6 +66,10 @@
wpabuf_len(hapd->dpp_auth->resp_msg));
}
+#ifdef CONFIG_DPP2
+ dpp_controller_new_qr_code(hapd->iface->interfaces->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
return bi->id;
}
@@ -237,6 +248,10 @@
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
@@ -478,12 +493,35 @@
}
+#ifdef CONFIG_DPP2
+static int hostapd_dpp_process_conf_obj(void *ctx,
+ struct dpp_authentication *auth)
+{
+ struct hostapd_data *hapd = ctx;
+ unsigned int i;
+
+ for (i = 0; i < auth->num_conf_obj; i++)
+ hostapd_dpp_handle_config_obj(hapd, auth,
+ &auth->conf_obj[i]);
+
+ return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+ struct dpp_authentication *auth;
u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
unsigned int neg_freq = 0;
+ int tcp = 0;
+#ifdef CONFIG_DPP2
+ int tcp_port = DPP_TCP_PORT;
+ struct hostapd_ip_addr ipaddr;
+ char *addr;
+#endif /* CONFIG_DPP2 */
pos = os_strstr(cmd, " peer=");
if (!pos)
@@ -496,6 +534,25 @@
return -1;
}
+#ifdef CONFIG_DPP2
+ pos = os_strstr(cmd, " tcp_port=");
+ if (pos) {
+ pos += 10;
+ tcp_port = atoi(pos);
+ }
+
+ addr = get_param(cmd, " tcp_addr=");
+ if (addr) {
+ int res;
+
+ res = hostapd_parse_ip_addr(addr, &ipaddr);
+ os_free(addr);
+ if (res)
+ return -1;
+ tcp = 1;
+ }
+#endif /* CONFIG_DPP2 */
+
pos = os_strstr(cmd, " own=");
if (pos) {
pos += 5;
@@ -533,36 +590,46 @@
if (pos)
neg_freq = atoi(pos + 10);
- if (hapd->dpp_auth) {
+ if (!tcp && hapd->dpp_auth) {
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
}
- hapd->dpp_auth = dpp_auth_init(hapd->iface->interfaces->dpp,
- hapd->msg_ctx, peer_bi, own_bi,
- allowed_roles, neg_freq,
- hapd->iface->hw_features,
- hapd->iface->num_hw_features);
- if (!hapd->dpp_auth)
+ auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ peer_bi, own_bi, allowed_roles, neg_freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ if (!auth)
goto fail;
- hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- if (dpp_set_configurator(hapd->dpp_auth, cmd) < 0) {
- dpp_auth_deinit(hapd->dpp_auth);
- hapd->dpp_auth = NULL;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, cmd) < 0) {
+ dpp_auth_deinit(auth);
goto fail;
}
- hapd->dpp_auth->neg_freq = neg_freq;
+ auth->neg_freq = neg_freq;
if (!is_zero_ether_addr(peer_bi->mac_addr))
- os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
- ETH_ALEN);
+ os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
+#ifdef CONFIG_DPP2
+ if (tcp)
+ return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
+ &ipaddr, tcp_port, hapd->conf->dpp_name,
+ DPP_NETROLE_AP, hapd->msg_ctx, hapd,
+ hostapd_dpp_process_conf_obj);
+#endif /* CONFIG_DPP2 */
+
+ hapd->dpp_auth = auth;
return hostapd_dpp_auth_init_next(hapd);
fail:
return -1;
@@ -618,6 +685,10 @@
wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
MAC2STR(src));
+#ifdef CONFIG_DPP2
+ hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
+
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
@@ -1157,6 +1228,9 @@
r_bootstrap, r_bootstrap_len);
peer_bi = dpp_bootstrap_find_chirp(hapd->iface->interfaces->dpp,
r_bootstrap);
+ dpp_notify_chirp_received(hapd->msg_ctx,
+ peer_bi ? (int) peer_bi->id : -1,
+ src, freq, r_bootstrap);
if (!peer_bi) {
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, NULL,
@@ -1178,8 +1252,8 @@
0);
if (!auth)
return;
- hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- if (dpp_set_configurator(hapd->dpp_auth,
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth,
hapd->dpp_configurator_params) < 0) {
dpp_auth_deinit(auth);
return;
@@ -1197,6 +1271,160 @@
}
}
+
+static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+ u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+ struct dpp_configurator *conf;
+ struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
+ u16 group;
+
+ if (hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+ MAC2STR(src));
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
+ csign_hash);
+ if (!conf) {
+ if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
+ src, hdr, buf, len, freq, NULL,
+ NULL) == 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return;
+ }
+
+ fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &fcgroup_len);
+ if (!fcgroup || fcgroup_len != 2) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Finite Cyclic Group attribute");
+ return;
+ }
+ group = WPA_GET_LE16(fcgroup);
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+ a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+ e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+ auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ conf, freq, group, a_nonce, a_nonce_len,
+ e_id, e_id_len);
+ if (!auth)
+ return;
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return;
+ }
+
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ hapd->dpp_auth = auth;
+
+ hapd->dpp_in_response_listen = 0;
+ hapd->dpp_auth_ok_on_ack = 0;
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+ wait_time -= 10;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+ if (hostapd_drv_send_action(hapd, freq, wait_time, src,
+ wpabuf_head(auth->reconfig_req_msg),
+ wpabuf_len(auth->reconfig_req_msg)) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ }
+}
+
+
+static void
+hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *conf;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+ MACSTR, MAC2STR(src));
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return;
+
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+ if (hostapd_drv_send_action(hapd, freq, 500, src,
+ wpabuf_head(conf), wpabuf_len(conf)) < 0) {
+ wpabuf_free(conf);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+ wpabuf_free(conf);
+}
+
#endif /* CONFIG_DPP2 */
@@ -1206,9 +1434,13 @@
enum dpp_status_error status)
{
struct wpabuf *msg;
+ size_t len;
- msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
- 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+ len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+#ifdef CONFIG_DPP2
+ len += 5;
+#endif /* CONFIG_DPP2 */
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
if (!msg)
return;
@@ -1281,6 +1513,15 @@
skip_connector:
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1) {
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
" status=%d", MAC2STR(src), status);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
@@ -1650,6 +1891,14 @@
hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
freq);
break;
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
+ freq);
+ break;
#endif /* CONFIG_DPP2 */
default:
wpa_printf(MSG_DEBUG,
@@ -1679,7 +1928,7 @@
struct wpabuf *resp;
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
- if (!auth || !auth->auth_success ||
+ if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
@@ -1715,6 +1964,8 @@
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
if (ok && auth->peer_version >= 2 &&
auth->conf_resp_status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
@@ -1959,10 +2210,13 @@
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
+ hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
NULL);
eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
NULL);
+ hostapd_dpp_chirp_stop(hapd);
#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
@@ -1971,3 +2225,359 @@
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
}
+
+
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_controller_config config;
+ const char *pos;
+
+ os_memset(&config, 0, sizeof(config));
+ config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+ config.netrole = DPP_NETROLE_AP;
+ config.msg_ctx = hapd->msg_ctx;
+ config.cb_ctx = hapd;
+ config.process_conf_obj = hostapd_dpp_process_conf_obj;
+ if (cmd) {
+ pos = os_strstr(cmd, " tcp_port=");
+ if (pos) {
+ pos += 10;
+ config.tcp_port = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ config.allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ return -1;
+ }
+
+ config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+ }
+ config.configurator_params = hapd->dpp_configurator_params;
+ return dpp_controller_start(hapd->iface->interfaces->dpp, &config);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
+
+static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_chirp_next(hapd, NULL);
+}
+
+
+static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
+{
+ struct wpabuf *msg;
+ int type;
+
+ msg = hapd->dpp_presence_announcement;
+ type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+ wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
+ if (hostapd_drv_send_action(
+ hapd, hapd->dpp_chirp_freq, 2000, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
+ eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+}
+
+
+static struct hostapd_hw_modes *
+dpp_get_mode(struct hostapd_data *hapd,
+ enum hostapd_hw_mode mode)
+{
+ struct hostapd_hw_modes *modes = hapd->iface->hw_features;
+ u16 num_modes = hapd->iface->num_hw_features;
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode != mode ||
+ !modes[i].num_channels || !modes[i].channels)
+ continue;
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static void
+hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct wpa_scan_results *scan_res;
+ struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
+ unsigned int i;
+ struct hostapd_hw_modes *mode;
+ int c;
+
+ if (!bi)
+ return;
+
+ hapd->dpp_chirp_scan_done = 1;
+
+ scan_res = hostapd_driver_get_scan_results(hapd);
+
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+
+ /* Channels from own bootstrapping info */
+ if (bi) {
+ for (i = 0; i < bi->num_freq; i++)
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bi->freq[i]);
+ }
+
+ /* Preferred chirping channels */
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
+ if (mode) {
+ int chan44 = 0, chan149 = 0;
+
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (chan->freq == 5220)
+ chan44 = 1;
+ if (chan->freq == 5745)
+ chan149 = 1;
+ }
+ if (chan149)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
+ else if (chan44)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
+ }
+
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR)) ||
+ chan->freq != 60480)
+ continue;
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
+ break;
+ }
+ }
+
+ /* Add channels from scan results for APs that advertise Configurator
+ * Connectivity element */
+ for (i = 0; scan_res && i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ size_t ie_len = bss->ie_len;
+
+ if (!ie_len)
+ ie_len = bss->beacon_ie_len;
+ if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
+ DPP_CC_IE_VENDOR_TYPE))
+ int_array_add_unique(&hapd->dpp_chirp_freqs,
+ bss->freq);
+ }
+
+ if (!hapd->dpp_chirp_freqs ||
+ eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
+ hapd, NULL) < 0)
+ hostapd_dpp_chirp_stop(hapd);
+
+ wpa_scan_results_free(scan_res);
+}
+
+
+static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ int i;
+
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+
+ if (hapd->dpp_chirp_freq == 0) {
+ if (hapd->dpp_chirp_round % 4 == 0 &&
+ !hapd->dpp_chirp_scan_done) {
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Update channel list for chirping");
+ os_memset(¶ms, 0, sizeof(params));
+ ret = hostapd_driver_scan(hapd, ¶ms);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to request a scan ret=%d (%s)",
+ ret, strerror(-ret));
+ hostapd_dpp_chirp_scan_res_handler(hapd->iface);
+ } else {
+ hapd->iface->scan_cb =
+ hostapd_dpp_chirp_scan_res_handler;
+ }
+ return;
+ }
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
+ hapd->dpp_chirp_round++;
+ wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
+ hapd->dpp_chirp_round);
+ } else {
+ for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
+ if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
+ break;
+ if (!hapd->dpp_chirp_freqs[i]) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Previous chirp freq %d not found",
+ hapd->dpp_chirp_freq);
+ return;
+ }
+ i++;
+ if (hapd->dpp_chirp_freqs[i]) {
+ hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
+ } else {
+ hapd->dpp_chirp_iter--;
+ if (hapd->dpp_chirp_iter <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Chirping iterations completed");
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ hapd->dpp_chirp_freq = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ if (eloop_register_timeout(30, 0,
+ hostapd_dpp_chirp_next,
+ hapd, NULL) < 0) {
+ hostapd_dpp_chirp_stop(hapd);
+ return;
+ }
+ if (hapd->dpp_chirp_listen) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Listen on %d MHz during chirp 30 second wait",
+ hapd->dpp_chirp_listen);
+ /* TODO: start listen on the channel */
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Wait 30 seconds before starting the next chirping round");
+ }
+ return;
+ }
+ }
+
+ hostapd_dpp_chirp_start(hapd);
+}
+
+
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ int iter = 1, listen_freq = 0;
+ struct dpp_bootstrap_info *bi;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
+ if (!bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not found");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " iter=");
+ if (pos) {
+ iter = atoi(pos + 6);
+ if (iter <= 0)
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " listen=");
+ if (pos) {
+ listen_freq = atoi(pos + 8);
+ if (listen_freq <= 0)
+ return -1;
+ }
+
+ hostapd_dpp_chirp_stop(hapd);
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = 0;
+ hapd->dpp_chirp_bi = bi;
+ hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
+ if (!hapd->dpp_presence_announcement)
+ return -1;
+ hapd->dpp_chirp_iter = iter;
+ hapd->dpp_chirp_round = 0;
+ hapd->dpp_chirp_scan_done = 0;
+ hapd->dpp_chirp_listen = listen_freq;
+
+ return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
+}
+
+
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
+{
+ if (hapd->dpp_presence_announcement) {
+ hostapd_drv_send_action_cancel_wait(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
+ }
+ hapd->dpp_chirp_bi = NULL;
+ wpabuf_free(hapd->dpp_presence_announcement);
+ hapd->dpp_presence_announcement = NULL;
+ if (hapd->dpp_chirp_listen)
+ hostapd_dpp_listen_stop(hapd);
+ hapd->dpp_chirp_listen = 0;
+ hapd->dpp_chirp_freq = 0;
+ os_free(hapd->dpp_chirp_freqs);
+ hapd->dpp_chirp_freqs = NULL;
+ eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
+ if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
+ /* TODO: abort ongoing scan */
+ hapd->iface->scan_cb = NULL;
+ }
+}
+
+
+static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
+{
+ struct dpp_bootstrap_info *bi = ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (bi == hapd->dpp_chirp_bi)
+ hostapd_dpp_chirp_stop(hapd);
+ }
+
+ return 0;
+}
+
+
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
+{
+ struct hapd_interfaces *interfaces = ctx;
+
+ hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index b1fa99e..264d3e4 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -10,6 +10,8 @@
#ifndef DPP_HOSTAPD_H
#define DPP_HOSTAPD_H
+struct dpp_bootstrap_info;
+
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd);
@@ -39,4 +41,9 @@
void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
+int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_chirp_stop(struct hostapd_data *hapd);
+void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi);
+
#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 524a151..9af5445 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -105,6 +105,32 @@
#endif /* CONFIG_FILS */
+static bool check_sa_query_need(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if ((sta->flags &
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+ return false;
+
+ if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+
+ if (!sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA Query timeout
+ * has not been reached. Reject the association attempt
+ * temporarily and start SA Query, if one is not pending.
+ */
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+ return true;
+ }
+
+ return false;
+}
+
+
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *req_ies, size_t req_ies_len, int reassoc)
{
@@ -293,6 +319,17 @@
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
struct wpabuf *wps;
+ if (check_sa_query_need(hapd, sta)) {
+ status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+ p = hostapd_eid_assoc_comeback_time(hapd, sta,
+ p);
+
+ hostapd_sta_assoc(hapd, addr, reassoc, status,
+ buf, p - buf);
+ return 0;
+ }
+
sta->flags |= WLAN_STA_WPS;
wps = ieee802_11_vendor_ie_concat(ie, ielen,
WPS_IE_VENDOR_TYPE);
@@ -388,25 +425,7 @@
goto fail;
}
- if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
- (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
- !sta->sa_query_timed_out &&
- sta->sa_query_count > 0)
- ap_check_sa_query_timeout(hapd, sta);
- if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
- (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
- !sta->sa_query_timed_out &&
- (sta->auth_alg != WLAN_AUTH_FT)) {
- /*
- * STA has already been associated with MFP and SA
- * Query timeout has not been reached. Reject the
- * association attempt temporarily and start SA Query,
- * if one is not pending.
- */
-
- if (sta->sa_query_count == 0)
- ap_sta_start_sa_query(hapd, sta);
-
+ if (check_sa_query_need(hapd, sta)) {
status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
@@ -439,7 +458,7 @@
#ifdef CONFIG_SAE
if (hapd->conf->sae_pwe == 2 &&
sta->auth_alg == WLAN_AUTH_SAE &&
- sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+ sta->sae && !sta->sae->h2e &&
elems.rsnxe && elems.rsnxe_len >= 1 &&
(elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
wpa_printf(MSG_INFO, "SAE: " MACSTR
@@ -844,8 +863,6 @@
int offset, int width, int cf1, int cf2,
int finished)
{
- /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
-
#ifdef NEED_AP_MLME
int channel, chwidth, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
@@ -895,9 +912,18 @@
switch (hapd->iface->current_mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
- if (cf1 > 5000)
+ if (cf1 == 5935)
+ seg0_idx = (cf1 - 5925) / 5;
+ else if (cf1 > 5950)
+ seg0_idx = (cf1 - 5950) / 5;
+ else if (cf1 > 5000)
seg0_idx = (cf1 - 5000) / 5;
- if (cf2 > 5000)
+
+ if (cf2 == 5935)
+ seg1_idx = (cf2 - 5925) / 5;
+ else if (cf2 > 5950)
+ seg1_idx = (cf2 - 5950) / 5;
+ else if (cf2 > 5000)
seg1_idx = (cf2 - 5000) / 5;
break;
default:
@@ -958,6 +984,29 @@
for (i = 0; i < hapd->iface->num_bss; i++)
hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
+
+#ifdef CONFIG_OCV
+ if (hapd->conf->ocv) {
+ struct sta_info *sta;
+ bool check_sa_query = false;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ !(sta->flags & WLAN_STA_WNM_SLEEP_MODE)) {
+ sta->post_csa_sa_query = 1;
+ check_sa_query = true;
+ }
+ }
+
+ if (check_sa_query) {
+ wpa_printf(MSG_DEBUG,
+ "OCV: Check post-CSA SA Query initiation in 15 seconds");
+ eloop_register_timeout(15, 0,
+ hostapd_ocv_check_csa_sa_query,
+ hapd, NULL);
+ }
+ }
+#endif /* CONFIG_OCV */
#endif /* NEED_AP_MLME */
}
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 9567e20..90f1577 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1555,11 +1555,14 @@
di->prot = prot;
di->sd_resp = buf;
di->sd_resp_pos = 0;
+ di->dpp = 1;
tx_buf = gas_build_initial_resp(
dialog_token, WLAN_STATUS_SUCCESS,
- comeback_delay, 10);
- if (tx_buf)
+ comeback_delay, 10 + 2);
+ if (tx_buf) {
gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, 0);
+ }
}
} else {
wpa_printf(MSG_DEBUG,
@@ -1782,9 +1785,10 @@
tx_buf = gas_build_comeback_resp(dialog_token,
WLAN_STATUS_SUCCESS,
dialog->sd_frag_id, more, 0,
- 10 + frag_len);
+ 10 + 2 + frag_len);
if (tx_buf) {
gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, frag_len);
wpabuf_put_buf(tx_buf, buf);
}
} else
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5515ab3..b37f49f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -76,6 +76,8 @@
int ret;
for (i = 0; i < interfaces->count; i++) {
+ if (!interfaces->iface[i])
+ continue;
ret = cb(interfaces->iface[i], ctx);
if (ret)
return ret;
@@ -437,6 +439,10 @@
hostapd_clean_rrm(hapd);
fils_hlp_deinit(hapd);
+#ifdef CONFIG_OCV
+ eloop_cancel_timeout(hostapd_ocv_check_csa_sa_query, hapd, NULL);
+#endif /* CONFIG_OCV */
+
#ifdef CONFIG_SAE
{
struct hostapd_sae_commit_queue *q;
@@ -1175,12 +1181,12 @@
#endif /* CONFIG_MESH */
if (flush_old_stations)
- hostapd_flush_old_stations(hapd,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_flush(hapd);
hostapd_set_privacy(hapd, 0);
#ifdef CONFIG_WEP
- hostapd_broadcast_wep_clear(hapd);
+ if (!hostapd_drv_nl80211(hapd))
+ hostapd_broadcast_wep_clear(hapd);
if (hostapd_setup_encryption(conf->iface, hapd))
return -1;
#endif /* CONFIG_WEP */
@@ -1369,6 +1375,21 @@
if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
return -1;
+ if (flush_old_stations && !conf->start_disabled &&
+ conf->broadcast_deauth) {
+ u8 addr[ETH_ALEN];
+
+ /* Should any previously associated STA not have noticed that
+ * the AP had stopped and restarted, send one more
+ * deauthentication notification now that the AP is ready to
+ * operate. */
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations at BSS start");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
@@ -1894,6 +1915,13 @@
if (!bss->conf->owe_transition_ifname[0])
continue;
+ if (bss->iface->state != HAPD_IFACE_ENABLED) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Interface %s state %s - defer beacon update",
+ bss->conf->iface,
+ hostapd_state_text(bss->iface->state));
+ continue;
+ }
res = hostapd_owe_trans_get_info(bss);
if (res == 0)
continue;
@@ -3134,6 +3162,7 @@
hostapd_prune_associations(hapd, sta->addr);
ap_sta_clear_disconnect_timeouts(hapd, sta);
+ sta->post_csa_sa_query = 0;
#ifdef CONFIG_P2P
if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
@@ -3645,3 +3674,25 @@
#endif /* CONFIG_NO_RADIUS */
}
}
+
+
+#ifdef CONFIG_OCV
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG, "OCV: Post-CSA SA Query initiation check");
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->post_csa_sa_query)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "OCV: OCVC STA " MACSTR
+ " did not start SA Query after CSA - disconnect",
+ MAC2STR(sta->addr));
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ }
+}
+#endif /* CONFIG_OCV */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index c8f691e..9d3c85d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -349,6 +349,11 @@
int last_igtk_key_idx;
u8 last_igtk[WPA_IGTK_MAX_LEN];
size_t last_igtk_len;
+
+ enum wpa_alg last_bigtk_alg;
+ int last_bigtk_key_idx;
+ u8 last_bigtk[WPA_BIGTK_MAX_LEN];
+ size_t last_bigtk_len;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
@@ -386,6 +391,16 @@
unsigned int dpp_resp_wait_time;
unsigned int dpp_resp_max_tries;
unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_presence_announcement;
+ struct dpp_bootstrap_info *dpp_chirp_bi;
+ int dpp_chirp_freq;
+ int *dpp_chirp_freqs;
+ int dpp_chirp_iter;
+ int dpp_chirp_round;
+ int dpp_chirp_scan_done;
+ int dpp_chirp_listen;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;
@@ -618,6 +633,7 @@
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
void hostapd_periodic_iface(struct hostapd_iface *iface);
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
+void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index e6aa83d..a872893 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -24,6 +24,7 @@
#include "common/dpp.h"
#include "common/ocv.h"
#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -385,7 +386,8 @@
auth_alg == WLAN_AUTH_SAE) {
if (auth_transaction == 1 && sta &&
(resp == WLAN_STATUS_SUCCESS ||
- resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT)) {
+ resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ resp == WLAN_STATUS_SAE_PK)) {
wpa_printf(MSG_DEBUG,
"TESTING: Postpone SAE Commit transmission until Confirm is ready");
os_free(sta->sae_postponed_commit);
@@ -477,17 +479,23 @@
const char *rx_id = NULL;
int use_pt = 0;
struct sae_pt *pt = NULL;
+ const struct sae_pk *pk = NULL;
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
- use_pt = sta->sae->tmp->h2e;
+ use_pt = sta->sae->h2e;
+#ifdef CONFIG_SAE_PK
+ os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
+ os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
+#endif /* CONFIG_SAE_PK */
}
if (rx_id && hapd->conf->sae_pwe != 3)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
- else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK)
use_pt = 1;
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
@@ -501,6 +509,8 @@
continue;
password = pw->password;
pt = pw->pt;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ pk = pw->pk;
break;
}
if (!password) {
@@ -514,7 +524,7 @@
if (update && use_pt &&
sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
- NULL) < 0)
+ NULL, pk) < 0)
return NULL;
if (update && !use_pt &&
@@ -557,7 +567,17 @@
if (buf == NULL)
return NULL;
- sae_write_confirm(sta->sae, buf);
+#ifdef CONFIG_SAE_PK
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sta->sae->tmp)
+ sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
+
+ if (sae_write_confirm(sta->sae, buf) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
return buf;
}
@@ -577,8 +597,21 @@
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- status = (sta->sae->tmp && sta->sae->tmp->h2e) ?
- WLAN_STATUS_SAE_HASH_TO_ELEMENT : WLAN_STATUS_SUCCESS;
+ if (sta->sae->tmp && sta->sae->pk)
+ status = WLAN_STATUS_SAE_PK;
+ else if (sta->sae->tmp && sta->sae->h2e)
+ status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+ else
+ status = WLAN_STATUS_SUCCESS;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->sae_commit_status >= 0 &&
+ hapd->conf->sae_commit_status != status) {
+ wpa_printf(MSG_INFO,
+ "TESTING: Override SAE commit status code %u --> %d",
+ status, hapd->conf->sae_commit_status);
+ status = hapd->conf->sae_commit_status;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
@@ -899,9 +932,14 @@
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
- if (sta->sae->tmp)
- sta->sae->tmp->h2e = status_code ==
- WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+ if (sta->sae->tmp) {
+ sta->sae->h2e =
+ (status_code ==
+ WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK);
+ sta->sae->pk =
+ status_code == WLAN_STATUS_SAE_PK;
+ }
ret = auth_sae_send_commit(hapd, sta, bssid,
!allow_reuse, status_code);
if (ret)
@@ -1117,14 +1155,20 @@
sae_pwe = 1;
else if (id_in_use == 1 && sae_pwe == 0)
sae_pwe = 2;
+#ifdef CONFIG_SAE_PK
+ if (sae_pwe == 0 && hostapd_sae_pk_in_use(hapd->conf))
+ sae_pwe = 2;
+#endif /* CONFIG_SAE_PK */
return ((sae_pwe == 0 || sae_pwe == 3) &&
status_code == WLAN_STATUS_SUCCESS) ||
(sae_pwe == 1 &&
- status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) ||
+ (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK)) ||
(sae_pwe == 2 &&
(status_code == WLAN_STATUS_SUCCESS ||
- status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
+ status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK));
}
@@ -1147,11 +1191,15 @@
static int check_sae_rejected_groups(struct hostapd_data *hapd,
- const struct wpabuf *groups)
+ struct sae_data *sae)
{
+ const struct wpabuf *groups;
size_t i, count;
const u8 *pos;
+ if (!sae->tmp)
+ return 0;
+ groups = sae->tmp->peer_rejected_groups;
if (!groups)
return 0;
@@ -1193,6 +1241,7 @@
wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
+ resp = status_code;
send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
@@ -1338,7 +1387,8 @@
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
&token_len, groups, status_code ==
- WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+ WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1368,9 +1418,7 @@
if (resp != WLAN_STATUS_SUCCESS)
goto reply;
- if (sta->sae->tmp &&
- check_sae_rejected_groups(
- hapd, sta->sae->tmp->peer_rejected_groups)) {
+ if (check_sae_rejected_groups(hapd, sta->sae)) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
@@ -1382,8 +1430,9 @@
"SAE: Request anti-clogging token from "
MACSTR, MAC2STR(sta->addr));
if (sta->sae->tmp)
- h2e = sta->sae->tmp->h2e;
- if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ h2e = sta->sae->h2e;
+ if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK)
h2e = 1;
data = auth_build_token_req(hapd, sta->sae->group,
sta->addr, h2e);
@@ -3104,6 +3153,34 @@
#endif /* CONFIG_OWE */
+static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
+ int reassoc)
+{
+ if ((sta->flags &
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
+ (WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
+ return false;
+
+ if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+
+ if (!sta->sa_query_timed_out &&
+ (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA Query timeout
+ * has not been reached. Reject the association attempt
+ * temporarily and start SA Query, if one is not pending.
+ */
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+ return true;
+ }
+
+ return false;
+}
+
+
static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
@@ -3181,6 +3258,19 @@
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ if (!(sta->flags & WLAN_STA_HE)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station does not support mandatory HE PHY - reject association");
+ return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+ }
+ resp = copy_sta_he_6ghz_capab(hapd, sta,
+ elems.he_6ghz_band_cap);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
}
#endif /* CONFIG_IEEE80211AX */
@@ -3214,6 +3304,8 @@
if (hapd->conf->wps_state && elems.wps_ie) {
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
"Request - assume WPS is used");
+ if (check_sa_query(hapd, sta, reassoc))
+ return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
sta->flags |= WLAN_STA_WPS;
wpabuf_free(sta->wps_ie);
sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
@@ -3267,27 +3359,9 @@
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
- if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
- (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
- !sta->sa_query_timed_out &&
- sta->sa_query_count > 0)
- ap_check_sa_query_timeout(hapd, sta);
- if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
- (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
- !sta->sa_query_timed_out &&
- (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
- /*
- * STA has already been associated with MFP and SA
- * Query timeout has not been reached. Reject the
- * association attempt temporarily and start SA Query,
- * if one is not pending.
- */
- if (sta->sa_query_count == 0)
- ap_sta_start_sa_query(hapd, sta);
-
+ if (check_sa_query(hapd, sta, reassoc))
return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
- }
if (wpa_auth_uses_mfp(sta->wpa_sm))
sta->flags |= WLAN_STA_MFP;
@@ -3340,7 +3414,7 @@
if (hapd->conf->sae_pwe == 2 &&
sta->auth_alg == WLAN_AUTH_SAE &&
- sta->sae && sta->sae->tmp && !sta->sae->tmp->h2e &&
+ sta->sae && !sta->sae->h2e &&
elems.rsnxe && elems.rsnxe_len >= 1 &&
(elems.rsnxe[0] & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
wpa_printf(MSG_INFO, "SAE: " MACSTR
@@ -3365,7 +3439,8 @@
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ if (DPP_VERSION > 1 &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
elems.owe_dh) {
@@ -3487,6 +3562,7 @@
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
+ enum oci_verify_result res;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
@@ -3500,9 +3576,20 @@
&tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
- tx_chanwidth, tx_seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+ res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
+ res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_printf(MSG_INFO,
+ "FILS: Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sta->wpa_sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
+ ocv_errorstr);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+ MACSTR " frame=fils-reassoc-req error=%s",
+ MAC2STR(sta->addr), ocv_errorstr);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
}
@@ -3626,6 +3713,7 @@
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
sta->flags & WLAN_STA_HE ? &he_cap : NULL,
sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+ sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set)) {
@@ -3785,6 +3873,7 @@
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_he_6ghz_band_cap(hapd, p);
}
#endif /* CONFIG_IEEE80211AX */
@@ -3822,7 +3911,8 @@
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
- wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ !wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
struct wpabuf *pub;
pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
@@ -3843,7 +3933,7 @@
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
@@ -4076,7 +4166,7 @@
{
u16 capab_info, listen_interval, seq_ctrl, fc;
int resp = WLAN_STATUS_SUCCESS;
- u16 reply_res;
+ u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
const u8 *pos;
int left, i;
struct sta_info *sta;
@@ -4456,7 +4546,7 @@
os_free(tmp);
/*
- * Remove the station in case tranmission of a success response fails
+ * Remove the station in case transmission of a success response fails
* (the STA was added associated to the driver) or if the station was
* previously added unassociated.
*/
@@ -4857,6 +4947,11 @@
return 0;
}
+ if (hapd->iface->state != HAPD_IFACE_ENABLED) {
+ wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
+ return 1;
+ }
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
handle_probe_req(hapd, mgmt, len, ssi_signal);
@@ -5497,4 +5592,57 @@
}
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bw, chan1, chan2 = 0;
+ int freq1;
+
+ if (!hapd->cs_freq_params.channel ||
+ (!hapd->cs_freq_params.vht_enabled &&
+ !hapd->cs_freq_params.he_enabled))
+ return eid;
+
+ /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
+ switch (hapd->cs_freq_params.bandwidth) {
+ case 40:
+ bw = 0;
+ break;
+ case 80:
+ /* check if it's 80+80 */
+ if (!hapd->cs_freq_params.center_freq2)
+ bw = 1;
+ else
+ bw = 3;
+ break;
+ case 160:
+ bw = 2;
+ break;
+ default:
+ /* not valid VHT bandwidth or not in CSA */
+ return eid;
+ }
+
+ freq1 = hapd->cs_freq_params.center_freq1 ?
+ hapd->cs_freq_params.center_freq1 :
+ hapd->cs_freq_params.freq;
+ if (ieee80211_freq_to_chan(freq1, &chan1) !=
+ HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ if (hapd->cs_freq_params.center_freq2 &&
+ ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+ &chan2) != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
+ *eid++ = 5; /* Length of Channel Switch Wrapper */
+ *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
+ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+ *eid++ = bw; /* New Channel Width */
+ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+ return eid;
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c7bdb4b..ea8c608 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -63,6 +63,7 @@
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -95,6 +96,8 @@
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode, const u8 *he_capab,
size_t he_capab_len);
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab);
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 57c6b18..85b7140 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -311,6 +311,35 @@
}
+u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
+ struct he_capabilities *he_cap;
+ struct ieee80211_he_6ghz_band_cap *cap;
+ u16 capab;
+ u8 *pos;
+
+ if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
+ !is_6ghz_freq(hapd->iface->freq))
+ return eid;
+
+ he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+ capab = he_cap->he_6ghz_capa;
+ capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*cap);
+ *pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
+
+ cap = (struct ieee80211_he_6ghz_band_cap *) pos;
+ cap->capab = host_to_le16(capab);
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
void hostapd_get_he_capab(struct hostapd_data *hapd,
const struct ieee80211_he_capabilities *he_cap,
struct ieee80211_he_capabilities *neg_he_cap,
@@ -415,6 +444,32 @@
}
+u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_6ghz_capab)
+{
+ if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
+ !is_6ghz_op_class(hapd->iconf->op_class)) {
+ sta->flags &= ~WLAN_STA_6GHZ;
+ os_free(sta->he_6ghz_capab);
+ sta->he_6ghz_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_6ghz_capab) {
+ sta->he_6ghz_capab =
+ os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+ if (!sta->he_6ghz_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_6GHZ;
+ os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
+ sizeof(struct ieee80211_he_6ghz_band_cap));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode)
{
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 113b4ef..17003d5 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_config.h"
@@ -72,6 +73,16 @@
"Failed to get channel info for OCI element in SA Query Request");
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_saquery_req) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_saquery_req);
+ ci.frequency =
+ hapd->conf->oci_freq_override_saquery_req;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
@@ -151,6 +162,16 @@
"Failed to get channel info for OCI element in SA Query Response");
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_saquery_resp) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_saquery_resp);
+ ci.frequency =
+ hapd->conf->oci_freq_override_saquery_resp;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
@@ -254,14 +275,21 @@
return;
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
- tx_chanwidth, tx_seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ tx_chanwidth, tx_seg1_idx) !=
+ OCI_SUCCESS) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
+ MACSTR " frame=saquery%s error=%s",
+ MAC2STR(sa),
+ action_type == WLAN_SA_QUERY_REQUEST ?
+ "req" : "resp", ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (action_type == WLAN_SA_QUERY_REQUEST) {
+ if (sta)
+ sta->post_csa_sa_query = 0;
ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
return;
}
@@ -400,6 +428,14 @@
if (hapd->conf->beacon_prot)
*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
break;
+ case 11: /* Bits 88-95 */
+#ifdef CONFIG_SAE_PK
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ hostapd_sae_pk_exclusively(hapd->conf))
+ *pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
+#endif /* CONFIG_SAE_PK */
+ break;
}
}
@@ -460,6 +496,12 @@
#endif /* CONFIG_SAE */
if (len < 11 && hapd->conf->beacon_prot)
len = 11;
+#ifdef CONFIG_SAE_PK
+ if (len < 12 && hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ hostapd_sae_pk_exclusively(hapd->conf))
+ len = 12;
+#endif /* CONFIG_SAE_PK */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -886,9 +928,9 @@
u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len)
{
-#ifdef CONFIG_DPP2
u8 *pos = eid;
+#ifdef CONFIG_DPP2
if (!hapd->conf->dpp_configurator_connectivity || len < 6)
return pos;
@@ -897,10 +939,9 @@
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = DPP_CC_OUI_TYPE;
+#endif /* CONFIG_DPP2 */
return pos;
-#endif /* CONFIG_DPP2 */
- return eid;
}
@@ -1055,11 +1096,16 @@
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
+ bool sae_pk = false;
+
+#ifdef CONFIG_SAE_PK
+ sae_pk = hostapd_sae_pk_in_use(hapd->conf);
+#endif /* CONFIG_SAE_PK */
if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
!wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) ||
(hapd->conf->sae_pwe != 1 && hapd->conf->sae_pwe != 2 &&
- !hostapd_sae_pw_id_in_use(hapd->conf)) ||
+ !hostapd_sae_pw_id_in_use(hapd->conf) && !sae_pk) ||
hapd->conf->sae_pwe == 3 ||
len < 3)
return pos;
@@ -1068,7 +1114,12 @@
*pos++ = 1;
/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
* used for now */
- *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+ if (sae_pk)
+ *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+ pos++;
return pos;
}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index f50f142..c925bf1 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -167,59 +167,6 @@
}
-u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
-{
- u8 bw, chan1, chan2 = 0;
- int freq1;
-
- if (!hapd->cs_freq_params.channel ||
- !hapd->cs_freq_params.vht_enabled)
- return eid;
-
- /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
- switch (hapd->cs_freq_params.bandwidth) {
- case 40:
- bw = 0;
- break;
- case 80:
- /* check if it's 80+80 */
- if (!hapd->cs_freq_params.center_freq2)
- bw = 1;
- else
- bw = 3;
- break;
- case 160:
- bw = 2;
- break;
- default:
- /* not valid VHT bandwidth or not in CSA */
- return eid;
- }
-
- freq1 = hapd->cs_freq_params.center_freq1 ?
- hapd->cs_freq_params.center_freq1 :
- hapd->cs_freq_params.freq;
- if (ieee80211_freq_to_chan(freq1, &chan1) !=
- HOSTAPD_MODE_IEEE80211A)
- return eid;
-
- if (hapd->cs_freq_params.center_freq2 &&
- ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
- &chan2) != HOSTAPD_MODE_IEEE80211A)
- return eid;
-
- *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
- *eid++ = 5; /* Length of Channel Switch Wrapper */
- *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
- *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
- *eid++ = bw; /* New Channel Width */
- *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
- *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
-
- return eid;
-}
-
-
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
{
struct hostapd_iface *iface = hapd->iface;
@@ -425,7 +372,9 @@
{
u8 *pos = eid;
- if (!hapd->iface->current_mode)
+ /* Vendor VHT is applicable only to 2.4 GHz */
+ if (!hapd->iface->current_mode ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return eid;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 93f1f0c..67b5e98 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -326,6 +326,7 @@
os_free(sta->vht_capabilities);
os_free(sta->vht_operation);
os_free(sta->he_capab);
+ os_free(sta->he_6ghz_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -1424,7 +1425,8 @@
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen,
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1444,6 +1446,7 @@
(flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_HE ? "[HE]" : ""),
+ (flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
"[WNM_SLEEP_MODE]" : ""));
@@ -1515,7 +1518,7 @@
if (hostapd_sta_add(hapd, sta->addr, 0, 0,
sta->supported_rates,
sta->supported_rates_len,
- 0, NULL, NULL, NULL, 0,
+ 0, NULL, NULL, NULL, 0, NULL,
sta->flags, 0, 0, 0, 0)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 308aa29..ef48561 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -38,6 +38,7 @@
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_HE BIT(24)
+#define WLAN_STA_6GHZ BIT(25)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -121,6 +122,7 @@
unsigned int hs20_t_c_filtering:1;
unsigned int ft_over_ds:1;
unsigned int external_dh_updated:1;
+ unsigned int post_csa_sa_query:1;
u16 auth_alg;
@@ -170,6 +172,7 @@
u8 vht_opmode;
struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
+ struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 67281b3..be81797 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -103,6 +103,15 @@
os_free(wnmtfs_ie);
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->oci_freq_override_wnm_sleep) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ hapd->conf->oci_freq_override_wnm_sleep);
+ ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
@@ -317,8 +326,9 @@
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
+ ocv_errorstr);
return;
}
}
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 30e7258..9d74bfc 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -14,6 +14,8 @@
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
#include "common/ocv.h"
+#include "common/dpp.h"
+#include "common/wpa_ctrl.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/aes_siv.h"
@@ -2321,7 +2323,6 @@
struct wpa_auth_config *conf = &wpa_auth->conf;
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
- size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
conf->ssid, conf->ssid_len,
@@ -2332,10 +2333,6 @@
use_sha384) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
- pmk_r0, pmk_r0_len);
- wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
- pmk_r0_name, WPA_PMK_NAME_LEN);
wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
forced_memzero(fils_ft, sizeof(fils_ft));
@@ -2775,6 +2772,15 @@
wpabuf_clear_free(plain);
return NULL;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->oci_freq_override_fils_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ conf->oci_freq_override_fils_assoc);
+ ci.frequency = conf->oci_freq_override_fils_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
if (ocv_insert_extended_oci(&ci, pos) < 0) {
@@ -3031,6 +3037,7 @@
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
+ enum oci_verify_result res;
if (wpa_channel_info(wpa_auth, &ci) != 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -3044,10 +3051,21 @@
&tx_seg1_idx) < 0)
return;
- if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
- tx_chanwidth, tx_seg1_idx) != 0) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- ocv_errorstr);
+ res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "OCV failed: %s", ocv_errorstr);
+ if (wpa_auth->conf.msg_ctx)
+ wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=eapol-key-m2 error=%s",
+ MAC2STR(sm->addr), ocv_errorstr);
return;
}
}
@@ -3079,6 +3097,24 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && kde.dpp_kde) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer Protocol Version %u Flags 0x%x",
+ kde.dpp_kde[0], kde.dpp_kde[1]);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
+ wpa_auth->conf.dpp_pfs != 2 &&
+ (kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
+ !sm->dpp_z) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+ }
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
@@ -3192,6 +3228,14 @@
else
os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each OSEN STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(bigtk.bigtk, len) < 0)
+ return pos;
+ }
pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
(const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
NULL, 0);
@@ -3209,7 +3253,9 @@
return 0;
}
-static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos,
+ unsigned int freq)
{
#ifdef CONFIG_OCV
struct wpa_channel_info ci;
@@ -3222,6 +3268,14 @@
"Failed to get channel info for OCI element");
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (freq) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %u MHz",
+ ci.frequency, freq);
+ ci.frequency = freq;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
return ocv_insert_oci_kde(&ci, argpos);
#else /* CONFIG_OCV */
@@ -3397,6 +3451,11 @@
if (conf->transition_disable)
kde_len += 2 + RSN_SELECTOR_LEN + 1;
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2;
+#endif /* CONFIG_DPP2 */
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
@@ -3433,7 +3492,7 @@
gtk, gtk_len);
}
pos = ieee80211w_kde_add(sm, pos);
- if (ocv_oci_add(sm, &pos) < 0)
+ if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
goto done;
#ifdef CONFIG_IEEE80211R_AP
@@ -3492,6 +3551,22 @@
pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
&conf->transition_disable, 1, NULL, 0);
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ u8 payload[2];
+
+ payload[0] = DPP_VERSION; /* Protocol Version */
+ payload[1] = 0; /* Flags */
+ if (conf->dpp_pfs == 0)
+ payload[1] |= DPP_KDE_PFS_ALLOWED;
+ else if (conf->dpp_pfs == 1)
+ payload[1] |= DPP_KDE_PFS_ALLOWED |
+ DPP_KDE_PFS_REQUIRED;
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
+ payload, sizeof(payload), NULL, 0);
+ }
+#endif /* CONFIG_DPP2 */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
@@ -3767,7 +3842,8 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
- if (ocv_oci_add(sm, &pos) < 0) {
+ if (ocv_oci_add(sm, &pos,
+ conf->oci_freq_override_eapol_g1) < 0) {
os_free(kde_buf);
return;
}
@@ -3833,7 +3909,7 @@
if (wpa_channel_info(wpa_auth, &ci) != 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2");
+ "Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
return;
}
@@ -3844,9 +3920,15 @@
return;
if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
- tx_chanwidth, tx_seg1_idx) != 0) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- ocv_errorstr);
+ tx_chanwidth, tx_seg1_idx) !=
+ OCI_SUCCESS) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "OCV failed: %s", ocv_errorstr);
+ if (wpa_auth->conf.msg_ctx)
+ wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=eapol-key-g2 error=%s",
+ MAC2STR(sm->addr), ocv_errorstr);
return;
}
}
@@ -4032,6 +4114,7 @@
int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *start = pos;
@@ -4050,6 +4133,14 @@
return 0;
pos += 8;
os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(pos, gsm->GTK_len) < 0)
+ return 0;
+ }
pos += gsm->GTK_len;
wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
@@ -4063,6 +4154,7 @@
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *start = pos;
size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
@@ -4080,6 +4172,14 @@
pos += 6;
os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use
+ * of IGTK in the BSS.
+ */
+ if (random_get_bytes(pos, len) < 0)
+ return 0;
+ }
pos += len;
wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
@@ -4110,6 +4210,14 @@
pos += 6;
os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(pos, len) < 0)
+ return 0;
+ }
pos += len;
wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
@@ -5188,6 +5296,14 @@
#endif /* CONFIG_DPP2 */
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+ u8 val)
+{
+ if (wpa_auth)
+ wpa_auth->conf.transition_disable = val;
+}
+
+
#ifdef CONFIG_TESTING_OPTIONS
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
@@ -5219,6 +5335,7 @@
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
u8 *opos;
size_t gtk_len, kde_len;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int wpa_ie_len, secure, gtkidx, encr = 0;
@@ -5329,7 +5446,7 @@
opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
- if (ocv_oci_add(sm, &pos) < 0) {
+ if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0) {
os_free(kde);
return -1;
}
@@ -5337,9 +5454,7 @@
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
- struct wpa_auth_config *conf;
- conf = &sm->wpa_auth->conf;
if (sm->assoc_resp_ftie &&
kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
os_memcpy(pos, sm->assoc_resp_ftie,
@@ -5396,6 +5511,7 @@
void *ctx1, void *ctx2)
{
u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
const u8 *kde;
u8 *kde_buf = NULL, *pos, hdr[2];
@@ -5430,7 +5546,8 @@
opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
- if (ocv_oci_add(sm, &pos) < 0) {
+ if (ocv_oci_add(sm, &pos,
+ conf->oci_freq_override_eapol_g1) < 0) {
os_free(kde_buf);
return -1;
}
@@ -5472,4 +5589,27 @@
wpa_auth->conf.ft_rsnxe_used = val;
}
+
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+ enum wpa_auth_ocv_override_frame frame,
+ unsigned int freq)
+{
+ if (!wpa_auth)
+ return;
+ switch (frame) {
+ case WPA_AUTH_OCV_OVERRIDE_EAPOL_M3:
+ wpa_auth->conf.oci_freq_override_eapol_m3 = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_EAPOL_G1:
+ wpa_auth->conf.oci_freq_override_eapol_g1 = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_FT_ASSOC:
+ wpa_auth->conf.oci_freq_override_ft_assoc = freq;
+ break;
+ case WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC:
+ wpa_auth->conf.oci_freq_override_fils_assoc = freq;
+ break;
+ }
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 1ea067b..5f9df9c 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -168,6 +168,7 @@
struct wpa_auth_config {
+ void *msg_ctx;
int wpa;
int extended_key_id;
int wpa_key_mgmt;
@@ -240,6 +241,10 @@
unsigned int igtk_rsc_override_set:1;
int ft_rsnxe_used;
#endif /* CONFIG_TESTING_OPTIONS */
+ unsigned int oci_freq_override_eapol_m3;
+ unsigned int oci_freq_override_eapol_g1;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
#ifdef CONFIG_P2P
u8 ip_addr_go[4];
u8 ip_addr_mask[4];
@@ -251,6 +256,7 @@
u8 fils_cache_id[FILS_CACHE_ID_LEN];
#endif /* CONFIG_FILS */
int sae_pwe;
+ bool sae_pk;
int owe_ptk_workaround;
u8 transition_disable;
#ifdef CONFIG_DPP2
@@ -511,6 +517,8 @@
const u8 *req_ies, size_t req_ies_len);
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
+void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
+ u8 val);
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
void (*cb)(void *ctx1, void *ctx2),
@@ -525,4 +533,14 @@
void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val);
+enum wpa_auth_ocv_override_frame {
+ WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+ WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+ WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+ WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC,
+};
+void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
+ enum wpa_auth_ocv_override_frame frame,
+ unsigned int freq);
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 5af65aa..5aa363e 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -14,6 +14,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
@@ -1897,7 +1898,7 @@
return;
}
- wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+ wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
if (r0kh) {
@@ -1985,7 +1986,7 @@
return -1;
}
if (is_zero_ether_addr(r0kh->addr)) {
- wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
@@ -2128,8 +2129,6 @@
pmk_r0, pmk_r0_name,
wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
pmk_r0_name,
@@ -2140,9 +2139,6 @@
if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
pmk_r1, sm->pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
- WPA_PMK_NAME_LEN);
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
sm->pmk_r1_name, sm->pairwise, &vlan,
@@ -2167,11 +2163,12 @@
static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
size_t subelem_len, pad_len;
const u8 *key;
size_t key_len;
- u8 keybuf[32];
+ u8 keybuf[WPA_GTK_MAX_LEN];
const u8 *kek;
size_t kek_len;
@@ -2198,12 +2195,30 @@
pad_len += 8;
if (pad_len && key_len < sizeof(keybuf)) {
os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
+ if (conf->disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(keybuf, key_len) < 0)
+ return NULL;
+ }
os_memset(keybuf + key_len, 0, pad_len);
keybuf[key_len] = 0xdd;
key_len += pad_len;
key = keybuf;
- } else
+ } else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use of GTK
+ * in the BSS.
+ */
+ if (random_get_bytes(keybuf, key_len) < 0)
+ return NULL;
+ key = keybuf;
+ } else {
key = gsm->GTK[gsm->GN - 1];
+ }
/*
* Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
@@ -2237,11 +2252,13 @@
static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
- const u8 *kek;
+ const u8 *kek, *igtk;
size_t kek_len;
size_t igtk_len;
+ u8 dummy_igtk[WPA_IGTK_MAX_LEN];
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
@@ -2268,8 +2285,19 @@
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
*pos++ = igtk_len;
- if (aes_wrap(kek, kek_len, igtk_len / 8,
- gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ igtk = gsm->IGTK[gsm->GN_igtk - 4];
+ if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use of
+ * IGTK in the BSS.
+ */
+ if (random_get_bytes(dummy_igtk, igtk_len / 8) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ igtk = dummy_igtk;
+ }
+ if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
wpa_printf(MSG_DEBUG,
"FT: IGTK subelem encryption failed: kek_len=%d",
(int) kek_len);
@@ -2287,9 +2315,10 @@
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
- const u8 *kek;
+ const u8 *kek, *bigtk;
size_t kek_len;
size_t bigtk_len;
+ u8 dummy_bigtk[WPA_IGTK_MAX_LEN];
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
@@ -2316,8 +2345,19 @@
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
- if (aes_wrap(kek, kek_len, bigtk_len / 8,
- gsm->IGTK[gsm->GN_bigtk - 6], pos)) {
+ bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random BIGTK to each OSEN STA to prevent use
+ * of BIGTK in the BSS.
+ */
+ if (random_get_bytes(dummy_bigtk, bigtk_len / 8) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ bigtk = dummy_bigtk;
+ }
+ if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
wpa_printf(MSG_DEBUG,
"FT: BIGTK subelem encryption failed: kek_len=%d",
(int) kek_len);
@@ -2621,6 +2661,15 @@
os_free(subelem);
return NULL;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->oci_freq_override_ft_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI frequency %d -> %u MHz",
+ ci.frequency,
+ conf->oci_freq_override_ft_assoc);
+ ci.frequency = conf->oci_freq_override_ft_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
subelem_len += 2 + OCV_OCI_LEN;
nbuf = os_realloc(subelem, subelem_len);
@@ -2961,8 +3010,6 @@
conf->r1_key_holder,
sm->addr, out_pmk_r1, pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
os_get_reltime(&now);
if (r0->expiration)
@@ -3091,8 +3138,6 @@
sm->wpa_auth->conf.r1_key_holder, sm->addr,
pmk_r1_name, use_sha384) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
- pmk_r1_name, WPA_PMK_NAME_LEN);
if (conf->ft_psk_generate_local &&
wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
@@ -3460,6 +3505,7 @@
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
+ enum oci_verify_result res;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
@@ -3473,10 +3519,21 @@
&tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
- tx_chanwidth, tx_seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx);
+ if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
+ /* Work around misbehaving STAs */
+ wpa_printf(MSG_INFO,
+ "Disable OCV with a STA that does not send OCI");
+ wpa_auth_set_ocv(sm, 0);
+ } else if (res != OCI_SUCCESS) {
+ wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
+ if (sm->wpa_auth->conf.msg_ctx)
+ wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
+ OCV_FAILURE "addr=" MACSTR
+ " frame=ft-reassoc-req error=%s",
+ MAC2STR(sm->addr), ocv_errorstr);
+ return WLAN_STATUS_INVALID_FTIE;
}
}
#endif /* CONFIG_OCV */
@@ -3699,14 +3756,11 @@
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
+ wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
pmk_r0->pmk_r0_name, r1kh_id,
s1kh_id, pmk_r1, pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
- pmk_r1, pmk_r1_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
- pmk_r1_name, WPA_PMK_NAME_LEN);
WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
os_get_reltime(&now);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 44ab830..c01654f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -175,6 +175,11 @@
wconf->igtk_rsc_override_set = 1;
}
wconf->ft_rsnxe_used = conf->ft_rsnxe_used;
+ wconf->oci_freq_override_eapol_m3 = conf->oci_freq_override_eapol_m3;
+ wconf->oci_freq_override_eapol_g1 = conf->oci_freq_override_eapol_g1;
+ wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
+ wconf->oci_freq_override_fils_assoc =
+ conf->oci_freq_override_fils_assoc;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -193,6 +198,9 @@
wconf->sae_pwe = 1;
else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
wconf->sae_pwe = 2;
+#ifdef CONFIG_SAE_PK
+ wconf->sae_pk = hostapd_sae_pk_in_use(conf);
+#endif /* CONFIG_SAE_PK */
#ifdef CONFIG_OWE
wconf->owe_ptk_workaround = conf->owe_ptk_workaround;
#endif /* CONFIG_OWE */
@@ -453,15 +461,23 @@
os_memcpy(sta->last_tk, key, key_len);
sta->last_tk_len = key_len;
}
- } else if (alg == WPA_ALG_IGTK ||
+ } else if (alg == WPA_ALG_BIP_CMAC_128 ||
alg == WPA_ALG_BIP_GMAC_128 ||
alg == WPA_ALG_BIP_GMAC_256 ||
alg == WPA_ALG_BIP_CMAC_256) {
- hapd->last_igtk_alg = alg;
- hapd->last_igtk_key_idx = idx;
- if (key)
- os_memcpy(hapd->last_igtk, key, key_len);
- hapd->last_igtk_len = key_len;
+ if (idx == 4 || idx == 5) {
+ hapd->last_igtk_alg = alg;
+ hapd->last_igtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_igtk, key, key_len);
+ hapd->last_igtk_len = key_len;
+ } else if (idx == 6 || idx == 7) {
+ hapd->last_bigtk_alg = alg;
+ hapd->last_bigtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_bigtk, key, key_len);
+ hapd->last_bigtk_len = key_len;
+ }
} else {
hapd->last_gtk_alg = alg;
hapd->last_gtk_key_idx = idx;
@@ -1443,6 +1459,7 @@
size_t wpa_ie_len;
hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
+ _conf.msg_ctx = hapd->msg_ctx;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index af0aaca..a6dc1a5 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -95,8 +95,9 @@
#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1;
+
#ifdef CONFIG_OCV
- unsigned int ocv_enabled:1;
+ int ocv_enabled;
#endif /* CONFIG_OCV */
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
@@ -277,7 +278,8 @@
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt);
void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
- logger_level level, const char *fmt, ...);
+ logger_level level, const char *fmt, ...)
+ PRINTF_FORMAT(4, 5);
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 8dfd657..3704fc0 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -378,7 +378,7 @@
{
u8 *pos = buf;
- if (conf->sae_pwe != 1 && conf->sae_pwe != 2)
+ if (conf->sae_pwe != 1 && conf->sae_pwe != 2 && !conf->sae_pk)
return 0; /* no supported extended RSN capabilities */
if (len < 3)
@@ -388,7 +388,12 @@
*pos++ = 1;
/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
* used for now */
- *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+ if (conf->sae_pk)
+ *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+ pos++;
return pos - buf;
}
@@ -803,14 +808,26 @@
#endif /* CONFIG_SAE */
#ifdef CONFIG_OCV
- if ((data.capabilities & WPA_CAPABILITY_OCVC) &&
+ if (wpa_auth->conf.ocv && (data.capabilities & WPA_CAPABILITY_OCVC) &&
!(data.capabilities & WPA_CAPABILITY_MFPC)) {
- wpa_printf(MSG_DEBUG,
- "Management frame protection required with OCV, but client did not enable it");
- return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ /* Some legacy MFP incapable STAs wrongly copy OCVC bit from
+ * AP RSN capabilities. To improve interoperability with such
+ * legacy STAs allow connection without enabling OCV when the
+ * workaround mode (ocv=2) is enabled.
+ */
+ if (wpa_auth->conf.ocv == 2) {
+ wpa_printf(MSG_DEBUG,
+ "Allow connecting MFP incapable and OCV capable STA without enabling OCV");
+ wpa_auth_set_ocv(sm, 0);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with OCV, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+ } else {
+ wpa_auth_set_ocv(sm, (data.capabilities & WPA_CAPABILITY_OCVC) ?
+ wpa_auth->conf.ocv : 0);
}
- wpa_auth_set_ocv(sm, wpa_auth->conf.ocv &&
- (data.capabilities & WPA_CAPABILITY_OCVC));
#endif /* CONFIG_OCV */
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
diff --git a/src/build.rules b/src/build.rules
new file mode 100644
index 0000000..acda884
--- /dev/null
+++ b/src/build.rules
@@ -0,0 +1,109 @@
+.PHONY: all
+all: _all
+
+# disable built-in rules
+.SUFFIXES:
+
+# setup some variables
+ROOTDIR := $(dir $(lastword $(MAKEFILE_LIST)))
+ROOTDIR := $(dir $(ROOTDIR:%../src/=%))../
+BUILDDIR ?= $(abspath $(ROOTDIR)build)
+BUILDDIR := $(BUILDDIR:%/=%)
+ABSROOT := $(abspath $(ROOTDIR))
+ifeq ($(origin OUT),command line)
+_PROJ := $(OUT:%/=%)
+_PROJ := $(_PROJ:$(BUILDDIR)/%=%)
+else
+_PROJ := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
+_PROJ := $(_PROJ:$(ABSROOT)/%=%)
+endif
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+ifneq ($(CONFIG_FILE),)
+-include $(CONFIG_FILE)
+
+# export for sub-makefiles
+export CONFIG_CODE_COVERAGE
+
+.PHONY: verify_config
+verify_config:
+ @if [ ! -r $(CONFIG_FILE) ]; then \
+ echo 'Building $(firstword $(ALL)) requires a configuration file'; \
+ echo '(.config). See README for more instructions. You can'; \
+ echo 'run "cp defconfig .config" to create an example'; \
+ echo 'configuration.'; \
+ exit 1; \
+ fi
+VERIFY := verify_config
+else
+VERIFY :=
+endif
+
+# default target
+.PHONY: _all
+_all: $(VERIFY) $(ALL) $(EXTRA_TARGETS)
+
+# continue setup
+COVSUFFIX := $(if $(CONFIG_CODE_COVERAGE),-cov,)
+PROJ := $(_PROJ)$(COVSUFFIX)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+ifeq ($(Q),@)
+MAKEFLAGS += --no-print-directory
+endif
+
+_DIRS := $(BUILDDIR)/$(PROJ)
+.PHONY: _make_dirs
+_make_dirs:
+ @mkdir -p $(_DIRS)
+
+$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+$(BUILDDIR)/$(PROJ)/%.o: %.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+# for the fuzzing tests
+$(BUILDDIR)/$(PROJ)/wpa_supplicant/%.o: $(ROOTDIR)wpa_supplicant/%.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+
+# libraries - they know how to build themselves
+# (lib_phony so we recurse all the time)
+.PHONY: lib_phony
+lib_phony:
+# nothing
+
+$(BUILDDIR)/$(PROJ)/%.a: $(CONFIG_FILE) lib_phony
+ $(Q)$(MAKE) -C $(ROOTDIR)$(dir $(@:$(BUILDDIR)/$(PROJ)/%=%)) OUT=$(abspath $(dir $@))/
+
+BUILDOBJ = $(patsubst %,$(BUILDDIR)/$(PROJ)/%,$(patsubst $(ROOTDIR)%,%,$(1)))
+
+.PHONY: common-clean
+common-clean:
+ $(Q)rm -rf $(ALL) $(BUILDDIR)/$(PROJ)
diff --git a/src/common/Makefile b/src/common/Makefile
index ccb280e..59ba6c5 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -1,13 +1,3 @@
-all: libcommon.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_IEEE80211R
CFLAGS += -DCONFIG_HS20
CFLAGS += -DCONFIG_SAE
@@ -21,7 +11,4 @@
sae.o \
wpa_common.o
-libcommon.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/common/brcm_vendor.h b/src/common/brcm_vendor.h
new file mode 100644
index 0000000..b1bf397
--- /dev/null
+++ b/src/common/brcm_vendor.h
@@ -0,0 +1,73 @@
+/*
+ * Broadcom Corporation OUI and vendor specific assignments
+ * Copyright (c) 2015, Broadcom Corporation.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BRCM_VENDOR_H
+#define BRCM_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Broadcom
+ * OUI 00:10:18 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_BRCM 0x001018
+
+/**
+ * enum brcm_nl80211_vendor_subcmds - BRCM nl80211 vendor command identifiers
+ *
+ * @BRCM_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_SUBCMD_PRIV_STR: String command/event
+ */
+enum brcm_nl80211_vendor_subcmds {
+ BRCM_VENDOR_SUBCMD_UNSPEC = 0,
+ BRCM_VENDOR_SUBCMD_SET_MAC = 6,
+ BRCM_VENDOR_SCMD_ACS = 9,
+ BRCM_VENDOR_SCMD_MAX = 10
+};
+
+/**
+ * enum brcm_nl80211_vendor_events - BRCM nl80211 asynchoronous event identifiers
+ *
+ * @BRCM_VENDOR_EVENT_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_EVENT_PRIV_STR: String command/event
+ */
+enum brcm_nl80211_vendor_events {
+ BRCM_VENDOR_EVENT_UNSPEC,
+ BRCM_VENDOR_EVENT_ACS = 42,
+ BRCM_VENDOR_EVENT_LAST = 44
+};
+
+enum wl_vendor_attr_acs_offload {
+ BRCM_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+ BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ,
+ BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ,
+ BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+ BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+
+ BRCM_VENDOR_ATTR_ACS_HW_MODE,
+ BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_CHWIDTH,
+ BRCM_VENDOR_ATTR_ACS_CH_LIST,
+ BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+
+ BRCM_VENDOR_ATTR_ACS_LAST
+};
+
+enum brcm_wlan_vendor_attr {
+ BRCM_ATTR_DRIVER_CMD = 0,
+ BRCM_ATTR_DRIVER_MAC_ADDR = 3,
+ BRCM_ATTR_DRIVER_AFTER_LAST = 5,
+ BRCM_ATTR_DRIVER_MAX = BRCM_ATTR_DRIVER_AFTER_LAST - 1,
+};
+#endif /* BRCM_VENDOR_H */
+
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index a58bf66..00308d4 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -548,6 +548,95 @@
}
+static int sae_pk_tests(void)
+{
+#ifdef CONFIG_SAE_PK
+ const char *invalid[] = { "a2bc-de3f-ghim-", "a2bcde3fghim", "", NULL };
+ struct {
+ const char *pw;
+ const u8 *val;
+ } valid[] = {
+ { "a2bc-de3f-ghim", (u8 *) "\x06\x82\x21\x93\x65\x31\xd0\xc0" },
+ { "aaaa-aaaa-aaaj", (u8 *) "\x00\x00\x00\x00\x00\x00\x00\x90" },
+ { "7777-7777-777f", (u8 *) "\xff\xff\xff\xff\xff\xff\xfe\x50" },
+ { NULL, NULL }
+ };
+ int i;
+ bool failed;
+
+ for (i = 0; invalid[i]; i++) {
+ if (sae_pk_valid_password(invalid[i])) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Invalid password '%s' not recognized",
+ invalid[i]);
+ return -1;
+ }
+ }
+
+ failed = false;
+ for (i = 0; valid[i].pw; i++) {
+ u8 *res;
+ size_t res_len;
+ char *b32;
+ const char *pw = valid[i].pw;
+ const u8 *val = valid[i].val;
+ size_t pw_len = os_strlen(pw);
+ size_t bits = (pw_len - pw_len / 5) * 5;
+ size_t bytes = (bits + 7) / 8;
+
+ if (!sae_pk_valid_password(pw)) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Valid password '%s' not recognized",
+ pw);
+ failed = true;
+ continue;
+ }
+
+ res = sae_pk_base32_decode(pw, pw_len, &res_len);
+ if (!res) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Failed to decode password '%s'",
+ valid[i].pw);
+ failed = true;
+ continue;
+ }
+ if (res_len != bytes || os_memcmp(val, res, res_len) != 0) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Mismatch for decoded password '%s'",
+ valid[i].pw);
+ wpa_hexdump(MSG_INFO, "SAE-PK: Decoded value",
+ res, res_len);
+ wpa_hexdump(MSG_INFO, "SAE-PK: Expected value",
+ val, bytes);
+ failed = true;
+ }
+ os_free(res);
+
+ b32 = sae_pk_base32_encode(val, bits - 5);
+ if (!b32) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Failed to encode password '%s'",
+ pw);
+ failed = true;
+ continue;
+ }
+ if (os_strcmp(b32, pw) != 0) {
+ wpa_printf(MSG_ERROR,
+ "SAE-PK: Mismatch for password '%s'", pw);
+ wpa_printf(MSG_INFO, "SAE-PK: Encoded value: '%s'",
+ b32);
+ failed = true;
+ }
+ os_free(b32);
+ }
+
+ return failed ? -1 : 0;
+#else /* CONFIG_SAE_PK */
+ return 0;
+#endif /* CONFIG_SAE_PK */
+}
+
+
int common_module_tests(void)
{
int ret = 0;
@@ -557,6 +646,7 @@
if (ieee802_11_parse_tests() < 0 ||
gas_tests() < 0 ||
sae_tests() < 0 ||
+ sae_pk_tests() < 0 ||
rsn_ie_parse_tests() < 0)
ret = -1;
diff --git a/src/common/defs.h b/src/common/defs.h
index bcf6f54..bbe3120 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -190,7 +190,7 @@
WPA_ALG_WEP,
WPA_ALG_TKIP,
WPA_ALG_CCMP,
- WPA_ALG_IGTK,
+ WPA_ALG_BIP_CMAC_128,
WPA_ALG_GCMP,
WPA_ALG_SMS4,
WPA_ALG_KRK,
@@ -201,6 +201,14 @@
WPA_ALG_BIP_CMAC_256
};
+static inline int wpa_alg_bip(enum wpa_alg alg)
+{
+ return alg == WPA_ALG_BIP_CMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256;
+}
+
/**
* enum wpa_states - wpa_supplicant state
*
diff --git a/src/common/dpp.c b/src/common/dpp.c
index b33ab15..67b8f07 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -8,47 +8,34 @@
*/
#include "utils/includes.h"
-#include <fcntl.h>
#include <openssl/opensslv.h>
#include <openssl/err.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
#include "utils/common.h"
#include "utils/base64.h"
#include "utils/json.h"
-#include "utils/ip_addr.h"
-#include "utils/eloop.h"
#include "common/ieee802_11_common.h"
-#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/gas.h"
+#include "eap_common/eap_defs.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
-#include "crypto/sha384.h"
-#include "crypto/sha512.h"
-#include "tls/asn1.h"
#include "drivers/driver.h"
#include "dpp.h"
+#include "dpp_i.h"
static const char * dpp_netrole_str(enum dpp_netrole netrole);
#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_DPP2
+int dpp_version_override = 2;
+#else
+int dpp_version_override = 1;
+#endif
enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
-u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-u8 dpp_pkex_ephemeral_key_override[600];
-size_t dpp_pkex_ephemeral_key_override_len = 0;
-u8 dpp_protocol_key_override[600];
-size_t dpp_protocol_key_override_len = 0;
-u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
-size_t dpp_nonce_override_len = 0;
-
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
- const struct dpp_curve_params *curve);
#endif /* CONFIG_TESTING_OPTIONS */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
@@ -56,24 +43,6 @@
LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
-static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-
-static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
- const BIGNUM **ps)
-{
- if (pr)
- *pr = sig->r;
- if (ps)
- *ps = sig->s;
-}
-
-
#ifdef CONFIG_DPP2
static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
{
@@ -86,700 +55,7 @@
#endif
-struct dpp_connection {
- struct dl_list list;
- struct dpp_controller *ctrl;
- struct dpp_relay_controller *relay;
- struct dpp_global *global;
- struct dpp_authentication *auth;
- int sock;
- u8 mac_addr[ETH_ALEN];
- unsigned int freq;
- u8 msg_len[4];
- size_t msg_len_octets;
- struct wpabuf *msg;
- struct wpabuf *msg_out;
- size_t msg_out_pos;
- unsigned int read_eloop:1;
- unsigned int write_eloop:1;
- unsigned int on_tcp_tx_complete_gas_done:1;
- unsigned int on_tcp_tx_complete_remove:1;
- unsigned int on_tcp_tx_complete_auth_ok:1;
-};
-
-/* Remote Controller */
-struct dpp_relay_controller {
- struct dl_list list;
- struct dpp_global *global;
- u8 pkhash[SHA256_MAC_LEN];
- struct hostapd_ip_addr ipaddr;
- void *cb_ctx;
- void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
- size_t len);
- void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
- int prot, struct wpabuf *buf);
- struct dl_list conn; /* struct dpp_connection */
-};
-
-/* Local Controller */
-struct dpp_controller {
- struct dpp_global *global;
- u8 allowed_roles;
- int qr_mutual;
- int sock;
- struct dl_list conn; /* struct dpp_connection */
- char *configurator_params;
-};
-
-struct dpp_global {
- void *msg_ctx;
- struct dl_list bootstrap; /* struct dpp_bootstrap_info */
- struct dl_list configurator; /* struct dpp_configurator */
-#ifdef CONFIG_DPP2
- struct dl_list controllers; /* struct dpp_relay_controller */
- struct dpp_controller *controller;
- struct dl_list tcp_init; /* struct dpp_connection */
- void *cb_ctx;
- int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
- void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
-#endif /* CONFIG_DPP2 */
-};
-
-static const struct dpp_curve_params dpp_curves[] = {
- /* The mandatory to support and the default NIST P-256 curve needs to
- * be the first entry on this list. */
- { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
- { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
- { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
- { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
- { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
- { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
- { NULL, 0, 0, 0, 0, NULL, 0, NULL }
-};
-
-
-/* Role-specific elements for PKEX */
-
-/* NIST P-256 */
-static const u8 pkex_init_x_p256[32] = {
- 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
- 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
- 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
- 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
- };
-static const u8 pkex_init_y_p256[32] = {
- 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
- 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
- 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
- 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
- };
-static const u8 pkex_resp_x_p256[32] = {
- 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
- 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
- 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
- 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
-};
-static const u8 pkex_resp_y_p256[32] = {
- 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
- 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
- 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
- 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
-};
-
-/* NIST P-384 */
-static const u8 pkex_init_x_p384[48] = {
- 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
- 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
- 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
- 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
- 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
- 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
-};
-static const u8 pkex_init_y_p384[48] = {
- 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
- 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
- 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
- 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
- 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
- 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
-};
-static const u8 pkex_resp_x_p384[48] = {
- 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
- 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
- 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
- 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
- 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
- 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
-};
-static const u8 pkex_resp_y_p384[48] = {
- 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
- 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
- 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
- 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
- 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
- 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
-};
-
-/* NIST P-521 */
-static const u8 pkex_init_x_p521[66] = {
- 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
- 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
- 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
- 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
- 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
- 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
- 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
- 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
- 0x97, 0x76
-};
-static const u8 pkex_init_y_p521[66] = {
- 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
- 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
- 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
- 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
- 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
- 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
- 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
- 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
- 0x03, 0xa8
-};
-static const u8 pkex_resp_x_p521[66] = {
- 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
- 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
- 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
- 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
- 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
- 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
- 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
- 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
- 0x84, 0xb4
-};
-static const u8 pkex_resp_y_p521[66] = {
- 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
- 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
- 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
- 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
- 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
- 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
- 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
- 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
- 0xce, 0xe1
-};
-
-/* Brainpool P-256r1 */
-static const u8 pkex_init_x_bp_p256r1[32] = {
- 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
- 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
- 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
- 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
-};
-static const u8 pkex_init_y_bp_p256r1[32] = {
- 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
- 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
- 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
- 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
-};
-static const u8 pkex_resp_x_bp_p256r1[32] = {
- 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
- 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
- 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
- 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
-};
-static const u8 pkex_resp_y_bp_p256r1[32] = {
- 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
- 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
- 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
- 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
-};
-
-/* Brainpool P-384r1 */
-static const u8 pkex_init_x_bp_p384r1[48] = {
- 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
- 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
- 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
- 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
- 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
- 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
-};
-static const u8 pkex_init_y_bp_p384r1[48] = {
- 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
- 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
- 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
- 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
- 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
- 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
-};
-static const u8 pkex_resp_x_bp_p384r1[48] = {
- 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
- 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
- 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
- 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
- 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
- 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
-};
-static const u8 pkex_resp_y_bp_p384r1[48] = {
- 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
- 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
- 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
- 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
- 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
- 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
-};
-
-/* Brainpool P-512r1 */
-static const u8 pkex_init_x_bp_p512r1[64] = {
- 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
- 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
- 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
- 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
- 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
- 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
- 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
- 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
-};
-static const u8 pkex_init_y_bp_p512r1[64] = {
- 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
- 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
- 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
- 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
- 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
- 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
- 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
- 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
-};
-static const u8 pkex_resp_x_bp_p512r1[64] = {
- 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
- 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
- 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
- 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
- 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
- 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
- 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
- 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
-};
-static const u8 pkex_resp_y_bp_p512r1[64] = {
- 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
- 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
- 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
- 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
- 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
- 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
- 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
- 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
-};
-
-
-static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
- const EC_POINT *point)
-{
- BIGNUM *x, *y;
- BN_CTX *ctx;
- char *x_str = NULL, *y_str = NULL;
-
- if (!wpa_debug_show_keys)
- return;
-
- ctx = BN_CTX_new();
- x = BN_new();
- y = BN_new();
- if (!ctx || !x || !y ||
- EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
- goto fail;
-
- x_str = BN_bn2hex(x);
- y_str = BN_bn2hex(y);
- if (!x_str || !y_str)
- goto fail;
-
- wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
-
-fail:
- OPENSSL_free(x_str);
- OPENSSL_free(y_str);
- BN_free(x);
- BN_free(y);
- BN_CTX_free(ctx);
-}
-
-
-static int dpp_hash_vector(const struct dpp_curve_params *curve,
- size_t num_elem, const u8 *addr[], const size_t *len,
- u8 *mac)
-{
- if (curve->hash_len == 32)
- return sha256_vector(num_elem, addr, len, mac);
- if (curve->hash_len == 48)
- return sha384_vector(num_elem, addr, len, mac);
- if (curve->hash_len == 64)
- return sha512_vector(num_elem, addr, len, mac);
- return -1;
-}
-
-
-static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
- const char *label, u8 *out, size_t outlen)
-{
- if (hash_len == 32)
- return hmac_sha256_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- if (hash_len == 48)
- return hmac_sha384_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- if (hash_len == 64)
- return hmac_sha512_kdf(secret, secret_len, NULL,
- (const u8 *) label, os_strlen(label),
- out, outlen);
- return -1;
-}
-
-
-static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
- size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
-{
- if (hash_len == 32)
- return hmac_sha256_vector(key, key_len, num_elem, addr, len,
- mac);
- if (hash_len == 48)
- return hmac_sha384_vector(key, key_len, num_elem, addr, len,
- mac);
- if (hash_len == 64)
- return hmac_sha512_vector(key, key_len, num_elem, addr, len,
- mac);
- return -1;
-}
-
-
-static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
- const u8 *data, size_t data_len, u8 *mac)
-{
- if (hash_len == 32)
- return hmac_sha256(key, key_len, data, data_len, mac);
- if (hash_len == 48)
- return hmac_sha384(key, key_len, data, data_len, mac);
- if (hash_len == 64)
- return hmac_sha512(key, key_len, data, data_len, mac);
- return -1;
-}
-
-
-#ifdef CONFIG_DPP2
-
-static int dpp_pbkdf2_f(size_t hash_len,
- const u8 *password, size_t password_len,
- const u8 *salt, size_t salt_len,
- unsigned int iterations, unsigned int count, u8 *digest)
-{
- unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
- unsigned int i;
- size_t j;
- u8 count_buf[4];
- const u8 *addr[2];
- size_t len[2];
-
- addr[0] = salt;
- len[0] = salt_len;
- addr[1] = count_buf;
- len[1] = 4;
-
- /* F(P, S, c, i) = U1 xor U2 xor ... Uc
- * U1 = PRF(P, S || i)
- * U2 = PRF(P, U1)
- * Uc = PRF(P, Uc-1)
- */
-
- WPA_PUT_BE32(count_buf, count);
- if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
- tmp))
- return -1;
- os_memcpy(digest, tmp, hash_len);
-
- for (i = 1; i < iterations; i++) {
- if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
- tmp2))
- return -1;
- os_memcpy(tmp, tmp2, hash_len);
- for (j = 0; j < hash_len; j++)
- digest[j] ^= tmp2[j];
- }
-
- return 0;
-}
-
-
-static int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
- const u8 *salt, size_t salt_len, unsigned int iterations,
- u8 *buf, size_t buflen)
-{
- unsigned int count = 0;
- unsigned char *pos = buf;
- size_t left = buflen, plen;
- unsigned char digest[DPP_MAX_HASH_LEN];
-
- while (left > 0) {
- count++;
- if (dpp_pbkdf2_f(hash_len, password, password_len,
- salt, salt_len, iterations, count, digest))
- return -1;
- plen = left > hash_len ? hash_len : left;
- os_memcpy(pos, digest, plen);
- pos += plen;
- left -= plen;
- }
-
- return 0;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
- int num_bytes, offset;
-
- num_bytes = BN_num_bytes(bn);
- if ((size_t) num_bytes > len)
- return -1;
- offset = len - num_bytes;
- os_memset(pos, 0, offset);
- BN_bn2bin(bn, pos + offset);
- return 0;
-}
-
-
-static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
-{
- int len, res;
- EC_KEY *eckey;
- struct wpabuf *buf;
- unsigned char *pos;
-
- eckey = EVP_PKEY_get1_EC_KEY(pkey);
- if (!eckey)
- return NULL;
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
- len = i2o_ECPublicKey(eckey, NULL);
- if (len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to determine public key encoding length");
- EC_KEY_free(eckey);
- return NULL;
- }
-
- buf = wpabuf_alloc(len);
- if (!buf) {
- EC_KEY_free(eckey);
- return NULL;
- }
-
- pos = wpabuf_put(buf, len);
- res = i2o_ECPublicKey(eckey, &pos);
- EC_KEY_free(eckey);
- if (res != len) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to encode public key (res=%d/%d)",
- res, len);
- wpabuf_free(buf);
- return NULL;
- }
-
- if (!prefix) {
- /* Remove 0x04 prefix to match DPP definition */
- pos = wpabuf_mhead(buf);
- os_memmove(pos, pos + 1, len - 1);
- buf->used--;
- }
-
- return buf;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
- const u8 *buf_x, const u8 *buf_y,
- size_t len)
-{
- EC_KEY *eckey = NULL;
- BN_CTX *ctx;
- EC_POINT *point = NULL;
- BIGNUM *x = NULL, *y = NULL;
- EVP_PKEY *pkey = NULL;
-
- ctx = BN_CTX_new();
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- return NULL;
- }
-
- point = EC_POINT_new(group);
- x = BN_bin2bn(buf_x, len, NULL);
- y = BN_bin2bn(buf_y, len, NULL);
- if (!point || !x || !y) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- goto fail;
- }
-
- if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
- wpa_printf(MSG_ERROR,
- "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (!EC_POINT_is_on_curve(group, point, ctx) ||
- EC_POINT_is_at_infinity(group, point)) {
- wpa_printf(MSG_ERROR, "DPP: Invalid point");
- goto fail;
- }
- dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
-
- eckey = EC_KEY_new();
- if (!eckey ||
- EC_KEY_set_group(eckey, group) != 1 ||
- EC_KEY_set_public_key(eckey, point) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to set EC_KEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
-
- pkey = EVP_PKEY_new();
- if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
- goto fail;
- }
-
-out:
- BN_free(x);
- BN_free(y);
- EC_KEY_free(eckey);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- return pkey;
-fail:
- EVP_PKEY_free(pkey);
- pkey = NULL;
- goto out;
-}
-
-
-static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
- const u8 *buf, size_t len)
-{
- EC_KEY *eckey;
- const EC_GROUP *group;
- EVP_PKEY *pkey = NULL;
-
- if (len & 1)
- return NULL;
-
- eckey = EVP_PKEY_get1_EC_KEY(group_key);
- if (!eckey) {
- wpa_printf(MSG_ERROR,
- "DPP: Could not get EC_KEY from group_key");
- return NULL;
- }
-
- group = EC_KEY_get0_group(eckey);
- if (group)
- pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
- len / 2);
- else
- wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
-
- EC_KEY_free(eckey);
- return pkey;
-}
-
-
-static int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer,
- u8 *secret, size_t *secret_len)
-{
- EVP_PKEY_CTX *ctx;
- int ret = -1;
-
- ERR_clear_error();
- *secret_len = 0;
-
- ctx = EVP_PKEY_CTX_new(own, NULL);
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return -1;
- }
-
- if (EVP_PKEY_derive_init(ctx) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: EVP_PKEY_derive_set_peet failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- u8 buf[200];
- int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
-
- /* It looks like OpenSSL can return unexpectedly large buffer
- * need for shared secret from EVP_PKEY_derive(NULL) in some
- * cases. For example, group 19 has shown cases where secret_len
- * is set to 72 even though the actual length ends up being
- * updated to 32 when EVP_PKEY_derive() is called with a buffer
- * for the value. Work around this by trying to fetch the value
- * and continue if it is within supported range even when the
- * initial buffer need is claimed to be larger. */
- wpa_printf(level,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- if (*secret_len > 200)
- goto fail;
- if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_ERROR,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- goto fail;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
- buf, *secret_len);
- os_memcpy(secret, buf, *secret_len);
- forced_memzero(buf, sizeof(buf));
- goto done;
- }
-
- if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
-done:
- ret = 0;
-
-fail:
- EVP_PKEY_CTX_free(ctx);
- return ret;
-}
-
-
-static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
{
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
}
@@ -969,6 +245,7 @@
wpa_printf(MSG_DEBUG,
"DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
opclass, channel, freq);
+ bi->channels_listed = true;
if (freq < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
@@ -1037,57 +314,21 @@
}
-static const struct dpp_curve_params *
-dpp_get_curve_oid(const ASN1_OBJECT *poid)
+int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
{
- ASN1_OBJECT *oid;
- int i;
+#ifdef CONFIG_DPP2
+ if (!version || DPP_VERSION < 2)
+ return 0;
- for (i = 0; dpp_curves[i].name; i++) {
- oid = OBJ_txt2obj(dpp_curves[i].name, 0);
- if (oid && OBJ_cmp(poid, oid) == 0)
- return &dpp_curves[i];
- }
- return NULL;
-}
+ if (*version == '1')
+ bi->version = 1;
+ else if (*version == '2')
+ bi->version = 2;
+ else
+ wpa_printf(MSG_DEBUG, "DPP: Unknown URI version");
-
-static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
-{
- int i, tmp;
-
- if (!nid)
- return NULL;
- for (i = 0; dpp_curves[i].name; i++) {
- tmp = OBJ_txt2nid(dpp_curves[i].name);
- if (tmp == nid)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
- const u8 *data, size_t data_len)
-{
- const u8 *addr[2];
- size_t len[2];
-
- addr[0] = data;
- len[0] = data_len;
- if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
- return -1;
- wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
- bi->pubkey_hash, SHA256_MAC_LEN);
-
- addr[0] = (const u8 *) "chirp";
- len[0] = 5;
- addr[1] = data;
- len[1] = data_len;
- if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
- return -1;
- wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
- bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+ wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version);
+#endif /* CONFIG_DPP2 */
return 0;
}
@@ -1095,28 +336,10 @@
static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
{
- const char *end;
u8 *data;
size_t data_len;
- EVP_PKEY *pkey;
- const unsigned char *p;
int res;
- X509_PUBKEY *pub = NULL;
- ASN1_OBJECT *ppkalg;
- const unsigned char *pk;
- int ppklen;
- X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20800000L)
- ASN1_OBJECT *pa_oid;
-#else
- const ASN1_OBJECT *pa_oid;
-#endif
- const void *pval;
- int ptype;
- const ASN1_OBJECT *poid;
- char buf[100];
+ const char *end;
end = os_strchr(info, ';');
if (!end)
@@ -1131,101 +354,9 @@
wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
data, data_len);
- if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- os_free(data);
- return -1;
- }
-
- /* DER encoded ASN.1 SubjectPublicKeyInfo
- *
- * SubjectPublicKeyInfo ::= SEQUENCE {
- * algorithm AlgorithmIdentifier,
- * subjectPublicKey BIT STRING }
- *
- * AlgorithmIdentifier ::= SEQUENCE {
- * algorithm OBJECT IDENTIFIER,
- * parameters ANY DEFINED BY algorithm OPTIONAL }
- *
- * subjectPublicKey = compressed format public key per ANSI X9.63
- * algorithm = ecPublicKey (1.2.840.10045.2.1)
- * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
- * prime256v1 (1.2.840.10045.3.1.7)
- */
-
- p = data;
- pkey = d2i_PUBKEY(NULL, &p, data_len);
+ res = dpp_get_subject_public_key(bi, data, data_len);
os_free(data);
-
- if (!pkey) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
- return -1;
- }
-
- if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo does not describe an EC key");
- EVP_PKEY_free(pkey);
- return -1;
- }
-
- res = X509_PUBKEY_set(&pub, pkey);
- if (res != 1) {
- wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
- goto fail;
- }
-
- res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters");
- goto fail;
- }
- res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo algorithm");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
- if (os_strcmp(buf, "id-ecPublicKey") != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo algorithm");
- goto fail;
- }
-
- X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
- if (ptype != V_ASN1_OBJECT) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
- goto fail;
- }
- poid = pval;
- res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
- bi->curve = dpp_get_curve_oid(poid);
- if (!bi->curve) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
- buf);
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
-
- X509_PUBKEY_free(pub);
- bi->pubkey = pkey;
- return 0;
-fail:
- X509_PUBKEY_free(pub);
- EVP_PKEY_free(pkey);
- return -1;
+ return res;
}
@@ -1234,6 +365,7 @@
const char *pos = uri;
const char *end;
const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+ const char *version = NULL;
struct dpp_bootstrap_info *bi;
wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
@@ -1264,6 +396,8 @@
info = pos + 2;
else if (pos[0] == 'K' && pos[1] == ':' && !pk)
pk = pos + 2;
+ else if (pos[0] == 'V' && pos[1] == ':' && !version)
+ version = pos + 2;
else
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: Ignore unrecognized URI parameter",
@@ -1284,6 +418,7 @@
dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
dpp_parse_uri_mac(bi, mac) < 0 ||
dpp_parse_uri_info(bi, info) < 0 ||
+ dpp_parse_uri_version(bi, version) < 0 ||
dpp_parse_uri_pk(bi, pk) < 0) {
dpp_bootstrap_info_free(bi);
bi = NULL;
@@ -1293,462 +428,7 @@
}
-static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
-{
- EC_KEY *eckey;
- BIO *out;
- size_t rlen;
- char *txt;
- int res;
- unsigned char *der = NULL;
- int der_len;
- const EC_GROUP *group;
- const EC_POINT *point;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return;
-
- EVP_PKEY_print_private(out, key, 0, NULL);
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (txt) {
- res = BIO_read(out, txt, rlen);
- if (res > 0) {
- txt[res] = '\0';
- wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
- }
- os_free(txt);
- }
- BIO_free(out);
-
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!eckey)
- return;
-
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (group && point)
- dpp_debug_print_point(title, group, point);
-
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
- OPENSSL_free(der);
- if (der_len <= 0) {
- der = NULL;
- der_len = i2d_EC_PUBKEY(eckey, &der);
- if (der_len > 0)
- wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
- OPENSSL_free(der);
- }
-
- EC_KEY_free(eckey);
-}
-
-
-static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
-{
- EVP_PKEY_CTX *kctx = NULL;
- EC_KEY *ec_params = NULL;
- EVP_PKEY *params = NULL, *key = NULL;
- int nid;
-
- wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
-
- nid = OBJ_txt2nid(curve->name);
- if (nid == NID_undef) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
- return NULL;
- }
-
- ec_params = EC_KEY_new_by_curve_name(nid);
- if (!ec_params) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EC_KEY parameters");
- goto fail;
- }
- EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
- params = EVP_PKEY_new();
- if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EVP_PKEY parameters");
- goto fail;
- }
-
- kctx = EVP_PKEY_CTX_new(params, NULL);
- if (!kctx ||
- EVP_PKEY_keygen_init(kctx) != 1 ||
- EVP_PKEY_keygen(kctx, &key) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
- key = NULL;
- goto fail;
- }
-
- if (wpa_debug_show_keys)
- dpp_debug_print_key("Own generated key", key);
-
-fail:
- EC_KEY_free(ec_params);
- EVP_PKEY_free(params);
- EVP_PKEY_CTX_free(kctx);
- return key;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_name(const char *name)
-{
- int i;
-
- for (i = 0; dpp_curves[i].name; i++) {
- if (os_strcmp(name, dpp_curves[i].name) == 0 ||
- (dpp_curves[i].jwk_crv &&
- os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static const struct dpp_curve_params *
-dpp_get_curve_jwk_crv(const char *name)
-{
- int i;
-
- for (i = 0; dpp_curves[i].name; i++) {
- if (dpp_curves[i].jwk_crv &&
- os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
- const u8 *privkey, size_t privkey_len)
-{
- EVP_PKEY *pkey;
- EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
-
- pkey = EVP_PKEY_new();
- if (!pkey)
- return NULL;
- eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_free(pkey);
- return NULL;
- }
- group = EC_KEY_get0_group(eckey);
- if (!group) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
- nid = EC_GROUP_get_curve_name(group);
- *curve = dpp_get_curve_nid(nid);
- if (!*curve) {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported curve (nid=%d) in pre-assigned key",
- nid);
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
-
- if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
- return pkey;
-}
-
-
-typedef struct {
- /* AlgorithmIdentifier ecPublicKey with optional parameters present
- * as an OID identifying the curve */
- X509_ALGOR *alg;
- /* Compressed format public key per ANSI X9.63 */
- ASN1_BIT_STRING *pub_key;
-} DPP_BOOTSTRAPPING_KEY;
-
-ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
-} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
-
-IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
-
-
-static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
-{
- unsigned char *der = NULL;
- int der_len;
- EC_KEY *eckey;
- struct wpabuf *ret = NULL;
- size_t len;
- const EC_GROUP *group;
- const EC_POINT *point;
- BN_CTX *ctx;
- DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
- int nid;
-
- ctx = BN_CTX_new();
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!ctx || !eckey)
- goto fail;
-
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (!group || !point)
- goto fail;
- dpp_debug_print_point("DPP: bootstrap public key", group, point);
- nid = EC_GROUP_get_curve_name(group);
-
- bootstrap = DPP_BOOTSTRAPPING_KEY_new();
- if (!bootstrap ||
- X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
- V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
- goto fail;
-
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- NULL, 0, ctx);
- if (len == 0)
- goto fail;
-
- der = OPENSSL_malloc(len);
- if (!der)
- goto fail;
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- der, len, ctx);
-
- OPENSSL_free(bootstrap->pub_key->data);
- bootstrap->pub_key->data = der;
- der = NULL;
- bootstrap->pub_key->length = len;
- /* No unused bits */
- bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
- bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
- der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to build DER encoded public key");
- goto fail;
- }
-
- ret = wpabuf_alloc_copy(der, der_len);
-fail:
- DPP_BOOTSTRAPPING_KEY_free(bootstrap);
- OPENSSL_free(der);
- EC_KEY_free(eckey);
- BN_CTX_free(ctx);
- return ret;
-}
-
-
-static int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
-{
- struct wpabuf *der;
- int res;
-
- der = dpp_bootstrap_key_der(bi->pubkey);
- if (!der)
- return -1;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
- der);
- res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
- if (res < 0)
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- wpabuf_free(der);
- return res;
-}
-
-
-static int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
- const u8 *privkey, size_t privkey_len)
-{
- char *base64 = NULL;
- char *pos, *end;
- size_t len;
- struct wpabuf *der = NULL;
-
- if (!curve) {
- bi->curve = &dpp_curves[0];
- } else {
- bi->curve = dpp_get_curve_name(curve);
- if (!bi->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- return -1;
- }
- }
- if (privkey)
- bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
- else
- bi->pubkey = dpp_gen_keypair(bi->curve);
- if (!bi->pubkey)
- goto fail;
- bi->own = 1;
-
- der = dpp_bootstrap_key_der(bi->pubkey);
- if (!der)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
- der);
-
- if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
- goto fail;
- }
-
- base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
- wpabuf_free(der);
- der = NULL;
- if (!base64)
- goto fail;
- pos = base64;
- end = pos + len;
- for (;;) {
- pos = os_strchr(pos, '\n');
- if (!pos)
- break;
- os_memmove(pos, pos + 1, end - pos);
- }
- os_free(bi->pk);
- bi->pk = base64;
- return 0;
-fail:
- os_free(base64);
- wpabuf_free(der);
- return -1;
-}
-
-
-static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
- unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "first intermediate key";
- int res;
-
- /* k1 = HKDF(<>, "first intermediate key", M.x) */
-
- /* HKDF-Extract(<>, M.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
- k1, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
- unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "second intermediate key";
- int res;
-
- /* k2 = HKDF(<>, "second intermediate key", N.x) */
-
- /* HKDF-Extract(<>, N.x) */
- os_memset(salt, 0, hash_len);
- res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
- if (res < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
- k2, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
- unsigned int hash_len)
-{
- size_t nonce_len;
- u8 nonces[2 * DPP_MAX_NONCE_LEN];
- const char *info_ke = "DPP Key";
- u8 prk[DPP_MAX_HASH_LEN];
- int res;
- const u8 *addr[3];
- size_t len[3];
- size_t num_elem = 0;
-
- if (!auth->Mx_len || !auth->Nx_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Mx/Nx not available - cannot derive ke");
- return -1;
- }
-
- /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
-
- /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
- nonce_len = auth->curve->nonce_len;
- os_memcpy(nonces, auth->i_nonce, nonce_len);
- os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
- addr[num_elem] = auth->Mx;
- len[num_elem] = auth->Mx_len;
- num_elem++;
- addr[num_elem] = auth->Nx;
- len[num_elem] = auth->Nx_len;
- num_elem++;
- if (auth->peer_bi && auth->own_bi) {
- if (!auth->Lx_len) {
- wpa_printf(MSG_DEBUG,
- "DPP: Lx not available - cannot derive ke");
- return -1;
- }
- addr[num_elem] = auth->Lx;
- len[num_elem] = auth->secret_len;
- num_elem++;
- }
- res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
- num_elem, addr, len, prk);
- if (res < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
- ke, hash_len);
- return 0;
-}
-
-
-static void dpp_build_attr_status(struct wpabuf *msg,
- enum dpp_status_error status)
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status)
{
wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
wpabuf_put_le16(msg, DPP_ATTR_STATUS);
@@ -1757,8 +437,7 @@
}
-static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
- const u8 *hash)
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash)
{
if (hash) {
wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
@@ -1769,374 +448,6 @@
}
-static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
- const u8 *hash)
-{
- if (hash) {
- wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
- wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
- wpabuf_put_le16(msg, SHA256_MAC_LEN);
- wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
- }
-}
-
-
-static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
- const struct wpabuf *pi,
- size_t nonce_len,
- const u8 *r_pubkey_hash,
- const u8 *i_pubkey_hash,
- unsigned int neg_freq)
-{
- struct wpabuf *msg;
- u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
- u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
- u8 *pos;
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end;
-
- /* Build DPP Authentication Request frame attributes */
- attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
- 4 + sizeof(wrapped_data);
- if (neg_freq > 0)
- attr_len += 4 + 2;
-#ifdef CONFIG_DPP2
- attr_len += 5;
-#endif /* CONFIG_DPP2 */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
- if (!msg)
- return NULL;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
- /* Initiator Protocol Key */
- if (pi) {
- wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pi));
- wpabuf_put_buf(msg, pi);
- }
-
- /* Channel */
- if (neg_freq > 0) {
- u8 op_class, channel;
-
- if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
- &channel) ==
- NUM_HOSTAPD_MODES) {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported negotiation frequency request: %d",
- neg_freq);
- wpabuf_free(msg);
- return NULL;
- }
- wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_u8(msg, op_class);
- wpabuf_put_u8(msg, channel);
- }
-
-#ifdef CONFIG_DPP2
- /* Protocol Version */
- wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, 2);
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Wrapped data ({I-nonce, I-capabilities}k1) */
- pos = clear;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- goto skip_i_nonce;
- }
- if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len - 1);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len - 1);
- pos += nonce_len - 1;
- goto skip_i_nonce;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, auth->i_nonce, nonce_len);
- pos += nonce_len;
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_i_nonce:
- if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
- goto skip_i_capab;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->i_capab = auth->allowed_roles;
- *pos++ = auth->i_capab;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
- wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
- pos[-1] = 0;
- }
-skip_i_capab:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_end = wpabuf_put(msg, 0);
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0) {
- wpabuf_free(msg);
- return NULL;
- }
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Request frame attributes", msg);
-
- return msg;
-}
-
-
-static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
- enum dpp_status_error status,
- const struct wpabuf *pr,
- size_t nonce_len,
- const u8 *r_pubkey_hash,
- const u8 *i_pubkey_hash,
- const u8 *r_nonce, const u8 *i_nonce,
- const u8 *wrapped_r_auth,
- size_t wrapped_r_auth_len,
- const u8 *siv_key)
-{
- struct wpabuf *msg;
-#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
- 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
- u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
- u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
- const u8 *addr[2];
- size_t len[2], siv_len, attr_len;
- u8 *attr_start, *attr_end, *pos;
-
- auth->waiting_auth_conf = 1;
- auth->auth_resp_tries = 0;
-
- /* Build DPP Authentication Response frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
-#ifdef CONFIG_DPP2
- attr_len += 5;
-#endif /* CONFIG_DPP2 */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
- if (!msg)
- return NULL;
-
- attr_start = wpabuf_put(msg, 0);
-
- /* DPP Status */
- if (status != 255)
- dpp_build_attr_status(msg, status);
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash (mutual authentication) */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
- /* Responder Protocol Key */
- if (pr) {
- wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
- wpabuf_put_le16(msg, wpabuf_len(pr));
- wpabuf_put_buf(msg, pr);
- }
-
-#ifdef CONFIG_DPP2
- /* Protocol Version */
- if (auth->peer_version >= 2) {
- wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
- wpabuf_put_le16(msg, 1);
- wpabuf_put_u8(msg, 2);
- }
-#endif /* CONFIG_DPP2 */
-
- attr_end = wpabuf_put(msg, 0);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
- pos = clear;
-
- if (r_nonce) {
- /* R-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, r_nonce, nonce_len);
- pos += nonce_len;
- }
-
- if (i_nonce) {
- /* I-nonce */
- WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
- pos += 2;
- WPA_PUT_LE16(pos, nonce_len);
- pos += 2;
- os_memcpy(pos, i_nonce, nonce_len);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
- pos[nonce_len / 2] ^= 0x01;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- pos += nonce_len;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
- goto skip_r_capab;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* R-capabilities */
- WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
- pos += 2;
- WPA_PUT_LE16(pos, 1);
- pos += 2;
- auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
- DPP_CAPAB_ENROLLEE;
- *pos++ = auth->r_capab;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
- wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
- pos[-1] = 0;
- } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - incompatible R-capabilities");
- if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
- (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
- pos[-1] = 0;
- else
- pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
- DPP_CAPAB_CONFIGURATOR;
- }
-skip_r_capab:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (wrapped_r_auth) {
- /* {R-auth}ke */
- WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
- pos += 2;
- WPA_PUT_LE16(pos, wrapped_r_auth_len);
- pos += 2;
- os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
- pos += wrapped_r_auth_len;
- }
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- siv_len = pos - clear;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
- if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
- 2, addr, len, wrapped_data) < 0) {
- wpabuf_free(msg);
- return NULL;
- }
- siv_len += AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, siv_len);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, siv_len);
- wpabuf_put_data(msg, wrapped_data, siv_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Response frame attributes", msg);
- return msg;
-}
-
-
static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
u16 num_modes, unsigned int freq)
{
@@ -2255,10 +566,9 @@
}
-static int dpp_prepare_channel_list(struct dpp_authentication *auth,
- unsigned int neg_freq,
- struct hostapd_hw_modes *own_modes,
- u16 num_modes)
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes, u16 num_modes)
{
int res;
char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
@@ -2269,6 +579,7 @@
return -1;
auth->num_freq = 1;
auth->freq[0] = neg_freq;
+ auth->curr_freq = neg_freq;
return 0;
}
@@ -2304,7 +615,7 @@
}
-static int dpp_gen_uri(struct dpp_bootstrap_info *bi)
+int dpp_gen_uri(struct dpp_bootstrap_info *bi)
{
char macstr[ETH_ALEN * 2 + 10];
size_t len;
@@ -2320,50 +631,27 @@
len += os_strlen(macstr); /* M:...; */
if (bi->info)
len += 3 + os_strlen(bi->info); /* I:...; */
+#ifdef CONFIG_DPP2
+ len += 4; /* V:2; */
+#endif /* CONFIG_DPP2 */
len += 4 + os_strlen(bi->pk); /* K:...;; */
os_free(bi->uri);
bi->uri = os_malloc(len + 1);
if (!bi->uri)
return -1;
- os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%sK:%s;;",
+ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%sK:%s;;",
bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
bi->chan ? ";" : "",
macstr,
bi->info ? "I:" : "", bi->info ? bi->info : "",
bi->info ? ";" : "",
+ DPP_VERSION == 2 ? "V:2;" : "",
bi->pk);
return 0;
}
-static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
-{
- struct dpp_bootstrap_info *bi;
-
- if (auth->own_bi)
- return 0; /* already generated */
-
- bi = os_zalloc(sizeof(*bi));
- if (!bi)
- return -1;
- bi->type = DPP_BOOTSTRAP_QR_CODE;
- if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
- dpp_gen_uri(bi) < 0)
- goto fail;
- wpa_printf(MSG_DEBUG,
- "DPP: Auto-generated own bootstrapping key info: URI %s",
- bi->uri);
-
- auth->tmp_own_bi = auth->own_bi = bi;
-
- return 0;
-fail:
- dpp_bootstrap_info_free(bi);
- return -1;
-}
-
-
struct dpp_authentication *
dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx)
{
@@ -2379,151 +667,6 @@
}
-struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
- struct dpp_bootstrap_info *peer_bi,
- struct dpp_bootstrap_info *own_bi,
- u8 dpp_allowed_roles,
- unsigned int neg_freq,
- struct hostapd_hw_modes *own_modes,
- u16 num_modes)
-{
- struct dpp_authentication *auth;
- size_t nonce_len;
- size_t secret_len;
- struct wpabuf *pi = NULL;
- const u8 *r_pubkey_hash, *i_pubkey_hash;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- auth = dpp_alloc_auth(dpp, msg_ctx);
- if (!auth)
- return NULL;
- if (peer_bi->configurator_params &&
- dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
- goto fail;
- auth->initiator = 1;
- auth->waiting_auth_resp = 1;
- auth->allowed_roles = dpp_allowed_roles;
- auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
- auth->peer_bi = peer_bi;
- auth->own_bi = own_bi;
- auth->curve = peer_bi->curve;
-
- if (dpp_autogen_bootstrap_key(auth) < 0 ||
- dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_nonce_override_len > 0) {
- wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
- nonce_len = dpp_nonce_override_len;
- os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
- } else {
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->i_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate I-nonce");
- goto fail;
- }
- }
-#else /* CONFIG_TESTING_OPTIONS */
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->i_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_protocol_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override protocol key");
- auth->own_protocol_key = dpp_set_keypair(
- &tmp_curve, dpp_protocol_key_override,
- dpp_protocol_key_override_len);
- } else {
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!auth->own_protocol_key)
- goto fail;
-
- pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (!pi)
- goto fail;
-
- /* ECDH: M = pI * BR */
- if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
- auth->Mx, &secret_len) < 0)
- goto fail;
- auth->secret_len = secret_len;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
- auth->Mx, auth->secret_len);
- auth->Mx_len = auth->secret_len;
-
- if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
- auth->curve->hash_len) < 0)
- goto fail;
-
- r_pubkey_hash = auth->peer_bi->pubkey_hash;
- i_pubkey_hash = auth->own_bi->pubkey_hash;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
- wpabuf_free(pi);
- pi = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
- wpabuf_free(pi);
- pi = wpabuf_alloc(2 * auth->curve->prime_len);
- if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
- neg_freq = 0;
- auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
- i_pubkey_hash, neg_freq);
- if (!auth->req_msg)
- goto fail;
-
-out:
- wpabuf_free(pi);
- return auth;
-fail:
- dpp_auth_deinit(auth);
- auth = NULL;
- goto out;
-}
-
-
static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
const char *json)
{
@@ -2629,7 +772,7 @@
}
-static void dpp_write_adv_proto(struct wpabuf *buf)
+void dpp_write_adv_proto(struct wpabuf *buf)
{
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
@@ -2643,7 +786,7 @@
}
-static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
{
/* GAS Query */
wpabuf_put_le16(buf, wpabuf_len(query));
@@ -2687,6 +830,7 @@
const char *tech = "infra";
const char *dpp_name;
struct wpabuf *buf, *json;
+ char *csr = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
@@ -2703,6 +847,17 @@
len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
if (mud_url && mud_url[0])
len += 10 + os_strlen(mud_url);
+#ifdef CONFIG_DPP2
+ if (auth->csr) {
+ size_t csr_len;
+
+ csr = base64_encode_no_lf(wpabuf_head(auth->csr),
+ wpabuf_len(auth->csr), &csr_len);
+ if (!csr)
+ return NULL;
+ len += 30 + csr_len;
+ }
+#endif /* CONFIG_DPP2 */
json = wpabuf_alloc(len);
if (!json)
return NULL;
@@ -2729,1713 +884,20 @@
wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
json_end_array(json);
}
+ if (csr) {
+ json_value_sep(json);
+ json_add_string(json, "pkcs10", csr);
+ }
json_end_object(json);
buf = dpp_build_conf_req(auth, wpabuf_head(json));
wpabuf_free(json);
+ os_free(csr);
return buf;
}
-static void dpp_auth_success(struct dpp_authentication *auth)
-{
- wpa_printf(MSG_DEBUG,
- "DPP: Authentication success - clear temporary keys");
- os_memset(auth->Mx, 0, sizeof(auth->Mx));
- auth->Mx_len = 0;
- os_memset(auth->Nx, 0, sizeof(auth->Nx));
- auth->Nx_len = 0;
- os_memset(auth->Lx, 0, sizeof(auth->Lx));
- auth->Lx_len = 0;
- os_memset(auth->k1, 0, sizeof(auth->k1));
- os_memset(auth->k2, 0, sizeof(auth->k2));
-
- auth->auth_success = 1;
-}
-
-
-static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
-{
- struct wpabuf *pix, *prx, *bix, *brx;
- const u8 *addr[7];
- size_t len[7];
- size_t i, num_elem = 0;
- size_t nonce_len;
- u8 zero = 0;
- int res = -1;
-
- /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- nonce_len = auth->curve->nonce_len;
-
- if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- else
- bix = NULL;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- } else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- else
- bix = NULL;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- }
- if (!pix || !prx || !brx)
- goto fail;
-
- addr[num_elem] = auth->i_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = auth->r_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(pix);
- len[num_elem] = wpabuf_len(pix) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(prx);
- len[num_elem] = wpabuf_len(prx) / 2;
- num_elem++;
-
- if (bix) {
- addr[num_elem] = wpabuf_head(bix);
- len[num_elem] = wpabuf_len(bix) / 2;
- num_elem++;
- }
-
- addr[num_elem] = wpabuf_head(brx);
- len[num_elem] = wpabuf_len(brx) / 2;
- num_elem++;
-
- addr[num_elem] = &zero;
- len[num_elem] = 1;
- num_elem++;
-
- wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
- for (i = 0; i < num_elem; i++)
- wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
- res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
- if (res == 0)
- wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
- auth->curve->hash_len);
-fail:
- wpabuf_free(pix);
- wpabuf_free(prx);
- wpabuf_free(bix);
- wpabuf_free(brx);
- return res;
-}
-
-
-static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
-{
- struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
- const u8 *addr[7];
- size_t len[7];
- size_t i, num_elem = 0;
- size_t nonce_len;
- u8 one = 1;
- int res = -1;
-
- /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
- nonce_len = auth->curve->nonce_len;
-
- if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- else
- bix = NULL;
- if (!auth->peer_bi)
- goto fail;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- } else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
- else
- bix = NULL;
- if (!auth->own_bi)
- goto fail;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
- }
- if (!pix || !prx || !brx)
- goto fail;
-
- addr[num_elem] = auth->r_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = auth->i_nonce;
- len[num_elem] = nonce_len;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(prx);
- len[num_elem] = wpabuf_len(prx) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(pix);
- len[num_elem] = wpabuf_len(pix) / 2;
- num_elem++;
-
- addr[num_elem] = wpabuf_head(brx);
- len[num_elem] = wpabuf_len(brx) / 2;
- num_elem++;
-
- if (bix) {
- addr[num_elem] = wpabuf_head(bix);
- len[num_elem] = wpabuf_len(bix) / 2;
- num_elem++;
- }
-
- addr[num_elem] = &one;
- len[num_elem] = 1;
- num_elem++;
-
- wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
- for (i = 0; i < num_elem; i++)
- wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
- res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
- if (res == 0)
- wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
- auth->curve->hash_len);
-fail:
- wpabuf_free(pix);
- wpabuf_free(prx);
- wpabuf_free(bix);
- wpabuf_free(brx);
- return res;
-}
-
-
-static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
-{
- const EC_GROUP *group;
- EC_POINT *l = NULL;
- EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
- const EC_POINT *BI_point;
- BN_CTX *bnctx;
- BIGNUM *lx, *sum, *q;
- const BIGNUM *bR_bn, *pR_bn;
- int ret = -1;
-
- /* L = ((bR + pR) modulo q) * BI */
-
- bnctx = BN_CTX_new();
- sum = BN_new();
- q = BN_new();
- lx = BN_new();
- if (!bnctx || !sum || !q || !lx)
- goto fail;
- BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
- if (!BI)
- goto fail;
- BI_point = EC_KEY_get0_public_key(BI);
- group = EC_KEY_get0_group(BI);
- if (!group)
- goto fail;
-
- bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
- pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
- if (!bR || !pR)
- goto fail;
- bR_bn = EC_KEY_get0_private_key(bR);
- pR_bn = EC_KEY_get0_private_key(pR);
- if (!bR_bn || !pR_bn)
- goto fail;
- if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
- BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
- goto fail;
- l = EC_POINT_new(group);
- if (!l ||
- EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
- auth->Lx_len = auth->secret_len;
- ret = 0;
-fail:
- EC_POINT_clear_free(l);
- EC_KEY_free(BI);
- EC_KEY_free(bR);
- EC_KEY_free(pR);
- BN_clear_free(lx);
- BN_clear_free(sum);
- BN_free(q);
- BN_CTX_free(bnctx);
- return ret;
-}
-
-
-static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
-{
- const EC_GROUP *group;
- EC_POINT *l = NULL, *sum = NULL;
- EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
- const EC_POINT *BR_point, *PR_point;
- BN_CTX *bnctx;
- BIGNUM *lx;
- const BIGNUM *bI_bn;
- int ret = -1;
-
- /* L = bI * (BR + PR) */
-
- bnctx = BN_CTX_new();
- lx = BN_new();
- if (!bnctx || !lx)
- goto fail;
- BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
- PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
- if (!BR || !PR)
- goto fail;
- BR_point = EC_KEY_get0_public_key(BR);
- PR_point = EC_KEY_get0_public_key(PR);
-
- bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
- if (!bI)
- goto fail;
- group = EC_KEY_get0_group(bI);
- bI_bn = EC_KEY_get0_private_key(bI);
- if (!group || !bI_bn)
- goto fail;
- sum = EC_POINT_new(group);
- l = EC_POINT_new(group);
- if (!sum || !l ||
- EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
- EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
- auth->Lx_len = auth->secret_len;
- ret = 0;
-fail:
- EC_POINT_clear_free(l);
- EC_POINT_clear_free(sum);
- EC_KEY_free(bI);
- EC_KEY_free(BR);
- EC_KEY_free(PR);
- BN_clear_free(lx);
- BN_CTX_free(bnctx);
- return ret;
-}
-
-
-static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
-{
- size_t nonce_len;
- size_t secret_len;
- struct wpabuf *msg, *pr = NULL;
- u8 r_auth[4 + DPP_MAX_HASH_LEN];
- u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
- size_t wrapped_r_auth_len;
- int ret = -1;
- const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
- enum dpp_status_error status = DPP_STATUS_OK;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
- if (!auth->own_bi)
- return -1;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_nonce_override_len > 0) {
- wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
- nonce_len = dpp_nonce_override_len;
- os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
- } else {
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->r_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate R-nonce");
- goto fail;
- }
- }
-#else /* CONFIG_TESTING_OPTIONS */
- nonce_len = auth->curve->nonce_len;
- if (random_get_bytes(auth->r_nonce, nonce_len)) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
-
- EVP_PKEY_free(auth->own_protocol_key);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_protocol_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override protocol key");
- auth->own_protocol_key = dpp_set_keypair(
- &tmp_curve, dpp_protocol_key_override,
- dpp_protocol_key_override_len);
- } else {
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- auth->own_protocol_key = dpp_gen_keypair(auth->curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!auth->own_protocol_key)
- goto fail;
-
- pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- if (!pr)
- goto fail;
-
- /* ECDH: N = pR * PI */
- if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
- auth->Nx, &secret_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
- auth->Nx, auth->secret_len);
- auth->Nx_len = auth->secret_len;
-
- if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
- auth->curve->hash_len) < 0)
- goto fail;
-
- if (auth->own_bi && auth->peer_bi) {
- /* Mutual authentication */
- if (dpp_auth_derive_l_responder(auth) < 0)
- goto fail;
- }
-
- if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
- goto fail;
-
- /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
- WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
- if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
- goto fail;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
- r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
- if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
- r_auth, 4 + auth->curve->hash_len,
- 0, NULL, NULL, wrapped_r_auth) < 0)
- goto fail;
- wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
- wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
- wrapped_r_auth, wrapped_r_auth_len);
- w_r_auth = wrapped_r_auth;
-
- r_pubkey_hash = auth->own_bi->pubkey_hash;
- if (auth->peer_bi)
- i_pubkey_hash = auth->peer_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
- i_nonce = auth->i_nonce;
- r_nonce = auth->r_nonce;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
- wpabuf_free(pr);
- pr = NULL;
- } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
- wpabuf_free(pr);
- pr = wpabuf_alloc(2 * auth->curve->prime_len);
- if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
- goto fail;
- } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
- w_r_auth = NULL;
- wrapped_r_auth_len = 0;
- } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- status = 255;
- } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 254;
- } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
- r_nonce = NULL;
- } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- i_nonce = NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
- r_pubkey_hash, i_pubkey_hash,
- r_nonce, i_nonce,
- w_r_auth, wrapped_r_auth_len,
- auth->k2);
- if (!msg)
- goto fail;
- wpabuf_free(auth->resp_msg);
- auth->resp_msg = msg;
- ret = 0;
-fail:
- wpabuf_free(pr);
- return ret;
-}
-
-
-static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
- enum dpp_status_error status)
-{
- struct wpabuf *msg;
- const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->own_bi)
- return -1;
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
-
- r_pubkey_hash = auth->own_bi->pubkey_hash;
- if (auth->peer_bi)
- i_pubkey_hash = auth->peer_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
- i_nonce = auth->i_nonce;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- status = 255;
- } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
- i_nonce = NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
- r_pubkey_hash, i_pubkey_hash,
- NULL, i_nonce, NULL, 0, auth->k1);
- if (!msg)
- return -1;
- wpabuf_free(auth->resp_msg);
- auth->resp_msg = msg;
- return 0;
-}
-
-
-struct dpp_authentication *
-dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
- int qr_mutual, struct dpp_bootstrap_info *peer_bi,
- struct dpp_bootstrap_info *own_bi,
- unsigned int freq, const u8 *hdr, const u8 *attr_start,
- size_t attr_len)
-{
- EVP_PKEY *pi = NULL;
- EVP_PKEY_CTX *ctx = NULL;
- size_t secret_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
- *channel;
- u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
- i_bootstrap_len, channel_len;
- struct dpp_authentication *auth = NULL;
-#ifdef CONFIG_DPP2
- const u8 *version;
- u16 version_len;
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Request");
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid required Wrapped Data attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
- wrapped_data, wrapped_data_len);
- attr_len = wrapped_data - 4 - attr_start;
-
- auth = dpp_alloc_auth(dpp, msg_ctx);
- if (!auth)
- goto fail;
- if (peer_bi && peer_bi->configurator_params &&
- dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
- goto fail;
- auth->peer_bi = peer_bi;
- auth->own_bi = own_bi;
- auth->curve = own_bi->curve;
- auth->curr_freq = freq;
-
- auth->peer_version = 1; /* default to the first version */
-#ifdef CONFIG_DPP2
- version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
- &version_len);
- if (version) {
- if (version_len < 1 || version[0] == 0) {
- dpp_auth_fail(auth,
- "Invalid Protocol Version attribute");
- goto fail;
- }
- auth->peer_version = version[0];
- wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
- auth->peer_version);
- }
-#endif /* CONFIG_DPP2 */
-
- channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
- &channel_len);
- if (channel) {
- int neg_freq;
-
- if (channel_len < 2) {
- dpp_auth_fail(auth, "Too short Channel attribute");
- goto fail;
- }
-
- neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
- wpa_printf(MSG_DEBUG,
- "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
- channel[0], channel[1], neg_freq);
- if (neg_freq < 0) {
- dpp_auth_fail(auth,
- "Unsupported Channel attribute value");
- goto fail;
- }
-
- if (auth->curr_freq != (unsigned int) neg_freq) {
- wpa_printf(MSG_DEBUG,
- "DPP: Changing negotiation channel from %u MHz to %u MHz",
- freq, neg_freq);
- auth->curr_freq = neg_freq;
- }
- }
-
- i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
- &i_proto_len);
- if (!i_proto) {
- dpp_auth_fail(auth,
- "Missing required Initiator Protocol Key attribute");
- goto fail;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
- i_proto, i_proto_len);
-
- /* M = bR * PI */
- pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
- if (!pi) {
- dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
- goto fail;
- }
- dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
-
- if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
- goto fail;
- auth->secret_len = secret_len;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
- auth->Mx, auth->secret_len);
- auth->Mx_len = auth->secret_len;
-
- if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
- auth->curve->hash_len) < 0)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
-
- i_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_I_CAPABILITIES,
- &i_capab_len);
- if (!i_capab || i_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid I-capabilities");
- goto fail;
- }
- auth->i_capab = i_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
-
- bin_clear_free(unwrapped, unwrapped_len);
- unwrapped = NULL;
-
- switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
- case DPP_CAPAB_ENROLLEE:
- if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Configurator role");
- goto not_compatible;
- }
- wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
- auth->configurator = 1;
- break;
- case DPP_CAPAB_CONFIGURATOR:
- if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Enrollee role");
- goto not_compatible;
- }
- wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
- auth->configurator = 0;
- break;
- case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
- if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
- wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
- auth->configurator = 0;
- } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
- wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
- auth->configurator = 1;
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Local policy does not allow Configurator/Enrollee role");
- goto not_compatible;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
- auth->i_capab & DPP_CAPAB_ROLE_MASK);
- goto fail;
- }
-
- auth->peer_protocol_key = pi;
- pi = NULL;
- if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
- char hex[SHA256_MAC_LEN * 2 + 1];
-
- wpa_printf(MSG_DEBUG,
- "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
- if (dpp_auth_build_resp_status(auth,
- DPP_STATUS_RESPONSE_PENDING) < 0)
- goto fail;
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
- auth->response_pending = 1;
- os_memcpy(auth->waiting_pubkey_hash,
- i_bootstrap, i_bootstrap_len);
- wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
- i_bootstrap_len);
- } else {
- hex[0] = '\0';
- }
-
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
- "%s", hex);
- return auth;
- }
- if (dpp_auth_build_resp_ok(auth) < 0)
- goto fail;
-
- return auth;
-
-not_compatible:
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
- "i-capab=0x%02x", auth->i_capab);
- if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
- auth->configurator = 1;
- else
- auth->configurator = 0;
- auth->peer_protocol_key = pi;
- pi = NULL;
- if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
- goto fail;
-
- auth->remove_on_tx_status = 1;
- return auth;
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- EVP_PKEY_free(pi);
- EVP_PKEY_CTX_free(ctx);
- dpp_auth_deinit(auth);
- return NULL;
-}
-
-
-int dpp_notify_new_qr_code(struct dpp_authentication *auth,
- struct dpp_bootstrap_info *peer_bi)
-{
- if (!auth || !auth->response_pending ||
- os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0)
- return 0;
-
- wpa_printf(MSG_DEBUG,
- "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
- MACSTR, MAC2STR(auth->peer_mac_addr));
- auth->peer_bi = peer_bi;
-
- if (dpp_auth_build_resp_ok(auth) < 0)
- return -1;
-
- return 1;
-}
-
-
-static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
- enum dpp_status_error status)
-{
- struct wpabuf *msg;
- u8 i_auth[4 + DPP_MAX_HASH_LEN];
- size_t i_auth_len;
- u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
- size_t r_nonce_len;
- const u8 *addr[2];
- size_t len[2], attr_len;
- u8 *wrapped_i_auth;
- u8 *wrapped_r_nonce;
- u8 *attr_start, *attr_end;
- const u8 *r_pubkey_hash, *i_pubkey_hash;
-#ifdef CONFIG_TESTING_OPTIONS
- u8 test_hash[SHA256_MAC_LEN];
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
-
- i_auth_len = 4 + auth->curve->hash_len;
- r_nonce_len = 4 + auth->curve->nonce_len;
- /* Build DPP Authentication Confirmation frame attributes */
- attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
- 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
- if (!msg)
- goto fail;
-
- attr_start = wpabuf_put(msg, 0);
-
- r_pubkey_hash = auth->peer_bi->pubkey_hash;
- if (auth->own_bi)
- i_pubkey_hash = auth->own_bi->pubkey_hash;
- else
- i_pubkey_hash = NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- goto skip_status;
- } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 254;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* DPP Status */
- dpp_build_attr_status(msg, status);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_status:
- if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
- r_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid R-Bootstrap Key Hash");
- os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- r_pubkey_hash = test_hash;
- } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
- i_pubkey_hash = NULL;
- } else if (dpp_test ==
- DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - invalid I-Bootstrap Key Hash");
- if (i_pubkey_hash)
- os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
- else
- os_memset(test_hash, 0, SHA256_MAC_LEN);
- test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
- i_pubkey_hash = test_hash;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Responder Bootstrapping Key Hash */
- dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
-
- /* Initiator Bootstrapping Key Hash (mutual authentication) */
- dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
- goto skip_wrapped_data;
- if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
- i_auth_len = 0;
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_end = wpabuf_put(msg, 0);
-
- /* OUI, OUI type, Crypto Suite, DPP frame type */
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = 3 + 1 + 1 + 1;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
- /* Attributes before Wrapped Data */
- addr[1] = attr_start;
- len[1] = attr_end - attr_start;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (status == DPP_STATUS_OK) {
- /* I-auth wrapped with ke */
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
- wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
- goto skip_i_auth;
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
- * 1) */
- WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
- WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
- if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
- i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
- }
-skip_i_auth:
-#endif /* CONFIG_TESTING_OPTIONS */
- if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
- i_auth, i_auth_len,
- 2, addr, len, wrapped_i_auth) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
- wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
- } else {
- /* R-nonce wrapped with k2 */
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
- wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
-
- WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
- WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
- os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
-
- if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
- r_nonce, r_nonce_len,
- 2, addr, len, wrapped_r_nonce) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
- wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- wpa_hexdump_buf(MSG_DEBUG,
- "DPP: Authentication Confirmation frame attributes",
- msg);
- if (status == DPP_STATUS_OK)
- dpp_auth_success(auth);
-
- return msg;
-
-fail:
- wpabuf_free(msg);
- return NULL;
-}
-
-
-static void
-dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len,
- const u8 *wrapped_data, u16 wrapped_data_len,
- enum dpp_status_error status)
-{
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *i_nonce, *r_capab;
- u16 i_nonce_len, r_capab_len;
-
- if (status == DPP_STATUS_NOT_COMPATIBLE) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported incompatible roles");
- } else if (status == DPP_STATUS_RESPONSE_PENDING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported more time needed");
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder reported failure (status %d)",
- status);
- dpp_auth_fail(auth, "Responder reported failure");
- return;
- }
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- dpp_auth_fail(auth, "I-nonce mismatch");
- goto fail;
- }
-
- r_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_R_CAPABILITIES,
- &r_capab_len);
- if (!r_capab || r_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid R-capabilities");
- goto fail;
- }
- auth->r_capab = r_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
- if (status == DPP_STATUS_NOT_COMPATIBLE) {
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
- "r-capab=0x%02x", auth->r_capab);
- } else if (status == DPP_STATUS_RESPONSE_PENDING) {
- u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
-
- if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
- (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
- role);
- } else {
- wpa_printf(MSG_DEBUG,
- "DPP: Continue waiting for full DPP Authentication Response");
- wpa_msg(auth->msg_ctx, MSG_INFO,
- DPP_EVENT_RESPONSE_PENDING "%s",
- auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
- }
- }
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
-}
-
-
-struct wpabuf *
-dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len)
-{
- EVP_PKEY *pr;
- size_t secret_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL, *unwrapped2 = NULL;
- size_t unwrapped_len = 0, unwrapped2_len = 0;
- const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
- *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
- u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
- r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
- wrapped2_len, r_auth_len;
- u8 r_auth2[DPP_MAX_HASH_LEN];
- u8 role;
-#ifdef CONFIG_DPP2
- const u8 *version;
- u16 version_len;
-#endif /* CONFIG_DPP2 */
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Response");
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->initiator || !auth->peer_bi) {
- dpp_auth_fail(auth, "Unexpected Authentication Response");
- return NULL;
- }
-
- auth->waiting_auth_resp = 0;
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid required Wrapped Data attribute");
- return NULL;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
- wrapped_data, wrapped_data_len);
-
- attr_len = wrapped_data - 4 - attr_start;
-
- r_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return NULL;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Unexpected Responder Bootstrapping Key Hash value");
- wpa_hexdump(MSG_DEBUG,
- "DPP: Expected Responder Bootstrapping Key Hash",
- auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
- return NULL;
- }
-
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap) {
- if (i_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Invalid Initiator Bootstrapping Key Hash attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP,
- "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
- if (!auth->own_bi ||
- os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Initiator Bootstrapping Key Hash attribute did not match");
- return NULL;
- }
- } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
- /* PKEX bootstrapping mandates use of mutual authentication */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return NULL;
- } else if (auth->own_bi &&
- auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
- auth->own_bi->nfc_negotiated) {
- /* NFC negotiated connection handover bootstrapping mandates
- * use of mutual authentication */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return NULL;
- }
-
- auth->peer_version = 1; /* default to the first version */
-#ifdef CONFIG_DPP2
- version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
- &version_len);
- if (version) {
- if (version_len < 1 || version[0] == 0) {
- dpp_auth_fail(auth,
- "Invalid Protocol Version attribute");
- return NULL;
- }
- auth->peer_version = version[0];
- wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
- auth->peer_version);
- }
-#endif /* CONFIG_DPP2 */
-
- status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
- &status_len);
- if (!status || status_len < 1) {
- dpp_auth_fail(auth,
- "Missing or invalid required DPP Status attribute");
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
- auth->auth_resp_status = status[0];
- if (status[0] != DPP_STATUS_OK) {
- dpp_auth_resp_rx_status(auth, hdr, attr_start,
- attr_len, wrapped_data,
- wrapped_data_len, status[0]);
- return NULL;
- }
-
- if (!i_bootstrap && auth->own_bi) {
- wpa_printf(MSG_DEBUG,
- "DPP: Responder decided not to use mutual authentication");
- auth->own_bi = NULL;
- }
-
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
- auth->own_bi != NULL);
-
- r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
- &r_proto_len);
- if (!r_proto) {
- dpp_auth_fail(auth,
- "Missing required Responder Protocol Key attribute");
- return NULL;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
- r_proto, r_proto_len);
-
- /* N = pI * PR */
- pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
- if (!pr) {
- dpp_auth_fail(auth, "Invalid Responder Protocol Key");
- return NULL;
- }
- dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
-
- if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
- dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
- goto fail;
- }
- EVP_PKEY_free(auth->peer_protocol_key);
- auth->peer_protocol_key = pr;
- pr = NULL;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
- auth->Nx, auth->secret_len);
- auth->Nx_len = auth->secret_len;
-
- if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
- auth->curve->hash_len) < 0)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
- if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
- &r_nonce_len);
- if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
- os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
-
- i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
- &i_nonce_len);
- if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "Missing or invalid I-nonce");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
- if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
- dpp_auth_fail(auth, "I-nonce mismatch");
- goto fail;
- }
-
- if (auth->own_bi) {
- /* Mutual authentication */
- if (dpp_auth_derive_l_initiator(auth) < 0)
- goto fail;
- }
-
- r_capab = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_R_CAPABILITIES,
- &r_capab_len);
- if (!r_capab || r_capab_len < 1) {
- dpp_auth_fail(auth, "Missing or invalid R-capabilities");
- goto fail;
- }
- auth->r_capab = r_capab[0];
- wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
- role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
- if ((auth->allowed_roles ==
- (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
- (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
- /* Peer selected its role, so move from "either role" to the
- * role that is compatible with peer's selection. */
- auth->configurator = role == DPP_CAPAB_ENROLLEE;
- wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
- auth->configurator ? "Configurator" : "Enrollee");
- } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
- (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
- wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
- wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Unexpected role in R-capabilities 0x%02x",
- role);
- if (role != DPP_CAPAB_ENROLLEE &&
- role != DPP_CAPAB_CONFIGURATOR)
- goto fail;
- bin_clear_free(unwrapped, unwrapped_len);
- auth->remove_on_tx_status = 1;
- return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
- }
-
- wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
- DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
- if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid Secondary Wrapped Data");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped2, wrapped2_len);
-
- if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
- goto fail;
-
- unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
- unwrapped2 = os_malloc(unwrapped2_len);
- if (!unwrapped2)
- goto fail;
- if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
- wrapped2, wrapped2_len,
- 0, NULL, NULL, unwrapped2) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped2, unwrapped2_len);
-
- if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
- dpp_auth_fail(auth,
- "Invalid attribute in secondary unwrapped data");
- goto fail;
- }
-
- r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
- &r_auth_len);
- if (!r_auth || r_auth_len != auth->curve->hash_len) {
- dpp_auth_fail(auth,
- "Missing or invalid Responder Authenticating Tag");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
- r_auth, r_auth_len);
- /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
- if (dpp_gen_r_auth(auth, r_auth2) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
- r_auth2, r_auth_len);
- if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
- dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
- auth->remove_on_tx_status = 1;
- return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
- }
-
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - Authentication Response in place of Confirm");
- if (dpp_auth_build_resp_ok(auth) < 0)
- return NULL;
- return wpabuf_dup(auth->resp_msg);
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- return dpp_auth_build_conf(auth, DPP_STATUS_OK);
-
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- bin_clear_free(unwrapped2, unwrapped2_len);
- EVP_PKEY_free(pr);
- return NULL;
-}
-
-
-static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
- const u8 *hdr,
- const u8 *attr_start, size_t attr_len,
- const u8 *wrapped_data,
- u16 wrapped_data_len,
- enum dpp_status_error status)
-{
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- const u8 *r_nonce;
- u16 r_nonce_len;
-
- /* Authentication Confirm failure cases are expected to include
- * {R-nonce}k2 in the Wrapped Data attribute. */
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped) {
- dpp_auth_fail(auth, "Authentication failed");
- goto fail;
- }
- if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
- &r_nonce_len);
- if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
- dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
- goto fail;
- }
- if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
- wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
- r_nonce, r_nonce_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
- auth->r_nonce, r_nonce_len);
- dpp_auth_fail(auth, "R-nonce mismatch");
- goto fail;
- }
-
- if (status == DPP_STATUS_NOT_COMPATIBLE)
- dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
- else if (status == DPP_STATUS_AUTH_FAILURE)
- dpp_auth_fail(auth, "Peer reported authentication failure)");
-
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- return -1;
-}
-
-
-int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
- const u8 *attr_start, size_t attr_len)
-{
- const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
- u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
- i_auth_len;
- const u8 *addr[2];
- size_t len[2];
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- u8 i_auth2[DPP_MAX_HASH_LEN];
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Confirm");
- return -1;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf) {
- wpa_printf(MSG_DEBUG,
- "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
- auth->initiator, !!auth->own_bi,
- auth->waiting_auth_conf);
- dpp_auth_fail(auth, "Unexpected Authentication Confirm");
- return -1;
- }
-
- auth->waiting_auth_conf = 0;
-
- wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_auth_fail(auth,
- "Missing or invalid required Wrapped Data attribute");
- return -1;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
- wrapped_data, wrapped_data_len);
-
- attr_len = wrapped_data - 4 - attr_start;
-
- r_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Expected Responder Bootstrapping Key Hash",
- auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
- dpp_auth_fail(auth,
- "Responder Bootstrapping Key Hash mismatch");
- return -1;
- }
-
- i_bootstrap = dpp_get_attr(attr_start, attr_len,
- DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (i_bootstrap) {
- if (i_bootstrap_len != SHA256_MAC_LEN) {
- dpp_auth_fail(auth,
- "Invalid Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP,
- "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
- if (!auth->peer_bi ||
- os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
- SHA256_MAC_LEN) != 0) {
- dpp_auth_fail(auth,
- "Initiator Bootstrapping Key Hash mismatch");
- return -1;
- }
- } else if (auth->peer_bi) {
- /* Mutual authentication and peer did not include its
- * Bootstrapping Key Hash attribute. */
- dpp_auth_fail(auth,
- "Missing Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
-
- status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
- &status_len);
- if (!status || status_len < 1) {
- dpp_auth_fail(auth,
- "Missing or invalid required DPP Status attribute");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
- if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
- status[0] == DPP_STATUS_AUTH_FAILURE)
- return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
- attr_len, wrapped_data,
- wrapped_data_len, status[0]);
-
- if (status[0] != DPP_STATUS_OK) {
- dpp_auth_fail(auth, "Authentication failed");
- return -1;
- }
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- addr[1] = attr_start;
- len[1] = attr_len;
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- return -1;
- if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_auth_fail(auth, "AES-SIV decryption failed");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
- &i_auth_len);
- if (!i_auth || i_auth_len != auth->curve->hash_len) {
- dpp_auth_fail(auth,
- "Missing or invalid Initiator Authenticating Tag");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
- i_auth, i_auth_len);
- /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
- if (dpp_gen_i_auth(auth, i_auth2) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
- i_auth2, i_auth_len);
- if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
- dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
- goto fail;
- }
-
- bin_clear_free(unwrapped, unwrapped_len);
- dpp_auth_success(auth);
- return 0;
-fail:
- bin_clear_free(unwrapped, unwrapped_len);
- return -1;
-}
-
-
static int bin_str_eq(const char *val, size_t len, const char *cmp)
{
return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
@@ -4473,6 +935,8 @@
conf->akm = DPP_AKM_PSK_SAE_DPP;
else if (bin_str_eq(type, len, "dpp"))
conf->akm = DPP_AKM_DPP;
+ else if (bin_str_eq(type, len, "dot1x"))
+ conf->akm = DPP_AKM_DOT1X;
else
goto fail;
@@ -4535,6 +999,7 @@
return;
str_clear_free(conf->passphrase);
os_free(conf->group_id);
+ os_free(conf->csrattrs);
bin_clear_free(conf, sizeof(*conf));
}
@@ -4545,6 +1010,7 @@
const char *pos, *end;
struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
struct dpp_configuration *conf = NULL;
+ size_t len;
pos = os_strstr(cmd, " conf=sta-");
if (pos) {
@@ -4649,6 +1115,17 @@
conf->netaccesskey_expiry = val;
}
+ pos = os_strstr(cmd, " csrattrs=");
+ if (pos) {
+ pos += 10;
+ end = os_strchr(pos, ' ');
+ len = end ? (size_t) (end - pos) : os_strlen(pos);
+ conf->csrattrs = os_zalloc(len + 1);
+ if (!conf->csrattrs)
+ goto fail;
+ os_memcpy(conf->csrattrs, pos, len);
+ }
+
if (!dpp_configuration_valid(conf))
goto fail;
@@ -4747,7 +1224,7 @@
wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
pos = os_strstr(cmd, " configurator=");
- if (pos) {
+ if (!auth->conf && pos) {
pos += 14;
auth->conf = dpp_configurator_get_id(auth->global, atoi(pos));
if (!auth->conf) {
@@ -4781,20 +1258,6 @@
}
-static void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
-{
- while (key) {
- struct dpp_asymmetric_key *next = key->next;
-
- EVP_PKEY_free(key->csign);
- str_clear_free(key->config_template);
- str_clear_free(key->connector_template);
- os_free(key);
- key = next;
- }
-}
-
-
void dpp_auth_deinit(struct dpp_authentication *auth)
{
unsigned int i;
@@ -4807,18 +1270,39 @@
dpp_configuration_free(auth->conf2_sta);
EVP_PKEY_free(auth->own_protocol_key);
EVP_PKEY_free(auth->peer_protocol_key);
+ EVP_PKEY_free(auth->reconfig_old_protocol_key);
wpabuf_free(auth->req_msg);
wpabuf_free(auth->resp_msg);
wpabuf_free(auth->conf_req);
+ wpabuf_free(auth->reconfig_req_msg);
+ wpabuf_free(auth->reconfig_resp_msg);
for (i = 0; i < auth->num_conf_obj; i++) {
struct dpp_config_obj *conf = &auth->conf_obj[i];
os_free(conf->connector);
wpabuf_free(conf->c_sign_key);
+ wpabuf_free(conf->certbag);
+ wpabuf_free(conf->certs);
+ wpabuf_free(conf->cacert);
+ os_free(conf->server_name);
+ wpabuf_free(conf->pp_key);
}
+#ifdef CONFIG_DPP2
dpp_free_asymmetric_key(auth->conf_key_pkg);
+ os_free(auth->csrattrs);
+ wpabuf_free(auth->csr);
+ wpabuf_free(auth->priv_key);
+ wpabuf_free(auth->cacert);
+ wpabuf_free(auth->certbag);
+ os_free(auth->trusted_eap_server_name);
+ wpabuf_free(auth->conf_resp_tcp);
+#endif /* CONFIG_DPP2 */
wpabuf_free(auth->net_access_key);
dpp_bootstrap_info_free(auth->tmp_own_bi);
+ if (auth->tmp_peer_bi) {
+ dl_list_del(&auth->tmp_peer_bi->list);
+ dpp_bootstrap_info_free(auth->tmp_peer_bi);
+ }
#ifdef CONFIG_TESTING_OPTIONS
os_free(auth->config_obj_override);
os_free(auth->discovery_override);
@@ -4876,8 +1360,8 @@
}
-static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
- const char *kid, const struct dpp_curve_params *curve)
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve)
{
struct wpabuf *pub;
const u8 *pos;
@@ -4948,20 +1432,10 @@
struct dpp_configuration *conf)
{
struct wpabuf *buf = NULL;
- char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ char *signed_conn = NULL;
size_t tailroom;
const struct dpp_curve_params *curve;
- struct wpabuf *jws_prot_hdr;
- size_t signed1_len, signed2_len, signed3_len;
struct wpabuf *dppcon = NULL;
- unsigned char *signature = NULL;
- const unsigned char *p;
- size_t signature_len;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
- char *dot = ".";
- const EVP_MD *sign_md;
- const BIGNUM *r, *s;
size_t extra_len = 1000;
int incl_legacy;
enum dpp_akm akm;
@@ -4973,16 +1447,6 @@
goto fail;
}
curve = auth->conf->curve;
- if (curve->hash_len == SHA256_MAC_LEN) {
- sign_md = EVP_sha256();
- } else if (curve->hash_len == SHA384_MAC_LEN) {
- sign_md = EVP_sha384();
- } else if (curve->hash_len == SHA512_MAC_LEN) {
- sign_md = EVP_sha512();
- } else {
- wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
- goto fail;
- }
akm = conf->akm;
if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -5030,7 +1494,8 @@
#ifdef CONFIG_TESTING_OPTIONS
skip_groups:
#endif /* CONFIG_TESTING_OPTIONS */
- if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+ if (!auth->peer_protocol_key ||
+ dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
auth->curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
goto fail;
@@ -5055,81 +1520,25 @@
wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
(const char *) wpabuf_head(dppcon));
- jws_prot_hdr = wpabuf_alloc(100);
- if (!jws_prot_hdr)
- goto fail;
- json_start_object(jws_prot_hdr, NULL);
- json_add_string(jws_prot_hdr, "typ", "dppCon");
- json_value_sep(jws_prot_hdr);
- json_add_string(jws_prot_hdr, "kid", auth->conf->kid);
- json_value_sep(jws_prot_hdr);
- json_add_string(jws_prot_hdr, "alg", curve->jws_alg);
- json_end_object(jws_prot_hdr);
- signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
- wpabuf_len(jws_prot_hdr),
- &signed1_len);
- wpabuf_free(jws_prot_hdr);
- signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
- &signed2_len);
- if (!signed1 || !signed2)
- goto fail;
-
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
- goto fail;
-
- ERR_clear_error();
- if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
- auth->conf->csign) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
- EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
- EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- signature = os_malloc(signature_len);
- if (!signature)
- goto fail;
- if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
- signature, signature_len);
- /* Convert to raw coordinates r,s */
- p = signature;
- sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
- if (!sig)
- goto fail;
- ECDSA_SIG_get0(sig, &r, &s);
- if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(s, signature + curve->prime_len,
- curve->prime_len) < 0)
- goto fail;
- signature_len = 2 * curve->prime_len;
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
- signature, signature_len);
- signed3 = base64_url_encode(signature, signature_len, &signed3_len);
- if (!signed3)
+ signed_conn = dpp_sign_connector(auth->conf, dppcon);
+ if (!signed_conn)
goto fail;
incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
tailroom = 1000;
tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
- tailroom += signed1_len + signed2_len + signed3_len;
+ tailroom += os_strlen(signed_conn);
if (incl_legacy)
tailroom += 1000;
+ if (akm == DPP_AKM_DOT1X) {
+ if (auth->certbag)
+ tailroom += 2 * wpabuf_len(auth->certbag);
+ if (auth->cacert)
+ tailroom += 2 * wpabuf_len(auth->cacert);
+ if (auth->trusted_eap_server_name)
+ tailroom += os_strlen(auth->trusted_eap_server_name);
+ tailroom += 1000;
+ }
buf = dpp_build_conf_start(auth, conf, tailroom);
if (!buf)
goto fail;
@@ -5145,12 +1554,32 @@
dpp_build_legacy_cred_params(buf, conf);
json_value_sep(buf);
}
+ if (akm == DPP_AKM_DOT1X) {
+ json_start_object(buf, "entCreds");
+ if (!auth->certbag)
+ goto fail;
+ json_add_base64(buf, "certBag", wpabuf_head(auth->certbag),
+ wpabuf_len(auth->certbag));
+ if (auth->cacert) {
+ json_value_sep(buf);
+ json_add_base64(buf, "caCert",
+ wpabuf_head(auth->cacert),
+ wpabuf_len(auth->cacert));
+ }
+ if (auth->trusted_eap_server_name) {
+ json_value_sep(buf);
+ json_add_string(buf, "trustedEapServerName",
+ auth->trusted_eap_server_name);
+ }
+ json_value_sep(buf);
+ json_start_array(buf, "eapMethods");
+ wpabuf_printf(buf, "%d", EAP_TYPE_TLS);
+ json_end_array(buf);
+ json_end_object(buf);
+ json_value_sep(buf);
+ }
wpabuf_put_str(buf, "\"signedConnector\":\"");
- wpabuf_put_str(buf, signed1);
- wpabuf_put_u8(buf, '.');
- wpabuf_put_str(buf, signed2);
- wpabuf_put_u8(buf, '.');
- wpabuf_put_str(buf, signed3);
+ wpabuf_put_str(buf, signed_conn);
wpabuf_put_str(buf, "\"");
json_value_sep(buf);
if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
@@ -5158,6 +1587,16 @@
wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
goto fail;
}
+#ifdef CONFIG_DPP2
+ if (auth->peer_version >= 2 && auth->conf->pp_key) {
+ json_value_sep(buf);
+ if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL,
+ curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK");
+ goto fail;
+ }
+ }
+#endif /* CONFIG_DPP2 */
json_end_object(buf);
json_end_object(buf);
@@ -5166,12 +1605,7 @@
wpabuf_head(buf), wpabuf_len(buf));
out:
- EVP_MD_CTX_destroy(md_ctx);
- ECDSA_SIG_free(sig);
- os_free(signed1);
- os_free(signed2);
- os_free(signed3);
- os_free(signature);
+ os_free(signed_conn);
wpabuf_free(dppcon);
return buf;
fail:
@@ -5213,7 +1647,7 @@
static struct wpabuf *
dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
- int idx)
+ int idx, bool cert_req)
{
struct dpp_configuration *conf = NULL;
@@ -5246,497 +1680,28 @@
return NULL;
}
+ if (conf->akm == DPP_AKM_DOT1X) {
+ if (!auth->conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Configurator data available");
+ return NULL;
+ }
+ if (!cert_req && !auth->certbag) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No certificate data available for dot1x configuration");
+ return NULL;
+ }
+ return dpp_build_conf_obj_dpp(auth, conf);
+ }
if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf))
return dpp_build_conf_obj_dpp(auth, conf);
return dpp_build_conf_obj_legacy(auth, conf);
}
-#ifdef CONFIG_DPP2
-
-static struct wpabuf * dpp_build_conf_params(void)
-{
- struct wpabuf *buf;
- size_t len;
- /* TODO: proper template values */
- const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
- const char *connector_template = NULL;
-
- len = 100 + os_strlen(conf_template);
- if (connector_template)
- len += os_strlen(connector_template);
- buf = wpabuf_alloc(len);
- if (!buf)
- return NULL;
-
- /*
- * DPPConfigurationParameters ::= SEQUENCE {
- * configurationTemplate UTF8String,
- * connectorTemplate UTF8String OPTIONAL}
- */
-
- asn1_put_utf8string(buf, conf_template);
- if (connector_template)
- asn1_put_utf8string(buf, connector_template);
- return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_attribute(void)
-{
- struct wpabuf *conf_params, *attr;
-
- /*
- * aa-DPPConfigurationParameters ATTRIBUTE ::=
- * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF Type
- */
- conf_params = dpp_build_conf_params();
- conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
- ASN1_TAG_SET);
- if (!conf_params)
- return NULL;
-
- attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
- if (!attr) {
- wpabuf_clear_free(conf_params);
- return NULL;
- }
-
- asn1_put_oid(attr, &asn1_dpp_config_params_oid);
- wpabuf_put_buf(attr, conf_params);
- wpabuf_clear_free(conf_params);
-
- return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
-{
- const struct asn1_oid *oid;
- struct wpabuf *params, *res;
-
- switch (curve->ike_group) {
- case 19:
- oid = &asn1_prime256v1_oid;
- break;
- case 20:
- oid = &asn1_secp384r1_oid;
- break;
- case 21:
- oid = &asn1_secp521r1_oid;
- break;
- case 28:
- oid = &asn1_brainpoolP256r1_oid;
- break;
- case 29:
- oid = &asn1_brainpoolP384r1_oid;
- break;
- case 30:
- oid = &asn1_brainpoolP512r1_oid;
- break;
- default:
- return NULL;
- }
-
- params = wpabuf_alloc(20);
- if (!params)
- return NULL;
- asn1_put_oid(params, oid); /* namedCurve */
-
- res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
- wpabuf_free(params);
- return res;
-}
-
-
-static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
-{
- struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
- EC_KEY *eckey;
- unsigned char *der = NULL;
- int der_len;
-
- eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
- if (!eckey)
- return NULL;
-
- EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- priv_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
-
- alg = dpp_build_key_alg(auth->conf->curve);
-
- /* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
- attr = dpp_build_attribute();
- attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
- if (!priv_key || !attr || !alg)
- goto fail;
-
- /*
- * OneAsymmetricKey ::= SEQUENCE {
- * version Version,
- * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
- * privateKey PrivateKey,
- * attributes [0] Attributes OPTIONAL,
- * ...,
- * [[2: publicKey [1] BIT STRING OPTIONAL ]],
- * ...
- * }
- */
-
- key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
- wpabuf_len(attr));
- if (!key)
- goto fail;
-
- asn1_put_integer(key, 1); /* version = v2(1) */
-
- /* PrivateKeyAlgorithmIdentifier */
- wpabuf_put_buf(key, alg);
-
- /* PrivateKey ::= OCTET STRING */
- asn1_put_octet_string(key, priv_key);
-
- /* [0] Attributes OPTIONAL */
- asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
- wpabuf_put_buf(key, attr);
-
-fail:
- wpabuf_clear_free(attr);
- wpabuf_clear_free(priv_key);
- wpabuf_free(alg);
-
- /*
- * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
- *
- * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
- *
- * OneAsymmetricKey ::= SEQUENCE
- */
- return asn1_encaps(asn1_encaps(key,
- ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
- ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
- size_t hash_len)
-{
- struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
- const struct asn1_oid *oid;
-
- /*
- * PBKDF2-params ::= SEQUENCE {
- * salt CHOICE {
- * specified OCTET STRING,
- * otherSource AlgorithmIdentifier}
- * iterationCount INTEGER (1..MAX),
- * keyLength INTEGER (1..MAX),
- * prf AlgorithmIdentifier}
- *
- * salt is an 64 octet value, iterationCount is 1000, keyLength is based
- * on Configurator signing key length, prf is
- * id-hmacWithSHA{256,384,512} based on Configurator signing key.
- */
-
- if (hash_len == 32)
- oid = &asn1_pbkdf2_hmac_sha256_oid;
- else if (hash_len == 48)
- oid = &asn1_pbkdf2_hmac_sha384_oid;
- else if (hash_len == 64)
- oid = &asn1_pbkdf2_hmac_sha512_oid;
- else
- goto fail;
- prf = asn1_build_alg_id(oid, NULL);
- if (!prf)
- goto fail;
- params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
- if (!params)
- goto fail;
- asn1_put_octet_string(params, salt); /* salt.specified */
- asn1_put_integer(params, 1000); /* iterationCount */
- asn1_put_integer(params, hash_len); /* keyLength */
- wpabuf_put_buf(params, prf);
- params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
- if (!params)
- goto fail;
- buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
-fail:
- wpabuf_free(params);
- wpabuf_free(prf);
- return buf;
-}
-
-
-static struct wpabuf *
-dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
- *key_enc_alg = NULL, *salt;
- u8 kek[DPP_MAX_HASH_LEN];
- const u8 *key;
- size_t key_len;
-
- salt = wpabuf_alloc(64);
- if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
-
- /* TODO: For initial testing, use ke as the key. Replace this with a
- * new key once that has been defined. */
- key = auth->ke;
- key_len = auth->curve->hash_len;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
- if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
- kek, hash_len)) {
- wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
- goto fail;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
- kek, hash_len);
-
- enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
- if (!enc_key ||
- aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
- wpabuf_len(cont_enc_key), 0, NULL, NULL,
- wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
- goto fail;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
-
- /*
- * PasswordRecipientInfo ::= SEQUENCE {
- * version CMSVersion,
- * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
- * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
- * encryptedKey EncryptedKey}
- *
- * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
- * parameters contains PBKDF2-params SEQUENCE.
- */
-
- key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
- key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
- if (!key_der_alg || !key_enc_alg)
- goto fail;
- pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
- wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
- if (!pwri)
- goto fail;
-
- /* version = 0 */
- asn1_put_integer(pwri, 0);
-
- /* [0] KeyDerivationAlgorithmIdentifier */
- asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
- wpabuf_len(key_der_alg));
- wpabuf_put_buf(pwri, key_der_alg);
-
- /* KeyEncryptionAlgorithmIdentifier */
- wpabuf_put_buf(pwri, key_enc_alg);
-
- /* EncryptedKey ::= OCTET STRING */
- asn1_put_octet_string(pwri, enc_key);
-
-fail:
- wpabuf_clear_free(key_der_alg);
- wpabuf_free(key_enc_alg);
- wpabuf_free(enc_key);
- wpabuf_free(salt);
- forced_memzero(kek, sizeof(kek));
- return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
-}
-
-
-static struct wpabuf *
-dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *pwri;
-
- /*
- * RecipientInfo ::= CHOICE {
- * ktri KeyTransRecipientInfo,
- * kari [1] KeyAgreeRecipientInfo,
- * kekri [2] KEKRecipientInfo,
- * pwri [3] PasswordRecipientInfo,
- * ori [4] OtherRecipientInfo}
- *
- * Shall always use the pwri CHOICE.
- */
-
- pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
- return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
-}
-
-
-static struct wpabuf *
-dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
- const struct wpabuf *cont_enc_key)
-{
- struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
- *enc_alg;
- const struct asn1_oid *oid;
- size_t enc_cont_len;
-
- /*
- * EncryptedContentInfo ::= SEQUENCE {
- * contentType ContentType,
- * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
- * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
- */
-
- if (hash_len == 32)
- oid = &asn1_aes_siv_cmac_aead_256_oid;
- else if (hash_len == 48)
- oid = &asn1_aes_siv_cmac_aead_384_oid;
- else if (hash_len == 64)
- oid = &asn1_aes_siv_cmac_aead_512_oid;
- else
- return NULL;
-
- key_pkg = dpp_build_key_pkg(auth);
- enc_alg = asn1_build_alg_id(oid, NULL);
- if (!key_pkg || !enc_alg)
- goto fail;
-
- wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
- key_pkg);
-
- enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
- enc_cont = wpabuf_alloc(enc_cont_len);
- if (!enc_cont ||
- aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
- wpabuf_head(key_pkg), wpabuf_len(key_pkg),
- 0, NULL, NULL,
- wpabuf_put(enc_cont, enc_cont_len)) < 0)
- goto fail;
-
- enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
- wpabuf_len(enc_cont));
- if (!enc_cont_info)
- goto fail;
-
- /* ContentType ::= OBJECT IDENTIFIER */
- asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
-
- /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
- wpabuf_put_buf(enc_cont_info, enc_alg);
-
- /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
- * EncryptedContent ::= OCTET STRING */
- asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
- wpabuf_len(enc_cont));
- wpabuf_put_buf(enc_cont_info, enc_cont);
-
-fail:
- wpabuf_clear_free(key_pkg);
- wpabuf_free(enc_cont);
- wpabuf_free(enc_alg);
- return enc_cont_info;
-}
-
-
-static struct wpabuf * dpp_gen_random(size_t len)
-{
- struct wpabuf *key;
-
- key = wpabuf_alloc(len);
- if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
- wpabuf_free(key);
- key = NULL;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
- return key;
-}
-
-
-static struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
-{
- struct wpabuf *env = NULL;
- struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
- struct wpabuf *cont_enc_key = NULL;
- size_t hash_len;
-
- if (!auth->conf) {
- wpa_printf(MSG_DEBUG,
- "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
- return NULL;
- }
-
- if (!auth->provision_configurator) {
- wpa_printf(MSG_DEBUG,
- "DPP: Configurator provisioning not allowed");
- return NULL;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
-
- hash_len = auth->conf->curve->hash_len;
- cont_enc_key = dpp_gen_random(hash_len);
- if (!cont_enc_key)
- goto fail;
- recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
- enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
- if (!recipient_info || !enc_cont_info)
- goto fail;
-
- env = wpabuf_alloc(wpabuf_len(recipient_info) +
- wpabuf_len(enc_cont_info) +
- 100);
- if (!env)
- goto fail;
-
- /*
- * DPPEnvelopedData ::= EnvelopedData
- *
- * EnvelopedData ::= SEQUENCE {
- * version CMSVersion,
- * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
- * recipientInfos RecipientInfos,
- * encryptedContentInfo EncryptedContentInfo,
- * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
- *
- * For DPP, version is 3, both originatorInfo and
- * unprotectedAttrs are omitted, and recipientInfos contains a single
- * RecipientInfo.
- */
-
- /* EnvelopedData.version = 3 */
- asn1_put_integer(env, 3);
-
- /* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
- asn1_put_set(env, recipient_info);
-
- /* EncryptedContentInfo ::= SEQUENCE */
- asn1_put_sequence(env, enc_cont_info);
-
- env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
-out:
- wpabuf_clear_free(cont_enc_key);
- wpabuf_clear_free(recipient_info);
- wpabuf_free(enc_cont_info);
- return env;
-fail:
- wpabuf_free(env);
- env = NULL;
- goto out;
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static struct wpabuf *
+struct wpabuf *
dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
- u16 e_nonce_len, enum dpp_netrole netrole)
+ u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req)
{
struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL;
size_t clear_len, attr_len;
@@ -5746,21 +1711,33 @@
size_t len[1];
enum dpp_status_error status;
+ if (auth->force_conf_resp_status != DPP_STATUS_OK) {
+ status = auth->force_conf_resp_status;
+ goto forced_status;
+ }
+
if (netrole == DPP_NETROLE_CONFIGURATOR) {
#ifdef CONFIG_DPP2
env_data = dpp_build_enveloped_data(auth);
#endif /* CONFIG_DPP2 */
} else {
- conf = dpp_build_conf_obj(auth, netrole, 0);
+ conf = dpp_build_conf_obj(auth, netrole, 0, cert_req);
if (conf) {
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: configurationObject JSON",
wpabuf_head(conf), wpabuf_len(conf));
- conf2 = dpp_build_conf_obj(auth, netrole, 1);
+ conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req);
}
}
- status = (conf || env_data) ? DPP_STATUS_OK :
- DPP_STATUS_CONFIGURE_FAILURE;
+
+ if (conf || env_data)
+ status = DPP_STATUS_OK;
+ else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta &&
+ auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr)
+ status = DPP_STATUS_CSR_NEEDED;
+ else
+ status = DPP_STATUS_CONFIGURE_FAILURE;
+forced_status:
auth->conf_resp_status = status;
/* { E-nonce, configurationObject[, sendConnStatus]}ke */
@@ -5774,6 +1751,9 @@
if (auth->peer_version >= 2 && auth->send_conn_status &&
netrole == DPP_NETROLE_STA)
clear_len += 4;
+ if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+ auth->conf_sta->csrattrs)
+ clear_len += 4 + os_strlen(auth->conf_sta->csrattrs);
clear = wpabuf_alloc(clear_len);
attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
@@ -5836,12 +1816,21 @@
}
if (auth->peer_version >= 2 && auth->send_conn_status &&
- netrole == DPP_NETROLE_STA) {
+ netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
wpabuf_put_le16(clear, 0);
}
+ if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
+ auth->conf_sta->csrattrs) {
+ auth->waiting_csr = true;
+ wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request");
+ wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ);
+ wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs));
+ wpabuf_put_str(clear, auth->conf_sta->csrattrs);
+ }
+
#ifdef CONFIG_TESTING_OPTIONS
skip_config_obj:
if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
@@ -5912,6 +1901,7 @@
struct wpabuf *resp = NULL;
struct json_token *root = NULL, *token;
enum dpp_netrole netrole;
+ struct wpabuf *cert_req = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
@@ -6020,14 +2010,22 @@
dpp_auth_fail(auth, "Unsupported netRole");
goto fail;
}
+ auth->e_netrole = netrole;
token = json_get_member(root, "mudurl");
- if (token && token->type == JSON_STRING)
+ if (token && token->type == JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string);
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s",
+ token->string);
+ }
token = json_get_member(root, "bandSupport");
auth->band_list_size = 0;
if (token && token->type == JSON_ARRAY) {
+ int *opclass = NULL;
+ char txt[200], *pos, *end;
+ int i, res;
+
memset(auth->band_list, 0, sizeof(auth->band_list));
wpa_printf(MSG_DEBUG, "DPP: bandSupport");
token = token->child;
@@ -6042,100 +2040,84 @@
wpa_printf(MSG_DEBUG,
"DPP: Supported global operating class: %d",
token->number);
+ int_array_add_unique(&opclass, token->number);
}
token = token->sibling;
}
+
+ txt[0] = '\0';
+ pos = txt;
+ end = txt + sizeof(txt);
+ for (i = 0; opclass && opclass[i]; i++) {
+ res = os_snprintf(pos, end - pos, "%s%d",
+ pos == txt ? "" : ",", opclass[i]);
+ if (os_snprintf_error(end - pos, res)) {
+ *pos = '\0';
+ break;
+ }
+ pos += res;
+ }
+ os_free(opclass);
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s",
+ txt);
}
- resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole);
+#ifdef CONFIG_DPP2
+ cert_req = json_get_member_base64(root, "pkcs10");
+ if (cert_req) {
+ char *txt;
+ int id;
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req);
+ if (dpp_validate_csr(auth, cert_req) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: CSR is not valid");
+ auth->force_conf_resp_status = DPP_STATUS_CSR_BAD;
+ goto cont;
+ }
+
+ if (auth->peer_bi) {
+ id = auth->peer_bi->id;
+ } else if (auth->tmp_peer_bi) {
+ id = auth->tmp_peer_bi->id;
+ } else {
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ goto fail;
+ bi->id = dpp_next_id(auth->global);
+ dl_list_add(&auth->global->bootstrap, &bi->list);
+ auth->tmp_peer_bi = bi;
+ id = bi->id;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA");
+ txt = base64_encode_no_lf(wpabuf_head(cert_req),
+ wpabuf_len(cert_req), NULL);
+ if (!txt)
+ goto fail;
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s",
+ id, txt);
+ os_free(txt);
+ auth->waiting_csr = false;
+ auth->waiting_cert = true;
+ goto fail;
+ }
+cont:
+#endif /* CONFIG_DPP2 */
+
+ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole,
+ cert_req);
fail:
+ wpabuf_free(cert_req);
json_free(root);
os_free(unwrapped);
return resp;
}
-static struct wpabuf *
-dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
- const u8 *prot_hdr, u16 prot_hdr_len,
- const EVP_MD **ret_md)
-{
- struct json_token *root, *token;
- struct wpabuf *kid = NULL;
-
- root = json_parse((const char *) prot_hdr, prot_hdr_len);
- if (!root) {
- wpa_printf(MSG_DEBUG,
- "DPP: JSON parsing failed for JWS Protected Header");
- goto fail;
- }
-
- if (root->type != JSON_OBJECT) {
- wpa_printf(MSG_DEBUG,
- "DPP: JWS Protected Header root is not an object");
- goto fail;
- }
-
- token = json_get_member(root, "typ");
- if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
- token->string);
- if (os_strcmp(token->string, "dppCon") != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported JWS Protected Header typ=%s",
- token->string);
- goto fail;
- }
-
- token = json_get_member(root, "alg");
- if (!token || token->type != JSON_STRING) {
- wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
- token->string);
- if (os_strcmp(token->string, curve->jws_alg) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
- token->string, curve->jws_alg);
- goto fail;
- }
- if (os_strcmp(token->string, "ES256") == 0 ||
- os_strcmp(token->string, "BS256") == 0)
- *ret_md = EVP_sha256();
- else if (os_strcmp(token->string, "ES384") == 0 ||
- os_strcmp(token->string, "BS384") == 0)
- *ret_md = EVP_sha384();
- else if (os_strcmp(token->string, "ES512") == 0 ||
- os_strcmp(token->string, "BS512") == 0)
- *ret_md = EVP_sha512();
- else
- *ret_md = NULL;
- if (!*ret_md) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported JWS Protected Header alg=%s",
- token->string);
- goto fail;
- }
-
- kid = json_get_member_base64url(root, "kid");
- if (!kid) {
- wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
- goto fail;
- }
- wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
- kid);
-
-fail:
- json_free(root);
- return kid;
-}
-
-
static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
struct json_token *cred)
{
@@ -6183,8 +2165,8 @@
}
-static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
- const struct dpp_curve_params **key_curve)
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve)
{
struct json_token *token;
const struct dpp_curve_params *curve;
@@ -6438,38 +2420,6 @@
}
-static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
-{
- struct wpabuf *uncomp;
- int res;
- u8 hash[SHA256_MAC_LEN];
- const u8 *addr[1];
- size_t len[1];
-
- if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
- return -1;
- uncomp = dpp_get_pubkey_point(pub, 1);
- if (!uncomp)
- return -1;
- addr[0] = wpabuf_head(uncomp);
- len[0] = wpabuf_len(uncomp);
- wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
- addr[0], len[0]);
- res = sha256_vector(1, addr, len, hash);
- wpabuf_free(uncomp);
- if (res < 0)
- return -1;
- if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Received hash value does not match calculated public key hash value");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
- hash, SHA256_MAC_LEN);
- return -1;
- }
- return 0;
-}
-
-
static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
{
unsigned char *der = NULL;
@@ -6484,14 +2434,35 @@
}
+static void dpp_copy_ppkey(struct dpp_config_obj *conf, EVP_PKEY *ppkey)
+{
+ unsigned char *der = NULL;
+ int der_len;
+
+ der_len = i2d_PUBKEY(ppkey, &der);
+ if (der_len <= 0)
+ return;
+ wpabuf_free(conf->pp_key);
+ conf->pp_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+}
+
+
static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
struct dpp_config_obj *conf)
{
unsigned char *der = NULL;
int der_len;
EC_KEY *eckey;
+ EVP_PKEY *own_key;
- eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+ own_key = auth->own_protocol_key;
+#ifdef CONFIG_DPP2
+ if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY &&
+ auth->reconfig_old_protocol_key)
+ own_key = auth->reconfig_old_protocol_key;
+#endif /* CONFIG_DPP2 */
+ eckey = EVP_PKEY_get1_EC_KEY(own_key);
if (!eckey)
return;
@@ -6507,183 +2478,15 @@
}
-struct dpp_signed_connector_info {
- unsigned char *payload;
- size_t payload_len;
-};
-
-static enum dpp_status_error
-dpp_process_signed_connector(struct dpp_signed_connector_info *info,
- EVP_PKEY *csign_pub, const char *connector)
-{
- enum dpp_status_error ret = 255;
- const char *pos, *end, *signed_start, *signed_end;
- struct wpabuf *kid = NULL;
- unsigned char *prot_hdr = NULL, *signature = NULL;
- size_t prot_hdr_len = 0, signature_len = 0;
- const EVP_MD *sign_md = NULL;
- unsigned char *der = NULL;
- int der_len;
- int res;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
- BIGNUM *r = NULL, *s = NULL;
- const struct dpp_curve_params *curve;
- EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
-
- eckey = EVP_PKEY_get1_EC_KEY(csign_pub);
- if (!eckey)
- goto fail;
- group = EC_KEY_get0_group(eckey);
- if (!group)
- goto fail;
- nid = EC_GROUP_get_curve_name(group);
- curve = dpp_get_curve_nid(nid);
- if (!curve)
- goto fail;
- wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
- os_memset(info, 0, sizeof(*info));
-
- signed_start = pos = connector;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
- if (!prot_hdr) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector JWS Protected Header");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump_ascii(MSG_DEBUG,
- "DPP: signedConnector - JWS Protected Header",
- prot_hdr, prot_hdr_len);
- kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
- if (!kid) {
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- if (wpabuf_len(kid) != SHA256_MAC_LEN) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
- (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- pos = end + 1;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG,
- "DPP: Missing dot(2) in signedConnector");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- signed_end = end - 1;
- info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
- if (!info->payload) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector JWS Payload");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump_ascii(MSG_DEBUG,
- "DPP: signedConnector - JWS Payload",
- info->payload, info->payload_len);
- pos = end + 1;
- signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
- if (!signature) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode signedConnector signature");
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
- signature, signature_len);
-
- if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
- ret = DPP_STATUS_NO_MATCH;
- goto fail;
- }
-
- if (signature_len & 0x01) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected signedConnector signature length (%d)",
- (int) signature_len);
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- /* JWS Signature encodes the signature (r,s) as two octet strings. Need
- * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
- r = BN_bin2bn(signature, signature_len / 2, NULL);
- s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
- sig = ECDSA_SIG_new();
- if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
- goto fail;
- r = NULL;
- s = NULL;
-
- der_len = i2d_ECDSA_SIG(sig, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
- goto fail;
-
- ERR_clear_error();
- if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
- signed_end - signed_start + 1) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
- res, ERR_error_string(ERR_get_error(), NULL));
- ret = DPP_STATUS_INVALID_CONNECTOR;
- goto fail;
- }
-
- ret = DPP_STATUS_OK;
-fail:
- EC_KEY_free(eckey);
- EVP_MD_CTX_destroy(md_ctx);
- os_free(prot_hdr);
- wpabuf_free(kid);
- os_free(signature);
- ECDSA_SIG_free(sig);
- BN_free(r);
- BN_free(s);
- OPENSSL_free(der);
- return ret;
-}
-
-
static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
struct dpp_config_obj *conf,
struct json_token *cred)
{
struct dpp_signed_connector_info info;
- struct json_token *token, *csign;
+ struct json_token *token, *csign, *ppkey;
int ret = -1;
- EVP_PKEY *csign_pub = NULL;
- const struct dpp_curve_params *key_curve = NULL;
+ EVP_PKEY *csign_pub = NULL, *pp_pub = NULL;
+ const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL;
const char *signed_connector;
os_memset(&info, 0, sizeof(info));
@@ -6710,6 +2513,21 @@
}
dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
+ ppkey = json_get_member(cred, "ppKey");
+ if (ppkey && ppkey->type == JSON_OBJECT) {
+ pp_pub = dpp_parse_jwk(ppkey, &pp_curve);
+ if (!pp_pub) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Received ppKey", pp_pub);
+ if (key_curve != pp_curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: C-sign-key and ppKey do not use the same curve");
+ goto fail;
+ }
+ }
+
token = json_get_member(cred, "signedConnector");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
@@ -6740,17 +2558,72 @@
conf->connector = os_strdup(signed_connector);
dpp_copy_csign(conf, csign_pub);
+ if (pp_pub)
+ dpp_copy_ppkey(conf, pp_pub);
if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2)
dpp_copy_netaccesskey(auth, conf);
ret = 0;
fail:
EVP_PKEY_free(csign_pub);
+ EVP_PKEY_free(pp_pub);
os_free(info.payload);
return ret;
}
+#ifdef CONFIG_DPP2
+static int dpp_parse_cred_dot1x(struct dpp_authentication *auth,
+ struct dpp_config_obj *conf,
+ struct json_token *cred)
+{
+ struct json_token *ent, *name;
+
+ ent = json_get_member(cred, "entCreds");
+ if (!ent || ent->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No entCreds in JSON");
+ return -1;
+ }
+
+ conf->certbag = json_get_member_base64(ent, "certBag");
+ if (!conf->certbag) {
+ dpp_auth_fail(auth, "No certBag in JSON");
+ return -1;
+ }
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag);
+ conf->certs = dpp_pkcs7_certs(conf->certbag);
+ if (!conf->certs) {
+ dpp_auth_fail(auth, "No certificates in certBag");
+ return -1;
+ }
+
+ conf->cacert = json_get_member_base64(ent, "caCert");
+ if (conf->cacert)
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert",
+ conf->cacert);
+
+ name = json_get_member(ent, "trustedEapServerName");
+ if (name &&
+ (name->type != JSON_STRING ||
+ has_ctrl_char((const u8 *) name->string,
+ os_strlen(name->string)))) {
+ dpp_auth_fail(auth,
+ "Invalid trustedEapServerName type in JSON");
+ return -1;
+ }
+ if (name && name->string) {
+ wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s",
+ name->string);
+ conf->server_name = os_strdup(name->string);
+ if (!conf->server_name)
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_DPP2 */
+
+
const char * dpp_akm_str(enum dpp_akm akm)
{
switch (akm) {
@@ -6766,6 +2639,8 @@
return "dpp+sae";
case DPP_AKM_PSK_SAE_DPP:
return "dpp+psk+sae";
+ case DPP_AKM_DOT1X:
+ return "dot1x";
default:
return "??";
}
@@ -6787,6 +2662,8 @@
return "506F9A02+000FAC08";
case DPP_AKM_PSK_SAE_DPP:
return "506F9A02+000FAC08+000FAC02+000FAC06";
+ case DPP_AKM_DOT1X:
+ return "000FAC01+000FAC05";
default:
return "??";
}
@@ -6796,7 +2673,7 @@
static enum dpp_akm dpp_akm_from_str(const char *akm)
{
const char *pos;
- int dpp = 0, psk = 0, sae = 0;
+ int dpp = 0, psk = 0, sae = 0, dot1x = 0;
if (os_strcmp(akm, "psk") == 0)
return DPP_AKM_PSK;
@@ -6810,6 +2687,8 @@
return DPP_AKM_SAE_DPP;
if (os_strcmp(akm, "dpp+psk+sae") == 0)
return DPP_AKM_PSK_SAE_DPP;
+ if (os_strcmp(akm, "dot1x") == 0)
+ return DPP_AKM_DOT1X;
pos = akm;
while (*pos) {
@@ -6823,6 +2702,10 @@
psk = 1;
else if (os_strncasecmp(pos, "000FAC08", 8) == 0)
sae = 1;
+ else if (os_strncasecmp(pos, "000FAC01", 8) == 0)
+ dot1x = 1;
+ else if (os_strncasecmp(pos, "000FAC05", 8) == 0)
+ dot1x = 1;
pos += 8;
if (*pos != '+')
break;
@@ -6841,6 +2724,8 @@
return DPP_AKM_SAE;
if (psk)
return DPP_AKM_PSK;
+ if (dot1x)
+ return DPP_AKM_DOT1X;
return DPP_AKM_UNKNOWN;
}
@@ -6958,6 +2843,12 @@
(auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) {
if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
goto fail;
+#ifdef CONFIG_DPP2
+ } else if (conf->akm == DPP_AKM_DOT1X) {
+ if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 ||
+ dpp_parse_cred_dpp(auth, conf, cred) < 0)
+ goto fail;
+#endif /* CONFIG_DPP2 */
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
token->string);
@@ -6975,705 +2866,16 @@
#ifdef CONFIG_DPP2
-
-struct dpp_enveloped_data {
- const u8 *enc_cont;
- size_t enc_cont_len;
- const u8 *enc_key;
- size_t enc_key_len;
- const u8 *salt;
- size_t pbkdf2_key_len;
- size_t prf_hash_len;
-};
-
-
-static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
- struct dpp_enveloped_data *data)
+static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len)
{
- struct asn1_hdr hdr;
- const u8 *end = pos + len;
- const u8 *next, *e_end;
- struct asn1_oid oid;
- int val;
- const u8 *params;
- size_t params_len;
+ const u8 *b64;
+ u16 b64_len;
- wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
-
- /*
- * RecipientInfo ::= CHOICE {
- * ktri KeyTransRecipientInfo,
- * kari [1] KeyAgreeRecipientInfo,
- * kekri [2] KEKRecipientInfo,
- * pwri [3] PasswordRecipientInfo,
- * ori [4] OtherRecipientInfo}
- *
- * Shall always use the pwri CHOICE.
- */
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
- hdr.payload, hdr.length);
- pos = hdr.payload;
- end = pos + hdr.length;
-
- /*
- * PasswordRecipientInfo ::= SEQUENCE {
- * version CMSVersion,
- * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
- * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
- * encryptedKey EncryptedKey}
- *
- * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
- * parameters contains PBKDF2-params SEQUENCE.
- */
-
- if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
- return -1;
- pos = hdr.payload;
-
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- return -1;
- if (val != 0) {
- wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
- return -1;
- }
-
- wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
- pos, end - pos);
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- pos = hdr.payload;
- e_end = pos + hdr.length;
-
- /* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, e_end - pos, &oid, ¶ms, ¶ms_len,
- &next) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
- buf);
- return -1;
- }
-
- /*
- * PBKDF2-params ::= SEQUENCE {
- * salt CHOICE {
- * specified OCTET STRING,
- * otherSource AlgorithmIdentifier}
- * iterationCount INTEGER (1..MAX),
- * keyLength INTEGER (1..MAX),
- * prf AlgorithmIdentifier}
- *
- * salt is an 64 octet value, iterationCount is 1000, keyLength is based
- * on Configurator signing key length, prf is
- * id-hmacWithSHA{256,384,512} based on Configurator signing key.
- */
- if (!params ||
- asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
- return -1;
- pos = hdr.payload;
-
- if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
- hdr.payload, hdr.length);
- if (hdr.length != 64) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
- hdr.length);
- return -1;
- }
- data->salt = hdr.payload;
- pos = hdr.payload + hdr.length;
-
- if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
- return -1;
- if (val != 1000) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
- return -1;
- }
-
- if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
- return -1;
- if (val != 32 && val != 48 && val != 64) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
- return -1;
- }
- data->pbkdf2_key_len = val;
-
- if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
- asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
- return -1;
- }
- if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
- data->prf_hash_len = 32;
- } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
- data->prf_hash_len = 48;
- } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
- data->prf_hash_len = 64;
- } else {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
- buf);
- return -1;
- }
-
- pos = next;
-
- /* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
- *
- * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
- *
- * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
- * id-alg-AES-SIV-CMAC-aed-512. */
- if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
- buf);
- return -1;
- }
-
- /*
- * encryptedKey EncryptedKey
- *
- * EncryptedKey ::= OCTET STRING
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
- hdr.payload, hdr.length);
- data->enc_key = hdr.payload;
- data->enc_key_len = hdr.length;
-
- return 0;
-}
-
-
-static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
- struct dpp_enveloped_data *data)
-{
- struct asn1_hdr hdr;
- struct asn1_oid oid;
-
- /*
- * EncryptedContentInfo ::= SEQUENCE {
- * contentType ContentType,
- * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
- * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- return -1;
- wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
- hdr.payload, hdr.length);
- if (pos < end) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Unexpected extra data after EncryptedContentInfo",
- pos, end - pos);
- return -1;
- }
-
- end = pos;
- pos = hdr.payload;
-
- /* ContentType ::= OBJECT IDENTIFIER */
- if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
- return -1;
- }
- if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
- return -1;
- }
-
- /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
- return -1;
- if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
- !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
- char buf[80];
-
- asn1_oid_to_str(&oid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
- buf);
- return -1;
- }
- /* ignore optional parameters */
-
- /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
- * EncryptedContent ::= OCTET STRING */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
- hdr.payload, hdr.length);
- data->enc_cont = hdr.payload;
- data->enc_cont_len = hdr.length;
- return 0;
-}
-
-
-static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
- struct dpp_enveloped_data *data)
-{
- struct asn1_hdr hdr;
- const u8 *pos, *end;
- int val;
-
- os_memset(data, 0, sizeof(*data));
-
- /*
- * DPPEnvelopedData ::= EnvelopedData
- *
- * EnvelopedData ::= SEQUENCE {
- * version CMSVersion,
- * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
- * recipientInfos RecipientInfos,
- * encryptedContentInfo EncryptedContentInfo,
- * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
- *
- * CMSVersion ::= INTEGER
- *
- * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
- *
- * For DPP, version is 3, both originatorInfo and
- * unprotectedAttrs are omitted, and recipientInfos contains a single
- * RecipientInfo.
- */
- if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
- return -1;
- pos = hdr.payload;
- if (end < env_data + env_data_len) {
- wpa_hexdump(MSG_DEBUG,
- "DPP: Unexpected extra data after DPPEnvelopedData",
- end, env_data + env_data_len - end);
- return -1;
- }
-
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- return -1;
- if (val != 3) {
- wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
- return -1;
- }
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- return -1;
- }
-
- if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
- return -1;
- return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
- data);
-}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
-{
- struct asn1_hdr hdr;
- const u8 *pos = buf, *end = buf + len, *next;
- int val;
- const u8 *params;
- size_t params_len;
- struct asn1_oid oid;
- char txt[80];
- struct dpp_asymmetric_key *key;
- EC_KEY *eckey;
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
-
- key = os_zalloc(sizeof(*key));
- if (!key)
+ b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len);
+ if (!b64)
return NULL;
-
- /*
- * OneAsymmetricKey ::= SEQUENCE {
- * version Version,
- * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
- * privateKey PrivateKey,
- * attributes [0] Attributes OPTIONAL,
- * ...,
- * [[2: publicKey [1] BIT STRING OPTIONAL ]],
- * ...
- * }
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
- goto fail;
- pos = hdr.payload;
-
- /* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
- if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
- goto fail;
- if (val != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
- val);
- goto fail;
- }
-
- /* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
- if (asn1_get_alg_id(pos, end - pos, &oid, ¶ms, ¶ms_len,
- &pos) < 0)
- goto fail;
- if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
- txt);
- goto fail;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
- params, params_len);
- /*
- * ECParameters ::= CHOICE {
- * namedCurve OBJECT IDENTIFIER
- * -- implicitCurve NULL
- * -- specifiedCurve SpecifiedECDomain}
- */
- if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not parse ECParameters.namedCurve");
- goto fail;
- }
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
- /* Assume the curve is identified within ECPrivateKey, so that this
- * separate indication is not really needed. */
-
- /*
- * PrivateKey ::= OCTET STRING
- * (Contains DER encoding of ECPrivateKey)
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_OCTETSTRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
- hdr.payload, hdr.length);
- pos = hdr.payload + hdr.length;
- eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- key->csign = EVP_PKEY_new();
- if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
- EC_KEY_free(eckey);
- goto fail;
- }
- if (wpa_debug_show_keys)
- dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
-
- /*
- * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
- *
- * Exactly one instance of type Attribute in OneAsymmetricKey.
- */
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected [0] Attributes - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
- hdr.payload, hdr.length);
- if (hdr.payload + hdr.length < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of OneAsymmetricKey",
- hdr.payload + hdr.length,
- end - (hdr.payload + hdr.length));
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- if (hdr.payload + hdr.length < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
- hdr.payload + hdr.length,
- end - (hdr.payload + hdr.length));
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- /*
- * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
- * aa-DPPConfigurationParameters,
- * ... -- For local profiles
- * }
- *
- * aa-DPPConfigurationParameters ATTRIBUTE ::=
- * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
- *
- * Attribute ::= SEQUENCE {
- * type OBJECT IDENTIFIER,
- * values SET SIZE(1..MAX) OF Type
- *
- * Exactly one instance of ATTRIBUTE in attrValues.
- */
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- goto fail;
- if (pos < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data at the end of ATTRIBUTE",
- pos, end - pos);
- }
- end = pos;
- pos = hdr.payload;
-
- if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
- goto fail;
- if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
- asn1_oid_to_str(&oid, txt, sizeof(txt));
- wpa_printf(MSG_DEBUG,
- "DPP: Unexpected Attribute identifier %s", txt);
- goto fail;
- }
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- pos = hdr.payload;
- end = hdr.payload + hdr.length;
-
- /*
- * DPPConfigurationParameters ::= SEQUENCE {
- * configurationTemplate UTF8String,
- * connectorTemplate UTF8String OPTIONAL}
- */
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
- pos, end - pos);
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
- goto fail;
- if (pos < end) {
- wpa_hexdump_key(MSG_MSGDUMP,
- "DPP: Ignore additional data after DPPConfigurationParameters",
- pos, end - pos);
- }
- end = pos;
- pos = hdr.payload;
-
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_UTF8STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
- hdr.payload, hdr.length);
- key->config_template = os_zalloc(hdr.length + 1);
- if (!key->config_template)
- goto fail;
- os_memcpy(key->config_template, hdr.payload, hdr.length);
-
- pos = hdr.payload + hdr.length;
-
- if (pos < end) {
- if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
- hdr.class != ASN1_CLASS_UNIVERSAL ||
- hdr.tag != ASN1_TAG_UTF8STRING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
- hdr.class, hdr.tag);
- goto fail;
- }
- wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
- hdr.payload, hdr.length);
- key->connector_template = os_zalloc(hdr.length + 1);
- if (!key->connector_template)
- goto fail;
- os_memcpy(key->connector_template, hdr.payload, hdr.length);
- }
-
- return key;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
- dpp_free_asymmetric_key(key);
- return NULL;
+ return base64_decode((const char *) b64, b64_len, len);
}
-
-
-static struct dpp_asymmetric_key *
-dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
-{
- struct asn1_hdr hdr;
- const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
- struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
-
- wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
- key_pkg, key_pkg_len);
-
- /*
- * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
- *
- * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
- */
- while (pos < end) {
- if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
- !(key = dpp_parse_one_asymmetric_key(hdr.payload,
- hdr.length))) {
- dpp_free_asymmetric_key(first);
- return NULL;
- }
- if (!last) {
- first = last = key;
- } else {
- last->next = key;
- last = key;
- }
- }
-
- return first;
-}
-
-
-static int dpp_conf_resp_env_data(struct dpp_authentication *auth,
- const u8 *env_data, size_t env_data_len)
-{
- const u8 *key;
- size_t key_len;
- u8 kek[DPP_MAX_HASH_LEN];
- u8 cont_encr_key[DPP_MAX_HASH_LEN];
- size_t cont_encr_key_len;
- int res;
- u8 *key_pkg;
- size_t key_pkg_len;
- struct dpp_enveloped_data data;
- struct dpp_asymmetric_key *keys;
-
- wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
-
- if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
- return -1;
-
- /* TODO: For initial testing, use ke as the key. Replace this with a
- * new key once that has been defined. */
- key = auth->ke;
- key_len = auth->curve->hash_len;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
-
- if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
- kek, data.pbkdf2_key_len)) {
- wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
- return -1;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
- kek, data.pbkdf2_key_len);
-
- if (data.enc_key_len < AES_BLOCK_SIZE ||
- data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
- return -1;
- }
- res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
- data.enc_key, data.enc_key_len,
- 0, NULL, NULL, cont_encr_key);
- forced_memzero(kek, data.pbkdf2_key_len);
- if (res < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: AES-SIV decryption of encryptedKey failed");
- return -1;
- }
- cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
- wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
- cont_encr_key, cont_encr_key_len);
-
- if (data.enc_cont_len < AES_BLOCK_SIZE)
- return -1;
- key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
- key_pkg = os_malloc(key_pkg_len);
- if (!key_pkg)
- return -1;
- res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
- data.enc_cont, data.enc_cont_len,
- 0, NULL, NULL, key_pkg);
- forced_memzero(cont_encr_key, cont_encr_key_len);
- if (res < 0) {
- bin_clear_free(key_pkg, key_pkg_len);
- wpa_printf(MSG_DEBUG,
- "DPP: AES-SIV decryption of encryptedContent failed");
- return -1;
- }
-
- keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
- bin_clear_free(key_pkg, key_pkg_len);
- dpp_free_asymmetric_key(auth->conf_key_pkg);
- auth->conf_key_pkg = keys;
-
- return keys != NULL;;
-}
-
#endif /* CONFIG_DPP2 */
@@ -7754,6 +2956,28 @@
}
auth->conf_resp_status = status[0];
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+#ifdef CONFIG_DPP2
+ if (status[0] == DPP_STATUS_CSR_NEEDED) {
+ u8 *csrattrs;
+ size_t csrattrs_len;
+
+ wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR");
+
+ csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len,
+ &csrattrs_len);
+ if (!csrattrs) {
+ dpp_auth_fail(auth,
+ "Missing or invalid CSR Attributes Request attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len);
+ os_free(auth->csrattrs);
+ auth->csrattrs = csrattrs;
+ auth->csrattrs_len = csrattrs_len;
+ ret = -2;
+ goto fail;
+ }
+#endif /* CONFIG_DPP2 */
if (status[0] != DPP_STATUS_OK) {
dpp_auth_fail(auth, "Configurator rejected configuration");
goto fail;
@@ -8072,6 +3296,36 @@
}
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+ const u8 *ssid, size_t ssid_len,
+ const char *channel_list)
+{
+ struct wpabuf *json;
+
+ json = wpabuf_alloc(1000);
+ if (!json)
+ return NULL;
+ json_start_object(json, NULL);
+ json_add_int(json, "result", result);
+ if (ssid) {
+ json_value_sep(json);
+ if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) {
+ wpabuf_free(json);
+ return NULL;
+ }
+ }
+ if (channel_list) {
+ json_value_sep(json);
+ json_add_string(json, "channelList", channel_list);
+ }
+ json_end_object(json);
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
+ wpabuf_head(json), wpabuf_len(json));
+
+ return json;
+}
+
+
struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
@@ -8083,23 +3337,9 @@
size_t len[2];
u8 *wrapped;
- json = wpabuf_alloc(1000);
+ json = dpp_build_conn_status(result, ssid, ssid_len, channel_list);
if (!json)
return NULL;
- json_start_object(json, NULL);
- json_add_int(json, "result", result);
- if (ssid) {
- json_value_sep(json);
- if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0)
- goto fail;
- }
- if (channel_list) {
- json_value_sep(json);
- json_add_string(json, "channelList", channel_list);
- }
- json_end_object(json);
- wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
- wpabuf_head(json), wpabuf_len(json));
nonce_len = auth->curve->nonce_len;
clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json);
@@ -8161,6 +3401,9 @@
return;
EVP_PKEY_free(conf->csign);
os_free(conf->kid);
+ os_free(conf->connector);
+ EVP_PKEY_free(conf->connector_key);
+ EVP_PKEY_free(conf->pp_key);
os_free(conf);
}
@@ -8192,7 +3435,6 @@
static int dpp_configurator_gen_kid(struct dpp_configurator *conf)
{
struct wpabuf *csign_pub = NULL;
- u8 kid_hash[SHA256_MAC_LEN];
const u8 *addr[1];
size_t len[1];
int res;
@@ -8206,7 +3448,7 @@
/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
addr[0] = wpabuf_head(csign_pub);
len[0] = wpabuf_len(csign_pub);
- res = sha256_vector(1, addr, len, kid_hash);
+ res = sha256_vector(1, addr, len, conf->kid_hash);
wpabuf_free(csign_pub);
if (res < 0) {
wpa_printf(MSG_DEBUG,
@@ -8214,14 +3456,15 @@
return -1;
}
- conf->kid = base64_url_encode(kid_hash, sizeof(kid_hash), NULL);
+ conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash),
+ NULL);
return conf->kid ? 0 : -1;
}
-struct dpp_configurator *
+static struct dpp_configurator *
dpp_keygen_configurator(const char *curve, const u8 *privkey,
- size_t privkey_len)
+ size_t privkey_len, const u8 *pp_key, size_t pp_key_len)
{
struct dpp_configurator *conf;
@@ -8229,23 +3472,24 @@
if (!conf)
return NULL;
- if (!curve) {
- conf->curve = &dpp_curves[0];
- } else {
- conf->curve = dpp_get_curve_name(curve);
- if (!conf->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- os_free(conf);
- return NULL;
- }
+ conf->curve = dpp_get_curve_name(curve);
+ if (!conf->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ os_free(conf);
+ return NULL;
}
+
if (privkey)
conf->csign = dpp_set_keypair(&conf->curve, privkey,
privkey_len);
else
conf->csign = dpp_gen_keypair(conf->curve);
- if (!conf->csign)
+ if (pp_key)
+ conf->pp_key = dpp_set_keypair(&conf->curve, pp_key,
+ pp_key_len);
+ else
+ conf->pp_key = dpp_gen_keypair(conf->curve);
+ if (!conf->csign || !conf->pp_key)
goto fail;
conf->own = 1;
@@ -8269,16 +3513,12 @@
return -1;
}
- if (!curve) {
- auth->curve = &dpp_curves[0];
- } else {
- auth->curve = dpp_get_curve_name(curve);
- if (!auth->curve) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
- curve);
- return -1;
- }
+ auth->curve = dpp_get_curve_name(curve);
+ if (!auth->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ return -1;
}
+
wpa_printf(MSG_DEBUG,
"DPP: Building own configuration/connector with curve %s",
auth->curve->name);
@@ -8290,7 +3530,7 @@
auth->peer_protocol_key = auth->own_protocol_key;
dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
- conf_obj = dpp_build_conf_obj(auth, ap, 0);
+ conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL);
if (!conf_obj) {
wpabuf_free(auth->conf_obj[0].c_sign_key);
auth->conf_obj[0].c_sign_key = NULL;
@@ -8314,7 +3554,8 @@
static int dpp_connector_compatible_group(struct json_token *root,
const char *group_id,
- const char *net_role)
+ const char *net_role,
+ bool reconfig)
{
struct json_token *groups, *token;
@@ -8338,7 +3579,9 @@
os_strcmp(id->string, group_id) != 0)
continue;
- if (dpp_compatible_netrole(role->string, net_role))
+ if (reconfig && os_strcmp(net_role, "configurator") == 0)
+ return 1;
+ if (!reconfig && dpp_compatible_netrole(role->string, net_role))
return 1;
}
@@ -8346,8 +3589,8 @@
}
-static int dpp_connector_match_groups(struct json_token *own_root,
- struct json_token *peer_root)
+int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root, bool reconfig)
{
struct json_token *groups, *token;
@@ -8377,7 +3620,7 @@
"DPP: peer connector group: groupId='%s' netRole='%s'",
id->string, role->string);
if (dpp_connector_compatible_group(own_root, id->string,
- role->string)) {
+ role->string, reconfig)) {
wpa_printf(MSG_DEBUG,
"DPP: Compatible group/netRole in own connector");
return 1;
@@ -8388,71 +3631,37 @@
}
-static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk,
- unsigned int hash_len)
+struct json_token * dpp_parse_own_connector(const char *own_connector)
{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- const char *info = "DPP PMK";
- int res;
+ unsigned char *own_conn;
+ size_t own_conn_len;
+ const char *pos, *end;
+ struct json_token *own_root;
- /* PMK = HKDF(<>, "DPP PMK", N.x) */
-
- /* HKDF-Extract(<>, N.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
- prk, hash_len);
-
- /* HKDF-Expand(PRK, info, L) */
- res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
- pmk, hash_len);
- return 0;
-}
-
-
-static int dpp_derive_pmkid(const struct dpp_curve_params *curve,
- EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
-{
- struct wpabuf *nkx, *pkx;
- int ret = -1, res;
- const u8 *addr[2];
- size_t len[2];
- u8 hash[SHA256_MAC_LEN];
-
- /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
- nkx = dpp_get_pubkey_point(own_key, 0);
- pkx = dpp_get_pubkey_point(peer_key, 0);
- if (!nkx || !pkx)
- goto fail;
- addr[0] = wpabuf_head(nkx);
- len[0] = wpabuf_len(nkx) / 2;
- addr[1] = wpabuf_head(pkx);
- len[1] = wpabuf_len(pkx) / 2;
- if (len[0] != len[1])
- goto fail;
- if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
- addr[0] = wpabuf_head(pkx);
- addr[1] = wpabuf_head(nkx);
+ pos = os_strchr(own_connector, '.');
+ if (!pos) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+ return NULL;
}
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
- res = sha256_vector(2, addr, len, hash);
- if (res < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
- os_memcpy(pmkid, hash, PMKID_LEN);
- wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
- ret = 0;
-fail:
- wpabuf_free(nkx);
- wpabuf_free(pkx);
- return ret;
+ pos++;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
+ return NULL;
+ }
+ own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
+ if (!own_conn) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode own signedConnector JWS Payload");
+ return NULL;
+ }
+
+ own_root = json_parse((const char *) own_conn, own_conn_len);
+ os_free(own_conn);
+ if (!own_root)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
+
+ return own_root;
}
@@ -8470,12 +3679,6 @@
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
struct dpp_signed_connector_info info;
- const unsigned char *p;
- EVP_PKEY *csign = NULL;
- char *signed_connector = NULL;
- const char *pos, *end;
- unsigned char *own_conn = NULL;
- size_t own_conn_len;
size_t Nx_len;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
@@ -8484,14 +3687,6 @@
if (expiry)
*expiry = 0;
- p = csign_key;
- csign = d2i_PUBKEY(NULL, &p, csign_key_len);
- if (!csign) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to parse local C-sign-key information");
- goto fail;
- }
-
own_key = dpp_set_keypair(&own_curve, net_access_key,
net_access_key_len);
if (!own_key) {
@@ -8499,39 +3694,12 @@
goto fail;
}
- pos = os_strchr(own_connector, '.');
- if (!pos) {
- wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
+ own_root = dpp_parse_own_connector(own_connector);
+ if (!own_root)
goto fail;
- }
- pos++;
- end = os_strchr(pos, '.');
- if (!end) {
- wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
- goto fail;
- }
- own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
- if (!own_conn) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to base64url decode own signedConnector JWS Payload");
- goto fail;
- }
- own_root = json_parse((const char *) own_conn, own_conn_len);
- if (!own_root) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
- goto fail;
- }
-
- wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
- peer_connector, peer_connector_len);
- signed_connector = os_malloc(peer_connector_len + 1);
- if (!signed_connector)
- goto fail;
- os_memcpy(signed_connector, peer_connector, peer_connector_len);
- signed_connector[peer_connector_len] = '\0';
-
- res = dpp_process_signed_connector(&info, csign, signed_connector);
+ res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+ peer_connector, peer_connector_len);
if (res != DPP_STATUS_OK) {
ret = res;
goto fail;
@@ -8544,7 +3712,7 @@
goto fail;
}
- if (!dpp_connector_match_groups(own_root, root)) {
+ if (!dpp_connector_match_groups(own_root, root, false)) {
wpa_printf(MSG_DEBUG,
"DPP: Peer connector does not include compatible group netrole with own connector");
ret = DPP_STATUS_NO_MATCH;
@@ -8612,1751 +3780,17 @@
if (ret != DPP_STATUS_OK)
os_memset(intro, 0, sizeof(*intro));
os_memset(Nx, 0, sizeof(Nx));
- os_free(own_conn);
- os_free(signed_connector);
os_free(info.payload);
EVP_PKEY_free(own_key);
wpabuf_free(own_key_pub);
EVP_PKEY_free(peer_key);
- EVP_PKEY_free(csign);
json_free(root);
json_free(own_root);
return ret;
}
-static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
- int init)
-{
- EC_GROUP *group;
- size_t len = curve->prime_len;
- const u8 *x, *y;
- EVP_PKEY *res;
-
- switch (curve->ike_group) {
- case 19:
- x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
- y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
- break;
- case 20:
- x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
- y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
- break;
- case 21:
- x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
- y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
- break;
- case 28:
- x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
- y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
- break;
- case 29:
- x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
- y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
- break;
- case 30:
- x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
- y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
- break;
- default:
- return NULL;
- }
-
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return NULL;
- res = dpp_set_pubkey_point_group(group, x, y, len);
- EC_GROUP_free(group);
- return res;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
- const u8 *mac_init, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
-{
- u8 hash[DPP_MAX_HASH_LEN];
- const u8 *addr[3];
- size_t len[3];
- unsigned int num_elem = 0;
- EC_POINT *Qi = NULL;
- EVP_PKEY *Pi = NULL;
- EC_KEY *Pi_ec = NULL;
- const EC_POINT *Pi_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
-
- wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
- addr[num_elem] = mac_init;
- len[num_elem] = ETH_ALEN;
- num_elem++;
- if (identifier) {
- wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
- identifier);
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
- addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
- num_elem++;
- if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG,
- "DPP: H(MAC-Initiator | [identifier |] code)",
- hash, curve->hash_len);
- Pi = dpp_pkex_get_role_elem(curve, 1);
- if (!Pi)
- goto fail;
- dpp_debug_print_key("DPP: Pi", Pi);
- Pi_ec = EVP_PKEY_get1_EC_KEY(Pi);
- if (!Pi_ec)
- goto fail;
- Pi_point = EC_KEY_get0_public_key(Pi_ec);
-
- group = EC_KEY_get0_group(Pi_ec);
- if (!group)
- goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qi = EC_POINT_new(group2);
- if (!Qi) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
- goto fail;
- if (EC_POINT_is_at_infinity(group, Qi)) {
- wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
- goto fail;
- }
- dpp_debug_print_point("DPP: Qi", group, Qi);
-out:
- EC_KEY_free(Pi_ec);
- EVP_PKEY_free(Pi);
- BN_clear_free(hash_bn);
- if (ret_group && Qi)
- *ret_group = group2;
- else
- EC_GROUP_free(group2);
- return Qi;
-fail:
- EC_POINT_free(Qi);
- Qi = NULL;
- goto out;
-}
-
-
-static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
- const u8 *mac_resp, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
-{
- u8 hash[DPP_MAX_HASH_LEN];
- const u8 *addr[3];
- size_t len[3];
- unsigned int num_elem = 0;
- EC_POINT *Qr = NULL;
- EVP_PKEY *Pr = NULL;
- EC_KEY *Pr_ec = NULL;
- const EC_POINT *Pr_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
-
- /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
-
- wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
- addr[num_elem] = mac_resp;
- len[num_elem] = ETH_ALEN;
- num_elem++;
- if (identifier) {
- wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
- identifier);
- addr[num_elem] = (const u8 *) identifier;
- len[num_elem] = os_strlen(identifier);
- num_elem++;
- }
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
- addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
- num_elem++;
- if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
- goto fail;
- wpa_hexdump_key(MSG_DEBUG,
- "DPP: H(MAC-Responder | [identifier |] code)",
- hash, curve->hash_len);
- Pr = dpp_pkex_get_role_elem(curve, 0);
- if (!Pr)
- goto fail;
- dpp_debug_print_key("DPP: Pr", Pr);
- Pr_ec = EVP_PKEY_get1_EC_KEY(Pr);
- if (!Pr_ec)
- goto fail;
- Pr_point = EC_KEY_get0_public_key(Pr_ec);
-
- group = EC_KEY_get0_group(Pr_ec);
- if (!group)
- goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qr = EC_POINT_new(group2);
- if (!Qr) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
- goto fail;
- if (EC_POINT_is_at_infinity(group, Qr)) {
- wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
- goto fail;
- }
- dpp_debug_print_point("DPP: Qr", group, Qr);
-out:
- EC_KEY_free(Pr_ec);
- EVP_PKEY_free(Pr);
- BN_clear_free(hash_bn);
- if (ret_group && Qr)
- *ret_group = group2;
- else
- EC_GROUP_free(group2);
- return Qr;
-fail:
- EC_POINT_free(Qr);
- Qr = NULL;
- goto out;
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-static int dpp_test_gen_invalid_key(struct wpabuf *msg,
- const struct dpp_curve_params *curve)
-{
- BN_CTX *ctx;
- BIGNUM *x, *y;
- int ret = -1;
- EC_GROUP *group;
- EC_POINT *point;
-
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return -1;
-
- ctx = BN_CTX_new();
- point = EC_POINT_new(group);
- x = BN_new();
- y = BN_new();
- if (!ctx || !point || !x || !y)
- goto fail;
-
- if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
- goto fail;
-
- /* Generate a random y coordinate that results in a point that is not
- * on the curve. */
- for (;;) {
- if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
- goto fail;
-
- if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
- ctx) != 1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
- /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
- * return an error from EC_POINT_set_affine_coordinates_GFp()
- * when the point is not on the curve. */
- break;
-#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
- goto fail;
-#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
- }
-
- if (!EC_POINT_is_on_curve(group, point, ctx))
- break;
- }
-
- if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
- ret = 0;
-fail:
- if (ret < 0)
- wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
- BN_free(x);
- BN_free(y);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- EC_GROUP_free(group);
-
- return ret;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
-{
- EC_KEY *X_ec = NULL;
- const EC_POINT *X_point;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- EC_POINT *Qi = NULL, *M = NULL;
- struct wpabuf *M_buf = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
- struct wpabuf *msg = NULL;
- size_t attr_len;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
- wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
- pkex->identifier, bnctx, &group);
- if (!Qi)
- goto fail;
-
- /* Generate a random ephemeral keypair x/X */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_pkex_ephemeral_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override ephemeral key x/X");
- pkex->x = dpp_set_keypair(&tmp_curve,
- dpp_pkex_ephemeral_key_override,
- dpp_pkex_ephemeral_key_override_len);
- } else {
- pkex->x = dpp_gen_keypair(curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- pkex->x = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!pkex->x)
- goto fail;
-
- /* M = X + Qi */
- X_ec = EVP_PKEY_get1_EC_KEY(pkex->x);
- if (!X_ec)
- goto fail;
- X_point = EC_KEY_get0_public_key(X_ec);
- if (!X_point)
- goto fail;
- dpp_debug_print_point("DPP: X", group, X_point);
- M = EC_POINT_new(group);
- Mx = BN_new();
- My = BN_new();
- if (!M || !Mx || !My ||
- EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
- goto fail;
- dpp_debug_print_point("DPP: M", group, M);
-
- /* Initiator -> Responder: group, [identifier,] M */
- attr_len = 4 + 2;
- if (pkex->identifier)
- attr_len += 4 + os_strlen(pkex->identifier);
- attr_len += 4 + 2 * curve->prime_len;
- msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
- if (!msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
- goto skip_finite_cyclic_group;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Finite Cyclic Group attribute */
- wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_le16(msg, curve->ike_group);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_finite_cyclic_group:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Code Identifier attribute */
- if (pkex->identifier) {
- wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
- wpabuf_put_le16(msg, os_strlen(pkex->identifier));
- wpabuf_put_str(msg, pkex->identifier);
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
- goto out;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* M in Encrypted Key attribute */
- wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
- wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
- if (dpp_test_gen_invalid_key(msg, curve) < 0)
- goto fail;
- goto out;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
-out:
- wpabuf_free(M_buf);
- EC_KEY_free(X_ec);
- EC_POINT_free(M);
- EC_POINT_free(Qi);
- BN_clear_free(Mx);
- BN_clear_free(My);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
- return msg;
-fail:
- wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
-{
- wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
-}
-
-
-struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
- const u8 *own_mac,
- const char *identifier,
- const char *code)
-{
- struct dpp_pkex *pkex;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
- MAC2STR(dpp_pkex_own_mac_override));
- own_mac = dpp_pkex_own_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- return NULL;
- pkex->msg_ctx = msg_ctx;
- pkex->initiator = 1;
- pkex->own_bi = bi;
- os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
- if (identifier) {
- pkex->identifier = os_strdup(identifier);
- if (!pkex->identifier)
- goto fail;
- }
- pkex->code = os_strdup(code);
- if (!pkex->code)
- goto fail;
- pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
- if (!pkex->exchange_req)
- goto fail;
- return pkex;
-fail:
- dpp_pkex_free(pkex);
- return NULL;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
- enum dpp_status_error status,
- const BIGNUM *Nx, const BIGNUM *Ny)
-{
- struct wpabuf *msg = NULL;
- size_t attr_len;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
-
- /* Initiator -> Responder: DPP Status, [identifier,] N */
- attr_len = 4 + 1;
- if (pkex->identifier)
- attr_len += 4 + os_strlen(pkex->identifier);
- attr_len += 4 + 2 * curve->prime_len;
- msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
- if (!msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
- goto skip_status;
- }
-
- if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
- status = 255;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* DPP Status */
- dpp_build_attr_status(msg, status);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_status:
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* Code Identifier attribute */
- if (pkex->identifier) {
- wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
- wpabuf_put_le16(msg, os_strlen(pkex->identifier));
- wpabuf_put_str(msg, pkex->identifier);
- }
-
- if (status != DPP_STATUS_OK)
- goto skip_encrypted_key;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
- goto skip_encrypted_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* N in Encrypted Key attribute */
- wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
- wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
- if (dpp_test_gen_invalid_key(msg, curve) < 0)
- goto fail;
- goto skip_encrypted_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
-skip_encrypted_key:
- if (status == DPP_STATUS_BAD_GROUP) {
- /* Finite Cyclic Group attribute */
- wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
- wpabuf_put_le16(msg, 2);
- wpabuf_put_le16(msg, curve->ike_group);
- }
-
- return msg;
-fail:
- wpabuf_free(msg);
- return NULL;
-}
-
-
-static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
- const u8 *Mx, size_t Mx_len,
- const u8 *Nx, size_t Nx_len,
- const char *code,
- const u8 *Kx, size_t Kx_len,
- u8 *z, unsigned int hash_len)
-{
- u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
- int res;
- u8 *info, *pos;
- size_t info_len;
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
-
- /* HKDF-Extract(<>, IKM=K.x) */
- os_memset(salt, 0, hash_len);
- if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
- return -1;
- wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
- prk, hash_len);
- info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
- info = os_malloc(info_len);
- if (!info)
- return -1;
- pos = info;
- os_memcpy(pos, mac_init, ETH_ALEN);
- pos += ETH_ALEN;
- os_memcpy(pos, mac_resp, ETH_ALEN);
- pos += ETH_ALEN;
- os_memcpy(pos, Mx, Mx_len);
- pos += Mx_len;
- os_memcpy(pos, Nx, Nx_len);
- pos += Nx_len;
- os_memcpy(pos, code, os_strlen(code));
-
- /* HKDF-Expand(PRK, info, L) */
- if (hash_len == 32)
- res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else if (hash_len == 48)
- res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else if (hash_len == 64)
- res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
- z, hash_len);
- else
- res = -1;
- os_free(info);
- os_memset(prk, 0, hash_len);
- if (res < 0)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
- z, hash_len);
- return 0;
-}
-
-
-static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
- const char *identifier)
-{
- if (!attr_id && identifier) {
- wpa_printf(MSG_DEBUG,
- "DPP: No PKEX code identifier received, but expected one");
- return 0;
- }
-
- if (attr_id && !identifier) {
- wpa_printf(MSG_DEBUG,
- "DPP: PKEX code identifier received, but not expecting one");
- return 0;
- }
-
- if (attr_id && identifier &&
- (os_strlen(identifier) != attr_id_len ||
- os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
- wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
- return 0;
- }
-
- return 1;
-}
-
-
-struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
- struct dpp_bootstrap_info *bi,
- const u8 *own_mac,
- const u8 *peer_mac,
- const char *identifier,
- const char *code,
- const u8 *buf, size_t len)
-{
- const u8 *attr_group, *attr_id, *attr_key;
- u16 attr_group_len, attr_id_len, attr_key_len;
- const struct dpp_curve_params *curve = bi->curve;
- u16 ike_group;
- struct dpp_pkex *pkex = NULL;
- EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
- EC_KEY *Y_ec = NULL, *X_ec = NULL;;
- const EC_POINT *Y_point;
- BIGNUM *Nx = NULL, *Ny = NULL;
- u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
- size_t Kx_len;
- int res;
-
- if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "PKEX counter t limit reached - ignore message");
- return NULL;
- }
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
- MAC2STR(dpp_pkex_peer_mac_override));
- peer_mac = dpp_pkex_peer_mac_override;
- }
- if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
- MAC2STR(dpp_pkex_own_mac_override));
- own_mac = dpp_pkex_own_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- attr_id_len = 0;
- attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
- &attr_id_len);
- if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
- return NULL;
-
- attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
- &attr_group_len);
- if (!attr_group || attr_group_len != 2) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid Finite Cyclic Group attribute");
- return NULL;
- }
- ike_group = WPA_GET_LE16(attr_group);
- if (ike_group != curve->ike_group) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Mismatching PKEX curve: peer=%u own=%u",
- ike_group, curve->ike_group);
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- goto fail;
- pkex->own_bi = bi;
- pkex->failed = 1;
- pkex->exchange_resp = dpp_pkex_build_exchange_resp(
- pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
- if (!pkex->exchange_resp)
- goto fail;
- return pkex;
- }
-
- /* M in Encrypted Key attribute */
- attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
- &attr_key_len);
- if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
- attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing Encrypted Key attribute");
- return NULL;
- }
-
- /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
- &group);
- if (!Qi)
- goto fail;
-
- /* X' = M - Qi */
- X = EC_POINT_new(group);
- M = EC_POINT_new(group);
- Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!X || !M || !Mx || !My ||
- EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, M) ||
- !EC_POINT_is_on_curve(group, M, bnctx) ||
- EC_POINT_invert(group, Qi, bnctx) != 1 ||
- EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, X) ||
- !EC_POINT_is_on_curve(group, X, bnctx)) {
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Invalid Encrypted Key value");
- bi->pkex_t++;
- goto fail;
- }
- dpp_debug_print_point("DPP: M", group, M);
- dpp_debug_print_point("DPP: X'", group, X);
-
- pkex = os_zalloc(sizeof(*pkex));
- if (!pkex)
- goto fail;
- pkex->t = bi->pkex_t;
- pkex->msg_ctx = msg_ctx;
- pkex->own_bi = bi;
- os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
- os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
- if (identifier) {
- pkex->identifier = os_strdup(identifier);
- if (!pkex->identifier)
- goto fail;
- }
- pkex->code = os_strdup(code);
- if (!pkex->code)
- goto fail;
-
- os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
-
- X_ec = EC_KEY_new();
- if (!X_ec ||
- EC_KEY_set_group(X_ec, group) != 1 ||
- EC_KEY_set_public_key(X_ec, X) != 1)
- goto fail;
- pkex->x = EVP_PKEY_new();
- if (!pkex->x ||
- EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
- goto fail;
-
- /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
- Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
- if (!Qr)
- goto fail;
-
- /* Generate a random ephemeral keypair y/Y */
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_pkex_ephemeral_key_override_len) {
- const struct dpp_curve_params *tmp_curve;
-
- wpa_printf(MSG_INFO,
- "DPP: TESTING - override ephemeral key y/Y");
- pkex->y = dpp_set_keypair(&tmp_curve,
- dpp_pkex_ephemeral_key_override,
- dpp_pkex_ephemeral_key_override_len);
- } else {
- pkex->y = dpp_gen_keypair(curve);
- }
-#else /* CONFIG_TESTING_OPTIONS */
- pkex->y = dpp_gen_keypair(curve);
-#endif /* CONFIG_TESTING_OPTIONS */
- if (!pkex->y)
- goto fail;
-
- /* N = Y + Qr */
- Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y);
- if (!Y_ec)
- goto fail;
- Y_point = EC_KEY_get0_public_key(Y_ec);
- if (!Y_point)
- goto fail;
- dpp_debug_print_point("DPP: Y", group, Y_point);
- N = EC_POINT_new(group);
- Nx = BN_new();
- Ny = BN_new();
- if (!N || !Nx || !Ny ||
- EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
- goto fail;
- dpp_debug_print_point("DPP: N", group, N);
-
- pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
- Nx, Ny);
- if (!pkex->exchange_resp)
- goto fail;
-
- /* K = y * X' */
- if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
- Kx, Kx_len);
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
- res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
- pkex->Mx, curve->prime_len,
- pkex->Nx, curve->prime_len, pkex->code,
- Kx, Kx_len, pkex->z, curve->hash_len);
- os_memset(Kx, 0, Kx_len);
- if (res < 0)
- goto fail;
-
- pkex->exchange_done = 1;
-
-out:
- BN_CTX_free(bnctx);
- EC_POINT_free(Qi);
- EC_POINT_free(Qr);
- BN_free(Mx);
- BN_free(My);
- BN_free(Nx);
- BN_free(Ny);
- EC_POINT_free(M);
- EC_POINT_free(N);
- EC_POINT_free(X);
- EC_KEY_free(X_ec);
- EC_KEY_free(Y_ec);
- EC_GROUP_free(group);
- return pkex;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
- dpp_pkex_free(pkex);
- pkex = NULL;
- goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
- const struct wpabuf *A_pub, const u8 *u)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- struct wpabuf *msg = NULL;
- size_t clear_len, attr_len;
- struct wpabuf *clear = NULL;
- u8 *wrapped;
- u8 octet;
- const u8 *addr[2];
- size_t len[2];
-
- /* {A, u, [bootstrapping info]}z */
- clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
- clear = wpabuf_alloc(clear_len);
- attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
- if (!clear || !msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
- goto skip_bootstrap_key;
- }
- if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, 2 * curve->prime_len);
- if (dpp_test_gen_invalid_key(clear, curve) < 0)
- goto fail;
- goto skip_bootstrap_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* A in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(A_pub));
- wpabuf_put_buf(clear, A_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
- if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
- goto skip_i_auth_tag;
- }
- if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
- wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, u, curve->hash_len - 1);
- wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
- goto skip_i_auth_tag;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* u in I-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, u, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_i_auth_tag:
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = DPP_HDR_LEN;
- octet = 0;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
- if (aes_siv_encrypt(pkex->z, curve->hash_len,
- wpabuf_head(clear), wpabuf_len(clear),
- 2, addr, len, wrapped) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-out:
- wpabuf_free(clear);
- return msg;
-
-fail:
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
- const u8 *peer_mac,
- const u8 *buf, size_t buflen)
-{
- const u8 *attr_status, *attr_id, *attr_key, *attr_group;
- u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
- EC_GROUP *group = NULL;
- BN_CTX *bnctx = NULL;
- struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
- BIGNUM *Nx = NULL, *Ny = NULL;
- EC_KEY *Y_ec = NULL;
- size_t Jx_len, Kx_len;
- u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
- const u8 *addr[4];
- size_t len[4];
- u8 u[DPP_MAX_HASH_LEN];
- int res;
-
- if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
- return NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX Exchange Response");
- pkex->failed = 1;
- return NULL;
- }
-
- if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
- wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
- MAC2STR(dpp_pkex_peer_mac_override));
- peer_mac = dpp_pkex_peer_mac_override;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
-
- attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
- &attr_status_len);
- if (!attr_status || attr_status_len != 1) {
- dpp_pkex_fail(pkex, "No DPP Status attribute");
- return NULL;
- }
- wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
-
- if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
- attr_group = dpp_get_attr(buf, buflen,
- DPP_ATTR_FINITE_CYCLIC_GROUP,
- &attr_group_len);
- if (attr_group && attr_group_len == 2) {
- wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Peer indicated mismatching PKEX group - proposed %u",
- WPA_GET_LE16(attr_group));
- return NULL;
- }
- }
-
- if (attr_status[0] != DPP_STATUS_OK) {
- dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
- return NULL;
- }
-
- attr_id_len = 0;
- attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
- &attr_id_len);
- if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
- pkex->identifier)) {
- dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
- return NULL;
- }
-
- /* N in Encrypted Key attribute */
- attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
- &attr_key_len);
- if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
- dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
- return NULL;
- }
-
- /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
- pkex->identifier, bnctx, &group);
- if (!Qr)
- goto fail;
-
- /* Y' = N - Qr */
- Y = EC_POINT_new(group);
- N = EC_POINT_new(group);
- Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!Y || !N || !Nx || !Ny ||
- EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, N) ||
- !EC_POINT_is_on_curve(group, N, bnctx) ||
- EC_POINT_invert(group, Qr, bnctx) != 1 ||
- EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, Y) ||
- !EC_POINT_is_on_curve(group, Y, bnctx)) {
- dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
- pkex->t++;
- goto fail;
- }
- dpp_debug_print_point("DPP: N", group, N);
- dpp_debug_print_point("DPP: Y'", group, Y);
-
- pkex->exchange_done = 1;
-
- /* ECDH: J = a * Y’ */
- Y_ec = EC_KEY_new();
- if (!Y_ec ||
- EC_KEY_set_group(Y_ec, group) != 1 ||
- EC_KEY_set_public_key(Y_ec, Y) != 1)
- goto fail;
- pkex->y = EVP_PKEY_new();
- if (!pkex->y ||
- EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
- goto fail;
- if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
- Jx, Jx_len);
-
- /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */
- A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- if (!A_pub || !Y_pub || !X_pub)
- goto fail;
- addr[0] = pkex->own_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(A_pub);
- len[1] = wpabuf_len(A_pub) / 2;
- addr[2] = wpabuf_head(Y_pub);
- len[2] = wpabuf_len(Y_pub) / 2;
- addr[3] = wpabuf_head(X_pub);
- len[3] = wpabuf_len(X_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
-
- /* K = x * Y’ */
- if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
- Kx, Kx_len);
-
- /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
- */
- res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
- pkex->Mx, curve->prime_len,
- attr_key /* N.x */, attr_key_len / 2,
- pkex->code, Kx, Kx_len,
- pkex->z, curve->hash_len);
- os_memset(Kx, 0, Kx_len);
- if (res < 0)
- goto fail;
-
- msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
- if (!msg)
- goto fail;
-
-out:
- wpabuf_free(A_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- EC_POINT_free(Qr);
- EC_POINT_free(Y);
- EC_POINT_free(N);
- BN_free(Nx);
- BN_free(Ny);
- EC_KEY_free(Y_ec);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
- return msg;
-fail:
- wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
- goto out;
-}
-
-
-static struct wpabuf *
-dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
- const struct wpabuf *B_pub, const u8 *v)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- struct wpabuf *msg = NULL;
- const u8 *addr[2];
- size_t len[2];
- u8 octet;
- u8 *wrapped;
- struct wpabuf *clear = NULL;
- size_t clear_len, attr_len;
-
- /* {B, v [bootstrapping info]}z */
- clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
- clear = wpabuf_alloc(clear_len);
- attr_len = 4 + clear_len + AES_BLOCK_SIZE;
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
- attr_len += 5;
-#endif /* CONFIG_TESTING_OPTIONS */
- msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
- if (!clear || !msg)
- goto fail;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
- goto skip_bootstrap_key;
- }
- if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, 2 * curve->prime_len);
- if (dpp_test_gen_invalid_key(clear, curve) < 0)
- goto fail;
- goto skip_bootstrap_key;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* B in Bootstrap Key attribute */
- wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
- wpabuf_put_le16(clear, wpabuf_len(B_pub));
- wpabuf_put_buf(clear, B_pub);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_bootstrap_key:
- if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
- goto skip_r_auth_tag;
- }
- if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
- wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, v, curve->hash_len - 1);
- wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
- goto skip_r_auth_tag;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- /* v in R-Auth tag attribute */
- wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
- wpabuf_put_le16(clear, curve->hash_len);
- wpabuf_put_data(clear, v, curve->hash_len);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_r_auth_tag:
- if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
- goto skip_wrapped_data;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- addr[0] = wpabuf_head_u8(msg) + 2;
- len[0] = DPP_HDR_LEN;
- octet = 1;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
- wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
- wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
- wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
- if (aes_siv_encrypt(pkex->z, curve->hash_len,
- wpabuf_head(clear), wpabuf_len(clear),
- 2, addr, len, wrapped) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
- dpp_build_attr_status(msg, DPP_STATUS_OK);
- }
-skip_wrapped_data:
-#endif /* CONFIG_TESTING_OPTIONS */
-
-out:
- wpabuf_free(clear);
- return msg;
-
-fail:
- wpabuf_free(msg);
- msg = NULL;
- goto out;
-}
-
-
-struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
- const u8 *hdr,
- const u8 *buf, size_t buflen)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- size_t Jx_len, Lx_len;
- u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
- u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
- const u8 *wrapped_data, *b_key, *peer_u;
- u16 wrapped_data_len, b_key_len, peer_u_len = 0;
- const u8 *addr[4];
- size_t len[4];
- u8 octet;
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
- struct wpabuf *B_pub = NULL;
- u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX CR Request");
- pkex->failed = 1;
- return NULL;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!pkex->exchange_done || pkex->failed ||
- pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
- goto fail;
-
- wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_pkex_fail(pkex,
- "Missing or invalid required Wrapped Data attribute");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- octet = 0;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (aes_siv_decrypt(pkex->z, curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_pkex_fail(pkex,
- "AES-SIV decryption failed - possible PKEX code mismatch");
- pkex->failed = 1;
- pkex->t++;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
- &b_key_len);
- if (!b_key || b_key_len != 2 * curve->prime_len) {
- dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
- goto fail;
- }
- pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
- b_key_len);
- if (!pkex->peer_bootstrap_key) {
- dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
- goto fail;
- }
- dpp_debug_print_key("DPP: Peer bootstrap public key",
- pkex->peer_bootstrap_key);
-
- /* ECDH: J' = y * A' */
- if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
- Jx, Jx_len);
-
- /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
- A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- if (!A_pub || !Y_pub || !X_pub)
- goto fail;
- addr[0] = pkex->peer_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(A_pub);
- len[1] = wpabuf_len(A_pub) / 2;
- addr[2] = wpabuf_head(Y_pub);
- len[2] = wpabuf_len(Y_pub) / 2;
- addr[3] = wpabuf_head(X_pub);
- len[3] = wpabuf_len(X_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
- goto fail;
-
- peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
- &peer_u_len);
- if (!peer_u || peer_u_len != curve->hash_len ||
- os_memcmp(peer_u, u, curve->hash_len) != 0) {
- dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
- u, curve->hash_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
- pkex->t++;
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
-
- /* ECDH: L = b * X' */
- if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
- Lx, Lx_len);
-
- /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
- B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
- if (!B_pub)
- goto fail;
- addr[0] = pkex->own_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(B_pub);
- len[1] = wpabuf_len(B_pub) / 2;
- addr[2] = wpabuf_head(X_pub);
- len[2] = wpabuf_len(X_pub) / 2;
- addr[3] = wpabuf_head(Y_pub);
- len[3] = wpabuf_len(Y_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
-
- msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
- if (!msg)
- goto fail;
-
-out:
- os_free(unwrapped);
- wpabuf_free(A_pub);
- wpabuf_free(B_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- return msg;
-fail:
- wpa_printf(MSG_DEBUG,
- "DPP: PKEX Commit-Reveal Request processing failed");
- goto out;
-}
-
-
-int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
- const u8 *buf, size_t buflen)
-{
- const struct dpp_curve_params *curve = pkex->own_bi->curve;
- const u8 *wrapped_data, *b_key, *peer_v;
- u16 wrapped_data_len, b_key_len, peer_v_len = 0;
- const u8 *addr[4];
- size_t len[4];
- u8 octet;
- u8 *unwrapped = NULL;
- size_t unwrapped_len = 0;
- int ret = -1;
- u8 v[DPP_MAX_HASH_LEN];
- size_t Lx_len;
- u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
- struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
-
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at PKEX CR Response");
- pkex->failed = 1;
- goto fail;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!pkex->exchange_done || pkex->failed ||
- pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
- goto fail;
-
- wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
- &wrapped_data_len);
- if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
- dpp_pkex_fail(pkex,
- "Missing or invalid required Wrapped Data attribute");
- goto fail;
- }
-
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
- wrapped_data, wrapped_data_len);
- unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
- unwrapped = os_malloc(unwrapped_len);
- if (!unwrapped)
- goto fail;
-
- addr[0] = hdr;
- len[0] = DPP_HDR_LEN;
- octet = 1;
- addr[1] = &octet;
- len[1] = sizeof(octet);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
- wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
- if (aes_siv_decrypt(pkex->z, curve->hash_len,
- wrapped_data, wrapped_data_len,
- 2, addr, len, unwrapped) < 0) {
- dpp_pkex_fail(pkex,
- "AES-SIV decryption failed - possible PKEX code mismatch");
- pkex->t++;
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
- unwrapped, unwrapped_len);
-
- if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
- dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
- goto fail;
- }
-
- b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
- &b_key_len);
- if (!b_key || b_key_len != 2 * curve->prime_len) {
- dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
- goto fail;
- }
- pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
- b_key_len);
- if (!pkex->peer_bootstrap_key) {
- dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
- goto fail;
- }
- dpp_debug_print_key("DPP: Peer bootstrap public key",
- pkex->peer_bootstrap_key);
-
- /* ECDH: L' = x * B' */
- if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
- goto fail;
-
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
- Lx, Lx_len);
-
- /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
- B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- if (!B_pub || !X_pub || !Y_pub)
- goto fail;
- addr[0] = pkex->peer_mac;
- len[0] = ETH_ALEN;
- addr[1] = wpabuf_head(B_pub);
- len[1] = wpabuf_len(B_pub) / 2;
- addr[2] = wpabuf_head(X_pub);
- len[2] = wpabuf_len(X_pub) / 2;
- addr[3] = wpabuf_head(Y_pub);
- len[3] = wpabuf_len(Y_pub) / 2;
- if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
- goto fail;
-
- peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
- &peer_v_len);
- if (!peer_v || peer_v_len != curve->hash_len ||
- os_memcmp(peer_v, v, curve->hash_len) != 0) {
- dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
- wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
- v, curve->hash_len);
- wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
- pkex->t++;
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
-
- ret = 0;
-out:
- wpabuf_free(B_pub);
- wpabuf_free(X_pub);
- wpabuf_free(Y_pub);
- os_free(unwrapped);
- return ret;
-fail:
- goto out;
-}
-
-
-void dpp_pkex_free(struct dpp_pkex *pkex)
-{
- if (!pkex)
- return;
-
- os_free(pkex->identifier);
- os_free(pkex->code);
- EVP_PKEY_free(pkex->x);
- EVP_PKEY_free(pkex->y);
- EVP_PKEY_free(pkex->peer_bootstrap_key);
- wpabuf_free(pkex->exchange_req);
- wpabuf_free(pkex->exchange_resp);
- os_free(pkex);
-}
-
-
-#ifdef CONFIG_TESTING_OPTIONS
-char * dpp_corrupt_connector_signature(const char *connector)
-{
- char *tmp, *pos, *signed3 = NULL;
- unsigned char *signature = NULL;
- size_t signature_len = 0, signed3_len;
-
- tmp = os_zalloc(os_strlen(connector) + 5);
- if (!tmp)
- goto fail;
- os_memcpy(tmp, connector, os_strlen(connector));
-
- pos = os_strchr(tmp, '.');
- if (!pos)
- goto fail;
-
- pos = os_strchr(pos + 1, '.');
- if (!pos)
- goto fail;
- pos++;
-
- wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
- pos);
- signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
- if (!signature || signature_len == 0)
- goto fail;
- wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
- signature, signature_len);
- signature[signature_len - 1] ^= 0x01;
- wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
- signature, signature_len);
- signed3 = base64_url_encode(signature, signature_len, &signed3_len);
- if (!signed3)
- goto fail;
- os_memcpy(pos, signed3, signed3_len);
- pos[signed3_len] = '\0';
- wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
- pos);
-
-out:
- os_free(signature);
- os_free(signed3);
- return tmp;
-fail:
- os_free(tmp);
- tmp = NULL;
- goto out;
-}
-#endif /* CONFIG_TESTING_OPTIONS */
-
-
-#ifdef CONFIG_DPP2
-
-struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
- size_t net_access_key_len)
-{
- struct wpabuf *pub = NULL;
- EVP_PKEY *own_key;
- struct dpp_pfs *pfs;
-
- pfs = os_zalloc(sizeof(*pfs));
- if (!pfs)
- return NULL;
-
- own_key = dpp_set_keypair(&pfs->curve, net_access_key,
- net_access_key_len);
- if (!own_key) {
- wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
- goto fail;
- }
- EVP_PKEY_free(own_key);
-
- pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
- if (!pfs->ecdh)
- goto fail;
-
- pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
- pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
- if (!pub)
- goto fail;
-
- pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
- if (!pfs->ie)
- goto fail;
- wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
- wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
- wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
- wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
- wpabuf_put_buf(pfs->ie, pub);
- wpabuf_free(pub);
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
- pfs->ie);
-
- return pfs;
-fail:
- wpabuf_free(pub);
- dpp_pfs_free(pfs);
- return NULL;
-}
-
-
-int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
-{
- if (peer_ie_len < 2)
- return -1;
- if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
- wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
- return -1;
- }
-
- pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
- peer_ie_len - 2);
- pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
- if (!pfs->secret) {
- wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
- return -1;
- }
- wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
- return 0;
-}
-
-
-void dpp_pfs_free(struct dpp_pfs *pfs)
-{
- if (!pfs)
- return;
- crypto_ecdh_deinit(pfs->ecdh);
- wpabuf_free(pfs->ie);
- wpabuf_clear_free(pfs->secret);
- os_free(pfs);
-}
-
-#endif /* CONFIG_DPP2 */
-
-
-static unsigned int dpp_next_id(struct dpp_global *dpp)
+unsigned int dpp_next_id(struct dpp_global *dpp)
{
struct dpp_bootstrap_info *bi;
unsigned int max_id = 0;
@@ -10527,33 +3961,6 @@
}
-struct dpp_bootstrap_info *
-dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
- unsigned int freq)
-{
- struct dpp_bootstrap_info *bi;
-
- bi = os_zalloc(sizeof(*bi));
- if (!bi)
- return NULL;
- bi->id = dpp_next_id(dpp);
- bi->type = DPP_BOOTSTRAP_PKEX;
- os_memcpy(bi->mac_addr, peer, ETH_ALEN);
- bi->num_freq = 1;
- bi->freq[0] = freq;
- bi->curve = pkex->own_bi->curve;
- bi->pubkey = pkex->peer_bootstrap_key;
- pkex->peer_bootstrap_key = NULL;
- if (dpp_bootstrap_key_hash(bi) < 0) {
- dpp_bootstrap_info_free(bi);
- return NULL;
- }
- dpp_pkex_free(pkex);
- dl_list_add(&dpp->bootstrap, &bi->list);
- return bi;
-}
-
-
const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
{
struct dpp_bootstrap_info *bi;
@@ -10582,14 +3989,16 @@
"num_freq=%u\n"
"use_freq=%u\n"
"curve=%s\n"
- "pkhash=%s\n",
+ "pkhash=%s\n"
+ "version=%d\n",
dpp_bootstrap_type_txt(bi->type),
MAC2STR(bi->mac_addr),
bi->info ? bi->info : "",
bi->num_freq,
bi->num_freq == 1 ? bi->freq[0] : 0,
bi->curve->name,
- pkhash);
+ pkhash,
+ bi->version);
}
@@ -10676,11 +4085,11 @@
u8 op_class, channel;
char chan[20];
- if (peer_bi->num_freq == 0)
+ if (peer_bi->num_freq == 0 && !peer_bi->channels_listed)
return 0; /* no channel preference/constraint */
for (i = 0; i < peer_bi->num_freq; i++) {
- if (own_bi->num_freq == 0 ||
+ if ((own_bi->num_freq == 0 && !own_bi->channels_listed) ||
freq_included(own_bi->freq, own_bi->num_freq,
peer_bi->freq[i])) {
freq = peer_bi->freq[i];
@@ -10767,14 +4176,15 @@
int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
{
char *curve = NULL;
- char *key = NULL;
- u8 *privkey = NULL;
- size_t privkey_len = 0;
+ char *key = NULL, *ppkey = NULL;
+ u8 *privkey = NULL, *pp_key = NULL;
+ size_t privkey_len = 0, pp_key_len = 0;
int ret = -1;
struct dpp_configurator *conf = NULL;
curve = get_param(cmd, " curve=");
key = get_param(cmd, " key=");
+ ppkey = get_param(cmd, " ppkey=");
if (key) {
privkey_len = os_strlen(key) / 2;
@@ -10784,7 +4194,16 @@
goto fail;
}
- conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+ if (ppkey) {
+ pp_key_len = os_strlen(key) / 2;
+ pp_key = os_malloc(pp_key_len);
+ if (!pp_key ||
+ hexstr2bin(ppkey, pp_key, pp_key_len) < 0)
+ goto fail;
+ }
+
+ conf = dpp_keygen_configurator(curve, privkey, privkey_len,
+ pp_key, pp_key_len);
if (!conf)
goto fail;
@@ -10795,7 +4214,9 @@
fail:
os_free(curve);
str_clear_free(key);
+ str_clear_free(ppkey);
bin_clear_free(privkey, privkey_len);
+ bin_clear_free(pp_key, pp_key_len);
dpp_configurator_free(conf);
return ret;
}
@@ -10859,12 +4280,12 @@
struct dpp_asymmetric_key *key)
{
struct dpp_configurator *conf;
- const EC_KEY *eckey;
- const EC_GROUP *group;
+ const EC_KEY *eckey, *eckey_pp;
+ const EC_GROUP *group, *group_pp;
int nid;
const struct dpp_curve_params *curve;
- if (!key->csign)
+ if (!key->csign || !key->pp_key)
return -1;
eckey = EVP_PKEY_get0_EC_KEY(key->csign);
if (!eckey)
@@ -10878,6 +4299,18 @@
wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key");
return -1;
}
+ eckey_pp = EVP_PKEY_get0_EC_KEY(key->pp_key);
+ if (!eckey_pp)
+ return -1;
+ group_pp = EC_KEY_get0_group(eckey_pp);
+ if (!group_pp)
+ return -1;
+ if (EC_GROUP_get_curve_name(group) !=
+ EC_GROUP_get_curve_name(group_pp)) {
+ wpa_printf(MSG_INFO,
+ "DPP: Mismatch in c-sign-key and ppKey groups");
+ return -1;
+ }
conf = os_zalloc(sizeof(*conf));
if (!conf)
@@ -10885,6 +4318,8 @@
conf->curve = curve;
conf->csign = key->csign;
key->csign = NULL;
+ conf->pp_key = key->pp_key;
+ key->pp_key = NULL;
conf->own = 1;
if (dpp_configurator_gen_kid(conf) < 0) {
dpp_configurator_free(conf);
@@ -10897,68 +4332,20 @@
}
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
- void *timeout_ctx);
-
-
-static void dpp_connection_free(struct dpp_connection *conn)
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+ const u8 *kid)
{
- if (conn->sock >= 0) {
- wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
- conn->sock);
- eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- close(conn->sock);
- }
- eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- wpabuf_free(conn->msg);
- wpabuf_free(conn->msg_out);
- dpp_auth_deinit(conn->auth);
- os_free(conn);
-}
-
-
-static void dpp_connection_remove(struct dpp_connection *conn)
-{
- dl_list_del(&conn->list);
- dpp_connection_free(conn);
-}
-
-
-static void dpp_tcp_init_flush(struct dpp_global *dpp)
-{
- struct dpp_connection *conn, *tmp;
-
- dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
-}
-
-
-static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
-{
- struct dpp_connection *conn, *tmp;
-
- dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
- os_free(ctrl);
-}
-
-
-static void dpp_relay_flush_controllers(struct dpp_global *dpp)
-{
- struct dpp_relay_controller *ctrl, *tmp;
+ struct dpp_configurator *conf;
if (!dpp)
- return;
+ return NULL;
- dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
- struct dpp_relay_controller, list) {
- dl_list_del(&ctrl->list);
- dpp_relay_controller_free(ctrl);
+ dl_list_for_each(conf, &dpp->configurator,
+ struct dpp_configurator, list) {
+ if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0)
+ return conf;
}
+ return NULL;
}
#endif /* CONFIG_DPP2 */
@@ -10971,10 +4358,8 @@
dpp = os_zalloc(sizeof(*dpp));
if (!dpp)
return NULL;
- dpp->msg_ctx = config->msg_ctx;
#ifdef CONFIG_DPP2
dpp->cb_ctx = config->cb_ctx;
- dpp->process_conf_obj = config->process_conf_obj;
dpp->remove_bi = config->remove_bi;
#endif /* CONFIG_DPP2 */
@@ -11013,1300 +4398,6 @@
#ifdef CONFIG_DPP2
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
-static void dpp_controller_auth_success(struct dpp_connection *conn,
- int initiator);
-
-
-int dpp_relay_add_controller(struct dpp_global *dpp,
- struct dpp_relay_config *config)
-{
- struct dpp_relay_controller *ctrl;
-
- if (!dpp)
- return -1;
-
- ctrl = os_zalloc(sizeof(*ctrl));
- if (!ctrl)
- return -1;
- dl_list_init(&ctrl->conn);
- ctrl->global = dpp;
- os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
- os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
- ctrl->cb_ctx = config->cb_ctx;
- ctrl->tx = config->tx;
- ctrl->gas_resp_tx = config->gas_resp_tx;
- dl_list_add(&dpp->controllers, &ctrl->list);
- return 0;
-}
-
-
-static struct dpp_relay_controller *
-dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
-{
- struct dpp_relay_controller *ctrl;
-
- if (!dpp)
- return NULL;
-
- dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
- list) {
- if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
- return ctrl;
- }
-
- return NULL;
-}
-
-
-static void dpp_controller_gas_done(struct dpp_connection *conn)
-{
- struct dpp_authentication *auth = conn->auth;
-
- if (auth->peer_version >= 2 &&
- auth->conf_resp_status == DPP_STATUS_OK) {
- wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
- auth->waiting_conf_result = 1;
- return;
- }
-
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
- dpp_connection_remove(conn);
-}
-
-
-static int dpp_tcp_send(struct dpp_connection *conn)
-{
- int res;
-
- if (!conn->msg_out) {
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- conn->write_eloop = 0;
- return -1;
- }
- res = send(conn->sock,
- wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
- wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
- strerror(errno));
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->msg_out_pos += res;
- if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
- wpa_printf(MSG_DEBUG,
- "DPP: %u/%u bytes of message sent to Controller",
- (unsigned int) conn->msg_out_pos,
- (unsigned int) wpabuf_len(conn->msg_out));
- if (!conn->write_eloop &&
- eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) == 0)
- conn->write_eloop = 1;
- return 1;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
- wpabuf_free(conn->msg_out);
- conn->msg_out = NULL;
- conn->msg_out_pos = 0;
- eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
- conn->write_eloop = 0;
- if (!conn->read_eloop &&
- eloop_register_sock(conn->sock, EVENT_TYPE_READ,
- dpp_controller_rx, conn, NULL) == 0)
- conn->read_eloop = 1;
- if (conn->on_tcp_tx_complete_remove) {
- dpp_connection_remove(conn);
- } else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done &&
- conn->auth) {
- dpp_controller_gas_done(conn);
- } else if (conn->on_tcp_tx_complete_auth_ok) {
- conn->on_tcp_tx_complete_auth_ok = 0;
- dpp_controller_auth_success(conn, 1);
- }
-
- return 0;
-}
-
-
-static int dpp_tcp_send_msg(struct dpp_connection *conn,
- const struct wpabuf *msg)
-{
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
- if (!conn->msg_out)
- return -1;
- wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
- wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
- wpabuf_len(msg) - 1);
-
- if (dpp_tcp_send(conn) == 1) {
- if (!conn->write_eloop) {
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready,
- conn, NULL) < 0)
- return -1;
- conn->write_eloop = 1;
- }
- }
-
- return 0;
-}
-
-
-static void dpp_controller_start_gas_client(struct dpp_connection *conn)
-{
- struct dpp_authentication *auth = conn->auth;
- struct wpabuf *buf;
- int netrole_ap = 0; /* TODO: make this configurable */
-
- buf = dpp_build_conf_req_helper(auth, "Test", netrole_ap, NULL, NULL);
- if (!buf) {
- wpa_printf(MSG_DEBUG,
- "DPP: No configuration request data available");
- return;
- }
-
- dpp_tcp_send_msg(conn, buf);
- wpabuf_free(buf);
-}
-
-
-static void dpp_controller_auth_success(struct dpp_connection *conn,
- int initiator)
-{
- struct dpp_authentication *auth = conn->auth;
-
- if (!auth)
- return;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
- wpa_msg(conn->global->msg_ctx, MSG_INFO,
- DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
-#ifdef CONFIG_TESTING_OPTIONS
- if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
- wpa_printf(MSG_INFO,
- "DPP: TESTING - stop at Authentication Confirm");
- if (auth->configurator) {
- /* Prevent GAS response */
- auth->auth_success = 0;
- }
- return;
- }
-#endif /* CONFIG_TESTING_OPTIONS */
-
- if (!auth->configurator)
- dpp_controller_start_gas_client(conn);
-}
-
-
-static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
-
- wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
- dpp_tcp_send(conn);
-}
-
-
-static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
- const struct hostapd_ip_addr *ipaddr,
- int port)
-{
- struct sockaddr_in *dst;
-#ifdef CONFIG_IPV6
- struct sockaddr_in6 *dst6;
-#endif /* CONFIG_IPV6 */
-
- switch (ipaddr->af) {
- case AF_INET:
- dst = (struct sockaddr_in *) addr;
- os_memset(dst, 0, sizeof(*dst));
- dst->sin_family = AF_INET;
- dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
- dst->sin_port = htons(port);
- *addrlen = sizeof(*dst);
- break;
-#ifdef CONFIG_IPV6
- case AF_INET6:
- dst6 = (struct sockaddr_in6 *) addr;
- os_memset(dst6, 0, sizeof(*dst6));
- dst6->sin6_family = AF_INET6;
- os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
- sizeof(struct in6_addr));
- dst6->sin6_port = htons(port);
- *addrlen = sizeof(*dst6);
- break;
-#endif /* CONFIG_IPV6 */
- default:
- return -1;
- }
-
- return 0;
-}
-
-
-static struct dpp_connection *
-dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
- unsigned int freq)
-{
- struct dpp_connection *conn;
- struct sockaddr_storage addr;
- socklen_t addrlen;
- char txt[100];
-
- if (dl_list_len(&ctrl->conn) >= 15) {
- wpa_printf(MSG_DEBUG,
- "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
- return NULL;
- }
-
- if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
- &ctrl->ipaddr, DPP_TCP_PORT) < 0)
- return NULL;
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn)
- return NULL;
-
- conn->global = ctrl->global;
- conn->relay = ctrl;
- os_memcpy(conn->mac_addr, src, ETH_ALEN);
- conn->freq = freq;
-
- conn->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (conn->sock < 0)
- goto fail;
- wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
- conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
- if (errno != EINPROGRESS) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
- strerror(errno));
- goto fail;
- }
-
- /*
- * Continue connecting in the background; eloop will call us
- * once the connection is ready (or failed).
- */
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) < 0)
- goto fail;
- conn->write_eloop = 1;
-
- /* TODO: eloop timeout to clear a connection if it does not complete
- * properly */
-
- dl_list_add(&ctrl->conn, &conn->list);
- return conn;
-fail:
- dpp_connection_free(conn);
- return NULL;
-}
-
-
-static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
-{
- struct wpabuf *msg;
-
- msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
- if (!msg)
- return NULL;
- wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
- wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
- wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
- wpabuf_put_data(msg, buf, len);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
- return msg;
-}
-
-
-static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
- const u8 *buf, size_t len)
-{
- u8 type = hdr[DPP_HDR_LEN - 1];
-
- wpa_printf(MSG_DEBUG,
- "DPP: Continue already established Relay/Controller connection for this session");
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
- if (!conn->msg_out) {
- dpp_connection_remove(conn);
- return -1;
- }
-
- /* TODO: for proto ver 1, need to do remove connection based on GAS Resp
- * TX status */
- if (type == DPP_PA_CONFIGURATION_RESULT)
- conn->on_tcp_tx_complete_remove = 1;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
- const u8 *buf, size_t len, unsigned int freq,
- const u8 *i_bootstrap, const u8 *r_bootstrap)
-{
- struct dpp_relay_controller *ctrl;
- struct dpp_connection *conn;
- u8 type = hdr[DPP_HDR_LEN - 1];
-
- /* Check if there is an already started session for this peer and if so,
- * continue that session (send this over TCP) and return 0.
- */
- if (type != DPP_PA_PEER_DISCOVERY_REQ &&
- type != DPP_PA_PEER_DISCOVERY_RESP &&
- type != DPP_PA_PRESENCE_ANNOUNCEMENT) {
- dl_list_for_each(ctrl, &dpp->controllers,
- struct dpp_relay_controller, list) {
- dl_list_for_each(conn, &ctrl->conn,
- struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr,
- ETH_ALEN) == 0)
- return dpp_relay_tx(conn, hdr, buf, len);
- }
- }
- }
-
- if (!r_bootstrap)
- return -1;
-
- if (type == DPP_PA_PRESENCE_ANNOUNCEMENT) {
- /* TODO: Could send this to all configured Controllers. For now,
- * only the first Controller is supported. */
- ctrl = dl_list_first(&dpp->controllers,
- struct dpp_relay_controller, list);
- } else {
- ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
- }
- if (!ctrl)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Authentication Request for a configured Controller");
- conn = dpp_relay_new_conn(ctrl, src, freq);
- if (!conn)
- return -1;
-
- conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
- if (!conn->msg_out) {
- dpp_connection_remove(conn);
- return -1;
- }
- /* Message will be sent in dpp_conn_tx_ready() */
-
- return 0;
-}
-
-
-int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
- size_t data_len)
-{
- struct dpp_relay_controller *ctrl;
- struct dpp_connection *conn, *found = NULL;
- struct wpabuf *msg;
-
- /* Check if there is a successfully completed authentication for this
- * and if so, continue that session (send this over TCP) and return 0.
- */
- dl_list_for_each(ctrl, &dpp->controllers,
- struct dpp_relay_controller, list) {
- if (found)
- break;
- dl_list_for_each(conn, &ctrl->conn,
- struct dpp_connection, list) {
- if (os_memcmp(src, conn->mac_addr,
- ETH_ALEN) == 0) {
- found = conn;
- break;
- }
- }
- }
-
- if (!found)
- return -1;
-
- msg = wpabuf_alloc(4 + 1 + data_len);
- if (!msg)
- return -1;
- wpabuf_put_be32(msg, 1 + data_len);
- wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
- wpabuf_put_data(msg, data, data_len);
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
-
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = msg;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-static void dpp_controller_free(struct dpp_controller *ctrl)
-{
- struct dpp_connection *conn, *tmp;
-
- if (!ctrl)
- return;
-
- dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
- list)
- dpp_connection_remove(conn);
-
- if (ctrl->sock >= 0) {
- close(ctrl->sock);
- eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
- }
- os_free(ctrl->configurator_params);
- os_free(ctrl);
-}
-
-
-static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- const u8 *r_bootstrap, *i_bootstrap;
- u16 r_bootstrap_len, i_bootstrap_len;
- struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
-
- r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_INFO,
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
-
- i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
- &i_bootstrap_len);
- if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
- wpa_printf(MSG_INFO,
- "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
- i_bootstrap, i_bootstrap_len);
-
- /* Try to find own and peer bootstrapping key matches based on the
- * received hash values */
- dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
- &own_bi, &peer_bi);
- if (!own_bi) {
- wpa_printf(MSG_INFO,
- "No matching own bootstrapping key found - ignore message");
- return -1;
- }
-
- if (conn->auth) {
- wpa_printf(MSG_INFO,
- "Already in DPP authentication exchange - ignore new one");
- return 0;
- }
-
- conn->auth = dpp_auth_req_rx(conn->ctrl->global,
- conn->ctrl->global->msg_ctx,
- conn->ctrl->allowed_roles,
- conn->ctrl->qr_mutual,
- peer_bi, own_bi, -1, hdr, buf, len);
- if (!conn->auth) {
- wpa_printf(MSG_DEBUG, "DPP: No response generated");
- return -1;
- }
-
- if (dpp_set_configurator(conn->auth,
- conn->ctrl->configurator_params) < 0) {
- dpp_connection_remove(conn);
- return -1;
- }
-
- return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
-}
-
-
-static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- struct wpabuf *msg;
- int res;
-
- if (!auth)
- return -1;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
-
- msg = dpp_auth_resp_rx(auth, hdr, buf, len);
- if (!msg) {
- if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
- wpa_printf(MSG_DEBUG,
- "DPP: Start wait for full response");
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->on_tcp_tx_complete_auth_ok = 1;
- res = dpp_tcp_send_msg(conn, msg);
- wpabuf_free(msg);
- return res;
-}
-
-
-static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf, size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
-
- wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
-
- if (!auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Authentication in progress - drop");
- return -1;
- }
-
- if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
- return -1;
- }
-
- dpp_controller_auth_success(conn, 0);
- return 0;
-}
-
-
-static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
- void *timeout_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
-
- if (!conn->auth->waiting_conf_result)
- return;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Timeout while waiting for Connection Status Result");
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONN_STATUS_RESULT "timeout");
- dpp_connection_remove(conn);
-}
-
-
-static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- enum dpp_status_error status;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
-
- if (!auth || !auth->waiting_conf_result) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Configuration waiting for result - drop");
- return -1;
- }
-
- status = dpp_conf_result_rx(auth, hdr, buf, len);
- if (status == DPP_STATUS_OK && auth->send_conn_status) {
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_SENT "wait_conn_status=1");
- wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
- eloop_cancel_timeout(
- dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- eloop_register_timeout(
- 16, 0, dpp_controller_conn_status_result_wait_timeout,
- conn, NULL);
- return 0;
- }
- if (status == DPP_STATUS_OK)
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_SENT);
- else
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_FAILED);
- return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- struct dpp_authentication *auth = conn->auth;
- enum dpp_status_error status;
- u8 ssid[SSID_MAX_LEN];
- size_t ssid_len = 0;
- char *channel_list = NULL;
-
- if (!conn->ctrl)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
-
- if (!auth || !auth->waiting_conn_status_result) {
- wpa_printf(MSG_DEBUG,
- "DPP: No DPP Configuration waiting for connection status result - drop");
- return -1;
- }
-
- status = dpp_conn_status_result_rx(auth, hdr, buf, len,
- ssid, &ssid_len, &channel_list);
- wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO,
- DPP_EVENT_CONN_STATUS_RESULT
- "result=%d ssid=%s channel_list=%s",
- status, wpa_ssid_txt(ssid, ssid_len),
- channel_list ? channel_list : "N/A");
- os_free(channel_list);
- return -1; /* to remove the completed connection */
-}
-
-
-static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
- const u8 *hdr, const u8 *buf,
- size_t len)
-{
- const u8 *r_bootstrap;
- u16 r_bootstrap_len;
- struct dpp_bootstrap_info *peer_bi;
- struct dpp_authentication *auth;
- struct dpp_global *dpp = conn->ctrl->global;
-
- if (conn->auth) {
- wpa_printf(MSG_DEBUG,
- "DPP: Ignore Presence Announcement during ongoing Authentication");
- return -1;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
-
- r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
- &r_bootstrap_len);
- if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
- wpa_msg(dpp->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
- "Missing or invalid required Responder Bootstrapping Key Hash attribute");
- return -1;
- }
- wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
- r_bootstrap, r_bootstrap_len);
- peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
- if (!peer_bi) {
- wpa_printf(MSG_DEBUG,
- "DPP: No matching bootstrapping information found");
- return -1;
- }
-
- auth = dpp_auth_init(dpp, dpp->msg_ctx, peer_bi, NULL,
- DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
- if (!auth)
- return -1;
- if (dpp_set_configurator(conn->auth,
- conn->ctrl->configurator_params) < 0) {
- dpp_auth_deinit(auth);
- dpp_connection_remove(conn);
- return -1;
- }
-
- conn->auth = auth;
- return dpp_tcp_send_msg(conn, conn->auth->req_msg);
-}
-
-
-static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- const u8 *pos, *end;
- u8 type;
-
- wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
- pos = msg;
- end = msg + len;
-
- if (end - pos < DPP_HDR_LEN ||
- WPA_GET_BE24(pos) != OUI_WFA ||
- pos[3] != DPP_OUI_TYPE) {
- wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
- return -1;
- }
-
- if (pos[4] != 1) {
- wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
- pos[4]);
- return -1;
- }
- type = pos[5];
- wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
- pos += DPP_HDR_LEN;
-
- wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
- pos, end - pos);
- if (dpp_check_attrs(pos, end - pos) < 0)
- return -1;
-
- if (conn->relay) {
- wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
- conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
- conn->freq, msg, len);
- return 0;
- }
-
- switch (type) {
- case DPP_PA_AUTHENTICATION_REQ:
- return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
- case DPP_PA_AUTHENTICATION_RESP:
- return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
- case DPP_PA_AUTHENTICATION_CONF:
- return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
- case DPP_PA_CONFIGURATION_RESULT:
- return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
- case DPP_PA_CONNECTION_STATUS_RESULT:
- return dpp_controller_rx_conn_status_result(conn, msg, pos,
- end - pos);
- case DPP_PA_PRESENCE_ANNOUNCEMENT:
- return dpp_controller_rx_presence_announcement(conn, msg, pos,
- end - pos);
- default:
- /* TODO: missing messages types */
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported frame subtype %d", type);
- return -1;
- }
-}
-
-
-static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- const u8 *pos, *end, *next;
- u8 dialog_token;
- const u8 *adv_proto;
- u16 slen;
- struct wpabuf *resp, *buf;
- struct dpp_authentication *auth = conn->auth;
-
- if (len < 1 + 2)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Received DPP Configuration Request over TCP");
-
- if (!conn->ctrl || !auth || !auth->auth_success) {
- wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
- return -1;
- }
-
- pos = msg;
- end = msg + len;
-
- dialog_token = *pos++;
- adv_proto = pos++;
- slen = *pos++;
- if (*adv_proto != WLAN_EID_ADV_PROTO ||
- slen > end - pos || slen < 2)
- return -1;
-
- next = pos + slen;
- pos++; /* skip QueryRespLenLimit and PAME-BI */
-
- if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
- pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
- pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
- return -1;
-
- pos = next;
- /* Query Request */
- if (end - pos < 2)
- return -1;
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (slen > end - pos)
- return -1;
-
- resp = dpp_conf_req_rx(auth, pos, slen);
- if (!resp)
- return -1;
-
- buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp));
- if (!buf) {
- wpabuf_free(resp);
- return -1;
- }
-
- wpabuf_put_be32(buf, 18 + wpabuf_len(resp));
-
- wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
- wpabuf_put_u8(buf, dialog_token);
- wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
- wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
-
- dpp_write_adv_proto(buf);
- dpp_write_gas_query(buf, resp);
- wpabuf_free(resp);
-
- /* Send Config Response over TCP; GAS fragmentation is taken care of by
- * the Relay */
- wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
- wpabuf_free(conn->msg_out);
- conn->msg_out_pos = 0;
- conn->msg_out = buf;
- conn->on_tcp_tx_complete_gas_done = 1;
- dpp_tcp_send(conn);
- return 0;
-}
-
-
-static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
-{
- struct dpp_authentication *auth = conn->auth;
- int res;
- struct wpabuf *msg;
- enum dpp_status_error status;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Configuration Response for local stack from TCP");
-
- res = dpp_conf_resp_rx(auth, resp);
- wpabuf_free(resp);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
- return -1;
- }
-
- if (conn->global->process_conf_obj)
- res = conn->global->process_conf_obj(conn->global->cb_ctx,
- auth);
- else
- res = 0;
-
- if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
- return -1;
-
- wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
- status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
- msg = dpp_build_conf_result(auth, status);
- if (!msg)
- return -1;
-
- conn->on_tcp_tx_complete_remove = 1;
- res = dpp_tcp_send_msg(conn, msg);
- wpabuf_free(msg);
-
- /* This exchange will be terminated in the TX status handler */
-
- return res;
-}
-
-
-static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
- size_t len)
-{
- struct wpabuf *buf;
- u8 dialog_token;
- const u8 *pos, *end, *next, *adv_proto;
- u16 status, slen;
-
- if (len < 5 + 2)
- return -1;
-
- wpa_printf(MSG_DEBUG,
- "DPP: Received DPP Configuration Response over TCP");
-
- pos = msg;
- end = msg + len;
-
- dialog_token = *pos++;
- status = WPA_GET_LE16(pos);
- if (status != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
- return -1;
- }
- pos += 2;
- pos += 2; /* ignore GAS Comeback Delay */
-
- adv_proto = pos++;
- slen = *pos++;
- if (*adv_proto != WLAN_EID_ADV_PROTO ||
- slen > end - pos || slen < 2)
- return -1;
-
- next = pos + slen;
- pos++; /* skip QueryRespLenLimit and PAME-BI */
-
- if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
- pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
- pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
- return -1;
-
- pos = next;
- /* Query Response */
- if (end - pos < 2)
- return -1;
- slen = WPA_GET_LE16(pos);
- pos += 2;
- if (slen > end - pos)
- return -1;
-
- buf = wpabuf_alloc(slen);
- if (!buf)
- return -1;
- wpabuf_put_data(buf, pos, slen);
-
- if (!conn->relay && !conn->ctrl)
- return dpp_tcp_rx_gas_resp(conn, buf);
-
- if (!conn->relay) {
- wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
- wpabuf_free(buf);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
- conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
- dialog_token, 0, buf);
-
- return 0;
-}
-
-
-static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_connection *conn = eloop_ctx;
- int res;
- const u8 *pos;
-
- wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
- sd);
-
- if (conn->msg_len_octets < 4) {
- u32 msglen;
-
- res = recv(sd, &conn->msg_len[conn->msg_len_octets],
- 4 - conn->msg_len_octets, 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
- strerror(errno));
- dpp_connection_remove(conn);
- return;
- }
- if (res == 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: No more data available over TCP");
- dpp_connection_remove(conn);
- return;
- }
- wpa_printf(MSG_DEBUG,
- "DPP: Received %d/%d octet(s) of message length field",
- res, (int) (4 - conn->msg_len_octets));
- conn->msg_len_octets += res;
-
- if (conn->msg_len_octets < 4) {
- wpa_printf(MSG_DEBUG,
- "DPP: Need %d more octets of message length field",
- (int) (4 - conn->msg_len_octets));
- return;
- }
-
- msglen = WPA_GET_BE32(conn->msg_len);
- wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
- if (msglen > 65535) {
- wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
- dpp_connection_remove(conn);
- return;
- }
-
- wpabuf_free(conn->msg);
- conn->msg = wpabuf_alloc(msglen);
- }
-
- if (!conn->msg) {
- wpa_printf(MSG_DEBUG,
- "DPP: No buffer available for receiving the message");
- dpp_connection_remove(conn);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
- (unsigned int) wpabuf_tailroom(conn->msg));
-
- res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
- dpp_connection_remove(conn);
- return;
- }
- if (res == 0) {
- wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
- dpp_connection_remove(conn);
- return;
- }
- wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
- wpabuf_put(conn->msg, res);
-
- if (wpabuf_tailroom(conn->msg) > 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Need %u more octets of message payload",
- (unsigned int) wpabuf_tailroom(conn->msg));
- return;
- }
-
- conn->msg_len_octets = 0;
- wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
- if (wpabuf_len(conn->msg) < 1) {
- dpp_connection_remove(conn);
- return;
- }
-
- pos = wpabuf_head(conn->msg);
- switch (*pos) {
- case WLAN_PA_VENDOR_SPECIFIC:
- if (dpp_controller_rx_action(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- case WLAN_PA_GAS_INITIAL_REQ:
- if (dpp_controller_rx_gas_req(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- case WLAN_PA_GAS_INITIAL_RESP:
- if (dpp_rx_gas_resp(conn, pos + 1,
- wpabuf_len(conn->msg) - 1) < 0)
- dpp_connection_remove(conn);
- break;
- default:
- wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
- *pos);
- break;
- }
-}
-
-
-static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
-{
- struct dpp_controller *ctrl = eloop_ctx;
- struct sockaddr_in addr;
- socklen_t addr_len = sizeof(addr);
- int fd;
- struct dpp_connection *conn;
-
- wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
-
- fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
- if (fd < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Failed to accept new connection: %s",
- strerror(errno));
- return;
- }
- wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
- inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn)
- goto fail;
-
- conn->global = ctrl->global;
- conn->ctrl = ctrl;
- conn->sock = fd;
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
- dpp_controller_rx, conn, NULL) < 0)
- goto fail;
- conn->read_eloop = 1;
-
- /* TODO: eloop timeout to expire connections that do not complete in
- * reasonable time */
- dl_list_add(&ctrl->conn, &conn->list);
- return;
-
-fail:
- close(fd);
- os_free(conn);
-}
-
-
-int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
- const struct hostapd_ip_addr *addr, int port)
-{
- struct dpp_connection *conn;
- struct sockaddr_storage saddr;
- socklen_t addrlen;
- const u8 *hdr, *pos, *end;
- char txt[100];
-
- wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
- hostapd_ip_txt(addr, txt, sizeof(txt)), port);
- if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
- addr, port) < 0) {
- dpp_auth_deinit(auth);
- return -1;
- }
-
- conn = os_zalloc(sizeof(*conn));
- if (!conn) {
- dpp_auth_deinit(auth);
- return -1;
- }
-
- conn->global = dpp;
- conn->auth = auth;
- conn->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (conn->sock < 0)
- goto fail;
-
- if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
- wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
- if (errno != EINPROGRESS) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
- strerror(errno));
- goto fail;
- }
-
- /*
- * Continue connecting in the background; eloop will call us
- * once the connection is ready (or failed).
- */
- }
-
- if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
- dpp_conn_tx_ready, conn, NULL) < 0)
- goto fail;
- conn->write_eloop = 1;
-
- hdr = wpabuf_head(auth->req_msg);
- end = hdr + wpabuf_len(auth->req_msg);
- hdr += 2; /* skip Category and Actiom */
- pos = hdr + DPP_HDR_LEN;
- conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
- if (!conn->msg_out)
- goto fail;
- /* Message will be sent in dpp_conn_tx_ready() */
-
- /* TODO: eloop timeout to clear a connection if it does not complete
- * properly */
- dl_list_add(&dpp->tcp_init, &conn->list);
- return 0;
-fail:
- dpp_connection_free(conn);
- return -1;
-}
-
-
-int dpp_controller_start(struct dpp_global *dpp,
- struct dpp_controller_config *config)
-{
- struct dpp_controller *ctrl;
- int on = 1;
- struct sockaddr_in sin;
- int port;
-
- if (!dpp || dpp->controller)
- return -1;
-
- ctrl = os_zalloc(sizeof(*ctrl));
- if (!ctrl)
- return -1;
- ctrl->global = dpp;
- if (config->configurator_params)
- ctrl->configurator_params =
- os_strdup(config->configurator_params);
- dl_list_init(&ctrl->conn);
- /* TODO: configure these somehow */
- ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
- ctrl->qr_mutual = 0;
-
- ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
- if (ctrl->sock < 0)
- goto fail;
-
- if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on)) < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: setsockopt(SO_REUSEADDR) failed: %s",
- strerror(errno));
- /* try to continue anyway */
- }
-
- if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
- wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
- strerror(errno));
- goto fail;
- }
-
- /* TODO: IPv6 */
- os_memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
- sin.sin_port = htons(port);
- if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- wpa_printf(MSG_INFO,
- "DPP: Failed to bind Controller TCP port: %s",
- strerror(errno));
- goto fail;
- }
- if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
- fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
- eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
- dpp_controller_tcp_cb, ctrl, NULL))
- goto fail;
-
- dpp->controller = ctrl;
- wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
- return 0;
-fail:
- dpp_controller_free(ctrl);
- return -1;
-}
-
-
-void dpp_controller_stop(struct dpp_global *dpp)
-{
- if (dpp) {
- dpp_controller_free(dpp->controller);
- dpp->controller = NULL;
- }
-}
-
-
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
{
struct wpabuf *msg;
@@ -12324,4 +4415,16 @@
return msg;
}
+
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+ unsigned int freq, const u8 *hash)
+{
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN);
+ wpa_msg(msg_ctx, MSG_INFO,
+ DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s",
+ id, MAC2STR(src), freq, hex);
+}
+
#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 585d398..99a1ca7 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -20,6 +20,19 @@
struct crypto_ecdh;
struct hostapd_ip_addr;
struct dpp_global;
+struct json_token;
+struct dpp_reconfig_id;
+
+#ifdef CONFIG_TESTING_OPTIONS
+#define DPP_VERSION (dpp_version_override)
+extern int dpp_version_override;
+#else /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+#define DPP_VERSION 2
+#else
+#define DPP_VERSION 1
+#endif
+#endif /* CONFIG_TESTING_OPTIONS */
#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
#define DPP_TCP_PORT 7871
@@ -75,6 +88,9 @@
DPP_ATTR_RECONFIG_FLAGS = 0x101D,
DPP_ATTR_C_SIGN_KEY_HASH = 0x101E,
DPP_ATTR_CSR_ATTR_REQ = 0x101F,
+ DPP_ATTR_A_NONCE = 0x1020,
+ DPP_ATTR_E_PRIME_ID = 0x1021,
+ DPP_ATTR_CONFIGURATOR_NONCE = 0x1022,
};
enum dpp_status_error {
@@ -94,6 +110,12 @@
DPP_STATUS_CSR_BAD = 13,
};
+/* DPP Reconfig Flags object - connectorKey values */
+enum dpp_connector_key {
+ DPP_CONFIG_REUSEKEY = 0,
+ DPP_CONFIG_REPLACEKEY = 1,
+};
+
#define DPP_CAPAB_ENROLLEE BIT(0)
#define DPP_CAPAB_CONFIGURATOR BIT(1)
#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
@@ -102,6 +124,7 @@
#define DPP_MAX_NONCE_LEN 32
#define DPP_MAX_HASH_LEN 64
#define DPP_MAX_SHARED_SECRET_LEN 66
+#define DPP_CP_LEN 64
struct dpp_curve_params {
const char *name;
@@ -131,6 +154,8 @@
char *pk;
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq;
+ bool channels_listed;
+ u8 version;
int own;
EVP_PKEY *pubkey;
u8 pubkey_hash[SHA256_MAC_LEN];
@@ -177,6 +202,7 @@
DPP_AKM_PSK_SAE,
DPP_AKM_SAE_DPP,
DPP_AKM_PSK_SAE_DPP,
+ DPP_AKM_DOT1X,
};
enum dpp_netrole {
@@ -202,11 +228,14 @@
char *passphrase;
u8 psk[32];
int psk_set;
+
+ char *csrattrs;
};
struct dpp_asymmetric_key {
struct dpp_asymmetric_key *next;
EVP_PKEY *csign;
+ EVP_PKEY *pp_key;
char *config_template;
char *connector_template;
};
@@ -222,20 +251,29 @@
struct dpp_bootstrap_info *peer_bi;
struct dpp_bootstrap_info *own_bi;
struct dpp_bootstrap_info *tmp_own_bi;
+ struct dpp_bootstrap_info *tmp_peer_bi;
u8 waiting_pubkey_hash[SHA256_MAC_LEN];
int response_pending;
+ int reconfig;
+ enum dpp_connector_key reconfig_connector_key;
enum dpp_status_error auth_resp_status;
enum dpp_status_error conf_resp_status;
+ enum dpp_status_error force_conf_resp_status;
u8 peer_mac_addr[ETH_ALEN];
u8 i_nonce[DPP_MAX_NONCE_LEN];
u8 r_nonce[DPP_MAX_NONCE_LEN];
u8 e_nonce[DPP_MAX_NONCE_LEN];
+ u8 c_nonce[DPP_MAX_NONCE_LEN];
u8 i_capab;
u8 r_capab;
+ enum dpp_netrole e_netrole;
EVP_PKEY *own_protocol_key;
EVP_PKEY *peer_protocol_key;
+ EVP_PKEY *reconfig_old_protocol_key;
struct wpabuf *req_msg;
struct wpabuf *resp_msg;
+ struct wpabuf *reconfig_req_msg;
+ struct wpabuf *reconfig_resp_msg;
/* Intersection of possible frequencies for initiating DPP
* Authentication exchange */
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
@@ -253,6 +291,7 @@
u8 k1[DPP_MAX_HASH_LEN];
u8 k2[DPP_MAX_HASH_LEN];
u8 ke[DPP_MAX_HASH_LEN];
+ u8 bk[DPP_MAX_HASH_LEN];
int initiator;
int waiting_auth_resp;
int waiting_auth_conf;
@@ -265,8 +304,10 @@
int waiting_conf_result;
int waiting_conn_status_result;
int auth_success;
+ bool reconfig_success;
struct wpabuf *conf_req;
const struct wpabuf *conf_resp; /* owned by GAS server */
+ struct wpabuf *conf_resp_tcp;
struct dpp_configuration *conf_ap;
struct dpp_configuration *conf2_ap;
struct dpp_configuration *conf_sta;
@@ -283,6 +324,11 @@
int psk_set;
enum dpp_akm akm;
struct wpabuf *c_sign_key;
+ struct wpabuf *certbag;
+ struct wpabuf *certs;
+ struct wpabuf *cacert;
+ char *server_name;
+ struct wpabuf *pp_key;
} conf_obj[DPP_MAX_CONF_OBJ];
unsigned int num_conf_obj;
struct dpp_asymmetric_key *conf_key_pkg;
@@ -292,6 +338,17 @@
int conn_status_requested;
int akm_use_selector;
int configurator_set;
+ u8 transaction_id;
+ u8 *csrattrs;
+ size_t csrattrs_len;
+ bool waiting_csr;
+ struct wpabuf *csr;
+ struct wpabuf *priv_key; /* DER-encoded private key used for csr */
+ bool waiting_cert;
+ char *trusted_eap_server_name;
+ struct wpabuf *cacert;
+ struct wpabuf *certbag;
+ void *cert_resp_ctx;
#ifdef CONFIG_TESTING_OPTIONS
char *config_obj_override;
char *discovery_override;
@@ -307,8 +364,12 @@
unsigned int id;
int own;
EVP_PKEY *csign;
+ u8 kid_hash[SHA256_MAC_LEN];
char *kid;
const struct dpp_curve_params *curve;
+ char *connector; /* own Connector for reconfiguration */
+ EVP_PKEY *connector_key;
+ EVP_PKEY *pp_key;
};
struct dpp_introduction {
@@ -321,6 +382,7 @@
const struct hostapd_ip_addr *ipaddr;
const u8 *pkhash;
+ void *msg_ctx;
void *cb_ctx;
void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
size_t len);
@@ -331,6 +393,12 @@
struct dpp_controller_config {
const char *configurator_params;
int tcp_port;
+ u8 allowed_roles;
+ int qr_mutual;
+ enum dpp_netrole netrole;
+ void *msg_ctx;
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
};
#ifdef CONFIG_TESTING_OPTIONS
@@ -488,6 +556,10 @@
int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd);
void dpp_auth_deinit(struct dpp_authentication *auth);
struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+ u16 e_nonce_len, enum dpp_netrole netrole,
+ bool cert_req);
+struct wpabuf *
dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
size_t attr_len);
int dpp_conf_resp_rx(struct dpp_authentication *auth,
@@ -517,9 +589,6 @@
int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
size_t buflen);
void dpp_configurator_free(struct dpp_configurator *conf);
-struct dpp_configurator *
-dpp_keygen_configurator(const char *curve, const u8 *privkey,
- size_t privkey_len);
int dpp_configurator_own_config(struct dpp_authentication *auth,
const char *curve, int ap);
enum dpp_status_error
@@ -564,6 +633,11 @@
int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
void dpp_pfs_free(struct dpp_pfs *pfs);
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
+ const char *name);
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7);
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
+
struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
const char *uri);
struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
@@ -591,6 +665,8 @@
char *buf, size_t buflen);
int dpp_configurator_from_backup(struct dpp_global *dpp,
struct dpp_asymmetric_key *key);
+struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
+ const u8 *kid);
int dpp_relay_add_controller(struct dpp_global *dpp,
struct dpp_relay_config *config);
int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
@@ -601,14 +677,23 @@
int dpp_controller_start(struct dpp_global *dpp,
struct dpp_controller_config *config);
void dpp_controller_stop(struct dpp_global *dpp);
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+ unsigned int id);
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+ struct dpp_bootstrap_info *bi);
int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
- const struct hostapd_ip_addr *addr, int port);
+ const struct hostapd_ip_addr *addr, int port,
+ const char *name, enum dpp_netrole netrole, void *msg_ctx,
+ void *cb_ctx,
+ int (*process_conf_obj)(void *ctx,
+ struct dpp_authentication *auth));
+
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi);
+void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
+ unsigned int freq, const u8 *hash);
struct dpp_global_config {
- void *msg_ctx;
void *cb_ctx;
- int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
};
@@ -616,5 +701,37 @@
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);
+/* dpp_reconfig.c */
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct dpp_reconfig_id *id);
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_configurator *conf, unsigned int freq, u16 group,
+ const u8 *a_nonce_attr, size_t a_nonce_len,
+ const u8 *e_id_attr, size_t e_id_len);
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+ const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ unsigned int freq, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *pp_key,
+ size_t pp_key_len);
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
+
#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/src/common/dpp_auth.c b/src/common/dpp_auth.c
new file mode 100644
index 0000000..f79cfef
--- /dev/null
+++ b/src/common/dpp_auth.c
@@ -0,0 +1,1976 @@
+/*
+ * DPP authentication exchange
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/random.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication success - clear temporary keys");
+ os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
+ os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
+ os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
+ os_memset(auth->k1, 0, sizeof(auth->k1));
+ os_memset(auth->k2, 0, sizeof(auth->k2));
+
+ auth->auth_success = 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
+ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+ u8 *pos;
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end;
+
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (DPP_VERSION > 1) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_tries = 0;
+
+ /* Build DPP Authentication Response frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (auth->peer_version >= 2) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_len;
+ }
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *msg, *pr = NULL;
+ u8 r_auth[4 + DPP_MAX_HASH_LEN];
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+ size_t wrapped_r_auth_len;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+ EVP_PKEY_free(auth->own_protocol_key);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ /* ECDH: N = pR * PI */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+ auth->Nx, &secret_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_responder(auth) < 0)
+ goto fail;
+ }
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ r_auth, 4 + auth->curve->hash_len,
+ 0, NULL, NULL, wrapped_r_auth) < 0)
+ goto fail;
+ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+ wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
+ if (!msg)
+ goto fail;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ ret = 0;
+fail:
+ wpabuf_free(pr);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->own_bi)
+ return -1;
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
+ if (!msg)
+ return -1;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
+ int qr_mutual, struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len)
+{
+ EVP_PKEY *pi = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+ i_bootstrap_len, channel_len;
+ struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ goto fail;
+ if (peer_bi && peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = own_bi->curve;
+ auth->curr_freq = freq;
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
+ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+ &i_proto_len);
+ if (!i_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+ i_proto, i_proto_len);
+
+ /* M = bR * PI */
+ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+ if (!pi) {
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+ if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_I_CAPABILITIES,
+ &i_capab_len);
+ if (!i_capab || i_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+ goto fail;
+ }
+ auth->i_capab = i_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ unwrapped = NULL;
+
+ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+ case DPP_CAPAB_ENROLLEE:
+ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ break;
+ case DPP_CAPAB_CONFIGURATOR:
+ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Enrollee role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
+ }
+
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+ if (dpp_auth_build_resp_status(auth,
+ DPP_STATUS_RESPONSE_PENDING) < 0)
+ goto fail;
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+ auth->response_pending = 1;
+ os_memcpy(auth->waiting_pubkey_hash,
+ i_bootstrap, i_bootstrap_len);
+ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+ i_bootstrap_len);
+ } else {
+ hex[0] = '\0';
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+ "%s", hex);
+ return auth;
+ }
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ goto fail;
+
+ return auth;
+
+not_compatible:
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "i-capab=0x%02x", auth->i_capab);
+ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+ auth->configurator = 1;
+ else
+ auth->configurator = 0;
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+ goto fail;
+
+ auth->remove_on_tx_status = 1;
+ return auth;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ EVP_PKEY_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ dpp_auth_deinit(auth);
+ return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ if (!auth || !auth->response_pending ||
+ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+ MACSTR, MAC2STR(auth->peer_mac_addr));
+ auth->peer_bi = peer_bi;
+
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return -1;
+
+ return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ u8 i_auth[4 + DPP_MAX_HASH_LEN];
+ size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
+ const u8 *addr[2];
+ size_t len[2], attr_len;
+ u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
+ u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+ i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
+ /* Build DPP Authentication Confirmation frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Confirmation frame attributes",
+ msg);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
+ dpp_gen_uri(bi) < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ return 0;
+fail:
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+ if (peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = peer_bi->curve;
+
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pi)
+ goto fail;
+
+ /* ECDH: M = pI * BR */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
+ auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
+ neg_freq = 0;
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
+ goto fail;
+
+out:
+ wpabuf_free(pi);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data, u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *i_nonce, *r_capab;
+ u16 i_nonce_len, r_capab_len;
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported incompatible roles");
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported more time needed");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported failure (status %d)",
+ status);
+ dpp_auth_fail(auth, "Responder reported failure");
+ return;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "r-capab=0x%02x", auth->r_capab);
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
+ }
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ EVP_PKEY *pr;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL, *unwrapped2 = NULL;
+ size_t unwrapped_len = 0, unwrapped2_len = 0;
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+ wrapped2_len, r_auth_len;
+ u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator || !auth->peer_bi || auth->reconfig) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ return NULL;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->own_bi ||
+ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
+ return NULL;
+ }
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ } else if (auth->own_bi &&
+ auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
+ auth->own_bi->nfc_negotiated) {
+ /* NFC negotiated connection handover bootstrapping mandates
+ * use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ return NULL;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ auth->auth_resp_status = status[0];
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_resp_rx_status(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+ return NULL;
+ }
+
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ /* N = pI * PR */
+ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ return NULL;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+ if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ if (auth->own_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_initiator(auth) < 0)
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+ }
+
+ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped2, wrapped2_len);
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+ unwrapped2 = os_malloc(unwrapped2_len);
+ if (!unwrapped2)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped2, wrapped2_len,
+ 0, NULL, NULL, unwrapped2) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped2, unwrapped2_len);
+
+ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
+ goto fail;
+ }
+
+ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+ &r_auth_len);
+ if (!r_auth || r_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+ r_auth, r_auth_len);
+ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ if (dpp_gen_r_auth(auth, r_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+ r_auth2, r_auth_len);
+ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ EVP_PKEY_free(pr);
+ return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ i_auth_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf ||
+ auth->reconfig) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
+ auth->initiator, !!auth->own_bi,
+ auth->waiting_auth_conf);
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->peer_bi ||
+ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+ } else if (auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Authentication failed");
+ return -1;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return -1;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &i_auth_len);
+ if (!i_auth || i_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+ i_auth, i_auth_len);
+ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ if (dpp_gen_i_auth(auth, i_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+ i_auth2, i_auth_len);
+ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+ goto fail;
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ dpp_auth_success(auth);
+ return 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
new file mode 100644
index 0000000..3b81f09
--- /dev/null
+++ b/src/common/dpp_backup.c
@@ -0,0 +1,1292 @@
+/*
+ * DPP configurator backup
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "tls/asn1.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
+{
+ while (key) {
+ struct dpp_asymmetric_key *next = key->next;
+
+ EVP_PKEY_free(key->csign);
+ EVP_PKEY_free(key->pp_key);
+ str_clear_free(key->config_template);
+ str_clear_free(key->connector_template);
+ os_free(key);
+ key = next;
+ }
+}
+
+
+static struct wpabuf * dpp_build_conf_params(struct dpp_configurator *conf)
+{
+ struct wpabuf *buf, *priv_key = NULL;
+ size_t len;
+ /* TODO: proper template values */
+ const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
+ const char *connector_template = NULL;
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+
+ if (!conf->pp_key)
+ return NULL;
+ eckey = EVP_PKEY_get0_EC_KEY(conf->pp_key);
+ if (!eckey)
+ return NULL;
+
+ EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ if (!priv_key)
+ goto fail;
+
+ len = 100 + os_strlen(conf_template);
+ if (connector_template)
+ len += os_strlen(connector_template);
+ if (priv_key)
+ len += wpabuf_len(priv_key);
+ buf = wpabuf_alloc(len);
+ if (!buf)
+ goto fail;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * privacyProtectionKey PrivateKey,
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ /* PrivateKey ::= OCTET STRING */
+ asn1_put_octet_string(buf, priv_key);
+
+ asn1_put_utf8string(buf, conf_template);
+ if (connector_template)
+ asn1_put_utf8string(buf, connector_template);
+ wpabuf_clear_free(priv_key);
+ return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+fail:
+ wpabuf_clear_free(priv_key);
+ return NULL;
+}
+
+
+static struct wpabuf * dpp_build_attribute(struct dpp_configurator *conf)
+{
+ struct wpabuf *conf_params, *attr;
+
+ /*
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ */
+ conf_params = dpp_build_conf_params(conf);
+ conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
+ ASN1_TAG_SET);
+ if (!conf_params)
+ return NULL;
+
+ attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
+ if (!attr) {
+ wpabuf_clear_free(conf_params);
+ return NULL;
+ }
+
+ asn1_put_oid(attr, &asn1_dpp_config_params_oid);
+ wpabuf_put_buf(attr, conf_params);
+ wpabuf_clear_free(conf_params);
+
+ return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
+{
+ const struct asn1_oid *oid;
+ struct wpabuf *params, *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ oid = &asn1_prime256v1_oid;
+ break;
+ case 20:
+ oid = &asn1_secp384r1_oid;
+ break;
+ case 21:
+ oid = &asn1_secp521r1_oid;
+ break;
+ case 28:
+ oid = &asn1_brainpoolP256r1_oid;
+ break;
+ case 29:
+ oid = &asn1_brainpoolP384r1_oid;
+ break;
+ case 30:
+ oid = &asn1_brainpoolP512r1_oid;
+ break;
+ default:
+ return NULL;
+ }
+
+ params = wpabuf_alloc(20);
+ if (!params)
+ return NULL;
+ asn1_put_oid(params, oid); /* namedCurve */
+
+ res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
+ wpabuf_free(params);
+ return res;
+}
+
+
+static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
+{
+ struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+
+ eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
+ if (!eckey)
+ return NULL;
+
+ EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ alg = dpp_build_key_alg(auth->conf->curve);
+
+ /* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
+ attr = dpp_build_attribute(auth->conf);
+ attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
+ if (!priv_key || !attr || !alg)
+ goto fail;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+
+ key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
+ wpabuf_len(attr));
+ if (!key)
+ goto fail;
+
+ asn1_put_integer(key, 0); /* version = v1(0) */
+
+ /* PrivateKeyAlgorithmIdentifier */
+ wpabuf_put_buf(key, alg);
+
+ /* PrivateKey ::= OCTET STRING */
+ asn1_put_octet_string(key, priv_key);
+
+ /* [0] Attributes OPTIONAL */
+ asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
+ wpabuf_put_buf(key, attr);
+
+fail:
+ wpabuf_clear_free(attr);
+ wpabuf_clear_free(priv_key);
+ wpabuf_free(alg);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ *
+ * OneAsymmetricKey ::= SEQUENCE
+ */
+ return asn1_encaps(asn1_encaps(key,
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
+ size_t hash_len)
+{
+ struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
+ const struct asn1_oid *oid;
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_pbkdf2_hmac_sha256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_pbkdf2_hmac_sha384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_pbkdf2_hmac_sha512_oid;
+ else
+ goto fail;
+ prf = asn1_build_alg_id(oid, NULL);
+ if (!prf)
+ goto fail;
+ params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
+ if (!params)
+ goto fail;
+ asn1_put_octet_string(params, salt); /* salt.specified */
+ asn1_put_integer(params, 1000); /* iterationCount */
+ asn1_put_integer(params, hash_len); /* keyLength */
+ wpabuf_put_buf(params, prf);
+ params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ if (!params)
+ goto fail;
+ buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
+fail:
+ wpabuf_free(params);
+ wpabuf_free(prf);
+ return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
+ *key_enc_alg = NULL, *salt;
+ u8 kek[DPP_MAX_HASH_LEN];
+ u8 key[DPP_MAX_HASH_LEN];
+ size_t key_len;
+ int res;
+
+ salt = wpabuf_alloc(64);
+ if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
+
+ key_len = auth->curve->hash_len;
+ /* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+ res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+ "Enveloped Data Password", key, key_len);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
+ kek, hash_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, hash_len);
+
+ enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
+ if (!enc_key ||
+ aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
+ wpabuf_len(cont_enc_key), 0, NULL, NULL,
+ wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
+ key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
+ if (!key_der_alg || !key_enc_alg)
+ goto fail;
+ pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
+ wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
+ if (!pwri)
+ goto fail;
+
+ /* version = 0 */
+ asn1_put_integer(pwri, 0);
+
+ /* [0] KeyDerivationAlgorithmIdentifier */
+ asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
+ wpabuf_len(key_der_alg));
+ wpabuf_put_buf(pwri, key_der_alg);
+
+ /* KeyEncryptionAlgorithmIdentifier */
+ wpabuf_put_buf(pwri, key_enc_alg);
+
+ /* EncryptedKey ::= OCTET STRING */
+ asn1_put_octet_string(pwri, enc_key);
+
+fail:
+ wpabuf_clear_free(key_der_alg);
+ wpabuf_free(key_enc_alg);
+ wpabuf_free(enc_key);
+ wpabuf_free(salt);
+ forced_memzero(kek, sizeof(kek));
+ return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf *
+dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri;
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
+ return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+}
+
+
+static struct wpabuf *
+dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
+ *enc_alg;
+ const struct asn1_oid *oid;
+ size_t enc_cont_len;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_aes_siv_cmac_aead_256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_aes_siv_cmac_aead_384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_aes_siv_cmac_aead_512_oid;
+ else
+ return NULL;
+
+ key_pkg = dpp_build_key_pkg(auth);
+ enc_alg = asn1_build_alg_id(oid, NULL);
+ if (!key_pkg || !enc_alg)
+ goto fail;
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg);
+
+ enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
+ enc_cont = wpabuf_alloc(enc_cont_len);
+ if (!enc_cont ||
+ aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
+ wpabuf_head(key_pkg), wpabuf_len(key_pkg),
+ 0, NULL, NULL,
+ wpabuf_put(enc_cont, enc_cont_len)) < 0)
+ goto fail;
+
+ enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
+ wpabuf_len(enc_cont));
+ if (!enc_cont_info)
+ goto fail;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ wpabuf_put_buf(enc_cont_info, enc_alg);
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
+ wpabuf_len(enc_cont));
+ wpabuf_put_buf(enc_cont_info, enc_cont);
+
+fail:
+ wpabuf_clear_free(key_pkg);
+ wpabuf_free(enc_cont);
+ wpabuf_free(enc_alg);
+ return enc_cont_info;
+}
+
+
+static struct wpabuf * dpp_gen_random(size_t len)
+{
+ struct wpabuf *key;
+
+ key = wpabuf_alloc(len);
+ if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
+ wpabuf_free(key);
+ key = NULL;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
+ return key;
+}
+
+
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
+{
+ struct wpabuf *env = NULL;
+ struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
+ struct wpabuf *cont_enc_key = NULL;
+ size_t hash_len;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
+ return NULL;
+ }
+
+ if (!auth->provision_configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configurator provisioning not allowed");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
+
+ hash_len = auth->conf->curve->hash_len;
+ cont_enc_key = dpp_gen_random(hash_len);
+ if (!cont_enc_key)
+ goto fail;
+ recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
+ enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
+ if (!recipient_info || !enc_cont_info)
+ goto fail;
+
+ env = wpabuf_alloc(wpabuf_len(recipient_info) +
+ wpabuf_len(enc_cont_info) +
+ 100);
+ if (!env)
+ goto fail;
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+
+ /* EnvelopedData.version = 3 */
+ asn1_put_integer(env, 3);
+
+ /* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
+ asn1_put_set(env, recipient_info);
+
+ /* EncryptedContentInfo ::= SEQUENCE */
+ asn1_put_sequence(env, enc_cont_info);
+
+ env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
+out:
+ wpabuf_clear_free(cont_enc_key);
+ wpabuf_clear_free(recipient_info);
+ wpabuf_free(enc_cont_info);
+ return env;
+fail:
+ wpabuf_free(env);
+ env = NULL;
+ goto out;
+}
+
+
+struct dpp_enveloped_data {
+ const u8 *enc_cont;
+ size_t enc_cont_len;
+ const u8 *enc_key;
+ size_t enc_key_len;
+ const u8 *salt;
+ size_t pbkdf2_key_len;
+ size_t prf_hash_len;
+};
+
+
+static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *end = pos + len;
+ const u8 *next, *e_end;
+ struct asn1_oid oid;
+ int val;
+ const u8 *params;
+ size_t params_len;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
+ hdr.payload, hdr.length);
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
+ pos, end - pos);
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ pos = hdr.payload;
+ e_end = pos + hdr.length;
+
+ /* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, e_end - pos, &oid, ¶ms, ¶ms_len,
+ &next) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+ if (!params ||
+ asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
+ hdr.payload, hdr.length);
+ if (hdr.length != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
+ hdr.length);
+ return -1;
+ }
+ data->salt = hdr.payload;
+ pos = hdr.payload + hdr.length;
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 1000) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 32 && val != 48 && val != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
+ return -1;
+ }
+ data->pbkdf2_key_len = val;
+
+ if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
+ asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
+ return -1;
+ }
+ if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
+ data->prf_hash_len = 32;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
+ data->prf_hash_len = 48;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
+ data->prf_hash_len = 64;
+ } else {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
+ buf);
+ return -1;
+ }
+
+ pos = next;
+
+ /* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
+ *
+ * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
+ * id-alg-AES-SIV-CMAC-aed-512. */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * encryptedKey EncryptedKey
+ *
+ * EncryptedKey ::= OCTET STRING
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
+ hdr.payload, hdr.length);
+ data->enc_key = hdr.payload;
+ data->enc_key_len = hdr.length;
+
+ return 0;
+}
+
+
+static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ return -1;
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
+ hdr.payload, hdr.length);
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after EncryptedContentInfo",
+ pos, end - pos);
+ return -1;
+ }
+
+ end = pos;
+ pos = hdr.payload;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
+ return -1;
+ }
+ if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
+ return -1;
+ }
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+ /* ignore optional parameters */
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
+ hdr.payload, hdr.length);
+ data->enc_cont = hdr.payload;
+ data->enc_cont_len = hdr.length;
+ return 0;
+}
+
+
+static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ int val;
+
+ os_memset(data, 0, sizeof(*data));
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * CMSVersion ::= INTEGER
+ *
+ * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+ if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+ if (end < env_data + env_data_len) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after DPPEnvelopedData",
+ end, env_data + env_data_len - end);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 3) {
+ wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+
+ if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
+ return -1;
+ return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
+ data);
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = buf, *end = buf + len, *next;
+ int val;
+ const u8 *params;
+ size_t params_len;
+ struct asn1_oid oid;
+ char txt[80];
+ struct dpp_asymmetric_key *key;
+ EC_KEY *eckey;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
+
+ key = os_zalloc(sizeof(*key));
+ if (!key)
+ return NULL;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ goto fail;
+ pos = hdr.payload;
+
+ /* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ goto fail;
+ if (val != 0 && val != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
+ val);
+ goto fail;
+ }
+
+ /* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, ¶ms, ¶ms_len,
+ &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
+ txt);
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
+ params, params_len);
+ /*
+ * ECParameters ::= CHOICE {
+ * namedCurve OBJECT IDENTIFIER
+ * -- implicitCurve NULL
+ * -- specifiedCurve SpecifiedECDomain}
+ */
+ if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse ECParameters.namedCurve");
+ goto fail;
+ }
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
+ /* Assume the curve is identified within ECPrivateKey, so that this
+ * separate indication is not really needed. */
+
+ /*
+ * PrivateKey ::= OCTET STRING
+ * (Contains DER encoding of ECPrivateKey)
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
+ hdr.payload, hdr.length);
+ pos = hdr.payload + hdr.length;
+ eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ key->csign = EVP_PKEY_new();
+ if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
+ EC_KEY_free(eckey);
+ goto fail;
+ }
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
+
+ /*
+ * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+ *
+ * Exactly one instance of type Attribute in OneAsymmetricKey.
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected [0] Attributes - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
+ hdr.payload, hdr.length);
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
+ * aa-DPPConfigurationParameters,
+ * ... -- For local profiles
+ * }
+ *
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ *
+ * Exactly one instance of ATTRIBUTE in attrValues.
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of ATTRIBUTE",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected Attribute identifier %s", txt);
+ goto fail;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * privacyProtectionKey PrivateKey,
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
+ pos, end - pos);
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data after DPPConfigurationParameters",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ /*
+ * PrivateKey ::= OCTET STRING
+ * (Contains DER encoding of ECPrivateKey)
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_OCTETSTRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: privacyProtectionKey",
+ hdr.payload, hdr.length);
+ pos = hdr.payload + hdr.length;
+ eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ key->pp_key = EVP_PKEY_new();
+ if (!key->pp_key || EVP_PKEY_assign_EC_KEY(key->pp_key, eckey) != 1) {
+ EC_KEY_free(eckey);
+ goto fail;
+ }
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("DPP: Received privacyProtectionKey",
+ key->pp_key);
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_UTF8STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
+ hdr.payload, hdr.length);
+ key->config_template = os_zalloc(hdr.length + 1);
+ if (!key->config_template)
+ goto fail;
+ os_memcpy(key->config_template, hdr.payload, hdr.length);
+
+ pos = hdr.payload + hdr.length;
+
+ if (pos < end) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_UTF8STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
+ hdr.payload, hdr.length);
+ key->connector_template = os_zalloc(hdr.length + 1);
+ if (!key->connector_template)
+ goto fail;
+ os_memcpy(key->connector_template, hdr.payload, hdr.length);
+ }
+
+ return key;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
+ dpp_free_asymmetric_key(key);
+ return NULL;
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
+ struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg, key_pkg_len);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ */
+ while (pos < end) {
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
+ !(key = dpp_parse_one_asymmetric_key(hdr.payload,
+ hdr.length))) {
+ dpp_free_asymmetric_key(first);
+ return NULL;
+ }
+ if (!last) {
+ first = last = key;
+ } else {
+ last->next = key;
+ last = key;
+ }
+ }
+
+ return first;
+}
+
+
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len)
+{
+ u8 key[DPP_MAX_HASH_LEN];
+ size_t key_len;
+ u8 kek[DPP_MAX_HASH_LEN];
+ u8 cont_encr_key[DPP_MAX_HASH_LEN];
+ size_t cont_encr_key_len;
+ int res;
+ u8 *key_pkg;
+ size_t key_pkg_len;
+ struct dpp_enveloped_data data;
+ struct dpp_asymmetric_key *keys;
+
+ wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
+
+ if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
+ return -1;
+
+ key_len = auth->curve->hash_len;
+ /* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+ res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+ "Enveloped Data Password", key, key_len);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
+ kek, data.pbkdf2_key_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, data.pbkdf2_key_len);
+
+ if (data.enc_key_len < AES_BLOCK_SIZE ||
+ data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
+ return -1;
+ }
+ res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
+ data.enc_key, data.enc_key_len,
+ 0, NULL, NULL, cont_encr_key);
+ forced_memzero(kek, data.pbkdf2_key_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedKey failed");
+ return -1;
+ }
+ cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
+ cont_encr_key, cont_encr_key_len);
+
+ if (data.enc_cont_len < AES_BLOCK_SIZE)
+ return -1;
+ key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
+ key_pkg = os_malloc(key_pkg_len);
+ if (!key_pkg)
+ return -1;
+ res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
+ data.enc_cont, data.enc_cont_len,
+ 0, NULL, NULL, key_pkg);
+ forced_memzero(cont_encr_key, cont_encr_key_len);
+ if (res < 0) {
+ bin_clear_free(key_pkg, key_pkg_len);
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedContent failed");
+ return -1;
+ }
+
+ keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
+ bin_clear_free(key_pkg, key_pkg_len);
+ dpp_free_asymmetric_key(auth->conf_key_pkg);
+ auth->conf_key_pkg = keys;
+
+ return keys != NULL;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
new file mode 100644
index 0000000..7c48015
--- /dev/null
+++ b/src/common/dpp_crypto.c
@@ -0,0 +1,3326 @@
+/*
+ * DPP crypto functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+static const struct dpp_curve_params dpp_curves[] = {
+ /* The mandatory to support and the default NIST P-256 curve needs to
+ * be the first entry on this list. */
+ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+const struct dpp_curve_params * dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ if (!name)
+ return &dpp_curves[0];
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].ike_group == group)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
+void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (curve->hash_len == 32)
+ return sha256_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 48)
+ return sha384_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 64)
+ return sha512_vector(num_elem, addr, len, mac);
+ return -1;
+}
+
+
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen)
+{
+ if (hash_len == 32)
+ return hmac_sha256_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 48)
+ return hmac_sha384_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 64)
+ return hmac_sha512_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ return -1;
+}
+
+
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 48)
+ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 64)
+ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+ mac);
+ return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256(key, key_len, data, data_len, mac);
+ if (hash_len == 48)
+ return hmac_sha384(key, key_len, data, data_len, mac);
+ if (hash_len == 64)
+ return hmac_sha512(key, key_len, data, data_len, mac);
+ return -1;
+}
+
+
+#ifdef CONFIG_DPP2
+
+static int dpp_pbkdf2_f(size_t hash_len,
+ const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len,
+ unsigned int iterations, unsigned int count, u8 *digest)
+{
+ unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
+ unsigned int i;
+ size_t j;
+ u8 count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = salt;
+ len[0] = salt_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ WPA_PUT_BE32(count_buf, count);
+ if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
+ tmp))
+ return -1;
+ os_memcpy(digest, tmp, hash_len);
+
+ for (i = 1; i < iterations; i++) {
+ if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
+ tmp2))
+ return -1;
+ os_memcpy(tmp, tmp2, hash_len);
+ for (j = 0; j < hash_len; j++)
+ digest[j] ^= tmp2[j];
+ }
+
+ return 0;
+}
+
+
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen)
+{
+ unsigned int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[DPP_MAX_HASH_LEN];
+
+ while (left > 0) {
+ count++;
+ if (dpp_pbkdf2_f(hash_len, password, password_len,
+ salt, salt_len, iterations, count, digest))
+ return -1;
+ plen = left > hash_len ? hash_len : left;
+ os_memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix to match DPP definition */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len)
+{
+ EC_KEY *eckey = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ctx = BN_CTX_new();
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ return NULL;
+ }
+
+ point = EC_POINT_new(group);
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ if (!point || !x || !y) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ goto fail;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx) ||
+ EC_POINT_is_at_infinity(group, point)) {
+ wpa_printf(MSG_ERROR, "DPP: Invalid point");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return pkey;
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
+{
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ if (len & 1)
+ return NULL;
+
+ eckey = EVP_PKEY_get0_EC_KEY(group_key);
+ if (!eckey) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Could not get EC_KEY from group_key");
+ return NULL;
+ }
+
+ group = EC_KEY_get0_group(eckey);
+ if (group)
+ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+ len / 2);
+ else
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+ return pkey;
+}
+
+
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params = NULL;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ key = NULL;
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+fail:
+ EC_KEY_free(ec_params);
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+}
+
+
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
+
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get0_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+ OPENSSL_free(der);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+ res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ wpabuf_free(der);
+ return res;
+}
+
+
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ struct wpabuf *der = NULL;
+
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ return -1;
+ }
+
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
+ der = NULL;
+ if (!base64)
+ goto fail;
+ pos = base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ os_free(bi->pk);
+ bi->pk = base64;
+ return 0;
+fail:
+ os_free(base64);
+ wpabuf_free(der);
+ return -1;
+}
+
+
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "first intermediate key";
+ int res;
+
+ /* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+ /* HKDF-Extract(<>, M.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+ k1, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "second intermediate key";
+ int res;
+
+ /* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+ k2, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_bk_ke(struct dpp_authentication *auth)
+{
+ unsigned int hash_len = auth->curve->hash_len;
+ size_t nonce_len = auth->curve->nonce_len;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+ const char *info_ke = "DPP Key";
+ int res;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem = 0;
+
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
+ /* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+ os_memcpy(nonces, auth->i_nonce, nonce_len);
+ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+ addr[num_elem] = auth->Mx;
+ len[num_elem] = auth->Mx_len;
+ num_elem++;
+ addr[num_elem] = auth->Nx;
+ len[num_elem] = auth->Nx_len;
+ num_elem++;
+ if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
+ addr[num_elem] = auth->Lx;
+ len[num_elem] = auth->secret_len;
+ num_elem++;
+ }
+ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+ num_elem, addr, len, auth->bk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
+ auth->bk, hash_len);
+
+ /* ke = HKDF-Expand(bk, "DPP Key", length) */
+ res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
+ hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF-Expand(bk, \"DPP Key\", length)",
+ auth->ke, hash_len);
+
+ return 0;
+}
+
+
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len)
+{
+ EVP_PKEY_CTX *ctx;
+ int ret = -1;
+
+ ERR_clear_error();
+ *secret_len = 0;
+
+ ctx = EVP_PKEY_CTX_new(own, NULL);
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: EVP_PKEY_derive_set_peet failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ u8 buf[200];
+ int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
+
+ /* It looks like OpenSSL can return unexpectedly large buffer
+ * need for shared secret from EVP_PKEY_derive(NULL) in some
+ * cases. For example, group 19 has shown cases where secret_len
+ * is set to 72 even though the actual length ends up being
+ * updated to 32 when EVP_PKEY_derive() is called with a buffer
+ * for the value. Work around this by trying to fetch the value
+ * and continue if it is within supported range even when the
+ * initial buffer need is claimed to be larger. */
+ wpa_printf(level,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ if (*secret_len > 200)
+ goto fail;
+ if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
+ buf, *secret_len);
+ os_memcpy(secret, buf, *secret_len);
+ forced_memzero(buf, sizeof(buf));
+ goto done;
+ }
+
+ if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+done:
+ ret = 0;
+
+fail:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = data;
+ len[0] = data_len;
+ if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+ bi->pubkey_hash, SHA256_MAC_LEN);
+
+ addr[0] = (const u8 *) "chirp";
+ len[0] = 5;
+ addr[1] = data;
+ len[1] = data_len;
+ if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
+ bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ ASN1_OBJECT *pa_oid;
+#else
+ const ASN1_OBJECT *pa_oid;
+#endif
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ return -1;
+ }
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+ const u8 *prot_hdr, u16 prot_hdr_len,
+ const EVP_MD **ret_md)
+{
+ struct json_token *root, *token;
+ struct wpabuf *kid = NULL;
+
+ root = json_parse((const char *) prot_hdr, prot_hdr_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JSON parsing failed for JWS Protected Header");
+ goto fail;
+ }
+
+ if (root->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JWS Protected Header root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "typ");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+ token->string);
+ if (os_strcmp(token->string, "dppCon") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header typ=%s",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(root, "alg");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+ token->string);
+ if (os_strcmp(token->string, curve->jws_alg) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+ token->string, curve->jws_alg);
+ goto fail;
+ }
+ if (os_strcmp(token->string, "ES256") == 0 ||
+ os_strcmp(token->string, "BS256") == 0)
+ *ret_md = EVP_sha256();
+ else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0)
+ *ret_md = EVP_sha384();
+ else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0)
+ *ret_md = EVP_sha512();
+ else
+ *ret_md = NULL;
+ if (!*ret_md) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header alg=%s",
+ token->string);
+ goto fail;
+ }
+
+ kid = json_get_member_base64url(root, "kid");
+ if (!kid) {
+ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+ kid);
+
+fail:
+ json_free(root);
+ return kid;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+ struct wpabuf *uncomp;
+ int res;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+
+ if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+ return -1;
+ uncomp = dpp_get_pubkey_point(pub, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+ addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ return -1;
+ if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received hash value does not match calculated public key hash value");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+ hash, SHA256_MAC_LEN);
+ return -1;
+ }
+ return 0;
+}
+
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector)
+{
+ enum dpp_status_error ret = 255;
+ const char *pos, *end, *signed_start, *signed_end;
+ struct wpabuf *kid = NULL;
+ unsigned char *prot_hdr = NULL, *signature = NULL;
+ size_t prot_hdr_len = 0, signature_len = 0;
+ const EVP_MD *sign_md = NULL;
+ unsigned char *der = NULL;
+ int der_len;
+ int res;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ BIGNUM *r = NULL, *s = NULL;
+ const struct dpp_curve_params *curve;
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ eckey = EVP_PKEY_get0_EC_KEY(csign_pub);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+ nid = EC_GROUP_get_curve_name(group);
+ curve = dpp_get_curve_nid(nid);
+ if (!curve)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+ os_memset(info, 0, sizeof(*info));
+
+ signed_start = pos = connector;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
+ if (!prot_hdr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Protected Header",
+ prot_hdr, prot_hdr_len);
+ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+ if (!kid) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+ (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ pos = end + 1;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing dot(2) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ signed_end = end - 1;
+ info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
+ if (!info->payload) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Payload");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Payload",
+ info->payload, info->payload_len);
+ pos = end + 1;
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector signature");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+ signature, signature_len);
+
+ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+ ret = DPP_STATUS_NO_MATCH;
+ goto fail;
+ }
+
+ if (signature_len & 0x01) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector signature length (%d)",
+ (int) signature_len);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ /* JWS Signature encodes the signature (r,s) as two octet strings. Need
+ * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+ r = BN_bin2bn(signature, signature_len / 2, NULL);
+ s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+ sig = ECDSA_SIG_new();
+ if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+ goto fail;
+ r = NULL;
+ s = NULL;
+
+ der_len = i2d_ECDSA_SIG(sig, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+ signed_end - signed_start + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+ res, ERR_error_string(ERR_get_error(), NULL));
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ ret = DPP_STATUS_OK;
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ os_free(prot_hdr);
+ wpabuf_free(kid);
+ os_free(signature);
+ ECDSA_SIG_free(sig);
+ BN_free(r);
+ BN_free(s);
+ OPENSSL_free(der);
+ return ret;
+}
+
+
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len)
+{
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL;
+ char *signed_connector = NULL;
+ enum dpp_status_error res = DPP_STATUS_INVALID_CONNECTOR;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+ peer_connector, peer_connector_len);
+ signed_connector = os_malloc(peer_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, peer_connector, peer_connector_len);
+ signed_connector[peer_connector_len] = '\0';
+ res = dpp_process_signed_connector(info, csign, signed_connector);
+fail:
+ os_free(signed_connector);
+ EVP_PKEY_free(csign);
+ return res;
+}
+
+
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+ struct wpabuf *pix, *prx, *bix, *brx;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 zero = 0;
+ int res = -1;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ addr[num_elem] = &zero;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 one = 1;
+ int res = -1;
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->peer_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->own_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = &one;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL;
+ const EC_KEY *BI, *bR, *pR;
+ const EC_POINT *BI_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx, *sum, *q;
+ const BIGNUM *bR_bn, *pR_bn;
+ int ret = -1;
+
+ /* L = ((bR + pR) modulo q) * BI */
+
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ q = BN_new();
+ lx = BN_new();
+ if (!bnctx || !sum || !q || !lx)
+ goto fail;
+ BI = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ if (!BI)
+ goto fail;
+ BI_point = EC_KEY_get0_public_key(BI);
+ group = EC_KEY_get0_group(BI);
+ if (!group)
+ goto fail;
+
+ bR = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ if (!bR || !pR)
+ goto fail;
+ bR_bn = EC_KEY_get0_private_key(bR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!bR_bn || !pR_bn)
+ goto fail;
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+ goto fail;
+ l = EC_POINT_new(group);
+ if (!l ||
+ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ BN_clear_free(lx);
+ BN_clear_free(sum);
+ BN_free(q);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL, *sum = NULL;
+ const EC_KEY *bI, *BR, *PR;
+ const EC_POINT *BR_point, *PR_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx;
+ const BIGNUM *bI_bn;
+ int ret = -1;
+
+ /* L = bI * (BR + PR) */
+
+ bnctx = BN_CTX_new();
+ lx = BN_new();
+ if (!bnctx || !lx)
+ goto fail;
+ BR = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ if (!BR || !PR)
+ goto fail;
+ BR_point = EC_KEY_get0_public_key(BR);
+ PR_point = EC_KEY_get0_public_key(PR);
+
+ bI = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ if (!bI)
+ goto fail;
+ group = EC_KEY_get0_group(bI);
+ bI_bn = EC_KEY_get0_private_key(bI);
+ if (!group || !bI_bn)
+ goto fail;
+ sum = EC_POINT_new(group);
+ l = EC_POINT_new(group);
+ if (!sum || !l ||
+ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
+ BN_clear_free(lx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "DPP PMK";
+ int res;
+
+ /* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+ pmk, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+ struct wpabuf *nkx, *pkx;
+ int ret = -1, res;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA256_MAC_LEN];
+
+ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+ nkx = dpp_get_pubkey_point(own_key, 0);
+ pkx = dpp_get_pubkey_point(peer_key, 0);
+ if (!nkx || !pkx)
+ goto fail;
+ addr[0] = wpabuf_head(nkx);
+ len[0] = wpabuf_len(nkx) / 2;
+ addr[1] = wpabuf_head(pkx);
+ len[1] = wpabuf_len(pkx) / 2;
+ if (len[0] != len[1])
+ goto fail;
+ if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+ addr[0] = wpabuf_head(pkx);
+ addr[1] = wpabuf_head(nkx);
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+ ret = 0;
+fail:
+ wpabuf_free(nkx);
+ wpabuf_free(pkx);
+ return ret;
+}
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+ 0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+ 0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+ int init)
+{
+ EC_GROUP *group;
+ size_t len = curve->prime_len;
+ const u8 *x, *y;
+ EVP_PKEY *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+ y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+ break;
+ case 20:
+ x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+ y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+ break;
+ case 21:
+ x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+ y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+ break;
+ case 28:
+ x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+ y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+ break;
+ case 29:
+ x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+ y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+ break;
+ case 30:
+ x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+ y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+ break;
+ default:
+ return NULL;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return NULL;
+ res = dpp_set_pubkey_point_group(group, x, y, len);
+ EC_GROUP_free(group);
+ return res;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qi = NULL;
+ EVP_PKEY *Pi = NULL;
+ const EC_KEY *Pi_ec;
+ const EC_POINT *Pi_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+ addr[num_elem] = mac_init;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Initiator | [identifier |] code)",
+ hash, curve->hash_len);
+ Pi = dpp_pkex_get_role_elem(curve, 1);
+ if (!Pi)
+ goto fail;
+ dpp_debug_print_key("DPP: Pi", Pi);
+ Pi_ec = EVP_PKEY_get0_EC_KEY(Pi);
+ if (!Pi_ec)
+ goto fail;
+ Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+ group = EC_KEY_get0_group(Pi_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qi = EC_POINT_new(group2);
+ if (!Qi) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qi)) {
+ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qi", group, Qi);
+out:
+ EVP_PKEY_free(Pi);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qi)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qi;
+fail:
+ EC_POINT_free(Qi);
+ Qi = NULL;
+ goto out;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qr = NULL;
+ EVP_PKEY *Pr = NULL;
+ const EC_KEY *Pr_ec;
+ const EC_POINT *Pr_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+ addr[num_elem] = mac_resp;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Responder | [identifier |] code)",
+ hash, curve->hash_len);
+ Pr = dpp_pkex_get_role_elem(curve, 0);
+ if (!Pr)
+ goto fail;
+ dpp_debug_print_key("DPP: Pr", Pr);
+ Pr_ec = EVP_PKEY_get0_EC_KEY(Pr);
+ if (!Pr_ec)
+ goto fail;
+ Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+ group = EC_KEY_get0_group(Pr_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qr = EC_POINT_new(group2);
+ if (!Qr) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qr)) {
+ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qr", group, Qr);
+out:
+ EVP_PKEY_free(Pr);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qr)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qr;
+fail:
+ EC_POINT_free(Qr);
+ Qr = NULL;
+ goto out;
+}
+
+
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ int res;
+ u8 *info, *pos;
+ size_t info_len;
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+
+ /* HKDF-Extract(<>, IKM=K.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+ info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+ info = os_malloc(info_len);
+ if (!info)
+ return -1;
+ pos = info;
+ os_memcpy(pos, mac_init, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, mac_resp, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, Mx, Mx_len);
+ pos += Mx_len;
+ os_memcpy(pos, Nx, Nx_len);
+ pos += Nx_len;
+ os_memcpy(pos, code, os_strlen(code));
+
+ /* HKDF-Expand(PRK, info, L) */
+ if (hash_len == 32)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 48)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 64)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else
+ res = -1;
+ os_free(info);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+ z, hash_len);
+ return 0;
+}
+
+
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *own_key = NULL, *peer_key = NULL;
+ BIGNUM *sum = NULL, *q = NULL, *mx = NULL;
+ EC_POINT *m = NULL;
+ const EC_KEY *cR, *pR;
+ const EC_GROUP *group;
+ const BIGNUM *cR_bn, *pR_bn;
+ const EC_POINT *CI_point;
+ const EC_KEY *CI;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ const struct dpp_curve_params *curve;
+ int res = -1;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+
+ own_key = dpp_set_keypair(&auth->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ dpp_auth_fail(auth, "Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ peer_key = dpp_parse_jwk(peer_net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ auth->own_protocol_key = dpp_gen_keypair(curve);
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ if (random_get_bytes(auth->e_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: E-nonce",
+ auth->e_nonce, auth->curve->nonce_len);
+
+ /* M = { cR + pR } * CI */
+ cR = EVP_PKEY_get0_EC_KEY(own_key);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ group = EC_KEY_get0_group(pR);
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ mx = BN_new();
+ q = BN_new();
+ m = EC_POINT_new(group);
+ if (!cR || !pR || !bnctx || !sum || !mx || !q || !m)
+ goto fail;
+ cR_bn = EC_KEY_get0_private_key(cR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!cR_bn || !pR_bn)
+ goto fail;
+ CI = EVP_PKEY_get0_EC_KEY(peer_key);
+ CI_point = EC_KEY_get0_public_key(CI);
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, cR_bn, pR_bn, q, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, CI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(C-nonce | E-nonce, M.x) */
+ os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+ os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+ if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+ EVP_PKEY_free(auth->reconfig_old_protocol_key);
+ auth->reconfig_old_protocol_key = own_key;
+ own_key = NULL;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EC_POINT_clear_free(m);
+ BN_free(q);
+ BN_clear_free(mx);
+ BN_clear_free(sum);
+ EVP_PKEY_free(own_key);
+ EVP_PKEY_free(peer_key);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *pr = NULL, *peer_key = NULL;
+ EC_POINT *sum = NULL, *m = NULL;
+ BIGNUM *mx = NULL;
+ const EC_KEY *cI, *CR, *PR;
+ const EC_GROUP *group;
+ const EC_POINT *CR_point, *PR_point;
+ const BIGNUM *cI_bn;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ int res = -1;
+ const struct dpp_curve_params *curve;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+
+ pr = dpp_set_pubkey_point(auth->conf->connector_key,
+ r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ peer_key = dpp_parse_jwk(net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ /* M = cI * { CR + PR } */
+ cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
+ cI_bn = EC_KEY_get0_private_key(cI);
+ group = EC_KEY_get0_group(cI);
+ bnctx = BN_CTX_new();
+ sum = EC_POINT_new(group);
+ m = EC_POINT_new(group);
+ mx = BN_new();
+ CR = EVP_PKEY_get0_EC_KEY(peer_key);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ CR_point = EC_KEY_get0_public_key(CR);
+ PR_point = EC_KEY_get0_public_key(PR);
+ if (!bnctx || !sum || !m || !mx ||
+ EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1 ||
+ dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(C-nonce | E-nonce, M.x) */
+ os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+ os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+ if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EVP_PKEY_free(pr);
+ EVP_PKEY_free(peer_key);
+ EC_POINT_clear_free(sum);
+ EC_POINT_clear_free(m);
+ BN_clear_free(mx);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+static char *
+dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
+{
+ struct wpabuf *jws_prot_hdr;
+ char *signed1;
+
+ jws_prot_hdr = wpabuf_alloc(100);
+ if (!jws_prot_hdr)
+ return NULL;
+ json_start_object(jws_prot_hdr, NULL);
+ json_add_string(jws_prot_hdr, "typ", "dppCon");
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "kid", conf->kid);
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "alg", conf->curve->jws_alg);
+ json_end_object(jws_prot_hdr);
+ signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+ wpabuf_len(jws_prot_hdr),
+ signed1_len);
+ wpabuf_free(jws_prot_hdr);
+ return signed1;
+}
+
+
+static char *
+dpp_build_conn_signature(struct dpp_configurator *conf,
+ const char *signed1, size_t signed1_len,
+ const char *signed2, size_t signed2_len,
+ size_t *signed3_len)
+{
+ const struct dpp_curve_params *curve;
+ char *signed3 = NULL;
+ unsigned char *signature = NULL;
+ const unsigned char *p;
+ size_t signature_len;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ char *dot = ".";
+ const EVP_MD *sign_md;
+ const BIGNUM *r, *s;
+
+ curve = conf->curve;
+ if (curve->hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (curve->hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (curve->hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, conf->csign) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ signature = os_malloc(signature_len);
+ if (!signature)
+ goto fail;
+ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+ signature, signature_len);
+ /* Convert to raw coordinates r,s */
+ p = signature;
+ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(s, signature + curve->prime_len,
+ curve->prime_len) < 0)
+ goto fail;
+ signature_len = 2 * curve->prime_len;
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, signed3_len);
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ ECDSA_SIG_free(sig);
+ os_free(signature);
+ return signed3;
+}
+
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon)
+{
+ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ char *signed_conn = NULL, *pos;
+ size_t signed1_len, signed2_len, signed3_len;
+
+ signed1 = dpp_build_jws_prot_hdr(conf, &signed1_len);
+ signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+ &signed2_len);
+ if (!signed1 || !signed2)
+ goto fail;
+
+ signed3 = dpp_build_conn_signature(conf, signed1, signed1_len,
+ signed2, signed2_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+
+ signed_conn = os_malloc(signed1_len + signed2_len + signed3_len + 3);
+ if (!signed_conn)
+ goto fail;
+ pos = signed_conn;
+ os_memcpy(pos, signed1, signed1_len);
+ pos += signed1_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed2, signed2_len);
+ pos += signed2_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed3, signed3_len);
+ pos += signed3_len;
+ *pos = '\0';
+
+fail:
+ os_free(signed1);
+ os_free(signed2);
+ os_free(signed3);
+ return signed_conn;
+}
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+ size_t net_access_key_len)
+{
+ struct wpabuf *pub = NULL;
+ EVP_PKEY *own_key;
+ struct dpp_pfs *pfs;
+
+ pfs = os_zalloc(sizeof(*pfs));
+ if (!pfs)
+ return NULL;
+
+ own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+ EVP_PKEY_free(own_key);
+
+ pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+ if (!pfs->ecdh)
+ goto fail;
+
+ pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+ pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+ if (!pub)
+ goto fail;
+
+ pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+ if (!pfs->ie)
+ goto fail;
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+ wpabuf_put_buf(pfs->ie, pub);
+ wpabuf_free(pub);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+ pfs->ie);
+
+ return pfs;
+fail:
+ wpabuf_free(pub);
+ dpp_pfs_free(pfs);
+ return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+ if (peer_ie_len < 2)
+ return -1;
+ if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+ return -1;
+ }
+
+ pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+ peer_ie_len - 2);
+ pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+ if (!pfs->secret) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+ return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+ if (!pfs)
+ return;
+ crypto_ecdh_deinit(pfs->ecdh);
+ wpabuf_free(pfs->ie);
+ wpabuf_clear_free(pfs->secret);
+ os_free(pfs);
+}
+
+
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name)
+{
+ X509_REQ *req = NULL;
+ struct wpabuf *buf = NULL;
+ unsigned char *der;
+ int der_len;
+ EVP_PKEY *key;
+ const EVP_MD *sign_md;
+ unsigned int hash_len = auth->curve->hash_len;
+ EC_KEY *eckey;
+ BIO *out = NULL;
+ u8 cp[DPP_CP_LEN];
+ char *password;
+ size_t password_len;
+ int res;
+
+ /* TODO: use auth->csrattrs */
+
+ /* TODO: support generation of a new private key if csrAttrs requests
+ * a specific group to be used */
+ key = auth->own_protocol_key;
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ goto fail;
+ der = NULL;
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len <= 0)
+ goto fail;
+ wpabuf_free(auth->priv_key);
+ auth->priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ if (!auth->priv_key)
+ goto fail;
+
+ req = X509_REQ_new();
+ if (!req || !X509_REQ_set_pubkey(req, key))
+ goto fail;
+
+ if (name) {
+ X509_NAME *n;
+
+ n = X509_REQ_get_subject_name(req);
+ if (!n)
+ goto fail;
+
+ if (X509_NAME_add_entry_by_txt(
+ n, "CN", MBSTRING_UTF8,
+ (const unsigned char *) name, -1, -1, 0) != 1)
+ goto fail;
+ }
+
+ /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+ "CSR challengePassword", cp, DPP_CP_LEN) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+ cp, DPP_CP_LEN);
+ password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len);
+ forced_memzero(cp, DPP_CP_LEN);
+ if (!password)
+ goto fail;
+
+ res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword,
+ V_ASN1_UTF8STRING,
+ (const unsigned char *) password,
+ password_len);
+ bin_clear_free(password, password_len);
+ if (!res)
+ goto fail;
+
+ /* TODO */
+
+ /* TODO: hash func selection based on csrAttrs */
+ if (hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+ if (!X509_REQ_sign(req, key, sign_md))
+ goto fail;
+
+ der = NULL;
+ der_len = i2d_X509_REQ(req, &der);
+ if (der_len < 0)
+ goto fail;
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf);
+
+fail:
+ BIO_free_all(out);
+ X509_REQ_free(req);
+ return buf;
+}
+
+
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+ CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+ STACK_OF(X509) *certs;
+ int i, num;
+ BIO *out = NULL;
+ size_t rlen;
+ struct wpabuf *pem = NULL;
+ int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+ certs = sk_X509_new_null();
+ if (!certs)
+ goto fail;
+ CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+ wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
+ p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+ if (!p7) {
+ wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ switch (OBJ_obj2nid(p7->type)) {
+ case NID_pkcs7_signed:
+ certs = p7->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped:
+ certs = p7->d.signed_and_enveloped->cert;
+ break;
+ default:
+ certs = NULL;
+ break;
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+
+ if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+ wpa_printf(MSG_INFO,
+ "DPP: No certificates found in PKCS#7 object");
+ goto fail;
+ }
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ goto fail;
+
+ for (i = 0; i < num; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+
+ PEM_write_bio_X509(out, cert);
+ }
+
+ rlen = BIO_ctrl_pending(out);
+ pem = wpabuf_alloc(rlen);
+ if (!pem)
+ goto fail;
+ res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+ if (res <= 0) {
+ wpabuf_free(pem);
+ goto fail;
+ }
+ wpabuf_put(pem, res);
+
+fail:
+#ifdef OPENSSL_IS_BORINGSSL
+ if (certs)
+ sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
+ if (out)
+ BIO_free_all(out);
+
+ return pem;
+}
+
+
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr)
+{
+ X509_REQ *req;
+ const unsigned char *pos;
+ EVP_PKEY *pkey;
+ int res, loc, ret = -1;
+ X509_ATTRIBUTE *attr;
+ ASN1_TYPE *type;
+ ASN1_STRING *str;
+ unsigned char *utf8 = NULL;
+ unsigned char *cp = NULL;
+ size_t cp_len;
+ u8 exp_cp[DPP_CP_LEN];
+ unsigned int hash_len = auth->curve->hash_len;
+
+ pos = wpabuf_head(csr);
+ req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr));
+ if (!req) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR");
+ return -1;
+ }
+
+ pkey = X509_REQ_get_pubkey(req);
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR");
+ goto fail;
+ }
+
+ res = X509_REQ_verify(req, pkey);
+ EVP_PKEY_free(pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR does not have a valid signature");
+ goto fail;
+ }
+
+ loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1);
+ if (loc < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR does not include challengePassword");
+ goto fail;
+ }
+
+ attr = X509_REQ_get_attr(req, loc);
+ if (!attr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get challengePassword attribute");
+ goto fail;
+ }
+
+ type = X509_ATTRIBUTE_get0_type(attr, 0);
+ if (!type) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get challengePassword attribute type");
+ goto fail;
+ }
+
+ res = ASN1_TYPE_get(type);
+ /* This is supposed to be UTF8String, but allow other strings as well
+ * since challengePassword is using ASCII (base64 encoded). */
+ if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING &&
+ res != V_ASN1_IA5STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected challengePassword attribute type %d",
+ res);
+ goto fail;
+ }
+
+ str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL);
+ if (!str) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get ASN.1 string for challengePassword");
+ goto fail;
+ }
+
+ res = ASN1_STRING_to_UTF8(&utf8, str);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get UTF8 version of challengePassword");
+ goto fail;
+ }
+
+ cp = base64_decode((const char *) utf8, res, &cp_len);
+ OPENSSL_free(utf8);
+ if (!cp) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not base64 decode challengePassword");
+ goto fail;
+ }
+ if (cp_len != DPP_CP_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected cp length (%zu) in CSR challengePassword",
+ cp_len);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword",
+ cp, cp_len);
+
+ /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+ "CSR challengePassword", exp_cp, DPP_CP_LEN) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+ exp_cp, DPP_CP_LEN);
+ if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR challengePassword does not match calculated cp");
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ os_free(cp);
+ X509_REQ_free(req);
+ return ret;
+}
+
+
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *pp_key,
+ size_t pp_key_len)
+{
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL, *ppkey = NULL;
+ struct dpp_reconfig_id *id = NULL;
+ BN_CTX *ctx = NULL;
+ BIGNUM *bn = NULL, *q = NULL;
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ EC_POINT *e_id = NULL;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign)
+ goto fail;
+
+ if (!pp_key)
+ goto fail;
+ p = pp_key;
+ ppkey = d2i_PUBKEY(NULL, &p, pp_key_len);
+ if (!ppkey)
+ goto fail;
+
+ eckey = EVP_PKEY_get0_EC_KEY(csign);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+
+ e_id = EC_POINT_new(group);
+ ctx = BN_CTX_new();
+ bn = BN_new();
+ q = BN_new();
+ if (!e_id || !ctx || !bn || !q ||
+ !EC_GROUP_get_order(group, q, ctx) ||
+ !BN_rand_range(bn, q) ||
+ !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx))
+ goto fail;
+
+ dpp_debug_print_point("DPP: Generated random point E-id", group, e_id);
+
+ id = os_zalloc(sizeof(*id));
+ if (!id)
+ goto fail;
+ id->group = group;
+ id->e_id = e_id;
+ e_id = NULL;
+ id->csign = csign;
+ csign = NULL;
+ id->pp_key = ppkey;
+ ppkey = NULL;
+fail:
+ EC_POINT_free(e_id);
+ EVP_PKEY_free(csign);
+ EVP_PKEY_free(ppkey);
+ BN_clear_free(bn);
+ BN_CTX_free(ctx);
+ return id;
+}
+
+
+static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ EC_KEY *eckey;
+ EVP_PKEY *pkey = NULL;
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto fail;
+ }
+
+fail:
+ EC_KEY_free(eckey);
+ return pkey;
+}
+
+
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id)
+{
+ BN_CTX *ctx = NULL;
+ BIGNUM *bn = NULL, *q = NULL;
+ EC_POINT *e_prime_id = NULL, *a_nonce = NULL;
+ int ret = -1;
+ const EC_KEY *pp;
+ const EC_POINT *pp_point;
+
+ pp = EVP_PKEY_get0_EC_KEY(id->pp_key);
+ if (!pp)
+ goto fail;
+ pp_point = EC_KEY_get0_public_key(pp);
+ e_prime_id = EC_POINT_new(id->group);
+ a_nonce = EC_POINT_new(id->group);
+ ctx = BN_CTX_new();
+ bn = BN_new();
+ q = BN_new();
+ /* Generate random 0 <= a-nonce < q
+ * A-NONCE = a-nonce * G
+ * E'-id = E-id + a-nonce * P_pk */
+ if (!pp_point || !e_prime_id || !a_nonce || !ctx || !bn || !q ||
+ !EC_GROUP_get_order(id->group, q, ctx) ||
+ !BN_rand_range(bn, q) || /* bn = a-nonce */
+ !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) ||
+ !EC_POINT_mul(id->group, e_prime_id, NULL, pp_point, bn, ctx) ||
+ !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx))
+ goto fail;
+
+ dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce);
+ dpp_debug_print_point("DPP: Encrypted E-id to E'-id",
+ id->group, e_prime_id);
+
+ EVP_PKEY_free(id->a_nonce);
+ EVP_PKEY_free(id->e_prime_id);
+ id->a_nonce = dpp_pkey_from_point(id->group, a_nonce);
+ id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id);
+ if (!id->a_nonce || !id->e_prime_id)
+ goto fail;
+
+ ret = 0;
+
+fail:
+ EC_POINT_free(e_prime_id);
+ EC_POINT_free(a_nonce);
+ BN_clear_free(bn);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id)
+{
+ if (id) {
+ EC_POINT_clear_free(id->e_id);
+ EVP_PKEY_free(id->csign);
+ EVP_PKEY_free(id->a_nonce);
+ EVP_PKEY_free(id->e_prime_id);
+ EVP_PKEY_free(id->pp_key);
+ os_free(id);
+ }
+}
+
+
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+ EVP_PKEY *e_prime_id)
+{
+ const EC_KEY *pp_ec, *a_nonce_ec, *e_prime_id_ec;
+ const BIGNUM *pp_bn;
+ const EC_GROUP *group;
+ EC_POINT *e_id = NULL;
+ const EC_POINT *a_nonce_point, *e_prime_id_point;
+ BN_CTX *ctx = NULL;
+
+ if (!ppkey)
+ return NULL;
+
+ /* E-id = E'-id - s_C * A-NONCE */
+ pp_ec = EVP_PKEY_get0_EC_KEY(ppkey);
+ a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce);
+ e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id);
+ if (!pp_ec || !a_nonce_ec || !e_prime_id_ec)
+ return NULL;
+ pp_bn = EC_KEY_get0_private_key(pp_ec);
+ group = EC_KEY_get0_group(pp_ec);
+ a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec);
+ e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec);
+ ctx = BN_CTX_new();
+ if (!pp_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx)
+ goto fail;
+ e_id = EC_POINT_new(group);
+ if (!e_id ||
+ !EC_POINT_mul(group, e_id, NULL, a_nonce_point, pp_bn, ctx) ||
+ !EC_POINT_invert(group, e_id, ctx) ||
+ !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) {
+ EC_POINT_clear_free(e_id);
+ goto fail;
+ }
+
+ dpp_debug_print_point("DPP: Decrypted E-id", group, e_id);
+
+fail:
+ BN_CTX_free(ctx);
+ return e_id;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve)
+{
+ BN_CTX *ctx;
+ BIGNUM *x, *y;
+ int ret = -1;
+ EC_GROUP *group;
+ EC_POINT *point;
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return -1;
+
+ ctx = BN_CTX_new();
+ point = EC_POINT_new(group);
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !point || !x || !y)
+ goto fail;
+
+ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ /* Generate a random y coordinate that results in a point that is not
+ * on the curve. */
+ for (;;) {
+ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+ ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
+ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+ * return an error from EC_POINT_set_affine_coordinates_GFp()
+ * when the point is not on the curve. */
+ break;
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
+ goto fail;
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx))
+ break;
+ }
+
+ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ EC_GROUP_free(group);
+
+ return ret;
+}
+
+
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+ char *tmp, *pos, *signed3 = NULL;
+ unsigned char *signature = NULL;
+ size_t signature_len = 0, signed3_len;
+
+ tmp = os_zalloc(os_strlen(connector) + 5);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, connector, os_strlen(connector));
+
+ pos = os_strchr(tmp, '.');
+ if (!pos)
+ goto fail;
+
+ pos = os_strchr(pos + 1, '.');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+ pos);
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature || signature_len == 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+ signature, signature_len);
+ signature[signature_len - 1] ^= 0x01;
+ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+ os_memcpy(pos, signed3, signed3_len);
+ pos[signed3_len] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+ pos);
+
+out:
+ os_free(signature);
+ os_free(signed3);
+ return tmp;
+fail:
+ os_free(tmp);
+ tmp = NULL;
+ goto out;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
new file mode 100644
index 0000000..af12467
--- /dev/null
+++ b/src/common/dpp_i.h
@@ -0,0 +1,160 @@
+/*
+ * DPP module internal definitions
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_I_H
+#define DPP_I_H
+
+#ifdef CONFIG_DPP
+
+struct dpp_global {
+ void *msg_ctx;
+ struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list configurator; /* struct dpp_configurator */
+#ifdef CONFIG_DPP2
+ struct dl_list controllers; /* struct dpp_relay_controller */
+ struct dpp_controller *controller;
+ struct dl_list tcp_init; /* struct dpp_connection */
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+ void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
+#endif /* CONFIG_DPP2 */
+};
+
+/* dpp.c */
+
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status);
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash);
+unsigned int dpp_next_id(struct dpp_global *dpp);
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+ const u8 *ssid, size_t ssid_len,
+ const char *channel_list);
+struct json_token * dpp_parse_own_connector(const char *own_connector);
+int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root, bool reconfig);
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve);
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve);
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes, u16 num_modes);
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt);
+int dpp_gen_uri(struct dpp_bootstrap_info *bi);
+void dpp_write_adv_proto(struct wpabuf *buf);
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query);
+
+/* dpp_backup.c */
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key);
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth);
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len);
+
+/* dpp_crypto.c */
+
+struct dpp_signed_connector_info {
+ unsigned char *payload;
+ size_t payload_len;
+};
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector);
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len);
+const struct dpp_curve_params * dpp_get_curve_name(const char *name);
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
+const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group);
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len);
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen);
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len);
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point);
+void dpp_debug_print_key(const char *title, EVP_PKEY *key);
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen);
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve);
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
+int dpp_derive_bk_ke(struct dpp_authentication *auth);
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth);
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth);
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth);
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth);
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len);
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid);
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len);
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key);
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key);
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+ EVP_PKEY *e_prime_id);
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon);
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+
+struct dpp_reconfig_id {
+ const EC_GROUP *group;
+ EC_POINT *e_id; /* E-id */
+ EVP_PKEY *csign;
+ EVP_PKEY *a_nonce; /* A-NONCE */
+ EVP_PKEY *e_prime_id; /* E'-id */
+ EVP_PKEY *pp_key;
+};
+
+/* dpp_tcp.c */
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+void dpp_tcp_init_flush(struct dpp_global *dpp);
+void dpp_relay_flush_controllers(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
+#endif /* DPP_I_H */
diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c
new file mode 100644
index 0000000..807ab7d
--- /dev/null
+++ b/src/common/dpp_pkex.c
@@ -0,0 +1,1324 @@
+/*
+ * DPP PKEX functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+ const EC_KEY *X_ec;
+ const EC_POINT *X_point;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ EC_POINT *Qi = NULL, *M = NULL;
+ struct wpabuf *M_buf = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qi)
+ goto fail;
+
+ /* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key x/X");
+ pkex->x = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->x = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->x)
+ goto fail;
+
+ /* M = X + Qi */
+ X_ec = EVP_PKEY_get0_EC_KEY(pkex->x);
+ if (!X_ec)
+ goto fail;
+ X_point = EC_KEY_get0_public_key(X_ec);
+ if (!X_point)
+ goto fail;
+ dpp_debug_print_point("DPP: X", group, X_point);
+ M = EC_POINT_new(group);
+ Mx = BN_new();
+ My = BN_new();
+ if (!M || !Mx || !My ||
+ EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: M", group, M);
+
+ /* Initiator -> Responder: group, [identifier,] M */
+ attr_len = 4 + 2;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+ goto skip_finite_cyclic_group;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* M in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+out:
+ wpabuf_free(M_buf);
+ EC_POINT_free(M);
+ EC_POINT_free(Qi);
+ BN_clear_free(Mx);
+ BN_clear_free(My);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const char *identifier,
+ const char *code)
+{
+ struct dpp_pkex *pkex;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ return NULL;
+ pkex->msg_ctx = msg_ctx;
+ pkex->initiator = 1;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+ pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+ if (!pkex->exchange_req)
+ goto fail;
+ return pkex;
+fail:
+ dpp_pkex_free(pkex);
+ return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+ enum dpp_status_error status,
+ const BIGNUM *Nx, const BIGNUM *Ny)
+{
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ /* Initiator -> Responder: DPP Status, [identifier,] N */
+ attr_len = 4 + 1;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+ if (status != DPP_STATUS_OK)
+ goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* N in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+skip_encrypted_key:
+ if (status == DPP_STATUS_BAD_GROUP) {
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+ }
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
+ const char *identifier)
+{
+ if (!attr_id && identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code identifier received, but expected one");
+ return 0;
+ }
+
+ if (attr_id && !identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX code identifier received, but not expecting one");
+ return 0;
+ }
+
+ if (attr_id && identifier &&
+ (os_strlen(identifier) != attr_id_len ||
+ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len)
+{
+ const u8 *attr_group, *attr_id, *attr_key;
+ u16 attr_group_len, attr_id_len, attr_key_len;
+ const struct dpp_curve_params *curve = bi->curve;
+ u16 ike_group;
+ struct dpp_pkex *pkex = NULL;
+ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ const EC_KEY *Y_ec;
+ EC_KEY *X_ec = NULL;
+ const EC_POINT *Y_point;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Kx_len;
+ int res;
+
+ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "PKEX counter t limit reached - ignore message");
+ return NULL;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
+ return NULL;
+
+ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (!attr_group || attr_group_len != 2) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid Finite Cyclic Group attribute");
+ return NULL;
+ }
+ ike_group = WPA_GET_LE16(attr_group);
+ if (ike_group != curve->ike_group) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching PKEX curve: peer=%u own=%u",
+ ike_group, curve->ike_group);
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->own_bi = bi;
+ pkex->failed = 1;
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+ if (!pkex->exchange_resp)
+ goto fail;
+ return pkex;
+ }
+
+ /* M in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+ &group);
+ if (!Qi)
+ goto fail;
+
+ /* X' = M - Qi */
+ X = EC_POINT_new(group);
+ M = EC_POINT_new(group);
+ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!X || !M || !Mx || !My ||
+ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, M) ||
+ !EC_POINT_is_on_curve(group, M, bnctx) ||
+ EC_POINT_invert(group, Qi, bnctx) != 1 ||
+ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, X) ||
+ !EC_POINT_is_on_curve(group, X, bnctx)) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Invalid Encrypted Key value");
+ bi->pkex_t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: M", group, M);
+ dpp_debug_print_point("DPP: X'", group, X);
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->t = bi->pkex_t;
+ pkex->msg_ctx = msg_ctx;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+
+ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+ X_ec = EC_KEY_new();
+ if (!X_ec ||
+ EC_KEY_set_group(X_ec, group) != 1 ||
+ EC_KEY_set_public_key(X_ec, X) != 1)
+ goto fail;
+ pkex->x = EVP_PKEY_new();
+ if (!pkex->x ||
+ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+ goto fail;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ if (!Qr)
+ goto fail;
+
+ /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key y/Y");
+ pkex->y = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->y = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->y)
+ goto fail;
+
+ /* N = Y + Qr */
+ Y_ec = EVP_PKEY_get0_EC_KEY(pkex->y);
+ if (!Y_ec)
+ goto fail;
+ Y_point = EC_KEY_get0_public_key(Y_ec);
+ if (!Y_point)
+ goto fail;
+ dpp_debug_print_point("DPP: Y", group, Y_point);
+ N = EC_POINT_new(group);
+ Nx = BN_new();
+ Ny = BN_new();
+ if (!N || !Nx || !Ny ||
+ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: N", group, N);
+
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+ Nx, Ny);
+ if (!pkex->exchange_resp)
+ goto fail;
+
+ /* K = y * X' */
+ if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+ pkex->Mx, curve->prime_len,
+ pkex->Nx, curve->prime_len, pkex->code,
+ Kx, Kx_len, pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ pkex->exchange_done = 1;
+
+out:
+ BN_CTX_free(bnctx);
+ EC_POINT_free(Qi);
+ EC_POINT_free(Qr);
+ BN_free(Mx);
+ BN_free(My);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_POINT_free(M);
+ EC_POINT_free(N);
+ EC_POINT_free(X);
+ EC_KEY_free(X_ec);
+ EC_GROUP_free(group);
+ return pkex;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+ dpp_pkex_free(pkex);
+ pkex = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+ const struct wpabuf *A_pub, const u8 *u)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL;
+ u8 *wrapped;
+ u8 octet;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* {A, u, [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* A in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(A_pub));
+ wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+ goto skip_i_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len - 1);
+ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+ goto skip_i_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* u in I-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t buflen)
+{
+ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+ EC_GROUP *group = NULL;
+ BN_CTX *bnctx = NULL;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ EC_KEY *Y_ec = NULL;
+ size_t Jx_len, Kx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 u[DPP_MAX_HASH_LEN];
+ int res;
+
+ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX Exchange Response");
+ pkex->failed = 1;
+ return NULL;
+ }
+
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
+ attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+ &attr_status_len);
+ if (!attr_status || attr_status_len != 1) {
+ dpp_pkex_fail(pkex, "No DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+ attr_group = dpp_get_attr(buf, buflen,
+ DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (attr_group && attr_group_len == 2) {
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Peer indicated mismatching PKEX group - proposed %u",
+ WPA_GET_LE16(attr_group));
+ return NULL;
+ }
+ }
+
+ if (attr_status[0] != DPP_STATUS_OK) {
+ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
+ return NULL;
+ }
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
+ pkex->identifier)) {
+ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
+ return NULL;
+ }
+
+ /* N in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qr)
+ goto fail;
+
+ /* Y' = N - Qr */
+ Y = EC_POINT_new(group);
+ N = EC_POINT_new(group);
+ Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!Y || !N || !Nx || !Ny ||
+ EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, N) ||
+ !EC_POINT_is_on_curve(group, N, bnctx) ||
+ EC_POINT_invert(group, Qr, bnctx) != 1 ||
+ EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, Y) ||
+ !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+ pkex->t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: N", group, N);
+ dpp_debug_print_point("DPP: Y'", group, Y);
+
+ pkex->exchange_done = 1;
+
+ /* ECDH: J = a * Y' */
+ Y_ec = EC_KEY_new();
+ if (!Y_ec ||
+ EC_KEY_set_group(Y_ec, group) != 1 ||
+ EC_KEY_set_public_key(Y_ec, Y) != 1)
+ goto fail;
+ pkex->y = EVP_PKEY_new();
+ if (!pkex->y ||
+ EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+ goto fail;
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x) */
+ A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+ /* K = x * Y' */
+ if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+ pkex->Mx, curve->prime_len,
+ attr_key /* N.x */, attr_key_len / 2,
+ pkex->code, Kx, Kx_len,
+ pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+ if (!msg)
+ goto fail;
+
+out:
+ wpabuf_free(A_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EC_POINT_free(Qr);
+ EC_POINT_free(Y);
+ EC_POINT_free(N);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_KEY_free(Y_ec);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+ const struct wpabuf *B_pub, const u8 *v)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 octet;
+ u8 *wrapped;
+ struct wpabuf *clear = NULL;
+ size_t clear_len, attr_len;
+
+ /* {B, v [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* B in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(B_pub));
+ wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+ goto skip_r_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len - 1);
+ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+ goto skip_r_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* v in R-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+ const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ size_t Jx_len, Lx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *wrapped_data, *b_key, *peer_u;
+ u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ struct wpabuf *B_pub = NULL;
+ u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Request");
+ pkex->failed = 1;
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->failed = 1;
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: J' = y * A' */
+ if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+ A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+
+ peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &peer_u_len);
+ if (!peer_u || peer_u_len != curve->hash_len ||
+ os_memcmp(peer_u, u, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+ u, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+ /* ECDH: L = b * X' */
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+ B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ if (!B_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+ if (!msg)
+ goto fail;
+
+out:
+ os_free(unwrapped);
+ wpabuf_free(A_pub);
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Commit-Reveal Request processing failed");
+ goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ const u8 *wrapped_data, *b_key, *peer_v;
+ u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int ret = -1;
+ u8 v[DPP_MAX_HASH_LEN];
+ size_t Lx_len;
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Response");
+ pkex->failed = 1;
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: L' = x * B' */
+ if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+ B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ if (!B_pub || !X_pub || !Y_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+
+ peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+ &peer_v_len);
+ if (!peer_v || peer_v_len != curve->hash_len ||
+ os_memcmp(peer_v, v, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+ v, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+ ret = 0;
+out:
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ os_free(unwrapped);
+ return ret;
+fail:
+ goto out;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+ unsigned int freq)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+ bi->id = dpp_next_id(dpp);
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+ bi->num_freq = 1;
+ bi->freq[0] = freq;
+ bi->curve = pkex->own_bi->curve;
+ bi->pubkey = pkex->peer_bootstrap_key;
+ pkex->peer_bootstrap_key = NULL;
+ if (dpp_bootstrap_key_hash(bi) < 0) {
+ dpp_bootstrap_info_free(bi);
+ return NULL;
+ }
+ dpp_pkex_free(pkex);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ return bi;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+ if (!pkex)
+ return;
+
+ os_free(pkex->identifier);
+ os_free(pkex->code);
+ EVP_PKEY_free(pkex->x);
+ EVP_PKEY_free(pkex->y);
+ EVP_PKEY_free(pkex->peer_bootstrap_key);
+ wpabuf_free(pkex->exchange_req);
+ wpabuf_free(pkex->exchange_resp);
+ os_free(pkex);
+}
diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c
new file mode 100644
index 0000000..c4a0273
--- /dev/null
+++ b/src/common/dpp_reconfig.c
@@ -0,0 +1,958 @@
+/*
+ * DPP reconfiguration
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/json.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_DPP2
+
+static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct dpp_reconfig_id *id)
+{
+ struct wpabuf *msg = NULL;
+ EVP_PKEY *csign = NULL;
+ const unsigned char *p;
+ struct wpabuf *uncomp;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+ int res;
+ size_t attr_len;
+ const struct dpp_curve_params *own_curve;
+ EVP_PKEY *own_key;
+ struct wpabuf *a_nonce = NULL, *e_id = NULL;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
+
+ own_key = dpp_set_keypair(&own_curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ uncomp = dpp_get_pubkey_point(csign, 1);
+ EVP_PKEY_free(csign);
+ if (!uncomp)
+ goto fail;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
+ hash, SHA256_MAC_LEN);
+
+ if (dpp_update_reconfig_id(id) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id");
+ goto fail;
+ }
+
+ a_nonce = dpp_get_pubkey_point(id->a_nonce, 0);
+ e_id = dpp_get_pubkey_point(id->e_prime_id, 0);
+ if (!a_nonce || !e_id)
+ goto fail;
+
+ attr_len = 4 + SHA256_MAC_LEN;
+ attr_len += 4 + 2;
+ attr_len += 4 + wpabuf_len(a_nonce);
+ attr_len += 4 + wpabuf_len(e_id);
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len);
+ if (!msg)
+ goto fail;
+
+ /* Configurator C-sign key Hash */
+ dpp_build_attr_csign_key_hash(msg, hash);
+
+ /* Finite Cyclic Group attribute */
+ wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
+ own_curve->ike_group);
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, own_curve->ike_group);
+
+ /* A-NONCE */
+ wpabuf_put_le16(msg, DPP_ATTR_A_NONCE);
+ wpabuf_put_le16(msg, wpabuf_len(a_nonce));
+ wpabuf_put_buf(msg, a_nonce);
+
+ /* E'-id */
+ wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID);
+ wpabuf_put_le16(msg, wpabuf_len(e_id));
+ wpabuf_put_buf(msg, e_id);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Announcement frame attributes", msg);
+fail:
+ wpabuf_free(a_nonce);
+ wpabuf_free(e_id);
+ EVP_PKEY_free(own_key);
+ return msg;
+}
+
+
+static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg;
+ size_t attr_len;
+
+ /* Build DPP Reconfig Authentication Request frame attributes */
+ attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
+ 4 + auth->curve->nonce_len;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* DPP Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
+ wpabuf_put_str(msg, auth->conf->connector);
+
+ /* C-nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(msg, auth->curve->nonce_len);
+ wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Request frame attributes",
+ msg);
+
+ return msg;
+}
+
+
+static int
+dpp_configurator_build_own_connector(struct dpp_configurator *conf,
+ const struct dpp_curve_params *curve)
+{
+ struct wpabuf *dppcon = NULL;
+ int ret = -1;
+
+ if (conf->connector)
+ return 0; /* already generated */
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
+ conf->curve->name);
+ conf->connector_key = dpp_gen_keypair(curve);
+ if (!conf->connector_key)
+ goto fail;
+
+ /* Connector (JSON dppCon object) */
+ dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3);
+ if (!dppcon)
+ goto fail;
+ json_start_object(dppcon, NULL);
+ json_start_array(dppcon, "groups");
+ json_start_object(dppcon, NULL);
+ json_add_string(dppcon, "groupId", "*");
+ json_value_sep(dppcon);
+ json_add_string(dppcon, "netRole", "configurator");
+ json_end_object(dppcon);
+ json_end_array(dppcon);
+ json_value_sep(dppcon);
+ if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
+ curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+ goto fail;
+ }
+ json_end_object(dppcon);
+ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+ (const char *) wpabuf_head(dppcon));
+
+ conf->connector = dpp_sign_connector(conf, dppcon);
+ if (!conf->connector)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector);
+
+ ret = 0;
+fail:
+ wpabuf_free(dppcon);
+ return ret;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_configurator *conf, unsigned int freq, u16 group,
+ const u8 *a_nonce_attr, size_t a_nonce_len,
+ const u8 *e_id_attr, size_t e_id_len)
+{
+ struct dpp_authentication *auth;
+ const struct dpp_curve_params *curve;
+ EVP_PKEY *a_nonce, *e_prime_id;
+ EC_POINT *e_id;
+
+ curve = dpp_get_curve_ike_group(group);
+ if (!curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported group %u - cannot reconfigure",
+ group);
+ return NULL;
+ }
+
+ if (!a_nonce_attr) {
+ wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len);
+ a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len);
+ if (!a_nonce) {
+ wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE");
+ return NULL;
+ }
+ dpp_debug_print_key("A-NONCE", a_nonce);
+
+ if (!e_id_attr) {
+ wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute");
+ return NULL;
+ }
+ e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len);
+ if (!e_prime_id) {
+ wpa_printf(MSG_INFO, "DPP: Invalid E'-id");
+ EVP_PKEY_free(a_nonce);
+ return NULL;
+ }
+ dpp_debug_print_key("E'-id", e_prime_id);
+ e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id);
+ EVP_PKEY_free(a_nonce);
+ EVP_PKEY_free(e_prime_id);
+ if (!e_id) {
+ wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id");
+ return NULL;
+ }
+ /* TODO: could use E-id to determine whether reconfiguration with this
+ * Enrollee has already been started and is waiting for updated
+ * configuration instead of replying again before such configuration
+ * becomes available */
+ EC_POINT_clear_free(e_id);
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->conf = conf;
+ auth->reconfig = 1;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ auth->configurator = 1;
+ auth->curve = curve;
+ auth->transaction_id = 1;
+ if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ if (dpp_configurator_build_own_connector(conf, curve) < 0)
+ goto fail;
+
+ if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce");
+ goto fail;
+ }
+
+ auth->reconfig_req_msg = dpp_reconfig_build_req(auth);
+ if (!auth->reconfig_req_msg)
+ goto fail;
+
+out:
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+static int dpp_reconfig_build_resp(struct dpp_authentication *auth,
+ const char *own_connector,
+ struct wpabuf *conn_status)
+{
+ struct wpabuf *msg = NULL, *clear, *pr = NULL;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+ int res = -1;
+
+ /* Build DPP Reconfig Authentication Response frame attributes */
+ clear_len = 4 + auth->curve->nonce_len +
+ 4 + wpabuf_len(conn_status);
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* C-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
+
+ /* Connection Status (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
+ wpabuf_put_le16(clear, wpabuf_len(conn_status));
+ wpabuf_put_buf(clear, conn_status);
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ attr_len = 4 + 1 + 4 + 1 +
+ 4 + os_strlen(own_connector) +
+ 4 + auth->curve->nonce_len +
+ 4 + wpabuf_len(pr) +
+ 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* R-Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(own_connector));
+ wpabuf_put_str(msg, own_connector);
+
+ /* E-nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(msg, auth->curve->nonce_len);
+ wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len);
+
+ /* Responder Protocol Key (Pr) */
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Response frame attributes",
+ msg);
+
+ wpabuf_free(auth->reconfig_resp_msg);
+ auth->reconfig_resp_msg = msg;
+
+ res = 0;
+out:
+ wpabuf_free(clear);
+ wpabuf_free(pr);
+ return res;
+fail:
+ wpabuf_free(msg);
+ goto out;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+ const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ unsigned int freq, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ struct dpp_authentication *auth = NULL;
+ const u8 *trans_id, *version, *i_connector, *c_nonce;
+ u16 trans_id_len, version_len, i_connector_len, c_nonce_len;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *own_root = NULL, *token;
+ unsigned char *own_conn = NULL;
+ struct wpabuf *conn_status = NULL;
+
+ os_memset(&info, 0, sizeof(info));
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &i_connector_len);
+ if (!i_connector) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
+ i_connector, i_connector_len);
+
+ c_nonce = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid C-nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+ i_connector, i_connector_len);
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ own_root = dpp_parse_own_connector(own_connector);
+ if (!root || !own_root ||
+ !dpp_connector_match_groups(own_root, root, true)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector does not include compatible group netrole with own connector");
+ goto fail;
+ }
+
+ token = json_get_member(root, "expiry");
+ if (token && token->type == JSON_STRING &&
+ dpp_key_expired(token->string, NULL)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector (netAccessKey) has expired");
+ goto fail;
+ }
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ goto fail;
+ }
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->reconfig = 1;
+ auth->allowed_roles = DPP_CAPAB_ENROLLEE;
+ if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ auth->transaction_id = trans_id[0];
+
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ os_memcpy(auth->c_nonce, c_nonce, c_nonce_len);
+
+ if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
+ net_access_key_len, token) < 0)
+ goto fail;
+
+ if (c_nonce_len != auth->curve->nonce_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected C-nonce length %u (curve nonce len %zu)",
+ c_nonce_len, auth->curve->nonce_len);
+ goto fail;
+ }
+
+ /* Build Connection Status object */
+ /* TODO: Get appropriate result value */
+ /* TODO: ssid64 and channelList */
+ conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL);
+ if (!conn_status)
+ goto fail;
+
+ if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0)
+ goto fail;
+
+out:
+ os_free(info.payload);
+ os_free(own_conn);
+ json_free(root);
+ json_free(own_root);
+ wpabuf_free(conn_status);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_build_conf(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg = NULL, *clear;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+ u8 flags;
+
+ /* Build DPP Reconfig Authentication Confirm frame attributes */
+ clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
+ 4 + 1;
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* Transaction ID */
+ wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->peer_version);
+
+ /* C-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
+
+ /* E-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len);
+
+ /* Reconfig-Flags (wrapped) */
+ flags = DPP_CONFIG_REPLACEKEY;
+ wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, flags);
+
+ attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ attr_len += 4 + 1;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Confirm frame attributes",
+ msg);
+
+out:
+ wpabuf_free(clear);
+ return msg;
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
+ *c_nonce, *e_nonce, *conn_status;
+ u16 trans_id_len, version_len, r_connector_len, r_proto_len,
+ wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len;
+ struct wpabuf *conf = NULL;
+ char *signed_connector = NULL;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *token, *conn_status_json = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+
+ os_memset(&info, 0, sizeof(info));
+
+ if (!auth->reconfig || !auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ dpp_auth_fail(auth, "Peer did not include Transaction ID");
+ goto fail;
+ }
+ if (trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth, "Transaction ID mismatch");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &r_connector_len);
+ if (!r_connector) {
+ dpp_auth_fail(auth, " Missing R-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
+ r_connector, r_connector_len);
+
+ e_nonce = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+ os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ signed_connector = os_malloc(r_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, r_connector, r_connector_len);
+ signed_connector[r_connector_len] = '\0';
+
+ res = dpp_process_signed_connector(&info, auth->conf->csign,
+ signed_connector);
+ if (res != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Invalid R-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Invalid Connector payload");
+ goto fail;
+ }
+
+ /* Do not check netAccessKey expiration for reconfiguration to allow
+ * expired Connector to be updated. */
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No netAccessKey object found");
+ goto fail;
+ }
+
+ if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
+ token) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid C-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ conn_status = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONN_STATUS, &conn_status_len);
+ if (!conn_status) {
+ dpp_auth_fail(auth, "Missing Connection Status attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
+ conn_status, conn_status_len);
+
+ conn_status_json = json_parse((const char *) conn_status,
+ conn_status_len);
+ if (!conn_status_json) {
+ dpp_auth_fail(auth, "Could not parse connStatus");
+ goto fail;
+ }
+ /* TODO: use connStatus information */
+
+ conf = dpp_reconfig_build_conf(auth);
+ if (conf)
+ auth->reconfig_success = true;
+
+out:
+ json_free(root);
+ json_free(conn_status_json);
+ bin_clear_free(unwrapped, unwrapped_len);
+ os_free(info.payload);
+ os_free(signed_connector);
+ return conf;
+fail:
+ wpabuf_free(conf);
+ conf = NULL;
+ goto out;
+}
+
+
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce,
+ *reconfig_flags, *status;
+ u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len,
+ e_nonce_len, reconfig_flags_len, status_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int res = -1;
+ u8 flags;
+
+ if (!auth->reconfig || auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth,
+ "Reconfiguration did not complete successfully");
+ goto fail;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ trans_id = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_TRANSACTION_ID, &trans_id_len);
+ if (!trans_id || trans_id_len != 1 ||
+ trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth,
+ "Peer did not include valid Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_PROTOCOL_VERSION, &version_len);
+ if (!version || version_len < 1 || version[0] != DPP_VERSION) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid C-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+
+ reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_RECONFIG_FLAGS,
+ &reconfig_flags_len);
+ if (!reconfig_flags || reconfig_flags_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
+ goto fail;
+ }
+ flags = reconfig_flags[0] & BIT(0);
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags);
+ auth->reconfig_connector_key = flags;
+
+ auth->reconfig_success = true;
+ res = 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return res;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
new file mode 100644
index 0000000..7e330d6
--- /dev/null
+++ b/src/common/dpp_tcp.c
@@ -0,0 +1,1794 @@
+/*
+ * DPP over TCP
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+struct dpp_connection {
+ struct dl_list list;
+ struct dpp_controller *ctrl;
+ struct dpp_relay_controller *relay;
+ struct dpp_global *global;
+ struct dpp_authentication *auth;
+ void *msg_ctx;
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+ int sock;
+ u8 mac_addr[ETH_ALEN];
+ unsigned int freq;
+ u8 msg_len[4];
+ size_t msg_len_octets;
+ struct wpabuf *msg;
+ struct wpabuf *msg_out;
+ size_t msg_out_pos;
+ unsigned int read_eloop:1;
+ unsigned int write_eloop:1;
+ unsigned int on_tcp_tx_complete_gas_done:1;
+ unsigned int on_tcp_tx_complete_remove:1;
+ unsigned int on_tcp_tx_complete_auth_ok:1;
+ unsigned int gas_comeback_in_progress:1;
+ u8 gas_dialog_token;
+ char *name;
+ enum dpp_netrole netrole;
+};
+
+/* Remote Controller */
+struct dpp_relay_controller {
+ struct dl_list list;
+ struct dpp_global *global;
+ u8 pkhash[SHA256_MAC_LEN];
+ struct hostapd_ip_addr ipaddr;
+ void *msg_ctx;
+ void *cb_ctx;
+ void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+ size_t len);
+ void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+ int prot, struct wpabuf *buf);
+ struct dl_list conn; /* struct dpp_connection */
+};
+
+/* Local Controller */
+struct dpp_controller {
+ struct dpp_global *global;
+ u8 allowed_roles;
+ int qr_mutual;
+ int sock;
+ struct dl_list conn; /* struct dpp_connection */
+ char *configurator_params;
+ enum dpp_netrole netrole;
+ void *msg_ctx;
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+};
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator);
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
+
+
+static void dpp_connection_free(struct dpp_connection *conn)
+{
+ if (conn->sock >= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
+ conn->sock);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ close(conn->sock);
+ }
+ eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
+ eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+ wpabuf_free(conn->msg);
+ wpabuf_free(conn->msg_out);
+ dpp_auth_deinit(conn->auth);
+ os_free(conn->name);
+ os_free(conn);
+}
+
+
+static void dpp_connection_remove(struct dpp_connection *conn)
+{
+ dl_list_del(&conn->list);
+ dpp_connection_free(conn);
+}
+
+
+int dpp_relay_add_controller(struct dpp_global *dpp,
+ struct dpp_relay_config *config)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ dl_list_init(&ctrl->conn);
+ ctrl->global = dpp;
+ os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
+ os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+ ctrl->msg_ctx = config->msg_ctx;
+ ctrl->cb_ctx = config->cb_ctx;
+ ctrl->tx = config->tx;
+ ctrl->gas_resp_tx = config->gas_resp_tx;
+ dl_list_add(&dpp->controllers, &ctrl->list);
+ return 0;
+}
+
+
+static struct dpp_relay_controller *
+dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+ list) {
+ if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
+ return ctrl;
+ }
+
+ return NULL;
+}
+
+
+static void dpp_controller_gas_done(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ if (auth->waiting_csr) {
+ wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+ conn->on_tcp_tx_complete_gas_done = 0;
+ return;
+ }
+
+ if (auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+ auth->waiting_conf_result = 1;
+ return;
+ }
+
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_tcp_send(struct dpp_connection *conn)
+{
+ int res;
+
+ if (!conn->msg_out) {
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ return -1;
+ }
+ res = send(conn->sock,
+ wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
+ wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->msg_out_pos += res;
+ if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: %u/%u bytes of message sent to Controller",
+ (unsigned int) conn->msg_out_pos,
+ (unsigned int) wpabuf_len(conn->msg_out));
+ if (!conn->write_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) == 0)
+ conn->write_eloop = 1;
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out = NULL;
+ conn->msg_out_pos = 0;
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ if (!conn->read_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) == 0)
+ conn->read_eloop = 1;
+ if (conn->on_tcp_tx_complete_remove) {
+ dpp_connection_remove(conn);
+ } else if (conn->auth && (conn->ctrl || conn->auth->configurator) &&
+ conn->on_tcp_tx_complete_gas_done) {
+ dpp_controller_gas_done(conn);
+ } else if (conn->on_tcp_tx_complete_auth_ok) {
+ conn->on_tcp_tx_complete_auth_ok = 0;
+ dpp_controller_auth_success(conn, 1);
+ }
+
+ return 0;
+}
+
+
+static int dpp_tcp_send_msg(struct dpp_connection *conn,
+ const struct wpabuf *msg)
+{
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+ if (!conn->msg_out)
+ return -1;
+ wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
+ wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
+ wpabuf_len(msg) - 1);
+
+ if (dpp_tcp_send(conn) == 1) {
+ if (!conn->write_eloop) {
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready,
+ conn, NULL) < 0)
+ return -1;
+ conn->write_eloop = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void dpp_controller_start_gas_client(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *buf;
+ const char *dpp_name;
+
+ dpp_name = conn->name ? conn->name : "Test";
+ buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
+ NULL);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration request data available");
+ return;
+ }
+
+ dpp_tcp_send_msg(conn, buf);
+ wpabuf_free(buf);
+}
+
+
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ wpa_msg(conn->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (auth->configurator) {
+ /* Prevent GAS response */
+ auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->configurator)
+ dpp_controller_start_gas_client(conn);
+}
+
+
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
+ dpp_tcp_send(conn);
+}
+
+
+static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
+ const struct hostapd_ip_addr *ipaddr,
+ int port)
+{
+ struct sockaddr_in *dst;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 *dst6;
+#endif /* CONFIG_IPV6 */
+
+ switch (ipaddr->af) {
+ case AF_INET:
+ dst = (struct sockaddr_in *) addr;
+ os_memset(dst, 0, sizeof(*dst));
+ dst->sin_family = AF_INET;
+ dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
+ dst->sin_port = htons(port);
+ *addrlen = sizeof(*dst);
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ dst6 = (struct sockaddr_in6 *) addr;
+ os_memset(dst6, 0, sizeof(*dst6));
+ dst6->sin6_family = AF_INET6;
+ os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
+ sizeof(struct in6_addr));
+ dst6->sin6_port = htons(port);
+ *addrlen = sizeof(*dst6);
+ break;
+#endif /* CONFIG_IPV6 */
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct dpp_connection *
+dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
+ unsigned int freq)
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ char txt[100];
+
+ if (dl_list_len(&ctrl->conn) >= 15) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+ return NULL;
+ }
+
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
+ &ctrl->ipaddr, DPP_TCP_PORT) < 0)
+ return NULL;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ return NULL;
+
+ conn->global = ctrl->global;
+ conn->relay = ctrl;
+ conn->msg_ctx = ctrl->msg_ctx;
+ conn->cb_ctx = ctrl->global->cb_ctx;
+ os_memcpy(conn->mac_addr, src, ETH_ALEN);
+ conn->freq = freq;
+
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
+ conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+
+ dl_list_add(&ctrl->conn, &conn->list);
+ return conn;
+fail:
+ dpp_connection_free(conn);
+ return NULL;
+}
+
+
+static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
+ if (!msg)
+ return NULL;
+ wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
+ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
+ wpabuf_put_data(msg, buf, len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+ return msg;
+}
+
+
+static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
+ const u8 *buf, size_t len)
+{
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue already established Relay/Controller connection for this session");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ /* TODO: for proto ver 1, need to do remove connection based on GAS Resp
+ * TX status */
+ if (type == DPP_PA_CONFIGURATION_RESULT)
+ conn->on_tcp_tx_complete_remove = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len, unsigned int freq,
+ const u8 *i_bootstrap, const u8 *r_bootstrap)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn;
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ /* Check if there is an already started session for this peer and if so,
+ * continue that session (send this over TCP) and return 0.
+ */
+ if (type != DPP_PA_PEER_DISCOVERY_REQ &&
+ type != DPP_PA_PEER_DISCOVERY_RESP &&
+ type != DPP_PA_PRESENCE_ANNOUNCEMENT &&
+ type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0)
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+ }
+ }
+
+ if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+ type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ /* TODO: Could send this to all configured Controllers. For now,
+ * only the first Controller is supported. */
+ ctrl = dl_list_first(&dpp->controllers,
+ struct dpp_relay_controller, list);
+ } else {
+ if (!r_bootstrap)
+ return -1;
+ ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
+ }
+ if (!ctrl)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication Request for a configured Controller");
+ conn = dpp_relay_new_conn(ctrl, src, freq);
+ if (!conn)
+ return -1;
+
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ return 0;
+}
+
+
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+ size_t data_len)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn, *found = NULL;
+ struct wpabuf *msg;
+
+ /* Check if there is a successfully completed authentication for this
+ * and if so, continue that session (send this over TCP) and return 0.
+ */
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ if (found)
+ break;
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0) {
+ found = conn;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return -1;
+
+ msg = wpabuf_alloc(4 + 1 + data_len);
+ if (!msg)
+ return -1;
+ wpabuf_put_be32(msg, 1 + data_len);
+ wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
+ wpabuf_put_data(msg, data, data_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = msg;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static void dpp_controller_free(struct dpp_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ if (!ctrl)
+ return;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+
+ if (ctrl->sock >= 0) {
+ close(ctrl->sock);
+ eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
+ }
+ os_free(ctrl->configurator_params);
+ os_free(ctrl);
+}
+
+
+static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
+ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+
+ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+
+ /* Try to find own and peer bootstrapping key matches based on the
+ * received hash values */
+ dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
+ &own_bi, &peer_bi);
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "No matching own bootstrapping key found - ignore message");
+ return -1;
+ }
+
+ if (conn->auth) {
+ wpa_printf(MSG_INFO,
+ "Already in DPP authentication exchange - ignore new one");
+ return 0;
+ }
+
+ conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx,
+ conn->ctrl->allowed_roles,
+ conn->ctrl->qr_mutual,
+ peer_bi, own_bi, -1, hdr, buf, len);
+ if (!conn->auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return -1;
+ }
+
+ if (dpp_set_configurator(conn->auth,
+ conn->ctrl->configurator_params) < 0) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+}
+
+
+static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *msg;
+ int res;
+
+ if (!auth)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
+
+ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+ if (!msg) {
+ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start wait for full response");
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->on_tcp_tx_complete_auth_ok = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+ return res;
+}
+
+
+static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return -1;
+ }
+
+ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ return -1;
+ }
+
+ dpp_controller_auth_success(conn, 0);
+ return 0;
+}
+
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ if (!conn->auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Connection Status Result");
+ wpa_msg(conn->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONN_STATUS_RESULT "timeout");
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ void *msg_ctx = conn->msg_ctx;
+
+ if (!conn->ctrl && (!auth || !auth->configurator))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
+
+ if (!auth || !auth->waiting_conf_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for result - drop");
+ return -1;
+ }
+
+ status = dpp_conf_result_rx(auth, hdr, buf, len);
+ if (status == DPP_STATUS_OK && auth->send_conn_status) {
+ wpa_msg(msg_ctx, MSG_INFO,
+ DPP_EVENT_CONF_SENT "wait_conn_status=1");
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+ eloop_cancel_timeout(
+ dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ eloop_register_timeout(
+ 16, 0, dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ return 0;
+ }
+ if (status == DPP_STATUS_OK)
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len = 0;
+ char *channel_list = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+ if (!auth || !auth->waiting_conn_status_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for connection status result - drop");
+ return -1;
+ }
+
+ status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+ ssid, &ssid_len, &channel_list);
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+ "result=%d ssid=%s channel_list=%s",
+ status, wpa_ssid_txt(ssid, ssid_len),
+ channel_list ? channel_list : "N/A");
+ os_free(channel_list);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *r_bootstrap;
+ u16 r_bootstrap_len;
+ struct dpp_bootstrap_info *peer_bi;
+ struct dpp_authentication *auth;
+ struct dpp_global *dpp = conn->ctrl->global;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
+ if (!peer_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching bootstrapping information found");
+ return -1;
+ }
+
+ auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
+ DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, conn->auth->req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+ u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+ struct dpp_configurator *conf;
+ struct dpp_global *dpp = conn->ctrl->global;
+ struct dpp_authentication *auth;
+ u16 group;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement");
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(dpp, csign_hash);
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return -1;
+ }
+
+ fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &fcgroup_len);
+ if (!fcgroup || fcgroup_len != 2) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Finite Cyclic Group attribute");
+ return -1;
+ }
+ group = WPA_GET_LE16(fcgroup);
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+ a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+ e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+ auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group,
+ a_nonce, a_nonce_len, e_id, e_id_len);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, auth->reconfig_req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *conf;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response");
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return -1;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return -1;
+
+ res = dpp_tcp_send_msg(conn, conf);
+ wpabuf_free(conf);
+ return res;
+}
+
+
+static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end;
+ u8 type;
+
+ wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
+ pos = msg;
+ end = msg + len;
+
+ if (end - pos < DPP_HDR_LEN ||
+ WPA_GET_BE24(pos) != OUI_WFA ||
+ pos[3] != DPP_OUI_TYPE) {
+ wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
+ return -1;
+ }
+
+ if (pos[4] != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
+ pos[4]);
+ return -1;
+ }
+ type = pos[5];
+ wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
+ pos += DPP_HDR_LEN;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
+ pos, end - pos);
+ if (dpp_check_attrs(pos, end - pos) < 0)
+ return -1;
+
+ if (conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
+ conn->freq, msg, len);
+ return 0;
+ }
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_RESP:
+ return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_CONF:
+ return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
+ case DPP_PA_CONFIGURATION_RESULT:
+ return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
+ case DPP_PA_CONNECTION_STATUS_RESULT:
+ return dpp_controller_rx_conn_status_result(conn, msg, pos,
+ end - pos);
+ case DPP_PA_PRESENCE_ANNOUNCEMENT:
+ return dpp_controller_rx_presence_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ return dpp_controller_rx_reconfig_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos,
+ end - pos);
+ default:
+ /* TODO: missing messages types */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported frame subtype %d", type);
+ return -1;
+ }
+}
+
+
+static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action)
+{
+ struct wpabuf *buf;
+ size_t len = 18;
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ len++;
+
+ buf = wpabuf_alloc(4 + len);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_be32(buf, len);
+
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, conn->gas_dialog_token);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */
+
+ dpp_write_adv_proto(buf);
+ wpabuf_put_le16(buf, 0); /* Query Response Length */
+
+ /* Send Config Response over TCP */
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = buf;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action,
+ struct wpabuf *resp)
+{
+ struct wpabuf *buf;
+ size_t len;
+
+ if (!resp)
+ return -1;
+
+ len = 18 + wpabuf_len(resp);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ len++;
+
+ buf = wpabuf_alloc(4 + len);
+ if (!buf) {
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ wpabuf_put_be32(buf, len);
+
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, conn->gas_dialog_token);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+ dpp_write_adv_proto(buf);
+ dpp_write_gas_query(buf, resp);
+ wpabuf_free(resp);
+
+ /* Send Config Response over TCP; GAS fragmentation is taken care of by
+ * the Relay */
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = buf;
+ conn->on_tcp_tx_complete_gas_done = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end, *next;
+ const u8 *adv_proto;
+ u16 slen;
+ struct wpabuf *resp;
+ struct dpp_authentication *auth = conn->auth;
+
+ if (len < 1 + 2)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Request over TCP");
+
+ if (!auth || (!conn->ctrl && !auth->configurator) ||
+ (!auth->auth_success && !auth->reconfig_success)) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return -1;
+ }
+
+ pos = msg;
+ end = msg + len;
+
+ conn->gas_dialog_token = *pos++;
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Request */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ resp = dpp_conf_req_rx(auth, pos, slen);
+ if (!resp && auth->waiting_cert) {
+ wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+ conn->gas_comeback_in_progress = 1;
+ return dpp_tcp_send_comeback_delay(conn,
+ WLAN_PA_GAS_INITIAL_RESP);
+ }
+
+ return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp);
+}
+
+
+static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn,
+ const u8 *msg, size_t len)
+{
+ u8 dialog_token;
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *resp;
+
+ if (len < 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Request over TCP (comeback)");
+
+ if (!auth || (!conn->ctrl && !auth->configurator) ||
+ (!auth->auth_success && !auth->reconfig_success) ||
+ !conn->gas_comeback_in_progress) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return -1;
+ }
+
+ dialog_token = msg[0];
+ if (dialog_token != conn->gas_dialog_token) {
+ wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)",
+ dialog_token, conn->gas_dialog_token);
+ return -1;
+ }
+
+ if (!auth->conf_resp_tcp) {
+ wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+ return dpp_tcp_send_comeback_delay(conn,
+ WLAN_PA_GAS_COMEBACK_RESP);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configuration response is ready to be sent out");
+ resp = auth->conf_resp_tcp;
+ auth->conf_resp_tcp = NULL;
+ return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp);
+}
+
+
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth || !auth->csrattrs)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+ wpabuf_free(auth->csr);
+ /* TODO: Additional information needed for CSR based on csrAttrs */
+ auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test");
+ if (!auth->csr) {
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ dpp_controller_start_gas_client(conn);
+}
+
+
+static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
+{
+ struct dpp_authentication *auth = conn->auth;
+ int res;
+ struct wpabuf *msg;
+ enum dpp_status_error status;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configuration Response for local stack from TCP");
+
+ if (auth)
+ res = dpp_conf_resp_rx(auth, resp);
+ else
+ res = -1;
+ wpabuf_free(resp);
+ if (res == -2) {
+ wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+ eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
+ return 0;
+ }
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ return -1;
+ }
+
+ if (conn->process_conf_obj)
+ res = conn->process_conf_obj(conn->cb_ctx, auth);
+ else
+ res = 0;
+
+ if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+ status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
+ msg = dpp_build_conf_result(auth, status);
+ if (!msg)
+ return -1;
+
+ conn->on_tcp_tx_complete_remove = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+
+ /* This exchange will be terminated in the TX status handler */
+
+ return res;
+}
+
+
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *msg;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request");
+ msg = wpabuf_alloc(4 + 2);
+ if (!msg)
+ return;
+ wpabuf_put_be32(msg, 2);
+ wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ);
+ wpabuf_put_u8(msg, conn->gas_dialog_token);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = msg;
+ dpp_tcp_send(conn);
+}
+
+
+static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
+ size_t len, bool comeback)
+{
+ struct wpabuf *buf;
+ u8 dialog_token;
+ const u8 *pos, *end, *next, *adv_proto;
+ u16 status, slen, comeback_delay;
+
+ if (len < 5 + 2 + (comeback ? 1 : 0))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Response over TCP");
+
+ pos = msg;
+ end = msg + len;
+
+ dialog_token = *pos++;
+ status = WPA_GET_LE16(pos);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
+ return -1;
+ }
+ pos += 2;
+ if (comeback)
+ pos++; /* ignore Fragment ID */
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Response */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ if (comeback_delay) {
+ unsigned int secs, usecs;
+
+ conn->gas_dialog_token = dialog_token;
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u",
+ comeback_delay);
+ eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+ eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback,
+ conn, NULL);
+ return 0;
+ }
+
+ buf = wpabuf_alloc(slen);
+ if (!buf)
+ return -1;
+ wpabuf_put_data(buf, pos, slen);
+
+ if (!conn->relay &&
+ (!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE)))
+ return dpp_tcp_rx_gas_resp(conn, buf);
+
+ if (!conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ wpabuf_free(buf);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
+ dialog_token, 0, buf);
+
+ return 0;
+}
+
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ int res;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
+ sd);
+
+ if (conn->msg_len_octets < 4) {
+ u32 msglen;
+
+ res = recv(sd, &conn->msg_len[conn->msg_len_octets],
+ 4 - conn->msg_len_octets, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received %d/%d octet(s) of message length field",
+ res, (int) (4 - conn->msg_len_octets));
+ conn->msg_len_octets += res;
+
+ if (conn->msg_len_octets < 4) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %d more octets of message length field",
+ (int) (4 - conn->msg_len_octets));
+ return;
+ }
+
+ msglen = WPA_GET_BE32(conn->msg_len);
+ wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
+ if (msglen > 65535) {
+ wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpabuf_free(conn->msg);
+ conn->msg = wpabuf_alloc(msglen);
+ }
+
+ if (!conn->msg) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No buffer available for receiving the message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+
+ res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
+ wpabuf_put(conn->msg, res);
+
+ if (wpabuf_tailroom(conn->msg) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+ return;
+ }
+
+ conn->msg_len_octets = 0;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
+ if (wpabuf_len(conn->msg) < 1) {
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ pos = wpabuf_head(conn->msg);
+ switch (*pos) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ if (dpp_controller_rx_action(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ if (dpp_controller_rx_gas_req(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ if (dpp_rx_gas_resp(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1,
+ *pos == WLAN_PA_GAS_COMEBACK_RESP) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ if (dpp_controller_rx_gas_comeback_req(
+ conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
+ *pos);
+ break;
+ }
+}
+
+
+static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_controller *ctrl = eloop_ctx;
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int fd;
+ struct dpp_connection *conn;
+
+ wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
+
+ fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
+ if (fd < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to accept new connection: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ goto fail;
+
+ conn->global = ctrl->global;
+ conn->ctrl = ctrl;
+ conn->msg_ctx = ctrl->msg_ctx;
+ conn->cb_ctx = ctrl->cb_ctx;
+ conn->process_conf_obj = ctrl->process_conf_obj;
+ conn->sock = fd;
+ conn->netrole = ctrl->netrole;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) < 0)
+ goto fail;
+ conn->read_eloop = 1;
+
+ /* TODO: eloop timeout to expire connections that do not complete in
+ * reasonable time */
+ dl_list_add(&ctrl->conn, &conn->list);
+ return;
+
+fail:
+ close(fd);
+ os_free(conn);
+}
+
+
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+ const struct hostapd_ip_addr *addr, int port, const char *name,
+ enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
+ int (*process_conf_obj)(void *ctx,
+ struct dpp_authentication *auth))
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage saddr;
+ socklen_t addrlen;
+ const u8 *hdr, *pos, *end;
+ char txt[100];
+
+ wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
+ hostapd_ip_txt(addr, txt, sizeof(txt)), port);
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
+ addr, port) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->msg_ctx = msg_ctx;
+ conn->cb_ctx = cb_ctx;
+ conn->process_conf_obj = process_conf_obj;
+ conn->name = os_strdup(name ? name : "Test");
+ conn->netrole = netrole;
+ conn->global = dpp;
+ conn->auth = auth;
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ hdr = wpabuf_head(auth->req_msg);
+ end = hdr + wpabuf_len(auth->req_msg);
+ hdr += 2; /* skip Category and Actiom */
+ pos = hdr + DPP_HDR_LEN;
+ conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
+ if (!conn->msg_out)
+ goto fail;
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+ dl_list_add(&dpp->tcp_init, &conn->list);
+ return 0;
+fail:
+ dpp_connection_free(conn);
+ return -1;
+}
+
+
+int dpp_controller_start(struct dpp_global *dpp,
+ struct dpp_controller_config *config)
+{
+ struct dpp_controller *ctrl;
+ int on = 1;
+ struct sockaddr_in sin;
+ int port;
+
+ if (!dpp || dpp->controller)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ ctrl->global = dpp;
+ if (config->configurator_params)
+ ctrl->configurator_params =
+ os_strdup(config->configurator_params);
+ dl_list_init(&ctrl->conn);
+ ctrl->allowed_roles = config->allowed_roles;
+ ctrl->qr_mutual = config->qr_mutual;
+ ctrl->netrole = config->netrole;
+ ctrl->msg_ctx = config->msg_ctx;
+ ctrl->cb_ctx = config->cb_ctx;
+ ctrl->process_conf_obj = config->process_conf_obj;
+
+ ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (ctrl->sock < 0)
+ goto fail;
+
+ if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
+ if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /* TODO: IPv6 */
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
+ sin.sin_port = htons(port);
+ if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to bind Controller TCP port: %s",
+ strerror(errno));
+ goto fail;
+ }
+ if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
+ fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
+ dpp_controller_tcp_cb, ctrl, NULL))
+ goto fail;
+
+ dpp->controller = ctrl;
+ wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
+ return 0;
+fail:
+ dpp_controller_free(ctrl);
+ return -1;
+}
+
+
+void dpp_controller_stop(struct dpp_global *dpp)
+{
+ if (dpp) {
+ dpp_controller_free(dpp->controller);
+ dpp->controller = NULL;
+ }
+}
+
+
+static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth,
+ unsigned int id)
+{
+ return auth &&
+ ((auth->peer_bi && auth->peer_bi->id == id) ||
+ (auth->tmp_peer_bi && auth->tmp_peer_bi->id == id));
+}
+
+
+static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp,
+ unsigned int id)
+{
+ struct dpp_connection *conn;
+
+ dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+ if (dpp_tcp_peer_id_match(conn->auth, id))
+ return conn->auth;
+ }
+
+ return NULL;
+}
+
+
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+ unsigned int id)
+{
+ struct dpp_controller *ctrl = dpp->controller;
+ struct dpp_connection *conn;
+
+ if (!ctrl)
+ return dpp_tcp_get_auth(dpp, id);
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ if (dpp_tcp_peer_id_match(conn->auth, id))
+ return conn->auth;
+ }
+
+ return dpp_tcp_get_auth(dpp, id);
+}
+
+
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+ struct dpp_bootstrap_info *bi)
+{
+ struct dpp_controller *ctrl = dpp->controller;
+ struct dpp_connection *conn;
+
+ if (!ctrl)
+ return;
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth->response_pending ||
+ dpp_notify_new_qr_code(auth, bi) != 1)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sending out pending authentication response");
+ dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+ }
+}
+
+
+void dpp_tcp_init_flush(struct dpp_global *dpp)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+}
+
+
+static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+ os_free(ctrl);
+}
+
+
+void dpp_relay_flush_controllers(struct dpp_global *dpp)
+{
+ struct dpp_relay_controller *ctrl, *tmp;
+
+ if (!dpp)
+ return;
+
+ dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_del(&ctrl->list);
+ dpp_relay_controller_free(ctrl);
+ }
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/src/common/gas_server.c b/src/common/gas_server.c
index ca46758..c000aeb 100644
--- a/src/common/gas_server.c
+++ b/src/common/gas_server.c
@@ -1,6 +1,7 @@
/*
* Generic advertisement service (GAS) server
* Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -23,8 +24,9 @@
struct dl_list list;
u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN];
u8 adv_proto_id_len;
- struct wpabuf * (*req_cb)(void *ctx, const u8 *sa,
- const u8 *query, size_t query_len);
+ struct wpabuf * (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+ const u8 *query, size_t query_len,
+ u16 *comeback_delay);
void (*status_cb)(void *ctx, struct wpabuf *resp, int ok);
void *ctx;
struct gas_server *gas;
@@ -39,6 +41,7 @@
u8 dst[ETH_ALEN];
u8 dialog_token;
struct gas_server_handler *handler;
+ u16 comeback_delay;
};
struct gas_server {
@@ -61,7 +64,8 @@
response, MAC2STR(response->dst), response->dialog_token,
response->freq, response->frag_id,
(unsigned long) response->offset,
- (unsigned long) wpabuf_len(response->resp));
+ (unsigned long) (response->resp ?
+ wpabuf_len(response->resp) : 0));
response->handler->status_cb(response->handler->ctx,
response->resp, 0);
response->resp = NULL;
@@ -83,30 +87,29 @@
static void
gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler,
+ struct gas_server_response *response,
const u8 *da, int freq, u8 dialog_token,
- struct wpabuf *query_resp)
+ struct wpabuf *query_resp, u16 comeback_delay)
{
size_t max_len = (freq > 56160) ? 928 : 1400;
size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2;
size_t resp_frag_len;
struct wpabuf *resp;
- u16 comeback_delay;
- struct gas_server_response *response;
- if (!query_resp)
- return;
-
- response = os_zalloc(sizeof(*response));
- if (!response) {
- wpabuf_free(query_resp);
+ if (comeback_delay == 0 && !query_resp) {
+ gas_server_free_response(response);
return;
}
- wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
+
response->freq = freq;
response->handler = handler;
os_memcpy(response->dst, da, ETH_ALEN);
response->dialog_token = dialog_token;
- if (hdr_len + wpabuf_len(query_resp) > max_len) {
+ if (comeback_delay) {
+ /* Need more time to prepare the response */
+ resp_frag_len = 0;
+ response->comeback_delay = comeback_delay;
+ } else if (hdr_len + wpabuf_len(query_resp) > max_len) {
/* Need to use comeback to initiate fragmentation */
comeback_delay = 1;
resp_frag_len = 0;
@@ -135,10 +138,12 @@
/* Query Response Length */
wpabuf_put_le16(resp, resp_frag_len);
- if (!comeback_delay)
+ if (!comeback_delay && query_resp)
wpabuf_put_buf(resp, query_resp);
- if (comeback_delay) {
+ if (comeback_delay && !query_resp) {
+ wpa_printf(MSG_DEBUG, "GAS: No response available yet");
+ } else if (comeback_delay) {
wpa_printf(MSG_DEBUG,
"GAS: Need to fragment query response");
} else {
@@ -165,6 +170,7 @@
u16 query_req_len;
struct gas_server_handler *handler;
struct wpabuf *resp;
+ struct gas_server_response *response;
wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame",
data, len);
@@ -210,8 +216,15 @@
pos, end - pos);
}
+ response = os_zalloc(sizeof(*response));
+ if (!response)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response);
dl_list_for_each(handler, &gas->handlers, struct gas_server_handler,
list) {
+ u16 comeback_delay = 0;
+
if (adv_proto_len < 1 + handler->adv_proto_id_len ||
os_memcmp(adv_proto + 1, handler->adv_proto_id,
handler->adv_proto_id_len) != 0)
@@ -219,17 +232,22 @@
wpa_printf(MSG_DEBUG,
"GAS: Calling handler for the requested Advertisement Protocol ID");
- resp = handler->req_cb(handler->ctx, sa, query_req,
- query_req_len);
+ resp = handler->req_cb(handler->ctx, response, sa, query_req,
+ query_req_len, &comeback_delay);
wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler",
resp);
- gas_server_send_resp(gas, handler, sa, freq, dialog_token,
- resp);
+ if (comeback_delay)
+ wpa_printf(MSG_DEBUG,
+ "GAS: Handler requested comeback delay: %u TU",
+ comeback_delay);
+ gas_server_send_resp(gas, handler, response, sa, freq,
+ dialog_token, resp, comeback_delay);
return 0;
}
wpa_printf(MSG_DEBUG,
"GAS: No registered handler for the requested Advertisement Protocol ID");
+ gas_server_free_response(response);
return -1;
}
@@ -243,6 +261,31 @@
size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2;
size_t remaining, resp_frag_len;
struct wpabuf *resp;
+ unsigned int wait_time = 0;
+
+ if (!response->resp) {
+ resp = gas_build_comeback_resp(response->dialog_token,
+ WLAN_STATUS_SUCCESS, 0, 0,
+ response->comeback_delay,
+ handler->adv_proto_id_len);
+ if (!resp) {
+ dl_list_del(&response->list);
+ gas_server_free_response(response);
+ return;
+ }
+
+ /* Advertisement Protocol element */
+ wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */
+ wpabuf_put_u8(resp, 0x7f);
+ /* Advertisement Protocol ID */
+ wpabuf_put_data(resp, handler->adv_proto_id,
+ handler->adv_proto_id_len);
+
+ /* Query Response Length */
+ wpabuf_put_le16(resp, 0);
+ goto send_resp;
+ }
remaining = wpabuf_len(response->resp) - response->offset;
if (hdr_len + remaining > max_len)
@@ -279,8 +322,11 @@
response->offset += resp_frag_len;
- gas->tx(gas->ctx, response->freq, response->dst, resp,
- remaining > resp_frag_len ? 2000 : 0);
+ if (remaining > resp_frag_len)
+ wait_time = 2000;
+
+send_resp:
+ gas->tx(gas->ctx, response->freq, response->dst, resp, wait_time);
wpabuf_free(resp);
}
@@ -359,12 +405,19 @@
static void gas_server_handle_tx_status(struct gas_server_response *response,
int ack)
{
- if (ack && response->offset < wpabuf_len(response->resp)) {
+ if (ack && response->resp &&
+ response->offset < wpabuf_len(response->resp)) {
wpa_printf(MSG_DEBUG,
"GAS: More fragments remaining - keep pending entry");
return;
}
+ if (ack && !response->resp && response->comeback_delay) {
+ wpa_printf(MSG_DEBUG,
+ "GAS: Waiting for response - keep pending entry");
+ return;
+ }
+
if (!ack)
wpa_printf(MSG_DEBUG,
"GAS: No ACK received - drop pending entry");
@@ -415,6 +468,27 @@
}
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+ struct wpabuf *resp)
+{
+ struct gas_server_response *tmp, *response = NULL;
+
+ dl_list_for_each(tmp, &gas->responses, struct gas_server_response,
+ list) {
+ if (tmp == resp_ctx) {
+ response = tmp;
+ break;
+ }
+ }
+
+ if (!response || response->resp)
+ return -1;
+
+ response->resp = resp;
+ return 0;
+}
+
+
struct gas_server * gas_server_init(void *ctx,
void (*tx)(void *ctx, int freq,
const u8 *da,
@@ -461,8 +535,9 @@
int gas_server_register(struct gas_server *gas,
const u8 *adv_proto_id, u8 adv_proto_id_len,
struct wpabuf *
- (*req_cb)(void *ctx, const u8 *sa,
- const u8 *query, size_t query_len),
+ (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+ const u8 *query, size_t query_len,
+ u16 *comeback_delay),
void (*status_cb)(void *ctx, struct wpabuf *resp,
int ok),
void *ctx)
diff --git a/src/common/gas_server.h b/src/common/gas_server.h
index 299f529..2611dde 100644
--- a/src/common/gas_server.h
+++ b/src/common/gas_server.h
@@ -1,6 +1,7 @@
/*
* Generic advertisement service (GAS) server
* Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -22,8 +23,9 @@
int gas_server_register(struct gas_server *gas,
const u8 *adv_proto_id, u8 adv_proto_id_len,
struct wpabuf *
- (*req_cb)(void *ctx, const u8 *sa,
- const u8 *query, size_t query_len),
+ (*req_cb)(void *ctx, void *resp_ctx, const u8 *sa,
+ const u8 *query, size_t query_len,
+ u16 *comeback_delay),
void (*status_cb)(void *ctx, struct wpabuf *resp,
int ok),
void *ctx);
@@ -32,6 +34,8 @@
int freq);
void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data,
size_t data_len, int ack);
+int gas_server_set_resp(struct gas_server *gas, void *resp_ctx,
+ struct wpabuf *resp);
#else /* CONFIG_GAS_SERVER */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index f6c67a3..511e68f 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -415,7 +415,7 @@
return -1;
}
- if (center_idx_to_bw_6ghz(channel) != 0) {
+ if (center_idx_to_bw_6ghz(channel) < 0) {
wpa_printf(MSG_ERROR,
"Invalid control channel for 6 GHz band");
return -1;
@@ -540,13 +540,20 @@
if (center_segment1 ||
(center_segment0 != 0 &&
5000 + center_segment0 * 5 != data->center_freq1 &&
- 2407 + center_segment0 * 5 != data->center_freq1))
+ 2407 + center_segment0 * 5 != data->center_freq1)) {
+ wpa_printf(MSG_ERROR,
+ "20/40 MHz: center segment 0 (=%d) and center freq 1 (=%d) not in sync",
+ center_segment0, data->center_freq1);
return -1;
+ }
break;
case CHANWIDTH_80P80MHZ:
if (center_segment1 == center_segment0 + 4 ||
- center_segment1 == center_segment0 - 4)
+ center_segment1 == center_segment0 - 4) {
+ wpa_printf(MSG_ERROR,
+ "80+80 MHz: center segment 1 only 20 MHz apart");
return -1;
+ }
data->center_freq2 = 5000 + center_segment1 * 5;
/* fall through */
case CHANWIDTH_80MHZ:
@@ -555,8 +562,11 @@
center_segment1) ||
(oper_chwidth == CHANWIDTH_80P80MHZ &&
!center_segment1) ||
- !sec_channel_offset)
+ !sec_channel_offset) {
+ wpa_printf(MSG_ERROR,
+ "80/80+80 MHz: center segment 1 wrong or no second channel offset");
return -1;
+ }
if (!center_segment0) {
if (channel <= 48)
center_segment0 = 42;
@@ -582,16 +592,25 @@
center_segment0 == channel - 2 ||
center_segment0 == channel - 6)
data->center_freq1 = 5000 + center_segment0 * 5;
- else
+ else {
+ wpa_printf(MSG_ERROR,
+ "Wrong coupling between HT and VHT/HE channel setting");
return -1;
+ }
}
break;
case CHANWIDTH_160MHZ:
data->bandwidth = 160;
- if (center_segment1)
+ if (center_segment1) {
+ wpa_printf(MSG_ERROR,
+ "160 MHz: center segment 1 should not be set");
return -1;
- if (!sec_channel_offset)
+ }
+ if (!sec_channel_offset) {
+ wpa_printf(MSG_ERROR,
+ "160 MHz: second channel offset not set");
return -1;
+ }
/*
* Note: HT/VHT config and params are coupled. Check if
* HT40 channel band is in VHT160 channel band configuration.
@@ -605,8 +624,11 @@
center_segment0 == channel - 10 ||
center_segment0 == channel - 14)
data->center_freq1 = 5000 + center_segment0 * 5;
- else
+ else {
+ wpa_printf(MSG_ERROR,
+ "160 MHz: HT40 channel band is not in 160 MHz band");
return -1;
+ }
break;
}
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 71c54ba..72d7dda 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -136,6 +136,10 @@
case DPP_CC_OUI_TYPE:
/* DPP Configurator Connectivity element */
break;
+ case SAE_PK_OUI_TYPE:
+ elems->sae_pk = pos + 4;
+ elems->sae_pk_len = elen - 4;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -298,6 +302,11 @@
elems->short_ssid_list = pos;
elems->short_ssid_list_len = elen;
break;
+ case WLAN_EID_EXT_HE_6GHZ_BAND_CAP:
+ if (elen < sizeof(struct ieee80211_he_6ghz_band_cap))
+ break;
+ elems->he_6ghz_band_cap = pos;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -764,6 +773,98 @@
}
+/* convert floats with one decimal place to value*10 int, i.e.,
+ * "1.5" will return 15
+ */
+static int hostapd_config_read_int10(const char *value)
+{
+ int i, d;
+ char *pos;
+
+ i = atoi(value);
+ pos = os_strchr(value, '.');
+ d = 0;
+ if (pos) {
+ pos++;
+ if (*pos >= '0' && *pos <= '9')
+ d = *pos - '0';
+ }
+
+ return i * 10 + d;
+}
+
+
+static int valid_cw(int cw)
+{
+ return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
+ cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
+ cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
+ cw == 32767);
+}
+
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params tx_queue[],
+ const char *name, const char *val)
+{
+ int num;
+ const char *pos;
+ struct hostapd_tx_queue_params *queue;
+
+ /* skip 'tx_queue_' prefix */
+ pos = name + 9;
+ if (os_strncmp(pos, "data", 4) == 0 &&
+ pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
+ num = pos[4] - '0';
+ pos += 6;
+ } else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
+ os_strncmp(pos, "beacon_", 7) == 0) {
+ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+ return 0;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
+ return -1;
+ }
+
+ if (num >= NUM_TX_QUEUES) {
+ /* for backwards compatibility, do not trigger failure */
+ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+ return 0;
+ }
+
+ queue = &tx_queue[num];
+
+ if (os_strcmp(pos, "aifs") == 0) {
+ queue->aifs = atoi(val);
+ if (queue->aifs < 0 || queue->aifs > 255) {
+ wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
+ queue->aifs);
+ return -1;
+ }
+ } else if (os_strcmp(pos, "cwmin") == 0) {
+ queue->cwmin = atoi(val);
+ if (!valid_cw(queue->cwmin)) {
+ wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
+ queue->cwmin);
+ return -1;
+ }
+ } else if (os_strcmp(pos, "cwmax") == 0) {
+ queue->cwmax = atoi(val);
+ if (!valid_cw(queue->cwmax)) {
+ wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
+ queue->cwmax);
+ return -1;
+ }
+ } else if (os_strcmp(pos, "burst") == 0) {
+ queue->burst = hostapd_config_read_int10(val);
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown queue field '%s'", pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
{
u8 op_class;
@@ -934,9 +1035,9 @@
return HOSTAPD_MODE_IEEE80211A;
}
- if (freq > 5940 && freq <= 7105) {
+ if (freq > 5950 && freq <= 7115) {
int bw;
- u8 idx = (freq - 5940) / 5;
+ u8 idx = (freq - 5950) / 5;
bw = center_idx_to_bw_6ghz(idx);
if (bw < 0)
@@ -947,6 +1048,12 @@
return HOSTAPD_MODE_IEEE80211A;
}
+ if (freq == 5935) {
+ *op_class = 136;
+ *channel = (freq - 5925) / 5;
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
/* 56.16 GHz, channel 1..6 */
if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
if (sec_channel)
@@ -1323,7 +1430,11 @@
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
if (chan < 1 || chan > 233)
return -1;
- return 5940 + chan * 5;
+ return 5950 + chan * 5;
+ case 136: /* UHB channels, 20 MHz: 2 */
+ if (chan == 2)
+ return 5935;
+ return -1;
case 180: /* 60 GHz band, channels 1..8 */
if (chan < 1 || chan > 8)
return -1;
@@ -1406,7 +1517,8 @@
static int is_11b(u8 rate)
{
- return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+ return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16
+ || rate == 0x82 || rate == 0x84 || rate == 0x8b || rate == 0x96;
}
@@ -1667,7 +1779,9 @@
S2S(FILS_AUTHENTICATION_FAILURE)
S2S(UNKNOWN_AUTHENTICATION_SERVER)
S2S(UNKNOWN_PASSWORD_IDENTIFIER)
+ S2S(DENIED_HE_NOT_SUPPORTED)
S2S(SAE_HASH_TO_ELEMENT)
+ S2S(SAE_PK)
}
return "UNKNOWN";
#undef S2S
@@ -1765,7 +1879,6 @@
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
- { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
/*
@@ -1777,6 +1890,12 @@
{ HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
+
+ /* Keep the operating class 130 as the last entry as a workaround for
+ * the OneHundredAndThirty Delimiter value used in the Supported
+ * Operating Classes element to indicate the end of the Operating
+ * Classes field. */
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
};
@@ -2111,10 +2230,13 @@
int is_6ghz_freq(int freq)
{
- if (freq < 5940 || freq > 7105)
+ if (freq < 5935 || freq > 7115)
return 0;
- if (center_idx_to_bw_6ghz((freq - 5940) / 5) < 0)
+ if (freq == 5935)
+ return 1;
+
+ if (center_idx_to_bw_6ghz((freq - 5950) / 5) < 0)
return 0;
return 1;
@@ -2123,7 +2245,7 @@
int is_6ghz_op_class(u8 op_class)
{
- return op_class >= 131 && op_class <= 135;
+ return op_class >= 131 && op_class <= 136;
}
@@ -2131,14 +2253,14 @@
{
int i;
- if (!is_6ghz_freq(freq))
+ if (!is_6ghz_freq(freq) || freq == 5935)
return 0;
- if ((((freq - 5940) / 5) & 0x3) != 0x1)
+ if ((((freq - 5950) / 5) & 0x3) != 0x1)
return 0;
- i = (freq - 5940 + 55) % 80;
+ i = (freq - 5950 + 55) % 80;
if (i == 0)
- i = (freq - 5940 + 55) / 80;
+ i = (freq - 5950 + 55) / 80;
if (i >= 1 && i <= 15)
return 1;
@@ -2374,6 +2496,8 @@
case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
return 160;
+ case 136: /* UHB channels, 20 MHz: 2 */
+ return 20;
case 180: /* 60 GHz band, channels 1..8 */
return 2160;
case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
@@ -2434,6 +2558,8 @@
return CHANWIDTH_160MHZ;
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
return CHANWIDTH_80P80MHZ;
+ case 136: /* UHB channels, 20 MHz: 2 */
+ return CHANWIDTH_USE_HT;
case 180: /* 60 GHz band, channels 1..8 */
return CHANWIDTH_2160MHZ;
case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index e395769..98a55ec 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -113,6 +113,8 @@
const u8 *he_capabilities;
const u8 *he_operation;
const u8 *short_ssid_list;
+ const u8 *he_6ghz_band_cap;
+ const u8 *sae_pk;
u8 ssid_len;
u8 supp_rates_len;
@@ -165,6 +167,7 @@
u8 he_capabilities_len;
u8 he_operation_len;
u8 short_ssid_list_len;
+ u8 sae_pk_len;
struct mb_ies_info mb_ies;
struct frag_ies_info frag_ies;
@@ -191,6 +194,18 @@
int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
const char *name, const char *val);
+
+struct hostapd_tx_queue_params {
+ int aifs;
+ int cwmin;
+ int cwmax;
+ int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
+};
+
+#define NUM_TX_QUEUES 4
+
+int hostapd_config_tx_queue(struct hostapd_tx_queue_params queue[],
+ const char *name, const char *val);
enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 4a5eb16..de41d7f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -204,7 +204,9 @@
#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
+#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
+#define WLAN_STATUS_SAE_PK 127
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -473,8 +475,11 @@
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_OCV_OCI 54
#define WLAN_EID_EXT_SHORT_SSID_LIST 58
+#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
#define WLAN_EID_EXT_EDMG_OPERATION 62
+#define WLAN_EID_EXT_MSCS_DESCRIPTOR 88
+#define WLAN_EID_EXT_TCLAS_MASK 89
#define WLAN_EID_EXT_REJECTED_GROUPS 92
#define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
@@ -559,11 +564,14 @@
#define WLAN_EXT_CAPAB_SAE_PW_ID 81
#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
#define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
+#define WLAN_EXT_CAPAB_MSCS 85
+#define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
/* Extended RSN Capabilities */
/* bits 0-3: Field length (n-1) */
#define WLAN_RSNX_CAPAB_PROTECTED_TWT 4
#define WLAN_RSNX_CAPAB_SAE_H2E 5
+#define WLAN_RSNX_CAPAB_SAE_PK 6
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -1324,6 +1332,8 @@
#define MULTI_AP_OUI_TYPE 0x1B
#define DPP_CC_IE_VENDOR_TYPE 0x506f9a1e
#define DPP_CC_OUI_TYPE 0x1e
+#define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f
+#define SAE_PK_OUI_TYPE 0x1f
#define MULTI_AP_SUB_ELEM_TYPE 0x06
#define MULTI_AP_TEAR_DOWN BIT(4)
@@ -2155,12 +2165,53 @@
le32 he_oper_params; /* HE Operation Parameters[3] and
* BSS Color Information[1] */
le16 he_mcs_nss_set;
- u8 vht_op_info_chwidth;
- u8 vht_op_info_chan_center_freq_seg0_idx;
- u8 vht_op_info_chan_center_freq_seg1_idx;
- /* Followed by conditional MaxBSSID Indicator subfield (u8) */
+ /* Followed by conditional VHT Operation Information (3 octets),
+ * Max Co-Hosted BSSID Indicator subfield (1 octet), and/or 6 GHz
+ * Operation Information subfield (5 octets). */
} STRUCT_PACKED;
+/* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */
+struct ieee80211_he_6ghz_oper_info {
+ u8 primary_chan;
+ u8 control;
+ u8 chan_center_freq_seg0;
+ u8 chan_center_freq_seg1;
+ u8 min_rate;
+} STRUCT_PACKED;
+
+#define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK (BIT(0) | BIT(1))
+#define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON BIT(2)
+
+/* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */
+struct ieee80211_he_6ghz_band_cap {
+ /* Minimum MPDU Start Spacing B0..B2
+ * Maximum A-MPDU Length Exponent B3..B5
+ * Maximum MPDU Length B6..B7 */
+ le16 capab;
+} STRUCT_PACKED;
+
+#define HE_6GHZ_BAND_CAP_MIN_MPDU_START (BIT(0) | BIT(1) | BIT(2))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_16K BIT(3)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_32K BIT(4)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_64K (BIT(3) | BIT(4))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_128K BIT(5)
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_256K (BIT(3) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_512K (BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_1024K (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK (BIT(3) | BIT(4) | BIT(5))
+#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT 3
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_7991 BIT(6)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_11454 BIT(7)
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK (BIT(6) | BIT(7))
+#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT 6
+#define HE_6GHZ_BAND_CAP_SMPS_MASK (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_SMPS_STATIC 0
+#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC BIT(9)
+#define HE_6GHZ_BAND_CAP_SMPS_DISABLED (BIT(9) | BIT(10))
+#define HE_6GHZ_BAND_CAP_RD_RESPONDER BIT(11)
+#define HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS BIT(12)
+#define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS BIT(13)
+
/*
* IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
*/
@@ -2320,4 +2371,25 @@
/* DPP Public Action frame identifiers - OUI_WFA */
#define DPP_OUI_TYPE 0x1A
+/* Robust AV streaming Action field values */
+enum robust_av_streaming_action {
+ ROBUST_AV_SCS_REQ = 0,
+ ROBUST_AV_SCS_RESP = 1,
+ ROBUST_AV_GROUP_MEMBERSHIP_REQ = 2,
+ ROBUST_AV_GROUP_MEMBERSHIP_RESP = 3,
+ ROBUST_AV_MSCS_REQ = 4,
+ ROBUST_AV_MSCS_RESP = 5,
+};
+
+enum scs_request_type {
+ SCS_REQ_ADD = 0,
+ SCS_REQ_REMOVE = 1,
+ SCS_REQ_CHANGE = 2,
+};
+
+/* Optional subelement IDs for MSCS Descriptor element */
+enum mscs_description_subelem {
+ MCSC_SUBELEM_STATUS = 1,
+};
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ocv.c b/src/common/ocv.c
index 06badfb..4bc2749 100644
--- a/src/common/ocv.c
+++ b/src/common/ocv.c
@@ -95,23 +95,24 @@
}
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
- struct wpa_channel_info *ci, int tx_chanwidth,
- int tx_seg1_idx)
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+ struct wpa_channel_info *ci, int tx_chanwidth,
+ int tx_seg1_idx)
{
struct oci_info oci;
if (!oci_ie) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: did not receive mandatory OCI");
- return -1;
+ "did not receive mandatory OCI");
+ return OCI_NOT_FOUND;
}
if (oci_ie_len != 3) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: received OCI of unexpected length (%d)",
+ "received OCI of unexpected length (%d)",
(int) oci_ie_len);
- return -1;
+ return OCI_INVALID_LENGTH;
}
os_memset(&oci, 0, sizeof(oci));
@@ -120,25 +121,25 @@
oci.seg1_idx = oci_ie[2];
if (ocv_derive_all_parameters(&oci) != 0) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: unable to interpret received OCI");
- return -1;
+ "unable to interpret received OCI");
+ return OCI_PARSE_ERROR;
}
/* Primary frequency used to send frames to STA must match the STA's */
if ((int) ci->frequency != oci.freq) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)",
+ "primary channel mismatch in received OCI (we use %d but receiver is using %d)",
ci->frequency, oci.freq);
- return -1;
+ return OCI_PRIMARY_FREQ_MISMATCH;
}
/* We shouldn't transmit with a higher bandwidth than the STA supports
*/
if (tx_chanwidth > oci.chanwidth) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
+ "channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
tx_chanwidth, oci.chanwidth);
- return -1;
+ return OCI_CHANNEL_WIDTH_MISMATCH;
}
/*
@@ -150,9 +151,9 @@
if (tx_chanwidth == 40 && ci->frequency < 2500 &&
ci->sec_channel != oci.sec_channel) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
+ "secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
ci->sec_channel, oci.sec_channel);
- return -1;
+ return OCI_SECONDARY_FREQ_MISMATCH;
}
/*
@@ -163,10 +164,10 @@
ci->chanwidth == CHAN_WIDTH_80P80) &&
tx_seg1_idx != oci.seg1_idx) {
os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
- "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
+ "frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
tx_seg1_idx, oci.seg1_idx);
- return -1;
+ return OCI_SEG_1_INDEX_MISMATCH;
}
- return 0;
+ return OCI_SUCCESS;
}
diff --git a/src/common/ocv.h b/src/common/ocv.h
index 6379d9d..7fa4522 100644
--- a/src/common/ocv.h
+++ b/src/common/ocv.h
@@ -27,14 +27,21 @@
#define OCV_OCI_EXTENDED_LEN (3 + OCV_OCI_LEN)
#define OCV_OCI_KDE_LEN (2 + RSN_SELECTOR_LEN + OCV_OCI_LEN)
+enum oci_verify_result {
+ OCI_SUCCESS, OCI_NOT_FOUND, OCI_INVALID_LENGTH, OCI_PARSE_ERROR,
+ OCI_PRIMARY_FREQ_MISMATCH, OCI_CHANNEL_WIDTH_MISMATCH,
+ OCI_SECONDARY_FREQ_MISMATCH, OCI_SEG_1_INDEX_MISMATCH
+};
+
extern char ocv_errorstr[256];
int ocv_derive_all_parameters(struct oci_info *oci);
int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos);
int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos);
int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos);
-int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
- struct wpa_channel_info *ci, int tx_chanwidth,
- int tx_seg1_idx);
+enum oci_verify_result
+ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+ struct wpa_channel_info *ci, int tx_chanwidth,
+ int tx_seg1_idx);
#endif /* OCV_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index e599b8d..eb6a2c1 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,7 +1,7 @@
/*
* Qualcomm Atheros OUI and vendor specific assignments
* Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
- * Copyright (c) 2018-2019, The Linux Foundation
+ * Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -174,6 +174,22 @@
* to notify the connected station's status. The attributes for this
* command are defined in enum qca_wlan_vendor_attr_link_properties.
*
+ * @QCA_NL80211_VENDOR_SUBCMD_SETBAND: Command to configure the enabled band(s)
+ * to the driver. This command sets the band(s) through either the
+ * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE or
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK (or both).
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE refers enum qca_set_band as unsigned
+ * integer values and QCA_WLAN_VENDOR_ATTR_SETBAND_MASK refers it as 32
+ * bit unsigned bitmask values. The allowed values for
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE are limited to QCA_SETBAND_AUTO,
+ * QCA_SETBAND_5G, and QCA_SETBAND_2G. Other values/bitmasks are valid for
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. The attribute
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is deprecated and the recommendation
+ * is to use the QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. If the both attributes
+ * are included for backwards compatibility, the configurations through
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK will take the precedence with drivers
+ * that support both attributes.
+ *
* @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure
* DFS policy and channel hint for ACS operation. This command uses the
* attributes defined in enum qca_wlan_vendor_attr_acs_config and
@@ -646,6 +662,44 @@
* code immediately prior to triggering cfg80211_disconnected(). The
* attributes used with this event are defined in enum
* qca_wlan_vendor_attr_driver_disconnect_reason.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC: This vendor subcommand is used to
+ * add/delete TSPEC for each AC. One command is for one specific AC only.
+ * This command can only be used in STA mode and the STA must be
+ * associated with an AP when the command is issued. Uses attributes
+ * defined in enum qca_wlan_vendor_attr_config_tspec.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT: Vendor subcommand to configure TWT.
+ * Uses attributes defined in enum qca_wlan_vendor_attr_config_twt.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GETBAND: Command to get the enabled band(s) from
+ * the driver. The band configurations obtained are referred through
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_MASK.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS: Vendor subcommand/event for medium
+ * assessment.
+ * Uses attributes defined in enum qca_wlan_vendor_attr_medium_assess.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID: This acts as a vendor event and is
+ * used to update SSID information in hostapd when it is updated in the
+ * driver. Uses the attribute NL80211_ATTR_SSID.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS: This vendor subcommand is used by
+ * the driver to send opaque data from the firmware to userspace. The
+ * driver sends an event to userspace whenever such data is received from
+ * the firmware.
+ *
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA is used as the attribute to
+ * send this opaque data for this event.
+ *
+ * The format of the opaque data is specific to the particular firmware
+ * version and there is no guarantee of the format remaining same.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS: This acts as an event.
+ * The host driver selects Tx VDEV, and notifies user. The attributes
+ * used with this event are defined in enum
+ * qca_wlan_vendor_attr_mbssid_tx_vdev_status.
+ *
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -825,6 +879,13 @@
QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT = 187,
QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO = 188,
QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON = 189,
+ QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC = 190,
+ QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT = 191,
+ QCA_NL80211_VENDOR_SUBCMD_GETBAND = 192,
+ QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS = 193,
+ QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194,
+ QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195,
+ QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196,
};
enum qca_wlan_vendor_attr {
@@ -855,7 +916,11 @@
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
- /* Unsigned 32-bit value from enum qca_set_band. */
+ /* Unsigned 32-bit value from enum qca_set_band. The allowed values for
+ * this attribute are limited to QCA_SETBAND_AUTO, QCA_SETBAND_5G, and
+ * QCA_SETBAND_2G. This attribute is deprecated. Recommendation is to
+ * use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead.
+ */
QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
/* Dummy (NOP) attribute for 64 bit padding */
QCA_WLAN_VENDOR_ATTR_PAD = 13,
@@ -1018,6 +1083,15 @@
*/
QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
+ /* Unsigned 32-bitmask value from enum qca_set_band. Substitutes the
+ * attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE for which only a subset
+ * of single values from enum qca_set_band are valid. This attribute
+ * uses bitmask combinations to define the respective allowed band
+ * combinations and this attributes takes precedence over
+ * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE if both attributes are included.
+ */
+ QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -1565,9 +1639,10 @@
};
enum qca_set_band {
- QCA_SETBAND_AUTO,
- QCA_SETBAND_5G,
- QCA_SETBAND_2G,
+ QCA_SETBAND_AUTO = 0,
+ QCA_SETBAND_5G = BIT(0),
+ QCA_SETBAND_2G = BIT(1),
+ QCA_SETBAND_6G = BIT(2),
};
/**
@@ -1695,6 +1770,8 @@
* (not including the Element ID Extension field). Please note that the
* draft is still work in progress and this element payload is subject to
* change.
+ *
+ * @QCA_VENDOR_ELEM_ALLPLAY: Allplay element
*/
enum qca_vendor_element_id {
QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
@@ -1703,6 +1780,7 @@
QCA_VENDOR_ELEM_RAPS = 3,
QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
+ QCA_VENDOR_ELEM_ALLPLAY = 6,
};
/**
@@ -2120,6 +2198,141 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON = 61,
+ /* 32-bit unsigned value to configure different PHY modes to the
+ * driver/firmware. The possible values are defined in
+ * enum qca_wlan_vendor_phy_mode. The configuration will be reset to
+ * default value, i.e., QCA_WLAN_VENDOR_PHY_MODE_AUTO upon restarting
+ * the driver.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE = 62,
+
+ /* 8-bit unsigned value to configure the maximum supported channel width
+ * for STA mode. If this value is configured when STA is in connected
+ * state, it should not exceed the negotiated channel width. If it is
+ * configured when STA is in disconnected state, the configured value
+ * will take effect for the next immediate connection.
+ * Possible values are:
+ * NL80211_CHAN_WIDTH_20
+ * NL80211_CHAN_WIDTH_40
+ * NL80211_CHAN_WIDTH_80
+ * NL80211_CHAN_WIDTH_80P80
+ * NL80211_CHAN_WIDTH_160
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63,
+
+ /* 8-bit unsigned value to enable/disable dynamic bandwidth adjustment.
+ * This attribute is only applicable for STA mode. When dynamic
+ * bandwidth adjustment is disabled, STA will use static channel width
+ * the value of which is negotiated during connection.
+ * 1-enable (default), 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW = 64,
+
+ /* 8-bit unsigned value to configure the maximum number of subframes of
+ * TX MSDU for aggregation. Possible values are 0-31. When set to 0,
+ * it is decided by the hardware.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION = 65,
+
+ /* 8-bit unsigned value to configure the maximum number of subframes of
+ * RX MSDU for aggregation. Possible values are 0-31. When set to 0,
+ * it is decided by the hardware.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION = 66,
+
+ /* 8-bit unsigned value. This attribute is used to dynamically
+ * enable/disable the LDPC capability of the device. When configured in
+ * the disconnected state, the updated configuration will be considered
+ * for the immediately following connection attempt. If this
+ * configuration is modified while the device is in the connected state,
+ * the LDPC TX will be updated with this configuration immediately,
+ * while the LDPC RX configuration update will take place starting from
+ * the subsequent association attempt.
+ * 1-Enable, 0-Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC = 67,
+
+ /* 8-bit unsigned value. This attribute is used to dynamically
+ * enable/disable the TX STBC capability of the device. When configured
+ * in the disconnected state, the updated configuration will be
+ * considered for the immediately following connection attempt. If the
+ * connection is formed with TX STBC enabled and if this configuration
+ * is disabled during that association, the TX will be impacted
+ * immediately. Further connection attempts will disable TX STBC.
+ * However, enabling the TX STBC for a connected session with disabled
+ * capability is not allowed and will fail.
+ * 1-Enable, 0-Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC = 68,
+
+ /* 8-bit unsigned value. This attribute is used to dynamically
+ * enable/disable the RX STBC capability of the device. When configured
+ * in the disconnected state, the updated configuration will be
+ * considered for the immediately following connection attempt. If the
+ * configuration is modified in the connected state, there will be no
+ * impact for the current association, but further connection attempts
+ * will use the updated configuration.
+ * 1-Enable, 0-Disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
+
+ /* 8-bit unsigned value. This attribute is used to dynamically configure
+ * the number of spatial streams. When configured in the disconnected
+ * state, the updated configuration will be considered for the
+ * immediately following connection attempt. If the NSS is updated after
+ * the connection, the updated NSS value is notified to the peer using
+ * the Operating Mode Notification/Spatial Multiplexing Power Save
+ * frame. The updated NSS value after the connection shall not be
+ * greater than the one negotiated during the connection. Any such
+ * higher value configuration shall be returned with a failure.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
+ /* 8-bit unsigned value to trigger Optimized Power Management:
+ * 1-Enable, 0-Disable
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
+
+ /* 8-bit unsigned value. This attribute takes the QoS/access category
+ * value represented by the enum qca_wlan_ac_type and expects the driver
+ * to upgrade the UDP frames to this access category. The value of
+ * QCA_WLAN_AC_ALL is invalid for this attribute. This will override the
+ * DSCP value configured in the frame with the intention to only upgrade
+ * the access category. That said, it is not intended to downgrade the
+ * access category for the frames.
+ * Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
+ * disabled, as BK is of the lowest priority and an upgrade to it does
+ * not result in any changes for the frames.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
+
+ /* 8-bit unsigned value. This attribute is used to dynamically configure
+ * the number of chains to be used for transmitting data. This
+ * configuration is allowed only when in connected state and will be
+ * effective until disconnected. The driver rejects this configuration
+ * if the number of spatial streams being used in the current connection
+ * cannot be supported by this configuration.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
+ /* 8-bit unsigned value. This attribute is used to dynamically configure
+ * the number of chains to be used for receiving data. This
+ * configuration is allowed only when in connected state and will be
+ * effective until disconnected. The driver rejects this configuration
+ * if the number of spatial streams being used in the current connection
+ * cannot be supported by this configuration.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
+
+ /* 8-bit unsigned value to configure ANI setting type.
+ * See &enum qca_wlan_ani_setting for possible values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING = 75,
+ /* 32-bit signed value to configure ANI level. This is used when
+ * ANI settings type is &QCA_WLAN_ANI_SETTING_FIXED.
+ * The set and get of ANI level with &QCA_WLAN_ANI_SETTING_AUTO
+ * is invalid, the driver will return a failure.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -2135,6 +2348,16 @@
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
/**
+ * enum qca_wlan_ani_setting - ANI setting type
+ * @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level
+ * @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter
+ */
+enum qca_wlan_ani_setting {
+ QCA_WLAN_ANI_SETTING_AUTO = 0,
+ QCA_WLAN_ANI_SETTING_FIXED = 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
*
* @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8)
@@ -2189,19 +2412,54 @@
/**
* enum qca_wlan_gpio_attr - Parameters for GPIO configuration
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND: Required (u32)
+ * value to specify the GPIO command. Please refer to enum qca_gpio_cmd_type
+ * for the available values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM: Required (u32)
+ * value to specify the GPIO number.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG or %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE: Required (u32)
+ * value to specify the GPIO output level. Please refer to enum qca_gpio_value
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_OUTPUT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE: Required (u32)
+ * value to specify the GPIO pull type. Please refer to enum qca_gpio_pull_type
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE: Required (u32)
+ * value to specify the GPIO interrupt mode. Please refer to enum
+ * qca_gpio_interrupt_mode for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR: Required (u32)
+ * value to specify the GPIO direction. Please refer to enum qca_gpio_direction
+ * for the available values.
+ * This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
+ * %QCA_WLAN_VENDOR_GPIO_CONFIG.
*/
enum qca_wlan_gpio_attr {
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
/* Unsigned 32-bit attribute for GPIO command */
- QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND,
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND = 1,
/* Unsigned 32-bit attribute for GPIO PIN number to configure */
- QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM,
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM = 2,
/* Unsigned 32-bit attribute for GPIO value to configure */
- QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE,
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE = 3,
/* Unsigned 32-bit attribute for GPIO pull type */
- QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE,
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE = 4,
/* Unsigned 32-bit attribute for GPIO interrupt mode */
- QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE,
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE = 5,
+ /* Unsigned 32-bit attribute for GPIO direction to configure */
+ QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR = 6,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
@@ -2210,6 +2468,73 @@
};
/**
+ * enum gpio_cmd_type - GPIO configuration command type
+ * @QCA_WLAN_VENDOR_GPIO_CONFIG: Set GPIO configuration info
+ * @QCA_WLAN_VENDOR_GPIO_OUTPUT: Set GPIO output level
+ */
+enum qca_gpio_cmd_type {
+ QCA_WLAN_VENDOR_GPIO_CONFIG = 0,
+ QCA_WLAN_VENDOR_GPIO_OUTPUT = 1,
+};
+
+/**
+ * enum qca_gpio_pull_type - GPIO pull type
+ * @QCA_WLAN_GPIO_PULL_NONE: Set GPIO pull type to none
+ * @QCA_WLAN_GPIO_PULL_UP: Set GPIO pull up
+ * @QCA_WLAN_GPIO_PULL_DOWN: Set GPIO pull down
+ */
+enum qca_gpio_pull_type {
+ QCA_WLAN_GPIO_PULL_NONE = 0,
+ QCA_WLAN_GPIO_PULL_UP = 1,
+ QCA_WLAN_GPIO_PULL_DOWN = 2,
+ QCA_WLAN_GPIO_PULL_MAX,
+};
+
+/**
+ * enum qca_gpio_direction - GPIO direction
+ * @QCA_WLAN_GPIO_INPUT: Set GPIO as input mode
+ * @QCA_WLAN_GPIO_OUTPUT: Set GPIO as output mode
+ * @QCA_WLAN_GPIO_VALUE_MAX: Invalid value
+ */
+enum qca_gpio_direction {
+ QCA_WLAN_GPIO_INPUT = 0,
+ QCA_WLAN_GPIO_OUTPUT = 1,
+ QCA_WLAN_GPIO_DIR_MAX,
+};
+
+/**
+ * enum qca_gpio_value - GPIO Value
+ * @QCA_WLAN_GPIO_LEVEL_LOW: set gpio output level to low
+ * @QCA_WLAN_GPIO_LEVEL_HIGH: set gpio output level to high
+ * @QCA_WLAN_GPIO_LEVEL_MAX: Invalid value
+ */
+enum qca_gpio_value {
+ QCA_WLAN_GPIO_LEVEL_LOW = 0,
+ QCA_WLAN_GPIO_LEVEL_HIGH = 1,
+ QCA_WLAN_GPIO_LEVEL_MAX,
+};
+
+/**
+ * enum gpio_interrupt_mode - GPIO interrupt mode
+ * @QCA_WLAN_GPIO_INTMODE_DISABLE: Disable interrupt trigger
+ * @QCA_WLAN_GPIO_INTMODE_RISING_EDGE: Interrupt with GPIO rising edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_FALLING_EDGE: Interrupt with GPIO falling edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_BOTH_EDGE: Interrupt with GPIO both edge trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_LOW: Interrupt with GPIO level low trigger
+ * @QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH: Interrupt with GPIO level high trigger
+ * @QCA_WLAN_GPIO_INTMODE_MAX: Invalid value
+ */
+enum qca_gpio_interrupt_mode {
+ QCA_WLAN_GPIO_INTMODE_DISABLE = 0,
+ QCA_WLAN_GPIO_INTMODE_RISING_EDGE = 1,
+ QCA_WLAN_GPIO_INTMODE_FALLING_EDGE = 2,
+ QCA_WLAN_GPIO_INTMODE_BOTH_EDGE = 3,
+ QCA_WLAN_GPIO_INTMODE_LEVEL_LOW = 4,
+ QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH = 5,
+ QCA_WLAN_GPIO_INTMODE_MAX,
+};
+
+/**
* qca_wlan_set_qdepth_thresh_attr - Parameters for setting
* MSDUQ depth threshold per peer per tid in the target
*
@@ -3297,7 +3622,7 @@
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
/* Signifies the nested list of channel attributes
- * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_*
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
@@ -3359,6 +3684,17 @@
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
+ /* u32 value representing total time in milliseconds for which the radio
+ * is transmitting on this channel. This attribute will be nested
+ * within QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_TX_TIME = 84,
+ /* u32 value representing total time in milliseconds for which the radio
+ * is receiving all 802.11 frames intended for this device on this
+ * channel. This attribute will be nested within
+ * QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_RX_TIME = 85,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -3706,6 +4042,30 @@
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1,
};
+/**
+ * enum qca_roam_scan_scheme: Scan scheme
+ *
+ * @QCA_ROAM_SCAN_SCHEME_NO_SCAN: No frequencies specified to scan.
+ * Indicates the driver to not scan on a Roam Trigger scenario, but
+ * disconnect. E.g., on a BTM request from the AP the driver/firmware shall
+ * disconnect from the current connected AP by notifying a failure
+ * code in the BTM response.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN: Indicates the driver/firmware to
+ * trigger partial frequency scans. These frequencies are the ones learned
+ * or maintained by the driver based on the probability of finding the
+ * BSSIDs in the ESS for which the roaming is triggered.
+ *
+ * @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to
+ * trigger the scan on all the valid frequencies to find better
+ * candidates to roam.
+ */
+enum qca_roam_scan_scheme {
+ QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0,
+ QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1,
+ QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2,
+};
+
/*
* enum qca_vendor_roam_triggers: Bitmap of roaming triggers
*
@@ -3726,6 +4086,18 @@
* when BTM Request frame is received from the connected AP.
* @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered
* when the channel utilization is goes above the configured threshold.
+ * @QCA_ROAM_TRIGGER_REASON_USER_TRIGGER: Set if the roam has to be triggered
+ * based on the request from the user (space).
+ * @QCA_ROAM_TRIGGER_REASON_DEAUTH: Set if the roam has to be triggered when
+ * device receives Deauthentication/Disassociation frame from connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_IDLE: Set if the roam has to be triggered when the
+ * device is in idle state (no TX/RX) and suspend mode, if the current RSSI
+ * is determined to be a poor one.
+ * @QCA_ROAM_TRIGGER_REASON_TX_FAILURES: Set if the roam has to be triggered
+ * based on continuous TX Data frame failures to the connected AP.
+ * @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
+ * based on the scan results obtained from an external scan (not triggered
+ * to aim roaming).
*
* Set the corresponding roam trigger reason bit to consider it for roam
* trigger.
@@ -3741,6 +4113,11 @@
QCA_ROAM_TRIGGER_REASON_DENSE = 1 << 5,
QCA_ROAM_TRIGGER_REASON_BTM = 1 << 6,
QCA_ROAM_TRIGGER_REASON_BSS_LOAD = 1 << 7,
+ QCA_ROAM_TRIGGER_REASON_USER_TRIGGER = 1 << 8,
+ QCA_ROAM_TRIGGER_REASON_DEAUTH = 1 << 9,
+ QCA_ROAM_TRIGGER_REASON_IDLE = 1 << 10,
+ QCA_ROAM_TRIGGER_REASON_TX_FAILURES = 1 << 11,
+ QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12,
};
/**
@@ -3897,6 +4274,40 @@
*
* Clears the selection criteria configured in the driver when specified
* with clear command.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME: Unsigned 32-bit value.
+ * Represents value of the scan frequency scheme from enum
+ * qca_roam_scan_scheme.
+ * It's an optional attribute. If this attribute is not configured, the
+ * driver shall proceed with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ * signifying the RSSI threshold of the current connected AP, indicating
+ * the driver to trigger roam only when the current connected AP's RSSI
+ * is less than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm,
+ * signifying the RSSI threshold of the candidate AP, indicating
+ * the driver to trigger roam only to the candidate AP with RSSI
+ * better than this threshold.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_USER_REASON: Unsigned 32-bit value. Represents the
+ * user defined reason code to be sent to the AP in response to AP's
+ * request to trigger the roam if the roaming cannot be triggered.
+ * Applies to all the scenarios of AP assisted roaming (e.g., BTM).
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS: Unsigned 32-bit value.
+ * Carries a bitmap of the roam triggers specified in
+ * enum qca_vendor_roam_triggers.
+ * Represents the roam triggers for which the specific scan scheme from
+ * enum qca_roam_scan_scheme has to be applied.
+ * It's an optional attribute. If this attribute is not configured, but
+ * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is specified, the scan scheme
+ * specified through QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is applicable for
+ * all the roams.
+ * If both QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME and
+ * QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS are not specified, the
+ * driver shall proceed with the default behavior.
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
@@ -3907,6 +4318,11 @@
QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8,
+ QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME = 9,
+ QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD = 10,
+ QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD = 11,
+ QCA_ATTR_ROAM_CONTROL_USER_REASON = 12,
+ QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS = 13,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
@@ -3926,7 +4342,7 @@
* Represents the Request ID for the specific set of commands.
* This also helps to map specific set of commands to the respective
* ID / client. e.g., helps to identify the user entity configuring the
- * Blacklist BSSID and accordingly clear the respective ones with the
+ * ignored BSSIDs and accordingly clear the respective ones with the
* matching ID.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned
@@ -3993,17 +4409,18 @@
* the BSSID for the purpose of comparing it with other roam candidate.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute,
- * represents the BSSIDs to get blacklisted for roaming.
+ * represents the BSSIDs to get ignored for roaming.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned
- * 32-bit value, represents the number of blacklisted BSSIDs.
+ * 32-bit value, represents the number of ignored BSSIDs.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC
- * address representing the Blacklisted BSSID.
+ * address representing the ignored BSSID.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute,
- * indicates this BSSID blacklist as a hint to the driver. The driver can
- * select this BSSID in the worst case (when no other BSSIDs are better).
+ * indicates this request to ignore the BSSID as a hint to the driver. The
+ * driver can select this BSSID in the worst case (when no other BSSIDs are
+ * better).
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to
* set/get/clear the roam control config as
@@ -4038,11 +4455,11 @@
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
- /* Attribute for set_blacklist bssid params */
+ /* Attribute for setting ignored BSSID parameters */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
- /* Flag attribute indicates this BSSID blacklist as a hint */
+ /* Flag attribute indicates this entry as a hint */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22,
@@ -4078,9 +4495,9 @@
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID
* preference.
*
- * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the Blacklist
- * BSSIDs. Refers QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to
- * set the same.
+ * @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the list of BSSIDs
+ * to ignore in roaming decision. Uses
+ * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to set the list.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the
* roam control config to the driver with the attribute
@@ -4742,9 +5159,9 @@
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6,
/* Station only channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
- /* DFS radar history for slave device (STA mode) */
+ /* DFS radar history for client device (STA mode) */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8,
- /* DFS CAC valid for slave device (STA mode) */
+ /* DFS CAC valid for client device (STA mode) */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9,
};
@@ -6471,8 +6888,9 @@
/**
* enum qca_wlan_vendor_thermal_level - Defines various thermal levels
- * configured by userspace to the driver/firmware. The values will be
- * encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL attribute.
+ * configured by userspace to the driver/firmware.
+ * The values can be encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL or
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL attribute.
* The driver/firmware takes actions requested by userspace such as throttling
* wifi TX etc. in order to mitigate high temperature.
*
@@ -6506,8 +6924,9 @@
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
/* Userspace uses this attribute to configure thermal level to the
- * driver/firmware. Used in request, u32 attribute, possible values
- * are defined in enum qca_wlan_vendor_thermal_level.
+ * driver/firmware, or get thermal level from the driver/firmware.
+ * Used in request or response, u32 attribute,
+ * possible values are defined in enum qca_wlan_vendor_thermal_level.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL = 2,
/* Userspace uses this attribute to configure the time in which the
@@ -6545,6 +6964,9 @@
* resume action.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL: Configure thermal level to
* the driver/firmware.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL: Request to get the current
+ * thermal level from the driver/firmware. The driver should respond with a
+ * thermal level defined in enum qca_wlan_vendor_thermal_level.
*/
enum qca_wlan_vendor_attr_thermal_cmd_type {
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
@@ -6552,6 +6974,7 @@
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL,
};
/**
@@ -6634,6 +7057,11 @@
* NLA_FLAG attribute.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
+ /* Thermal level from the driver.
+ * u32 attribute. Possible values are defined in
+ * enum qca_wlan_vendor_thermal_level.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
@@ -6847,6 +7275,72 @@
QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
};
+ /**
+ * enum qca_wlan_vendor_phy_mode - Different PHY modes
+ * These values are used with %QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE.
+ *
+ * @QCA_WLAN_VENDOR_PHY_MODE_AUTO: autoselect
+ * @QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO: 2.4 GHz 802.11b/g/n/ax autoselect
+ * @QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO: 5 GHz 802.11a/n/ac/ax autoselect
+ * @QCA_WLAN_VENDOR_PHY_MODE_11A: 5 GHz, OFDM
+ * @QCA_WLAN_VENDOR_PHY_MODE_11B: 2.4 GHz, CCK
+ * @QCA_WLAN_VENDOR_PHY_MODE_11G: 2.4 GHz, OFDM
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AGN: Support 802.11n in both 2.4 GHz and 5 GHz
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20: 2.4 GHz, HT20
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS: 2.4 GHz, HT40 (ext ch +1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS: 2.4 GHz, HT40 (ext ch -1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40: 2.4 GHz, Auto HT40
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20: 5 GHz, HT20
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS: 5 GHz, HT40 (ext ch +1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS: 5 GHz, HT40 (ext ch -1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40: 5 GHz, Auto HT40
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20: 5 GHz, VHT20
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS: 5 GHz, VHT40 (Ext ch +1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS: 5 GHz VHT40 (Ext ch -1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40: 5 GHz, VHT40
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80: 5 GHz, VHT80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80: 5 GHz, VHT80+80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160: 5 GHz, VHT160
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20: HE20
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40: HE40
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS: HE40 (ext ch +1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS: HE40 (ext ch -1)
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80
+ * @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160
+ */
+enum qca_wlan_vendor_phy_mode {
+ QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0,
+ QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO = 1,
+ QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO = 2,
+ QCA_WLAN_VENDOR_PHY_MODE_11A = 3,
+ QCA_WLAN_VENDOR_PHY_MODE_11B = 4,
+ QCA_WLAN_VENDOR_PHY_MODE_11G = 5,
+ QCA_WLAN_VENDOR_PHY_MODE_11AGN = 6,
+ QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20 = 7,
+ QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS = 8,
+ QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS = 9,
+ QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40 = 10,
+ QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20 = 11,
+ QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS = 12,
+ QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS = 13,
+ QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40 = 14,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20 = 15,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS = 16,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS = 17,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40 = 18,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80 = 19,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80 = 20,
+ QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160 = 21,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20 = 22,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40 = 23,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS = 24,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS = 25,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27,
+ QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28,
+};
+
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
*/
@@ -7130,6 +7624,67 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
+ /* 8-bit unsigned value to configure protection for Management
+ * frames when PMF is enabled for the association.
+ * This attribute is used to configure the testbed device.
+ * 0-use the correct key, 1-use an incorrect key, 2-disable protection.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION = 38,
+
+ /* Flag attribute to inject Disassociation frame to the connected AP.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX = 39,
+
+ /* 8-bit unsigned value to configure an override for the RSNXE Used
+ * subfield in the MIC control field of the FTE in FT Reassociation
+ * Request frame.
+ * 0 - Default behavior, 1 - override with 1, 2 - override with 0.
+ * This attribute is used to configure the testbed device.
+ * This attribute can be configured only when STA is in associated state
+ * and the configuration is valid until the disconnection.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED = 40,
+
+ /* 8-bit unsigned value to configure the driver to ignore CSA (Channel
+ * Switch Announcement) when STA is in connected state.
+ * 0 - Default behavior, 1 - Ignore CSA.
+ * This attribute is used to configure the testbed device.
+ * This attribute can be configured only when STA is in associated state
+ * and the configuration is valid until the disconnection.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA = 41,
+
+ /* Nested attribute values required to configure OCI (Operating Channel
+ * Information). Attributes defined in enum
+ * qca_wlan_vendor_attr_oci_override are nested within this attribute.
+ * This attribute is used to configure the testbed device.
+ * This attribute can be configured only when STA is in associated state
+ * and the configuration is valid until the disconnection.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE = 42,
+
+ /* 8-bit unsigned value to configure the driver/firmware to ignore SA
+ * Query timeout. If this configuration is enabled STA shall not send
+ * Deauthentication frmae when SA Query times out (mainly, after a
+ * channel switch when OCV is enabled).
+ * 0 - Default behavior, 1 - Ignore SA Query timeout.
+ * This attribute is used to configure the testbed device.
+ * This attribute can be configured only when STA is in associated state
+ * and the configuration is valid until the disconnection.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT = 43,
+
+ /* 8-bit unsigned value to configure the driver/firmware to start or
+ * stop transmitting FILS discovery frames.
+ * 0 - Stop transmitting FILS discovery frames
+ * 1 - Start transmitting FILS discovery frames
+ * This attribute is used to configure the testbed device.
+ * This attribute can be configured only in AP mode and the
+ * configuration is valid until AP restart.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX = 44,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -7137,6 +7692,63 @@
};
/**
+ * enum qca_wlan_twt_operation - Operation of the config TWT request
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION.
+ *
+ * @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured
+ * through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup.
+ *
+ * @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are
+ * obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
+ *
+ * @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are
+ * configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
+ * qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup.
+ */
+enum qca_wlan_twt_operation {
+ QCA_WLAN_TWT_SET = 0,
+ QCA_WLAN_TWT_GET = 1,
+ QCA_WLAN_TWT_TERMINATE = 2,
+ QCA_WLAN_TWT_SUSPEND = 3,
+ QCA_WLAN_TWT_RESUME = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_twt: Defines attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION: u8 attribute. Specify the TWT
+ * operation of this request. Possible values are defined in enum
+ * qca_wlan_twt_operation. The parameters for the respective operation is
+ * specified through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the
+ * parameters configured for TWT. These parameters are represented by
+ * enum qca_wlan_vendor_attr_twt_setup or enum qca_wlan_vendor_attr_twt_resume
+ * based on the operation.
+ */
+enum qca_wlan_vendor_attr_config_twt {
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
* The user can add/delete the filter by specifying the BSSID/STA MAC address in
@@ -7287,7 +7899,8 @@
* enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
* TWT (Target Wake Time) setup request. These attributes are sent as part of
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
* Disable (flag attribute not present) - Individual TWT
@@ -7297,10 +7910,13 @@
* STA and AP.
* Broadcast means the session is across multiple STAs and an AP. The
* configuration parameters are announced in Beacon frames by the AP.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
* Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
- * specify the TWT request type
+ * specify the TWT request type. This is used in TWT SET operation.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
* Enable (flag attribute present) - TWT with trigger support.
@@ -7308,40 +7924,113 @@
* Trigger means the AP will send the trigger frame to allow STA to send data.
* Without trigger, the STA will wait for the MU EDCA timer before
* transmitting the data.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
* 0 - Announced TWT - In this mode, STA may skip few service periods to
* save more power. If STA wants to wake up, it will send a PS-POLL/QoS
* NULL frame to AP.
* 1 - Unannounced TWT - The STA will wakeup during every SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
* Flow ID is the unique identifier for each TWT session.
- * Currently this is not required and dialog ID will be set to zero.
+ * If not provided then dialog ID will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Request and Response
+ * 3. TWT TERMINATE Request and Response
+ * 4. TWT SUSPEND Request and Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
* This attribute (exp) is used along with the mantissa to derive the
* wake interval using the following formula:
* pow(2,exp) = wake_intvl_us/wake_intvl_mantis
* Wake interval is the interval between 2 successive SP.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
* Enable (flag attribute present) - Protection required.
* Disable (flag attribute not present) - Protection not required.
* If protection is enabled, then the AP will use protection
* mechanism using RTS/CTS to self to reserve the airtime.
+ * This is used in
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
* This attribute is used as the SP offset which is the offset from
* TSF after which the wake happens. The units are in microseconds. If
* this attribute is not provided, then the value will be set to zero.
+ * This is an optional parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
- * This is the duration of the service period. The units are in TU.
+ * This is the duration of the service period. This is specified as
+ * multiples of 256 microseconds. Valid values are 0x1 to 0xFF.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
* This attribute is used to configure wake interval mantissa.
* The units are in TU.
+ * This is a required parameter for
+ * 1. TWT SET Request and Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS: Required (u8)
+ * This field is applicable for TWT response only.
+ * This contains status values in enum qca_wlan_vendor_twt_status
+ * and is passed to the userspace. This is used in TWT SET operation.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT TERMINATE Response
+ * 3. TWT SUSPEND Response
+ * 4. TWT RESUME Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE: Required (u8)
+ * This field is applicable for TWT response only.
+ * This field contains response type from the TWT responder and is
+ * passed to the userspace. The values for this field are defined in
+ * enum qca_wlan_vendor_twt_setup_resp_type. This is used in TWT SET
+ * response.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF: Required (u64)
+ * This field is applicable for TWT response only.
+ * This field contains absolute TSF value of the wake time received
+ * from the TWT responder and is passed to the userspace.
+ * This is a required parameter for
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED: Flag attribute.
+ * Enable (flag attribute present) - Indicates that the TWT responder
+ * supports reception of TWT information frame from the TWT requestor.
+ * Disable (flag attribute not present) - Indicates that the responder
+ * doesn't support reception of TWT information frame from requestor.
+ * This is used in
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer for which the TWT session
+ * is being configured. This is used in AP mode to represent the respective
+ * client. In AP mode, this is an optional parameter for response and is
+ * a required parameter for
+ * 1. TWT SET Request
+ * 2. TWT GET Request
+ * 3. TWT TERMINATE Request
+ * 4. TWT SUSPEND Request
+ * In STA mode, this is an optional parameter in request and response for
+ * the above four TWT operations.
*/
enum qca_wlan_vendor_attr_twt_setup {
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
@@ -7356,6 +8045,14 @@
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
+ /* TWT Response only attributes */
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS = 11,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE = 12,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF = 13,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14,
+
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
@@ -7363,24 +8060,83 @@
};
/**
- * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for
+ * enum qca_wlan_vendor_twt_status - Represents the status of the requested
+ * TWT operation
+ *
+ * @QCA_WLAN_VENDOR_TWT_STATUS_OK: TWT request successfully completed
+ * @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED: TWT not enabled
+ * @QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID: TWT dialog ID is already used
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY: TWT session is busy
+ * @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST: TWT session does not exist
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED: TWT session not in suspend state
+ * @QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM: Invalid parameters
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY: FW not ready
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE: FW resource exhausted
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK: Peer AP/STA did not ACK the
+ * request/response frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE: Peer AP did not send the response
+ * frame
+ * @QCA_WLAN_VENDOR_TWT_STATUS_DENIED: AP did not accept the request
+ * @QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR: Adding TWT dialog failed due to an
+ * unknown reason
+ * @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in
+ * suspend state
+ */
+enum qca_wlan_vendor_twt_status {
+ QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
+ QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED = 1,
+ QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID = 2,
+ QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY = 3,
+ QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST = 4,
+ QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED = 5,
+ QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM = 6,
+ QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY = 7,
+ QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE = 8,
+ QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK = 9,
+ QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE = 10,
+ QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11,
+ QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12,
+ QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_twt_resume - Represents attributes for
* TWT (Target Wake Time) resume request. These attributes are sent as part of
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
- * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
+ * attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
- * This attribute is used as the SP offset which is the offset from
- * TSF after which the wake happens. The units are in microseconds.
- * If this attribute is not provided, then the value will be set to
- * zero.
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT: Optional (u32)
+ * These attributes are used as the SP offset which is the offset from TSF after
+ * which the wake happens. The units are in microseconds. Please note that
+ * _NEXT_TWT is limited to u8 whereas _NEXT2_TWT takes the u32 data.
+ * _NEXT2_TWT takes the precedence over _NEXT_TWT and thus the recommendation
+ * is to use _NEXT2_TWT. If neither of these attributes is provided, the value
+ * will be set to zero.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
* This attribute represents the next TWT subfield size.
+ * Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
+ * and 4 for 64 bits.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8).
+ * Flow ID is the unique identifier for each TWT session. This attribute
+ * represents the respective TWT session to resume.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address
+ * Represents the MAC address of the peer to which TWT Resume is
+ * being sent. This is used in AP mode to represent the respective
+ * client and is a required parameter. In STA mode, this is an optional
+ * parameter
*/
enum qca_wlan_vendor_attr_twt_resume {
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT = 4,
+ QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
@@ -7389,6 +8145,28 @@
};
/**
+ * enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by
+ * the TWT responder
+ *
+ * @QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE: TWT responder suggests TWT
+ * parameters that are different from TWT requesting STA suggested
+ * or demanded TWT parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_DICTATE: TWT responder demands TWT
+ * parameters that are different from TWT requesting STA TWT suggested
+ * or demanded parameters
+ * @QCA_WLAN_VENDOR_TWT_RESP_REJECT: TWT responder rejects TWT
+ * setup
+ * @QCA_WLAN_VENDOR_TWT_RESP_ACCEPT: TWT responder accepts the TWT
+ * setup.
+ */
+enum qca_wlan_vendor_twt_setup_resp_type {
+ QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE = 1,
+ QCA_WLAN_VENDOR_TWT_RESP_DICTATE = 2,
+ QCA_WLAN_VENDOR_TWT_RESP_REJECT = 3,
+ QCA_WLAN_VENDOR_TWT_RESP_ACCEPT = 4,
+};
+
+/**
* enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
* Represents the setup type being requested for TWT.
* @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
@@ -8321,10 +9099,14 @@
* enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event
* QCA_NL80211_VENDOR_SUBCMD_OEM_DATA.
*
- * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: The binary blob for the vendor
- * command/event QCA_NL80211_VENDOR_SUBCMD_OEM_DATA are carried through this
- * attribute.
- * NLA_BINARY attribute, the max size is 1024 bytes.
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: This NLA_BINARY attribute is
+ * used to set/query the data to/from the firmware. On query, the same
+ * attribute is used to carry the respective data in the reply sent by the
+ * driver to userspace. The request to set/query the data and the format of the
+ * respective data from the firmware are embedded in the attribute. The
+ * maximum size of the attribute payload is 1024 bytes.
+ * Userspace has to set the QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED
+ * attribute when the data is queried from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed
* based on this field. This optional attribute is included to specify whether
@@ -8333,11 +9115,16 @@
* command/event.
* This u8 attribute is used to carry information for the device type using
* values defined by enum qca_vendor_oem_device_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED: This NLA_FLAG attribute
+ * is set when the userspace queries data from the firmware. This attribute
+ * should not be set when userspace sets the OEM data to the firmware.
*/
enum qca_wlan_vendor_attr_oem_data_params {
QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1,
QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2,
+ QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST,
@@ -8671,6 +9458,36 @@
* disconnect reason for the last disconnection if the disconnection is
* triggered from the host driver. The values are referred from
* enum qca_disconnect_reason_codes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with an invalid MIC or a missing MME when PMF is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents the number of group addressed robust management frames
+ * received from this station with the packet number less than or equal to the
+ * last received packet number when PMF is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT: u32, used in STA
+ * mode only. This represents the number of Beacon frames received from this
+ * station with an invalid MIC or a missing MME when beacon protection is
+ * enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT: u32, used in STA mode
+ * only. This represents number of Beacon frames received from this station with
+ * the packet number less than or equal to the last received packet number when
+ * beacon protection is enabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE: u32, used in
+ * STA mode only. The driver uses this attribute to populate the connection
+ * failure reason codes and the values are defined in
+ * enum qca_sta_connect_fail_reason_codes. Userspace applications can send
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command after receiving
+ * a connection failure indication from the driver. The driver shall not
+ * include this attribute in response to the
+ * QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection
+ * failure observed in the last attempted connection.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -8712,6 +9529,11 @@
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES = 36,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES = 37,
QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON = 38,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT = 39,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT = 40,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -8841,4 +9663,352 @@
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_tspec_operation - Operation of the config TSPEC request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION.
+ */
+enum qca_wlan_tspec_operation {
+ QCA_WLAN_TSPEC_ADD = 0,
+ QCA_WLAN_TSPEC_DEL = 1,
+ QCA_WLAN_TSPEC_GET = 2,
+};
+
+/**
+ * enum qca_wlan_tspec_direction - Direction in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-139.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION.
+ */
+enum qca_wlan_tspec_direction {
+ QCA_WLAN_TSPEC_DIRECTION_UPLINK = 0,
+ QCA_WLAN_TSPEC_DIRECTION_DOWNLINK = 1,
+ QCA_WLAN_TSPEC_DIRECTION_DIRECT = 2,
+ QCA_WLAN_TSPEC_DIRECTION_BOTH = 3,
+};
+
+/**
+ * enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC
+ * As what is defined in IEEE Std 802.11-2016, Table 9-141.
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY.
+ */
+enum qca_wlan_tspec_ack_policy {
+ QCA_WLAN_TSPEC_NORMAL_ACK = 0,
+ QCA_WLAN_TSPEC_NO_ACK = 1,
+ /* Reserved */
+ QCA_WLAN_TSPEC_BLOCK_ACK = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_config_tspec - Defines attributes
+ * used by %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION:
+ * u8 attribute. Specify the TSPEC operation of this request. Possible values
+ * are defined in enum qca_wlan_tspec_operation.
+ * Mandatory attribute for all kinds of config TSPEC requests.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID:
+ * u8 attribute. TS ID. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD, QCA_WLAN_TSPEC_DEL,
+ * QCA_WLAN_TSPEC_GET. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION:
+ * u8 attribute. Direction of data carried by the TS. Possible values are
+ * defined in enum qca_wlan_tspec_direction.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD:
+ * Flag attribute. Indicate whether APSD is enabled for the traffic associated
+ * with the TS. set - enabled, not set - disabled.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY:
+ * u8 attribute. User priority to be used for the transport of MSDUs/A-MSDUs
+ * belonging to this TS. Possible values are 0-7.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY:
+ * u8 attribute. Indicate whether MAC acknowledgements are required for
+ * MPDUs/A-MSDUs belonging to this TS and the form of those acknowledgements.
+ * Possible values are defined in enum qca_wlan_tspec_ack_policy.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE:
+ * u16 attribute. Specify the nominal size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE:
+ * u16 attribute. Specify the maximum size in bytes of MSDUs/A-MSDUs
+ * belonging to this TS.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL:
+ * u32 attribute. Specify the maximum interval in microseconds between the
+ * start of two successive SPs.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MPDU belonging to the TS before this TS
+ * is deleted by the MAC entity at the HC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL:
+ * u32 attribute. Specify the minimum interval in microseconds that can elapse
+ * without arrival or transfer of an MSDU belonging to the TS before the
+ * generation of successive QoS(+)CF-Poll is stopped for this TS. A value of
+ * 0xFFFFFFFF disables the suspension interval. The value of the suspension
+ * interval is always less than or equal to the inactivity interval.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE:
+ * u32 attribute. Indicate the lowest data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE:
+ * u32 attribute. Indicate the average data rate in bps specified at the MAC
+ * SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
+ * bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE:
+ * u32 attribute. Indicate the maximum allowable data rate in bps specified at
+ * the MAC SAP for transport of MSDUs or A-MSDUs belonging to this TS within
+ * the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE:
+ * u32 attribute. Specify the maximum burst size in bytes of the MSDUs/A-MSDUs
+ * belonging to this TS that arrive at the MAC SAP at the peak data rate. A
+ * value of 0 indicates that there are no bursts.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE:
+ * u32 attribute. Indicate the minimum PHY rate in bps for transport of
+ * MSDUs/A-MSDUs belonging to this TS within the bounds of this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE:
+ * u16 attribute. Specify the excess allocation of time (and bandwidth) over
+ * and above the stated application rates required to transport an MSDU/A-MSDU
+ * belonging to the TS in this TSPEC.
+ * Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
+ */
+enum qca_wlan_vendor_attr_config_tspec {
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION = 1,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID = 2,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION = 3,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD = 4,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY = 5,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY = 6,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE = 7,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE = 8,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL = 9,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL = 10,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL = 11,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL = 12,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE = 13,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE = 14,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE = 15,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE = 16,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE = 17,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE = 18,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX =
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_oci_override_frame_type - OCI override frame type
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: SA Query Request frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: SA Query Response frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: FT Reassociation Request
+ * frame
+ * @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: FILS Reassociation
+ * Request frame.
+ */
+enum qca_wlan_vendor_oci_override_frame_type {
+ QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ = 1,
+ QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP = 2,
+ QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ = 3,
+ QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_oci_override: Represents attributes for
+ * OCI override request. These attributes are used inside nested attribute
+ * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE in QCA vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE: Required attribute, u8.
+ * Values from enum qca_wlan_vendor_oci_override_frame_type used in this
+ * attribute to specify the frame type in which the OCI is to be overridden.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY: Required (u32)
+ * OCI frequency (in MHz) to override in the specified frame type.
+ */
+enum qca_wlan_vendor_attr_oci_override {
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX =
+ QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_medium_assess_type - Type of medium assess request
+ *
+ * Values for %QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE.
+ */
+enum qca_wlan_medium_assess_type {
+ QCA_WLAN_MEDIUM_ASSESS_CCA = 0,
+ QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_medium_assess - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE:
+ * u8 attribute. Mandatory in all kinds of medium assess requests/responses.
+ * Specify the type of medium assess request and indicate its type in response.
+ * Possible values are defined in enum qca_wlan_medium_assess_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD:
+ * u32 attribute. Mandatory in CCA request.
+ * Specify the assessment period in terms of seconds. Assessment result will be
+ * sent as the response to the CCA request after the assessment period.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Total timer tick count of the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of idle time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Intra BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT:
+ * u32 attribute. Mandatory in response to CCA request.
+ * Timer tick count of Overlapping BSS traffic RX time in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Maximum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI:
+ * s32 attribute. Mandatory in response to CCA request.
+ * Minimum RSSI of Intra BSS traffic in the assessment cycle.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE:
+ * u8 attribute. Mandatory in congestion report request.
+ * 1-enable 0-disable.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD:
+ * u8 attribute. Mandatory in congestion report enable request and will be
+ * ignored if present in congestion report disable request. Possible values are
+ * 0-100. A vendor event QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS with the type
+ * QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT will be sent to userspace if
+ * congestion percentage reaches the configured threshold.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL:
+ * u8 attribute. Optional in congestion report enable request and will be
+ * ignored if present in congestion report disable request.
+ * Specify the interval of congestion report event in terms of seconds. Possible
+ * values are 1-255. Default value 1 will be used if this attribute is omitted
+ * or using invalid values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE:
+ * u8 attribute. Mandatory in congestion report event.
+ * Indicate the actual congestion percentage. Possible values are 0-100.
+ */
+enum qca_wlan_vendor_attr_medium_assess {
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE = 1,
+
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD = 2,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT = 3,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT = 4,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT = 5,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT = 6,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI = 7,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI = 8,
+
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE = 9,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD = 10,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL = 11,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE = 12,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX =
+ QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mbssid_tx_vdev_status - Defines attributes
+ * used by QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL:
+ * u8 attribute. Notify the TX VDEV status. Possible values 0, 1
+ * belonging to MBSSID/EMA_AP configuration. 0 means Non-Tx VDEV,
+ * 1 means Tx VDEV. Mandatory attribute for all MBSSID VDEV status events.
+ */
+enum qca_wlan_vendor_attr_mbssid_tx_vdev_status {
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_MAX =
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_sta_connect_fail_reason_codes - Defines values carried
+ * by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor
+ * attribute.
+ * @QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
+ * for unicast Probe Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
+ * auth request.
+ * @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
+ * received from AP.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
+ * Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
+ * Association Request frame.
+ * @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
+ * frame is not received from AP.
+ */
+enum qca_sta_connect_fail_reason_codes {
+ QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
+ QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
+ QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
+ QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
+ QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
+ QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
+ QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 543640d..057e1ce 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -169,7 +169,7 @@
* being smaller than prime. */
in_range = const_time_fill_msb((unsigned int) cmp_prime);
/* The algorithm description would skip the next steps if
- * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to
+ * cmp_prime >= 0 (return 0 here), but go through them regardless to
* minimize externally observable differences in behavior. */
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
@@ -713,6 +713,8 @@
goto fail;
const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
x1 = crypto_bignum_init_set(bin, prime_len);
+ if (!x1)
+ goto fail;
debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
/* gx1 = x1^3 + a * x1 + b */
@@ -753,6 +755,8 @@
goto fail;
const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
v = crypto_bignum_init_set(bin, prime_len);
+ if (!v)
+ goto fail;
debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
/* x = CSEL(l, x1, x2) */
@@ -1052,10 +1056,17 @@
wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
+ if (ssid_len > 32)
+ return NULL;
+
pt = os_zalloc(sizeof(*pt));
if (!pt)
return NULL;
+#ifdef CONFIG_SAE_PK
+ os_memcpy(pt->ssid, ssid, ssid_len);
+ pt->ssid_len = ssid_len;
+#endif /* CONFIG_SAE_PK */
pt->group = group;
pt->ec = crypto_ec_init(group);
if (pt->ec) {
@@ -1354,14 +1365,15 @@
identifier) < 0))
return -1;
- sae->tmp->h2e = 0;
+ sae->h2e = 0;
+ sae->pk = 0;
return sae_derive_commit(sae);
}
int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2,
- int *rejected_groups)
+ int *rejected_groups, const struct sae_pk *pk)
{
if (!sae->tmp)
return -1;
@@ -1377,6 +1389,11 @@
return -1;
}
+#ifdef CONFIG_SAE_PK
+ os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len);
+ sae->tmp->ssid_len = pt->ssid_len;
+ sae->tmp->ap_pk = pk;
+#endif /* CONFIG_SAE_PK */
sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
wpabuf_free(sae->tmp->own_rejected_groups);
sae->tmp->own_rejected_groups = NULL;
@@ -1409,7 +1426,7 @@
return -1;
}
- sae->tmp->h2e = 1;
+ sae->h2e = 1;
return sae_derive_commit(sae);
}
@@ -1515,7 +1532,7 @@
const u8 *salt;
struct wpabuf *rejected_groups = NULL;
u8 keyseed[SAE_MAX_HASH_LEN];
- u8 keys[SAE_MAX_HASH_LEN + SAE_PMK_LEN];
+ u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
struct crypto_bignum *tmp;
int ret = -1;
size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
@@ -1530,15 +1547,18 @@
* KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
* (commit-scalar + peer-commit-scalar) modulo r)
* PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+ *
+ * When SAE-PK is used,
+ * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context)
*/
- if (!sae->tmp->h2e)
+ if (!sae->h2e)
hash_len = SHA256_MAC_LEN;
else if (sae->tmp->dh)
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
else
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
- if (sae->tmp->h2e && (sae->tmp->own_rejected_groups ||
- sae->tmp->peer_rejected_groups)) {
+ if (sae->h2e && (sae->tmp->own_rejected_groups ||
+ sae->tmp->peer_rejected_groups)) {
struct wpabuf *own, *peer;
own = sae->tmp->own_rejected_groups;
@@ -1589,15 +1609,32 @@
* octets). */
crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
- if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
+ if (!sae->pk &&
+ sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
val, sae->tmp->order_len,
keys, hash_len + SAE_PMK_LEN) < 0)
goto fail;
+#ifdef CONFIG_SAE_PK
+ if (sae->pk &&
+ sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
+ val, sae->tmp->order_len,
+ keys, 2 * hash_len + SAE_PMK_LEN) < 0)
+ goto fail;
+#endif /* CONFIG_SAE_PK */
forced_memzero(keyseed, sizeof(keyseed));
os_memcpy(sae->tmp->kck, keys, hash_len);
sae->tmp->kck_len = hash_len;
os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
+#ifdef CONFIG_SAE_PK
+ if (sae->pk) {
+ os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN,
+ hash_len);
+ sae->tmp->kek_len = hash_len;
+ wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK",
+ sae->tmp->kek, sae->tmp->kek_len);
+ }
+#endif /* CONFIG_SAE_PK */
forced_memzero(keys, sizeof(keys));
wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
sae->tmp->kck, sae->tmp->kck_len);
@@ -1632,7 +1669,7 @@
return -1;
wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
- if (!sae->tmp->h2e && token) {
+ if (!sae->h2e && token) {
wpabuf_put_buf(buf, token);
wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
wpabuf_head(token), wpabuf_len(token));
@@ -1673,7 +1710,7 @@
identifier);
}
- if (sae->tmp->h2e && sae->tmp->own_rejected_groups) {
+ if (sae->h2e && sae->tmp->own_rejected_groups) {
wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
sae->tmp->own_rejected_groups);
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
@@ -1683,7 +1720,7 @@
wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
}
- if (sae->tmp->h2e && token) {
+ if (sae->h2e && token) {
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + wpabuf_len(token));
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
@@ -2189,13 +2226,14 @@
}
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
{
const u8 *sc;
size_t hash_len;
+ int res;
if (sae->tmp == NULL)
- return;
+ return -1;
hash_len = sae->tmp->kck_len;
@@ -2206,17 +2244,26 @@
sae->send_confirm++;
if (sae->tmp->ec)
- sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
- sae->tmp->own_commit_element_ecc,
- sae->peer_commit_scalar,
- sae->tmp->peer_commit_element_ecc,
- wpabuf_put(buf, hash_len));
+ res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+ sae->tmp->own_commit_element_ecc,
+ sae->peer_commit_scalar,
+ sae->tmp->peer_commit_element_ecc,
+ wpabuf_put(buf, hash_len));
else
- sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
- sae->tmp->own_commit_element_ffc,
- sae->peer_commit_scalar,
- sae->tmp->peer_commit_element_ffc,
- wpabuf_put(buf, hash_len));
+ res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+ sae->tmp->own_commit_element_ffc,
+ sae->peer_commit_scalar,
+ sae->tmp->peer_commit_element_ffc,
+ wpabuf_put(buf, hash_len));
+ if (res)
+ return res;
+
+#ifdef CONFIG_SAE_PK
+ if (sae_write_confirm_pk(sae, buf) < 0)
+ return -1;
+#endif /* CONFIG_SAE_PK */
+
+ return 0;
}
@@ -2270,6 +2317,12 @@
return -1;
}
+#ifdef CONFIG_SAE_PK
+ if (sae_check_confirm_pk(sae, data + 2 + hash_len,
+ len - 2 - hash_len) < 0)
+ return -1;
+#endif /* CONFIG_SAE_PK */
+
return 0;
}
diff --git a/src/common/sae.h b/src/common/sae.h
index 7966d70..2243c0f 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -16,11 +16,27 @@
#define SAE_MAX_ECC_PRIME_LEN 66
#define SAE_MAX_HASH_LEN 64
#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255)
+#ifdef CONFIG_SAE_PK
+#define SAE_CONFIRM_MAX_LEN ((2 + SAE_MAX_HASH_LEN) + 1500)
+#else /* CONFIG_SAE_PK */
#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN)
+#endif /* CONFIG_SAE_PK */
+#define SAE_PK_M_LEN 16
/* Special value returned by sae_parse_commit() */
#define SAE_SILENTLY_DISCARD 65535
+struct sae_pk {
+ struct wpabuf *m;
+ struct crypto_ec_key *key;
+ int group;
+ struct wpabuf *pubkey; /* DER encoded subjectPublicKey */
+#ifdef CONFIG_TESTING_OPTIONS
+ struct crypto_ec_key *sign_key_override;
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+
struct sae_temporary_data {
u8 kck[SAE_MAX_HASH_LEN];
size_t kck_len;
@@ -46,8 +62,25 @@
u8 bssid[ETH_ALEN];
struct wpabuf *own_rejected_groups;
struct wpabuf *peer_rejected_groups;
- unsigned int h2e:1;
unsigned int own_addr_higher:1;
+
+#ifdef CONFIG_SAE_PK
+ u8 kek[SAE_MAX_HASH_LEN];
+ size_t kek_len;
+ const struct sae_pk *ap_pk;
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ u8 fingerprint[SAE_MAX_HASH_LEN];
+ size_t fingerprint_bytes;
+ size_t fingerprint_bits;
+ size_t lambda;
+ unsigned int sec;
+ u8 ssid[32];
+ size_t ssid_len;
+#ifdef CONFIG_TESTING_OPTIONS
+ bool omit_pk_elem;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_SAE_PK */
};
struct sae_pt {
@@ -58,6 +91,10 @@
const struct dh_group *dh;
struct crypto_bignum *ffc_pt;
+#ifdef CONFIG_SAE_PK
+ u8 ssid[32];
+ size_t ssid_len;
+#endif /* CONFIG_SAE_PK */
};
enum sae_state {
@@ -74,6 +111,8 @@
int group;
unsigned int sync; /* protocol instance variable: Sync */
u16 rc; /* protocol instance variable: Rc (received send-confirm) */
+ unsigned int h2e:1;
+ unsigned int pk:1;
struct sae_temporary_data *tmp;
};
@@ -86,17 +125,19 @@
const char *identifier, struct sae_data *sae);
int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2,
- int *rejected_groups);
+ int *rejected_groups, const struct sae_pk *pk);
int sae_process_commit(struct sae_data *sae);
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
const struct wpabuf *token, const char *identifier);
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
int h2e);
-void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
const char * sae_state_txt(enum sae_state state);
+size_t sae_ecc_prime_len_2_hash_len(size_t prime_len);
+size_t sae_ffc_prime_len_2_hash_len(size_t prime_len);
struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier);
@@ -108,4 +149,24 @@
const u8 *addr1, const u8 *addr2);
void sae_deinit_pt(struct sae_pt *pt);
+/* sae_pk.c */
+#ifdef CONFIG_SAE_PK
+bool sae_pk_valid_password(const char *pw);
+#else /* CONFIG_SAE_PK */
+static inline bool sae_pk_valid_password(const char *pw)
+{
+ return false;
+}
+#endif /* CONFIG_SAE_PK */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits);
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len);
+int sae_pk_set_password(struct sae_data *sae, const char *password);
+void sae_deinit_pk(struct sae_pk *pk);
+struct sae_pk * sae_parse_pk(const char *val);
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len);
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash);
+u32 sae_pk_get_be19(const u8 *buf);
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len);
+
#endif /* SAE_H */
diff --git a/src/common/sae_pk.c b/src/common/sae_pk.c
new file mode 100644
index 0000000..df79e5f
--- /dev/null
+++ b/src/common/sae_pk.c
@@ -0,0 +1,884 @@
+/*
+ * SAE-PK
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <stdint.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/crypto.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "sae.h"
+
+
+/* RFC 4648 base 32 alphabet with lowercase characters */
+static const char *sae_pk_base32_table = "abcdefghijklmnopqrstuvwxyz234567";
+
+
+static const u8 d_mult_table[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
+ 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7,
+ 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
+ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
+ 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
+ 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
+ 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2,
+ 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+ 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3,
+ 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,
+ 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
+ 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+ 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5,
+ 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
+ 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,
+ 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23,
+ 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7,
+ 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24,
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
+ 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25,
+ 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9,
+ 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10,
+ 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27,
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11,
+ 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28,
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12,
+ 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29,
+ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13,
+ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30,
+ 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14,
+ 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31,
+ 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,
+ 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+};
+
+static const u8 d_perm_table[] = {
+ 7, 2, 1, 30, 16, 20, 27, 11, 31, 6, 8, 13, 29, 5, 10, 21,
+ 22, 3, 24, 0, 23, 25, 12, 9, 28, 14, 4, 15, 17, 18, 19, 26
+};
+
+
+static u8 d_permute(u8 val, unsigned int iter)
+{
+ if (iter == 0)
+ return val;
+ return d_permute(d_perm_table[val], iter - 1);
+}
+
+
+static u8 d_invert(u8 val)
+{
+ if (val > 0 && val < 16)
+ return 16 - val;
+ return val;
+}
+
+
+static char d_check_char(const char *str, size_t len)
+{
+ size_t i;
+ u8 val = 0;
+ u8 dtable[256];
+ unsigned int iter = 1;
+ int j;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+
+ for (j = len - 1; j >= 0; j--) {
+ u8 c, p;
+
+ c = dtable[(u8) str[j]];
+ if (c == 0x80)
+ continue;
+ p = d_permute(c, iter);
+ iter++;
+ val = d_mult_table[val * 32 + p];
+ }
+
+ return sae_pk_base32_table[d_invert(val)];
+}
+
+
+bool sae_pk_valid_password(const char *pw)
+{
+ int pos;
+ size_t i, pw_len = os_strlen(pw);
+ u8 sec_1b;
+ u8 dtable[256];
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+
+ /* SAE-PK password has at least three four character components
+ * separated by hyphens. */
+ if (pw_len < 14 || pw_len % 5 != 4) {
+ wpa_printf(MSG_DEBUG, "SAE-PK: Not a valid password (length)");
+ return false;
+ }
+
+ for (pos = 0; pw[pos]; pos++) {
+ if (pos && pos % 5 == 4) {
+ if (pw[pos] != '-') {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (separator)");
+ return false;
+ }
+ continue;
+ }
+ if (dtable[(u8) pw[pos]] == 0x80) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (character)");
+ return false;
+ }
+ }
+
+ /* Verify that the checksum character is valid */
+ if (pw[pw_len - 1] != d_check_char(pw, pw_len - 1)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (checksum)");
+ return false;
+ }
+
+ /* Verify that Sec_1b bits match */
+ sec_1b = dtable[(u8) pw[0]] & BIT(4);
+ for (i = 5; i < pw_len; i += 5) {
+ if (sec_1b != (dtable[(u8) pw[i]] & BIT(4))) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (Sec_1b)");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static char * add_char(const char *start, char *pos, u8 idx, size_t *bits)
+{
+ if (*bits == 0)
+ return pos;
+ if (*bits > 5)
+ *bits -= 5;
+ else
+ *bits = 0;
+
+ if ((pos - start) % 5 == 4)
+ *pos++ = '-';
+ *pos++ = sae_pk_base32_table[idx];
+ return pos;
+}
+
+
+/* Base32 encode a password and add hyper separators and checksum */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
+{
+ char *out, *pos;
+ size_t olen, extra_pad, i;
+ u64 block = 0;
+ u8 val;
+ size_t len = (len_bits + 7) / 8;
+ size_t left = len_bits;
+ int j;
+
+ if (len == 0 || len >= SIZE_MAX / 8)
+ return NULL;
+ olen = len * 8 / 5 + 1;
+ olen += olen / 4; /* hyphen separators */
+ pos = out = os_zalloc(olen + 2); /* include room for ChkSum and nul */
+ if (!out)
+ return NULL;
+
+ extra_pad = (5 - len % 5) % 5;
+ for (i = 0; i < len + extra_pad; i++) {
+ val = i < len ? src[i] : 0;
+ block <<= 8;
+ block |= val;
+ if (i % 5 == 4) {
+ for (j = 7; j >= 0; j--)
+ pos = add_char(out, pos,
+ (block >> j * 5) & 0x1f, &left);
+ block = 0;
+ }
+ }
+
+ *pos = d_check_char(out, os_strlen(out));
+
+ return out;
+}
+
+
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len)
+{
+ u8 dtable[256], *out, *pos, tmp;
+ u64 block = 0;
+ size_t i, count, olen;
+ int pad = 0;
+ size_t extra_pad;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[(u8) src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0)
+ return NULL;
+ extra_pad = (8 - count % 8) % 8;
+
+ olen = (count + extra_pad) / 8 * 5;
+ pos = out = os_malloc(olen);
+ if (!out)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len + extra_pad; i++) {
+ u8 val;
+
+ if (i >= len)
+ val = '=';
+ else
+ val = src[i];
+ tmp = dtable[val];
+ if (tmp == 0x80)
+ continue;
+
+ if (val == '=')
+ pad++;
+ block <<= 5;
+ block |= tmp;
+ count++;
+ if (count == 8) {
+ *pos++ = (block >> 32) & 0xff;
+ *pos++ = (block >> 24) & 0xff;
+ *pos++ = (block >> 16) & 0xff;
+ *pos++ = (block >> 8) & 0xff;
+ *pos++ = block & 0xff;
+ count = 0;
+ block = 0;
+ if (pad) {
+ /* Leave in all the available bits with zero
+ * padding to full octets from right. */
+ pos -= pad * 5 / 8;
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
+
+
+u32 sae_pk_get_be19(const u8 *buf)
+{
+ return (buf[0] << 11) | (buf[1] << 3) | (buf[2] >> 5);
+}
+
+
+/* shift left by two octets and three bits; fill in zeros from right;
+ * len must be at least three */
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len)
+{
+ u8 *dst, *src, *end;
+
+ dst = buf;
+ src = buf + 2;
+ end = buf + len;
+
+ while (src + 1 < end) {
+ *dst++ = (src[0] << 3) | (src[1] >> 5);
+ src++;
+ }
+ *dst++ = *src << 3;
+ *dst++ = 0;
+ *dst++ = 0;
+}
+
+
+static void sae_pk_buf_shift_left_1(u8 *buf, size_t len)
+{
+ u8 *dst, *src, *end;
+
+ dst = buf;
+ src = buf;
+ end = buf + len;
+
+ while (src + 1 < end) {
+ *dst++ = (src[0] << 1) | (src[1] >> 7);
+ src++;
+ }
+ *dst++ = *src << 1;
+}
+
+
+int sae_pk_set_password(struct sae_data *sae, const char *password)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ size_t len, pw_len;
+ u8 *pw, *pos;
+ int bits;
+ u32 val = 0, val19;
+ unsigned int val_bits = 0;
+
+ if (!tmp)
+ return -1;
+
+ os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint));
+ tmp->fingerprint_bytes = tmp->fingerprint_bits = 0;
+
+ len = os_strlen(password);
+ if (len < 1 || !sae_pk_valid_password(password))
+ return -1;
+
+ pw = sae_pk_base32_decode(password, len, &pw_len);
+ if (!pw)
+ return -1;
+
+ tmp->sec = (pw[0] & BIT(7)) ? 3 : 5;
+ tmp->lambda = len - len / 5;
+ tmp->fingerprint_bits = 8 * tmp->sec + 19 * tmp->lambda / 4 - 5;
+ wpa_printf(MSG_DEBUG, "SAE-PK: Sec=%u Lambda=%zu fingerprint_bits=%zu",
+ tmp->sec, tmp->lambda, tmp->fingerprint_bits);
+
+ /* Construct Fingerprint from PasswordBase by prefixing with Sec zero
+ * octets and skipping the Sec_1b bits */
+ pos = &tmp->fingerprint[tmp->sec];
+ bits = tmp->fingerprint_bits - 8 * tmp->sec;
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: PasswordBase", pw, pw_len);
+ while (bits > 0) {
+ if (val_bits < 8) {
+ sae_pk_buf_shift_left_1(pw, pw_len); /* Sec_1b */
+ val19 = sae_pk_get_be19(pw);
+ sae_pk_buf_shift_left_19(pw, pw_len);
+ val = (val << 19) | val19;
+ val_bits += 19;
+ }
+ if (val_bits >= 8) {
+ if (bits < 8)
+ break;
+ *pos++ = (val >> (val_bits - 8)) & 0xff;
+ val_bits -= 8;
+ bits -= 8;
+ }
+ }
+ if (bits > 0) {
+ val >>= val_bits - bits;
+ *pos++ = val << (8 - bits);
+ }
+ tmp->fingerprint_bytes = pos - tmp->fingerprint;
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Fingerprint",
+ tmp->fingerprint, tmp->fingerprint_bytes);
+ bin_clear_free(pw, pw_len);
+ return 0;
+}
+
+
+static size_t sae_group_2_hash_len(int group)
+{
+ switch (group) {
+ case 19:
+ return 32;
+ case 20:
+ return 48;
+ case 21:
+ return 64;
+ }
+
+ return 0;
+}
+
+
+void sae_deinit_pk(struct sae_pk *pk)
+{
+ if (pk) {
+ wpabuf_free(pk->m);
+ crypto_ec_key_deinit(pk->key);
+#ifdef CONFIG_TESTING_OPTIONS
+ crypto_ec_key_deinit(pk->sign_key_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_free(pk->pubkey);
+ os_free(pk);
+ }
+}
+
+
+struct sae_pk * sae_parse_pk(const char *val)
+{
+ struct sae_pk *pk;
+ const char *pos;
+#ifdef CONFIG_TESTING_OPTIONS
+ const char *pos2;
+#endif /* CONFIG_TESTING_OPTIONS */
+ size_t len;
+ unsigned char *der;
+ size_t der_len, b_len;
+
+ /* <m-as-hexdump>:<base64-encoded-DER-encoded-key> */
+
+ pos = os_strchr(val, ':');
+ if (!pos || (pos - val) & 0x01)
+ return NULL;
+ len = (pos - val) / 2;
+ if (len != SAE_PK_M_LEN) {
+ wpa_printf(MSG_INFO, "SAE: Unexpected Modifier M length %zu",
+ len);
+ return NULL;
+ }
+
+ pk = os_zalloc(sizeof(*pk));
+ if (!pk)
+ return NULL;
+ pk->m = wpabuf_alloc(len);
+ if (!pk->m || hexstr2bin(val, wpabuf_put(pk->m, len), len)) {
+ wpa_printf(MSG_INFO, "SAE: Failed to parse m");
+ goto fail;
+ }
+
+ pos++;
+ b_len = os_strlen(pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ pos2 = os_strchr(pos, ':');
+ if (pos2) {
+ b_len = pos2 - pos;
+ pos2++;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ der = base64_decode(pos, b_len, &der_len);
+ if (!der) {
+ wpa_printf(MSG_INFO, "SAE: Failed to base64 decode PK key");
+ goto fail;
+ }
+
+ pk->key = crypto_ec_key_parse_priv(der, der_len);
+ bin_clear_free(der, der_len);
+ if (!pk->key)
+ goto fail;
+ pk->group = crypto_ec_key_group(pk->key);
+ pk->pubkey = crypto_ec_key_get_subject_public_key(pk->key);
+ if (!pk->pubkey)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pos2) {
+ der = base64_decode(pos2, os_strlen(pos2), &der_len);
+ if (!der) {
+ wpa_printf(MSG_INFO,
+ "SAE: Failed to base64 decode PK key");
+ goto fail;
+ }
+
+ pk->sign_key_override = crypto_ec_key_parse_priv(der, der_len);
+ bin_clear_free(der, der_len);
+ if (!pk->sign_key_override)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return pk;
+fail:
+ sae_deinit_pk(pk);
+ return NULL;
+}
+
+
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash)
+{
+ if (hash_len == 32)
+ return sha256_vector(1, &data, &len, hash);
+#ifdef CONFIG_SHA384
+ if (hash_len == 48)
+ return sha384_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ if (hash_len == 64)
+ return sha512_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA512 */
+ return -1;
+}
+
+
+static int sae_pk_hash_sig_data(struct sae_data *sae, size_t hash_len,
+ bool ap, const u8 *m, size_t m_len,
+ const u8 *pubkey, size_t pubkey_len, u8 *hash)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ struct wpabuf *sig_data;
+ u8 *pos;
+ int ret = -1;
+
+ /* Signed data for KeyAuth: eleAP || eleSTA || scaAP || scaSTA ||
+ * M || K_AP || AP-BSSID || STA-MAC */
+ sig_data = wpabuf_alloc(tmp->prime_len * 6 + m_len + pubkey_len +
+ 2 * ETH_ALEN);
+ if (!sig_data)
+ goto fail;
+ pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+ if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->own_commit_element_ecc :
+ tmp->peer_commit_element_ecc,
+ pos, pos + tmp->prime_len) < 0)
+ goto fail;
+ pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+ if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->peer_commit_element_ecc :
+ tmp->own_commit_element_ecc,
+ pos, pos + tmp->prime_len) < 0)
+ goto fail;
+ if (crypto_bignum_to_bin(ap ? tmp->own_commit_scalar :
+ sae->peer_commit_scalar,
+ wpabuf_put(sig_data, tmp->prime_len),
+ tmp->prime_len, tmp->prime_len) < 0 ||
+ crypto_bignum_to_bin(ap ? sae->peer_commit_scalar :
+ tmp->own_commit_scalar,
+ wpabuf_put(sig_data, tmp->prime_len),
+ tmp->prime_len, tmp->prime_len) < 0)
+ goto fail;
+ wpabuf_put_data(sig_data, m, m_len);
+ wpabuf_put_data(sig_data, pubkey, pubkey_len);
+ wpabuf_put_data(sig_data, ap ? tmp->own_addr : tmp->peer_addr,
+ ETH_ALEN);
+ wpabuf_put_data(sig_data, ap ? tmp->peer_addr : tmp->own_addr,
+ ETH_ALEN);
+ wpa_hexdump_buf_key(MSG_DEBUG, "SAE-PK: Data to be signed for KeyAuth",
+ sig_data);
+ if (sae_hash(hash_len, wpabuf_head(sig_data), wpabuf_len(sig_data),
+ hash) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: hash(data to be signed)",
+ hash, hash_len);
+ ret = 0;
+fail:
+ wpabuf_free(sig_data);
+ return ret;
+}
+
+
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ struct wpabuf *sig = NULL;
+ size_t need;
+ int ret = -1;
+ u8 *encr_mod;
+ size_t encr_mod_len;
+ const struct sae_pk *pk;
+ u8 hash[SAE_MAX_HASH_LEN];
+ size_t hash_len;
+ struct crypto_ec_key *key;
+
+ if (!tmp)
+ return -1;
+
+ pk = tmp->ap_pk;
+ if (!sae->pk || !pk)
+ return 0;
+
+ key = pk->key;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (tmp->omit_pk_elem)
+ return 0;
+ if (pk->sign_key_override) {
+ wpa_printf(MSG_INFO, "TESTING: Override SAE-PK signing key");
+ key = pk->sign_key_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No KEK available for writing confirm");
+ return -1;
+ }
+
+ if (!tmp->ec) {
+ /* Only ECC groups are supported for SAE-PK in the current
+ * implementation. */
+ wpa_printf(MSG_INFO,
+ "SAE-PK: SAE commit did not use an ECC group");
+ return -1;
+ }
+
+ hash_len = sae_group_2_hash_len(pk->group);
+ if (sae_pk_hash_sig_data(sae, hash_len, true, wpabuf_head(pk->m),
+ wpabuf_len(pk->m), wpabuf_head(pk->pubkey),
+ wpabuf_len(pk->pubkey), hash) < 0)
+ goto fail;
+ sig = crypto_ec_key_sign(key, hash, hash_len);
+ if (!sig)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "SAE-PK: KeyAuth = Sig_AP()", sig);
+
+ /* TODO: fragmentation if any of the elements needs it for a group
+ * using sufficiently large primes (none of the currently supported
+ * ones do) */
+
+ encr_mod_len = wpabuf_len(pk->m) + AES_BLOCK_SIZE;
+ need = 4 + wpabuf_len(pk->pubkey) + 3 + wpabuf_len(sig) +
+ 6 + encr_mod_len;
+ if (wpabuf_tailroom(buf) < need) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No room in message buffer for SAE-PK elements (%zu < %zu)",
+ wpabuf_tailroom(buf), need);
+ goto fail;
+ }
+
+ /* FILS Public Key element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 2 + wpabuf_len(pk->pubkey));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_PUBLIC_KEY);
+ wpabuf_put_u8(buf, 2); /* Key Type: ECDSA public key */
+ wpabuf_put_buf(buf, pk->pubkey);
+
+ /* FILS Key Confirmation element (KeyAuth) */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(sig));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ /* KeyAuth = Sig_AP(eleAP || eleSTA || scaAP || scaSTA || M || K_AP ||
+ * AP-BSSID || STA-MAC) */
+ wpabuf_put_buf(buf, sig);
+
+ /* SAE-PK element */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + encr_mod_len);
+ wpabuf_put_be32(buf, SAE_PK_IE_VENDOR_TYPE);
+ /* EncryptedModifier = AES-SIV-Q(M); no AAD */
+ encr_mod = wpabuf_put(buf, encr_mod_len);
+ if (aes_siv_encrypt(tmp->kek, tmp->kek_len,
+ wpabuf_head(pk->m), wpabuf_len(pk->m),
+ 0, NULL, NULL, encr_mod) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+ encr_mod, encr_mod_len);
+
+ ret = 0;
+fail:
+ wpabuf_free(sig);
+ return ret;
+
+}
+
+
+static bool sae_pk_valid_fingerprint(struct sae_data *sae,
+ const u8 *m, size_t m_len,
+ const u8 *k_ap, size_t k_ap_len, int group)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ u8 *hash_data, *pos;
+ size_t hash_len, hash_data_len;
+ u8 hash[SAE_MAX_HASH_LEN];
+ int res;
+
+ if (!tmp->fingerprint_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: No PW available for K_AP fingerprint check");
+ return false;
+ }
+
+ /* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5)
+ */
+
+ hash_len = sae_group_2_hash_len(group);
+ hash_data_len = tmp->ssid_len + m_len + k_ap_len;
+ hash_data = os_malloc(hash_data_len);
+ if (!hash_data)
+ return false;
+ pos = hash_data;
+ os_memcpy(pos, tmp->ssid, tmp->ssid_len);
+ pos += tmp->ssid_len;
+ os_memcpy(pos, m, m_len);
+ pos += m_len;
+ os_memcpy(pos, k_ap, k_ap_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: SSID || M || K_AP",
+ hash_data, hash_data_len);
+ res = sae_hash(hash_len, hash_data, hash_data_len, hash);
+ bin_clear_free(hash_data, hash_data_len);
+ if (res < 0)
+ return false;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Hash(SSID || M || K_AP)",
+ hash, hash_len);
+
+ if (tmp->fingerprint_bits > hash_len * 8) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Not enough hash output bits for the fingerprint");
+ return false;
+ }
+ if (tmp->fingerprint_bits % 8) {
+ size_t extra;
+
+ /* Zero out the extra bits in the last octet */
+ extra = 8 - tmp->fingerprint_bits % 8;
+ pos = &hash[tmp->fingerprint_bits / 8];
+ *pos = (*pos >> extra) << extra;
+ }
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Fingerprint", hash,
+ tmp->fingerprint_bytes);
+ res = os_memcmp_const(hash, tmp->fingerprint, tmp->fingerprint_bytes);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "SAE-PK: K_AP fingerprint mismatch");
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Expected fingerprint",
+ tmp->fingerprint, tmp->fingerprint_bytes);
+ return false;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE-PK: Valid K_AP fingerprint");
+ return true;
+}
+
+
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ const u8 *k_ap;
+ u8 m[SAE_PK_M_LEN];
+ size_t k_ap_len;
+ struct crypto_ec_key *key;
+ int res;
+ u8 hash[SAE_MAX_HASH_LEN];
+ size_t hash_len;
+ int group;
+ struct ieee802_11_elems elems;
+
+ if (!tmp)
+ return -1;
+ if (!sae->pk || tmp->ap_pk)
+ return 0;
+
+ if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No KEK available for checking confirm");
+ return -1;
+ }
+
+ if (!tmp->ec) {
+ /* Only ECC groups are supported for SAE-PK in the current
+ * implementation. */
+ wpa_printf(MSG_INFO,
+ "SAE-PK: SAE commit did not use an ECC group");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received confirm IEs", ies, ies_len);
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_INFO, "SAE-PK: Failed to parse confirm IEs");
+ return -1;
+ }
+ if (!elems.fils_pk || !elems.fils_key_confirm || !elems.sae_pk) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Not all mandatory IEs included in confirm");
+ return -1;
+ }
+
+ /* TODO: Fragment reassembly */
+
+ if (elems.sae_pk_len < SAE_PK_M_LEN + AES_BLOCK_SIZE) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No room for EncryptedModifier in SAE-PK element");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+ elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE);
+
+ if (aes_siv_decrypt(tmp->kek, tmp->kek_len,
+ elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE,
+ 0, NULL, NULL, m) < 0) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Failed to decrypt EncryptedModifier");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Modifier M", m, SAE_PK_M_LEN);
+
+ if (elems.fils_pk[0] != 2) {
+ wpa_printf(MSG_INFO, "SAE-PK: Unsupported public key type %u",
+ elems.fils_pk[0]);
+ return -1;
+ }
+ k_ap_len = elems.fils_pk_len - 1;
+ k_ap = elems.fils_pk + 1;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received K_AP", k_ap, k_ap_len);
+ /* TODO: Check against the public key, if one is stored in the network
+ * profile */
+
+ key = crypto_ec_key_parse_pub(k_ap, k_ap_len);
+ if (!key) {
+ wpa_printf(MSG_INFO, "SAE-PK: Failed to parse K_AP");
+ return -1;
+ }
+
+ group = crypto_ec_key_group(key);
+ if (!sae_pk_valid_fingerprint(sae, m, SAE_PK_M_LEN, k_ap, k_ap_len,
+ group)) {
+ crypto_ec_key_deinit(key);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received KeyAuth",
+ elems.fils_key_confirm, elems.fils_key_confirm_len);
+
+ hash_len = sae_group_2_hash_len(group);
+ if (sae_pk_hash_sig_data(sae, hash_len, false, m, SAE_PK_M_LEN,
+ k_ap, k_ap_len, hash) < 0) {
+ crypto_ec_key_deinit(key);
+ return -1;
+ }
+
+ res = crypto_ec_key_verify_signature(key, hash, hash_len,
+ elems.fils_key_confirm,
+ elems.fils_key_confirm_len);
+ crypto_ec_key_deinit(key);
+
+ if (res != 1) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Invalid or incorrect signature in KeyAuth");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE-PK: Valid KeyAuth signature received");
+
+ /* TODO: Store validated public key into network profile */
+
+ return 0;
+}
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index eb1861a..82a5a17 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1633,7 +1633,8 @@
if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
- os_memset(r0_key_data, 0, sizeof(r0_key_data));
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+ forced_memzero(r0_key_data, sizeof(r0_key_data));
return 0;
}
@@ -1670,6 +1671,7 @@
if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
return 0;
}
@@ -1839,7 +1841,7 @@
wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
- os_memset(tmp, 0, sizeof(tmp));
+ forced_memzero(tmp, sizeof(tmp));
return 0;
}
@@ -2305,7 +2307,7 @@
case WPA_CIPHER_TKIP:
return WPA_ALG_TKIP;
case WPA_CIPHER_AES_128_CMAC:
- return WPA_ALG_IGTK;
+ return WPA_ALG_BIP_CMAC_128;
case WPA_CIPHER_BIP_GMAC_128:
return WPA_ALG_BIP_GMAC_128;
case WPA_CIPHER_BIP_GMAC_256:
@@ -2801,6 +2803,15 @@
return 0;
}
+ if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) {
+ ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN;
+ ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+
return 2;
}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c0ef689..065dc71 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -124,6 +124,7 @@
#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
#define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20)
+#define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21)
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
@@ -362,6 +363,10 @@
#define TRANSITION_DISABLE_WPA3_ENTERPRISE BIT(2)
#define TRANSITION_DISABLE_ENHANCED_OPEN BIT(3)
+/* DPP KDE Flags */
+#define DPP_KDE_PFS_ALLOWED BIT(0)
+#define DPP_KDE_PFS_REQUIRED BIT(1)
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
@@ -528,6 +533,8 @@
const u8 *ip_addr_alloc;
const u8 *transition_disable;
size_t transition_disable_len;
+ const u8 *dpp_kde;
+ size_t dpp_kde_len;
const u8 *oci;
size_t oci_len;
const u8 *osen;
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index ca1c35f..acc2d6c 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -97,6 +97,10 @@
"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
/** Unprotected Beacon frame dropped */
#define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
+/** Decision made to do a within-ESS roam */
+#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM "
+/** Decision made to skip a within-ESS roam */
+#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM "
/** IP subnet status change notification
*
@@ -122,6 +126,8 @@
#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
/** Frequency ranges that the driver recommends to avoid */
#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
+/** Result of MSCS setup */
+#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT "
/** WPS overlap detected in PBC mode */
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
@@ -178,7 +184,11 @@
#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
+#define DPP_EVENT_PP_KEY "DPP-PP-KEY "
#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
+#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME "
+#define DPP_EVENT_CERTBAG "DPP-CERTBAG "
+#define DPP_EVENT_CACERT "DPP-CACERT "
#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
#define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID "
@@ -190,6 +200,10 @@
#define DPP_EVENT_INTRO "DPP-INTRO "
#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
+#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
+#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
+#define DPP_EVENT_CSR "DPP-CSR "
+#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
@@ -386,6 +400,10 @@
/* Transition mode disabled indication - followed by bitmap */
#define TRANSITION_DISABLE "TRANSITION-DISABLE "
+/* OCV validation failure; parameters: addr=<src addr>
+ * frame=<saqueryreq/saqueryresp> error=<error string> */
+#define OCV_FAILURE "OCV-FAILURE "
+
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index c40e955..ce09970 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -1,14 +1,3 @@
-all: libcrypto.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_CRYPTO_INTERNAL
CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
@@ -68,8 +57,4 @@
LIB_OBJS += random.o
endif
-
-libcrypto.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 7c7515f..7d2ebd6 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -485,7 +485,7 @@
u8 *data, size_t data_len);
/**
- * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * crypto_get_random - Generate cryptographically strong pseudo-random bytes
* @buf: Buffer for data
* @len: Number of bytes to generate
* Returns: 0 on success, -1 on failure
@@ -918,4 +918,16 @@
void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh);
+struct crypto_ec_key;
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len);
+void crypto_ec_key_deinit(struct crypto_ec_key *key);
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+ size_t len);
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+ size_t len, const u8 *sig, size_t sig_len);
+int crypto_ec_key_group(struct crypto_ec_key *key);
+
#endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 1cc73d8..fafb688 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -744,6 +744,155 @@
}
+static int test_aes_ctr(void)
+{
+ int res = 0;
+
+#if defined(CONFIG_MESH) || defined(CONFIG_PSK)
+ /* CTR-AES*.Encrypt test vectors from NIST SP 800-38a */
+ const u8 key128[] = {
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+ };
+ const u8 counter128[] = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ const u8 plain128[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+ const u8 cipher128[] = {
+ 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
+ 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
+ 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
+ 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
+ 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
+ 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
+ 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
+ 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
+ };
+ const u8 key192[] = {
+ 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52,
+ 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
+ 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
+ };
+ const u8 counter192[] = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ const u8 plain192[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+ const u8 cipher192[] = {
+ 0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2,
+ 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
+ 0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef,
+ 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
+ 0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70,
+ 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
+ 0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58,
+ 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50
+ };
+ const u8 key256[] = {
+ 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
+ 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
+ 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
+ 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
+ };
+ const u8 counter256[] = {
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ const u8 plain256[] = {
+ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+ 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+ 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+ 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+ 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+ 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+ 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+ 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
+ };
+ const u8 cipher256[] = {
+ 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5,
+ 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
+ 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a,
+ 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
+ 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c,
+ 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
+ 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6,
+ 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6
+ };
+ size_t len;
+ u8 *tmp;
+
+ wpa_printf(MSG_DEBUG, "CTR-AES128.Encrypt");
+ len = sizeof(plain128);
+ tmp = os_malloc(len);
+ if (!tmp)
+ return -1;
+ os_memcpy(tmp, plain128, len);
+ if (aes_ctr_encrypt(key128, sizeof(key128), counter128, tmp, len) < 0) {
+ wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+ res = -1;
+ } else if (os_memcmp(tmp, cipher128, len) != 0) {
+ wpa_printf(MSG_ERROR,
+ "CTR-AES128.Encrypt test vector did not match");
+ res = -1;
+ }
+ os_free(tmp);
+
+ wpa_printf(MSG_DEBUG, "CTR-AES192.Encrypt");
+ len = sizeof(plain192);
+ tmp = os_malloc(len);
+ if (!tmp)
+ return -1;
+ os_memcpy(tmp, plain192, len);
+ if (aes_ctr_encrypt(key192, sizeof(key192), counter192, tmp, len) < 0) {
+ wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+ res = -1;
+ } else if (os_memcmp(tmp, cipher192, len) != 0) {
+ wpa_printf(MSG_ERROR,
+ "CTR-AES192.Encrypt test vector did not match");
+ res = -1;
+ }
+ os_free(tmp);
+
+ wpa_printf(MSG_DEBUG, "CTR-AES256.Encrypt");
+ len = sizeof(plain256);
+ tmp = os_malloc(len);
+ if (!tmp)
+ return -1;
+ os_memcpy(tmp, plain256, len);
+ if (aes_ctr_encrypt(key256, sizeof(key256), counter256, tmp, len) < 0) {
+ wpa_printf(MSG_ERROR, "aes_ctr_encrypt() failed");
+ res = -1;
+ } else if (os_memcmp(tmp, cipher256, len) != 0) {
+ wpa_printf(MSG_ERROR,
+ "CTR-AES256.Encrypt test vector did not match");
+ res = -1;
+ }
+ os_free(tmp);
+#endif
+
+ return res;
+}
+
+
static int test_md5(void)
{
#ifndef CONFIG_FIPS
@@ -2154,6 +2303,7 @@
test_cbc() ||
test_ecb() ||
test_key_wrap() ||
+ test_aes_ctr() ||
test_md5() ||
test_sha1() ||
test_sha256() ||
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 47b6ebb..72f93c1 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -21,6 +21,7 @@
#endif /* CONFIG_OPENSSL_CMAC */
#ifdef CONFIG_ECC
#include <openssl/ec.h>
+#include <openssl/x509.h>
#endif /* CONFIG_ECC */
#include "common.h"
@@ -79,6 +80,14 @@
bin_clear_free(ctx, sizeof(*ctx));
}
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
#endif /* OpenSSL version < 1.1.0 */
static BIGNUM * get_group5_prime(void)
@@ -2174,4 +2183,164 @@
return crypto_ec_prime_len(ecdh->ec);
}
+
+struct crypto_ec_key {
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+};
+
+
+struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
+{
+ struct crypto_ec_key *key;
+
+ key = os_zalloc(sizeof(*key));
+ if (!key)
+ return NULL;
+
+ key->eckey = d2i_ECPrivateKey(NULL, &der, der_len);
+ if (!key->eckey) {
+ wpa_printf(MSG_INFO, "OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_conv_form(key->eckey, POINT_CONVERSION_COMPRESSED);
+
+ key->pkey = EVP_PKEY_new();
+ if (!key->pkey || EVP_PKEY_assign_EC_KEY(key->pkey, key->eckey) != 1) {
+ EC_KEY_free(key->eckey);
+ key->eckey = NULL;
+ goto fail;
+ }
+
+ return key;
+fail:
+ crypto_ec_key_deinit(key);
+ return NULL;
+}
+
+
+struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
+{
+ struct crypto_ec_key *key;
+
+ key = os_zalloc(sizeof(*key));
+ if (!key)
+ return NULL;
+
+ key->pkey = d2i_PUBKEY(NULL, &der, der_len);
+ if (!key->pkey) {
+ wpa_printf(MSG_INFO, "OpenSSL: d2i_PUBKEY() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ key->eckey = EVP_PKEY_get0_EC_KEY(key->pkey);
+ if (!key->eckey)
+ goto fail;
+ return key;
+fail:
+ crypto_ec_key_deinit(key);
+ return NULL;
+}
+
+
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
+{
+ if (key) {
+ EVP_PKEY_free(key->pkey);
+ os_free(key);
+ }
+}
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ struct wpabuf *buf;
+
+ der_len = i2d_PUBKEY(key->pkey, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ return buf;
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+ size_t len)
+{
+ EVP_PKEY_CTX *pkctx;
+ struct wpabuf *sig_der;
+ size_t sig_len;
+
+ sig_len = EVP_PKEY_size(key->pkey);
+ sig_der = wpabuf_alloc(sig_len);
+ if (!sig_der)
+ return NULL;
+
+ pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
+ if (!pkctx ||
+ EVP_PKEY_sign_init(pkctx) <= 0 ||
+ EVP_PKEY_sign(pkctx, wpabuf_put(sig_der, 0), &sig_len,
+ data, len) <= 0) {
+ wpabuf_free(sig_der);
+ sig_der = NULL;
+ } else {
+ wpabuf_put(sig_der, sig_len);
+ }
+
+ EVP_PKEY_CTX_free(pkctx);
+ return sig_der;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+ size_t len, const u8 *sig, size_t sig_len)
+{
+ EVP_PKEY_CTX *pkctx;
+ int ret;
+
+ pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
+ if (!pkctx || EVP_PKEY_verify_init(pkctx) <= 0) {
+ EVP_PKEY_CTX_free(pkctx);
+ return -1;
+ }
+
+ ret = EVP_PKEY_verify(pkctx, sig, sig_len, data, len);
+ EVP_PKEY_CTX_free(pkctx);
+ if (ret == 1)
+ return 1; /* signature ok */
+ if (ret == 0)
+ return 0; /* incorrect signature */
+ return -1;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+ const EC_GROUP *group;
+ int nid;
+
+ group = EC_KEY_get0_group(key->eckey);
+ if (!group)
+ return -1;
+ nid = EC_GROUP_get_curve_name(group);
+ switch (nid) {
+ case NID_X9_62_prime256v1:
+ return 19;
+ case NID_secp384r1:
+ return 20;
+ case NID_secp521r1:
+ return 21;
+ }
+ return -1;
+}
+
#endif /* CONFIG_ECC */
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index dc68bd6..2e4bf89 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -1104,19 +1104,21 @@
{
int ret = 0;
WC_RNG rng;
+ size_t len;
+ u8 *buf;
if (TEST_FAIL())
return -1;
if (wc_InitRng(&rng) != 0)
return -1;
- if (mp_rand_prime((mp_int *) r,
- (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
- &rng, NULL) != 0)
- ret = -1;
- if (ret == 0 &&
+ len = (mp_count_bits((mp_int *) m) + 7) / 8;
+ buf = os_malloc(len);
+ if (!buf || wc_RNG_GenerateBlock(&rng, buf, len) != 0 ||
+ mp_read_unsigned_bin((mp_int *) r, buf, len) != MP_OKAY ||
mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
ret = -1;
wc_FreeRng(&rng);
+ bin_clear_free(buf, len);
return ret;
}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index c8b1a82..09fb73b 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -670,4 +670,18 @@
*/
u16 tls_connection_get_cipher_suite(struct tls_connection *conn);
+/**
+ * tls_connection_get_peer_subject - Get peer subject
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Peer subject or %NULL if not authenticated or not available
+ */
+const char * tls_connection_get_peer_subject(struct tls_connection *conn);
+
+/**
+ * tls_connection_get_own_cert_used - Was own certificate used
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: true if own certificate was used during authentication
+ */
+bool tls_connection_get_own_cert_used(struct tls_connection *conn);
+
#endif /* TLS_H */
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 7ee371a..a789455 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -279,6 +279,7 @@
X509 *peer_cert;
X509 *peer_issuer;
X509 *peer_issuer_issuer;
+ char *peer_subject; /* peer subject info for authenticated peer */
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
@@ -1643,6 +1644,7 @@
os_free(conn->domain_match);
os_free(conn->check_cert_subject);
os_free(conn->session_ticket);
+ os_free(conn->peer_subject);
os_free(conn);
}
@@ -2597,6 +2599,11 @@
context->event_cb(context->cb_ctx,
TLS_CERT_CHAIN_SUCCESS, NULL);
+ if (depth == 0 && preverify_ok) {
+ os_free(conn->peer_subject);
+ conn->peer_subject = os_strdup(buf);
+ }
+
return preverify_ok;
}
@@ -3006,16 +3013,12 @@
/* Explicit request to enable TLS versions even if needing to
* override systemwide policies. */
- if (flags & TLS_CONN_ENABLE_TLSv1_0) {
+ if (flags & TLS_CONN_ENABLE_TLSv1_0)
version = TLS1_VERSION;
- } else if (flags & TLS_CONN_ENABLE_TLSv1_1) {
- if (!(flags & TLS_CONN_DISABLE_TLSv1_0))
- version = TLS1_1_VERSION;
- } else if (flags & TLS_CONN_ENABLE_TLSv1_2) {
- if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 |
- TLS_CONN_DISABLE_TLSv1_1)))
- version = TLS1_2_VERSION;
- }
+ else if (flags & TLS_CONN_ENABLE_TLSv1_1)
+ version = TLS1_1_VERSION;
+ else if (flags & TLS_CONN_ENABLE_TLSv1_2)
+ version = TLS1_2_VERSION;
if (!version) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Invalid TLS version configuration");
@@ -3029,6 +3032,18 @@
}
}
#endif /* >= 1.1.0 */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER) && \
+ !defined(OPENSSL_IS_BORINGSSL)
+ if ((flags & (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
+ SSL_get_security_level(ssl) >= 2) {
+ /*
+ * Need to drop to security level 1 to allow TLS versions older
+ * than 1.2 to be used when explicitly enabled in configuration.
+ */
+ SSL_set_security_level(conn->ssl, 1);
+ }
+#endif
#ifdef CONFIG_SUITEB
#ifdef OPENSSL_IS_BORINGSSL
@@ -3198,7 +3213,11 @@
if (conn == NULL)
return -1;
- if (verify_peer) {
+ if (verify_peer == 2) {
+ conn->ca_cert_verify = 1;
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+ SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+ } else if (verify_peer) {
conn->ca_cert_verify = 1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
@@ -3259,8 +3278,36 @@
"OK");
return 0;
} else if (client_cert_blob) {
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20901000L
tls_show_errors(MSG_DEBUG, __func__,
"SSL_use_certificate_ASN1 failed");
+#else
+ BIO *bio;
+ X509 *x509;
+
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_ASN1 failed");
+ bio = BIO_new(BIO_s_mem());
+ if (!bio)
+ return -1;
+ BIO_write(bio, client_cert_blob, client_cert_blob_len);
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (!x509 || SSL_use_certificate(conn->ssl, x509) != 1) {
+ X509_free(x509);
+ BIO_free(bio);
+ return -1;
+ }
+ X509_free(x509);
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Found PEM encoded certificate from blob");
+ while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Added an additional certificate into the chain");
+ SSL_add0_chain_cert(conn->ssl, x509);
+ }
+ BIO_free(bio);
+ return 0;
+#endif
}
if (client_cert == NULL)
@@ -3767,6 +3814,17 @@
break;
}
+#ifndef OPENSSL_NO_EC
+ if (SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, conn->ssl,
+ (u8 *) private_key_blob,
+ private_key_blob_len) == 1) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: SSL_use_PrivateKey_ASN1(EVP_PKEY_EC) --> OK");
+ ok = 1;
+ break;
+ }
+#endif /* OPENSSL_NO_EC */
+
if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
(u8 *) private_key_blob,
private_key_blob_len) == 1) {
@@ -5667,3 +5725,19 @@
return SSL_CIPHER_get_id(cipher) & 0xFFFF;
#endif
}
+
+
+const char * tls_connection_get_peer_subject(struct tls_connection *conn)
+{
+ if (conn)
+ return conn->peer_subject;
+ return NULL;
+}
+
+
+bool tls_connection_get_own_cert_used(struct tls_connection *conn)
+{
+ if (conn)
+ return SSL_get_certificate(conn->ssl) != NULL;
+ return false;
+}
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index d222d14..b8a7665 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -19,6 +19,7 @@
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
#include <wolfssl/wolfcrypt/asn.h>
+#include <wolfssl/openssl/x509v3.h>
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
#define HAVE_AESGCM
@@ -576,7 +577,7 @@
static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
const char *value, size_t len)
{
- WOLFSSL_ASN1_OBJECT *gen;
+ WOLFSSL_GENERAL_NAME *gen;
void *ext;
int found = 0;
int i;
@@ -585,14 +586,15 @@
for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
gen = wolfSSL_sk_value(ext, i);
- if (gen->type != type)
+ if (!gen || gen->type != type)
continue;
- if (os_strlen((char *) gen->obj) == len &&
- os_memcmp(value, gen->obj, len) == 0)
+ if ((size_t) wolfSSL_ASN1_STRING_length(gen->d.ia5) == len &&
+ os_memcmp(value, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+ len) == 0)
found++;
}
- wolfSSL_sk_ASN1_OBJECT_free(ext);
+ wolfSSL_sk_GENERAL_NAME_free(ext);
return found;
}
@@ -676,7 +678,7 @@
static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
size_t match_len, int full)
{
- WOLFSSL_ASN1_OBJECT *gen;
+ WOLFSSL_GENERAL_NAME *gen;
void *ext;
int i;
int j;
@@ -690,21 +692,23 @@
for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
gen = wolfSSL_sk_value(ext, j);
- if (gen->type != ASN_DNS_TYPE)
+ if (!gen || gen->type != ASN_DNS_TYPE)
continue;
dns_name++;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
- gen->obj, os_strlen((char *)gen->obj));
- if (domain_suffix_match((const char *) gen->obj,
- os_strlen((char *) gen->obj), match,
- match_len, full) == 1) {
+ wolfSSL_ASN1_STRING_data(gen->d.ia5),
+ wolfSSL_ASN1_STRING_length(gen->d.ia5));
+ if (domain_suffix_match(
+ (const char *) wolfSSL_ASN1_STRING_data(gen->d.ia5),
+ wolfSSL_ASN1_STRING_length(gen->d.ia5), match,
+ match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
wolfSSL_sk_ASN1_OBJECT_free(ext);
return 1;
}
}
- wolfSSL_sk_ASN1_OBJECT_free(ext);
+ wolfSSL_sk_GENERAL_NAME_free(ext);
if (dns_name) {
wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
@@ -858,7 +862,7 @@
struct tls_context *context = conn->context;
char *alt_subject[TLS_MAX_ALT_SUBJECT];
int alt, num_alt_subject = 0;
- WOLFSSL_ASN1_OBJECT *gen;
+ WOLFSSL_GENERAL_NAME *gen;
void *ext;
int i;
#ifdef CONFIG_SHA256
@@ -899,12 +903,14 @@
if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
break;
gen = wolfSSL_sk_value((void *) ext, i);
- if (gen->type != GEN_EMAIL &&
- gen->type != GEN_DNS &&
- gen->type != GEN_URI)
+ if (!gen ||
+ (gen->type != GEN_EMAIL &&
+ gen->type != GEN_DNS &&
+ gen->type != GEN_URI))
continue;
- pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
+ pos = os_malloc(10 + wolfSSL_ASN1_STRING_length(gen->d.ia5) +
+ 1);
if (!pos)
break;
alt_subject[num_alt_subject++] = pos;
@@ -924,11 +930,12 @@
break;
}
- os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
- pos += os_strlen((char *)gen->obj);
+ os_memcpy(pos, wolfSSL_ASN1_STRING_data(gen->d.ia5),
+ wolfSSL_ASN1_STRING_length(gen->d.ia5));
+ pos += wolfSSL_ASN1_STRING_length(gen->d.ia5);
*pos = '\0';
}
- wolfSSL_sk_ASN1_OBJECT_free(ext);
+ wolfSSL_sk_GENERAL_NAME_free(ext);
for (alt = 0; alt < num_alt_subject; alt++)
ev.peer_cert.altsubject[alt] = alt_subject[alt];
@@ -1741,7 +1748,7 @@
if (!conn)
return NULL;
- wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+ wpa_printf(MSG_DEBUG, "SSL: encrypt: %zu bytes", wpabuf_len(in_data));
wolfssl_reset_out_data(&conn->output);
@@ -1792,7 +1799,7 @@
}
wpabuf_put(buf, res);
- wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+ wpa_printf(MSG_DEBUG, "SSL: decrypt: %zu bytes", wpabuf_len(buf));
return buf;
}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e3b13bc..e8defab 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -197,6 +197,7 @@
u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
+ u16 he_6ghz_capa;
};
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
@@ -1553,8 +1554,9 @@
* alg - Encryption algorithm
*
* (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
- * %WPA_ALG_IGTK, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
- * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, %WPA_ALG_BIP_CMAC_256);
+ * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
+ * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+ * %WPA_ALG_BIP_CMAC_256);
* %WPA_ALG_NONE clears the key. */
enum wpa_alg alg;
@@ -1658,6 +1660,73 @@
enum key_flag key_flag;
};
+enum wpa_driver_if_type {
+ /**
+ * WPA_IF_STATION - Station mode interface
+ */
+ WPA_IF_STATION,
+
+ /**
+ * WPA_IF_AP_VLAN - AP mode VLAN interface
+ *
+ * This interface shares its address and Beacon frame with the main
+ * BSS.
+ */
+ WPA_IF_AP_VLAN,
+
+ /**
+ * WPA_IF_AP_BSS - AP mode BSS interface
+ *
+ * This interface has its own address and Beacon frame.
+ */
+ WPA_IF_AP_BSS,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP,
+
+ /**
+ * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
+ * abstracted P2P Device function in the driver
+ */
+ WPA_IF_P2P_DEVICE,
+
+ /*
+ * WPA_IF_MESH - Mesh interface
+ */
+ WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
+
+ /*
+ * WPA_IF_NAN - NAN Device
+ */
+ WPA_IF_NAN,
+
+ /* keep last */
+ WPA_IF_MAX
+};
+
/**
* struct wpa_driver_capa - Driver capability information
*/
@@ -1679,8 +1748,16 @@
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
+#define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256 0x00020000
+#define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 0x00040000
+#define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE 0x00080000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000
+#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000
+#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
+ unsigned int key_mgmt_iftype[WPA_IF_MAX];
#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
@@ -1854,6 +1931,8 @@
/** Driver supports a separate control port RX for EAPOL frames */
#define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL
+/** Driver supports TX status reports for EAPOL frames through control port */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2018,6 +2097,7 @@
u8 vht_opmode;
const struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
u32 flags; /* bitmask of WPA_STA_* flags */
u32 flags_mask; /* unset bits in flags */
#ifdef CONFIG_MESH
@@ -2045,65 +2125,6 @@
struct mac_address mac_acl[0];
};
-enum wpa_driver_if_type {
- /**
- * WPA_IF_STATION - Station mode interface
- */
- WPA_IF_STATION,
-
- /**
- * WPA_IF_AP_VLAN - AP mode VLAN interface
- *
- * This interface shares its address and Beacon frame with the main
- * BSS.
- */
- WPA_IF_AP_VLAN,
-
- /**
- * WPA_IF_AP_BSS - AP mode BSS interface
- *
- * This interface has its own address and Beacon frame.
- */
- WPA_IF_AP_BSS,
-
- /**
- * WPA_IF_P2P_GO - P2P Group Owner
- */
- WPA_IF_P2P_GO,
-
- /**
- * WPA_IF_P2P_CLIENT - P2P Client
- */
- WPA_IF_P2P_CLIENT,
-
- /**
- * WPA_IF_P2P_GROUP - P2P Group interface (will become either
- * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
- */
- WPA_IF_P2P_GROUP,
-
- /**
- * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
- * abstracted P2P Device function in the driver
- */
- WPA_IF_P2P_DEVICE,
-
- /*
- * WPA_IF_MESH - Mesh interface
- */
- WPA_IF_MESH,
-
- /*
- * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
- */
- WPA_IF_TDLS,
-
- /*
- * WPA_IF_IBSS - IBSS interface (used for pref freq only)
- */
- WPA_IF_IBSS,
-};
-
struct wpa_init_params {
void *global_priv;
const u8 *bssid;
@@ -4366,13 +4387,13 @@
int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
/**
- * set_bssid_blacklist - Set blacklist of BSSIDs to the driver
+ * set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver
* @priv: Private driver interface data
- * @num_bssid: Number of blacklist BSSIDs
- * @bssids: List of blacklisted BSSIDs
+ * @num_bssid: Number of temporarily disallowed BSSIDs
+ * @bssids: List of temporarily disallowed BSSIDs
*/
- int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid,
- const u8 *bssid);
+ int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid,
+ const u8 *bssid);
/**
* update_connect_params - Update the connection parameters
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index d630c3d..9b4166d 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -532,7 +532,7 @@
cipher = IEEE80211_CIPHER_AES_GCM_256;
break;
#endif /* ATH_GCM_SUPPORT */
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
cipher = IEEE80211_CIPHER_AES_CMAC;
break;
#ifdef ATH_GCM_SUPPORT
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index b4400d7..a29d2c8 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -137,7 +137,9 @@
ireq->i_data = arg;
if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, "
+ int level = drv->if_removed ? MSG_DEBUG : MSG_ERROR;
+
+ wpa_printf(level, "ioctl[SIOCG80211, op=%u, "
"arg_len=%u]: %s", op, arg_len, strerror(errno));
return -1;
}
@@ -1451,6 +1453,7 @@
#define GETPARAM(drv, param, v) \
(((v) = get80211param(drv, param)) != -1)
struct bsd_driver_data *drv;
+ int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -1467,6 +1470,9 @@
drv->global = priv;
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ /* Set the interface as removed until proven to work. */
+ drv->if_removed = 1;
+
if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
__func__, strerror(errno));
@@ -1486,10 +1492,17 @@
if (wpa_driver_bsd_capa(drv))
goto fail;
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
/* Down interface during setup. */
if (bsd_get_iface_flags(drv) < 0)
goto fail;
+ /* Proven to work, lets go! */
+ drv->if_removed = 0;
+
drv->opmode = get80211opmode(drv);
dl_list_add(&drv->global->ifaces, &drv->list);
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 23a6a42..a7ebe95 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -328,6 +328,7 @@
#define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x
switch (flag2) {
DF2S(CONTROL_PORT_RX);
+ DF2S(CONTROL_PORT_TX_STATUS);
}
return "UNKNOWN";
#undef DF2S
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 36a0757..3dba13c 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -712,6 +712,9 @@
if (!msg)
return ret;
+ if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+ goto nla_put_failure;
+
nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
if (!nest)
goto nla_put_failure;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 529fc3b..b5fff48 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -2809,6 +2809,7 @@
{
struct wpa_driver_ndis_data *drv;
u32 mode;
+ int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
@@ -2855,6 +2856,11 @@
}
wpa_driver_ndis_get_capability(drv);
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+
/* Make sure that the driver does not have any obsolete PMKID entries.
*/
wpa_driver_ndis_flush_pmkid(drv);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d48f8cb..4295f0c 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -37,7 +37,9 @@
#include "radiotap_iter.h"
#include "rfkill.h"
#include "driver_nl80211.h"
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
#ifndef NETLINK_CAP_ACK
#define NETLINK_CAP_ACK 10
@@ -278,6 +280,43 @@
return NL_STOP;
}
+
+struct nl80211_ack_ext_arg {
+ int *err;
+ void *ext_data;
+};
+
+
+static int ack_handler_cookie(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_ack_ext_arg *ext_arg = arg;
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+ u64 *cookie = ext_arg->ext_data;
+ struct nlattr *attrs;
+ size_t ack_len, attr_len;
+
+ *ext_arg->err = 0;
+ ack_len = sizeof(struct nlmsghdr) + sizeof(int) +
+ sizeof(struct nlmsghdr);
+ attrs = (struct nlattr *)
+ ((u8 *) nlmsg_data(nlmsg_hdr(msg)) + sizeof(struct nlmsghdr) +
+ sizeof(int));
+ if (nlmsg_hdr(msg)->nlmsg_len <= ack_len)
+ return NL_STOP;
+
+ attr_len = nlmsg_hdr(msg)->nlmsg_len - ack_len;
+
+ if(!(nlmsg_hdr(msg)->nlmsg_flags & NLM_F_ACK_TLVS))
+ return NL_STOP;
+
+ nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, attr_len, NULL);
+ if (tb[NLMSGERR_ATTR_COOKIE])
+ *cookie = nla_get_u64(tb[NLMSGERR_ATTR_COOKIE]);
+
+ return NL_STOP;
+}
+
+
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
@@ -352,7 +391,9 @@
static int send_and_recv(struct nl80211_global *global,
struct nl_sock *nl_handle, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data)
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data)
{
struct nl_cb *cb;
int err = -ENOMEM, opt;
@@ -390,7 +431,15 @@
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
- nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+ if (ack_handler_custom) {
+ struct nl80211_ack_ext_arg *ext_arg = ack_data;
+
+ ext_arg->err = &err;
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
+ ack_handler_custom, ack_data);
+ } else {
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+ }
if (valid_handler)
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
@@ -430,10 +479,13 @@
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data)
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data)
{
return send_and_recv(drv->global, drv->global->nl, msg,
- valid_handler, valid_data);
+ valid_handler, valid_data,
+ ack_handler_custom, ack_data);
}
@@ -447,7 +499,10 @@
struct nl_sock *handle, int set_owner,
int (*valid_handler)(struct nl_msg *,
void *),
- void *valid_data)
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *,
+ void *),
+ void *ack_data)
{
/* Control port over nl80211 needs the flags and attributes below.
*
@@ -469,7 +524,8 @@
return -1;
return send_and_recv(drv->global, handle ? handle : drv->global->nl,
- msg, valid_handler, valid_data);
+ msg, valid_handler, valid_data,
+ ack_handler_custom, ack_data);
}
@@ -537,7 +593,8 @@
return -1;
}
- ret = send_and_recv(global, global->nl, msg, family_handler, &res);
+ ret = send_and_recv(global, global->nl, msg, family_handler, &res,
+ NULL, NULL);
if (ret == 0)
ret = res.id;
return ret;
@@ -654,7 +711,8 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
- if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+ if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL) == 0)
return data.wiphy_idx;
return -1;
}
@@ -671,7 +729,8 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return NL80211_IFTYPE_UNSPECIFIED;
- if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
+ if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL) == 0)
return data.nlmode;
return NL80211_IFTYPE_UNSPECIFIED;
}
@@ -687,7 +746,8 @@
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
- return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data);
+ return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+ NULL, NULL);
}
@@ -707,7 +767,8 @@
return -1;
}
- ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
+ ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
"failed: ret=%d (%s)",
@@ -1200,6 +1261,10 @@
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
NULL);
}
+ } else if (ifi->ifi_flags & IFF_UP) {
+ /* Re-read MAC address as it may have changed */
+ nl80211_refresh_mac(drv, ifi->ifi_index, 1);
+ return;
}
/*
@@ -1403,7 +1468,7 @@
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
- &arg);
+ &arg, NULL, NULL);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1437,7 +1502,7 @@
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
- &arg);
+ &arg, NULL, NULL);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1540,7 +1605,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
}
@@ -1597,7 +1662,8 @@
sig_change->frequency = drv->assoc_freq;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
+ NULL, NULL);
}
@@ -1661,7 +1727,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_msgs(drv, msg, get_channel_info, ci);
+ return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
}
@@ -1710,7 +1776,7 @@
nlmsg_free(msg);
return -EINVAL;
}
- if (send_and_recv_msgs(drv, msg, NULL, NULL))
+ if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
return -EINVAL;
return 0;
}
@@ -1746,7 +1812,8 @@
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
alpha2[0] = '\0';
- ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2);
+ ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
+ NULL, NULL);
if (!alpha2[0])
ret = -1;
@@ -2111,6 +2178,11 @@
if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
goto failed;
+ if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
+ drv->control_port_ap = 1;
+ goto skip_wifi_status;
+ }
+
drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
if (drv->eapol_tx_sock < 0)
goto failed;
@@ -2121,17 +2193,20 @@
if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
&enabled, sizeof(enabled)) < 0) {
wpa_printf(MSG_DEBUG,
- "nl80211: wifi status sockopt failed\n");
+ "nl80211: wifi status sockopt failed: %s",
+ strerror(errno));
drv->data_tx_status = 0;
if (!drv->use_monitor)
drv->capa.flags &=
~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
} else {
- eloop_register_read_sock(drv->eapol_tx_sock,
+ eloop_register_read_sock(
+ drv->eapol_tx_sock,
wpa_driver_nl80211_handle_eapol_tx_status,
drv, NULL);
}
}
+skip_wifi_status:
if (drv->global) {
nl80211_check_global(drv->global);
@@ -2187,7 +2262,8 @@
return -1;
}
- ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
+ ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
"failed (type=%u): ret=%d (%s)",
@@ -2328,10 +2404,19 @@
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
ret = -1;
- else
+ else if (!drv->has_driver_key_mgmt) {
+ int i;
+
+ /* Update supported AKMs only if the driver doesn't advertize
+ * any AKM capabilities. */
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ }
+
/* WNM - BSS Transition Management Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
ret = -1;
@@ -2363,7 +2448,8 @@
ret = -1;
/* Radio Measurement - Radio Measurement Request */
- if (nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
+ if (!drv->no_rrm &&
+ nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
ret = -1;
/* Radio Measurement - Link Measurement Request */
@@ -2371,6 +2457,10 @@
(nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
ret = -1;
+ /* Robust AV MSCS Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0)
+ ret = -1;
+
nl80211_mgmt_handle_register_eloop(bss);
return ret;
@@ -2417,7 +2507,8 @@
int ret;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
- ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL);
+ ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
"failed: ret=%d (%s)",
@@ -2571,7 +2662,7 @@
int ret;
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
bss->ifname, (long long unsigned int) bss->wdev_id,
@@ -2586,7 +2677,7 @@
msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
NL80211_CMD_STOP_P2P_DEVICE);
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
start ? "Start" : "Stop",
@@ -2657,7 +2748,8 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+ ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
+ NULL, NULL);
wpa_printf(MSG_DEBUG,
"nl80211: QCA vendor test command returned %d (%s)",
ret, strerror(-ret));
@@ -2782,7 +2874,7 @@
drv->ifindex);
nl80211_put_wiphy_data_ap(bss);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -2918,7 +3010,7 @@
return RSN_CIPHER_SUITE_CCMP_256;
case WPA_ALG_GCMP_256:
return RSN_CIPHER_SUITE_GCMP_256;
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
return RSN_CIPHER_SUITE_AES_128_CMAC;
case WPA_ALG_BIP_GMAC_128:
return RSN_CIPHER_SUITE_BIP_GMAC_128;
@@ -3024,6 +3116,98 @@
return num_suites;
}
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static int wpa_driver_do_broadcom_acs(void *priv, struct drv_acs_params
+ *params)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int freq_list_len;
+ int ret = 0;
+ do {
+ freq_list_len =
+ int_array_len(params->freq_list);
+ wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
+ __FUNCTION__, freq_list_len);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg) {
+ wpa_printf(MSG_ERROR, "%s: *errof, no memory for msg", __FUNCTION__);
+ return ret;
+ }
+ if ((nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_ID",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+
+ if ((nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, BRCM_VENDOR_SCMD_ACS))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_SUBCMD",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if (!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, NL80211_ATTR_VENDOR_DATA", __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HW_MODE",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED, params->ht_enabled))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HT_ENABLED",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED, params->ht40_enabled))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if ((nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED, params->vht_enabled))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ if ((nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width))) {
+ wpa_printf(MSG_ERROR, "%s: *errof, BRCM_VENDOR_ATTR_ACS_CHWIDTH",
+ __FUNCTION__);
+ nlmsg_free(msg);
+ return ret;
+ }
+ wpa_printf(MSG_DEBUG, "%s: ht40=%d, ch_width=%d\n",
+ __FUNCTION__, params->ht40_enabled, params->ch_width);
+ if ((freq_list_len > 0) && (nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(int) * freq_list_len, params->freq_list))) {
+ wpa_printf(MSG_ERROR, "%s: *error, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,"
+ "list_len=%d\n", __FUNCTION__, freq_list_len);
+ nlmsg_free(msg);
+ return ret;
+ }
+ nla_nest_end(msg, data);
+ wpa_printf(MSG_DEBUG, "nl80211: ACS Params: HW_MODE: %d HT: %d HT40:"
+ " %d VHT: %d BW: %d\n",
+ params->hw_mode, params->ht_enabled, params->ht40_enabled,
+ params->vht_enabled, params->ch_width);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "nl80211: Failed to invoke vendor"
+ " driver ACS function: %s\n",
+ strerror(errno));
+ }
+ } while (0);
+ return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
#ifdef CONFIG_DRIVER_NL80211_QCA
static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
@@ -3044,7 +3228,7 @@
nlmsg_free(msg);
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Key management set key failed: ret=%d (%s)",
@@ -3084,7 +3268,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -3250,7 +3434,8 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL,
+ NULL, NULL);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
if (ret)
@@ -3275,10 +3460,7 @@
goto fail2;
if (!key_msg ||
nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
- nla_put_flag(key_msg, (alg == WPA_ALG_IGTK ||
- alg == WPA_ALG_BIP_GMAC_128 ||
- alg == WPA_ALG_BIP_GMAC_256 ||
- alg == WPA_ALG_BIP_CMAC_256) ?
+ nla_put_flag(key_msg, wpa_alg_bip(alg) ?
(key_idx == 6 || key_idx == 7 ?
NL80211_KEY_DEFAULT_BEACON :
NL80211_KEY_DEFAULT_MGMT) :
@@ -3315,7 +3497,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG,
"nl80211: set_key default failed; err=%d %s",
@@ -3347,7 +3529,7 @@
if (!suite)
return -1;
- if (defkey && alg == WPA_ALG_IGTK) {
+ if (defkey && wpa_alg_bip(alg)) {
if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
return -1;
} else if (defkey) {
@@ -3439,9 +3621,10 @@
}
if (nl_connect)
- ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+ ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL,
+ NULL, NULL);
else
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed: reason=%u ret=%d (%s)",
@@ -3689,7 +3872,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -3967,7 +4150,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -4011,7 +4194,7 @@
}
nlmsg_free(acl);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
ret, strerror(-ret));
@@ -4063,7 +4246,7 @@
return ret;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Mesh config set failed: %d (%s)",
@@ -4188,7 +4371,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
switch (ret) {
case 0:
@@ -4323,7 +4506,7 @@
num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
suites, ARRAY_SIZE(suites));
if (num_suites > NL80211_MAX_NR_AKM_SUITES)
- wpa_printf(MSG_WARNING,
+ wpa_printf(MSG_DEBUG,
"nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
num_suites);
else if (num_suites &&
@@ -4477,7 +4660,7 @@
#endif /* CONFIG_IEEE80211AX */
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
ret, strerror(-ret));
@@ -4646,7 +4829,7 @@
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0) {
bss->freq = freq->freq;
return 0;
@@ -4924,7 +5107,7 @@
nla_nest_end(msg, wme);
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
@@ -4996,7 +5179,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
" --> %d (%s)",
bss->ifname, MAC2STR(addr), ret, strerror(-ret));
@@ -5027,7 +5210,7 @@
}
msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
- if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+ if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
return;
wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
}
@@ -5056,6 +5239,10 @@
return "P2P_GO";
case NL80211_IFTYPE_P2P_DEVICE:
return "P2P_DEVICE";
+ case NL80211_IFTYPE_OCB:
+ return "OCB";
+ case NL80211_IFTYPE_NAN:
+ return "NAN";
default:
return "unknown";
}
@@ -5103,7 +5290,7 @@
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
goto fail;
- ret = send_and_recv_msgs(drv, msg, handler, arg);
+ ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
msg = NULL;
if (ret) {
fail:
@@ -5258,8 +5445,10 @@
u16 proto, const u8 *buf, size_t len,
int no_encrypt)
{
+ struct nl80211_ack_ext_arg ext_arg;
struct i802_bss *bss = priv;
struct nl_msg *msg;
+ u64 cookie = 0;
int ret;
wpa_printf(MSG_DEBUG,
@@ -5278,11 +5467,22 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
- if (ret)
+ os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+ ext_arg.ext_data = &cookie;
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
+ ack_handler_cookie, &ext_arg);
+ if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port failed: ret=%d (%s)",
ret, strerror(-ret));
+ } else {
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: tx_control_port cookie=0x%llx",
+ (long long unsigned int) cookie);
+ drv->eapol_tx_cookie = cookie;
+ }
return ret;
}
@@ -5434,7 +5634,7 @@
if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
goto fail;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
return -ENOBUFS;
@@ -5456,7 +5656,7 @@
nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
goto fail;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
return -ENOBUFS;
@@ -5502,7 +5702,7 @@
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
ret = send_and_recv_msgs_owner(drv, msg,
get_connect_handle(drv->first_bss), 1,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -5636,7 +5836,7 @@
ret = send_and_recv_msgs_owner(drv, msg,
get_connect_handle(drv->first_bss), 1,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
@@ -5683,13 +5883,13 @@
return -1;
}
- wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u",
- params->fils_erp_next_seq_num);
- if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
- params->fils_erp_next_seq_num))
- return -1;
-
if (params->fils_erp_rrk_len) {
+ wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u",
+ params->fils_erp_next_seq_num);
+ if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+ params->fils_erp_next_seq_num))
+ return -1;
+
wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)",
(unsigned long) params->fils_erp_rrk_len);
if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
@@ -6038,7 +6238,7 @@
goto fail;
ret = send_and_recv_msgs_owner(drv, msg, nl_connect, 1, NULL,
- (void *) -1);
+ (void *) -1, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
@@ -6154,7 +6354,7 @@
ret = send_and_recv_msgs_owner(drv, msg,
get_connect_handle(drv->first_bss), 1,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -6185,7 +6385,7 @@
if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (!ret)
return 0;
@@ -6261,6 +6461,20 @@
}
}
+ if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
+ bss->ifname, bss->brname);
+ if (linux_br_del_if(drv->global->ioctl_sock,
+ bss->brname, bss->ifname) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to remove interface %s from bridge %s: %s",
+ bss->ifname, bss->brname,
+ strerror(errno));
+ }
+
/* Try to set the mode again while the interface is down */
mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (mode_switch_res == -EBUSY) {
@@ -6333,6 +6547,29 @@
}
+void nl80211_restore_ap_mode(struct i802_bss *bss)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int was_ap = is_ap_interface(drv->nlmode);
+
+ wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
+ if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
+ bss->brname[0] &&
+ (bss->added_if_into_bridge || bss->already_in_bridge)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add AP interface %s back into the bridge %s",
+ bss->ifname, bss->brname);
+ if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+ bss->ifname) < 0) {
+ wpa_printf(MSG_WARNING,
+ "nl80211: Failed to add interface %s into bridge %s: %s",
+ bss->ifname, bss->brname, strerror(errno));
+ }
+ }
+ drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode)
{
@@ -6409,7 +6646,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
@@ -6474,7 +6711,7 @@
memset(seq, 0, 6);
- return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+ return send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
}
@@ -6497,7 +6734,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
@@ -6525,7 +6762,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
@@ -6547,7 +6784,7 @@
* XXX: FIX! this needs to flush all VLANs too
*/
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
- res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
if (res) {
wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
"(%s)", res, strerror(-res));
@@ -6756,7 +6993,8 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
+ return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
+ NULL, NULL);
}
@@ -6813,7 +7051,7 @@
nla_nest_end(msg, txq);
- res = send_and_recv_msgs(drv, msg, NULL, NULL);
+ res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG,
"nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
queue, aifs, cw_min, cw_max, burst_time, res);
@@ -6839,14 +7077,14 @@
MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
- ((drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+ (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
nlmsg_free(msg);
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
@@ -7451,8 +7689,10 @@
drv->global->if_add_wdevid = p2pdev_info.wdev_id;
drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
- if (!is_zero_ether_addr(p2pdev_info.macaddr))
+ if (!is_zero_ether_addr(p2pdev_info.macaddr)) {
os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+ os_memcpy(drv->global->p2p_perm_addr, p2pdev_info.macaddr, ETH_ALEN);
+ }
wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
ifname,
(long long unsigned int) p2pdev_info.wdev_id);
@@ -7693,7 +7933,7 @@
goto fail;
cookie = 0;
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
@@ -7742,7 +7982,8 @@
struct ieee80211_hdr *hdr;
int offchanok = 1;
- if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq)
+ if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq &&
+ bss->beacon_set)
offchanok = 0;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
@@ -7799,7 +8040,7 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -7847,7 +8088,7 @@
}
cookie = 0;
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
if (ret == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
"0x%llx for freq=%u MHz duration=%u",
@@ -7887,7 +8128,7 @@
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
@@ -7984,7 +8225,7 @@
nla_nest_end(msg, bands);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
"(%s)", ret, strerror(-ret));
@@ -8081,7 +8322,7 @@
}
nla_nest_end(msg, cqm);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -8119,7 +8360,7 @@
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
- return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+ return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
}
@@ -8182,15 +8423,34 @@
if (os_strstr(param, "control_port=0")) {
drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
- drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+ drv->capa.flags2 &= ~(WPA_DRIVER_FLAGS2_CONTROL_PORT_RX |
+ WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS);
+ drv->control_port_ap = 0;
}
if (os_strstr(param, "control_port_ap=1"))
drv->control_port_ap = 1;
+ if (os_strstr(param, "control_port_ap=0")) {
+ drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+ drv->control_port_ap = 0;
+ }
+
if (os_strstr(param, "full_ap_client_state=0"))
drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+ if (os_strstr(param, "no_rrm=1")) {
+ drv->no_rrm = 1;
+
+ if (!bss->in_deinit && !is_ap_interface(drv->nlmode) &&
+ !is_mesh_interface(drv->nlmode)) {
+ nl80211_mgmt_unsubscribe(bss, "no_rrm=1");
+ if (nl80211_mgmt_subscribe_non_ap(bss) < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to re-register Action frame processing - ignore for now");
+ }
+ }
+
return 0;
}
@@ -8305,7 +8565,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1);
+ return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1, NULL, NULL);
}
@@ -8370,7 +8630,7 @@
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
if (!msg)
return -ENOBUFS;
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
}
@@ -8535,7 +8795,7 @@
do {
wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
err = send_and_recv_msgs(drv, msg, survey_handler,
- survey_results);
+ survey_results, NULL, NULL);
} while (err > 0);
if (err)
@@ -8575,7 +8835,7 @@
nla_nest_end(msg, replay_nested);
- ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
if (ret == -EOPNOTSUPP) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver does not support rekey offload");
@@ -8642,7 +8902,7 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
MACSTR " failed: ret=%d (%s)",
@@ -8668,7 +8928,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Setting PS state %s failed: %d (%s)",
@@ -8728,7 +8988,7 @@
return -1;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
@@ -8775,7 +9035,7 @@
nla_put(msg, NL80211_ATTR_IE, len, buf))
goto fail;
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
@@ -8825,7 +9085,7 @@
return -ENOBUFS;
}
- res = send_and_recv_msgs(drv, msg, NULL, NULL);
+ res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
" --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
strerror(-res));
@@ -8859,7 +9119,7 @@
return ret;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -8885,7 +9145,7 @@
return -ENOBUFS;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
#endif /* CONFIG TDLS */
@@ -9028,7 +9288,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
"err=%d (%s)", ret, strerror(-ret));
@@ -9056,7 +9316,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: update_dh_ie failed err=%d (%s)",
@@ -9261,7 +9521,7 @@
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
if (send_and_recv_msgs(drv, msg, nl80211_get_country,
- alpha2) == 0 &&
+ alpha2, NULL, NULL) == 0 &&
alpha2[0]) {
res = os_snprintf(pos, end - pos, "country=%s\n",
alpha2);
@@ -9412,7 +9672,7 @@
goto fail;
nla_nest_end(msg, beacon_csa);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
ret, strerror(-ret));
@@ -9453,7 +9713,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
ret, strerror(-ret));
@@ -9480,7 +9740,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
ret, strerror(-ret));
@@ -9565,7 +9825,8 @@
* of send_and_recv_msgs(). */
ret = send_and_recv_msgs_owner(drv, msg,
get_connect_handle(bss), 0,
- cmd_reply_handler, buf);
+ cmd_reply_handler, buf,
+ NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
ret);
@@ -9580,7 +9841,8 @@
nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
goto fail;
- ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+ ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
+ NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
ret);
@@ -9609,7 +9871,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
@@ -9644,7 +9906,8 @@
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
- ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled);
+ ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
return 0;
@@ -9691,7 +9954,7 @@
nla_nest_end(msg, wowlan_triggers);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
@@ -9730,7 +9993,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -9758,15 +10021,15 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
#define WPA_SUPPLICANT_CLIENT_ID 1
-static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid,
- const u8 *bssid)
+static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
+ const u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -9774,7 +10037,8 @@
struct nlattr *params, *nlbssids, *attr;
unsigned int i;
- wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)",
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set temporarily disallowed BSSIDs (num=%u)",
num_bssid);
if (!drv->roam_vendor_cmd_avail)
@@ -9814,7 +10078,7 @@
nla_nest_end(msg, nlbssids);
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
@@ -9852,7 +10116,7 @@
}
nla_nest_end(msg, params);
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -9863,10 +10127,45 @@
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int new_addr = addr != NULL;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+ wpa_printf(MSG_DEBUG, "Enter: %s", __FUNCTION__);
if (TEST_FAIL())
return -1;
+ if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ if (!addr ) {
+ addr = drv->global->p2p_perm_addr;
+ }
+ if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ BRCM_VENDOR_SUBCMD_SET_MAC) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put(msg, BRCM_ATTR_DRIVER_MAC_ADDR, ETH_ALEN, addr)) {
+ wpa_printf(MSG_ERROR, "failed to put p2p randmac");
+ nl80211_nlmsg_clear(msg);
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "nl80211: p2p set macaddr failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+ memcpy(bss->addr, addr, ETH_ALEN);
+ return ret;
+#else
+ return -ENOTSUP;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+ }
if (!addr)
addr = drv->perm_addr;
@@ -9876,27 +10175,26 @@
if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
{
wpa_printf(MSG_DEBUG,
- "nl80211: failed to set_mac_addr for %s to " MACSTR,
- bss->ifname, MAC2STR(addr));
+ "nl80211: failed to set_mac_addr for %s to " MACSTR,
+ bss->ifname, MAC2STR(addr));
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
- 1) < 0) {
+ 1) < 0) {
wpa_printf(MSG_DEBUG,
- "nl80211: Could not restore interface UP after failed set_mac_addr");
+ "nl80211: Could not restore interface UP after failed set_mac_addr");
}
return -1;
}
wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
- bss->ifname, MAC2STR(addr));
+ bss->ifname, MAC2STR(addr));
drv->addr_changed = new_addr;
os_memcpy(bss->addr, addr, ETH_ALEN);
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
{
wpa_printf(MSG_DEBUG,
- "nl80211: Could not restore interface UP after set_mac_addr");
+ "nl80211: Could not restore interface UP after set_mac_addr");
}
-
return 0;
}
@@ -10021,7 +10319,7 @@
goto fail;
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 1,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
@@ -10079,7 +10377,7 @@
wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
ret = send_and_recv_msgs_owner(drv, msg, get_connect_handle(bss), 0,
- NULL, NULL);
+ NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -10113,7 +10411,7 @@
return -ENOBUFS;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
" failed: ret=%d (%s)",
@@ -10525,7 +10823,7 @@
params->hw_mode, params->ht_enabled, params->ht40_enabled,
params->vht_enabled, params->ch_width, params->edmg_enabled);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to invoke driver ACS function: %s",
@@ -10572,7 +10870,7 @@
}
nla_nest_end(msg, data);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver setband function failed: %s",
@@ -10705,7 +11003,8 @@
nla_nest_end(msg, params);
os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
- ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m);
+ ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m,
+ NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR,
"%s: err in send_and_recv_msgs", __func__);
@@ -10757,7 +11056,7 @@
}
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
@@ -10813,7 +11112,7 @@
goto fail;
nla_nest_end(msg, container);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -10848,7 +11147,7 @@
return -1;
}
- return send_and_recv_msgs(drv, msg, NULL, NULL);
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
@@ -10887,7 +11186,7 @@
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -11080,7 +11379,7 @@
ret = send_and_recv_msgs(drv, msg,
nl80211_get_bss_transition_status_handler,
- info);
+ info, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -11133,7 +11432,7 @@
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
@@ -11336,7 +11635,7 @@
nl80211_put_fils_connect_params(drv, params, msg))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret)
wpa_dbg(drv->ctx, MSG_DEBUG,
@@ -11379,7 +11678,7 @@
(params->bssid &&
nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -11419,7 +11718,7 @@
bss->added_if_into_bridge = 0;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (!ret) {
if (bridge_ifname[0] && val &&
@@ -11585,10 +11884,13 @@
.get_bss_transition_status = nl80211_get_bss_transition_status,
.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
#endif /* CONFIG_MBO */
- .set_bssid_blacklist = nl80211_set_bssid_blacklist,
+ .set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
.add_sta_node = nl80211_add_sta_node,
#endif /* CONFIG_DRIVER_NL80211_QCA */
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
+#if defined(CONFIG_DRIVER_NL80211_BRCM)
+ .do_acs = wpa_driver_do_broadcom_acs,
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
.get_ext_capab = nl80211_get_ext_capab,
.update_connect_params = nl80211_update_connection_params,
.send_external_auth_status = nl80211_send_external_auth_status,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index dc80a17..0db7d55 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -33,8 +33,8 @@
struct nl_sock *nl;
int nl80211_id;
int ioctl_sock; /* socket for ioctl() use */
-
struct nl_sock *nl_event;
+ u8 p2p_perm_addr[ETH_ALEN];
};
struct nl80211_wiphy_data {
@@ -111,6 +111,7 @@
unsigned int num_iface_ext_capa;
int has_capability;
+ int has_driver_key_mgmt;
int operstate;
@@ -169,10 +170,10 @@
unsigned int set_wifi_conf_vendor_cmd_avail:1;
unsigned int fetch_bss_trans_status:1;
unsigned int roam_vendor_cmd_avail:1;
- unsigned int get_supported_akm_suites_avail:1;
unsigned int add_sta_node_vendor_cmd_avail:1;
unsigned int control_port_ap:1;
unsigned int multicast_registrations:1;
+ unsigned int no_rrm:1;
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
@@ -180,6 +181,7 @@
#define MAX_SEND_FRAME_COOKIES 20
u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
unsigned int num_send_frame_cookies;
+ u64 eapol_tx_cookie;
unsigned int last_mgmt_freq;
@@ -230,7 +232,9 @@
struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
- void *valid_data);
+ void *valid_data,
+ int (*ack_handler_custom)(struct nl_msg *, void *),
+ void *ack_data);
struct nl_sock * get_connect_handle(struct i802_bss *bss);
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
@@ -275,6 +279,8 @@
const char * nl80211_iftype_str(enum nl80211_iftype mode);
+void nl80211_restore_ap_mode(struct i802_bss *bss);
+
#ifdef ANDROID
int android_nl_socket_set_nonblocking(struct nl_sock *handle);
int android_pno_start(struct i802_bss *bss,
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index f997577..416531f 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -17,7 +17,9 @@
#include "common/qca-vendor.h"
#include "common/qca-vendor-attr.h"
#include "driver_nl80211.h"
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
static int protocol_feature_handler(struct nl_msg *msg, void *arg)
{
@@ -49,7 +51,8 @@
return 0;
}
- if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+ if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
+ NULL, NULL) == 0)
return feat;
return 0;
@@ -79,6 +82,8 @@
unsigned int mac_addr_rand_scan_supported:1;
unsigned int mac_addr_rand_sched_scan_supported:1;
unsigned int update_ft_ies_supported:1;
+ unsigned int has_key_mgmt:1;
+ unsigned int has_key_mgmt_iftype:1;
};
@@ -252,6 +257,179 @@
}
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+ int i, num;
+ unsigned int key_mgmt = 0;
+ u32 *akms;
+
+ if (!tb)
+ return 0;
+
+ num = nla_len(tb) / sizeof(u32);
+ akms = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ switch (akms[i]) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_CCKM:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
+ break;
+ case RSN_AUTH_KEY_MGMT_OSEN:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ break;
+ case RSN_AUTH_KEY_MGMT_OWE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+ break;
+ case RSN_AUTH_KEY_MGMT_DPP:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ break;
+ }
+ }
+
+ return key_mgmt;
+}
+
+
+static void get_iface_akm_suites_info(struct wiphy_info_data *info,
+ struct nlattr *nl_akms)
+{
+ struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
+ struct nlattr *nl_iftype;
+ unsigned int key_mgmt;
+ int i;
+
+ if (!nl_akms)
+ return;
+
+ nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
+ nla_data(nl_akms), nla_len(nl_akms), NULL);
+
+ if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
+ !tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
+ return;
+
+ info->has_key_mgmt_iftype = 1;
+ key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
+
+ nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
+ switch (nla_type(nl_iftype)) {
+ case NL80211_IFTYPE_ADHOC:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_STATION:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
+ key_mgmt;
+ break;
+ case NL80211_IFTYPE_NAN:
+ info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
+ nl80211_iftype_str(nla_type(nl_iftype)),
+ key_mgmt);
+ }
+}
+
+
+static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ struct nlattr *nl_if;
+ int rem_if;
+
+ if (!tb)
+ return;
+
+ nla_for_each_nested(nl_if, tb, rem_if)
+ get_iface_akm_suites_info(info, nl_if);
+}
+
+
+static void wiphy_info_akm_suites(struct wiphy_info_data *info,
+ struct nlattr *tb)
+{
+ if (!tb)
+ return;
+
+ info->has_key_mgmt = 1;
+ info->capa->key_mgmt = get_akm_suites_info(tb);
+ wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
+ info->capa->key_mgmt);
+}
+
+
static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
struct nlattr *tb)
{
@@ -444,6 +622,10 @@
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH))
capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+ if (ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS))
+ capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_VLAN_OFFLOAD))
@@ -696,6 +878,8 @@
wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+ wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
+ wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -809,16 +993,27 @@
case QCA_NL80211_VENDOR_SUBCMD_ROAM:
drv->roam_vendor_cmd_avail = 1;
break;
- case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
- drv->get_supported_akm_suites_avail = 1;
- break;
case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
drv->add_sta_node_vendor_cmd_avail = 1;
break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
-
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ if (vinfo->vendor_id == OUI_BRCM) {
+ wpa_printf(MSG_MSGDUMP, "vendor:%x cmd:0x%x\n",
+ vinfo->vendor_id, vinfo->subcmd);
+ switch (vinfo->subcmd) {
+ case BRCM_VENDOR_SCMD_ACS:
+ drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+ drv->capa.flags |=
+ WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+ break;
+ default:
+ break;
+ }
+ }
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
vinfo->vendor_id, vinfo->subcmd);
}
@@ -878,7 +1073,7 @@
return -1;
}
- if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+ if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
return -1;
if (info->auth_supported)
@@ -984,132 +1179,13 @@
return;
}
- ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
+ ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
+ NULL, NULL);
if (!ret && dfs_capability)
drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
}
-static unsigned int get_akm_suites_info(struct nlattr *tb)
-{
- int i, num;
- unsigned int key_mgmt = 0;
- u32 *akms;
-
- if (!tb)
- return 0;
-
- num = nla_len(tb) / sizeof(u32);
- akms = nla_data(tb);
- for (i = 0; i < num; i++) {
- u32 a = akms[i];
-
- wpa_printf(MSG_DEBUG,
- "nl80211: Supported AKM %02x-%02x-%02x:%u",
- a >> 24, (a >> 16) & 0xff,
- (a >> 8) & 0xff, a & 0xff);
- switch (a) {
- case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
- break;
- case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
- break;
- case RSN_AUTH_KEY_MGMT_FT_802_1X:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
- break;
- case RSN_AUTH_KEY_MGMT_FT_PSK:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
- break;
- case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
- break;
- case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
- break;
- case RSN_AUTH_KEY_MGMT_OWE:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
- break;
- case RSN_AUTH_KEY_MGMT_DPP:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
- break;
- case RSN_AUTH_KEY_MGMT_FILS_SHA256:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
- break;
- case RSN_AUTH_KEY_MGMT_FILS_SHA384:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
- break;
- case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
- break;
- case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
- break;
- case RSN_AUTH_KEY_MGMT_SAE:
- key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
- break;
- }
- }
-
- return key_mgmt;
-}
-
-
-static int get_akm_suites_handler(struct nl_msg *msg, void *arg)
-{
- struct nlattr *tb[NL80211_ATTR_MAX + 1];
- struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
- unsigned int *key_mgmt = arg;
-
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
-
- if (tb[NL80211_ATTR_VENDOR_DATA]) {
- struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
- struct nlattr *tb_data[NL80211_ATTR_MAX + 1];
-
- nla_parse(tb_data, NL80211_ATTR_MAX,
- nla_data(nl_vend), nla_len(nl_vend), NULL);
-
- *key_mgmt =
- get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]);
- }
-
- return NL_SKIP;
-}
-
-
-static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv)
-{
- struct nl_msg *msg;
- unsigned int key_mgmt = 0;
- int ret;
-
- if (!drv->get_supported_akm_suites_avail)
- return -1;
-
- if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
- QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) {
- nlmsg_free(msg);
- return -1;
- }
-
- ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt);
- if (!ret) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x",
- key_mgmt);
- drv->capa.key_mgmt = key_mgmt;
- }
-
- return ret;
-}
-
-
struct features_info {
u8 *flags;
size_t flags_len;
@@ -1191,7 +1267,8 @@
os_memset(&info, 0, sizeof(info));
info.capa = &drv->capa;
- ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+ ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
+ NULL, NULL);
if (ret || !info.flags)
return;
@@ -1221,6 +1298,8 @@
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
struct wiphy_info_data info;
+ int i;
+
if (wpa_driver_nl80211_get_info(drv, &info))
return -1;
@@ -1228,33 +1307,62 @@
return -1;
drv->has_capability = 1;
- drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
- WPA_DRIVER_CAPA_KEY_MGMT_OWE |
- WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
- if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
- WPA_DRIVER_CAPA_ENC_GCMP_256))
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ /* Fallback to hardcoded defaults if the driver does nott advertize any
+ * AKM capabilities. */
+ if (!drv->has_driver_key_mgmt) {
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+ WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+ WPA_DRIVER_CAPA_KEY_MGMT_DPP;
- if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
- WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
- WPA_DRIVER_CAPA_KEY_MGMT_SAE;
- else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
- drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+ WPA_DRIVER_CAPA_ENC_GCMP_256))
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
-#ifdef CONFIG_DRIVER_NL80211_QCA
- /* Override drv->capa.key_mgmt based on driver advertised capability
- * constraints, if available. */
- qca_nl80211_get_akm_suites(drv);
-#endif /* CONFIG_DRIVER_NL80211_QCA */
+ if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+ drv->capa.key_mgmt |=
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+ WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ }
+
+ if (!info.has_key_mgmt_iftype) {
+ /* If the driver does not advertize per interface AKM
+ * capabilities, consider all interfaces to support default AKMs
+ * in key_mgmt. */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+ } else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
+ /* If the driver advertizes only per interface supported AKMs
+ * but does not advertize per wiphy AKM capabilities, consider
+ * the default key_mgmt as a mask of per interface supported
+ * AKMs. */
+ drv->capa.key_mgmt = 0;
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
+ } else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
+ /* If the driver advertizes AKM capabilities both per wiphy and
+ * per interface, consider the interfaces for which per
+ * interface AKM capabilities were not received to support the
+ * default key_mgmt capabilities.
+ */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ if (!drv->capa.key_mgmt_iftype[i])
+ drv->capa.key_mgmt_iftype[i] =
+ drv->capa.key_mgmt;
+ }
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
@@ -1705,6 +1813,13 @@
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
len);
}
+
+ if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
+ u16 capa;
+
+ capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
+ he_capab->he_6ghz_capa = le_to_host16(capa);
+ }
}
@@ -1847,7 +1962,10 @@
for (m = 0; m < *num_modes; m++) {
if (!modes[m].num_channels)
continue;
- if (modes[m].channels[0].freq < 4000) {
+ if (modes[m].channels[0].freq < 2000) {
+ modes[m].num_channels = 0;
+ continue;
+ } else if (modes[m].channels[0].freq < 4000) {
modes[m].mode = HOSTAPD_MODE_IEEE80211B;
for (i = 0; i < modes[m].num_rates; i++) {
if (modes[m].rates[i] > 200) {
@@ -2235,7 +2353,8 @@
}
}
- return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+ return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
+ NULL, NULL);
}
@@ -2324,7 +2443,8 @@
return NULL;
}
- if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
+ NULL, NULL) == 0) {
struct hostapd_hw_modes *modes;
nl80211_set_regulatory_flags(drv, &result);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 1152312..680d426 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -18,6 +18,14 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "driver_nl80211.h"
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+#include "common/brcm_vendor.h"
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie);
static const char * nl80211_command_to_string(enum nl80211_commands cmd)
@@ -138,6 +146,8 @@
C2S(NL80211_CMD_CONTROL_PORT_FRAME)
C2S(NL80211_CMD_UPDATE_OWE_INFO)
C2S(NL80211_CMD_UNPROT_BEACON)
+ C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS)
+
default:
return "NL80211_CMD_UNKNOWN";
}
@@ -698,6 +708,16 @@
WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val,
cookie ? "" : "(N/A)", ack != NULL);
+ if (cookie_val && cookie_val == drv->eapol_tx_cookie &&
+ len >= ETH_HLEN &&
+ WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Work around misdelivered control port TX status for EAPOL");
+ nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+ cookie);
+ return;
+ }
+
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
return;
@@ -1705,7 +1725,96 @@
wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
}
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data
+ *drv, const u8 *data, size_t len)
+{
+ struct nlattr *tb[BRCM_VENDOR_ATTR_ACS_LAST + 1];
+ union wpa_event_data event;
+ wpa_printf(MSG_DEBUG, "nl80211: vendor ACS channel selection vendor even received");
+
+ if (nla_parse(tb, BRCM_VENDOR_ATTR_ACS_LAST, (struct nlattr*) data, len, NULL)
+ || (!tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ])
+ || (!tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])) {
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ if (tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]) {
+ event.acs_selected_channels.pri_freq =
+ nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "got pri_freq=%d\n",
+ event.acs_selected_channels.pri_freq );
+
+ if (tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]) {
+ event.acs_selected_channels.sec_freq =
+ nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "got sec_freq=%d\n",
+ event.acs_selected_channels.sec_freq );
+ if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) {
+ event.acs_selected_channels.vht_seg0_center_ch =
+ nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+ }
+
+ if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]) {
+ event.acs_selected_channels.vht_seg1_center_ch =
+ nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+ }
+ if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]) {
+ event.acs_selected_channels.ch_width =
+ nla_get_u16(tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]);
+ }
+ if (tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]) {
+ event.acs_selected_channels.hw_mode =
+ nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]);
+ if ((event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES)
+ || (event.acs_selected_channels.hw_mode
+ == HOSTAPD_MODE_IEEE80211ANY)) {
+ wpa_printf(MSG_ERROR, "nl80211: Invalid hw_mode %d in ACS selection event",
+ event.acs_selected_channels.hw_mode);
+ return;
+ }
+ }
+
+ wpa_printf(MSG_INFO, "nl80211: ACS Results: PCH: %d SCH: %d BW: %d"
+ " VHT0: %d VHT1: %d HW_MODE: %d",
+ event.acs_selected_channels.pri_freq,
+ event.acs_selected_channels.sec_freq,
+ event.acs_selected_channels.ch_width,
+ event.acs_selected_channels.vht_seg0_center_ch,
+ event.acs_selected_channels.vht_seg1_center_ch,
+ event.acs_selected_channels.hw_mode);
+ wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED,
+ &event);
+ return;
+}
+
+static void nl80211_vendor_event_brcm( struct wpa_driver_nl80211_data *drv,
+ u32 subcmd, u8 *data, size_t len)
+{
+ union wpa_event_data event;
+ const struct nlattr *iter;
+ int rem = len;
+
+ wpa_printf(MSG_MSGDUMP, "got vendor event %d", subcmd);
+ memset(&event, 0, sizeof(event));
+ switch (subcmd) {
+ case BRCM_VENDOR_EVENT_ACS:
+ wpa_printf(MSG_DEBUG, "nl80211: Received VENDOR_EVENT_ACS");
+ brcm_nl80211_acs_select_ch(drv, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event %u", subcmd);
+ break;
+ }
+
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
#ifdef CONFIG_DRIVER_NL80211_QCA
static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
@@ -2267,6 +2376,11 @@
case OUI_QCA:
nl80211_vendor_event_qca(drv, subcmd, data, len);
break;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+ case OUI_BRCM:
+ nl80211_vendor_event_brcm(drv, subcmd, data, len);
+ break;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
break;
@@ -2557,6 +2671,29 @@
}
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+ const u8 *frame, size_t len,
+ struct nlattr *ack, struct nlattr *cookie)
+{
+ union wpa_event_data event;
+
+ if (!cookie || len < ETH_HLEN)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Control port TX status (ack=%d), cookie=%llu",
+ ack != NULL, (long long unsigned int) nla_get_u64(cookie));
+
+ os_memset(&event, 0, sizeof(event));
+ event.eapol_tx_status.dst = frame;
+ event.eapol_tx_status.data = frame + ETH_HLEN;
+ event.eapol_tx_status.data_len = len - ETH_HLEN;
+ event.eapol_tx_status.ack = ack != NULL;
+ wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
@@ -2581,11 +2718,8 @@
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
- cmd == NL80211_CMD_SCAN_ABORTED)) {
- wpa_driver_nl80211_set_mode(drv->first_bss,
- drv->ap_scan_as_station);
- drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- }
+ cmd == NL80211_CMD_SCAN_ABORTED))
+ nl80211_restore_ap_mode(bss);
switch (cmd) {
case NL80211_CMD_TRIGGER_SCAN:
@@ -2778,6 +2912,15 @@
mlme_event_unprot_beacon(drv, nla_data(frame),
nla_len(frame));
break;
+ case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+ if (!frame)
+ break;
+ nl80211_control_port_frame_tx_status(drv,
+ nla_data(frame),
+ nla_len(frame),
+ tb[NL80211_ATTR_ACK],
+ tb[NL80211_ATTR_COOKIE]);
+ break;
default:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", cmd);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 17e8b2c..233175d 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -82,7 +82,8 @@
os_memset(info, 0, sizeof(*info));
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
- return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info);
+ return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info,
+ NULL, NULL);
}
@@ -94,7 +95,7 @@
wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
ret, strerror(-ret));
@@ -125,7 +126,7 @@
nla_nest_end(msg, params);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_INFO,
@@ -166,11 +167,8 @@
wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
- if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
- wpa_driver_nl80211_set_mode(drv->first_bss,
- drv->ap_scan_as_station);
- drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
- }
+ if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
+ nl80211_restore_ap_mode(drv->first_bss);
wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
@@ -368,7 +366,7 @@
goto fail;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
@@ -621,7 +619,7 @@
params->sched_scan_start_delay))
goto fail;
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
/* TODO: if we get an error here, we should fall back to normal scan */
@@ -658,7 +656,7 @@
#endif /* ANDROID */
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Sched scan stop failed: ret=%d (%s)",
@@ -947,7 +945,7 @@
arg.drv = drv;
arg.res = res;
- ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+ ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg, NULL, NULL);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
@@ -1031,7 +1029,8 @@
ctx.idx = 0;
msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
if (msg)
- send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx);
+ send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx,
+ NULL, NULL);
}
@@ -1224,7 +1223,8 @@
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+ ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie,
+ NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -1287,7 +1287,7 @@
nla_nest_end(msg, attr);
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 978e1cf..0f0ad1f 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -968,6 +968,7 @@
static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
{
int send_rfkill_event = 0;
+ int i;
if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
if (rfkill_is_blocked(drv->rfkill)) {
@@ -996,6 +997,10 @@
wpa_driver_wext_get_range(drv);
+ /* Update per interface supported AKMs */
+ for (i = 0; i < WPA_IF_MAX; i++)
+ drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
/*
* Unlock the driver's BSSID and force to a random SSID to clear any
* previous association the driver might have when the supplicant
@@ -1768,7 +1773,7 @@
case WPA_ALG_CCMP:
ext->alg = IW_ENCODE_ALG_CCMP;
break;
- case WPA_ALG_IGTK:
+ case WPA_ALG_BIP_CMAC_128:
ext->alg = IW_ENCODE_ALG_AES_CMAC;
break;
default:
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 55a98ef..a03d4a0 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -26,6 +26,10 @@
CONFIG_LIBNL3_ROUTE=y
endif
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+
ifdef CONFIG_DRIVER_MACSEC_QCA
DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
DRV_OBJS += ../src/drivers/driver_macsec_qca.o
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 5a32a24..10eab6a 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -41,6 +41,9 @@
ifdef CONFIG_DRIVER_NL80211_QCA
DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
endif
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
NEED_SME=y
NEED_AP_MLME=y
NEED_NETLINK=y
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 5e500d7..dad8c8f 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -296,13 +296,14 @@
* to get a list of all present wiphys.
* @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
* %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
- * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
- * attributes determining the channel width; this is used for setting
- * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT,
- * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
- * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
- * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
- * instead, the support here is for backward compatibility only.
+ * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET (and the attributes determining the
+ * channel width; this is used for setting monitor mode channel),
+ * %NL80211_ATTR_WIPHY_RETRY_SHORT, %NL80211_ATTR_WIPHY_RETRY_LONG,
+ * %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, and/or
+ * %NL80211_ATTR_WIPHY_RTS_THRESHOLD. However, for setting the channel,
+ * see %NL80211_CMD_SET_CHANNEL instead, the support here is for backward
+ * compatibility only.
* @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
* or rename notification. Has attributes %NL80211_ATTR_WIPHY and
* %NL80211_ATTR_WIPHY_NAME.
@@ -351,7 +352,8 @@
* %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
* %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
* The channel to use can be set on the interface or be given using the
- * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
+ * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_FREQ_OFFSET, and the
+ * attributes determining channel width.
* @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
* @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
* @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
@@ -536,11 +538,12 @@
* interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
* BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
* the SSID (mainly for association, but is included in authentication
- * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
- * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
- * is used to specify the authentication type. %NL80211_ATTR_IE is used to
- * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
- * to be added to the frame.
+ * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
+ * channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+ * authentication type. %NL80211_ATTR_IE is used to define IEs
+ * (VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+ * to the frame.
* When used as an event, this reports reception of an Authentication
* frame in station and IBSS modes when the local MLME processed the
* frame, i.e., it was for the local STA and was received in correct
@@ -595,8 +598,9 @@
* requests to connect to a specified network but without separating
* auth and assoc steps. For this, you need to specify the SSID in a
* %NL80211_ATTR_SSID attribute, and can optionally specify the association
- * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
- * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE,
+ * %NL80211_ATTR_USE_MFP, %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ,
+ * %NL80211_ATTR_WIPHY_FREQ_OFFSET, %NL80211_ATTR_CONTROL_PORT,
* %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
* %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
@@ -1160,6 +1164,12 @@
* dropped because it did not include a valid MME MIC while beacon
* protection was enabled (BIGTK configured in station mode).
*
+ * @NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: Report TX status of a control
+ * port frame transmitted with %NL80211_CMD_CONTROL_PORT_FRAME.
+ * %NL80211_ATTR_COOKIE identifies the TX command and %NL80211_ATTR_FRAME
+ * includes the contents of the frame. %NL80211_ATTR_ACK flag is included
+ * if the recipient acknowledged the frame.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1388,6 +1398,8 @@
NL80211_CMD_UNPROT_BEACON,
+ NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1433,7 +1445,8 @@
* of &enum nl80211_chan_width, describing the channel width. See the
* documentation of the enum for more information.
* @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
- * channel, used for anything but 20 MHz bandwidth
+ * channel, used for anything but 20 MHz bandwidth. In S1G this is the
+ * operating channel center frequency.
* @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
* channel, used only for 80+80 MHz bandwidth
* @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
@@ -2480,9 +2493,17 @@
* entry without having to force a disconnection after the PMK timeout. If
* no roaming occurs between the reauth threshold and PMK expiration,
* disassociation is still forced.
- *
* @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
* %NL80211_CMD_REGISTER_FRAME command, see the description there.
+ * @NL80211_ATTR_WIPHY_FREQ_OFFSET: offset of the associated
+ * %NL80211_ATTR_WIPHY_FREQ in positive KHz. Only valid when supplied with
+ * an %NL80211_ATTR_WIPHY_FREQ_OFFSET.
+ * @NL80211_ATTR_CENTER_FREQ1_OFFSET: Center frequency offset in KHz for the
+ * first channel segment specified in %NL80211_ATTR_CENTER_FREQ1.
+ * @NL80211_ATTR_SCAN_FREQ_KHZ: nested attribute with KHz frequencies
+ *
+ * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
+ * association request when used with NL80211_CMD_NEW_STATION).
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2960,6 +2981,11 @@
NL80211_ATTR_PMK_REAUTH_THRESHOLD,
NL80211_ATTR_RECEIVE_MULTICAST,
+ NL80211_ATTR_WIPHY_FREQ_OFFSET,
+ NL80211_ATTR_CENTER_FREQ1_OFFSET,
+ NL80211_ATTR_SCAN_FREQ_KHZ,
+
+ NL80211_ATTR_HE_6GHZ_CAPABILITY,
/* add attributes here, update the policy in nl80211.c */
@@ -3539,6 +3565,8 @@
* defined in HE capabilities IE
* @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently
* defined
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
+ * given for all 6 GHz band channels
* @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
*/
enum nl80211_band_iftype_attr {
@@ -3549,6 +3577,7 @@
NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+ NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
/* keep last */
__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -3682,6 +3711,7 @@
* (see &enum nl80211_wmm_rule)
* @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel
* in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_OFFSET: frequency offset in KHz
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -3712,6 +3742,7 @@
NL80211_FREQUENCY_ATTR_NO_10MHZ,
NL80211_FREQUENCY_ATTR_WMM,
NL80211_FREQUENCY_ATTR_NO_HE,
+ NL80211_FREQUENCY_ATTR_OFFSET,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4482,6 +4513,7 @@
* @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
* Contains a nested array of signal strength attributes (u8, dBm),
* using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -4506,6 +4538,7 @@
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
NL80211_BSS_CHAIN_SIGNAL,
+ NL80211_BSS_FREQUENCY_OFFSET,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4816,6 +4849,17 @@
NL80211_TID_CONFIG_DISABLE,
};
+/* enum nl80211_tx_rate_setting - TX rate configuration type
+ * @NL80211_TX_RATE_AUTOMATIC: automatically determine TX rate
+ * @NL80211_TX_RATE_LIMITED: limit the TX rate by the TX rate parameter
+ * @NL80211_TX_RATE_FIXED: fix TX rate to the TX rate parameter
+ */
+enum nl80211_tx_rate_setting {
+ NL80211_TX_RATE_AUTOMATIC,
+ NL80211_TX_RATE_LIMITED,
+ NL80211_TX_RATE_FIXED,
+};
+
/* enum nl80211_tid_config_attr - TID specific configuration.
* @NL80211_TID_CONFIG_ATTR_PAD: pad attribute for 64-bit values
* @NL80211_TID_CONFIG_ATTR_VIF_SUPP: a bitmap (u64) of attributes supported
@@ -4823,12 +4867,10 @@
* (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
* @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
* per peer instead.
- * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribute, if no peer
- * is selected, if set indicates that the new configuration overrides
- * all previous peer configurations, otherwise previous peer specific
- * configurations should be left untouched. If peer is selected then
- * it will reset particular TID configuration of that peer and it will
- * not accept other TID config attributes along with peer.
+ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
+ * that the new configuration overrides all previous peer
+ * configurations, otherwise previous peer specific configurations
+ * should be left untouched.
* @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs (bit 0 to 7)
* Its type is u16.
* @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID.
@@ -4844,12 +4886,23 @@
* &NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and
* the max value is advertised by the driver in this attribute on
* output in wiphy capabilities.
- * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable aggregation for the TIDs
- * specified in %NL80211_TID_CONFIG_ATTR_TIDS. Its type is u8, using
- * the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable MPDU aggregation
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ * Its type is u8, using the values from &nl80211_tid_config.
* @NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL: Enable/Disable RTS_CTS for the TIDs
* specified in %NL80211_TID_CONFIG_ATTR_TIDS. It is u8 type, using
* the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMSDU_CTRL: Enable/Disable MSDU aggregation
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ * Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE: This attribute will be useful
+ * to notfiy the driver that what type of txrate should be used
+ * for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS. using
+ * the values form &nl80211_tx_rate_setting.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE: Data frame TX rate mask should be applied
+ * with the parameters passed through %NL80211_ATTR_TX_RATES.
+ * configuration is applied to the data frame for the tid to that connected
+ * station.
*/
enum nl80211_tid_config_attr {
__NL80211_TID_CONFIG_ATTR_INVALID,
@@ -4863,6 +4916,9 @@
NL80211_TID_CONFIG_ATTR_RETRY_LONG,
NL80211_TID_CONFIG_ATTR_AMPDU_CTRL,
NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL,
+ NL80211_TID_CONFIG_ATTR_AMSDU_CTRL,
+ NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE,
+ NL80211_TID_CONFIG_ATTR_TX_RATE,
/* keep last */
__NL80211_TID_CONFIG_ATTR_AFTER_LAST,
@@ -5340,6 +5396,8 @@
#define NL80211_KCK_LEN 16
#define NL80211_KEK_LEN 16
+#define NL80211_KCK_EXT_LEN 24
+#define NL80211_KEK_EXT_LEN 32
#define NL80211_REPLAY_CTR_LEN 8
/**
@@ -5348,6 +5406,7 @@
* @NL80211_REKEY_DATA_KEK: key encryption key (binary)
* @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
* @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NL80211_REKEY_DATA_AKM: AKM data (OUI, suite type)
* @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
* @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
*/
@@ -5356,6 +5415,7 @@
NL80211_REKEY_DATA_KEK,
NL80211_REKEY_DATA_KCK,
NL80211_REKEY_DATA_REPLAY_CTR,
+ NL80211_REKEY_DATA_AKM,
/* keep last */
NUM_NL80211_REKEY_DATA,
@@ -5705,6 +5765,14 @@
* @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
* are possible for multicast frames and those will be reported properly.
*
+ * @NL80211_EXT_FEATURE_SCAN_FREQ_KHZ: This driver supports receiving and
+ * reporting scan request with %NL80211_ATTR_SCAN_FREQ_KHZ. In order to
+ * report %NL80211_ATTR_SCAN_FREQ_KHZ, %NL80211_SCAN_FLAG_FREQ_KHZ must be
+ * included in the scan request.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS: The driver
+ * can report tx status for control port over nl80211 tx operations.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -5758,6 +5826,8 @@
NL80211_EXT_FEATURE_DEL_IBSS_STA,
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT,
+ NL80211_EXT_FEATURE_SCAN_FREQ_KHZ,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -5869,6 +5939,9 @@
* @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to
* only have supported rates and no additional capabilities (unless
* added by userspace explicitly.)
+ * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
+ * %NL80211_ATTR_SCAN_FREQ_KHZ. This also means
+ * %NL80211_ATTR_SCAN_FREQUENCIES will not be included.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
@@ -5884,6 +5957,7 @@
NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
NL80211_SCAN_FLAG_RANDOM_SN = 1<<11,
NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12,
+ NL80211_SCAN_FLAG_FREQ_KHZ = 1<<13,
};
/**
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
index f00b438..fd058a0 100644
--- a/src/eap_common/Makefile
+++ b/src/eap_common/Makefile
@@ -1,13 +1,3 @@
-all: libeap_common.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_common.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
LIB_OBJS= \
chap.o \
eap_common.o \
@@ -25,7 +15,4 @@
eap_wsc_common.o \
ikev2_common.o
-libeap_common.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eap_peer/.gitignore b/src/eap_peer/.gitignore
new file mode 100644
index 0000000..140f8cf
--- /dev/null
+++ b/src/eap_peer/.gitignore
@@ -0,0 +1 @@
+*.so
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
index 6531ccd..bdbead6 100644
--- a/src/eap_peer/Makefile
+++ b/src/eap_peer/Makefile
@@ -1,23 +1,13 @@
-all: libeap_peer.a
-
-clean:
- rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov libeap_peer.a
-
-install:
- if ls *.so >/dev/null 2>&1; then \
- install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
- cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
- ; fi
-
-include ../lib.rules
-
CFLAGS += -DIEEE8021X_EAPOL
LIB_OBJS= \
eap.o \
eap_methods.o
-libeap_peer.a: $(LIB_OBJS)
- $(AR) crT $@ $?
+include ../lib.rules
--include $(OBJS:%.o=%.d)
+install:
+ if ls *.so >/dev/null 2>&1; then \
+ install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+ cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+ ; fi
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index eaa1ad7..0986627 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -520,6 +520,12 @@
wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+ if (identity) {
+ wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
+ identity, identity_len);
+ eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+ identity, identity_len);
+ }
if (!data->reauth) {
wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT",
data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
@@ -531,13 +537,6 @@
data->selected_version, NULL, 0);
}
- if (identity) {
- wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY",
- identity, identity_len);
- eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
- identity, identity_len);
- }
-
resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
if (resp)
eap_sim_state(data, START_DONE);
diff --git a/src/eap_peer/eap_teap.c b/src/eap_peer/eap_teap.c
index 76179a3..e8cc784 100644
--- a/src/eap_peer/eap_teap.c
+++ b/src/eap_peer/eap_teap.c
@@ -1388,6 +1388,15 @@
"EAP-TEAP: PAC used - server may decide to skip inner authentication");
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_COND_SUCC;
+ } else if (data->result_success_done &&
+ tls_connection_get_own_cert_used(data->ssl.conn) &&
+ eap_teap_derive_msk(data) == 0) {
+ /* Assume the server might accept authentication without going
+ * through inner authentication. */
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Client certificate used - server may decide to skip inner authentication");
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_COND_SUCC;
}
if (tlv.pac) {
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
index 1172b72..cc9b76d 100644
--- a/src/eap_server/Makefile
+++ b/src/eap_server/Makefile
@@ -1,13 +1,3 @@
-all: libeap_server.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_server.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_HS20
LIB_OBJS= \
@@ -15,7 +5,4 @@
eap_server_identity.o \
eap_server_methods.o
-libeap_server.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
index d7b1b09..691b44a 100644
--- a/src/eap_server/eap_server_teap.c
+++ b/src/eap_server/eap_server_teap.c
@@ -64,7 +64,7 @@
struct wpabuf *pending_phase2_resp;
struct wpabuf *server_outer_tlvs;
struct wpabuf *peer_outer_tlvs;
- u8 *identity; /* from PAC-Opaque */
+ u8 *identity; /* from PAC-Opaque or client certificate */
size_t identity_len;
int eap_seq;
int tnc_started;
@@ -365,7 +365,9 @@
data->teap_version = EAP_TEAP_VERSION;
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl,
+ sm->cfg->eap_teap_auth == 2 ? 2 : 0,
+ EAP_TYPE_TEAP)) {
wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL.");
eap_teap_reset(sm, data);
return NULL;
@@ -502,6 +504,19 @@
wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
+ if (!data->identity && sm->cfg->eap_teap_auth == 2) {
+ const char *subject;
+
+ subject = tls_connection_get_peer_subject(data->ssl.conn);
+ if (subject) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TEAP: Peer subject from Phase 1 client certificate: '%s'",
+ subject);
+ data->identity = (u8 *) os_strdup(subject);
+ data->identity_len = os_strlen(subject);
+ }
+ }
+
data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn);
wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
data->tls_cs);
@@ -1775,9 +1790,10 @@
next_vendor = EAP_VENDOR_IETF;
next_type = EAP_TYPE_NONE;
eap_teap_state(data, PHASE2_METHOD);
- } else if (sm->cfg->eap_teap_pac_no_inner) {
+ } else if (sm->cfg->eap_teap_pac_no_inner ||
+ sm->cfg->eap_teap_auth == 2) {
wpa_printf(MSG_DEBUG,
- "EAP-TEAP: Used PAC and identity already known - skip inner auth");
+ "EAP-TEAP: Used PAC or client certificate and identity already known - skip inner auth");
data->skipped_inner_auth = 1;
/* FIX: Need to derive CMK here. However, how is that
* supposed to be done? RFC 7170 does not tell that for
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
index 7b927a1..c82042f 100644
--- a/src/eapol_auth/Makefile
+++ b/src/eapol_auth/Makefile
@@ -1,16 +1,2 @@
-all: libeapol_auth.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_auth.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
LIB_OBJS = eapol_auth_sm.o eapol_auth_dump.o
-
-libeapol_auth.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index e3a57e7..1c11cb6 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -56,6 +56,7 @@
}
+PRINTF_FORMAT(4, 5)
static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
const u8 *addr, eapol_logger_level level,
const char *fmt, ...)
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
index 80db9d4..67a1185 100644
--- a/src/eapol_supp/Makefile
+++ b/src/eapol_supp/Makefile
@@ -1,18 +1,5 @@
-all: libeapol_supp.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_supp.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DIEEE8021X_EAPOL
LIB_OBJS = eapol_supp_sm.o
-libeapol_supp.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
index 47925b7..c616626 100644
--- a/src/l2_packet/Makefile
+++ b/src/l2_packet/Makefile
@@ -1,16 +1,3 @@
-all: libl2_packet.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libl2_packet.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
LIB_OBJS = l2_packet_linux.o
-libl2_packet.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/lib.rules b/src/lib.rules
index a463154..947617b 100644
--- a/src/lib.rules
+++ b/src/lib.rules
@@ -1,10 +1,8 @@
-ifndef CC
-CC=gcc
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+_LIBMK := $(lastword $(wordlist 1,$(shell expr $(words $(MAKEFILE_LIST)) - 1),$(MAKEFILE_LIST)))
+_LIBNAME := $(notdir $(patsubst %/,%,$(dir $(abspath $(_LIBMK)))))
+ALL := $(OUT)lib$(_LIBNAME).a
+LIB_RULES := $(lastword $(MAKEFILE_LIST))
+include $(dir $(LIB_RULES))build.rules
ifdef TEST_FUZZ
CFLAGS += -DCONFIG_NO_RANDOM_POOL
@@ -14,18 +12,18 @@
CFLAGS += $(FUZZ_CFLAGS)
CFLAGS += -I.. -I../utils
+_OBJS_VAR := LIB_OBJS
+include ../objs.mk
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
+$(ALL): $(LIB_OBJS)
+ @$(E) " AR $(notdir $@)"
+ $(Q)$(AR) crT $@ $?
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
+install-default:
+ @echo Nothing to be made.
+
+%: %-default
+ @true
+
+clean: common-clean
+ $(Q)rm -f *~ *.o *.d *.gcno *.gcda *.gcov $(ALL)
diff --git a/src/objs.mk b/src/objs.mk
new file mode 100644
index 0000000..a3040b2
--- /dev/null
+++ b/src/objs.mk
@@ -0,0 +1,3 @@
+$(_OBJS_VAR) := $(call BUILDOBJ,$($(_OBJS_VAR)))
+-include $(filter-out %.a,$($(_OBJS_VAR):%.o=%.d))
+_DIRS += $(dir $($(_OBJS_VAR)))
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
index 5587fcf..4d16180 100644
--- a/src/p2p/Makefile
+++ b/src/p2p/Makefile
@@ -1,13 +1,3 @@
-all: libp2p.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_WIFI_DISPLAY
CFLAGS += -DCONFIG_WPS_NFC
@@ -23,7 +13,4 @@
p2p_sd.o \
p2p_utils.o
-libp2p.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 1dc5b92..360ca60 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -3984,6 +3984,7 @@
}
p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
p2p_listen_in_find(p2p, 0);
}
diff --git a/src/radius/Makefile b/src/radius/Makefile
index 3ad4751..8cfb33d 100644
--- a/src/radius/Makefile
+++ b/src/radius/Makefile
@@ -1,14 +1,3 @@
-all: libradius.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_IPV6
LIB_OBJS= \
@@ -17,7 +6,4 @@
radius_das.o \
radius_server.o
-libradius.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index eea0efb..d14d736 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -1,13 +1,3 @@
-all: librsn_supp.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov librsn_supp.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_IEEE80211R
CFLAGS += -DCONFIG_TDLS
CFLAGS += -DCONFIG_WNM
@@ -21,7 +11,4 @@
wpa.o \
wpa_ie.o
-librsn_supp.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index e46c89a..b124dd9 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -272,7 +272,8 @@
entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
entry->pmk, entry->pmk_len,
pmksa->sm->dot11RSNAConfigPMKLifetime,
- pmksa->sm->dot11RSNAConfigPMKReauthThreshold);
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold,
+ entry->akmp);
return entry;
}
@@ -488,6 +489,9 @@
{
if (sm == NULL)
return;
+ if (sm->cur_pmksa)
+ wpa_printf(MSG_DEBUG,
+ "RSN: Clear current PMKSA entry selection");
sm->cur_pmksa = NULL;
}
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6c49fa9..83faa05 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -28,6 +28,7 @@
*/
u8 fils_cache_id[2];
unsigned int fils_cache_id_set:1;
+ unsigned int dpp_pfs:1;
os_time_t reauth_time;
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index d4d1307..1a38bf6 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -349,7 +349,8 @@
* PMKIDs again, so report the existing data now. */
if (p) {
wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid,
- NULL, p->pmk, p->pmk_len, 0, 0);
+ NULL, p->pmk, p->pmk_len, 0, 0,
+ p->akmp);
}
dl_list_del(&candidate->list);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 7b47e3a..eff8cd8 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2807,6 +2807,11 @@
if (sm == NULL)
return -1;
+ if (sm->l2_tdls) {
+ l2_packet_deinit(sm->l2_tdls);
+ sm->l2_tdls = NULL;
+ }
+
sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
sm->ifname,
sm->own_addr,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 33e7f41..6c6de80 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -21,6 +21,8 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
+#include "common/dpp.h"
+#include "common/wpa_ctrl.h"
#include "eap_common/eap_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "drivers/driver.h"
@@ -737,7 +739,8 @@
kde_buf = os_malloc(kde_len +
2 + RSN_SELECTOR_LEN + 3 +
sm->assoc_rsnxe_len +
- 2 + RSN_SELECTOR_LEN + 1);
+ 2 + RSN_SELECTOR_LEN + 1 +
+ 2 + RSN_SELECTOR_LEN + 2);
if (!kde_buf)
goto failed;
os_memcpy(kde_buf, kde, kde_len);
@@ -754,6 +757,14 @@
"Failed to get channel info for OCI element in EAPOL-Key 2/4");
goto failed;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->oci_freq_override_eapol) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %d MHz",
+ ci.frequency, sm->oci_freq_override_eapol);
+ ci.frequency = sm->oci_freq_override_eapol;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
if (ocv_insert_oci_kde(&ci, &pos) < 0)
goto failed;
@@ -782,6 +793,27 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && sm->key_mgmt == WPA_KEY_MGMT_DPP) {
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "DPP: Add DPP KDE into EAPOL-Key 2/4");
+ pos = kde + kde_len;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 2;
+ RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_DPP);
+ pos += RSN_SELECTOR_LEN;
+ *pos++ = DPP_VERSION; /* Protocol Version */
+ *pos = 0; /* Flags */
+ if (sm->dpp_pfs == 0)
+ *pos |= DPP_KDE_PFS_ALLOWED;
+ else if (sm->dpp_pfs == 1)
+ *pos |= DPP_KDE_PFS_ALLOWED | DPP_KDE_PFS_REQUIRED;
+ pos++;
+ kde_len = pos - kde;
+ }
+#endif /* CONFIG_DPP2 */
+
if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
kde, kde_len, ptk) < 0)
goto failed;
@@ -1685,14 +1717,29 @@
if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
- ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-m3 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1 && ie.dpp_kde) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: peer Protocol Version %u Flags 0x%x",
+ ie.dpp_kde[0], ie.dpp_kde[1]);
+ if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_pfs != 2 &&
+ (ie.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) && !sm->dpp_z) {
+ wpa_printf(MSG_INFO,
+ "DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
+ goto failed;
+ }
+ }
+#endif /* CONFIG_DPP2 */
+
if (sm->use_ext_key_id &&
wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
goto failed;
@@ -1818,9 +1865,10 @@
if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
- ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=eapol-key-g1 error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
return -1;
}
}
@@ -1991,6 +2039,15 @@
os_free(rbuf);
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->oci_freq_override_eapol_g2) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %d MHz",
+ ci.frequency,
+ sm->oci_freq_override_eapol_g2);
+ ci.frequency = sm->oci_freq_override_eapol_g2;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
pos = key_mic + mic_len + 2; /* Key Data */
if (ocv_insert_oci_kde(&ci, &pos) < 0) {
@@ -2394,13 +2451,16 @@
u8 *tmp = NULL;
int ret = -1;
u8 *mic, *key_data;
- size_t mic_len, keyhdrlen;
+ size_t mic_len, keyhdrlen, pmk_len;
#ifdef CONFIG_IEEE80211R
sm->ft_completed = 0;
#endif /* CONFIG_IEEE80211R */
- mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
+ pmk_len = sm->pmk_len;
+ if (!pmk_len && sm->cur_pmksa)
+ pmk_len = sm->cur_pmksa->pmk_len;
+ mic_len = wpa_mic_len(sm->key_mgmt, pmk_len);
keyhdrlen = sizeof(*key) + mic_len + 2;
if (len < sizeof(*hdr) + keyhdrlen) {
@@ -3241,6 +3301,9 @@
case WPA_PARAM_SAE_PWE:
sm->sae_pwe = value;
break;
+ case WPA_PARAM_SAE_PK:
+ sm->sae_pk = value;
+ break;
case WPA_PARAM_DENY_PTK0_REKEY:
sm->wpa_deny_ptk0_rekey = value;
break;
@@ -3254,7 +3317,24 @@
case WPA_PARAM_FT_RSNXE_USED:
sm->ft_rsnxe_used = value;
break;
+ case WPA_PARAM_OCI_FREQ_EAPOL:
+ sm->oci_freq_override_eapol = value;
+ break;
+ case WPA_PARAM_OCI_FREQ_EAPOL_G2:
+ sm->oci_freq_override_eapol_g2 = value;
+ break;
+ case WPA_PARAM_OCI_FREQ_FT_ASSOC:
+ sm->oci_freq_override_ft_assoc = value;
+ break;
+ case WPA_PARAM_OCI_FREQ_FILS_ASSOC:
+ sm->oci_freq_override_fils_assoc = value;
+ break;
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+ case WPA_PARAM_DPP_PFS:
+ sm->dpp_pfs = value;
+ break;
+#endif /* CONFIG_DPP2 */
default:
break;
}
@@ -3292,6 +3372,15 @@
return pos - buf;
pos += ret;
+#ifdef CONFIG_DPP2
+ if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+ ret = os_snprintf(pos, end - pos, "dpp_pfs=1\n");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_DPP2 */
+
if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
struct wpa_ie_data rsn;
if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
@@ -4379,10 +4468,6 @@
return -1;
}
sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
- wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
- sm->pmk_r0, sm->pmk_r0_len);
- wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
- sm->pmk_r0_name, WPA_PMK_NAME_LEN);
wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
MAC2STR(sm->r1kh_id));
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
@@ -4391,8 +4476,6 @@
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
- wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
- WPA_PMK_NAME_LEN);
os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
@@ -4503,6 +4586,15 @@
wpabuf_free(buf);
return NULL;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->oci_freq_override_fils_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %d MHz",
+ ci.frequency,
+ sm->oci_freq_override_fils_assoc);
+ ci.frequency = sm->oci_freq_override_fils_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
if (ocv_insert_extended_oci(&ci, pos) < 0) {
@@ -4707,8 +4799,10 @@
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=fils-assoc error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
goto fail;
}
}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 0986c6c..2142772 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -43,7 +43,7 @@
int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
const u8 *pmkid, const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len, u32 pmk_lifetime,
- u8 pmk_reauth_threshold);
+ u8 pmk_reauth_threshold, int akmp);
int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
const u8 *pmkid, const u8 *fils_cache_id);
void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
@@ -103,10 +103,16 @@
WPA_PARAM_MFP,
WPA_PARAM_OCV,
WPA_PARAM_SAE_PWE,
+ WPA_PARAM_SAE_PK,
WPA_PARAM_DENY_PTK0_REKEY,
WPA_PARAM_EXT_KEY_ID,
WPA_PARAM_USE_EXT_KEY_ID,
WPA_PARAM_FT_RSNXE_USED,
+ WPA_PARAM_DPP_PFS,
+ WPA_PARAM_OCI_FREQ_EAPOL,
+ WPA_PARAM_OCI_FREQ_EAPOL_G2,
+ WPA_PARAM_OCI_FREQ_FT_ASSOC,
+ WPA_PARAM_OCI_FREQ_FILS_ASSOC,
};
struct rsn_supp_config {
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3e51cf2..bf73376 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -15,6 +15,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
+#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "wpa.h"
#include "wpa_i.h"
@@ -50,17 +51,11 @@
sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
- sm->pmk_r0_name, WPA_PMK_NAME_LEN);
sm->pmk_r1_len = sm->pmk_r0_len;
if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
- WPA_PMK_NAME_LEN);
return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
ptk_name, sm->key_mgmt, sm->pairwise_cipher);
@@ -358,6 +353,14 @@
os_free(buf);
return NULL;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->oci_freq_override_ft_assoc) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %d MHz",
+ ci.frequency, sm->oci_freq_override_ft_assoc);
+ ci.frequency = sm->oci_freq_override_ft_assoc;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
*pos++ = FTIE_SUBELEM_OCI;
*pos++ = OCV_OCI_LEN;
@@ -641,9 +644,6 @@
sm->pmk_r1_name) < 0)
return -1;
sm->pmk_r1_len = sm->pmk_r0_len;
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
- sm->pmk_r1_name, WPA_PMK_NAME_LEN);
bssid = target_ap;
if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
@@ -1167,8 +1167,10 @@
if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
+ "addr=" MACSTR " frame=ft-assoc error=%s",
+ MAC2STR(sm->bssid), ocv_errorstr);
return -1;
}
}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 497d128..96b07fc 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -95,6 +95,7 @@
int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
int ocv; /* Operating Channel Validation */
int sae_pwe; /* SAE PWE generation options */
+ int sae_pk; /* whether SAE-PK is used */
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
@@ -154,6 +155,10 @@
#ifdef CONFIG_TESTING_OPTIONS
struct wpabuf *test_assoc_ie;
int ft_rsnxe_used;
+ unsigned int oci_freq_override_eapol;
+ unsigned int oci_freq_override_eapol_g2;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS
@@ -183,6 +188,7 @@
#ifdef CONFIG_DPP2
struct wpabuf *dpp_z;
+ int dpp_pfs;
#endif /* CONFIG_DPP2 */
};
@@ -266,12 +272,12 @@
const u8 *bssid, const u8 *pmkid,
const u8 *cache_id, const u8 *pmk,
size_t pmk_len, u32 pmk_lifetime,
- u8 pmk_reauth_threshold)
+ u8 pmk_reauth_threshold, int akmp)
{
WPA_ASSERT(sm->ctx->add_pmkid);
return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
cache_id, pmk, pmk_len, pmk_lifetime,
- pmk_reauth_threshold);
+ pmk_reauth_threshold, akmp);
}
static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 9068781..20fdd69 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -357,7 +357,7 @@
if (!wpa_key_mgmt_sae(sm->key_mgmt))
return 0; /* SAE not in use */
- if (sm->sae_pwe != 1 && sm->sae_pwe != 2)
+ if (sm->sae_pwe != 1 && sm->sae_pwe != 2 && !sm->sae_pk)
return 0; /* no supported extended RSN capabilities */
if (rsnxe_len < 3)
@@ -367,7 +367,12 @@
*pos++ = 1;
/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
* used for now */
- *pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ *pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+#ifdef CONFIG_SAE_PK
+ if (sm->sae_pk)
+ *pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
+#endif /* CONFIG_SAE_PK */
+ pos++;
return pos - rsnxe;
}
diff --git a/src/tls/Makefile b/src/tls/Makefile
index 52a890a..c84fbe8 100644
--- a/src/tls/Makefile
+++ b/src/tls/Makefile
@@ -1,14 +1,3 @@
-all: libtls.a
-
-clean:
- rm -f *~ *.o *.d libtls.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
CFLAGS += -DCONFIG_CRYPTO_INTERNAL
CFLAGS += -DCONFIG_TLSV11
@@ -33,8 +22,4 @@
tlsv1_server_write.o \
x509v3.o
-
-libtls.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 1ee2bee..e8ad997 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -1,14 +1,3 @@
-all: libutils.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
#CFLAGS += -DWPA_TRACE
CFLAGS += -DCONFIG_IPV6
CFLAGS += -DCONFIG_DEBUG_FILE
@@ -37,7 +26,4 @@
#LIB_OBJS += pcsc_funcs.o
-libutils.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/utils/base64.c b/src/utils/base64.c
index a17d2d3..0d121c1 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include <stdint.h>
+#include "utils/common.h"
#include "os.h"
#include "base64.h"
@@ -18,6 +19,10 @@
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+#define BASE64_PAD BIT(0)
+#define BASE64_LF BIT(1)
+
+
static char * base64_gen_encode(const unsigned char *src, size_t len,
size_t *out_len, const char *table, int add_pad)
{
@@ -29,7 +34,7 @@
if (len >= SIZE_MAX / 4)
return NULL;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
- if (add_pad)
+ if (add_pad & BASE64_LF)
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
@@ -49,7 +54,7 @@
*pos++ = table[in[2] & 0x3f];
in += 3;
line_len += 4;
- if (add_pad && line_len >= 72) {
+ if ((add_pad & BASE64_LF) && line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
@@ -59,19 +64,19 @@
*pos++ = table[(in[0] >> 2) & 0x3f];
if (end - in == 1) {
*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
- if (add_pad)
+ if (add_pad & BASE64_PAD)
*pos++ = '=';
} else {
*pos++ = table[(((in[0] & 0x03) << 4) |
(in[1] >> 4)) & 0x3f];
*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
}
- if (add_pad)
+ if (add_pad & BASE64_PAD)
*pos++ = '=';
line_len += 4;
}
- if (add_pad && line_len)
+ if ((add_pad & BASE64_LF) && line_len)
*pos++ = '\n';
*pos = '\0';
@@ -164,7 +169,14 @@
*/
char * base64_encode(const void *src, size_t len, size_t *out_len)
{
- return base64_gen_encode(src, len, out_len, base64_table, 1);
+ return base64_gen_encode(src, len, out_len, base64_table,
+ BASE64_PAD | BASE64_LF);
+}
+
+
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len)
+{
+ return base64_gen_encode(src, len, out_len, base64_table, BASE64_PAD);
}
diff --git a/src/utils/base64.h b/src/utils/base64.h
index 6216f44..d545b29 100644
--- a/src/utils/base64.h
+++ b/src/utils/base64.h
@@ -10,6 +10,7 @@
#define BASE64_H
char * base64_encode(const void *src, size_t len, size_t *out_len);
+char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len);
unsigned char * base64_decode(const char *src, size_t len, size_t *out_len);
char * base64_url_encode(const void *src, size_t len, size_t *out_len);
unsigned char * base64_url_decode(const char *src, size_t len, size_t *out_len);
diff --git a/src/utils/common.h b/src/utils/common.h
index 8e5cfe1..45f72bb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -482,7 +482,8 @@
void inc_byte_array(u8 *counter, size_t len);
void buf_shift_right(u8 *buf, size_t len, size_t bits);
void wpa_get_ntp_timestamp(u8 *buf);
-int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+ PRINTF_FORMAT(3, 4);
int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
char sep);
int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
diff --git a/src/utils/json.c b/src/utils/json.c
index 5a0edf2..dd12f1b 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -528,6 +528,28 @@
}
+struct wpabuf * json_get_member_base64(struct json_token *json,
+ const char *name)
+{
+ struct json_token *token;
+ unsigned char *buf;
+ size_t buflen;
+ struct wpabuf *ret;
+
+ token = json_get_member(json, name);
+ if (!token || token->type != JSON_STRING)
+ return NULL;
+ buf = base64_decode(token->string, os_strlen(token->string), &buflen);
+ if (!buf)
+ return NULL;
+ ret = wpabuf_alloc_ext_data(buf, buflen);
+ if (!ret)
+ os_free(buf);
+
+ return ret;
+}
+
+
static const char * json_type_str(enum json_type type)
{
switch (type) {
@@ -620,6 +642,20 @@
}
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+ size_t len)
+{
+ char *b64;
+
+ b64 = base64_encode_no_lf(val, len, NULL);
+ if (!b64)
+ return -1;
+ json_add_string(json, name, b64);
+ os_free(b64);
+ return 0;
+}
+
+
void json_start_object(struct wpabuf *json, const char *name)
{
if (name)
diff --git a/src/utils/json.h b/src/utils/json.h
index ca4a2e4..8448bb0 100644
--- a/src/utils/json.h
+++ b/src/utils/json.h
@@ -37,6 +37,8 @@
struct json_token * json_get_member(struct json_token *json, const char *name);
struct wpabuf * json_get_member_base64url(struct json_token *json,
const char *name);
+struct wpabuf * json_get_member_base64(struct json_token *json,
+ const char *name);
void json_print_tree(struct json_token *root, char *buf, size_t buflen);
void json_add_int(struct wpabuf *json, const char *name, int val);
void json_add_string(struct wpabuf *json, const char *name, const char *val);
@@ -44,6 +46,8 @@
const void *val, size_t len);
int json_add_base64url(struct wpabuf *json, const char *name, const void *val,
size_t len);
+int json_add_base64(struct wpabuf *json, const char *name, const void *val,
+ size_t len);
void json_start_object(struct wpabuf *json, const char *name);
void json_end_object(struct wpabuf *json);
void json_start_array(struct wpabuf *json, const char *name);
diff --git a/src/utils/list.h b/src/utils/list.h
index 85aa5e3..5298c26 100644
--- a/src/utils/list.h
+++ b/src/utils/list.h
@@ -46,12 +46,12 @@
item->prev = NULL;
}
-static inline int dl_list_empty(struct dl_list *list)
+static inline int dl_list_empty(const struct dl_list *list)
{
return list->next == list;
}
-static inline unsigned int dl_list_len(struct dl_list *list)
+static inline unsigned int dl_list_len(const struct dl_list *list)
{
struct dl_list *item;
int count = 0;
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index 01da41b..b2a54b2 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -71,6 +71,21 @@
}
/**
+ * wpabuf_cmp - Check if two buffers contain the same data
+ * @a: wpabuf buffer
+ * @b: wpabuf buffer
+ * Returns: 0 if the two buffers contain the same data and non-zero otherwise
+ */
+static inline int wpabuf_cmp(const struct wpabuf *a, const struct wpabuf *b)
+{
+ if (!a && !b)
+ return 0;
+ if (a && b && wpabuf_size(a) == wpabuf_size(b))
+ return os_memcmp(a->buf, b->buf, wpabuf_size(a));
+ return -1;
+}
+
+/**
* wpabuf_head - Get pointer to the head of the buffer data
* @buf: wpabuf buffer
* Returns: Pointer to the head of the buffer data
diff --git a/src/wps/Makefile b/src/wps/Makefile
index 4806fe8..cddc686 100644
--- a/src/wps/Makefile
+++ b/src/wps/Makefile
@@ -1,13 +1,3 @@
-all: libwps.a
-
-clean:
- rm -f *~ *.o *.d *.gcno *.gcda *.gcov libwps.a
-
-install:
- @echo Nothing to be made.
-
-include ../lib.rules
-
CFLAGS += -DCONFIG_P2P
CFLAGS += -DCONFIG_WPS_OOB
CFLAGS += -DCONFIG_WPS_NFC
@@ -35,7 +25,4 @@
wps_upnp_ssdp.o \
wps_upnp_web.o
-libwps.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
+include ../lib.rules
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 6bded14..31d2e50 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1298,7 +1298,7 @@
"with %s", filter);
}
if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text,
- er->mac_addr)) {
+ NULL, er->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
"for %s. Does it have IP address?", er->ifname);
wps_er_deinit(er, NULL, NULL);
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 6e10e4b..ff58cb9 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -303,6 +303,14 @@
}
+static int local_network_addr(struct upnp_wps_device_sm *sm,
+ struct sockaddr_in *addr)
+{
+ return (addr->sin_addr.s_addr & sm->netmask.s_addr) ==
+ (sm->ip_addr & sm->netmask.s_addr);
+}
+
+
/* subscr_addr_add_url -- add address(es) for one url to subscription */
static void subscr_addr_add_url(struct subscription *s, const char *url,
size_t url_len)
@@ -320,9 +328,14 @@
int rerr;
size_t host_len, path_len;
- /* url MUST begin with http: */
- if (url_len < 7 || os_strncasecmp(url, "http://", 7))
+ /* URL MUST begin with HTTP scheme. In addition, limit the length of
+ * the URL to 700 characters which is around the limit that was
+ * implicitly enforced for more than 10 years due to a bug in
+ * generating the event messages. */
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7) || url_len > 700) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Reject an unacceptable URL");
goto fail;
+ }
url += 7;
url_len -= 7;
@@ -381,6 +394,7 @@
for (rp = result; rp; rp = rp->ai_next) {
struct subscr_addr *a;
+ struct sockaddr_in *addr = (struct sockaddr_in *) rp->ai_addr;
/* Limit no. of address to avoid denial of service attack */
if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
@@ -389,6 +403,13 @@
break;
}
+ if (!local_network_addr(s->sm, addr)) {
+ wpa_printf(MSG_INFO,
+ "WPS UPnP: Ignore a delivery URL that points to another network %s",
+ inet_ntoa(addr->sin_addr));
+ continue;
+ }
+
a = os_zalloc(sizeof(*a) + alloc_len);
if (a == NULL)
break;
@@ -841,7 +862,7 @@
}
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
@@ -882,7 +903,7 @@
}
return 0;
}
-#endif /* __FreeBSD__ */
+#endif /* __FreeBSD__ || __APPLE__ */
/**
@@ -890,11 +911,12 @@
* @net_if: Selected network interface name
* @ip_addr: Buffer for returning IP address in network byte order
* @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @netmask: Buffer for returning netmask or %NULL if not needed
* @mac: Buffer for returning MAC address
* Returns: 0 on success, -1 on failure
*/
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
- u8 mac[ETH_ALEN])
+ struct in_addr *netmask, u8 mac[ETH_ALEN])
{
struct ifreq req;
int sock = -1;
@@ -920,6 +942,19 @@
in_addr.s_addr = *ip_addr;
os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+ if (netmask) {
+ os_memset(&req, 0, sizeof(req));
+ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+ if (ioctl(sock, SIOCGIFNETMASK, &req) < 0) {
+ wpa_printf(MSG_ERROR,
+ "WPS UPnP: SIOCGIFNETMASK failed: %d (%s)",
+ errno, strerror(errno));
+ goto fail;
+ }
+ addr = (struct sockaddr_in *) &req.ifr_addr;
+ netmask->s_addr = addr->sin_addr.s_addr;
+ }
+
#ifdef __linux__
os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
@@ -928,7 +963,7 @@
goto fail;
}
os_memcpy(mac, req.ifr_addr.sa_data, 6);
-#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
if (eth_get(net_if, mac) < 0) {
wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
goto fail;
@@ -1026,11 +1061,15 @@
/* Determine which IP and mac address we're using */
if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
- sm->mac_addr)) {
+ &sm->netmask, sm->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
"for %s. Does it have IP address?", net_if);
goto fail;
}
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Local IP address %s netmask %s hwaddr "
+ MACSTR,
+ sm->ip_addr_text, inet_ntoa(sm->netmask),
+ MAC2STR(sm->mac_addr));
/* Listen for incoming TCP connections so that others
* can fetch our "xml files" from us.
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index cca3905..b6c9478 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -76,8 +76,10 @@
void upnp_er_remove_notification(struct wps_registrar *reg,
struct subscription *s)
{
+ bool was_sel_reg = s->selected_registrar;
+
s->selected_registrar = 0;
eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
- if (reg)
+ if (reg && was_sel_reg)
wps_registrar_selected_registrar_changed(reg, 0);
}
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index d7e6edc..c0d9e41 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -147,7 +147,8 @@
struct wpabuf *buf;
char *b;
- buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+ buf = wpabuf_alloc(1000 + os_strlen(e->addr->path) +
+ wpabuf_len(e->data));
if (buf == NULL)
return NULL;
wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
@@ -293,7 +294,7 @@
buf = event_build_message(e);
if (buf == NULL) {
- event_retry(e, 0);
+ event_addr_failure(e);
return -1;
}
@@ -301,7 +302,7 @@
event_http_cb, e);
if (e->http_event == NULL) {
wpabuf_free(buf);
- event_retry(e, 0);
+ event_addr_failure(e);
return -1;
}
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index e87a932..6ead7b4 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -128,6 +128,7 @@
u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
char *ip_addr_text; /* IP address of network i.f. we use */
unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+ struct in_addr netmask;
int multicast_sd; /* send multicast messages over this socket */
int ssdp_sd; /* receive discovery UPD packets on socket */
int ssdp_sd_registered; /* nonzero if we must unregister */
@@ -158,7 +159,7 @@
const u8 uuid[UUID_LEN]);
void subscr_addr_delete(struct subscr_addr *a);
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
- u8 mac[ETH_ALEN]);
+ struct in_addr *netmask, u8 mac[ETH_ALEN]);
/* wps_upnp_ssdp.c */
void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 37432d9..09b8a6d 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -68,6 +68,10 @@
L_CFLAGS += -DCONFIG_NO_ROAMING
endif
+ifeq ($(WIFI_PRIV_CMD_UPDATE_MBO_CELL_STATUS), enabled)
+L_CFLAGS += -DENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS
+endif
+
# Use Android specific directory for control interface sockets
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/vendor/wifi/wpa/sockets\"
L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/vendor/wifi/wpa/sockets\"
@@ -128,6 +132,7 @@
OBJS += wmm_ac.c
OBJS += op_classes.c
OBJS += rrm.c
+OBJS += robust_av.c
OBJS_p = wpa_passphrase.c
OBJS_p += src/utils/common.c
OBJS_p += src/utils/wpa_debug.c
@@ -265,6 +270,10 @@
ifdef CONFIG_SAE
L_CFLAGS += -DCONFIG_SAE
OBJS += src/common/sae.c
+ifdef CONFIG_SAE_PK
+L_CFLAGS += -DCONFIG_SAE_PK
+OBJS += src/common/sae_pk.c
+endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -277,6 +286,12 @@
ifdef CONFIG_DPP
L_CFLAGS += -DCONFIG_DPP
OBJS += src/common/dpp.c
+OBJS += src/common/dpp_auth.c
+OBJS += src/common/dpp_backup.c
+OBJS += src/common/dpp_crypto.c
+OBJS += src/common/dpp_pkex.c
+OBJS += src/common/dpp_reconfig.c
+OBJS += src/common/dpp_tcp.c
OBJS += dpp_supplicant.c
NEED_AES_SIV=y
NEED_HMAC_SHA256_KDF=y
@@ -1498,7 +1513,7 @@
ifdef CONFIG_CTRL_IFACE_HIDL
WPA_SUPPLICANT_USE_HIDL=y
L_CFLAGS += -DCONFIG_HIDL -DCONFIG_CTRL_IFACE_HIDL
-HIDL_INTERFACE_VERSION := 1.3
+HIDL_INTERFACE_VERSION := 1.4
endif
ifdef CONFIG_READLINE
@@ -1751,9 +1766,10 @@
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.1
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.2
LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.3
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.4
LOCAL_SHARED_LIBRARIES += libhidlbase libutils libbase
LOCAL_STATIC_LIBRARIES += libwpa_hidl
-LOCAL_VINTF_FRAGMENTS := hidl/$(HIDL_INTERFACE_VERSION)/manifest.xml
+LOCAL_VINTF_FRAGMENTS := hidl/$(HIDL_INTERFACE_VERSION)/android.hardware.wifi.supplicant.xml
ifeq ($(WIFI_HIDL_UNIFIED_SUPPLICANT_SERVICE_RC_ENTRY), true)
LOCAL_INIT_RC=hidl/$(HIDL_INTERFACE_VERSION)/android.hardware.wifi.supplicant-service.rc
endif
@@ -1819,6 +1835,7 @@
android.hardware.wifi.supplicant@1.1 \
android.hardware.wifi.supplicant@1.2 \
android.hardware.wifi.supplicant@1.3 \
+ android.hardware.wifi.supplicant@1.4 \
libbase \
libhidlbase \
libutils \
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 738b0bd..08522de 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -1,11 +1,24 @@
-ifndef CC
-CC=gcc
+BINALL=wpa_supplicant wpa_cli
+
+ifndef CONFIG_NO_WPA_PASSPHRASE
+BINALL += wpa_passphrase
endif
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
+ALL = $(BINALL)
+ALL += systemd/wpa_supplicant.service
+ALL += systemd/wpa_supplicant@.service
+ALL += systemd/wpa_supplicant-nl80211@.service
+ALL += systemd/wpa_supplicant-wired@.service
+ALL += dbus/fi.w1.wpa_supplicant1.service
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+ALL += libwpa_client.so
endif
+EXTRA_TARGETS=dynamic_eap_methods
+
+CONFIG_FILE=.config
+include ../src/build.rules
+
ifdef LIBS
# If LIBS is set with some global build system defaults, clone those for
# LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well.
@@ -26,8 +39,6 @@
CFLAGS += -I$(abspath ../src)
CFLAGS += -I$(abspath ../src/utils)
--include .config
-
ifndef CONFIG_NO_GITVER
# Add VERSION_STR postfix for builds from a git repository
ifeq ($(wildcard ../.git),../.git)
@@ -44,34 +55,6 @@
CONFIG_TDLS_TESTING=y
endif
-BINALL=wpa_supplicant wpa_cli
-
-ifndef CONFIG_NO_WPA_PASSPHRASE
-BINALL += wpa_passphrase
-endif
-
-ALL = $(BINALL)
-ALL += systemd/wpa_supplicant.service
-ALL += systemd/wpa_supplicant@.service
-ALL += systemd/wpa_supplicant-nl80211@.service
-ALL += systemd/wpa_supplicant-wired@.service
-ALL += dbus/fi.w1.wpa_supplicant1.service
-ifdef CONFIG_BUILD_WPA_CLIENT_SO
-ALL += libwpa_client.so
-endif
-
-
-all: verify_config $(ALL) dynamic_eap_methods
-
-verify_config:
- @if [ ! -r .config ]; then \
- echo 'Building wpa_supplicant requires a configuration file'; \
- echo '(.config). See README for more instructions. You can'; \
- echo 'run "cp defconfig .config" to create an example'; \
- echo 'configuration.'; \
- exit 1; \
- fi
-
mkconfig:
@if [ -f .config ]; then \
echo '.config exists - did not replace it'; \
@@ -107,6 +90,7 @@
OBJS += ../src/utils/crc32.o
OBJS += op_classes.o
OBJS += rrm.o
+OBJS += robust_av.o
OBJS_p = wpa_passphrase.o
OBJS_p += ../src/utils/common.o
OBJS_p += ../src/utils/wpa_debug.o
@@ -267,6 +251,10 @@
ifdef CONFIG_SAE
CFLAGS += -DCONFIG_SAE
OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_PK
+CFLAGS += -DCONFIG_SAE_PK
+OBJS += ../src/common/sae_pk.o
+endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
@@ -279,6 +267,12 @@
ifdef CONFIG_DPP
CFLAGS += -DCONFIG_DPP
OBJS += ../src/common/dpp.o
+OBJS += ../src/common/dpp_auth.o
+OBJS += ../src/common/dpp_backup.o
+OBJS += ../src/common/dpp_crypto.o
+OBJS += ../src/common/dpp_pkex.o
+OBJS += ../src/common/dpp_reconfig.o
+OBJS += ../src/common/dpp_tcp.o
OBJS += dpp_supplicant.o
NEED_AES_SIV=y
NEED_HMAC_SHA256_KDF=y
@@ -1075,7 +1069,7 @@
ifeq ($(CONFIG_TLS), wolfssl)
ifdef TLS_FUNCS
-CFLAGS += -DWOLFSSL_DER_LOAD -I/usr/local/include/wolfssl
+CFLAGS += -DWOLFSSL_DER_LOAD
OBJS += ../src/crypto/tls_wolfssl.o
endif
OBJS += ../src/crypto/crypto_wolfssl.o
@@ -1860,53 +1854,40 @@
CFLAGS += -DCONFIG_NO_TKIP
endif
-ifndef LDO
-LDO=$(CC)
-endif
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-endif
-
dynamic_eap_methods: $(EAPDYN)
-../src/drivers/build.wpa_supplicant:
- @if [ -f ../src/drivers/build.hostapd ]; then \
- $(MAKE) -C ../src/drivers clean; \
- fi
- @touch ../src/drivers/build.wpa_supplicant
-
-BCHECK=../src/drivers/build.wpa_supplicant
-
+_OBJS_VAR := OBJS_priv
+include ../src/objs.mk
wpa_priv: $(BCHECK) $(OBJS_priv)
$(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
@$(E) " LD " $@
-$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config
-
+_OBJS_VAR := OBJS
+include ../src/objs.mk
wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
@$(E) " LD " $@
+_OBJS_VAR := OBJS_t
+include ../src/objs.mk
eapol_test: $(OBJS_t)
$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
@$(E) " LD " $@
+_OBJS_VAR := OBJS_t2
+include ../src/objs.mk
preauth_test: $(OBJS_t2)
$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
@$(E) " LD " $@
+_OBJS_VAR := OBJS_p
+include ../src/objs.mk
wpa_passphrase: $(OBJS_p)
$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
@$(E) " LD " $@
+_OBJS_VAR := OBJS_c
+include ../src/objs.mk
wpa_cli: $(OBJS_c)
$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
@$(E) " LD " $@
@@ -1920,6 +1901,8 @@
LIBCTRLSO += ../src/utils/common.c
LIBCTRLSO += ../src/utils/wpa_debug.c
+_OBJS_VAR := LIBCTRL
+include ../src/objs.mk
libwpa_client.a: $(LIBCTRL)
$(Q)rm -f $@
$(Q)$(AR) crs $@ $?
@@ -1929,14 +1912,19 @@
@$(E) " CC $@ ($^)"
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
-libwpa_test1: libwpa_test.o libwpa_client.a
- $(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c)
+OBJS_wpatest := libwpa_test.o
+_OBJS_VAR := OBJS_wpatest
+include ../src/objs.mk
+libwpa_test1: $(OBJS_wpatest) libwpa_client.a
+ $(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 $(OBJS_wpatest) libwpa_client.a $(LIBS_c)
@$(E) " LD " $@
-libwpa_test2: libwpa_test.o libwpa_client.so
- $(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c)
+libwpa_test2: $(OBJS_wpatest) libwpa_client.so
+ $(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 $(OBJS_wpatest) -L. -lwpa_client $(LIBS_c)
@$(E) " LD " $@
+_OBJS_VAR := OBJS_nfc
+include ../src/objs.mk
nfc_pw_token: $(OBJS_nfc)
$(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS)
@$(E) " LD " $@
@@ -1973,16 +1961,6 @@
$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
-ifdef CONFIG_CODE_COVERAGE
-%.o: %.c
- @$(E) " CC " $<
- $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
-else
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-endif
-
%.service: %.service.in
$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
@$(E) " sed" $<
@@ -2024,15 +2002,16 @@
fips:
$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
-lcov-html: wpa_supplicant.gcda
- lcov -c -d .. > lcov.info
+.PHONY: lcov-html
+lcov-html: $(call BUILDOBJ,wpa_supplicant.gcda)
+ lcov -c -d $(BUILDDIR) > lcov.info
genhtml lcov.info --output-directory lcov-html
-clean:
+clean: common-clean
$(MAKE) -C ../src clean
$(MAKE) -C dbus clean
rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
- rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+ rm -f eap_*.so $(WINALL) eapol_test preauth_test
rm -f wpa_priv
rm -f nfc_pw_token
rm -f lcov.info
@@ -2040,5 +2019,3 @@
rm -f libwpa_client.a
rm -f libwpa_client.so
rm -f libwpa_test1 libwpa_test2
-
--include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 5f8c8f6..3c0431b 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -533,8 +533,9 @@
# Opportunistic Wireless Encryption (OWE)
CONFIG_OWE=y
-# Easy Connect (Device Provisioning Protocol - DPP R1)
+# Easy Connect (Device Provisioning Protocol - DPP R1 & R2)
CONFIG_DPP=y
+CONFIG_DPP2=y
# WPA3-Personal (SAE)
CONFIG_SAE=y
@@ -556,4 +557,7 @@
# be completely removed in a future release.
CONFIG_WEP=y
+# WPA3-Personal (SAE) PK (Public Key) mode
+CONFIG_SAE_PK=y
+
include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 6241682..ac88a7d 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -44,6 +44,27 @@
#endif /* CONFIG_WPS */
+static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode,
+ struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211AX
+ if (conf->ieee80211ax) {
+ struct he_capabilities *he_cap;
+
+ he_cap = &mode->he_capab[IEEE80211_MODE_AP];
+ if (he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))
+ return true;
+ }
+#endif /* CONFIG_IEEE80211AX */
+ if (mode->vht_capab & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ return true;
+ return false;
+}
+
+
static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf,
@@ -53,30 +74,37 @@
u8 center_chan = 0;
u8 channel = conf->channel;
#endif /* CONFIG_P2P */
+ u8 freq_seg_idx;
if (!conf->secondary_channel)
goto no_vht;
/* Use the maximum oper channel width if it's given. */
if (ssid->max_oper_chwidth)
- conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+ hostapd_set_oper_chwidth(conf, ssid->max_oper_chwidth);
- ieee80211_freq_to_chan(ssid->vht_center_freq2,
- &conf->vht_oper_centr_freq_seg1_idx);
+ if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) {
+ ieee80211_freq_to_chan(ssid->vht_center_freq2,
+ &freq_seg_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(conf, freq_seg_idx);
+ }
if (!ssid->p2p_group) {
- if (!ssid->vht_center_freq1 ||
- conf->vht_oper_chwidth == CHANWIDTH_USE_HT)
+ if (!ssid->vht_center_freq1)
goto no_vht;
ieee80211_freq_to_chan(ssid->vht_center_freq1,
- &conf->vht_oper_centr_freq_seg0_idx);
- wpa_printf(MSG_DEBUG, "VHT seg0 index %d for AP",
- conf->vht_oper_centr_freq_seg0_idx);
+ &freq_seg_idx);
+ hostapd_set_oper_centr_freq_seg0_idx(conf, freq_seg_idx);
+
+ wpa_printf(MSG_DEBUG,
+ "VHT seg0 index %d and seg1 index %d for AP",
+ hostapd_get_oper_centr_freq_seg0_idx(conf),
+ hostapd_get_oper_centr_freq_seg1_idx(conf));
return;
}
#ifdef CONFIG_P2P
- switch (conf->vht_oper_chwidth) {
+ switch (hostapd_get_oper_chwidth(conf)) {
case CHANWIDTH_80MHZ:
case CHANWIDTH_80P80MHZ:
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
@@ -96,14 +124,14 @@
* try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
* not supported.
*/
- conf->vht_oper_chwidth = CHANWIDTH_160MHZ;
+ hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
- if (center_chan) {
+ if (center_chan && is_chanwidth160_supported(mode, conf)) {
wpa_printf(MSG_DEBUG,
"VHT center channel %u for auto-selected 160 MHz bandwidth",
center_chan);
} else {
- conf->vht_oper_chwidth = CHANWIDTH_80MHZ;
+ hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
channel);
wpa_printf(MSG_DEBUG,
@@ -115,9 +143,9 @@
if (!center_chan)
goto no_vht;
- conf->vht_oper_centr_freq_seg0_idx = center_chan;
+ hostapd_set_oper_centr_freq_seg0_idx(conf, center_chan);
wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO",
- conf->vht_oper_centr_freq_seg0_idx);
+ hostapd_get_oper_centr_freq_seg0_idx(conf));
return;
#endif /* CONFIG_P2P */
@@ -125,9 +153,27 @@
wpa_printf(MSG_DEBUG,
"No VHT higher bandwidth support for the selected channel %d",
conf->channel);
- conf->vht_oper_centr_freq_seg0_idx =
- conf->channel + conf->secondary_channel * 2;
- conf->vht_oper_chwidth = CHANWIDTH_USE_HT;
+ hostapd_set_oper_centr_freq_seg0_idx(
+ conf, conf->channel + conf->secondary_channel * 2);
+ hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
+}
+
+
+static struct hostapd_hw_modes *
+wpa_supplicant_find_hw_mode(struct wpa_supplicant *wpa_s,
+ enum hostapd_hw_mode hw_mode)
+{
+ struct hostapd_hw_modes *mode = NULL;
+ int i;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ return mode;
}
@@ -144,9 +190,6 @@
return -1;
}
- /* TODO: enable HT40 if driver supports it;
- * drop to 11b if driver does not support 11g */
-
/*
* Enable HT20 if the driver supports it, by setting conf->ieee80211n
* and a mask of allowed capabilities within conf->ht_capab.
@@ -155,17 +198,28 @@
*/
if (wpa_s->hw.modes) {
struct hostapd_hw_modes *mode = NULL;
- int i, no_ht = 0;
+ int no_ht = 0;
wpa_printf(MSG_DEBUG,
"Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)",
ssid->frequency, conf->channel);
- for (i = 0; i < wpa_s->hw.num_modes; i++) {
- if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
- mode = &wpa_s->hw.modes[i];
- break;
- }
+ mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode);
+
+ /* May drop to IEEE 802.11b if the driver does not support IEEE
+ * 802.11g */
+ if (!mode && conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+ wpa_printf(MSG_INFO,
+ "Try downgrade to IEEE 802.11b as 802.11g is not supported by the current hardware");
+ mode = wpa_supplicant_find_hw_mode(wpa_s,
+ conf->hw_mode);
+ }
+
+ if (!mode) {
+ wpa_printf(MSG_ERROR,
+ "No match between requested and supported hw modes found");
+ return -1;
}
#ifdef CONFIG_HT_OVERRIDES
@@ -190,6 +244,14 @@
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET),
ssid->ht40);
conf->ieee80211n = 1;
+
+ if (ssid->ht40 &&
+ (mode->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ conf->secondary_channel = ssid->ht40;
+ else
+ conf->secondary_channel = 0;
+
#ifdef CONFIG_P2P
if (ssid->p2p_group &&
conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
@@ -231,16 +293,19 @@
HT_CAP_INFO_TX_STBC |
HT_CAP_INFO_MAX_AMSDU_SIZE);
+ /* check this before VHT, because setting oper chan
+ * width and friends is the same call for HE and VHT
+ * and checks if conf->ieee8021ax == 1 */
+ if (mode->he_capab[wpas_mode_to_ieee80211_mode(
+ ssid->mode)].he_supported &&
+ ssid->he)
+ conf->ieee80211ax = 1;
+
if (mode->vht_capab && ssid->vht) {
conf->ieee80211ac = 1;
conf->vht_capab |= mode->vht_capab;
wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
}
-
- if (mode->he_capab[wpas_mode_to_ieee80211_mode(
- ssid->mode)].he_supported &&
- ssid->he)
- conf->ieee80211ax = 1;
}
}
@@ -858,6 +923,9 @@
wpa_s->conf->wmm_ac_params,
sizeof(wpa_s->conf->wmm_ac_params));
+ os_memcpy(wpa_s->ap_iface->conf->tx_queue, wpa_s->conf->tx_queue,
+ sizeof(wpa_s->conf->tx_queue));
+
if (params.uapsd > 0) {
conf->bss[0]->wmm_enabled = 1;
conf->bss[0]->wmm_uapsd = 1;
@@ -1415,10 +1483,17 @@
struct csa_settings *settings)
{
#ifdef NEED_AP_MLME
- if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+ struct hostapd_iface *iface = NULL;
+
+ if (wpa_s->ap_iface)
+ iface = wpa_s->ap_iface;
+ else if (wpa_s->ifmsh)
+ iface = wpa_s->ifmsh;
+
+ if (!iface || !iface->bss[0])
return -1;
- return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
+ return hostapd_switch_channel(iface->bss[0], settings);
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c
index e53dc38..2f32644 100644
--- a/wpa_supplicant/blacklist.c
+++ b/wpa_supplicant/blacklist.c
@@ -26,6 +26,15 @@
if (wpa_s == NULL || bssid == NULL)
return NULL;
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->was_recently_reconfigured) {
+ wpa_blacklist_clear(wpa_s);
+ wpa_s->current_ssid->was_recently_reconfigured = false;
+ return NULL;
+ }
+
+ wpa_blacklist_update(wpa_s);
+
e = wpa_s->blacklist;
while (e) {
if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
@@ -56,16 +65,29 @@
int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e;
+ struct os_reltime now;
if (wpa_s == NULL || bssid == NULL)
return -1;
e = wpa_blacklist_get(wpa_s, bssid);
+ os_get_reltime(&now);
if (e) {
+ e->blacklist_start = now;
e->count++;
- wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
- "incremented to %d",
- MAC2STR(bssid), e->count);
+ if (e->count > 5)
+ e->timeout_secs = 1800;
+ else if (e->count == 5)
+ e->timeout_secs = 600;
+ else if (e->count == 4)
+ e->timeout_secs = 120;
+ else if (e->count == 3)
+ e->timeout_secs = 60;
+ else
+ e->timeout_secs = 10;
+ wpa_printf(MSG_INFO, "BSSID " MACSTR
+ " blacklist count incremented to %d, blacklisting for %d seconds",
+ MAC2STR(bssid), e->count, e->timeout_secs);
return e->count;
}
@@ -74,10 +96,13 @@
return -1;
os_memcpy(e->bssid, bssid, ETH_ALEN);
e->count = 1;
+ e->timeout_secs = 10;
+ e->blacklist_start = now;
e->next = wpa_s->blacklist;
wpa_s->blacklist = e;
- wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
- MAC2STR(bssid));
+ wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR
+ " into blacklist, blacklisting for %d seconds",
+ MAC2STR(bssid), e->timeout_secs);
return e->count;
}
@@ -117,25 +142,80 @@
/**
+ * wpa_blacklist_is_blacklisted - Check the blacklist status of a BSS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be checked
+ * Returns: count if BSS is currently considered to be blacklisted, 0 otherwise
+ */
+int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_blacklist *e;
+ struct os_reltime now;
+
+ e = wpa_blacklist_get(wpa_s, bssid);
+ if (!e)
+ return 0;
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &e->blacklist_start, e->timeout_secs))
+ return 0;
+ return e->count;
+}
+
+
+/**
* wpa_blacklist_clear - Clear the blacklist of all entries
* @wpa_s: Pointer to wpa_supplicant data
*/
void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
{
struct wpa_blacklist *e, *prev;
- int max_count = 0;
e = wpa_s->blacklist;
wpa_s->blacklist = NULL;
while (e) {
- if (e->count > max_count)
- max_count = e->count;
prev = e;
e = e->next;
wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
"blacklist (clear)", MAC2STR(prev->bssid));
os_free(prev);
}
+}
- wpa_s->extra_blacklist_count += max_count;
+
+/**
+ * wpa_blacklist_update - Update the entries in the blacklist,
+ * deleting entries that have been expired for over an hour.
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_blacklist_update(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_blacklist *e, *prev = NULL;
+ struct os_reltime now;
+
+ if (!wpa_s)
+ return;
+
+ e = wpa_s->blacklist;
+ os_get_reltime(&now);
+ while (e) {
+ if (os_reltime_expired(&now, &e->blacklist_start,
+ e->timeout_secs + 3600)) {
+ struct wpa_blacklist *to_delete = e;
+
+ if (prev) {
+ prev->next = e->next;
+ e = prev->next;
+ } else {
+ wpa_s->blacklist = e->next;
+ e = wpa_s->blacklist;
+ }
+ wpa_printf(MSG_INFO, "Removed BSSID " MACSTR
+ " from blacklist (expired)",
+ MAC2STR(to_delete->bssid));
+ os_free(to_delete);
+ } else {
+ prev = e;
+ e = e->next;
+ }
+ }
}
diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h
index ae06986..a1c60d5 100644
--- a/wpa_supplicant/blacklist.h
+++ b/wpa_supplicant/blacklist.h
@@ -13,12 +13,21 @@
struct wpa_blacklist *next;
u8 bssid[ETH_ALEN];
int count;
+ /* Time of most recent blacklist event. */
+ struct os_reltime blacklist_start;
+ /*
+ * Number of seconds after blacklist_start that the entry will be
+ * considered blacklisted.
+ */
+ int timeout_secs;
};
struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
const u8 *bssid);
int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_blacklist_is_blacklisted(struct wpa_supplicant *wpa_s, const u8 *bssid);
void wpa_blacklist_clear(struct wpa_supplicant *wpa_s);
+void wpa_blacklist_update(struct wpa_supplicant *wpa_s);
#endif /* BLACKLIST_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index e1d9824..0d5b285 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2573,6 +2573,7 @@
{ STR_LEN(dpp_netaccesskey) },
{ INT(dpp_netaccesskey_expiry) },
{ STR_LEN(dpp_csign) },
+ { STR_LEN(dpp_pp_key) },
{ INT_RANGE(dpp_pfs, 0, 2) },
#endif /* CONFIG_DPP */
{ INT_RANGE(owe_group, 0, 65535) },
@@ -2582,6 +2583,7 @@
{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
+ { INT_RANGE(sae_pk, 0, 2) },
};
#undef OFFSET
@@ -2775,6 +2777,7 @@
os_free(ssid->dpp_connector);
bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
os_free(ssid->dpp_csign);
+ os_free(ssid->dpp_pp_key);
while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
list))) {
dl_list_del(&psk->list);
@@ -3035,6 +3038,8 @@
ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
ssid->ht = 1;
+ ssid->vht = 1;
+ ssid->he = 1;
#ifdef IEEE8021X_EAPOL
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -3141,6 +3146,7 @@
}
ret = -1;
}
+ ssid->was_recently_reconfigured = true;
return ret;
}
@@ -4254,6 +4260,8 @@
struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param)
{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
struct wpa_config *config;
const int aCWmin = 4, aCWmax = 10;
const struct hostapd_wmm_ac_params ac_bk =
@@ -4261,9 +4269,20 @@
const struct hostapd_wmm_ac_params ac_be =
{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
- { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+ { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
- { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+ { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+ { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
+ const struct hostapd_tx_queue_params txq_vi =
+ { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
+ const struct hostapd_tx_queue_params txq_vo =
+ { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
+
+#undef ecw2cw
config = os_zalloc(sizeof(*config));
if (config == NULL)
@@ -4293,6 +4312,10 @@
config->wmm_ac_params[1] = ac_bk;
config->wmm_ac_params[2] = ac_vi;
config->wmm_ac_params[3] = ac_vo;
+ config->tx_queue[0] = txq_vo;
+ config->tx_queue[1] = txq_vi;
+ config->tx_queue[2] = txq_be;
+ config->tx_queue[3] = txq_bk;
config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
@@ -4305,6 +4328,7 @@
config->disassoc_imminent_rssi_threshold =
DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
config->oce = DEFAULT_OCE_SUPPORT;
+ config->btm_offload = DEFAULT_BTM_OFFLOAD;
#endif /* CONFIG_MBO */
if (ctrl_interface)
@@ -4342,13 +4366,31 @@
#endif /* CONFIG_NO_STDOUT_DEBUG */
+/**
+ * Structure for global configuration parsing. This data is used to implement a
+ * generic parser for the global interface configuration. The table of variables
+ * is defined below in this file (global_fields[]).
+ */
struct global_parse_data {
+ /* Configuration variable name */
char *name;
+
+ /* Parser function for this variable. The parser functions return 0 or 1
+ * to indicate success. Value 0 indicates that the parameter value may
+ * have changed while value 1 means that the value did not change.
+ * Error cases (failure to parse the string) are indicated by returning
+ * -1. */
int (*parser)(const struct global_parse_data *data,
struct wpa_config *config, int line, const char *value);
+
+ /* Getter function to print the variable in text format to buf. */
int (*get)(const char *name, struct wpa_config *config, long offset,
char *buf, size_t buflen, int pretty_print);
+
+ /* Variable specific parameters for the parser. */
void *param1, *param2, *param3;
+
+ /* Indicates which configuration variable has changed. */
unsigned int changed_flag;
};
@@ -4359,6 +4401,7 @@
{
int val, *dst;
char *end;
+ bool same;
dst = (int *) (((u8 *) config) + (long) data->param1);
val = strtol(pos, &end, 0);
@@ -4367,6 +4410,7 @@
line, pos);
return -1;
}
+ same = *dst == val;
*dst = val;
wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
@@ -4387,7 +4431,7 @@
return -1;
}
- return 0;
+ return same;
}
@@ -4395,7 +4439,7 @@
struct wpa_config *config, int line,
const char *pos)
{
- size_t len;
+ size_t len, prev_len;
char **dst, *tmp;
len = os_strlen(pos);
@@ -4419,11 +4463,22 @@
return -1;
}
+ dst = (char **) (((u8 *) config) + (long) data->param1);
+ if (*dst)
+ prev_len = os_strlen(*dst);
+ else
+ prev_len = 0;
+
+ /* No change to the previously configured value */
+ if ((!(*dst) && !pos) ||
+ (*dst && pos && prev_len == len &&
+ os_memcmp(*dst, pos, len) == 0))
+ return 1;
+
tmp = os_strdup(pos);
if (tmp == NULL)
return -1;
- dst = (char **) (((u8 *) config) + (long) data->param1);
os_free(*dst);
*dst = tmp;
wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
@@ -4464,6 +4519,10 @@
return -1;
dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+ if (wpabuf_cmp(*dst, tmp) == 0) {
+ wpabuf_free(tmp);
+ return 1;
+ }
wpabuf_free(*dst);
*dst = tmp;
wpa_printf(MSG_DEBUG, "%s", data->name);
@@ -4505,6 +4564,8 @@
return -1;
dst = (u32 *) (((u8 *) config) + (long) data->param1);
+ if (os_memcmp(dst, &addr.u.v4.s_addr, 4) == 0)
+ return 1;
os_memcpy(dst, &addr.u.v4.s_addr, 4);
wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
WPA_GET_BE32((u8 *) dst));
@@ -4522,6 +4583,8 @@
wpa_printf(MSG_DEBUG, "Invalid country set");
return -1;
}
+ if (pos[0] == config->country[0] && pos[1] == config->country[1])
+ return 1;
config->country[0] = pos[0];
config->country[1] = pos[1];
wpa_printf(MSG_DEBUG, "country='%c%c'",
@@ -4905,7 +4968,7 @@
{ INT_RANGE(eapol_version, 1, 2), 0 },
#endif /* CONFIG_MACSEC */
{ INT(ap_scan), 0 },
- { FUNC(bgscan), 0 },
+ { FUNC(bgscan), CFG_CHANGED_BGSCAN },
#ifdef CONFIG_MESH
{ INT(user_mpm), 0 },
{ INT_RANGE(max_peer_links, 0, 255), 0 },
@@ -4979,6 +5042,7 @@
{ INT(p2p_device_random_mac_addr), 0 },
{ FUNC(p2p_device_persistent_mac_addr), 0 },
{ INT(p2p_interface_random_mac_addr), 0 },
+ { INT(p2p_6ghz_disable), 0 },
#endif /* CONFIG_P2P */
{ FUNC(country), CFG_CHANGED_COUNTRY },
{ INT(bss_max_count), 0 },
@@ -5048,6 +5112,7 @@
MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
{ INT_RANGE(oce, 0, 3), 0 },
+ { INT_RANGE(btm_offload, 0, 1), CFG_CHANGED_DISABLE_BTM_NOTIFY },
#endif /* CONFIG_MBO */
{ INT(gas_address3), 0 },
{ INT_RANGE(ftm_responder, 0, 1), 0 },
@@ -5140,6 +5205,19 @@
}
+/**
+ * wpa_config_process_global - Set a variable in global configuration
+ * @config: Pointer to global configuration data
+ * @pos: Name and value in the format "{name}={value}"
+ * @line: Line number in configuration file or 0 if not used
+ * Returns: 0 on success with a possible change in value, 1 on success with no
+ * change to previously configured value, or -1 on failure
+ *
+ * This function can be used to set global configuration variables based on
+ * both the configuration file and management interface input. The value
+ * parameter must be in the same format as the text-based configuration file is
+ * using. For example, strings are using double quotation marks.
+ */
int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
{
size_t i;
@@ -5152,11 +5230,14 @@
pos[flen] != '=')
continue;
- if (field->parser(field, config, line, pos + flen + 1)) {
+ ret = field->parser(field, config, line, pos + flen + 1);
+ if (ret < 0) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse '%s'.", line, pos);
ret = -1;
}
+ if (ret == 1)
+ break;
if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
config->wps_nfc_pw_from_config = 1;
config->changed_parameters |= field->changed_flag;
@@ -5164,6 +5245,26 @@
}
if (i == NUM_GLOBAL_FIELDS) {
#ifdef CONFIG_AP
+ if (os_strncmp(pos, "tx_queue_", 9) == 0) {
+ char *tmp = os_strchr(pos, '=');
+
+ if (!tmp) {
+ if (line < 0)
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid line %s",
+ line, pos);
+ return -1;
+ }
+ *tmp++ = '\0';
+ if (hostapd_config_tx_queue(config->tx_queue, pos,
+ tmp)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid TX queue item",
+ line);
+ return -1;
+ }
+ }
+
if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
char *tmp = os_strchr(pos, '=');
if (tmp == NULL) {
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 0ca27cb..868a8b3 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -45,6 +45,7 @@
#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
#define DEFAULT_OCE_SUPPORT OCE_STA
#define DEFAULT_EXTENDED_KEY_ID 0
+#define DEFAULT_BTM_OFFLOAD 0
#include "config_ssid.h"
#include "wps/wps.h"
@@ -376,6 +377,8 @@
#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
#define CFG_CHANGED_DISABLE_BTM BIT(19)
+#define CFG_CHANGED_BGSCAN BIT(20)
+#define CFG_CHANGED_DISABLE_BTM_NOTIFY BIT(21)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -779,6 +782,8 @@
int p2p_ignore_shared_freq;
int p2p_optimize_listen_chan;
+ int p2p_6ghz_disable;
+
struct wpabuf *wps_vendor_ext_m1;
#define MAX_WPS_VENDOR_EXT 10
@@ -974,7 +979,7 @@
int go_venue_type;
/**
- * hessid - Homogenous ESS identifier
+ * hessid - Homogeneous ESS identifier
*
* If this is set (any octet is non-zero), scans will be used to
* request response only from BSSes belonging to the specified
@@ -1058,6 +1063,7 @@
int p2p_go_max_inactivity;
struct hostapd_wmm_ac_params wmm_ac_params[4];
+ struct hostapd_tx_queue_params tx_queue[4];
/**
* auto_interworking - Whether to use network selection automatically
@@ -1447,6 +1453,14 @@
* - Set BIT(1) to enable OCE in STA-CFON mode
*/
unsigned int oce;
+
+ /**
+ * btm_offload - Set where to perform roaming logic
+ * - Set to 0 to handle fully roaming logic in supplicant
+ * - Set to 1 to skip roaming logic in supplicant for firmware roaming
+ * just parse BTM frame and notify framework
+ */
+ int btm_offload;
#endif /* CONFIG_MBO */
/**
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 52e1372..74f91d3 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -876,9 +876,10 @@
write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
INT(disabled);
INT(mixed_cell);
- INT(vht);
+ INT_DEF(vht, 1);
INT_DEF(ht, 1);
INT(ht40);
+ INT_DEF(he, 1);
INT_DEF(max_oper_chwidth, DEFAULT_MAX_OPER_CHWIDTH);
INT(vht_center_freq1);
INT(vht_center_freq2);
@@ -928,6 +929,7 @@
STR(dpp_netaccesskey);
INT(dpp_netaccesskey_expiry);
STR(dpp_csign);
+ STR(dpp_pp_key);
INT(dpp_pfs);
#endif /* CONFIG_DPP */
INT(owe_group);
@@ -937,6 +939,7 @@
INT(ft_eap_pmksa_caching);
INT(beacon_prot);
INT(transition_disable);
+ INT(sae_pk);
#ifdef CONFIG_HT_OVERRIDES
INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
@@ -1332,6 +1335,10 @@
if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
fprintf(f, "p2p_go_freq_change_policy=%u\n",
config->p2p_go_freq_change_policy);
+
+ if (config->p2p_6ghz_disable)
+ fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable);
+
if (WPA_GET_BE32(config->ip_addr_go))
fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
config->ip_addr_go[0], config->ip_addr_go[1],
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 6737223..ecf258c 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -61,6 +61,12 @@
WPAS_MODE_MESH = 5,
};
+enum sae_pk_mode {
+ SAE_PK_MODE_AUTOMATIC = 0,
+ SAE_PK_MODE_ONLY = 1,
+ SAE_PK_MODE_DISABLED = 2,
+};
+
/**
* struct wpa_ssid - Network configuration data
*
@@ -1017,6 +1023,16 @@
size_t dpp_csign_len;
/**
+ * dpp_pp_key - ppKey (Configurator privacy protection public key)
+ */
+ u8 *dpp_pp_key;
+
+ /**
+ * dpp_pp_key_len - ppKey length in octets
+ */
+ size_t dpp_pp_key_len;
+
+ /**
* dpp_pfs - DPP PFS
* 0: allow PFS to be used or not used
* 1: require PFS to be used (note: not compatible with DPP R1)
@@ -1120,6 +1136,25 @@
* OWE)
*/
u8 transition_disable;
+
+ /**
+ * sae_pk - SAE-PK mode
+ * 0 = automatic SAE/SAE-PK selection based on password; enable
+ * transition mode (allow SAE authentication without SAE-PK)
+ * 1 = SAE-PK only (disable transition mode; allow SAE authentication
+ * only with SAE-PK)
+ * 2 = disable SAE-PK (allow SAE authentication only without SAE-PK)
+ */
+ enum sae_pk_mode sae_pk;
+
+ /**
+ * was_recently_reconfigured - Whether this SSID config has been changed
+ * recently
+ *
+ * This is an internally used variable, i.e., not used in external
+ * configuration.
+ */
+ bool was_recently_reconfigured;
};
#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index e0547f1..f5c6587 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -696,6 +696,8 @@
ret = -1;
else
dpp_nonce_override_len = hex_len / 2;
+ } else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
+ dpp_version_override = atoi(value);
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
@@ -749,6 +751,26 @@
}
} else if (os_strcasecmp(cmd, "ft_rsnxe_used") == 0) {
wpa_s->ft_rsnxe_used = atoi(value);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_eapol") == 0) {
+ wpa_s->oci_freq_override_eapol = atoi(value);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_saquery_req") == 0) {
+ wpa_s->oci_freq_override_saquery_req = atoi(value);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_saquery_resp") == 0) {
+ wpa_s->oci_freq_override_saquery_resp = atoi(value);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_eapol_g2") == 0) {
+ wpa_s->oci_freq_override_eapol_g2 = atoi(value);
+ /* Populate value to wpa_sm if already associated. */
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
+ wpa_s->oci_freq_override_eapol_g2);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_ft_assoc") == 0) {
+ wpa_s->oci_freq_override_ft_assoc = atoi(value);
+ /* Populate value to wpa_sm if already associated. */
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
+ wpa_s->oci_freq_override_ft_assoc);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_fils_assoc") == 0) {
+ wpa_s->oci_freq_override_fils_assoc = atoi(value);
+ } else if (os_strcasecmp(cmd, "oci_freq_override_wnm_sleep") == 0) {
+ wpa_s->oci_freq_override_wnm_sleep = atoi(value);
} else if (os_strcasecmp(cmd, "rsne_override_eapol") == 0) {
wpabuf_free(wpa_s->rsne_override_eapol);
if (os_strcmp(value, "NULL") == 0)
@@ -876,6 +898,8 @@
ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
if (ret == 0)
wpa_supplicant_update_config(wpa_s);
+ else if (ret == 1)
+ ret = 0;
}
return ret;
@@ -891,6 +915,8 @@
if (os_strcmp(cmd, "version") == 0) {
res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+ } else if (os_strcasecmp(cmd, "max_command_len") == 0) {
+ res = os_snprintf(buf, buflen, "%u", CTRL_IFACE_MAX_LEN);
} else if (os_strcasecmp(cmd, "country") == 0) {
if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
res = os_snprintf(buf, buflen, "%c%c",
@@ -2273,8 +2299,12 @@
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
wpa_s->sme.sae.state == SAE_ACCEPTED) {
- ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
- wpa_s->sme.sae.group);
+ ret = os_snprintf(pos, end - pos, "sae_group=%d\n"
+ "sae_h2e=%d\n"
+ "sae_pk=%d\n",
+ wpa_s->sme.sae.group,
+ wpa_s->sme.sae.h2e,
+ wpa_s->sme.sae.pk);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
@@ -2930,7 +2960,7 @@
{
char *pos, *end;
int ret;
- const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe;
+ const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe, *rsnxe;
mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
@@ -2957,6 +2987,21 @@
pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
ie2, 2 + ie2[1]);
}
+ rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ if (rsnxe && rsnxe[1] >= 1) {
+ if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)) {
+ ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
+ ret = os_snprintf(pos, end - pos, "[SAE-PK]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ }
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
@@ -3435,38 +3480,12 @@
struct wpa_supplicant *wpa_s, char *cmd)
{
int id;
- struct wpa_ssid *ssid;
int result;
/* cmd: "<network id>" or "all" */
if (os_strcmp(cmd, "all") == 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
- if (wpa_s->sched_scanning)
- wpa_supplicant_cancel_sched_scan(wpa_s);
-
- eapol_sm_invalidate_cached_session(wpa_s->eapol);
- if (wpa_s->current_ssid) {
-#ifdef CONFIG_SME
- wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
- wpa_sm_set_config(wpa_s->wpa, NULL);
- eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
- if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
- wpa_s->own_disconnect_req = 1;
- wpa_supplicant_deauthenticate(
- wpa_s, WLAN_REASON_DEAUTH_LEAVING);
- }
- ssid = wpa_s->conf->ssid;
- while (ssid) {
- struct wpa_ssid *remove_ssid = ssid;
- id = ssid->id;
- ssid = ssid->next;
- if (wpa_s->last_ssid == remove_ssid)
- wpa_s->last_ssid = NULL;
- wpas_notify_network_removed(wpa_s, remove_ssid);
- wpa_config_remove_network(wpa_s->conf, id);
- }
- return 0;
+ return wpa_supplicant_remove_all_networks(wpa_s);
}
id = atoi(cmd);
@@ -3502,6 +3521,20 @@
if (ret == 1)
return 0; /* No change to the previously configured value */
+#ifdef CONFIG_BGSCAN
+ if (os_strcmp(name, "bgscan") == 0) {
+ /*
+ * Reset the bgscan parameters for the current network and
+ * return. There's no need to flush caches for bgscan parameter
+ * changes.
+ */
+ if (wpa_s->current_ssid == ssid &&
+ wpa_s->wpa_state == WPA_COMPLETED)
+ wpa_supplicant_reset_bgscan(wpa_s);
+ return 0;
+ }
+#endif /* CONFIG_BGSCAN */
+
if (os_strcmp(name, "bssid") != 0 &&
os_strcmp(name, "bssid_hint") != 0 &&
os_strcmp(name, "priority") != 0) {
@@ -3977,7 +4010,7 @@
};
-static int ctrl_iface_get_capability_pairwise(int res, char *strict,
+static int ctrl_iface_get_capability_pairwise(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4017,7 +4050,7 @@
}
-static int ctrl_iface_get_capability_group(int res, char *strict,
+static int ctrl_iface_get_capability_group(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4065,7 +4098,7 @@
}
-static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+static int ctrl_iface_get_capability_group_mgmt(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4094,11 +4127,49 @@
}
-static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
+static int iftype_str_to_index(const char *iftype_str)
+{
+ if (!iftype_str)
+ return WPA_IF_MAX;
+
+ if (os_strcmp(iftype_str, "STATION") == 0)
+ return WPA_IF_STATION;
+
+ if (os_strcmp(iftype_str, "AP_VLAN") == 0)
+ return WPA_IF_AP_VLAN;
+
+ if (os_strcmp(iftype_str, "AP") == 0)
+ return WPA_IF_AP_BSS;
+
+ if (os_strcmp(iftype_str, "P2P_GO") == 0)
+ return WPA_IF_P2P_GO;
+
+ if (os_strcmp(iftype_str, "P2P_CLIENT") == 0)
+ return WPA_IF_P2P_CLIENT;
+
+ if (os_strcmp(iftype_str, "P2P_DEVICE") == 0)
+ return WPA_IF_P2P_DEVICE;
+
+ if (os_strcmp(iftype_str, "MESH") == 0)
+ return WPA_IF_MESH;
+
+ if (os_strcmp(iftype_str, "IBSS") == 0)
+ return WPA_IF_IBSS;
+
+ if (os_strcmp(iftype_str, "NAN") == 0)
+ return WPA_IF_NAN;
+
+ return WPA_IF_MAX;
+}
+
+
+static int ctrl_iface_get_capability_key_mgmt(int res, bool strict,
struct wpa_driver_capa *capa,
+ const char *iftype_str,
char *buf, size_t buflen)
{
int ret;
+ unsigned int key_mgmt;
char *pos, *end;
size_t len;
@@ -4115,36 +4186,68 @@
return len;
}
+ if (iftype_str) {
+ enum wpa_driver_if_type iftype;
+
+ iftype = iftype_str_to_index(iftype_str);
+ if (iftype == WPA_IF_MAX)
+ return -1;
+ key_mgmt = capa->key_mgmt_iftype[iftype];
+ } else {
+ key_mgmt = capa->key_mgmt;
+ }
+
ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
- if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+ if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
ret = os_snprintf(pos, end - pos, " WPA-EAP");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
- if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
- WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+ if (key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
ret = os_snprintf(pos, end - pos, " WPA-PSK");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
ret = os_snprintf(pos, end - pos, " WPA-NONE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK) {
+ ret = os_snprintf(pos, end - pos, " WAPI-PSK");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE) {
+ ret = os_snprintf(pos, end - pos, " TPK-HANDSHAKE");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_CCKM) {
+ ret = os_snprintf(pos, end - pos, " CCKM");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
#ifdef CONFIG_SUITEB
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
@@ -4152,7 +4255,7 @@
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
@@ -4160,7 +4263,7 @@
}
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_OWE
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) {
ret = os_snprintf(pos, end - pos, " OWE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
@@ -4168,7 +4271,7 @@
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) {
ret = os_snprintf(pos, end - pos, " DPP");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
@@ -4176,26 +4279,26 @@
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_FILS
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, " FILS-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, " FILS-SHA384");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_IEEE80211R
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
@@ -4204,27 +4307,72 @@
#endif /* CONFIG_IEEE80211R */
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, " FT-PSK");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+ ret = os_snprintf(pos, end - pos, " FT-EAP");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#ifdef CONFIG_SAE
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE) {
+ ret = os_snprintf(pos, end - pos, " FT-SAE");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_SHA384
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384) {
+ ret = os_snprintf(pos, end - pos, " FT-EAP-SHA384");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SAE
- if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, " SAE");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_SHA256
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256) {
+ ret = os_snprintf(pos, end - pos, " WPA-EAP-SHA256");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, " WPA-PSK-SHA256");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_HS20
+ if (key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OSEN) {
+ ret = os_snprintf(pos, end - pos, " OSEN");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
return pos - buf;
}
-static int ctrl_iface_get_capability_proto(int res, char *strict,
+static int ctrl_iface_get_capability_proto(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4267,7 +4415,7 @@
static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
- int res, char *strict,
+ int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4345,7 +4493,7 @@
}
-static int ctrl_iface_get_capability_modes(int res, char *strict,
+static int ctrl_iface_get_capability_modes(int res, bool strict,
struct wpa_driver_capa *capa,
char *buf, size_t buflen)
{
@@ -4508,23 +4656,36 @@
{
struct wpa_driver_capa capa;
int res;
- char *strict;
- char field[30];
+ char *next_param, *curr_param, *iftype = NULL;
+ bool strict = false;
+ char field[50];
size_t len;
/* Determine whether or not strict checking was requested */
len = os_strlcpy(field, _field, sizeof(field));
if (len >= sizeof(field))
return -1;
- strict = os_strchr(field, ' ');
- if (strict != NULL) {
- *strict++ = '\0';
- if (os_strcmp(strict, "strict") != 0)
+
+ next_param = os_strchr(field, ' ');
+ while (next_param) {
+ *next_param++ = '\0';
+ curr_param = next_param;
+ next_param = os_strchr(next_param, ' ');
+
+ if (next_param)
+ *next_param = '\0';
+
+ if (os_strcmp(curr_param, "strict") == 0)
+ strict = true;
+ else if (os_strncmp(curr_param, "iftype=", 7) == 0)
+ iftype = curr_param + 7;
+ else
return -1;
}
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
- field, strict ? strict : "");
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'%s%s%s",
+ field, iftype ? " iftype=" : "", iftype ? iftype : "",
+ strict ? " strict" : "");
if (os_strcmp(field, "eap") == 0) {
return eap_get_names(buf, buflen);
@@ -4546,7 +4707,7 @@
if (os_strcmp(field, "key_mgmt") == 0)
return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
- buf, buflen);
+ iftype, buf, buflen);
if (os_strcmp(field, "proto") == 0)
return ctrl_iface_get_capability_proto(res, strict, &capa,
@@ -4639,6 +4800,20 @@
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_SAE
+ if (os_strcmp(field, "sae") == 0 &&
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+#ifdef CONFIG_SAE_PK
+ res = os_snprintf(buf, buflen, "H2E PK");
+#else /* CONFIG_SAE_PK */
+ res = os_snprintf(buf, buflen, "H2E");
+#endif /* CONFIG_SAE_PK */
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_SAE */
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
@@ -4759,7 +4934,7 @@
size_t i;
int ret;
char *pos, *end;
- const u8 *ie, *ie2, *osen_ie, *mesh, *owe;
+ const u8 *ie, *ie2, *osen_ie, *mesh, *owe, *rsnxe;
pos = buf;
end = buf + buflen;
@@ -4879,6 +5054,21 @@
pos = wpa_supplicant_ie_txt(pos, end,
mesh ? "RSN" : "WPA2", ie2,
2 + ie2[1]);
+ rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ if (rsnxe && rsnxe[1] >= 1) {
+ if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)) {
+ ret = os_snprintf(pos, end - pos, "[SAE-H2E]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ if (rsnxe[2] & BIT(WLAN_RSNX_CAPAB_SAE_PK)) {
+ ret = os_snprintf(pos, end - pos, "[SAE-PK]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ }
osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
if (osen_ie)
pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
@@ -7266,7 +7456,7 @@
static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
{
u8 dst_addr[ETH_ALEN];
- int used;
+ int used, freq = 0;
char *pos;
#define MAX_ANQP_INFO_ID 100
u16 id[MAX_ANQP_INFO_ID];
@@ -7280,6 +7470,15 @@
pos = dst + used;
if (*pos == ' ')
pos++;
+
+ if (os_strncmp(pos, "freq=", 5) == 0) {
+ freq = atoi(pos + 5);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ }
+
while (num_id < MAX_ANQP_INFO_ID) {
if (os_strncmp(pos, "hs20:", 5) == 0) {
#ifdef CONFIG_HS20
@@ -7314,7 +7513,7 @@
if (num_id == 0 && !subtypes && !mbo_subtypes)
return -1;
- return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
+ return anqp_send_req(wpa_s, dst_addr, freq, id, num_id, subtypes,
mbo_subtypes);
}
@@ -8158,6 +8357,11 @@
dpp_pkex_ephemeral_key_override_len = 0;
dpp_protocol_key_override_len = 0;
dpp_nonce_override_len = 0;
+#ifdef CONFIG_DPP2
+ dpp_version_override = 2;
+#else /* CONFIG_DPP2 */
+ dpp_version_override = 1;
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
@@ -8188,9 +8392,10 @@
wpa_s->set_sta_uapsd = 0;
wpa_s->sta_uapsd = 0;
+ wpa_s->consecutive_conn_failures = 0;
+
wpa_drv_radio_disable(wpa_s, 0);
wpa_blacklist_clear(wpa_s);
- wpa_s->extra_blacklist_count = 0;
wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
wpa_config_flush_blobs(wpa_s->conf);
@@ -8244,6 +8449,13 @@
wpabuf_free(wpa_s->rsnxe_override_eapol);
wpa_s->rsnxe_override_eapol = NULL;
wpas_clear_driver_signal_override(wpa_s);
+ wpa_s->oci_freq_override_eapol = 0;
+ wpa_s->oci_freq_override_saquery_req = 0;
+ wpa_s->oci_freq_override_saquery_resp = 0;
+ wpa_s->oci_freq_override_eapol_g2 = 0;
+ wpa_s->oci_freq_override_ft_assoc = 0;
+ wpa_s->oci_freq_override_fils_assoc = 0;
+ wpa_s->oci_freq_override_wnm_sleep = 0;
#ifdef CONFIG_DPP
os_free(wpa_s->dpp_config_obj_override);
wpa_s->dpp_config_obj_override = NULL;
@@ -8262,6 +8474,7 @@
wpa_s->next_scan_bssid_wildcard_ssid = 0;
os_free(wpa_s->select_network_scan_freqs);
wpa_s->select_network_scan_freqs = NULL;
+ os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
wpa_bss_flush(wpa_s);
if (!dl_list_empty(&wpa_s->bss)) {
@@ -8287,6 +8500,8 @@
wpa_supplicant_update_channel_list(wpa_s, NULL);
free_bss_tmp_disallowed(wpa_s);
+
+ os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
}
@@ -9127,7 +9342,7 @@
if (ip.ip_hl != 5 || ip.ip_v != 4 || ntohs(ip.ip_len) > HWSIM_IP_LEN) {
wpa_printf(MSG_DEBUG,
- "test data: RX - ignore unexpect IP header");
+ "test data: RX - ignore unexpected IP header");
return;
}
@@ -10121,6 +10336,76 @@
}
+static int wpas_ctrl_iface_configure_mscs(struct wpa_supplicant *wpa_s,
+ const char *cmd)
+{
+ size_t frame_classifier_len;
+ const char *pos, *end;
+ struct robust_av_data *robust_av = &wpa_s->robust_av;
+ int val;
+
+ /*
+ * format:
+ * <add|remove|change> [up_bitmap=<hex byte>] [up_limit=<integer>]
+ * [stream_timeout=<in TUs>] [frame_classifier=<hex bytes>]
+ */
+ os_memset(robust_av, 0, sizeof(struct robust_av_data));
+ if (os_strncmp(cmd, "add ", 4) == 0) {
+ robust_av->request_type = SCS_REQ_ADD;
+ } else if (os_strcmp(cmd, "remove") == 0) {
+ robust_av->request_type = SCS_REQ_REMOVE;
+ robust_av->valid_config = false;
+ return wpas_send_mscs_req(wpa_s);
+ } else if (os_strncmp(cmd, "change ", 7) == 0) {
+ robust_av->request_type = SCS_REQ_CHANGE;
+ } else {
+ return -1;
+ }
+
+ pos = os_strstr(cmd, "up_bitmap=");
+ if (!pos)
+ return -1;
+
+ val = hex2byte(pos + 10);
+ if (val < 0)
+ return -1;
+ robust_av->up_bitmap = val;
+
+ pos = os_strstr(cmd, "up_limit=");
+ if (!pos)
+ return -1;
+
+ robust_av->up_limit = atoi(pos + 9);
+
+ pos = os_strstr(cmd, "stream_timeout=");
+ if (!pos)
+ return -1;
+
+ robust_av->stream_timeout = atoi(pos + 15);
+ if (robust_av->stream_timeout == 0)
+ return -1;
+
+ pos = os_strstr(cmd, "frame_classifier=");
+ if (!pos)
+ return -1;
+
+ pos += 17;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ end = pos + os_strlen(pos);
+
+ frame_classifier_len = (end - pos) / 2;
+ if (frame_classifier_len > sizeof(robust_av->frame_classifier) ||
+ hexstr2bin(pos, robust_av->frame_classifier, frame_classifier_len))
+ return -1;
+
+ robust_av->frame_classifier_len = frame_classifier_len;
+ robust_av->valid_config = true;
+
+ return wpas_send_mscs_req(wpa_s);
+}
+
+
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
@@ -11004,8 +11289,17 @@
reply_len = -1;
} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
wpas_dpp_chirp_stop(wpa_s);
+ } else if (os_strncmp(buf, "DPP_RECONFIG ", 13) == 0) {
+ if (wpas_dpp_reconfig(wpa_s, buf + 13) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_CA_SET ", 11) == 0) {
+ if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0)
+ reply_len = -1;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
+ } else if (os_strncmp(buf, "MSCS ", 5) == 0) {
+ if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
+ reply_len = -1;
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
index d54cc07..510668d 100644
--- a/wpa_supplicant/ctrl_iface.h
+++ b/wpa_supplicant/ctrl_iface.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,6 +11,10 @@
#ifdef CONFIG_CTRL_IFACE
+#ifndef CTRL_IFACE_MAX_LEN
+#define CTRL_IFACE_MAX_LEN 8192
+#endif /* CTRL_IFACE_MAX_LEN */
+
/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
/**
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index 9c0a47e..79ff787 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -45,7 +45,7 @@
/* Per-interface ctrl_iface */
-#define REQUEST_BUFSIZE 256
+#define REQUEST_BUFSIZE CTRL_IFACE_MAX_LEN
#define REPLY_BUFSIZE 4096
struct ctrl_iface_priv;
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 1e92b97..1512080 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / UDP socket -based control interface
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -219,7 +219,7 @@
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
- char buf[4096], *pos;
+ char *buf, *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
@@ -235,11 +235,15 @@
int new_attached = 0;
u8 cookie[COOKIE_LEN];
- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+ if (!buf)
+ return;
+ res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
+ os_free(buf);
return;
}
@@ -249,6 +253,8 @@
if (os_strcmp(addr, "::1")) {
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
addr);
+ os_free(buf);
+ return;
}
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
@@ -260,11 +266,17 @@
*/
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
"source %s", inet_ntoa(from.sin_addr));
+ os_free(buf);
return;
}
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+ if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+ os_free(buf);
+ return;
+ }
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -282,18 +294,21 @@
if (os_strncmp(buf, "COOKIE=", 7) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
"drop request");
+ os_free(buf);
return;
}
if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
"request - drop request");
+ os_free(buf);
return;
}
if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
"drop request");
+ os_free(buf);
return;
}
@@ -339,6 +354,8 @@
fromlen);
}
+ os_free(buf);
+
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
}
@@ -600,10 +617,13 @@
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
- char buf[4096], *pos;
+ char *buf, *pos;
int res;
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+ char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
struct sockaddr_in from;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
@@ -612,16 +632,28 @@
size_t reply_len;
u8 cookie[COOKIE_LEN];
- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+ if (!buf)
+ return;
+ res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
+ os_free(buf);
return;
}
#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
-#ifndef CONFIG_CTRL_IFACE_UDP_IPV6
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+ inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+ if (os_strcmp(addr, "::1")) {
+ wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+ addr);
+ os_free(buf);
+ return;
+ }
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
@@ -631,11 +663,17 @@
*/
wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
"source %s", inet_ntoa(from.sin_addr));
+ os_free(buf);
return;
}
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+ if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+ os_free(buf);
+ return;
+ }
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -646,18 +684,21 @@
if (os_strncmp(buf, "COOKIE=", 7) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
"drop request");
+ os_free(buf);
return;
}
if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
"request - drop request");
+ os_free(buf);
return;
}
if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
"drop request");
+ os_free(buf);
return;
}
@@ -694,6 +735,8 @@
sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
fromlen);
}
+
+ os_free(buf);
}
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 71fe7ed..953fd2c 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -131,7 +131,7 @@
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
- char buf[4096];
+ char *buf;
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
@@ -139,11 +139,20 @@
size_t reply_len = 0;
int new_attached = 0;
- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+ if (!buf)
+ return;
+ res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
+ os_free(buf);
+ return;
+ }
+ if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+ os_free(buf);
return;
}
buf[res] = '\0';
@@ -221,6 +230,7 @@
}
}
os_free(reply_buf);
+ os_free(buf);
if (new_attached)
eapol_sm_notify_ctrl_attached(wpa_s->eapol);
@@ -1046,18 +1056,27 @@
{
struct wpa_global *global = eloop_ctx;
struct ctrl_iface_global_priv *priv = sock_ctx;
- char buf[4096];
+ char *buf;
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply = NULL, *reply_buf = NULL;
size_t reply_len;
- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
+ if (!buf)
+ return;
+ res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN + 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
+ os_free(buf);
+ return;
+ }
+ if ((size_t) res > CTRL_IFACE_MAX_LEN) {
+ wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
+ os_free(buf);
return;
}
buf[res] = '\0';
@@ -1105,6 +1124,7 @@
}
}
os_free(reply_buf);
+ os_free(buf);
}
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 793a881..6dcd948 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -3212,6 +3212,14 @@
END_ARGS
}
},
+ { "Roam", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_roam,
+ {
+ { "addr", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+
#ifndef CONFIG_NO_CONFIG_BLOBS
{ "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) wpas_dbus_handler_add_blob,
@@ -3613,7 +3621,7 @@
},
{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_bridge_ifname,
- NULL,
+ wpas_dbus_setter_bridge_ifname,
NULL
},
{ "ConfigFile", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index d1f9607..86f8560 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -139,6 +139,7 @@
"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
"bssid", "scan_freq", "freq_list", "scan_ssid", "bssid_hint",
"bssid_blacklist", "bssid_whitelist", "group_mgmt",
+ "ignore_broadcast_ssid",
#ifdef CONFIG_MESH
"mesh_basic_rates",
#endif /* CONFIG_MESH */
@@ -229,8 +230,6 @@
} else if (entry.type == DBUS_TYPE_STRING) {
if (should_quote_opt(entry.key)) {
size = os_strlen(entry.str_value);
- if (size == 0)
- goto error;
size += 3;
value = os_zalloc(size);
@@ -267,8 +266,28 @@
} else
goto error;
- if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+ ret = wpa_config_set(ssid, entry.key, value, 0);
+ if (ret < 0)
goto error;
+ if (ret == 1)
+ goto skip_update;
+
+#ifdef CONFIG_BGSCAN
+ if (os_strcmp(entry.key, "bgscan") == 0) {
+ /*
+ * Reset the bgscan parameters for the current network
+ * and continue. There's no need to flush caches for
+ * bgscan parameter changes.
+ */
+ if (wpa_s->current_ssid == ssid &&
+ wpa_s->wpa_state == WPA_COMPLETED)
+ wpa_supplicant_reset_bgscan(wpa_s);
+ os_free(value);
+ value = NULL;
+ wpa_dbus_dict_entry_clear(&entry);
+ continue;
+ }
+#endif /* CONFIG_BGSCAN */
if (os_strcmp(entry.key, "bssid") != 0 &&
os_strcmp(entry.key, "priority") != 0)
@@ -290,6 +309,7 @@
else if (os_strcmp(entry.key, "priority") == 0)
wpa_config_update_prio_list(wpa_s->conf);
+ skip_update:
os_free(value);
value = NULL;
wpa_dbus_dict_entry_clear(&entry);
@@ -1797,25 +1817,6 @@
}
-static void remove_network(void *arg, struct wpa_ssid *ssid)
-{
- struct wpa_supplicant *wpa_s = arg;
-
- wpas_notify_network_removed(wpa_s, ssid);
-
- if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
- wpa_printf(MSG_ERROR,
- "%s[dbus]: error occurred when removing network %d",
- __func__, ssid->id);
- return;
- }
-
- if (ssid == wpa_s->current_ssid)
- wpa_supplicant_deauthenticate(wpa_s,
- WLAN_REASON_DEAUTH_LEAVING);
-}
-
-
/**
* wpas_dbus_handler_remove_all_networks - Remove all configured networks
* @message: Pointer to incoming dbus message
@@ -1827,11 +1828,8 @@
DBusMessage * wpas_dbus_handler_remove_all_networks(
DBusMessage *message, struct wpa_supplicant *wpa_s)
{
- if (wpa_s->sched_scanning)
- wpa_supplicant_cancel_sched_scan(wpa_s);
-
/* NB: could check for failure and return an error */
- wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
+ wpa_supplicant_remove_all_networks(wpa_s);
return NULL;
}
@@ -1956,6 +1954,55 @@
}
+/**
+ * wpas_dbus_handler_roam - Initiate a roam to another BSS within the ESS
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "Roam" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_roam(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return wpas_dbus_error_unknown_error(message,
+ "scan processing not included");
+#else /* CONFIG_NO_SCAN_PROCESSING */
+ u8 bssid[ETH_ALEN];
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ char *addr;
+
+ if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (hwaddr_aton(addr, bssid))
+ return wpas_dbus_error_invalid_args(
+ message, "Invalid hardware address format");
+
+ wpa_printf(MSG_DEBUG, "dbus: Roam " MACSTR, MAC2STR(bssid));
+
+ if (!ssid)
+ return dbus_message_new_error(
+ message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+ "This interface is not connected");
+
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "dbus: Roam: Target BSS not found");
+ return wpas_dbus_error_invalid_args(
+ message, "Target BSS not found");
+ }
+
+ wpa_s->reassociate = 1;
+ wpa_supplicant_connect(wpa_s, bss, ssid);
+
+ return NULL;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
#ifndef CONFIG_NO_CONFIG_BLOBS
/**
@@ -3634,6 +3681,43 @@
}
+dbus_bool_t wpas_dbus_setter_bridge_ifname(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *bridge_ifname = NULL;
+ const char *msg;
+ int r;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &bridge_ifname))
+ return FALSE;
+
+ r = wpa_supplicant_update_bridge_ifname(wpa_s, bridge_ifname);
+ if (r != 0) {
+ switch (r) {
+ case -EINVAL:
+ msg = "invalid interface name";
+ break;
+ case -EBUSY:
+ msg = "interface is busy";
+ break;
+ case -EIO:
+ msg = "socket error";
+ break;
+ default:
+ msg = "unknown error";
+ break;
+ }
+ dbus_set_error_const(error, DBUS_ERROR_FAILED, msg);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
/**
* wpas_dbus_getter_config_file - Get interface configuration file path
* @iter: Pointer to incoming dbus message iter
@@ -3941,14 +4025,15 @@
return FALSE;
}
- if (wpa_config_process_global(wpa_s->conf, buf, -1)) {
+ ret = wpa_config_process_global(wpa_s->conf, buf, -1);
+ if (ret < 0) {
dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
"Failed to set interface property %s",
property_desc->dbus_property);
return FALSE;
+ } else if (ret == 0) {
+ wpa_supplicant_update_config(wpa_s);
}
-
- wpa_supplicant_update_config(wpa_s);
return TRUE;
}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index afa26ef..c36383f 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -117,6 +117,9 @@
DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_roam(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -167,6 +170,7 @@
DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
DECLARE_ACCESSOR(wpas_dbus_getter_driver);
DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_setter_bridge_ifname);
DECLARE_ACCESSOR(wpas_dbus_getter_config_file);
DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index aee105b..6c721bf 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -257,7 +257,7 @@
DBusMessage *reply;
struct wpabuf *xml;
- xml = wpabuf_alloc(20000);
+ xml = wpabuf_alloc(30000);
if (xml == NULL)
return NULL;
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index c570775..7e06fdd 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -506,7 +506,7 @@
CONFIG_P2P=y
# Enable TDLS support
-#CONFIG_TDLS=y
+CONFIG_TDLS=y
# Wi-Fi Display
# This can be used to enable Wi-Fi Display extensions for P2P using an external
diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore
index 8c3945c..dac35c5 100644
--- a/wpa_supplicant/doc/docbook/.gitignore
+++ b/wpa_supplicant/doc/docbook/.gitignore
@@ -1,5 +1,6 @@
manpage.links
manpage.refs
+manpage.log
*.8
*.5
*.html
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 6dfa2e5..2d0c636 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/ip_addr.h"
+#include "utils/base64.h"
#include "common/dpp.h"
#include "common/gas.h"
#include "common/gas_server.h"
@@ -46,6 +47,13 @@
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result);
+#ifdef CONFIG_DPP2
+static void wpas_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s);
+static int wpas_dpp_process_conf_obj(void *ctx,
+ struct dpp_authentication *auth);
+#endif /* CONFIG_DPP2 */
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -85,6 +93,10 @@
500, wpas_dpp_tx_status, 0);
}
+#ifdef CONFIG_DPP2
+ dpp_controller_new_qr_code(wpa_s->dpp, bi);
+#endif /* CONFIG_DPP2 */
+
return bi->id;
}
@@ -247,6 +259,35 @@
#ifdef CONFIG_DPP2
+static void wpas_dpp_stop_listen_for_tx(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ unsigned int wait_time)
+{
+ struct os_reltime now, res;
+ unsigned int remaining;
+
+ if (!wpa_s->dpp_listen_freq)
+ return;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&now, &wpa_s->dpp_listen_end)) {
+ os_reltime_sub(&wpa_s->dpp_listen_end, &now, &res);
+ remaining = res.sec * 1000 + res.usec / 1000;
+ } else {
+ remaining = 0;
+ }
+ if (wpa_s->dpp_listen_freq == freq && remaining > wait_time)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Stop listen on %u MHz ending in %u ms to allow immediate TX on %u MHz for %u ms",
+ wpa_s->dpp_listen_freq, remaining, freq, wait_time);
+ wpas_dpp_listen_stop(wpa_s);
+
+ /* TODO: Restart listen in some cases after TX? */
+}
+
+
static void wpas_dpp_conn_status_result_timeout(void *eloop_ctx,
void *timeout_ctx)
{
@@ -435,6 +476,10 @@
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+ wpa_s, NULL);
+#endif /* CONFIG_DPP2 */
offchannel_send_action_done(wpa_s);
dpp_auth_deinit(wpa_s->dpp_auth);
wpa_s->dpp_auth = NULL;
@@ -769,6 +814,10 @@
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s,
NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+ wpa_s, NULL);
+#endif /* CONFIG_DPP2 */
offchannel_send_action_done(wpa_s);
dpp_auth_deinit(wpa_s->dpp_auth);
wpa_s->dpp_auth = NULL;
@@ -791,7 +840,9 @@
#ifdef CONFIG_DPP2
if (tcp)
- return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port);
+ return dpp_tcp_init(wpa_s->dpp, auth, &ipaddr, tcp_port,
+ wpa_s->conf->dpp_name, DPP_NETROLE_STA,
+ wpa_s, wpa_s, wpas_dpp_process_conf_obj);
#endif /* CONFIG_DPP2 */
wpa_s->dpp_auth = auth;
@@ -941,6 +992,24 @@
}
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ if (wpa_s->dpp_listen_freq != freq)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Remain-on-channel started for listen on %u MHz for %u ms",
+ freq, duration);
+ os_get_reltime(&wpa_s->dpp_listen_end);
+ wpa_s->dpp_listen_end.usec += duration * 1000;
+ while (wpa_s->dpp_listen_end.usec >= 1000000) {
+ wpa_s->dpp_listen_end.sec++;
+ wpa_s->dpp_listen_end.usec -= 1000000;
+ }
+}
+
+
void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
@@ -1080,7 +1149,8 @@
res = wpa_drv_get_capa(wpa_s, &capa);
if (res == 0 &&
- !(capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) &&
+ !(capa.key_mgmt_iftype[WPA_IF_STATION] &
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE) &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
wpa_printf(MSG_DEBUG,
"DPP: SAE not supported by the driver");
@@ -1125,6 +1195,15 @@
ssid->dpp_csign_len = wpabuf_len(conf->c_sign_key);
}
+ if (conf->pp_key) {
+ ssid->dpp_pp_key = os_malloc(wpabuf_len(conf->pp_key));
+ if (!ssid->dpp_pp_key)
+ goto fail;
+ os_memcpy(ssid->dpp_pp_key, wpabuf_head(conf->pp_key),
+ wpabuf_len(conf->pp_key));
+ ssid->dpp_pp_key_len = wpabuf_len(conf->pp_key);
+ }
+
if (auth->net_access_key) {
ssid->dpp_netaccesskey =
os_malloc(wpabuf_len(auth->net_access_key));
@@ -1160,6 +1239,102 @@
}
}
+#if defined(CONFIG_DPP2) && defined(IEEE8021X_EAPOL)
+ if (conf->akm == DPP_AKM_DOT1X) {
+ int i;
+ char name[100], blobname[128];
+ struct wpa_config_blob *blob;
+
+ ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA256;
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+
+ if (conf->cacert) {
+ /* caCert is DER-encoded X.509v3 certificate for the
+ * server certificate if that is different from the
+ * trust root included in certBag. */
+ /* TODO: ssid->eap.cert.ca_cert */
+ }
+
+ if (conf->certs) {
+ for (i = 0; ; i++) {
+ os_snprintf(name, sizeof(name), "dpp-certs-%d",
+ i);
+ if (!wpa_config_get_blob(wpa_s->conf, name))
+ break;
+ }
+
+ blob = os_zalloc(sizeof(*blob));
+ if (!blob)
+ goto fail;
+ blob->len = wpabuf_len(conf->certs);
+ blob->name = os_strdup(name);
+ blob->data = os_malloc(blob->len);
+ if (!blob->name || !blob->data) {
+ wpa_config_free_blob(blob);
+ goto fail;
+ }
+ os_memcpy(blob->data, wpabuf_head(conf->certs),
+ blob->len);
+ os_snprintf(blobname, sizeof(blobname), "blob://%s",
+ name);
+ wpa_config_set_blob(wpa_s->conf, blob);
+ wpa_printf(MSG_DEBUG, "DPP: Added certificate blob %s",
+ name);
+ ssid->eap.cert.client_cert = os_strdup(blobname);
+ if (!ssid->eap.cert.client_cert)
+ goto fail;
+
+ /* TODO: ssid->eap.identity from own certificate */
+ if (wpa_config_set(ssid, "identity", "\"dpp-ent\"",
+ 0) < 0)
+ goto fail;
+ }
+
+ if (auth->priv_key) {
+ for (i = 0; ; i++) {
+ os_snprintf(name, sizeof(name), "dpp-key-%d",
+ i);
+ if (!wpa_config_get_blob(wpa_s->conf, name))
+ break;
+ }
+
+ blob = os_zalloc(sizeof(*blob));
+ if (!blob)
+ goto fail;
+ blob->len = wpabuf_len(auth->priv_key);
+ blob->name = os_strdup(name);
+ blob->data = os_malloc(blob->len);
+ if (!blob->name || !blob->data) {
+ wpa_config_free_blob(blob);
+ goto fail;
+ }
+ os_memcpy(blob->data, wpabuf_head(auth->priv_key),
+ blob->len);
+ os_snprintf(blobname, sizeof(blobname), "blob://%s",
+ name);
+ wpa_config_set_blob(wpa_s->conf, blob);
+ wpa_printf(MSG_DEBUG, "DPP: Added private key blob %s",
+ name);
+ ssid->eap.cert.private_key = os_strdup(blobname);
+ if (!ssid->eap.cert.private_key)
+ goto fail;
+ }
+
+ if (conf->server_name) {
+ ssid->eap.cert.domain_suffix_match =
+ os_strdup(conf->server_name);
+ if (!ssid->eap.cert.domain_suffix_match)
+ goto fail;
+ }
+
+ /* TODO: Use entCreds::eapMethods */
+ if (wpa_config_set(ssid, "eap", "TLS", 0) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_DPP2 && IEEE8021X_EAPOL */
+
os_memcpy(wpa_s->dpp_last_ssid, conf->ssid, conf->ssid_len);
wpa_s->dpp_last_ssid_len = conf->ssid_len;
@@ -1204,6 +1379,20 @@
static void wpas_dpp_post_process_config(struct wpa_supplicant *wpa_s,
struct dpp_authentication *auth)
{
+#ifdef CONFIG_DPP2
+ if (auth->reconfig && wpa_s->dpp_reconfig_ssid &&
+ wpa_config_get_network(wpa_s->conf, wpa_s->dpp_reconfig_ssid_id) ==
+ wpa_s->dpp_reconfig_ssid) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Remove reconfigured network profile");
+ wpas_notify_network_removed(wpa_s, wpa_s->dpp_reconfig_ssid);
+ wpa_config_remove_network(wpa_s->conf,
+ wpa_s->dpp_reconfig_ssid_id);
+ wpa_s->dpp_reconfig_ssid = NULL;
+ wpa_s->dpp_reconfig_ssid_id = -1;
+ }
+#endif /* CONFIG_DPP2 */
+
if (wpa_s->conf->dpp_config_processing < 2)
return;
@@ -1255,6 +1444,20 @@
os_free(hex);
}
}
+ if (conf->pp_key) {
+ char *hex;
+ size_t hexlen;
+
+ hexlen = 2 * wpabuf_len(conf->pp_key) + 1;
+ hex = os_malloc(hexlen);
+ if (hex) {
+ wpa_snprintf_hex(hex, hexlen,
+ wpabuf_head(conf->pp_key),
+ wpabuf_len(conf->pp_key));
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PP_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
if (auth->net_access_key) {
char *hex;
size_t hexlen;
@@ -1277,6 +1480,32 @@
}
}
+#ifdef CONFIG_DPP2
+ if (conf->certbag) {
+ char *b64;
+
+ b64 = base64_encode_no_lf(wpabuf_head(conf->certbag),
+ wpabuf_len(conf->certbag), NULL);
+ if (b64)
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CERTBAG "%s", b64);
+ os_free(b64);
+ }
+
+ if (conf->cacert) {
+ char *b64;
+
+ b64 = base64_encode_no_lf(wpabuf_head(conf->cacert),
+ wpabuf_len(conf->cacert), NULL);
+ if (b64)
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CACERT "%s", b64);
+ os_free(b64);
+ }
+
+ if (conf->server_name)
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_SERVER_NAME "%s",
+ conf->server_name);
+#endif /* CONFIG_DPP2 */
+
return wpas_dpp_process_config(wpa_s, auth, conf);
}
@@ -1292,6 +1521,7 @@
wpa_printf(MSG_DEBUG, "DPP: Received Configurator backup");
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+ wpa_s->dpp_conf_backup_received = true;
while (key) {
res = dpp_configurator_from_backup(wpa_s->dpp, key);
@@ -1307,6 +1537,31 @@
}
+#ifdef CONFIG_DPP2
+static void wpas_dpp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth || !auth->csrattrs)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+ wpabuf_free(auth->csr);
+ /* TODO: Additional information needed for CSR based on csrAttrs */
+ auth->csr = dpp_build_csr(auth, wpa_s->conf->dpp_name ?
+ wpa_s->conf->dpp_name : "Test");
+ if (!auth->csr) {
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+ }
+
+ wpas_dpp_start_gas_client(wpa_s);
+}
+#endif /* CONFIG_DPP2 */
+
+
static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
@@ -1321,7 +1576,8 @@
wpa_s->dpp_gas_dialog_token = -1;
- if (!auth || !auth->auth_success) {
+ if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
+ os_memcmp(addr, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return;
}
@@ -1350,11 +1606,20 @@
goto fail;
}
- if (dpp_conf_resp_rx(auth, resp) < 0) {
+ res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP2
+ if (res == -2) {
+ wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+ eloop_register_timeout(0, 0, wpas_dpp_build_csr, wpa_s, NULL);
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+ if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
goto fail;
}
+ wpa_s->dpp_conf_backup_received = false;
for (i = 0; i < auth->num_conf_obj; i++) {
res = wpas_dpp_handle_config_obj(wpa_s, auth,
&auth->conf_obj[i]);
@@ -1400,6 +1665,9 @@
wpabuf_free(msg);
/* This exchange will be terminated in the TX status handler */
+ if (wpa_s->conf->dpp_config_processing < 2 ||
+ wpa_s->dpp_conf_backup_received)
+ auth->remove_on_tx_status = 1;
return;
}
fail2:
@@ -1753,6 +2021,8 @@
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
peer_bi = dpp_bootstrap_find_chirp(wpa_s->dpp, r_bootstrap);
+ dpp_notify_chirp_received(wpa_s, peer_bi ? (int) peer_bi->id : -1, src,
+ freq, r_bootstrap);
if (!peer_bi) {
wpa_printf(MSG_DEBUG,
"DPP: No matching bootstrapping information found");
@@ -1781,6 +2051,249 @@
}
}
+
+static void wpas_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
+ offchannel_send_action_done(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+ dpp_auth_deinit(auth);
+ wpa_s->dpp_auth = NULL;
+}
+
+
+static void
+wpas_dpp_rx_reconfig_announcement(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+ u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+ struct dpp_configurator *conf;
+ struct dpp_authentication *auth;
+ unsigned int wait_time, max_wait_time;
+ u16 group;
+
+ if (!wpa_s->dpp)
+ return;
+
+ if (wpa_s->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
+ MAC2STR(src));
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(wpa_s->dpp, csign_hash);
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return;
+ }
+
+ fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &fcgroup_len);
+ if (!fcgroup || fcgroup_len != 2) {
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Finite Cyclic Group attribute");
+ return;
+ }
+ group = WPA_GET_LE16(fcgroup);
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+ a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+ e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+ auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq, group,
+ a_nonce, a_nonce_len, e_id, e_id_len);
+ if (!auth)
+ return;
+ wpas_dpp_set_testing_options(wpa_s, auth);
+ if (dpp_set_configurator(auth, wpa_s->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return;
+ }
+
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ wpa_s->dpp_auth = auth;
+
+ wpa_s->dpp_in_response_listen = 0;
+ wpa_s->dpp_auth_ok_on_ack = 0;
+ wait_time = wpa_s->max_remain_on_chan;
+ max_wait_time = wpa_s->dpp_resp_wait_time ?
+ wpa_s->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ wpas_dpp_reconfig_reply_wait_timeout,
+ wpa_s, NULL);
+ wait_time -= 10;
+
+ wpas_dpp_stop_listen_for_tx(wpa_s, freq, wait_time);
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
+ if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->reconfig_req_msg),
+ wpabuf_len(auth->reconfig_req_msg),
+ wait_time, wpas_dpp_tx_status, 0) < 0) {
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ }
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct wpa_ssid *ssid;
+ struct dpp_authentication *auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Request from "
+ MACSTR, MAC2STR(src));
+
+ if (!wpa_s->dpp)
+ return;
+ if (wpa_s->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not ready for reconfiguration - pending authentication exchange in progress");
+ return;
+ }
+ if (!wpa_s->dpp_reconfig_ssid) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not ready for reconfiguration - not requested");
+ return;
+ }
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->dpp_reconfig_ssid &&
+ ssid->id == wpa_s->dpp_reconfig_ssid_id)
+ break;
+ }
+ if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not ready for reconfiguration - no matching network profile with Connector found");
+ return;
+ }
+
+ auth = dpp_reconfig_auth_req_rx(wpa_s->dpp, wpa_s, ssid->dpp_connector,
+ ssid->dpp_netaccesskey,
+ ssid->dpp_netaccesskey_len,
+ ssid->dpp_csign, ssid->dpp_csign_len,
+ freq, hdr, buf, len);
+ if (!auth)
+ return;
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+ wpa_s->dpp_auth = auth;
+
+ wpas_dpp_chirp_stop(wpa_s);
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_RESP);
+ if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+ wpabuf_head(auth->reconfig_resp_msg),
+ wpabuf_len(auth->reconfig_resp_msg),
+ 500, wpas_dpp_tx_status, 0) < 0) {
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ }
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ struct wpabuf *conf;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
+ MACSTR, MAC2STR(src));
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return;
+
+ eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout, wpa_s, NULL);
+
+ wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
+ if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
+ wpabuf_head(conf), wpabuf_len(conf),
+ 500, wpas_dpp_tx_status, 0) < 0) {
+ wpabuf_free(conf);
+ dpp_auth_deinit(wpa_s->dpp_auth);
+ wpa_s->dpp_auth = NULL;
+ return;
+ }
+ wpabuf_free(conf);
+
+ wpas_dpp_start_gas_server(wpa_s);
+}
+
+
+static void
+wpas_dpp_rx_reconfig_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Confirm from "
+ MACSTR, MAC2STR(src));
+
+ if (!auth || !auth->reconfig || auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ if (dpp_reconfig_auth_conf_rx(auth, hdr, buf, len) < 0)
+ return;
+
+ wpas_dpp_start_gas_client(wpa_s);
+}
+
#endif /* CONFIG_DPP2 */
@@ -1791,6 +2304,11 @@
struct wpa_ssid *ssid;
const u8 *connector, *trans_id, *status;
u16 connector_len, trans_id_len, status_len;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+ u8 peer_version = 1;
struct dpp_introduction intro;
struct rsn_pmksa_cache_entry *entry;
struct os_time now;
@@ -1891,6 +2409,13 @@
os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
entry->pmk_len = intro.pmk_len;
entry->akmp = WPA_KEY_MGMT_DPP;
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && version_len >= 1)
+ peer_version = version[0];
+ entry->dpp_pfs = peer_version >= 2;
+#endif /* CONFIG_DPP2 */
if (expiry) {
os_get_time(&now);
seconds = expiry - now.sec;
@@ -1904,7 +2429,7 @@
wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
- " status=%u", MAC2STR(src), status[0]);
+ " status=%u version=%u", MAC2STR(src), status[0], peer_version);
wpa_printf(MSG_DEBUG,
"DPP: Try connection again after successful network introduction");
@@ -2330,6 +2855,19 @@
wpas_dpp_rx_presence_announcement(wpa_s, src, hdr, buf, len,
freq);
break;
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ wpas_dpp_rx_reconfig_announcement(wpa_s, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_REQ:
+ wpas_dpp_rx_reconfig_auth_req(wpa_s, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ wpas_dpp_rx_reconfig_auth_resp(wpa_s, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_RECONFIG_AUTH_CONF:
+ wpas_dpp_rx_reconfig_auth_conf(wpa_s, src, hdr, buf, len, freq);
+ break;
#endif /* CONFIG_DPP2 */
default:
wpa_printf(MSG_DEBUG,
@@ -2351,8 +2889,8 @@
static struct wpabuf *
-wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
- size_t query_len)
+wpas_dpp_gas_req_handler(void *ctx, void *resp_ctx, const u8 *sa,
+ const u8 *query, size_t query_len, u16 *comeback_delay)
{
struct wpa_supplicant *wpa_s = ctx;
struct dpp_authentication *auth = wpa_s->dpp_auth;
@@ -2360,7 +2898,7 @@
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
MAC2STR(sa));
- if (!auth || !auth->auth_success ||
+ if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return NULL;
@@ -2383,6 +2921,16 @@
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
MAC2STR(sa));
resp = dpp_conf_req_rx(auth, query, query_len);
+
+#ifdef CONFIG_DPP2
+ if (!resp && auth->waiting_cert) {
+ wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+ auth->cert_resp_ctx = resp_ctx;
+ *comeback_delay = 500;
+ return NULL;
+ }
+#endif /* CONFIG_DPP2 */
+
if (!resp) {
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
wpas_notify_dpp_configuration_failure(wpa_s);
@@ -2410,6 +2958,14 @@
return;
}
+#ifdef CONFIG_DPP2
+ if (auth->waiting_csr && ok) {
+ wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+ wpabuf_free(resp);
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+
wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
ok);
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
@@ -2501,6 +3057,7 @@
unsigned int wait_time;
const u8 *rsn;
struct wpa_ie_data ied;
+ size_t len;
if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss)
return 0; /* Not using DPP AKM - continue */
@@ -2534,8 +3091,11 @@
"DPP: Starting network introduction protocol to derive PMKSA for "
MACSTR, MAC2STR(bss->bssid));
- msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ,
- 5 + 4 + os_strlen(ssid->dpp_connector));
+ len = 5 + 4 + os_strlen(ssid->dpp_connector);
+#ifdef CONFIG_DPP2
+ len += 5;
+#endif /* CONFIG_DPP2 */
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ, len);
if (!msg)
return -1;
@@ -2590,6 +3150,15 @@
skip_connector:
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_DPP2
+ if (DPP_VERSION > 1) {
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
/* TODO: Timeout on AP response */
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
@@ -2758,10 +3327,8 @@
return -1;
os_memset(&config, 0, sizeof(config));
- config.msg_ctx = wpa_s;
config.cb_ctx = wpa_s;
#ifdef CONFIG_DPP2
- config.process_conf_obj = wpas_dpp_process_conf_obj;
config.remove_bi = wpas_dpp_remove_bi;
#endif /* CONFIG_DPP2 */
wpa_s->dpp = dpp_global_init(&config);
@@ -2782,7 +3349,6 @@
#endif /* CONFIG_TESTING_OPTIONS */
if (!wpa_s->dpp)
return;
- dpp_global_clear(wpa_s->dpp);
eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
@@ -2792,9 +3358,14 @@
eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout,
wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout,
+ wpa_s, NULL);
+ eloop_cancel_timeout(wpas_dpp_build_csr, wpa_s, NULL);
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = NULL;
wpas_dpp_chirp_stop(wpa_s);
+ dpp_free_reconfig_id(wpa_s->dpp_reconfig_id);
+ wpa_s->dpp_reconfig_id = NULL;
#endif /* CONFIG_DPP2 */
offchannel_send_action_done(wpa_s);
wpas_dpp_listen_stop(wpa_s);
@@ -2803,6 +3374,7 @@
os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
os_free(wpa_s->dpp_configurator_params);
wpa_s->dpp_configurator_params = NULL;
+ dpp_global_clear(wpa_s->dpp);
}
@@ -2814,12 +3386,33 @@
const char *pos;
os_memset(&config, 0, sizeof(config));
+ config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
+ config.netrole = DPP_NETROLE_STA;
+ config.msg_ctx = wpa_s;
+ config.cb_ctx = wpa_s;
+ config.process_conf_obj = wpas_dpp_process_conf_obj;
if (cmd) {
pos = os_strstr(cmd, " tcp_port=");
if (pos) {
pos += 10;
config.tcp_port = atoi(pos);
}
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ config.allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ return -1;
+ }
+
+ config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
}
config.configurator_params = wpa_s->dpp_configurator_params;
return dpp_controller_start(wpa_s->dpp, &config);
@@ -2862,17 +3455,41 @@
static void wpas_dpp_chirp_start(struct wpa_supplicant *wpa_s)
{
+ struct wpabuf *msg, *announce = NULL;
+ int type;
+
+ msg = wpa_s->dpp_presence_announcement;
+ type = DPP_PA_PRESENCE_ANNOUNCEMENT;
+ if (!msg) {
+ struct wpa_ssid *ssid = wpa_s->dpp_reconfig_ssid;
+
+ if (ssid && wpa_s->dpp_reconfig_id &&
+ wpa_config_get_network(wpa_s->conf,
+ wpa_s->dpp_reconfig_ssid_id) ==
+ ssid) {
+ announce = dpp_build_reconfig_announcement(
+ ssid->dpp_csign,
+ ssid->dpp_csign_len,
+ ssid->dpp_netaccesskey,
+ ssid->dpp_netaccesskey_len,
+ wpa_s->dpp_reconfig_id);
+ msg = announce;
+ }
+ if (!msg)
+ return;
+ type = DPP_PA_RECONFIG_ANNOUNCEMENT;
+ }
wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", wpa_s->dpp_chirp_freq);
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
- MAC2STR(broadcast), wpa_s->dpp_chirp_freq,
- DPP_PA_PRESENCE_ANNOUNCEMENT);
+ MAC2STR(broadcast), wpa_s->dpp_chirp_freq, type);
if (offchannel_send_action(
wpa_s, wpa_s->dpp_chirp_freq, broadcast,
wpa_s->own_addr, broadcast,
- wpabuf_head(wpa_s->dpp_presence_announcement),
- wpabuf_len(wpa_s->dpp_presence_announcement),
+ wpabuf_head(msg), wpabuf_len(msg),
2000, wpas_dpp_chirp_tx_status, 0) < 0)
wpas_dpp_chirp_stop(wpa_s);
+
+ wpabuf_free(announce);
}
@@ -2884,8 +3501,9 @@
struct hostapd_hw_modes *mode;
int c;
struct wpa_bss *bss;
+ bool chan6;
- if (!bi)
+ if (!bi && !wpa_s->dpp_reconfig_ssid)
return;
wpa_s->dpp_chirp_scan_done = 1;
@@ -2894,11 +3512,29 @@
wpa_s->dpp_chirp_freqs = NULL;
/* Channels from own bootstrapping info */
- for (i = 0; i < bi->num_freq; i++)
- int_array_add_unique(&wpa_s->dpp_chirp_freqs, bi->freq[i]);
+ if (bi) {
+ for (i = 0; i < bi->num_freq; i++)
+ int_array_add_unique(&wpa_s->dpp_chirp_freqs,
+ bi->freq[i]);
+ }
/* Preferred chirping channels */
- int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ HOSTAPD_MODE_IEEE80211G, 0);
+ chan6 = mode == NULL;
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+ chan->freq != 2437)
+ continue;
+ chan6 = true;
+ break;
+ }
+ }
+ if (chan6)
+ int_array_add_unique(&wpa_s->dpp_chirp_freqs, 2437);
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
HOSTAPD_MODE_IEEE80211A, 0);
@@ -3047,7 +3683,7 @@
pos = os_strstr(cmd, " listen=");
if (pos) {
listen_freq = atoi(pos + 8);
- if (iter <= 0)
+ if (listen_freq <= 0)
return -1;
}
@@ -3069,7 +3705,8 @@
void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s)
{
- if (wpa_s->dpp_presence_announcement) {
+ if (wpa_s->dpp_presence_announcement ||
+ wpa_s->dpp_reconfig_ssid) {
offchannel_send_action_done(wpa_s);
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
}
@@ -3090,4 +3727,168 @@
}
}
+
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct wpa_ssid *ssid;
+ int iter = 1;
+ const char *pos;
+
+ ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+ if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not a valid network profile for reconfiguration");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " iter=");
+ if (pos) {
+ iter = atoi(pos + 6);
+ if (iter <= 0)
+ return -1;
+ }
+
+ if (wpa_s->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not ready to start reconfiguration - pending authentication exchange in progress");
+ return -1;
+ }
+
+ dpp_free_reconfig_id(wpa_s->dpp_reconfig_id);
+ wpa_s->dpp_reconfig_id = dpp_gen_reconfig_id(ssid->dpp_csign,
+ ssid->dpp_csign_len,
+ ssid->dpp_pp_key,
+ ssid->dpp_pp_key_len);
+ if (!wpa_s->dpp_reconfig_id) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to generate E-id for reconfiguration");
+ return -1;
+ }
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_printf(MSG_DEBUG, "DPP: Disconnect for reconfiguration");
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ wpas_dpp_chirp_stop(wpa_s);
+ wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ wpa_s->dpp_qr_mutual = 0;
+ wpa_s->dpp_reconfig_ssid = ssid;
+ wpa_s->dpp_reconfig_ssid_id = ssid->id;
+ wpa_s->dpp_chirp_iter = iter;
+ wpa_s->dpp_chirp_round = 0;
+ wpa_s->dpp_chirp_scan_done = 0;
+ wpa_s->dpp_chirp_listen = 0;
+
+ return eloop_register_timeout(0, 0, wpas_dpp_chirp_next, wpa_s, NULL);
+}
+
+
+static int wpas_dpp_build_conf_resp(struct wpa_supplicant *wpa_s,
+ struct dpp_authentication *auth, bool tcp)
+{
+ struct wpabuf *resp;
+
+ resp = dpp_build_conf_resp(auth, auth->e_nonce, auth->curve->nonce_len,
+ auth->e_netrole, true);
+ if (!resp)
+ return -1;
+
+ if (tcp) {
+ auth->conf_resp_tcp = resp;
+ return 0;
+ }
+
+ if (gas_server_set_resp(wpa_s->gas_server, auth->cert_resp_ctx,
+ resp) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not find pending GAS response");
+ wpabuf_free(resp);
+ return -1;
+ }
+ auth->conf_resp = resp;
+ return 0;
+}
+
+
+int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ int peer = -1;
+ const char *pos, *value;
+ struct dpp_authentication *auth = wpa_s->dpp_auth;
+ u8 *bin;
+ size_t bin_len;
+ struct wpabuf *buf;
+ bool tcp = false;
+
+ pos = os_strstr(cmd, " peer=");
+ if (pos) {
+ peer = atoi(pos + 6);
+ if (!auth || !auth->waiting_cert ||
+ (auth->peer_bi &&
+ (unsigned int) peer != auth->peer_bi->id)) {
+ auth = dpp_controller_get_auth(wpa_s->dpp, peer);
+ tcp = true;
+ }
+ }
+
+ if (!auth || !auth->waiting_cert) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No authentication exchange waiting for certificate information");
+ return -1;
+ }
+
+ if (peer >= 0 &&
+ (!auth->peer_bi ||
+ (unsigned int) peer != auth->peer_bi->id) &&
+ (!auth->tmp_peer_bi ||
+ (unsigned int) peer != auth->tmp_peer_bi->id)) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer mismatch");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " value=");
+ if (!pos)
+ return -1;
+ value = pos + 7;
+
+ pos = os_strstr(cmd, " name=");
+ if (!pos)
+ return -1;
+ pos += 6;
+
+ if (os_strncmp(pos, "status ", 7) == 0) {
+ auth->force_conf_resp_status = atoi(value);
+ return wpas_dpp_build_conf_resp(wpa_s, auth, tcp);
+ }
+
+ if (os_strncmp(pos, "trustedEapServerName ", 21) == 0) {
+ os_free(auth->trusted_eap_server_name);
+ auth->trusted_eap_server_name = os_strdup(value);
+ return auth->trusted_eap_server_name ? 0 : -1;
+ }
+
+ bin = base64_decode(value, os_strlen(value), &bin_len);
+ if (!bin)
+ return -1;
+ buf = wpabuf_alloc_copy(bin, bin_len);
+ os_free(bin);
+
+ if (os_strncmp(pos, "caCert ", 7) == 0) {
+ wpabuf_free(auth->cacert);
+ auth->cacert = buf;
+ return 0;
+ }
+
+ if (os_strncmp(pos, "certBag ", 8) == 0) {
+ wpabuf_free(auth->certbag);
+ auth->certbag = buf;
+ return wpas_dpp_build_conf_resp(wpa_s, auth, tcp);
+ }
+
+ wpabuf_free(buf);
+ return -1;
+}
+
#endif /* CONFIG_DPP2 */
diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h
index 2ce378d..b0d5fcf 100644
--- a/wpa_supplicant/dpp_supplicant.h
+++ b/wpa_supplicant/dpp_supplicant.h
@@ -19,6 +19,8 @@
int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd);
void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s);
+void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq);
void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
@@ -37,5 +39,7 @@
enum dpp_status_error result);
int wpas_dpp_chirp(struct wpa_supplicant *wpa_s, const char *cmd);
void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s);
+int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd);
+int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd);
#endif /* DPP_SUPPLICANT_H */
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 54ae03b..ba8cc55 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1069,14 +1069,14 @@
return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val);
}
-static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s,
- unsigned int num_bssid,
- const u8 *bssids)
+static inline int wpa_drv_set_bssid_tmp_disallow(struct wpa_supplicant *wpa_s,
+ unsigned int num_bssid,
+ const u8 *bssids)
{
- if (!wpa_s->driver->set_bssid_blacklist)
+ if (!wpa_s->driver->set_bssid_tmp_disallow)
return -1;
- return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid,
- bssids);
+ return wpa_s->driver->set_bssid_tmp_disallow(wpa_s->drv_priv, num_bssid,
+ bssids);
}
static inline int wpa_drv_update_connect_params(
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index f531c39..fd4511d 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -356,6 +356,9 @@
int pmksa_set = -1;
size_t i;
+ /* Start with assumption of no PMKSA cache entry match */
+ pmksa_cache_clear_current(wpa_s->wpa);
+
if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
ie.pmkid == NULL)
return;
@@ -1080,32 +1083,407 @@
}
+static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const u8 *match_ssid, size_t match_ssid_len,
+ struct wpa_bss *bss, int blacklist_count,
+ bool debug_print);
+
+
+#ifdef CONFIG_SAE_PK
+static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *orig_bss,
+ struct wpa_ssid *ssid,
+ const u8 *match_ssid,
+ size_t match_ssid_len)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ int count;
+ const u8 *ie;
+ u8 rsnxe_capa = 0;
+
+ if (bss == orig_bss)
+ continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ if (ie && ie[1] >= 1)
+ rsnxe_capa = ie[2];
+ if (!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)))
+ continue;
+
+ /* TODO: Could be more thorough in checking what kind of
+ * signal strength or throughput estimate would be acceptable
+ * compared to the originally selected BSS. */
+ if (bss->est_throughput < 2000)
+ return false;
+
+ count = wpa_blacklist_is_blacklisted(wpa_s, bss->bssid);
+ if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
+ bss, count, 0))
+ return true;
+ }
+
+ return false;
+}
+#endif /* CONFIG_SAE_PK */
+
+
+static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ const u8 *match_ssid, size_t match_ssid_len,
+ struct wpa_bss *bss, int blacklist_count,
+ bool debug_print)
+{
+ int res;
+ bool wpa, check_ssid, osen, rsn_osen = false;
+ struct wpa_ie_data data;
+#ifdef CONFIG_MBO
+ const u8 *assoc_disallow;
+#endif /* CONFIG_MBO */
+#ifdef CONFIG_SAE
+ u8 rsnxe_capa = 0;
+#endif /* CONFIG_SAE */
+ const u8 *ie;
+
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ wpa = ie && ie[1];
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ wpa |= ie && ie[1];
+ if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
+ (data.key_mgmt & WPA_KEY_MGMT_OSEN))
+ rsn_osen = true;
+ ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+ osen = ie != NULL;
+
+#ifdef CONFIG_SAE
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ if (ie && ie[1] >= 1)
+ rsnxe_capa = ie[2];
+#endif /* CONFIG_SAE */
+
+ check_ssid = wpa || ssid->ssid_len > 0;
+
+ if (wpas_network_disabled(wpa_s, ssid)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
+ return false;
+ }
+
+ res = wpas_temp_disabled(wpa_s, ssid);
+ if (res > 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - disabled temporarily for %d second(s)",
+ res);
+ return false;
+ }
+
+#ifdef CONFIG_WPS
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && blacklist_count) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - blacklisted (WPS)");
+ return false;
+ }
+
+ if (wpa && ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = false;
+
+ if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Only allow wildcard SSID match if an AP advertises active
+ * WPS operation that matches our mode. */
+ check_ssid = ssid->ssid_len > 0 ||
+ !wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss);
+ }
+#endif /* CONFIG_WPS */
+
+ if (ssid->bssid_set && ssid->ssid_len == 0 &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+ check_ssid = false;
+
+ if (check_ssid &&
+ (match_ssid_len != ssid->ssid_len ||
+ os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
+ return false;
+ }
+
+ if (ssid->bssid_set &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
+ return false;
+ }
+
+ /* check blacklist */
+ if (ssid->num_bssid_blacklist &&
+ addr_in_list(bss->bssid, ssid->bssid_blacklist,
+ ssid->num_bssid_blacklist)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID blacklisted");
+ return false;
+ }
+
+ /* if there is a whitelist, only accept those APs */
+ if (ssid->num_bssid_whitelist &&
+ !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+ ssid->num_bssid_whitelist)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - BSSID not in whitelist");
+ return false;
+ }
+
+ if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print))
+ return false;
+
+ if (!osen && !wpa &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - non-WPA network not allowed");
+ return false;
+ }
+
+#ifdef CONFIG_WEP
+ if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && has_wep_key(ssid)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - ignore WPA/WPA2 AP for WEP network block");
+ return false;
+ }
+#endif /* CONFIG_WEP */
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - non-OSEN network not allowed");
+ return false;
+ }
+
+ if (!wpa_supplicant_match_privacy(bss, ssid)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy mismatch");
+ return false;
+ }
+
+ if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
+ !bss_is_pbss(bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - not ESS, PBSS, or MBSS");
+ return false;
+ }
+
+ if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - PBSS mismatch (ssid %d bss %d)",
+ ssid->pbss, bss_is_pbss(bss));
+ return false;
+ }
+
+ if (!freq_allowed(ssid->freq_list, bss->freq)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - frequency not allowed");
+ return false;
+ }
+
+#ifdef CONFIG_MESH
+ if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
+ ssid->frequency != bss->freq) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - frequency not allowed (mesh)");
+ return false;
+ }
+#endif /* CONFIG_MESH */
+
+ if (!rate_match(wpa_s, ssid, bss, debug_print)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - rate sets do not match");
+ return false;
+ }
+
+#ifdef CONFIG_SAE
+ if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
+ wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - SAE H2E required, but not supported by the AP");
+ return false;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_SAE_PK
+ if (ssid->sae_pk == SAE_PK_MODE_ONLY &&
+ !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK))) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - SAE-PK required, but not supported by the AP");
+ return false;
+ }
+#endif /* CONFIG_SAE_PK */
+
+#ifndef CONFIG_IBSS_RSN
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - IBSS RSN not supported in the build");
+ return false;
+ }
+#endif /* !CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group &&
+ !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+ !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen");
+ return false;
+ }
+
+ if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+ struct wpabuf *p2p_ie;
+ u8 dev_addr[ETH_ALEN];
+
+ ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (!ie) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no P2P element");
+ return false;
+ }
+ p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (!p2p_ie) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - could not fetch P2P element");
+ return false;
+ }
+
+ if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 ||
+ os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no matching GO P2P Device Address in P2P element");
+ wpabuf_free(p2p_ie);
+ return false;
+ }
+ wpabuf_free(p2p_ie);
+ }
+
+ /*
+ * TODO: skip the AP if its P2P IE has Group Formation bit set in the
+ * P2P Group Capability Bitmap and we are not in Group Formation with
+ * that device.
+ */
+#endif /* CONFIG_P2P */
+
+ if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) {
+ struct os_reltime diff;
+
+ os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff);
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - scan result not recent enough (%u.%06u seconds too old)",
+ (unsigned int) diff.sec,
+ (unsigned int) diff.usec);
+ return false;
+ }
+#ifdef CONFIG_MBO
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->ignore_assoc_disallow)
+ goto skip_assoc_disallow;
+#endif /* CONFIG_TESTING_OPTIONS */
+ assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+ if (assoc_disallow && assoc_disallow[1] >= 1) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - MBO association disallowed (reason %u)",
+ assoc_disallow[2]);
+ return false;
+ }
+
+ if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - AP temporarily disallowed");
+ return false;
+ }
+#ifdef CONFIG_TESTING_OPTIONS
+skip_assoc_disallow:
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_DPP
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+ !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
+ (!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
+ !ssid->dpp_csign)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no PMKSA entry for DPP");
+ return false;
+ }
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_SAE_PK
+ if (ssid->sae_pk == SAE_PK_MODE_AUTOMATIC &&
+ wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ ((ssid->sae_password &&
+ sae_pk_valid_password(ssid->sae_password)) ||
+ (!ssid->sae_password && ssid->passphrase &&
+ sae_pk_valid_password(ssid->passphrase))) &&
+ !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
+ sae_pk_acceptable_bss_with_pk(wpa_s, bss, ssid, match_ssid,
+ match_ssid_len)) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - another acceptable BSS with SAE-PK in the same ESS");
+ return false;
+ }
+#endif /* CONFIG_SAE_PK */
+
+ if (bss->ssid_len == 0) {
+ if (debug_print)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - no SSID known for the BSS");
+ return false;
+ }
+
+ /* Matching configuration found */
+ return true;
+}
+
+
struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
int only_first_ssid, int debug_print)
{
u8 wpa_ie_len, rsn_ie_len;
- int wpa;
- struct wpa_blacklist *e;
const u8 *ie;
struct wpa_ssid *ssid;
- int osen, rsn_osen = 0;
-#ifdef CONFIG_MBO
- const u8 *assoc_disallow;
-#endif /* CONFIG_MBO */
+ int osen;
const u8 *match_ssid;
size_t match_ssid_len;
- struct wpa_ie_data data;
+ int blacklist_count;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa_ie_len = ie ? ie[1] : 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
rsn_ie_len = ie ? ie[1] : 0;
- if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
- (data.key_mgmt & WPA_KEY_MGMT_OSEN))
- rsn_osen = 1;
ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
osen = ie != NULL;
@@ -1125,8 +1503,8 @@
osen ? " osen=1" : "");
}
- e = wpa_blacklist_get(wpa_s, bss->bssid);
- if (e) {
+ blacklist_count = wpa_blacklist_is_blacklisted(wpa_s, bss->bssid);
+ if (blacklist_count) {
int limit = 1;
if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
/*
@@ -1139,11 +1517,11 @@
*/
limit = 0;
}
- if (e->count > limit) {
+ if (blacklist_count > limit) {
if (debug_print) {
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - blacklisted (count=%d limit=%d)",
- e->count, limit);
+ blacklist_count, limit);
}
return NULL;
}
@@ -1177,299 +1555,10 @@
return NULL;
}
- wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
-
for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
- int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
- int res;
-
- if (wpas_network_disabled(wpa_s, ssid)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
- continue;
- }
-
- res = wpas_temp_disabled(wpa_s, ssid);
- if (res > 0) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - disabled temporarily for %d second(s)",
- res);
- continue;
- }
-
-#ifdef CONFIG_WPS
- if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - blacklisted (WPS)");
- continue;
- }
-
- if (wpa && ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
- check_ssid = 0;
-
- if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
- /* Only allow wildcard SSID match if an AP
- * advertises active WPS operation that matches
- * with our mode. */
- check_ssid = 1;
- if (ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
- check_ssid = 0;
- }
-#endif /* CONFIG_WPS */
-
- if (ssid->bssid_set && ssid->ssid_len == 0 &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
- check_ssid = 0;
-
- if (check_ssid &&
- (match_ssid_len != ssid->ssid_len ||
- os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - SSID mismatch");
- continue;
- }
-
- if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - BSSID mismatch");
- continue;
- }
-
- /* check blacklist */
- if (ssid->num_bssid_blacklist &&
- addr_in_list(bss->bssid, ssid->bssid_blacklist,
- ssid->num_bssid_blacklist)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - BSSID blacklisted");
- continue;
- }
-
- /* if there is a whitelist, only accept those APs */
- if (ssid->num_bssid_whitelist &&
- !addr_in_list(bss->bssid, ssid->bssid_whitelist,
- ssid->num_bssid_whitelist)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - BSSID not in whitelist");
- continue;
- }
-
- if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss,
- debug_print))
- continue;
-
- if (!osen && !wpa &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - non-WPA network not allowed");
- continue;
- }
-
-#ifdef CONFIG_WEP
- if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
- has_wep_key(ssid)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - ignore WPA/WPA2 AP for WEP network block");
- continue;
- }
-#endif /* CONFIG_WEP */
-
- if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen &&
- !rsn_osen) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - non-OSEN network not allowed");
- continue;
- }
-
- if (!wpa_supplicant_match_privacy(bss, ssid)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - privacy mismatch");
- continue;
- }
-
- if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
- !bss_is_pbss(bss)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - not ESS, PBSS, or MBSS");
- continue;
- }
-
- if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - PBSS mismatch (ssid %d bss %d)",
- ssid->pbss, bss_is_pbss(bss));
- continue;
- }
-
- if (!freq_allowed(ssid->freq_list, bss->freq)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - frequency not allowed");
- continue;
- }
-
-#ifdef CONFIG_MESH
- if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
- ssid->frequency != bss->freq) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - frequency not allowed (mesh)");
- continue;
- }
-#endif /* CONFIG_MESH */
-
- if (!rate_match(wpa_s, ssid, bss, debug_print)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - rate sets do not match");
- continue;
- }
-
-#ifdef CONFIG_SAE
- if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
- wpa_s->conf->sae_pwe != 3 &&
- wpa_key_mgmt_sae(ssid->key_mgmt) &&
- (!(ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX)) ||
- ie[1] < 1 ||
- !(ie[2] & BIT(WLAN_RSNX_CAPAB_SAE_H2E)))) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - SAE H2E required, but not supported by the AP");
- continue;
- }
-#endif /* CONFIG_SAE */
-
-#ifndef CONFIG_IBSS_RSN
- if (ssid->mode == WPAS_MODE_IBSS &&
- !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
- WPA_KEY_MGMT_WPA_NONE))) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - IBSS RSN not supported in the build");
- continue;
- }
-#endif /* !CONFIG_IBSS_RSN */
-
-#ifdef CONFIG_P2P
- if (ssid->p2p_group &&
- !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
- !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - no P2P IE seen");
- continue;
- }
-
- if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
- struct wpabuf *p2p_ie;
- u8 dev_addr[ETH_ALEN];
-
- ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
- if (ie == NULL) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - no P2P element");
- continue;
- }
- p2p_ie = wpa_bss_get_vendor_ie_multi(
- bss, P2P_IE_VENDOR_TYPE);
- if (p2p_ie == NULL) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - could not fetch P2P element");
- continue;
- }
-
- if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
- || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
- ETH_ALEN) != 0) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - no matching GO P2P Device Address in P2P element");
- wpabuf_free(p2p_ie);
- continue;
- }
- wpabuf_free(p2p_ie);
- }
-
- /*
- * TODO: skip the AP if its P2P IE has Group Formation
- * bit set in the P2P Group Capability Bitmap and we
- * are not in Group Formation with that device.
- */
-#endif /* CONFIG_P2P */
-
- if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
- {
- struct os_reltime diff;
-
- os_reltime_sub(&wpa_s->scan_min_time,
- &bss->last_update, &diff);
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - scan result not recent enough (%u.%06u seconds too old)",
- (unsigned int) diff.sec,
- (unsigned int) diff.usec);
- continue;
- }
-#ifdef CONFIG_MBO
-#ifdef CONFIG_TESTING_OPTIONS
- if (wpa_s->ignore_assoc_disallow)
- goto skip_assoc_disallow;
-#endif /* CONFIG_TESTING_OPTIONS */
- assoc_disallow = wpas_mbo_get_bss_attr(
- bss, MBO_ATTR_ID_ASSOC_DISALLOW);
- if (assoc_disallow && assoc_disallow[1] >= 1) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - MBO association disallowed (reason %u)",
- assoc_disallow[2]);
- continue;
- }
-
- if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - AP temporarily disallowed");
- continue;
- }
-#ifdef CONFIG_TESTING_OPTIONS
- skip_assoc_disallow:
-#endif /* CONFIG_TESTING_OPTIONS */
-#endif /* CONFIG_MBO */
-
-#ifdef CONFIG_DPP
- if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
- !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
- (!ssid->dpp_connector ||
- !ssid->dpp_netaccesskey ||
- !ssid->dpp_csign)) {
- if (debug_print)
- wpa_dbg(wpa_s, MSG_DEBUG,
- " skip - no PMKSA entry for DPP");
- continue;
- }
-#endif /* CONFIG_DPP */
-
- /* Matching configuration found */
- return ssid;
+ if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
+ bss, blacklist_count, debug_print))
+ return ssid;
}
/* No matching configuration found */
@@ -1769,52 +1858,19 @@
return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr);
}
-#endif /* CONFIG_NO_ROAMING */
-
-static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
- struct wpa_bss *selected,
- struct wpa_ssid *ssid)
+int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *current_bss,
+ struct wpa_bss *selected)
{
- struct wpa_bss *current_bss = NULL;
-#ifndef CONFIG_NO_ROAMING
int min_diff, diff;
int to_5ghz;
int cur_level;
unsigned int cur_est, sel_est;
struct wpa_signal_info si;
int cur_snr = 0;
-#endif /* CONFIG_NO_ROAMING */
+ int ret = 0;
- if (wpa_s->reassociate)
- return 1; /* explicit request to reassociate */
- if (wpa_s->wpa_state < WPA_ASSOCIATED)
- return 1; /* we are not associated; continue */
- if (wpa_s->current_ssid == NULL)
- return 1; /* unknown current SSID */
- if (wpa_s->current_ssid != ssid)
- return 1; /* different network block */
-
- if (wpas_driver_bss_selection(wpa_s))
- return 0; /* Driver-based roaming */
-
- if (wpa_s->current_ssid->ssid)
- current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
- wpa_s->current_ssid->ssid,
- wpa_s->current_ssid->ssid_len);
- if (!current_bss)
- current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
-
- if (!current_bss)
- return 1; /* current BSS not seen in scan results */
-
- if (current_bss == selected)
- return 0;
-
- if (selected->last_update_idx > current_bss->last_update_idx)
- return 1; /* current BSS not seen in the last scan */
-
-#ifndef CONFIG_NO_ROAMING
wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
" freq=%d level=%d snr=%d est_throughput=%u",
@@ -1933,13 +1989,64 @@
wpa_dbg(wpa_s, MSG_DEBUG,
"Skip roam - too small difference in signal level (%d < %d)",
diff, min_diff);
- return 0;
+ ret = 0;
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Allow reassociation due to difference in signal level (%d >= %d)",
+ diff, min_diff);
+ ret = 1;
}
+ wpa_msg_ctrl(wpa_s, MSG_INFO, "%scur_bssid=" MACSTR
+ " cur_freq=%d cur_level=%d cur_est=%d sel_bssid=" MACSTR
+ " sel_freq=%d sel_level=%d sel_est=%d",
+ ret ? WPA_EVENT_DO_ROAM : WPA_EVENT_SKIP_ROAM,
+ MAC2STR(current_bss->bssid),
+ current_bss->freq, cur_level, cur_est,
+ MAC2STR(selected->bssid),
+ selected->freq, selected->level, sel_est);
+ return ret;
+}
- wpa_dbg(wpa_s, MSG_DEBUG,
- "Allow reassociation due to difference in signal level (%d >= %d)",
- diff, min_diff);
- return 1;
+#endif /* CONFIG_NO_ROAMING */
+
+
+static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
+{
+ struct wpa_bss *current_bss = NULL;
+
+ if (wpa_s->reassociate)
+ return 1; /* explicit request to reassociate */
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return 1; /* we are not associated; continue */
+ if (wpa_s->current_ssid == NULL)
+ return 1; /* unknown current SSID */
+ if (wpa_s->current_ssid != ssid)
+ return 1; /* different network block */
+
+ if (wpas_driver_bss_selection(wpa_s))
+ return 0; /* Driver-based roaming */
+
+ if (wpa_s->current_ssid->ssid)
+ current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+ wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len);
+ if (!current_bss)
+ current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
+
+ if (!current_bss)
+ return 1; /* current BSS not seen in scan results */
+
+ if (current_bss == selected)
+ return 0;
+
+ if (selected->last_update_idx > current_bss->last_update_idx)
+ return 1; /* current BSS not seen in the last scan */
+
+#ifndef CONFIG_NO_ROAMING
+ return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss,
+ selected);
#else /* CONFIG_NO_ROAMING */
return 0;
#endif /* CONFIG_NO_ROAMING */
@@ -2571,11 +2678,11 @@
{
int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
const u8 *p;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE)
u8 bssid[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R || CONFIG_OWE */
+ bool bssid_known;
wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
+ bssid_known = wpa_drv_get_bssid(wpa_s, bssid) == 0;
if (data->assoc_info.req_ies)
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
data->assoc_info.req_ies_len);
@@ -2621,6 +2728,8 @@
data->assoc_info.resp_ies_len,
&resp_elems, 0) != ParseFailed) {
wpa_s->connection_set = 1;
+ wpa_s->connection_11b_only = supp_rates_11b_only(&req_elems) ||
+ supp_rates_11b_only(&resp_elems);
wpa_s->connection_ht = req_elems.ht_capabilities &&
resp_elems.ht_capabilities;
/* Do not include subset of VHT on 2.4 GHz vendor
@@ -2716,7 +2825,7 @@
#ifdef CONFIG_OWE
if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
- (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ (!bssid_known ||
owe_process_assoc_resp(wpa_s->wpa, bssid,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len) < 0)) {
@@ -2727,7 +2836,8 @@
#ifdef CONFIG_DPP2
wpa_sm_set_dpp_z(wpa_s->wpa, NULL);
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->dpp_pfs) {
+ if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP &&
+ wpa_s->dpp_pfs) {
struct ieee802_11_elems elems;
if (ieee802_11_parse_elems(data->assoc_info.resp_ies,
@@ -2750,7 +2860,7 @@
#ifdef CONFIG_IEEE80211R
#ifdef CONFIG_SME
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
- if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ if (!bssid_known ||
wpa_ft_validate_reassoc_resp(wpa_s->wpa,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
@@ -2810,7 +2920,7 @@
/* Process FT when SME is in the driver */
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
wpa_ft_is_completed(wpa_s->wpa)) {
- if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+ if (!bssid_known ||
wpa_ft_validate_reassoc_resp(wpa_s->wpa,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
@@ -2828,6 +2938,11 @@
data->assoc_info.resp_ies_len);
#endif /* CONFIG_IEEE80211R */
+ if (bssid_known)
+ wpas_handle_assoc_resp_mscs(wpa_s, bssid,
+ data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+
/* WPA/RSN IE from Beacon/ProbeResp */
p = data->assoc_info.beacon_ies;
l = data->assoc_info.beacon_ies_len;
@@ -4161,6 +4276,13 @@
}
#endif /* CONFIG_DPP */
+ if (category == WLAN_ACTION_ROBUST_AV_STREAMING &&
+ payload[0] == ROBUST_AV_MSCS_RESP) {
+ wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa,
+ payload + 1, plen - 1);
+ return;
+ }
+
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
category, payload, plen, freq);
if (wpa_s->ifmsh)
@@ -4364,8 +4486,8 @@
data->assoc_reject.timeout_reason ?
data->assoc_reject.timeout_reason : "");
wpa_s->assoc_status_code = data->assoc_reject.status_code;
- wpa_s->assoc_timed_out = data->assoc_reject.timed_out;
- wpas_notify_assoc_status_code(wpa_s);
+ wpas_notify_assoc_status_code(wpa_s,
+ bssid, data->assoc_reject.timed_out);
#ifdef CONFIG_OWE
if (data->assoc_reject.status_code ==
@@ -4398,8 +4520,10 @@
* the status code defined in the DPP R2 tech spec.
* WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an
* interoperability workaround with older hostapd implementation. */
- if (wpa_s->current_ssid &&
- wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
+ if (DPP_VERSION > 1 && wpa_s->current_ssid &&
+ (wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP ||
+ ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
+ wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)) &&
wpa_s->current_ssid->dpp_pfs == 0 &&
(data->assoc_reject.status_code ==
WLAN_STATUS_ASSOC_DENIED_UNSPEC ||
@@ -4862,7 +4986,9 @@
}
#endif /* CONFIG_AP */
- sme_event_ch_switch(wpa_s);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_ch_switch(wpa_s);
+
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
wnm_clear_coloc_intf_reporting(wpa_s);
break;
@@ -5029,6 +5155,11 @@
wpas_p2p_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
+#ifdef CONFIG_DPP
+ wpas_dpp_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_DPP */
break;
case EVENT_CANCEL_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
@@ -5368,8 +5499,6 @@
return;
wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
os_free(wpa_i);
- if (wpa_s)
- wpa_s->matched = 1;
}
#endif /* CONFIG_MATCH_IFACE */
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index f49da34..8e865f3 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -7,7 +7,10 @@
# This software may be distributed under the terms of the BSD license.
# See README for more details.
+import binascii
+import errno
import os
+import struct
import sys
import time
import threading
@@ -18,7 +21,7 @@
import logging
-scriptsdir = os.path.dirname(os.path.realpath("dpp-nfc.py"))
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
import wpaspy
@@ -28,17 +31,31 @@
in_raw_mode = False
prev_tcgetattr = 0
no_input = False
-srv = None
continue_loop = True
terminate_now = False
summary_file = None
success_file = None
+netrole = None
+operation_success = False
+mutex = threading.Lock()
-def summary(txt):
- print(txt)
- if summary_file:
- with open(summary_file, 'a') as f:
- f.write(txt + "\n")
+C_NORMAL = '\033[0m'
+C_RED = '\033[91m'
+C_GREEN = '\033[92m'
+C_YELLOW = '\033[93m'
+C_BLUE = '\033[94m'
+C_MAGENTA = '\033[95m'
+C_CYAN = '\033[96m'
+
+def summary(txt, color=None):
+ with mutex:
+ if color:
+ print(color + txt + C_NORMAL)
+ else:
+ print(txt)
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
def success_report(txt):
summary(txt)
@@ -52,23 +69,26 @@
try:
ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
except OSError as error:
- print("Could not find wpa_supplicant: ", error)
+ summary("Could not find wpa_supplicant: %s", str(error))
return None
if len(ifaces) < 1:
- print("No wpa_supplicant control interface found")
+ summary("No wpa_supplicant control interface found")
return None
for ctrl in ifaces:
- if ifname:
- if ifname not in ctrl:
- continue
+ if ifname and ifname not in ctrl:
+ continue
+ if os.path.basename(ctrl).startswith("p2p-dev-"):
+ # skip P2P management interface
+ continue
try:
- print("Trying to use control interface " + ctrl)
+ summary("Trying to use control interface " + ctrl)
wpas = wpaspy.Ctrl(ctrl)
return wpas
except Exception as e:
pass
+ summary("Could not connect to wpa_supplicant")
return None
def dpp_nfc_uri_process(uri):
@@ -77,38 +97,46 @@
return False
peer_id = wpas.request("DPP_NFC_URI " + uri)
if "FAIL" in peer_id:
- print("Could not parse DPP URI from NFC URI record")
+ summary("Could not parse DPP URI from NFC URI record", color=C_RED)
return False
peer_id = int(peer_id)
- print("peer_id=%d" % peer_id)
+ summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
cmd = "DPP_AUTH_INIT peer=%d" % peer_id
+ global enrollee_only, configurator_only, config_params
+ if enrollee_only:
+ cmd += " role=enrollee"
+ elif configurator_only:
+ cmd += " role=configurator"
+ if config_params:
+ cmd += " " + config_params
+ summary("Initiate DPP authentication: " + cmd)
res = wpas.request(cmd)
if "OK" not in res:
- print("Failed to initiate DPP Authentication")
+ summary("Failed to initiate DPP Authentication", color=C_RED)
return False
- print("DPP Authentication initiated")
+ summary("DPP Authentication initiated")
return True
def dpp_hs_tag_read(record):
wpas = wpas_connect()
if wpas is None:
return False
- print(record)
+ summary(record)
if len(record.data) < 5:
- print("Too short DPP HS")
+ summary("Too short DPP HS", color=C_RED)
return False
if record.data[0] != 0:
- print("Unexpected URI Identifier Code")
+ summary("Unexpected URI Identifier Code", color=C_RED)
return False
uribuf = record.data[1:]
try:
uri = uribuf.decode()
except:
- print("Invalid URI payload")
+ summary("Invalid URI payload", color=C_RED)
return False
- print("URI: " + uri)
+ summary("URI: " + uri)
if not uri.startswith("DPP:"):
- print("Not a DPP URI")
+ summary("Not a DPP URI", color=C_RED)
return False
return dpp_nfc_uri_process(uri)
@@ -124,7 +152,7 @@
try:
[name, value] = l.split('=', 1)
except ValueError:
- logger.info("Ignore unexpected status line: " + l)
+ summary("Ignore unexpected status line: %s" % l)
continue
vals[name] = value
return vals
@@ -136,7 +164,10 @@
return None
def own_addr(wpas):
- return get_status_field(wpas, "address")
+ addr = get_status_field(wpas, "address")
+ if addr is None:
+ addr = get_status_field(wpas, "bssid[0]")
+ return addr
def dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None,
curve=None, key=None):
@@ -146,7 +177,10 @@
if mac:
if mac is True:
mac = own_addr(wpas)
- cmd += " mac=" + mac.replace(':', '')
+ if mac is None:
+ summary("Could not determine local MAC address for bootstrap info")
+ else:
+ cmd += " mac=" + mac.replace(':', '')
if info:
cmd += " info=" + info
if curve:
@@ -158,17 +192,62 @@
raise Exception("Failed to generate bootstrapping info")
return int(res)
-def wpas_get_nfc_uri(start_listen=True):
+def dpp_start_listen(wpas, freq):
+ if get_status_field(wpas, "bssid[0]"):
+ summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
+ if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
+ summary("Enable beaconing to have radio ready for RX")
+ wpas.request("DISABLE")
+ wpas.request("SET start_disabled 0")
+ wpas.request("ENABLE")
+ cmd = "DPP_LISTEN %d" % freq
+ global enrollee_only
+ global configurator_only
+ if enrollee_only:
+ cmd += " role=enrollee"
+ elif configurator_only:
+ cmd += " role=configurator"
+ global netrole
+ if netrole:
+ cmd += " netrole=" + netrole
+ summary(cmd)
+ res = wpas.request(cmd)
+ if "OK" not in res:
+ summary("Failed to start DPP listen", color=C_RED)
+ return False
+ return True
+
+def wpas_get_nfc_uri(start_listen=True, pick_channel=False, chan_override=None):
+ listen_freq = 2412
wpas = wpas_connect()
if wpas is None:
return None
global own_id, chanlist
- own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chanlist, mac=True)
+ if chan_override:
+ chan = chan_override
+ else:
+ chan = chanlist
+ if chan and chan.startswith("81/"):
+ listen_freq = int(chan[3:].split(',')[0]) * 5 + 2407
+ if chan is None and get_status_field(wpas, "bssid[0]"):
+ freq = get_status_field(wpas, "freq")
+ if freq:
+ freq = int(freq)
+ if freq >= 2412 and freq <= 2462:
+ chan = "81/%d" % ((freq - 2407) / 5)
+ summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
+ listen_freq = freq
+ if chan is None and pick_channel:
+ chan = "81/6"
+ summary("Use channel 2437 MHz since no other preference provided")
+ listen_freq = 2437
+ own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
if "FAIL" in res:
return None
if start_listen:
- wpas.request("DPP_LISTEN 2412 netrole=configurator")
+ if not dpp_start_listen(wpas, listen_freq):
+ raise Exception("Failed to start listen operation on %d MHz" % listen_freq)
return res
def wpas_report_handover_req(uri):
@@ -187,100 +266,248 @@
cmd = "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id, uri)
return wpas.request(cmd)
-def dpp_handover_client(llc):
- uri = wpas_get_nfc_uri(start_listen=False)
+def dpp_handover_client(handover, alt=False):
+ summary("About to start run_dpp_handover_client (alt=%s)" % str(alt))
+ if alt:
+ handover.i_m_selector = False
+ run_dpp_handover_client(handover, alt)
+ summary("Done run_dpp_handover_client (alt=%s)" % str(alt))
+
+def run_client_alt(handover, alt):
+ if handover.start_client_alt and not alt:
+ handover.start_client_alt = False
+ summary("Try to send alternative handover request")
+ dpp_handover_client(handover, alt=True)
+
+class HandoverClient(nfc.handover.HandoverClient):
+ def __init__(self, handover, llc):
+ super(HandoverClient, self).__init__(llc)
+ self.handover = handover
+
+ def recv_records(self, timeout=None):
+ msg = self.recv_octets(timeout)
+ if msg is None:
+ return None
+ records = list(ndef.message_decoder(msg, 'relax'))
+ if records and records[0].type == 'urn:nfc:wkt:Hs':
+ summary("Handover client received message '{0}'".format(records[0].type))
+ return list(ndef.message_decoder(msg, 'relax'))
+ summary("Handover client received invalid message: %s" + binascii.hexlify(msg))
+ return None
+
+ def recv_octets(self, timeout=None):
+ start = time.time()
+ msg = bytearray()
+ while True:
+ poll_timeout = 0.1 if timeout is None or timeout > 0.1 else timeout
+ if not self.socket.poll('recv', poll_timeout):
+ if timeout:
+ timeout -= time.time() - start
+ if timeout <= 0:
+ return None
+ start = time.time()
+ continue
+ try:
+ r = self.socket.recv()
+ if r is None:
+ return None
+ msg += r
+ except TypeError:
+ return b''
+ try:
+ list(ndef.message_decoder(msg, 'strict', {}))
+ return bytes(msg)
+ except ndef.DecodeError:
+ if timeout:
+ timeout -= time.time() - start
+ if timeout <= 0:
+ return None
+ start = time.time()
+ continue
+ return None
+
+def run_dpp_handover_client(handover, alt=False):
+ chan_override = None
+ if alt:
+ chan_override = handover.altchanlist
+ handover.alt_proposal_used = True
+ global test_uri, test_alt_uri
+ if test_uri:
+ summary("TEST MODE: Using specified URI (alt=%s)" % str(alt))
+ uri = test_alt_uri if alt else test_uri
+ else:
+ uri = wpas_get_nfc_uri(start_listen=False, chan_override=chan_override)
+ if uri is None:
+ summary("Cannot start handover client - no bootstrap URI available",
+ color=C_RED)
+ return
+ handover.my_uri = uri
uri = ndef.UriRecord(uri)
- print("NFC URI record for DPP: " + str(uri))
+ summary("NFC URI record for DPP: " + str(uri))
carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
- hr = ndef.HandoverRequestRecord(version="1.4", crn=os.urandom(2))
+ global test_crn
+ if test_crn:
+ prev, = struct.unpack('>H', test_crn)
+ summary("TEST MODE: Use specified crn %d" % prev)
+ crn = test_crn
+ test_crn = struct.pack('>H', prev + 0x10)
+ else:
+ crn = os.urandom(2)
+ hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
hr.add_alternative_carrier('active', carrier.name)
message = [hr, carrier]
- print("NFC Handover Request message for DPP: " + str(message))
+ summary("NFC Handover Request message for DPP: " + str(message))
- client = nfc.handover.HandoverClient(llc)
- try:
- summary("Trying to initiate NFC connection handover")
- client.connect()
- summary("Connected for handover")
- except nfc.llcp.ConnectRefused:
- summary("Handover connection refused")
- client.close()
+ if handover.peer_crn is not None and not alt:
+ summary("NFC handover request from peer was already received - do not send own")
return
- except Exception as e:
- summary("Other exception: " + str(e))
- client.close()
+ if handover.client:
+ summary("Use already started handover client")
+ client = handover.client
+ else:
+ summary("Start handover client")
+ client = HandoverClient(handover, handover.llc)
+ try:
+ summary("Trying to initiate NFC connection handover")
+ client.connect()
+ summary("Connected for handover")
+ except nfc.llcp.ConnectRefused:
+ summary("Handover connection refused")
+ client.close()
+ return
+ except Exception as e:
+ summary("Other exception: " + str(e))
+ client.close()
+ return
+ handover.client = client
+
+ if handover.peer_crn is not None and not alt:
+ summary("NFC handover request from peer was already received - do not send own")
return
summary("Sending handover request")
+ handover.my_crn_ready = True
+
if not client.send_records(message):
- summary("Failed to send handover request")
- client.close()
+ handover.my_crn_ready = False
+ summary("Failed to send handover request", color=C_RED)
+ run_client_alt(handover, alt)
return
+ handover.my_crn, = struct.unpack('>H', crn)
+
summary("Receiving handover response")
- message = client.recv_records(timeout=3.0)
+ try:
+ start = time.time()
+ message = client.recv_records(timeout=3.0)
+ end = time.time()
+ summary("Received {} record(s) in {} seconds".format(len(message) if message is not None else -1, end - start))
+ except Exception as e:
+ # This is fine if we are the handover selector
+ if handover.hs_sent:
+ summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
+ elif handover.alt_proposal_used and not alt:
+ summary("Client received failed for initial proposal as expected since alternative proposal was also used: %s" % str(e))
+ else:
+ summary("Client receive failed: %s" % str(e), color=C_RED)
+ message = None
if message is None:
- summary("No response received")
- client.close()
+ if handover.hs_sent:
+ summary("No response received as expected since I'm the handover server")
+ elif handover.alt_proposal_used and not alt:
+ summary("No response received for initial proposal as expected since alternative proposal was also used")
+ elif handover.try_own and not alt:
+ summary("No response received for initial proposal as expected since alternative proposal will also be sent")
+ else:
+ summary("No response received", color=C_RED)
+ run_client_alt(handover, alt)
return
- print("Received message: " + str(message))
+ summary("Received message: " + str(message))
if len(message) < 1 or \
not isinstance(message[0], ndef.HandoverSelectRecord):
summary("Response was not Hs - received: " + message.type)
- client.close()
return
- print("Received message")
- print("alternative carriers: " + str(message[0].alternative_carriers))
+ summary("Received handover select message")
+ summary("alternative carriers: " + str(message[0].alternative_carriers))
+ if handover.i_m_selector:
+ summary("Ignore the received select since I'm the handover selector")
+ run_client_alt(handover, alt)
+ return
+
+ if handover.alt_proposal_used and not alt:
+ summary("Ignore received handover select for the initial proposal since alternative proposal was sent")
+ client.close()
+ return
dpp_found = False
for carrier in message:
if isinstance(carrier, ndef.HandoverSelectRecord):
continue
- print("Remote carrier type: " + carrier.type)
+ summary("Remote carrier type: " + carrier.type)
if carrier.type == "application/vnd.wfa.dpp":
if len(carrier.data) == 0 or carrier.data[0] != 0:
- print("URI Identifier Code 'None' not seen")
+ summary("URI Identifier Code 'None' not seen", color=C_RED)
continue
- print("DPP carrier type match - send to wpa_supplicant")
+ summary("DPP carrier type match - send to wpa_supplicant")
dpp_found = True
uri = carrier.data[1:].decode("utf-8")
- print("DPP URI: " + uri)
+ summary("DPP URI: " + uri)
+ handover.peer_uri = uri
+ if test_uri:
+ summary("TEST MODE: Fake processing")
+ break
res = wpas_report_handover_sel(uri)
if res is None or "FAIL" in res:
- summary("DPP handover report rejected")
+ summary("DPP handover report rejected", color=C_RED)
break
success_report("DPP handover reported successfully (initiator)")
- print("peer_id=" + res)
+ summary("peer_id=" + res)
peer_id = int(res)
- # TODO: Single Configurator instance
wpas = wpas_connect()
if wpas is None:
break
- res = wpas.request("DPP_CONFIGURATOR_ADD")
- if "FAIL" in res:
- print("Failed to initiate Configurator")
- break
- conf_id = int(res)
+
+ global enrollee_only
+ global config_params
+ if enrollee_only:
+ extra = " role=enrollee"
+ elif config_params:
+ extra = " role=configurator " + config_params
+ else:
+ # TODO: Single Configurator instance
+ res = wpas.request("DPP_CONFIGURATOR_ADD")
+ if "FAIL" in res:
+ summary("Failed to initiate Configurator", color=C_RED)
+ break
+ conf_id = int(res)
+ extra = " conf=sta-dpp configurator=%d" % conf_id
global own_id
- print("Initiate DPP authentication")
- cmd = "DPP_AUTH_INIT peer=%d own=%d conf=sta-dpp configurator=%d" % (peer_id, own_id, conf_id)
+ summary("Initiate DPP authentication")
+ cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id)
+ cmd += extra
res = wpas.request(cmd)
if "FAIL" in res:
- print("Failed to initiate DPP authentication")
+ summary("Failed to initiate DPP authentication", color=C_RED)
break
- if not dpp_found:
- print("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
- client.close()
- print("Returning from dpp_handover_client")
+ if not dpp_found and handover.no_alt_proposal:
+ summary("DPP carrier not seen in response - do not allow alternative proposal anymore")
+ elif not dpp_found:
+ summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
+ handover.alt_proposal = True
+ handover.my_crn_ready = False
+ handover.my_crn = None
+ handover.peer_crn = None
+ handover.hs_sent = False
+ summary("Returning from dpp_handover_client")
return
- print("Remove peer")
- client.close()
- print("Done with handover")
+ summary("Remove peer")
+ handover.close()
+ summary("Done with handover")
global only_one
if only_one:
print("only_one -> stop loop")
@@ -288,27 +515,107 @@
continue_loop = False
global no_wait
- if no_wait:
- print("Trying to exit..")
+ if no_wait or only_one:
+ summary("Trying to exit..")
global terminate_now
terminate_now = True
- print("Returning from dpp_handover_client")
+ summary("Returning from dpp_handover_client")
class HandoverServer(nfc.handover.HandoverServer):
- def __init__(self, llc):
+ def __init__(self, handover, llc):
super(HandoverServer, self).__init__(llc)
self.sent_carrier = None
self.ho_server_processing = False
self.success = False
- self.try_own = False
+ self.llc = llc
+ self.handover = handover
+
+ def serve(self, socket):
+ peer_sap = socket.getpeername()
+ summary("Serving handover client on remote sap {0}".format(peer_sap))
+ send_miu = socket.getsockopt(nfc.llcp.SO_SNDMIU)
+ try:
+ while socket.poll("recv"):
+ req = bytearray()
+ while socket.poll("recv"):
+ r = socket.recv()
+ if r is None:
+ return None
+ summary("Received %d octets" % len(r))
+ req += r
+ if len(req) == 0:
+ continue
+ try:
+ list(ndef.message_decoder(req, 'strict', {}))
+ except ndef.DecodeError:
+ continue
+ summary("Full message received")
+ resp = self._process_request_data(req)
+ if resp is None or len(resp) == 0:
+ summary("No handover select to send out - wait for a possible alternative handover request")
+ handover.alt_proposal = True
+ req = bytearray()
+ continue
+
+ for offset in range(0, len(resp), send_miu):
+ if not socket.send(resp[offset:offset + send_miu]):
+ summary("Failed to send handover select - connection closed")
+ return
+ summary("Sent out full handover select")
+ if handover.terminate_on_hs_send_completion:
+ handover.delayed_exit()
+
+ except nfc.llcp.Error as e:
+ global terminate_now
+ summary("HandoverServer exception: %s" % e,
+ color=None if e.errno == errno.EPIPE or terminate_now else C_RED)
+ finally:
+ socket.close()
+ summary("Handover serve thread exiting")
def process_handover_request_message(self, records):
+ handover = self.handover
self.ho_server_processing = True
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
clear_raw_mode()
- print("\nHandoverServer - request received: " + str(records))
+ if was_in_raw_mode:
+ print("\n")
+ summary("HandoverServer - request received: " + str(records))
- carrier = None
+ for carrier in records:
+ if not isinstance(carrier, ndef.HandoverRequestRecord):
+ continue
+ if carrier.collision_resolution_number:
+ handover.peer_crn = carrier.collision_resolution_number
+ summary("peer_crn: %d" % handover.peer_crn)
+
+ if handover.my_crn is None and handover.my_crn_ready:
+ summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
+ for i in range(10):
+ if handover.my_crn is not None:
+ break
+ time.sleep(0.01)
+ if handover.my_crn is not None:
+ summary("my_crn: %d" % handover.my_crn)
+
+ if handover.my_crn is not None and handover.peer_crn is not None:
+ if handover.my_crn == handover.peer_crn:
+ summary("Same crn used - automatic collision resolution failed")
+ # TODO: Should generate a new Handover Request message
+ return ''
+ if ((handover.my_crn & 1) == (handover.peer_crn & 1) and \
+ handover.my_crn > handover.peer_crn) or \
+ ((handover.my_crn & 1) != (handover.peer_crn & 1) and \
+ handover.my_crn < handover.peer_crn):
+ summary("I'm the Handover Selector Device")
+ handover.i_m_selector = True
+ else:
+ summary("Peer is the Handover Selector device")
+ summary("Ignore the received request.")
+ return ''
+
hs = ndef.HandoverSelectRecord('1.4')
sel = [hs]
@@ -317,61 +624,106 @@
for carrier in records:
if isinstance(carrier, ndef.HandoverRequestRecord):
continue
- print("Remote carrier type: " + carrier.type)
+ summary("Remote carrier type: " + carrier.type)
if carrier.type == "application/vnd.wfa.dpp":
- print("DPP carrier type match - add DPP carrier record")
+ summary("DPP carrier type match - add DPP carrier record")
if len(carrier.data) == 0 or carrier.data[0] != 0:
- print("URI Identifier Code 'None' not seen")
+ summary("URI Identifier Code 'None' not seen", color=C_RED)
continue
uri = carrier.data[1:].decode("utf-8")
- print("Received DPP URI: " + uri)
+ summary("Received DPP URI: " + uri)
- data = wpas_get_nfc_uri(start_listen=False)
- print("Own URI (pre-processing): %s" % data)
+ global test_uri, test_alt_uri
+ if test_uri:
+ summary("TEST MODE: Using specified URI")
+ data = test_sel_uri if test_sel_uri else test_uri
+ elif handover.alt_proposal and handover.altchanlist:
+ summary("Use alternative channel list while processing alternative proposal from peer")
+ data = wpas_get_nfc_uri(start_listen=False,
+ chan_override=handover.altchanlist,
+ pick_channel=True)
+ else:
+ data = wpas_get_nfc_uri(start_listen=False,
+ pick_channel=True)
+ summary("Own URI (pre-processing): %s" % data)
- res = wpas_report_handover_req(uri)
+ if test_uri:
+ summary("TEST MODE: Fake processing")
+ res = "OK"
+ data += " [%s]" % uri
+ else:
+ res = wpas_report_handover_req(uri)
if res is None or "FAIL" in res:
- print("DPP handover request processing failed")
+ summary("DPP handover request processing failed",
+ color=C_RED)
+ if handover.altchanlist:
+ data = wpas_get_nfc_uri(start_listen=False,
+ chan_override=handover.altchanlist)
+ summary("Own URI (try another channel list): %s" % data)
+ continue
+
+ if test_alt_uri:
+ summary("TEST MODE: Reject initial proposal")
continue
found = True
- self.received_carrier = carrier
- wpas = wpas_connect()
- if wpas is None:
- continue
- global own_id
- data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
- if "FAIL" in data:
- continue
- print("Own URI (post-processing): %s" % data)
+ if not test_uri:
+ wpas = wpas_connect()
+ if wpas is None:
+ continue
+ global own_id
+ data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
+ if "FAIL" in data:
+ continue
+ summary("Own URI (post-processing): %s" % data)
+ handover.my_uri = data
+ handover.peer_uri = uri
uri = ndef.UriRecord(data)
- print("Own bootstrapping NFC URI record: " + str(uri))
+ summary("Own bootstrapping NFC URI record: " + str(uri))
- info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
- freq = None
- for line in info.splitlines():
- if line.startswith("use_freq="):
- freq = int(line.split('=')[1])
- if freq is None:
- print("No channel negotiated over NFC - use channel 1")
- freq = 2412
- res = wpas.request("DPP_LISTEN %d" % freq)
- if "OK" not in res:
- print("Failed to start DPP listen")
- break
+ if not test_uri:
+ info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
+ freq = None
+ for line in info.splitlines():
+ if line.startswith("use_freq="):
+ freq = int(line.split('=')[1])
+ if freq is None or freq == 0:
+ summary("No channel negotiated over NFC - use channel 6")
+ freq = 2437
+ else:
+ summary("Negotiated channel: %d MHz" % freq)
+ if not dpp_start_listen(wpas, freq):
+ break
carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
- print("Own DPP carrier record: " + str(carrier))
+ summary("Own DPP carrier record: " + str(carrier))
hs.add_alternative_carrier('active', carrier.name)
sel = [hs, carrier]
break
summary("Sending handover select: " + str(sel))
if found:
+ summary("Handover completed successfully")
+ handover.terminate_on_hs_send_completion = True
self.success = True
+ handover.hs_sent = True
+ handover.i_m_selector = True
+ elif handover.no_alt_proposal:
+ summary("Do not try alternative proposal anymore - handover failed",
+ color=C_RED)
+ handover.hs_sent = True
else:
- self.try_own = True
+ summary("Try to initiate with alternative parameters")
+ handover.try_own = True
+ handover.hs_sent = False
+ handover.no_alt_proposal = True
+ if handover.client_thread:
+ handover.start_client_alt = True
+ else:
+ handover.client_thread = threading.Thread(target=llcp_worker,
+ args=(self.llc, True))
+ handover.client_thread.start()
return sel
def clear_raw_mode():
@@ -403,22 +755,22 @@
def dpp_tag_read(tag):
success = False
for record in tag.ndef.records:
- print(record)
- print("record type " + record.type)
+ summary(record)
+ summary("record type " + record.type)
if record.type == "application/vnd.wfa.dpp":
summary("DPP HS tag - send to wpa_supplicant")
success = dpp_hs_tag_read(record)
break
if isinstance(record, ndef.UriRecord):
- print("URI record: uri=" + record.uri)
- print("URI record: iri=" + record.iri)
+ summary("URI record: uri=" + record.uri)
+ summary("URI record: iri=" + record.iri)
if record.iri.startswith("DPP:"):
- print("DPP URI")
+ summary("DPP URI")
if not dpp_nfc_uri_process(record.iri):
break
success = True
else:
- print("Ignore unknown URI")
+ summary("Ignore unknown URI")
break
if success:
@@ -428,17 +780,25 @@
def rdwr_connected_write_tag(tag):
summary("Tag found - writing - " + str(tag))
+ if not tag.ndef:
+ summary("Not a formatted NDEF tag", color=C_RED)
+ return
if not tag.ndef.is_writeable:
- print("Not a writable tag")
+ summary("Not a writable tag", color=C_RED)
return
global dpp_tag_data
if tag.ndef.capacity < len(dpp_tag_data):
- print("Not enough room for the message")
+ summary("Not enough room for the message")
return
- tag.ndef.records = dpp_tag_data
+ try:
+ tag.ndef.records = dpp_tag_data
+ except ValueError as e:
+ summary("Writing the tag failed: %s" % str(e), color=C_RED)
+ return
success_report("Tag write succeeded")
- print("Done - remove tag")
- global only_one
+ summary("Tag writing completed - remove tag", color=C_GREEN)
+ global only_one, operation_success
+ operation_success = True
if only_one:
global continue_loop
continue_loop = False
@@ -446,45 +806,45 @@
return dpp_sel_wait_remove
def write_nfc_uri(clf, wait_remove=True):
- print("Write NFC URI record")
+ summary("Write NFC URI record")
data = wpas_get_nfc_uri()
if data is None:
- summary("Could not get NFC URI from wpa_supplicant")
+ summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
return
global dpp_sel_wait_remove
dpp_sel_wait_remove = wait_remove
- print("URI: %s" % data)
+ summary("URI: %s" % data)
uri = ndef.UriRecord(data)
- print(uri)
+ summary(uri)
- print("Touch an NFC tag")
+ summary("Touch an NFC tag to write URI record", color=C_CYAN)
global dpp_tag_data
dpp_tag_data = [uri]
clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
def write_nfc_hs(clf, wait_remove=True):
- print("Write NFC Handover Select record on a tag")
+ summary("Write NFC Handover Select record on a tag")
data = wpas_get_nfc_uri()
if data is None:
- summary("Could not get NFC URI from wpa_supplicant")
+ summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
return
global dpp_sel_wait_remove
dpp_sel_wait_remove = wait_remove
- print("URI: %s" % data)
+ summary("URI: %s" % data)
uri = ndef.UriRecord(data)
- print(uri)
+ summary(uri)
carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
hs = ndef.HandoverSelectRecord('1.4')
hs.add_alternative_carrier('active', carrier.name)
- print(hs)
- print(carrier)
+ summary(hs)
+ summary(carrier)
- print("Touch an NFC tag")
+ summary("Touch an NFC tag to write HS record", color=C_CYAN)
global dpp_tag_data
dpp_tag_data = [hs, carrier]
- print(dpp_tag_data)
+ summary(dpp_tag_data)
clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
def rdwr_connected(tag):
@@ -492,41 +852,50 @@
summary("Tag connected: " + str(tag))
if tag.ndef:
- print("NDEF tag: " + tag.type)
- print(tag.ndef.records)
+ summary("NDEF tag: " + tag.type)
+ summary(tag.ndef.records)
success = dpp_tag_read(tag)
if only_one and success:
global continue_loop
continue_loop = False
else:
- summary("Not an NDEF tag - remove tag")
+ summary("Not an NDEF tag - remove tag", color=C_RED)
return True
return not no_wait
-def llcp_worker(llc):
+def llcp_worker(llc, try_alt):
+ global handover
+ print("Start of llcp_worker()")
+ if try_alt:
+ summary("Starting handover client (try_alt)")
+ dpp_handover_client(handover, alt=True)
+ summary("Exiting llcp_worker thread (try_alt)")
+ return
global init_on_touch
if init_on_touch:
- print("Starting handover client")
- dpp_handover_client(llc)
- print("Exiting llcp_worker thread (init_in_touch)")
+ summary("Starting handover client (init_on_touch)")
+ dpp_handover_client(handover)
+ summary("Exiting llcp_worker thread (init_on_touch)")
return
global no_input
if no_input:
- print("Wait for handover to complete")
+ summary("Wait for handover to complete")
else:
print("Wait for handover to complete - press 'i' to initiate")
- global srv
- global wait_connection
- while not wait_connection and srv.sent_carrier is None:
- if srv.try_own:
- srv.try_own = False
- print("Try to initiate another handover with own parameters")
- dpp_handover_client(llc)
- print("Exiting llcp_worker thread (retry with own parameters)")
+ while not handover.wait_connection and handover.srv.sent_carrier is None:
+ if handover.try_own:
+ handover.try_own = False
+ summary("Try to initiate another handover with own parameters")
+ handover.my_crn_ready = False
+ handover.my_crn = None
+ handover.peer_crn = None
+ handover.hs_sent = False
+ dpp_handover_client(handover, alt=True)
+ summary("Exiting llcp_worker thread (retry with own parameters)")
return
- if srv.ho_server_processing:
+ if handover.srv.ho_server_processing:
time.sleep(0.025)
elif no_input:
time.sleep(0.5)
@@ -535,34 +904,85 @@
if res != 'i':
continue
clear_raw_mode()
- print("Starting handover client")
- dpp_handover_client(llc)
- print("Exiting llcp_worker thread (manual init)")
+ summary("Starting handover client")
+ dpp_handover_client(handover)
+ summary("Exiting llcp_worker thread (manual init)")
return
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
clear_raw_mode()
- print("\rExiting llcp_worker thread")
+ if was_in_raw_mode:
+ print("\r")
+ summary("Exiting llcp_worker thread")
+
+class ConnectionHandover():
+ def __init__(self):
+ self.client = None
+ self.client_thread = None
+ self.reset()
+ self.exit_thread = None
+
+ def reset(self):
+ self.wait_connection = False
+ self.my_crn_ready = False
+ self.my_crn = None
+ self.peer_crn = None
+ self.hs_sent = False
+ self.no_alt_proposal = False
+ self.alt_proposal_used = False
+ self.i_m_selector = False
+ self.start_client_alt = False
+ self.terminate_on_hs_send_completion = False
+ self.try_own = False
+ self.my_uri = None
+ self.peer_uri = None
+ self.connected = False
+ self.alt_proposal = False
+
+ def start_handover_server(self, llc):
+ summary("Start handover server")
+ self.llc = llc
+ self.srv = HandoverServer(self, llc)
+
+ def close(self):
+ if self.client:
+ self.client.close()
+ self.client = None
+
+ def run_delayed_exit(self):
+ summary("Trying to exit (delayed)..")
+ time.sleep(0.25)
+ summary("Trying to exit (after wait)..")
+ global terminate_now
+ terminate_now = True
+
+ def delayed_exit(self):
+ global only_one
+ if only_one:
+ self.exit_thread = threading.Thread(target=self.run_delayed_exit)
+ self.exit_thread.start()
def llcp_startup(llc):
- print("Start LLCP server")
- global srv
- srv = HandoverServer(llc)
+ global handover
+ handover.start_handover_server(llc)
return llc
def llcp_connected(llc):
- print("P2P LLCP connected")
- global wait_connection
- wait_connection = False
- global init_on_touch
- if not init_on_touch:
- global srv
- srv.start()
+ summary("P2P LLCP connected")
+ global handover
+ handover.connected = True
+ handover.srv.start()
if init_on_touch or not no_input:
- threading.Thread(target=llcp_worker, args=(llc,)).start()
+ handover.client_thread = threading.Thread(target=llcp_worker,
+ args=(llc, False))
+ handover.client_thread.start()
return True
def llcp_release(llc):
- print("LLCP release")
+ summary("LLCP release")
+ global handover
+ handover.close()
return True
def terminate_loop():
@@ -592,17 +1012,38 @@
help='tag read only (do not allow connection handover)')
parser.add_argument('--handover-only', action='store_true',
help='connection handover only (do not allow tag read)')
+ parser.add_argument('--enrollee', action='store_true',
+ help='run as Enrollee-only')
+ parser.add_argument('--configurator', action='store_true',
+ help='run as Configurator-only')
+ parser.add_argument('--config-params', default='',
+ help='configurator parameters')
+ parser.add_argument('--ctrl', default='/var/run/wpa_supplicant',
+ help='wpa_supplicant/hostapd control interface')
parser.add_argument('--summary',
help='summary file for writing status updates')
parser.add_argument('--success',
help='success file for writing success update')
parser.add_argument('--device', default='usb', help='NFC device to open')
- parser.add_argument('--chan', default='81/1', help='channel list')
+ parser.add_argument('--chan', default=None, help='channel list')
+ parser.add_argument('--altchan', default=None, help='alternative channel list')
+ parser.add_argument('--netrole', default=None, help='netrole for Enrollee')
+ parser.add_argument('--test-uri', default=None,
+ help='test mode: initial URI')
+ parser.add_argument('--test-alt-uri', default=None,
+ help='test mode: alternative URI')
+ parser.add_argument('--test-sel-uri', default=None,
+ help='test mode: handover select URI')
+ parser.add_argument('--test-crn', default=None,
+ help='test mode: hardcoded crn')
parser.add_argument('command', choices=['write-nfc-uri',
'write-nfc-hs'],
nargs='?')
args = parser.parse_args()
- print(args)
+ summary(args)
+
+ global handover
+ handover = ConnectionHandover()
global only_one
only_one = args.only_one
@@ -610,18 +1051,49 @@
global no_wait
no_wait = args.no_wait
- global chanlist
+ global chanlist, netrole, test_uri, test_alt_uri, test_sel_uri
+ global test_crn
chanlist = args.chan
+ handover.altchanlist = args.altchan
+ netrole = args.netrole
+ test_uri = args.test_uri
+ test_alt_uri = args.test_alt_uri
+ test_sel_uri = args.test_sel_uri
+ if args.test_crn:
+ test_crn = struct.pack('>H', int(args.test_crn))
+ else:
+ test_crn = None
logging.basicConfig(level=args.loglevel)
+ for l in ['nfc.clf.rcs380',
+ 'nfc.clf.transport',
+ 'nfc.clf.device',
+ 'nfc.clf.__init__',
+ 'nfc.llcp',
+ 'nfc.handover']:
+ log = logging.getLogger(l)
+ log.setLevel(args.loglevel)
global init_on_touch
init_on_touch = args.init_on_touch
+ global enrollee_only
+ enrollee_only = args.enrollee
+
+ global configurator_only
+ configurator_only = args.configurator
+
+ global config_params
+ config_params = args.config_params
+
if args.ifname:
global ifname
ifname = args.ifname
- print("Selected ifname " + ifname)
+ summary("Selected ifname " + ifname)
+
+ if args.ctrl:
+ global wpas_ctrl
+ wpas_ctrl = args.ctrl
if args.summary:
global summary_file
@@ -636,26 +1108,39 @@
no_input = True
clf = nfc.ContactlessFrontend()
- global wait_connection
try:
if not clf.open(args.device):
- print("Could not open connection with an NFC device")
- raise SystemExit
+ summary("Could not open connection with an NFC device", color=C_RED)
+ raise SystemExit(1)
if args.command == "write-nfc-uri":
write_nfc_uri(clf, wait_remove=not args.no_wait)
+ if not operation_success:
+ raise SystemExit(1)
raise SystemExit
if args.command == "write-nfc-hs":
write_nfc_hs(clf, wait_remove=not args.no_wait)
+ if not operation_success:
+ raise SystemExit(1)
raise SystemExit
global continue_loop
while continue_loop:
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
clear_raw_mode()
- print("\rWaiting for a tag or peer to be touched")
- wait_connection = True
+ if was_in_raw_mode:
+ print("\r")
+ if args.handover_only:
+ summary("Waiting a peer to be touched", color=C_MAGENTA)
+ elif args.tag_read_only:
+ summary("Waiting for a tag to be touched", color=C_BLUE)
+ else:
+ summary("Waiting for a tag or peer to be touched",
+ color=C_GREEN)
+ handover.wait_connection = True
try:
if args.tag_read_only:
if not clf.connect(rdwr={'on-connect': rdwr_connected}):
@@ -674,12 +1159,21 @@
terminate=terminate_loop):
break
except Exception as e:
- print("clf.connect failed: " + str(e))
+ summary("clf.connect failed: " + str(e))
break
- global srv
- if only_one and srv and srv.success:
- raise SystemExit
+ if only_one and handover.connected:
+ role = "selector" if handover.i_m_selector else "requestor"
+ summary("Connection handover result: I'm the %s" % role,
+ color=C_YELLOW)
+ if handover.peer_uri:
+ summary("Peer URI: " + handover.peer_uri, color=C_YELLOW)
+ if handover.my_uri:
+ summary("My URI: " + handover.my_uri, color=C_YELLOW)
+ if not (handover.peer_uri and handover.my_uri):
+ summary("Negotiated connection handover failed",
+ color=C_YELLOW)
+ break
except KeyboardInterrupt:
raise SystemExit
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 4b3fcfc..e60a8c1 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -694,13 +694,15 @@
return;
}
- if (!query->maintain_addr &&
- wpas_update_random_addr_disassoc(wpa_s) < 0) {
- wpa_msg(wpa_s, MSG_INFO,
- "Failed to assign random MAC address for GAS");
- gas_query_free(query, 1);
- radio_work_done(work);
- return;
+ if (!query->maintain_addr && !wpa_s->conf->gas_rand_mac_addr) {
+ if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Failed to assign random MAC address for GAS");
+ gas_query_free(query, 1);
+ radio_work_done(work);
+ return;
+ }
+ os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
}
gas->work = work;
diff --git a/wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc b/wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant-service.rc
similarity index 90%
rename from wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc
rename to wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant-service.rc
index 3cf2500..71318d4 100644
--- a/wpa_supplicant/hidl/1.3/android.hardware.wifi.supplicant-service.rc
+++ b/wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant-service.rc
@@ -9,6 +9,7 @@
interface android.hardware.wifi.supplicant@1.1::ISupplicant default
interface android.hardware.wifi.supplicant@1.2::ISupplicant default
interface android.hardware.wifi.supplicant@1.3::ISupplicant default
+ interface android.hardware.wifi.supplicant@1.4::ISupplicant default
class main
socket wpa_wlan0 dgram 660 wifi wifi
disabled
diff --git a/wpa_supplicant/hidl/1.3/manifest.xml b/wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant.xml
similarity index 89%
rename from wpa_supplicant/hidl/1.3/manifest.xml
rename to wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant.xml
index 33e4fd4..772096c 100644
--- a/wpa_supplicant/hidl/1.3/manifest.xml
+++ b/wpa_supplicant/hidl/1.4/android.hardware.wifi.supplicant.xml
@@ -2,7 +2,7 @@
<hal format="hidl">
<name>android.hardware.wifi.supplicant</name>
<transport>hwbinder</transport>
- <version>1.3</version>
+ <version>1.4</version>
<interface>
<name>ISupplicant</name>
<instance>default</instance>
diff --git a/wpa_supplicant/hidl/1.3/hidl.cpp b/wpa_supplicant/hidl/1.4/hidl.cpp
similarity index 95%
rename from wpa_supplicant/hidl/1.3/hidl.cpp
rename to wpa_supplicant/hidl/1.4/hidl.cpp
index 4c2d434..0145576 100644
--- a/wpa_supplicant/hidl/1.3/hidl.cpp
+++ b/wpa_supplicant/hidl/1.4/hidl.cpp
@@ -28,7 +28,7 @@
using android::hardware::wifi::supplicant::V1_3::DppFailureCode;
using android::hardware::wifi::supplicant::V1_3::DppProgressCode;
using android::hardware::wifi::supplicant::V1_3::DppSuccessCode;
-using android::hardware::wifi::supplicant::V1_3::implementation::HidlManager;
+using android::hardware::wifi::supplicant::V1_4::implementation::HidlManager;
static void wpas_hidl_notify_dpp_failure(struct wpa_supplicant *wpa_s, DppFailureCode code);
static void wpas_hidl_notify_dpp_progress(struct wpa_supplicant *wpa_s, DppProgressCode code);
@@ -250,13 +250,13 @@
void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
{
- if (!wpa_s || !wpa_s->global->hidl || !url)
+ if (!wpa_s || !wpa_s->global->hidl)
return;
wpa_printf(
MSG_DEBUG,
"Notifying HS20 deauth imminent notice rx to hidl control: %s",
- url);
+ url ? url : "<no URL>");
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager)
@@ -266,6 +266,23 @@
wpa_s, code, reauth_delay, url);
}
+void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+ struct wpa_supplicant *wpa_s, const char *url)
+{
+ if (!wpa_s || !wpa_s->global->hidl || !url)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "Notifying HS20 terms and conditions acceptance rx to hidl control: %s",
+ url);
+
+ HidlManager *hidl_manager = HidlManager::getInstance();
+ if (!hidl_manager)
+ return;
+
+ hidl_manager->notifyHs20RxTermsAndConditionsAcceptance(wpa_s, url);
+}
+
void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
{
if (!wpa_s)
@@ -282,7 +299,8 @@
hidl_manager->notifyDisconnectReason(wpa_s);
}
-void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s)
+void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out)
{
if (!wpa_s)
return;
@@ -295,7 +313,7 @@
if (!hidl_manager)
return;
- hidl_manager->notifyAssocReject(wpa_s);
+ hidl_manager->notifyAssocReject(wpa_s, bssid, timed_out);
}
void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s)
@@ -875,3 +893,17 @@
hidl_manager->notifyBssTmStatus(wpa_s);
}
+
+void wpas_hidl_notify_transition_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ u8 bitmap)
+{
+ if (!wpa_s || !ssid)
+ return;
+
+ HidlManager *hidl_manager = HidlManager::getInstance();
+ if (!hidl_manager)
+ return;
+
+ hidl_manager->notifyTransitionDisable(wpa_s, ssid, bitmap);
+}
diff --git a/wpa_supplicant/hidl/1.3/hidl.h b/wpa_supplicant/hidl/1.4/hidl.h
similarity index 95%
rename from wpa_supplicant/hidl/1.3/hidl.h
rename to wpa_supplicant/hidl/1.4/hidl.h
index 304a4d6..671dd41 100644
--- a/wpa_supplicant/hidl/1.3/hidl.h
+++ b/wpa_supplicant/hidl/1.4/hidl.h
@@ -49,8 +49,11 @@
void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
const char *url);
+ void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+ struct wpa_supplicant *wpa_s, const char *url);
void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
- void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s);
+ void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out);
void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s);
void wpas_hidl_notify_bssid_changed(struct wpa_supplicant *wpa_s);
void wpas_hidl_notify_wps_event_fail(
@@ -120,6 +123,8 @@
void wpas_hidl_notify_pmk_cache_added(
struct wpa_supplicant *wpas, struct rsn_pmksa_cache_entry *pmksa_entry);
void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s);
+ void wpas_hidl_notify_transition_disable(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, u8 bitmap);
#else // CONFIG_CTRL_IFACE_HIDL
static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
{
@@ -163,8 +168,12 @@
static void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
{}
+void wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(
+ struct wpa_supplicant *wpa_s, const char *url)
+{}
static void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s) {}
-static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out) {}
static void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s) {}
static void wpas_hidl_notify_wps_event_fail(
struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
@@ -261,6 +270,10 @@
{}
void wpas_hidl_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
{}
+static void wpas_hidl_notify_transition_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ u8 bitmap)
+{}
#endif // CONFIG_CTRL_IFACE_HIDL
#ifdef _cplusplus
diff --git a/wpa_supplicant/hidl/1.3/hidl_constants.h b/wpa_supplicant/hidl/1.4/hidl_constants.h
similarity index 100%
rename from wpa_supplicant/hidl/1.3/hidl_constants.h
rename to wpa_supplicant/hidl/1.4/hidl_constants.h
diff --git a/wpa_supplicant/hidl/1.3/hidl_i.h b/wpa_supplicant/hidl/1.4/hidl_i.h
similarity index 100%
rename from wpa_supplicant/hidl/1.3/hidl_i.h
rename to wpa_supplicant/hidl/1.4/hidl_i.h
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.cpp b/wpa_supplicant/hidl/1.4/hidl_manager.cpp
similarity index 88%
rename from wpa_supplicant/hidl/1.3/hidl_manager.cpp
rename to wpa_supplicant/hidl/1.4/hidl_manager.cpp
index e15e9fd..8325311 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.cpp
+++ b/wpa_supplicant/hidl/1.4/hidl_manager.cpp
@@ -17,6 +17,7 @@
extern "C" {
#include "scan.h"
#include "src/eap_common/eap_sim_common.h"
+#include "list.h"
}
namespace {
@@ -33,6 +34,7 @@
constexpr size_t kUmtsRandLenBytes = EAP_AKA_RAND_LEN;
constexpr size_t kUmtsAutnLenBytes = EAP_AKA_AUTN_LEN;
constexpr u8 kZeroBssid[6] = {0, 0, 0, 0, 0, 0};
+
/**
* Check if the provided |wpa_supplicant| structure represents a P2P iface or
* not.
@@ -62,32 +64,17 @@
*/
template <class CallbackType>
int registerForDeathAndAddCallbackHidlObjectToList(
+ const android::sp<DeathNotifier> &death_notifier,
const android::sp<CallbackType> &callback,
- const std::function<void(const android::sp<CallbackType> &)>
- &on_hidl_died_fctor,
std::vector<android::sp<CallbackType>> &callback_list)
{
-#if 0 // TODO(b/31632518): HIDL object death notifications.
- auto death_notifier = new CallbackObjectDeathNotifier<CallbackType>(
- callback, on_hidl_died_fctor);
- // Use the |callback.get()| as cookie so that we don't need to
- // store a reference to this |CallbackObjectDeathNotifier| instance
- // to use in |unlinkToDeath| later.
- // NOTE: This may cause an immediate callback if the object is already
- // dead, so add it to the list before we register for callback!
- if (android::hardware::IInterface::asBinder(callback)->linkToDeath(
- death_notifier, callback.get()) != android::OK) {
+ if (!callback->linkToDeath(death_notifier, 0)) {
wpa_printf(
MSG_ERROR,
"Error registering for death notification for "
"supplicant callback object");
- callback_list.erase(
- std::remove(
- callback_list.begin(), callback_list.end(), callback),
- callback_list.end());
return 1;
}
-#endif // TODO(b/31632518): HIDL object death notifications.
callback_list.push_back(callback);
return 0;
}
@@ -122,9 +109,8 @@
template <class CallbackType>
int addIfaceCallbackHidlObjectToMap(
+ const android::sp<DeathNotifier> &death_notifier,
const std::string &ifname, const android::sp<CallbackType> &callback,
- const std::function<void(const android::sp<CallbackType> &)>
- &on_hidl_died_fctor,
std::map<const std::string, std::vector<android::sp<CallbackType>>>
&callbacks_map)
{
@@ -138,15 +124,14 @@
// Register for death notification before we add it to our list.
return registerForDeathAndAddCallbackHidlObjectToList<CallbackType>(
- callback, on_hidl_died_fctor, iface_callback_list);
+ death_notifier, callback, iface_callback_list);
}
template <class CallbackType>
int addNetworkCallbackHidlObjectToMap(
+ const android::sp<DeathNotifier> &death_notifier,
const std::string &ifname, int network_id,
const android::sp<CallbackType> &callback,
- const std::function<void(const android::sp<CallbackType> &)>
- &on_hidl_died_fctor,
std::map<const std::string, std::vector<android::sp<CallbackType>>>
&callbacks_map)
{
@@ -163,11 +148,12 @@
// Register for death notification before we add it to our list.
return registerForDeathAndAddCallbackHidlObjectToList<CallbackType>(
- callback, on_hidl_died_fctor, network_callback_list);
+ death_notifier, callback, network_callback_list);
}
template <class CallbackType>
int removeAllIfaceCallbackHidlObjectsFromMap(
+ const android::sp<DeathNotifier> &death_notifier,
const std::string &ifname,
std::map<const std::string, std::vector<android::sp<CallbackType>>>
&callbacks_map)
@@ -175,25 +161,22 @@
auto iface_callback_map_iter = callbacks_map.find(ifname);
if (iface_callback_map_iter == callbacks_map.end())
return 1;
-#if 0 // TODO(b/31632518): HIDL object death notifications.
const auto &iface_callback_list = iface_callback_map_iter->second;
for (const auto &callback : iface_callback_list) {
- if (android::hardware::IInterface::asBinder(callback)
- ->unlinkToDeath(nullptr, callback.get()) !=
- android::OK) {
+ if (!callback->unlinkToDeath(death_notifier)) {
wpa_printf(
MSG_ERROR,
"Error deregistering for death notification for "
"iface callback object");
}
}
-#endif // TODO(b/31632518): HIDL object death notifications.
callbacks_map.erase(iface_callback_map_iter);
return 0;
}
template <class CallbackType>
int removeAllNetworkCallbackHidlObjectsFromMap(
+ const android::sp<DeathNotifier> &death_notifier,
const std::string &network_key,
std::map<const std::string, std::vector<android::sp<CallbackType>>>
&callbacks_map)
@@ -201,12 +184,9 @@
auto network_callback_map_iter = callbacks_map.find(network_key);
if (network_callback_map_iter == callbacks_map.end())
return 1;
-#if 0 // TODO(b/31632518): HIDL object death notifications.
const auto &network_callback_list = network_callback_map_iter->second;
for (const auto &callback : network_callback_list) {
- if (android::hardware::IInterface::asBinder(callback)
- ->unlinkToDeath(nullptr, callback.get()) !=
- android::OK) {
+ if (!callback->unlinkToDeath(death_notifier)) {
wpa_printf(
MSG_ERROR,
"Error deregistering for death "
@@ -214,7 +194,6 @@
"network callback object");
}
}
-#endif // TODO(b/31632518): HIDL object death notifications.
callbacks_map.erase(network_callback_map_iter);
return 0;
}
@@ -345,6 +324,38 @@
}
}
+template <class CallbackTypeBase, class CallbackTypeDerived>
+void callWithEachNetworkCallbackDerived(
+ const std::string &ifname, int network_id,
+ const std::function<
+ android::hardware::Return<void>(android::sp<CallbackTypeDerived>)> &method,
+ const std::map<
+ const std::string, std::vector<android::sp<CallbackTypeBase>>>
+ &callbacks_map)
+{
+ if (ifname.empty())
+ return;
+
+ // Generate the key to be used to lookup the network.
+ const std::string network_key =
+ getNetworkObjectMapKey(ifname, network_id);
+ auto network_callback_map_iter = callbacks_map.find(network_key);
+ if (network_callback_map_iter == callbacks_map.end())
+ return;
+ const auto &network_callback_list = network_callback_map_iter->second;
+ for (const auto &callback : network_callback_list) {
+ android::sp<CallbackTypeDerived> callback_derived =
+ CallbackTypeDerived::castFrom(callback);
+ if (callback_derived == nullptr)
+ continue;
+ if (!method(callback_derived).isOk()) {
+ wpa_printf(
+ MSG_ERROR,
+ "Failed to invoke HIDL network callback");
+ }
+ }
+}
+
int parseGsmAuthNetworkRequest(
const std::string ¶ms_str,
std::vector<hidl_array<uint8_t, kGsmRandLenBytes>> *out_rands)
@@ -400,7 +411,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicantStaIfaceCallback;
@@ -424,6 +435,8 @@
{
// Create the main hidl service object and register it.
supplicant_object_ = new Supplicant(global);
+ wpa_global_ = global;
+ death_notifier_ = sp<DeathNotifier>::make(global);
if (supplicant_object_->registerAsService() != android::NO_ERROR) {
return 1;
}
@@ -518,13 +531,13 @@
!removeHidlObjectFromMap(wpa_s->ifname, p2p_iface_object_map_);
if (success) { // assumed to be P2P
success = !removeAllIfaceCallbackHidlObjectsFromMap(
- wpa_s->ifname, p2p_iface_callbacks_map_);
+ death_notifier_, wpa_s->ifname, p2p_iface_callbacks_map_);
} else { // assumed to be STA
success = !removeHidlObjectFromMap(
wpa_s->ifname, sta_iface_object_map_);
if (success) {
success = !removeAllIfaceCallbackHidlObjectsFromMap(
- wpa_s->ifname, sta_iface_callbacks_map_);
+ death_notifier_, wpa_s->ifname, sta_iface_callbacks_map_);
}
}
if (!success) {
@@ -638,7 +651,7 @@
return 1;
}
if (removeAllNetworkCallbackHidlObjectsFromMap(
- network_key, p2p_network_callbacks_map_))
+ death_notifier_, network_key, p2p_network_callbacks_map_))
return 1;
// Invoke the |onNetworkRemoved| method on all registered
@@ -659,7 +672,7 @@
return 1;
}
if (removeAllNetworkCallbackHidlObjectsFromMap(
- network_key, sta_network_callbacks_map_))
+ death_notifier_, network_key, sta_network_callbacks_map_))
return 1;
// Invoke the |onNetworkRemoved| method on all registered
@@ -809,23 +822,33 @@
sta_iface_object_map_.end())
return;
- ISupplicantStaIfaceCallback::AnqpData hidl_anqp_data;
+ V1_4::ISupplicantStaIfaceCallback::AnqpData hidl_anqp_data;
ISupplicantStaIfaceCallback::Hs20AnqpData hidl_hs20_anqp_data;
if (std::string(result) == "SUCCESS") {
- hidl_anqp_data.venueName =
+ hidl_anqp_data.V1_0.venueName =
misc_utils::convertWpaBufToVector(anqp->venue_name);
- hidl_anqp_data.roamingConsortium =
+ hidl_anqp_data.V1_0.roamingConsortium =
misc_utils::convertWpaBufToVector(anqp->roaming_consortium);
- hidl_anqp_data.ipAddrTypeAvailability =
+ hidl_anqp_data.V1_0.ipAddrTypeAvailability =
misc_utils::convertWpaBufToVector(
anqp->ip_addr_type_availability);
- hidl_anqp_data.naiRealm =
+ hidl_anqp_data.V1_0.naiRealm =
misc_utils::convertWpaBufToVector(anqp->nai_realm);
- hidl_anqp_data.anqp3gppCellularNetwork =
+ hidl_anqp_data.V1_0.anqp3gppCellularNetwork =
misc_utils::convertWpaBufToVector(anqp->anqp_3gpp);
- hidl_anqp_data.domainName =
+ hidl_anqp_data.V1_0.domainName =
misc_utils::convertWpaBufToVector(anqp->domain_name);
+ struct wpa_bss_anqp_elem *elem;
+ dl_list_for_each(elem, &anqp->anqp_elems, struct wpa_bss_anqp_elem,
+ list) {
+ if (elem->infoid == ANQP_VENUE_URL) {
+ hidl_anqp_data.venueUrl =
+ misc_utils::convertWpaBufToVector(elem->payload);
+ break;
+ }
+ }
+
hidl_hs20_anqp_data.operatorFriendlyName =
misc_utils::convertWpaBufToVector(
anqp->hs20_operator_friendly_name);
@@ -839,9 +862,9 @@
anqp->hs20_osu_providers_list);
}
- callWithEachStaIfaceCallback(
+ callWithEachStaIfaceCallback_1_4(
wpa_s->ifname, std::bind(
- &ISupplicantStaIfaceCallback::onAnqpQueryDone,
+ &V1_4::ISupplicantStaIfaceCallback::onAnqpQueryDone_1_4,
std::placeholders::_1, bssid, hidl_anqp_data,
hidl_hs20_anqp_data));
}
@@ -908,18 +931,18 @@
}
/**
- * Notify all listeners about the reception of HS20 immient deauth
+ * Notify all listeners about the reception of HS20 imminent deauth
* notification from the server.
*
* @param wpa_s |wpa_supplicant| struct corresponding to the interface.
* @param code Deauth reason code sent from server.
* @param reauth_delay Reauthentication delay in seconds sent from server.
- * @param url URL of the server.
+ * @param url URL of the server containing the reason text.
*/
void HidlManager::notifyHs20RxDeauthImminentNotice(
struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
{
- if (!wpa_s || !url)
+ if (!wpa_s)
return;
if (sta_iface_object_map_.find(wpa_s->ifname) ==
@@ -934,6 +957,30 @@
}
/**
+ * Notify all listeners about the reception of HS20 terms and conditions
+ * acceptance notification from the server.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param url URL of the T&C server.
+ */
+void HidlManager::notifyHs20RxTermsAndConditionsAcceptance(
+ struct wpa_supplicant *wpa_s, const char *url)
+{
+ if (!wpa_s || !url)
+ return;
+
+ if (sta_iface_object_map_.find(wpa_s->ifname)
+ == sta_iface_object_map_.end())
+ return;
+
+ callWithEachStaIfaceCallback_1_4(wpa_s->ifname,
+ std::bind(
+ &V1_4::ISupplicantStaIfaceCallback
+ ::onHs20TermsAndConditionsAcceptanceRequestedNotification,
+ std::placeholders::_1, wpa_s->bssid, url));
+}
+
+/**
* Notify all listeners about the reason code for disconnection from the
* currently connected network.
*
@@ -969,8 +1016,12 @@
*
* @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
* the network is present.
+ * @param bssid bssid of AP that rejected the association.
+ * @param timed_out flag to indicate failure is due to timeout
+ * (auth, assoc, ...) rather than explicit rejection response from the AP.
*/
-void HidlManager::notifyAssocReject(struct wpa_supplicant *wpa_s)
+void HidlManager::notifyAssocReject(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out)
{
if (!wpa_s)
return;
@@ -979,19 +1030,13 @@
sta_iface_object_map_.end())
return;
- const u8 *bssid = wpa_s->bssid;
- if (is_zero_ether_addr(bssid)) {
- bssid = wpa_s->pending_bssid;
- }
-
callWithEachStaIfaceCallback(
wpa_s->ifname,
std::bind(
&ISupplicantStaIfaceCallback::onAssociationRejected,
std::placeholders::_1, bssid,
static_cast<ISupplicantStaIfaceCallback::StatusCode>(
- wpa_s->assoc_status_code),
- wpa_s->assoc_timed_out == 1));
+ wpa_s->assoc_status_code), timed_out == 1));
}
void HidlManager::notifyAuthTimeout(struct wpa_supplicant *wpa_s)
@@ -1238,9 +1283,8 @@
return;
// For group notifications, need to use the parent iface for callbacks.
- struct wpa_supplicant *wpa_s = wpa_group_s->parent;
- if (p2p_iface_object_map_.find(wpa_s->ifname) ==
- p2p_iface_object_map_.end())
+ struct wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
+ if (!wpa_s)
return;
uint32_t hidl_freq = wpa_group_s->current_bss
@@ -1281,9 +1325,8 @@
return;
// For group notifications, need to use the parent iface for callbacks.
- struct wpa_supplicant *wpa_s = wpa_group_s->parent;
- if (p2p_iface_object_map_.find(wpa_s->ifname) ==
- p2p_iface_object_map_.end())
+ struct wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
+ if (!wpa_s)
return;
bool hidl_is_go = (std::string(role) == "GO");
@@ -1388,15 +1431,15 @@
}
void HidlManager::notifyApStaAuthorized(
- struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+ struct wpa_supplicant *wpa_group_s, const u8 *sta, const u8 *p2p_dev_addr)
{
- if (!wpa_s || !wpa_s->parent || !sta)
+ if (!wpa_group_s || !wpa_group_s->parent || !sta)
return;
- if (p2p_iface_object_map_.find(wpa_s->parent->ifname) ==
- p2p_iface_object_map_.end())
+ wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
+ if (!wpa_s)
return;
callWithEachP2pIfaceCallback(
- wpa_s->parent->ifname,
+ wpa_s->ifname,
std::bind(
&ISupplicantP2pIfaceCallback::onStaAuthorized,
std::placeholders::_1, sta,
@@ -1404,16 +1447,16 @@
}
void HidlManager::notifyApStaDeauthorized(
- struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+ struct wpa_supplicant *wpa_group_s, const u8 *sta, const u8 *p2p_dev_addr)
{
- if (!wpa_s || !wpa_s->parent || !sta)
+ if (!wpa_group_s || !wpa_group_s->parent || !sta)
return;
- if (p2p_iface_object_map_.find(wpa_s->parent->ifname) ==
- p2p_iface_object_map_.end())
+ wpa_supplicant *wpa_s = getTargetP2pIfaceForGroup(wpa_group_s);
+ if (!wpa_s)
return;
callWithEachP2pIfaceCallback(
- wpa_s->parent->ifname,
+ wpa_s->ifname,
std::bind(
&ISupplicantP2pIfaceCallback::onStaDeauthorized,
std::placeholders::_1, sta,
@@ -1574,7 +1617,7 @@
* @param ifname Interface name
* @param code Status code
*/
-void HidlManager::notifyDppSuccess(struct wpa_supplicant *wpa_s, DppSuccessCode code)
+void HidlManager::notifyDppSuccess(struct wpa_supplicant *wpa_s, V1_3::DppSuccessCode code)
{
std::string hidl_ifname = wpa_s->ifname;
@@ -1761,6 +1804,56 @@
#endif
}
+uint32_t setTransitionDisableFlagsMask(u8 bitmap)
+{
+ uint32_t flags = 0;
+
+ if (bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) {
+ flags |= V1_4::ISupplicantStaNetworkCallback::
+ TransitionDisableIndication::
+ USE_WPA3_PERSONAL;
+ bitmap &= ~TRANSITION_DISABLE_WPA3_PERSONAL;
+ }
+ if (bitmap & TRANSITION_DISABLE_SAE_PK) {
+ flags |= V1_4::ISupplicantStaNetworkCallback::
+ TransitionDisableIndication::
+ USE_SAE_PK;
+ bitmap &= ~TRANSITION_DISABLE_SAE_PK;
+ }
+ if (bitmap & TRANSITION_DISABLE_WPA3_ENTERPRISE) {
+ flags |= V1_4::ISupplicantStaNetworkCallback::
+ TransitionDisableIndication::
+ USE_WPA3_ENTERPRISE;
+ bitmap &= ~TRANSITION_DISABLE_WPA3_ENTERPRISE;
+ }
+ if (bitmap & TRANSITION_DISABLE_ENHANCED_OPEN) {
+ flags |= V1_4::ISupplicantStaNetworkCallback::
+ TransitionDisableIndication::
+ USE_ENHANCED_OPEN;
+ bitmap &= ~TRANSITION_DISABLE_ENHANCED_OPEN;
+ }
+
+ if (bitmap != 0) {
+ wpa_printf(MSG_WARNING, "Unhandled transition disable bit: 0x%x", bitmap);
+ }
+
+ return flags;
+}
+
+void HidlManager::notifyTransitionDisable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, u8 bitmap)
+{
+ uint32_t flag = setTransitionDisableFlagsMask(bitmap);
+ const std::function<
+ Return<void>(android::sp<V1_4::ISupplicantStaNetworkCallback>)>
+ func = std::bind(
+ &V1_4::ISupplicantStaNetworkCallback::onTransitionDisable,
+ std::placeholders::_1, flag);
+
+ callWithEachStaNetworkCallbackDerived(wpa_s->ifname, ssid->id, func);
+}
+
+
/**
* Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
* ifname.
@@ -1848,7 +1941,7 @@
*/
int HidlManager::getStaNetworkHidlObjectByIfnameAndNetworkId(
const std::string &ifname, int network_id,
- android::sp<ISupplicantStaNetwork> *network_object)
+ android::sp<V1_3::ISupplicantStaNetwork> *network_object)
{
if (ifname.empty() || network_id < 0 || !network_object)
return 1;
@@ -1876,13 +1969,9 @@
int HidlManager::addSupplicantCallbackHidlObject(
const android::sp<ISupplicantCallback> &callback)
{
- // Register for death notification before we add it to our list.
- auto on_hidl_died_fctor = std::bind(
- &HidlManager::removeSupplicantCallbackHidlObject, this,
- std::placeholders::_1);
return registerForDeathAndAddCallbackHidlObjectToList<
ISupplicantCallback>(
- callback, on_hidl_died_fctor, supplicant_callbacks_);
+ death_notifier_, callback, supplicant_callbacks_);
}
/**
@@ -1898,13 +1987,8 @@
const std::string &ifname,
const android::sp<ISupplicantP2pIfaceCallback> &callback)
{
- const std::function<void(
- const android::sp<ISupplicantP2pIfaceCallback> &)>
- on_hidl_died_fctor = std::bind(
- &HidlManager::removeP2pIfaceCallbackHidlObject, this, ifname,
- std::placeholders::_1);
return addIfaceCallbackHidlObjectToMap(
- ifname, callback, on_hidl_died_fctor, p2p_iface_callbacks_map_);
+ death_notifier_, ifname, callback, p2p_iface_callbacks_map_);
}
/**
@@ -1920,13 +2004,8 @@
const std::string &ifname,
const android::sp<ISupplicantStaIfaceCallback> &callback)
{
- const std::function<void(
- const android::sp<ISupplicantStaIfaceCallback> &)>
- on_hidl_died_fctor = std::bind(
- &HidlManager::removeStaIfaceCallbackHidlObject, this, ifname,
- std::placeholders::_1);
return addIfaceCallbackHidlObjectToMap(
- ifname, callback, on_hidl_died_fctor, sta_iface_callbacks_map_);
+ death_notifier_, ifname, callback, sta_iface_callbacks_map_);
}
/**
@@ -1943,13 +2022,8 @@
const std::string &ifname, int network_id,
const android::sp<ISupplicantP2pNetworkCallback> &callback)
{
- const std::function<void(
- const android::sp<ISupplicantP2pNetworkCallback> &)>
- on_hidl_died_fctor = std::bind(
- &HidlManager::removeP2pNetworkCallbackHidlObject, this, ifname,
- network_id, std::placeholders::_1);
return addNetworkCallbackHidlObjectToMap(
- ifname, network_id, callback, on_hidl_died_fctor,
+ death_notifier_, ifname, network_id, callback,
p2p_network_callbacks_map_);
}
@@ -1967,17 +2041,41 @@
const std::string &ifname, int network_id,
const android::sp<ISupplicantStaNetworkCallback> &callback)
{
- const std::function<void(
- const android::sp<ISupplicantStaNetworkCallback> &)>
- on_hidl_died_fctor = std::bind(
- &HidlManager::removeStaNetworkCallbackHidlObject, this, ifname,
- network_id, std::placeholders::_1);
return addNetworkCallbackHidlObjectToMap(
- ifname, network_id, callback, on_hidl_died_fctor,
+ death_notifier_, ifname, network_id, callback,
sta_network_callbacks_map_);
}
/**
+ * Finds the correct |wpa_supplicant| object for P2P notifications
+ *
+ * @param wpa_s the |wpa_supplicant| that triggered the P2P event.
+ * @return appropriate |wpa_supplicant| object or NULL if not found.
+ */
+struct wpa_supplicant *HidlManager::getTargetP2pIfaceForGroup(
+ struct wpa_supplicant *wpa_group_s)
+{
+ if (!wpa_group_s || !wpa_group_s->parent)
+ return NULL;
+
+ struct wpa_supplicant *target_wpa_s = wpa_group_s->parent;
+ if (p2p_iface_object_map_.find(target_wpa_s->ifname) !=
+ p2p_iface_object_map_.end())
+ return target_wpa_s;
+
+ // try P2P device if available
+ if (!target_wpa_s->p2pdev || !target_wpa_s->p2pdev->p2p_mgmt)
+ return NULL;
+
+ target_wpa_s = target_wpa_s->p2pdev;
+ if (p2p_iface_object_map_.find(target_wpa_s->ifname) !=
+ p2p_iface_object_map_.end())
+ return target_wpa_s;
+
+ return NULL;
+}
+
+/**
* Removes the provided |ISupplicantCallback| hidl object reference
* from our global callback list.
*
@@ -2142,6 +2240,23 @@
/**
* Helper function to invoke the provided callback method on all the
+ * registered V1.4 interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * |V1_4::ISupplicantIfaceCallback|.
+ */
+void HidlManager::callWithEachStaIfaceCallback_1_4(
+ const std::string &ifname,
+ const std::function<
+ Return<void>(android::sp<V1_4::ISupplicantStaIfaceCallback>)> &method)
+{
+ callWithEachIfaceCallbackDerived(ifname, method, sta_iface_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
* registered derived interface callback hidl objects for the specified
* |ifname|.
*
@@ -2212,8 +2327,26 @@
callWithEachNetworkCallback(
ifname, network_id, method, sta_network_callbacks_map_);
}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered derived interface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * derived |V1_x::ISupplicantIfaceCallback|.
+ */
+template <class CallbackTypeDerived>
+void HidlManager::callWithEachStaNetworkCallbackDerived(
+ const std::string &ifname, int network_id,
+ const std::function<
+ Return<void>(android::sp<CallbackTypeDerived>)> &method)
+{
+ callWithEachNetworkCallbackDerived(ifname, network_id, method, sta_network_callbacks_map_);
+}
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/hidl_manager.h b/wpa_supplicant/hidl/1.4/hidl_manager.h
similarity index 86%
rename from wpa_supplicant/hidl/1.3/hidl_manager.h
rename to wpa_supplicant/hidl/1.4/hidl_manager.h
index e49e28d..f027676 100644
--- a/wpa_supplicant/hidl/1.3/hidl_manager.h
+++ b/wpa_supplicant/hidl/1.4/hidl_manager.h
@@ -16,8 +16,9 @@
#include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
-#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaNetworkCallback.h>
#include "p2p_iface.h"
#include "p2p_network.h"
@@ -34,11 +35,31 @@
#include "driver_i.h"
}
+class DeathNotifier : public android::hardware::hidl_death_recipient
+{
+public:
+ DeathNotifier(struct wpa_global *wpa_global)
+ : wpa_global_(wpa_global)
+ {}
+
+ void serviceDied(
+ uint64_t /*cookie*/,
+ const android::wp<android::hidl::base::V1_0::IBase>
+ & /* who */) override
+ {
+ wpa_printf(MSG_ERROR, "Client died. Terminating...");
+ wpa_supplicant_terminate_proc(wpa_global_);
+ }
+
+private:
+ struct wpa_global *wpa_global_;
+};
+
namespace android {
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicant;
using V1_0::ISupplicantP2pIface;
@@ -81,9 +102,12 @@
struct wpa_supplicant *wpa_s, const char *url, u8 osu_method);
void notifyHs20RxDeauthImminentNotice(
struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
- const char *url);
+ const char *url);
+ void notifyHs20RxTermsAndConditionsAcceptance(
+ struct wpa_supplicant *wpa_s, const char *url);
void notifyDisconnectReason(struct wpa_supplicant *wpa_s);
- void notifyAssocReject(struct wpa_supplicant *wpa_s);
+ void notifyAssocReject(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out);
void notifyAuthTimeout(struct wpa_supplicant *wpa_s);
void notifyBssidChanged(struct wpa_supplicant *wpa_s);
void notifyWpsEventFail(
@@ -133,7 +157,7 @@
void notifyDppConfigReceived(struct wpa_supplicant *wpa_s,
struct wpa_ssid *config);
void notifyDppConfigSent(struct wpa_supplicant *wpa_s);
- void notifyDppSuccess(struct wpa_supplicant *wpa_s, DppSuccessCode code);
+ void notifyDppSuccess(struct wpa_supplicant *wpa_s, V1_3::DppSuccessCode code);
void notifyDppFailure(struct wpa_supplicant *wpa_s,
android::hardware::wifi::supplicant::V1_3::DppFailureCode code);
void notifyDppFailure(struct wpa_supplicant *wpa_s,
@@ -145,6 +169,9 @@
void notifyPmkCacheAdded(struct wpa_supplicant *wpa_s,
struct rsn_pmksa_cache_entry *pmksa_entry);
void notifyBssTmStatus(struct wpa_supplicant *wpa_s);
+ void notifyTransitionDisable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ u8 bitmap);
// Methods called from hidl objects.
void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
@@ -162,7 +189,7 @@
android::sp<ISupplicantP2pNetwork> *network_object);
int getStaNetworkHidlObjectByIfnameAndNetworkId(
const std::string &ifname, int network_id,
- android::sp<ISupplicantStaNetwork> *network_object);
+ android::sp<V1_3::ISupplicantStaNetwork> *network_object);
int addSupplicantCallbackHidlObject(
const android::sp<ISupplicantCallback> &callback);
int addP2pIfaceCallbackHidlObject(
@@ -184,6 +211,8 @@
HidlManager(const HidlManager &) = default;
HidlManager &operator=(const HidlManager &) = default;
+ struct wpa_supplicant *getTargetP2pIfaceForGroup(
+ struct wpa_supplicant *wpa_s);
void removeSupplicantCallbackHidlObject(
const android::sp<ISupplicantCallback> &callback);
void removeP2pIfaceCallbackHidlObject(
@@ -222,6 +251,10 @@
const std::string &ifname,
const std::function<android::hardware::Return<void>(
android::sp<V1_3::ISupplicantStaIfaceCallback>)> &method);
+ void callWithEachStaIfaceCallback_1_4(
+ const std::string &ifname,
+ const std::function<android::hardware::Return<void>(
+ android::sp<V1_4::ISupplicantStaIfaceCallback>)> &method);
template <class CallbackTypeDerived>
void callWithEachStaIfaceCallbackDerived(
const std::string &ifname,
@@ -235,9 +268,18 @@
const std::string &ifname, int network_id,
const std::function<android::hardware::Return<void>(
android::sp<ISupplicantStaNetworkCallback>)> &method);
+ template <class CallbackTypeDerived>
+ void callWithEachStaNetworkCallbackDerived(
+ const std::string &ifname, int network_id,
+ const std::function<
+ Return<void>(android::sp<CallbackTypeDerived>)> &method);
// Singleton instance of this class.
static HidlManager *instance_;
+ // Raw pointer to the global structure maintained by the core.
+ struct wpa_global *wpa_global_;
+ // Death notifier.
+ android::sp<DeathNotifier> death_notifier_;
// The main hidl service object.
android::sp<Supplicant> supplicant_object_;
// Map of all the P2P interface specific hidl objects controlled by
@@ -291,40 +333,6 @@
const std::string,
std::vector<android::sp<ISupplicantStaNetworkCallback>>>
sta_network_callbacks_map_;
-
-#if 0 // TODO(b/31632518): HIDL object death notifications.
- /**
- * Helper class used to deregister the callback object reference from
- * our callback list on the death of the hidl object.
- * This class stores a reference of the callback hidl object and a
- * function to be called to indicate the death of the hidl object.
- */
- template <class CallbackType>
- class CallbackObjectDeathNotifier
- : public android::hardware::IBinder::DeathRecipient
- {
- public:
- CallbackObjectDeathNotifier(
- const android::sp<CallbackType> &callback,
- const std::function<void(const android::sp<CallbackType> &)>
- &on_hidl_died)
- : callback_(callback), on_hidl_died_(on_hidl_died)
- {
- }
- void binderDied(const android::wp<android::hardware::IBinder>
- & /* who */) override
- {
- on_hidl_died_(callback_);
- }
-
- private:
- // The callback hidl object reference.
- const android::sp<CallbackType> callback_;
- // Callback function to be called when the hidl dies.
- const std::function<void(const android::sp<CallbackType> &)>
- on_hidl_died_;
- };
-#endif
};
// The hidl interface uses some values which are the same as internal ones to
@@ -338,51 +346,51 @@
"Debug level value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::NONE) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::NONE) ==
WPA_KEY_MGMT_NONE,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK) ==
WPA_KEY_MGMT_PSK,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP) ==
WPA_KEY_MGMT_IEEE8021X,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X) ==
WPA_KEY_MGMT_IEEE8021X_NO_WPA,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_EAP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::FT_EAP) ==
WPA_KEY_MGMT_FT_IEEE8021X,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_PSK) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::FT_PSK) ==
WPA_KEY_MGMT_FT_PSK,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OSEN) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::KeyMgmtMask::OSEN) ==
WPA_KEY_MGMT_OSEN,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::SAE) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::KeyMgmtMask::SAE) ==
WPA_KEY_MGMT_SAE,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::SUITE_B_192) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::KeyMgmtMask::SUITE_B_192) ==
WPA_KEY_MGMT_IEEE8021X_SUITE_B_192,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OWE) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::KeyMgmtMask::OWE) ==
WPA_KEY_MGMT_OWE,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK_SHA256) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK_SHA256) ==
WPA_KEY_MGMT_PSK_SHA256,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP_SHA256) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP_SHA256) ==
WPA_KEY_MGMT_IEEE8021X_SHA256,
"KeyMgmt value mismatch");
static_assert(
@@ -394,15 +402,15 @@
WPA_KEY_MGMT_WAPI_CERT,
"KeyMgmt value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::WPA) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::ProtoMask::WPA) ==
WPA_PROTO_WPA,
"Proto value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::RSN) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::ProtoMask::RSN) ==
WPA_PROTO_RSN,
"Proto value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::OSEN) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::ProtoMask::OSEN) ==
WPA_PROTO_OSEN,
"Proto value mismatch");
static_assert(
@@ -410,35 +418,35 @@
WPA_PROTO_WAPI,
"Proto value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::OPEN) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::AuthAlgMask::OPEN) ==
WPA_AUTH_ALG_OPEN,
"AuthAlg value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::SHARED) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::AuthAlgMask::SHARED) ==
WPA_AUTH_ALG_SHARED,
"AuthAlg value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::LEAP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::AuthAlgMask::LEAP) ==
WPA_AUTH_ALG_LEAP,
"AuthAlg value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP40) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::GroupCipherMask::WEP40) ==
WPA_CIPHER_WEP40,
"GroupCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP104) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::GroupCipherMask::WEP104) ==
WPA_CIPHER_WEP104,
"GroupCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::TKIP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::GroupCipherMask::TKIP) ==
WPA_CIPHER_TKIP,
"GroupCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::CCMP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::GroupCipherMask::CCMP) ==
WPA_CIPHER_CCMP,
"GroupCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::GCMP_256) ==
+ static_cast<uint32_t>(V1_2::ISupplicantStaNetwork::GroupCipherMask::GCMP_256) ==
WPA_CIPHER_GCMP_256,
"GroupCipher value mismatch");
static_assert(
@@ -447,24 +455,24 @@
"GroupCipher value mismatch");
static_assert(
static_cast<uint32_t>(
- ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) ==
+ V1_0::ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) ==
WPA_CIPHER_GTK_NOT_USED,
"GroupCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::NONE) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::PairwiseCipherMask::NONE) ==
WPA_CIPHER_NONE,
"PairwiseCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::TKIP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::PairwiseCipherMask::TKIP) ==
WPA_CIPHER_TKIP,
"PairwiseCipher value mismatch");
static_assert(
- static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::CCMP) ==
+ static_cast<uint32_t>(V1_0::ISupplicantStaNetwork::PairwiseCipherMask::CCMP) ==
WPA_CIPHER_CCMP,
"PairwiseCipher value mismatch");
static_assert(
static_cast<uint32_t>(
- ISupplicantStaNetwork::PairwiseCipherMask::GCMP_256) ==
+ V1_2::ISupplicantStaNetwork::PairwiseCipherMask::GCMP_256) ==
WPA_CIPHER_GCMP_256,
"PairwiseCipher value mismatch");
static_assert(
@@ -760,7 +768,7 @@
P2P_PROV_DISC_INFO_UNAVAILABLE,
"P2P status code value mismatch");
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/hidl_return_util.h b/wpa_supplicant/hidl/1.4/hidl_return_util.h
similarity index 69%
rename from wpa_supplicant/hidl/1.3/hidl_return_util.h
rename to wpa_supplicant/hidl/1.4/hidl_return_util.h
index 4c1f919..217f215 100644
--- a/wpa_supplicant/hidl/1.3/hidl_return_util.h
+++ b/wpa_supplicant/hidl/1.4/hidl_return_util.h
@@ -14,10 +14,9 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
namespace hidl_return_util {
-using V1_0::SupplicantStatusCode;
/**
* These utility functions are used to invoke a method on the provided
@@ -30,10 +29,11 @@
* provided error status and default values.
*/
// Use for HIDL methods which return only an instance of SupplicantStatus.
-template <typename ObjT, typename WorkFuncT, typename... Args>
+template <typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+ typename SupplicantStatusT, typename... Args>
Return<void> validateAndCall(
- ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
- const std::function<void(const SupplicantStatus&)>& hidl_cb, Args&&... args)
+ ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+ const std::function<void(const SupplicantStatusT&)>& hidl_cb, Args&&... args)
{
if (obj->isValid()) {
hidl_cb((obj->*work)(std::forward<Args>(args)...));
@@ -45,16 +45,17 @@
// Use for HIDL methods which return instance of SupplicantStatus and a single
// return value.
-template <typename ObjT, typename WorkFuncT, typename ReturnT, typename... Args>
+template <typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+ typename SupplicantStatusT, typename ReturnT, typename... Args>
Return<void> validateAndCall(
- ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
- const std::function<void(const SupplicantStatus&, ReturnT)>& hidl_cb,
+ ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+ const std::function<void(const SupplicantStatusT&, ReturnT)>& hidl_cb,
Args&&... args)
{
if (obj->isValid()) {
const auto& ret_pair =
(obj->*work)(std::forward<Args>(args)...);
- const SupplicantStatus& status = std::get<0>(ret_pair);
+ const SupplicantStatusT& status = std::get<0>(ret_pair);
const auto& ret_value = std::get<1>(ret_pair);
hidl_cb(status, ret_value);
} else {
@@ -68,18 +69,18 @@
// Use for HIDL methods which return instance of SupplicantStatus and 2 return
// values.
template <
- typename ObjT, typename WorkFuncT, typename ReturnT1, typename ReturnT2,
- typename... Args>
+ typename ObjT, typename SupplicantStatusCodeT, typename WorkFuncT,
+ typename SupplicantStatusT, typename ReturnT1, typename ReturnT2, typename... Args>
Return<void> validateAndCall(
- ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
- const std::function<void(const SupplicantStatus&, ReturnT1, ReturnT2)>&
+ ObjT* obj, SupplicantStatusCodeT status_code_if_invalid, WorkFuncT&& work,
+ const std::function<void(const SupplicantStatusT&, ReturnT1, ReturnT2)>&
hidl_cb,
Args&&... args)
{
if (obj->isValid()) {
const auto& ret_tuple =
(obj->*work)(std::forward<Args>(args)...);
- const SupplicantStatus& status = std::get<0>(ret_tuple);
+ const SupplicantStatusT& status = std::get<0>(ret_tuple);
const auto& ret_value1 = std::get<1>(ret_tuple);
const auto& ret_value2 = std::get<2>(ret_tuple);
hidl_cb(status, ret_value1, ret_value2);
@@ -94,7 +95,7 @@
} // namespace hidl_return_util
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/iface_config_utils.cpp b/wpa_supplicant/hidl/1.4/iface_config_utils.cpp
similarity index 98%
rename from wpa_supplicant/hidl/1.3/iface_config_utils.cpp
rename to wpa_supplicant/hidl/1.4/iface_config_utils.cpp
index 31370a6..9af3ea9 100644
--- a/wpa_supplicant/hidl/1.3/iface_config_utils.cpp
+++ b/wpa_supplicant/hidl/1.4/iface_config_utils.cpp
@@ -81,9 +81,11 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
namespace iface_config_utils {
+using V1_0::SupplicantStatusCode;
+
SupplicantStatus setWpsDeviceName(
struct wpa_supplicant* wpa_s, const std::string& name)
{
@@ -176,7 +178,7 @@
}
} // namespace iface_config_utils
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/iface_config_utils.h b/wpa_supplicant/hidl/1.4/iface_config_utils.h
similarity index 97%
rename from wpa_supplicant/hidl/1.3/iface_config_utils.h
rename to wpa_supplicant/hidl/1.4/iface_config_utils.h
index 822d7ac..a94feb5 100644
--- a/wpa_supplicant/hidl/1.3/iface_config_utils.h
+++ b/wpa_supplicant/hidl/1.4/iface_config_utils.h
@@ -30,7 +30,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
namespace iface_config_utils {
SupplicantStatus setWpsDeviceName(
@@ -51,7 +51,7 @@
struct wpa_supplicant* wpa_s, bool useExternalSim);
} // namespace iface_config_utils
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/misc_utils.h b/wpa_supplicant/hidl/1.4/misc_utils.h
similarity index 98%
rename from wpa_supplicant/hidl/1.3/misc_utils.h
rename to wpa_supplicant/hidl/1.4/misc_utils.h
index b95b1ee..6e69d4c 100644
--- a/wpa_supplicant/hidl/1.3/misc_utils.h
+++ b/wpa_supplicant/hidl/1.4/misc_utils.h
@@ -27,7 +27,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
namespace misc_utils {
using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
@@ -103,7 +103,7 @@
}
} // namespace misc_utils
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/p2p_iface.cpp b/wpa_supplicant/hidl/1.4/p2p_iface.cpp
similarity index 97%
rename from wpa_supplicant/hidl/1.3/p2p_iface.cpp
rename to wpa_supplicant/hidl/1.4/p2p_iface.cpp
index ffa9b6a..7baffa2 100644
--- a/wpa_supplicant/hidl/1.3/p2p_iface.cpp
+++ b/wpa_supplicant/hidl/1.4/p2p_iface.cpp
@@ -406,7 +406,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using hidl_return_util::validateAndCall;
using V1_0::SupplicantStatusCode;
@@ -881,6 +881,20 @@
&P2pIface::setMacRandomizationInternal, _hidl_cb, enable);
}
+Return<void> P2pIface::setEdmg(bool enable, setEdmg_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &P2pIface::setEdmgInternal, _hidl_cb, enable);
+}
+
+Return<void> P2pIface::getEdmg(getEdmg_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &P2pIface::getEdmgInternal, _hidl_cb);
+}
+
std::pair<SupplicantStatus, std::string> P2pIface::getNameInternal()
{
return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
@@ -1083,8 +1097,9 @@
int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
const char* pin =
pre_selected_pin.length() > 0 ? pre_selected_pin.data() : nullptr;
+ bool auto_join = !join_existing_group;
int new_pin = wpas_p2p_connect(
- wpa_s, peer_address.data(), pin, wps_method, persistent, false,
+ wpa_s, peer_address.data(), pin, wps_method, persistent, auto_join,
join_existing_group, false, go_intent_signed, 0, 0, -1, false, ht40,
vht, CHANWIDTH_USE_HT, he, 0, nullptr, 0);
if (new_pin < 0) {
@@ -1781,14 +1796,6 @@
bool currentEnabledState = !!wpa_s->conf->p2p_device_random_mac_addr;
u8 *addr = NULL;
- // A dedicated p2p device is not managed by supplicant,
- // supplicant could not change its MAC address.
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) {
- wpa_printf(MSG_ERROR,
- "Dedicated P2P device don't support MAC randomization");
- return {SupplicantStatusCode::FAILURE_ARGS_INVALID, "NotSupported"};
- }
-
// The same state, no change is needed.
if (currentEnabledState == enable) {
wpa_printf(MSG_DEBUG, "The random MAC is %s already.",
@@ -1832,6 +1839,22 @@
return {SupplicantStatusCode::SUCCESS, ""};
}
+V1_4::SupplicantStatus P2pIface::setEdmgInternal(bool enable)
+{
+ struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+ wpa_printf(MSG_DEBUG, "set p2p_go_edmg to %d", enable);
+ wpa_s->conf->p2p_go_edmg = enable ? 1 : 0;
+ wpa_s->p2p_go_edmg = enable ? 1 : 0;
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<V1_4::SupplicantStatus, bool> P2pIface::getEdmgInternal()
+{
+ struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
+ (wpa_s->p2p_go_edmg == 1)};
+}
+
/**
* Retrieve the underlying |wpa_supplicant| struct
* pointer for this iface.
@@ -1853,7 +1876,7 @@
}
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/p2p_iface.h b/wpa_supplicant/hidl/1.4/p2p_iface.h
similarity index 96%
rename from wpa_supplicant/hidl/1.3/p2p_iface.h
rename to wpa_supplicant/hidl/1.4/p2p_iface.h
index 608dbd4..1694efe 100644
--- a/wpa_supplicant/hidl/1.3/p2p_iface.h
+++ b/wpa_supplicant/hidl/1.4/p2p_iface.h
@@ -15,7 +15,7 @@
#include <android-base/macros.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantP2pIface.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantP2pIface.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetwork.h>
@@ -30,11 +30,13 @@
#include "config.h"
}
+#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
+
namespace android {
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::SupplicantNetworkId;
using V1_0::SupplicantStatus;
@@ -47,7 +49,7 @@
* object is used for control operations on a specific interface
* controlled by wpa_supplicant.
*/
-class P2pIface : public V1_2::ISupplicantP2pIface
+class P2pIface : public V1_4::ISupplicantP2pIface
{
public:
P2pIface(struct wpa_global* wpa_global, const char ifname[]);
@@ -196,6 +198,8 @@
bool joinExistingGroup, addGroup_1_2_cb _hidl_cb) override;
Return<void> setMacRandomization(
bool enable, setMacRandomization_cb _hidl_cb) override;
+ Return<void> setEdmg(bool enable, setEdmg_cb _hidl_cb) override;
+ Return<void> getEdmg(getEdmg_cb _hidl_cb) override;
private:
// Corresponding worker functions for the HIDL methods.
@@ -305,6 +309,8 @@
bool persistent, uint32_t freq, const std::array<uint8_t, 6>& peer_address,
bool joinExistingGroup);
SupplicantStatus setMacRandomizationInternal(bool enable);
+ V1_4::SupplicantStatus setEdmgInternal(bool enable);
+ std::pair<V1_4::SupplicantStatus, bool> getEdmgInternal();
struct wpa_supplicant* retrieveIfacePtr();
struct wpa_supplicant* retrieveGroupIfacePtr(
@@ -321,7 +327,7 @@
};
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/p2p_network.cpp b/wpa_supplicant/hidl/1.4/p2p_network.cpp
similarity index 99%
rename from wpa_supplicant/hidl/1.3/p2p_network.cpp
rename to wpa_supplicant/hidl/1.4/p2p_network.cpp
index c87e4c0..cba2fdd 100644
--- a/wpa_supplicant/hidl/1.3/p2p_network.cpp
+++ b/wpa_supplicant/hidl/1.4/p2p_network.cpp
@@ -20,7 +20,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using hidl_return_util::validateAndCall;
using V1_0::SupplicantStatusCode;
@@ -250,7 +250,7 @@
(struct wpa_global *)wpa_global_, ifname_.c_str());
}
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/p2p_network.h b/wpa_supplicant/hidl/1.4/p2p_network.h
similarity index 98%
rename from wpa_supplicant/hidl/1.3/p2p_network.h
rename to wpa_supplicant/hidl/1.4/p2p_network.h
index 8c134b0..94e3763 100644
--- a/wpa_supplicant/hidl/1.3/p2p_network.h
+++ b/wpa_supplicant/hidl/1.4/p2p_network.h
@@ -26,7 +26,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicantP2pNetwork;
using V1_0::ISupplicantP2pNetworkCallback;
@@ -96,7 +96,7 @@
};
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/sta_iface.cpp b/wpa_supplicant/hidl/1.4/sta_iface.cpp
similarity index 81%
rename from wpa_supplicant/hidl/1.3/sta_iface.cpp
rename to wpa_supplicant/hidl/1.4/sta_iface.cpp
index b738ff7..731808d 100644
--- a/wpa_supplicant/hidl/1.3/sta_iface.cpp
+++ b/wpa_supplicant/hidl/1.4/sta_iface.cpp
@@ -35,10 +35,13 @@
using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
using android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
using android::hardware::wifi::supplicant::V1_0::ISupplicantStaNetwork;
-using android::hardware::wifi::supplicant::V1_3::ISupplicantStaIface;
-using android::hardware::wifi::supplicant::V1_3::ConnectionCapabilities;
using android::hardware::wifi::supplicant::V1_3::WifiTechnology;
-using android::hardware::wifi::supplicant::V1_3::implementation::HidlManager;
+using android::hardware::wifi::supplicant::V1_4::ISupplicantStaIface;
+using android::hardware::wifi::supplicant::V1_4::ConnectionCapabilities;
+using android::hardware::wifi::supplicant::V1_4::LegacyMode;
+using android::hardware::wifi::supplicant::V1_4::implementation::HidlManager;
+using android::hardware::wifi::supplicant::V1_4::DppResponderBootstrapInfo;
+using android::hardware::wifi::supplicant::V1_4::DppCurve;
constexpr uint32_t kMaxAnqpElems = 100;
constexpr char kGetMacAddress[] = "MACADDR";
@@ -217,16 +220,76 @@
return mask;
}
+const std::string getDppListenChannel(struct wpa_supplicant *wpa_s, int32_t *listen_channel)
+{
+ struct hostapd_hw_modes *mode;
+ int chan44 = 0, chan149 = 0;
+ *listen_channel = 0;
+
+ /* Check if device support 2.4GHz band*/
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ HOSTAPD_MODE_IEEE80211G, 0);
+ if (mode) {
+ *listen_channel = 6;
+ return "81/6";
+ }
+ /* Check if device support 5GHz band */
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ HOSTAPD_MODE_IEEE80211A, 0);
+ if (mode) {
+ for (int i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (chan->freq == 5220)
+ chan44 = 1;
+ if (chan->freq == 5745)
+ chan149 = 1;
+ }
+ if (chan149) {
+ *listen_channel = 149;
+ return "124/149";
+ } else if (chan44) {
+ *listen_channel = 44;
+ return "115/44";
+ }
+ }
+
+ return "";
+}
+
+const std::string convertCurveTypeToName(DppCurve curve)
+{
+ switch (curve) {
+ case DppCurve::PRIME256V1:
+ return "prime256v1";
+ case DppCurve::SECP384R1:
+ return "secp384r1";
+ case DppCurve::SECP521R1:
+ return "secp521r1";
+ case DppCurve::BRAINPOOLP256R1:
+ return "brainpoolP256r1";
+ case DppCurve::BRAINPOOLP384R1:
+ return "brainpoolP384r1";
+ case DppCurve::BRAINPOOLP512R1:
+ return "brainpoolP512r1";
+ }
+ WPA_ASSERT(false);
+}
+
} // namespace
namespace android {
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using hidl_return_util::validateAndCall;
using V1_0::ISupplicantStaIfaceCallback;
+using V1_0::SupplicantStatusCode;
StaIface::StaIface(struct wpa_global *wpa_global, const char ifname[])
: wpa_global_(wpa_global), ifname_(ifname), is_valid_(true)
@@ -337,6 +400,16 @@
&StaIface::registerCallbackInternal, _hidl_cb, callback_1_3);
}
+Return<void> StaIface::registerCallback_1_4(
+ const sp<V1_4::ISupplicantStaIfaceCallback> &callback,
+ registerCallback_1_4_cb _hidl_cb)
+{
+ sp<V1_4::ISupplicantStaIfaceCallback> callback_1_4 = callback;
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+ &StaIface::registerCallbackInternal_1_4, _hidl_cb, callback_1_4);
+}
+
Return<void> StaIface::reassociate(reassociate_cb _hidl_cb)
{
return validateAndCall(
@@ -390,7 +463,7 @@
}
Return<void> StaIface::initiateAnqpQuery(
const hidl_array<uint8_t, 6> &mac_address,
- const hidl_vec<ISupplicantStaIface::AnqpInfoId> &info_elements,
+ const hidl_vec<V1_0::ISupplicantStaIface::AnqpInfoId> &info_elements,
const hidl_vec<ISupplicantStaIface::Hs20AnqpSubtypes> &sub_types,
initiateAnqpQuery_cb _hidl_cb)
{
@@ -400,6 +473,15 @@
info_elements, sub_types);
}
+Return<void> StaIface::initiateVenueUrlAnqpQuery(
+ const hidl_array<uint8_t, 6> &mac_address,
+ initiateVenueUrlAnqpQuery_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+ &StaIface::initiateVenueUrlAnqpQueryInternal, _hidl_cb, mac_address);
+}
+
Return<void> StaIface::initiateHs20IconQuery(
const hidl_array<uint8_t, 6> &mac_address, const hidl_string &file_name,
initiateHs20IconQuery_cb _hidl_cb)
@@ -663,6 +745,31 @@
&StaIface::stopDppInitiatorInternal, _hidl_cb);
}
+Return<void> StaIface::generateDppBootstrapInfoForResponder(
+ const hidl_array<uint8_t, 6> &mac_address, const hidl_string& device_info,
+ DppCurve curve, generateDppBootstrapInfoForResponder_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+ &StaIface::generateDppBootstrapInfoForResponderInternal, _hidl_cb, mac_address,
+ device_info, curve);
+}
+
+Return<void> StaIface::startDppEnrolleeResponder(
+ uint32_t listen_channel, startDppEnrolleeResponder_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+ &StaIface::startDppEnrolleeResponderInternal, _hidl_cb, listen_channel);
+}
+
+Return<void> StaIface::stopDppResponder(uint32_t own_bootstrap_id, stopDppResponder_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_IFACE_INVALID,
+ &StaIface::stopDppResponderInternal, _hidl_cb, own_bootstrap_id);
+}
+
Return<void> StaIface::getWpaDriverCapabilities(
getWpaDriverCapabilities_cb _hidl_cb)
{
@@ -671,6 +778,14 @@
&StaIface::getWpaDriverCapabilitiesInternal, _hidl_cb);
}
+Return<void> StaIface::getWpaDriverCapabilities_1_4(
+ getWpaDriverCapabilities_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_UNKNOWN,
+ &StaIface::getWpaDriverCapabilitiesInternal_1_4, _hidl_cb);
+}
+
Return<void> StaIface::setMboCellularDataStatus(bool available,
setMboCellularDataStatus_cb _hidl_cb)
{
@@ -742,7 +857,7 @@
std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
StaIface::addNetworkInternal()
{
- android::sp<ISupplicantStaNetwork> network;
+ android::sp<V1_3::ISupplicantStaNetwork> network;
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
struct wpa_ssid *ssid = wpa_supplicant_add_network(wpa_s);
if (!ssid) {
@@ -765,6 +880,14 @@
&StaIface::getConnectionCapabilitiesInternal, _hidl_cb);
}
+Return<void> StaIface::getConnectionCapabilities_1_4(
+ getConnectionCapabilities_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_UNKNOWN,
+ &StaIface::getConnectionCapabilitiesInternal_1_4, _hidl_cb);
+}
+
SupplicantStatus StaIface::removeNetworkInternal(SupplicantNetworkId id)
{
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
@@ -781,7 +904,7 @@
std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
StaIface::getNetworkInternal(SupplicantNetworkId id)
{
- android::sp<ISupplicantStaNetwork> network;
+ android::sp<V1_3::ISupplicantStaNetwork> network;
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
struct wpa_ssid *ssid = wpa_config_get_network(wpa_s->conf, id);
if (!ssid) {
@@ -812,12 +935,18 @@
SupplicantStatus StaIface::registerCallbackInternal(
const sp<ISupplicantStaIfaceCallback> &callback)
{
+ return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
+}
+
+V1_4::SupplicantStatus StaIface::registerCallbackInternal_1_4(
+ const sp<V1_4::ISupplicantStaIfaceCallback> &callback)
+{
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager ||
hidl_manager->addStaIfaceCallbackHidlObject(ifname_, callback)) {
- return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+ return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
- return {SupplicantStatusCode::SUCCESS, ""};
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
}
SupplicantStatus StaIface::reassociateInternal()
@@ -943,14 +1072,28 @@
static_cast<std::underlying_type<
ISupplicantStaIface::Hs20AnqpSubtypes>::type>(type));
}
+
if (anqp_send_req(
- wpa_s, mac_address.data(), info_elems_buf, num_info_elems,
- sub_types_bitmask, false)) {
+ wpa_s, mac_address.data(), 0, info_elems_buf, num_info_elems,
+ sub_types_bitmask, 0)) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
return {SupplicantStatusCode::SUCCESS, ""};
}
+V1_4::SupplicantStatus StaIface::initiateVenueUrlAnqpQueryInternal(
+ const std::array<uint8_t, 6> &mac_address)
+{
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ uint16_t info_elems_buf[1] = {ANQP_VENUE_URL};
+
+ if (anqp_send_req(
+ wpa_s, mac_address.data(), 0, info_elems_buf, 1, 0, 0)) {
+ return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+ }
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
SupplicantStatus StaIface::initiateHs20IconQueryInternal(
const std::array<uint8_t, 6> &mac_address, const std::string &file_name)
{
@@ -1421,56 +1564,182 @@
#endif
}
-std::pair<SupplicantStatus, ConnectionCapabilities>
+std::pair<V1_4::SupplicantStatus, DppResponderBootstrapInfo>
+StaIface::generateDppBootstrapInfoForResponderInternal(const std::array<uint8_t, 6> &mac_address,
+ const std::string& device_info, DppCurve curve)
+{
+#ifdef CONFIG_DPP
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ std::string cmd = "type=qrcode";
+ int32_t id;
+ int32_t listen_channel = 0;
+ struct DppResponderBootstrapInfo bootstrap_info;
+ const char *uri;
+ std::string listen_channel_str;
+ std::string mac_addr_str;
+ char buf[3] = {0};
+
+ cmd += (device_info.empty()) ? "" : " info=" + device_info;
+
+ listen_channel_str = getDppListenChannel(wpa_s, &listen_channel);
+ if (listen_channel == 0) {
+ wpa_printf(MSG_ERROR, "StaIface: Failed to derive DPP listen channel");
+ return {{V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""}, bootstrap_info};
+ }
+ cmd += " chan=" + listen_channel_str;
+
+ cmd += " mac=";
+ for (int i = 0;i < 6;i++) {
+ snprintf(buf, sizeof(buf), "%02x", mac_address[i]);
+ mac_addr_str.append(buf);
+ }
+ cmd += mac_addr_str;
+
+ cmd += " curve=" + convertCurveTypeToName(curve);
+
+ id = dpp_bootstrap_gen(wpa_s->dpp, cmd.c_str());
+ wpa_printf(MSG_DEBUG,
+ "DPP generate bootstrap QR code command: %s id: %d", cmd.c_str(), id);
+ if (id > 0) {
+ uri = dpp_bootstrap_get_uri(wpa_s->dpp, id);
+ if (uri) {
+ wpa_printf(MSG_DEBUG, "DPP Bootstrap info: id: %d "
+ "listen_channel: %d uri: %s", id, listen_channel, uri);
+ bootstrap_info.bootstrapId = id;
+ bootstrap_info.listenChannel = listen_channel;
+ bootstrap_info.uri = uri;
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""}, bootstrap_info};
+ }
+ }
+ return {{V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""}, bootstrap_info};
+#else
+ return {{V1_4::SupplicantStatusCode::FAILURE_UNSUPPORTED, ""}, bootstrap_info};
+#endif
+}
+
+V1_4::SupplicantStatus StaIface::startDppEnrolleeResponderInternal(uint32_t listen_channel)
+{
+#ifdef CONFIG_DPP
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ std::string cmd = "";
+ uint32_t freq = (listen_channel <= 14 ? 2407 : 5000) + listen_channel * 5;
+
+ /* Report received configuration to HIDL and create an internal profile */
+ wpa_s->conf->dpp_config_processing = 1;
+
+ cmd += std::to_string(freq);
+ cmd += " role=enrollee netrole=sta";
+
+ wpa_printf(MSG_DEBUG,
+ "DPP Enrollee Responder command: %s", cmd.c_str());
+
+ if (wpas_dpp_listen(wpa_s, cmd.c_str()) == 0) {
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+ }
+ return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+#else
+ return {V1_4::SupplicantStatusCode::FAILURE_UNSUPPORTED, ""};
+#endif
+}
+
+V1_4::SupplicantStatus StaIface::stopDppResponderInternal(uint32_t own_bootstrap_id)
+{
+#ifdef CONFIG_DPP
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ std::string bootstrap_id_str;
+
+ if (own_bootstrap_id == 0) {
+ bootstrap_id_str = "*";
+ }
+ else {
+ bootstrap_id_str = std::to_string(own_bootstrap_id);
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP Stop DPP Responder id: %d ", own_bootstrap_id);
+ wpas_dpp_stop(wpa_s);
+ wpas_dpp_listen_stop(wpa_s);
+
+ if (dpp_bootstrap_remove(wpa_s->dpp, bootstrap_id_str.c_str()) < 0) {
+ wpa_printf(MSG_ERROR, "StaIface: dpp_bootstrap_remove failed");
+ }
+
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+#else
+ return {V1_4::SupplicantStatusCode::FAILURE_UNSUPPORTED, ""};
+#endif
+}
+
+std::pair<SupplicantStatus, android::hardware::wifi::supplicant::V1_3::ConnectionCapabilities>
StaIface::getConnectionCapabilitiesInternal()
{
+ struct android::hardware::wifi::supplicant::V1_3::ConnectionCapabilities capa;
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, capa};
+}
+
+std::pair<V1_4::SupplicantStatus, ConnectionCapabilities>
+StaIface::getConnectionCapabilitiesInternal_1_4()
+{
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
struct ConnectionCapabilities capa;
if (wpa_s->connection_set) {
+ capa.legacyMode = LegacyMode::UNKNOWN;
if (wpa_s->connection_he) {
- capa.technology = WifiTechnology::HE;
+ capa.V1_3.technology = WifiTechnology::HE;
} else if (wpa_s->connection_vht) {
- capa.technology = WifiTechnology::VHT;
+ capa.V1_3.technology = WifiTechnology::VHT;
} else if (wpa_s->connection_ht) {
- capa.technology = WifiTechnology::HT;
+ capa.V1_3.technology = WifiTechnology::HT;
} else {
- capa.technology = WifiTechnology::LEGACY;
+ capa.V1_3.technology = WifiTechnology::LEGACY;
+ if (wpas_freq_to_band(wpa_s->assoc_freq) == BAND_2_4_GHZ) {
+ capa.legacyMode = (wpa_s->connection_11b_only) ? LegacyMode::B_MODE
+ : LegacyMode::G_MODE;
+ } else {
+ capa.legacyMode = LegacyMode::A_MODE;
+ }
}
switch (wpa_s->connection_channel_bandwidth) {
case CHAN_WIDTH_20:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
break;
case CHAN_WIDTH_40:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_40;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_40;
break;
case CHAN_WIDTH_80:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80;
break;
case CHAN_WIDTH_160:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_160;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_160;
break;
case CHAN_WIDTH_80P80:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80P80;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_80P80;
break;
default:
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
break;
}
- capa.maxNumberRxSpatialStreams = wpa_s->connection_max_nss_rx;
- capa.maxNumberTxSpatialStreams = wpa_s->connection_max_nss_tx;
+ capa.V1_3.maxNumberRxSpatialStreams = wpa_s->connection_max_nss_rx;
+ capa.V1_3.maxNumberTxSpatialStreams = wpa_s->connection_max_nss_tx;
} else {
- capa.technology = WifiTechnology::UNKNOWN;
- capa.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
- capa.maxNumberTxSpatialStreams = 1;
- capa.maxNumberRxSpatialStreams = 1;
+ capa.V1_3.technology = WifiTechnology::UNKNOWN;
+ capa.V1_3.channelBandwidth = WifiChannelWidthInMhz::WIDTH_20;
+ capa.V1_3.maxNumberTxSpatialStreams = 1;
+ capa.V1_3.maxNumberRxSpatialStreams = 1;
+ capa.legacyMode = LegacyMode::UNKNOWN;
}
- return {{SupplicantStatusCode::SUCCESS, ""}, capa};
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""}, capa};
}
std::pair<SupplicantStatus, uint32_t>
StaIface::getWpaDriverCapabilitiesInternal()
{
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
+}
+
+std::pair<V1_4::SupplicantStatus, uint32_t>
+StaIface::getWpaDriverCapabilitiesInternal_1_4()
+{
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
uint32_t mask = 0;
@@ -1479,15 +1748,18 @@
* transition + Cellular steering. 11v is a default feature in
* supplicant. And cellular steering is handled in framework.
*/
- mask |= WpaDriverCapabilitiesMask::MBO;
+ mask |= V1_3::WpaDriverCapabilitiesMask::MBO;
if (wpa_s->enable_oce & OCE_STA) {
- mask |= WpaDriverCapabilitiesMask::OCE;
+ mask |= V1_3::WpaDriverCapabilitiesMask::OCE;
}
#endif
+#ifdef CONFIG_SAE_PK
+ mask |= V1_4::WpaDriverCapabilitiesMask::SAE_PK;
+#endif
wpa_printf(MSG_DEBUG, "Driver capability mask: 0x%x", mask);
- return {{SupplicantStatusCode::SUCCESS, ""}, mask};
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""}, mask};
}
SupplicantStatus StaIface::setMboCellularDataStatusInternal(bool available)
@@ -1501,7 +1773,19 @@
} else {
mbo_cell_capa = MBO_CELL_CAPA_NOT_AVAILABLE;
}
+
+#ifdef ENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS
+ char mbo_cmd[32];
+ char buf[32];
+
+ os_snprintf(mbo_cmd, sizeof(mbo_cmd), "%s %d", "MBO CELL_DATA_CAP", mbo_cell_capa);
+ if (wpa_drv_driver_cmd(wpa_s, mbo_cmd, buf, sizeof(buf)) < 0) {
+ wpa_printf(MSG_ERROR, "MBO CELL_DATA_CAP cmd failed CAP:%d", mbo_cell_capa);
+ }
+#else
wpas_mbo_update_cell_capa(wpa_s, mbo_cell_capa);
+#endif
+
return {SupplicantStatusCode::SUCCESS, ""};
#else
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
@@ -1535,7 +1819,7 @@
return wpa_supplicant_get_iface(wpa_global_, ifname_.c_str());
}
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/sta_iface.h b/wpa_supplicant/hidl/1.4/sta_iface.h
similarity index 86%
rename from wpa_supplicant/hidl/1.3/sta_iface.h
rename to wpa_supplicant/hidl/1.4/sta_iface.h
index ba06e5a..d49e469 100644
--- a/wpa_supplicant/hidl/1.3/sta_iface.h
+++ b/wpa_supplicant/hidl/1.4/sta_iface.h
@@ -15,8 +15,8 @@
#include <android-base/macros.h>
-#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaIface.h>
-#include <android/hardware/wifi/supplicant/1.2/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaIfaceCallback.h>
#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
extern "C"
@@ -33,7 +33,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicantNetwork;
using android::hardware::wifi::supplicant::V1_2::DppAkm;
@@ -44,7 +44,7 @@
* object is used for control operations on a specific interface
* controlled by wpa_supplicant.
*/
-class StaIface : public V1_3::ISupplicantStaIface
+class StaIface : public V1_4::ISupplicantStaIface
{
public:
StaIface(struct wpa_global* wpa_global, const char ifname[]);
@@ -90,6 +90,9 @@
Return<void> registerCallback_1_3(
const sp<V1_3::ISupplicantStaIfaceCallback>& callback,
registerCallback_cb _hidl_cb) override;
+ Return<void> registerCallback_1_4(
+ const sp<V1_4::ISupplicantStaIfaceCallback> &callback,
+ registerCallback_1_4_cb _hidl_cb) override;
Return<void> reassociate(reassociate_cb _hidl_cb) override;
Return<void> reconnect(reconnect_cb _hidl_cb) override;
Return<void> disconnect(disconnect_cb _hidl_cb) override;
@@ -106,9 +109,12 @@
initiateTdlsTeardown_cb _hidl_cb) override;
Return<void> initiateAnqpQuery(
const hidl_array<uint8_t, 6>& mac_address,
- const hidl_vec<ISupplicantStaIface::AnqpInfoId>& info_elements,
+ const hidl_vec<V1_0::ISupplicantStaIface::AnqpInfoId>& info_elements,
const hidl_vec<ISupplicantStaIface::Hs20AnqpSubtypes>& sub_types,
initiateAnqpQuery_cb _hidl_cb) override;
+ Return<void> initiateVenueUrlAnqpQuery(
+ const hidl_array<uint8_t, 6>& mac_address,
+ initiateVenueUrlAnqpQuery_cb _hidl_cb) override;
Return<void> initiateHs20IconQuery(
const hidl_array<uint8_t, 6>& mac_address,
const hidl_string& file_name,
@@ -189,12 +195,23 @@
Return<void> stopDppInitiator(stopDppInitiator_cb _hidl_cb) override;
Return<void> getConnectionCapabilities(
getConnectionCapabilities_cb _hidl_cb) override;
+ Return<void> getConnectionCapabilities_1_4(
+ getConnectionCapabilities_1_4_cb _hidl_cb) override;
Return<void> getWpaDriverCapabilities(
getWpaDriverCapabilities_cb _hidl_cb) override;
Return<void> setMboCellularDataStatus(bool available,
setMboCellularDataStatus_cb _hidl_cb) override;
Return<void> getKeyMgmtCapabilities_1_3(
getKeyMgmtCapabilities_1_3_cb _hidl_cb) override;
+ Return<void> getWpaDriverCapabilities_1_4(
+ getWpaDriverCapabilities_1_4_cb _hidl_cb) override;
+ Return<void> generateDppBootstrapInfoForResponder(const hidl_array<uint8_t, 6> &mac_address,
+ const hidl_string& device_info, DppCurve curve,
+ generateDppBootstrapInfoForResponder_cb _hidl_cb) override;
+ Return<void> startDppEnrolleeResponder(uint32_t listen_channel,
+ startDppEnrolleeResponder_cb _hidl_cb) override;
+ Return<void> stopDppResponder(uint32_t own_bootstrap_id,
+ stopDppResponder_cb _hidl_cb) override;
private:
// Corresponding worker functions for the HIDL methods.
@@ -215,6 +232,8 @@
const sp<V1_0::ISupplicantStaIfaceCallback>& callback);
SupplicantStatus registerCallbackInternal_1_1(
const sp<V1_1::ISupplicantStaIfaceCallback>& callback);
+ V1_4::SupplicantStatus registerCallbackInternal_1_4(
+ const sp<V1_4::ISupplicantStaIfaceCallback>& callback);
SupplicantStatus reassociateInternal();
SupplicantStatus reconnectInternal();
SupplicantStatus disconnectInternal();
@@ -230,6 +249,8 @@
const std::vector<ISupplicantStaIface::AnqpInfoId>& info_elements,
const std::vector<ISupplicantStaIface::Hs20AnqpSubtypes>&
sub_types);
+ V1_4::SupplicantStatus initiateVenueUrlAnqpQueryInternal(
+ const std::array<uint8_t, 6>& mac_address);
SupplicantStatus initiateHs20IconQueryInternal(
const std::array<uint8_t, 6>& mac_address,
const std::string& file_name);
@@ -282,10 +303,19 @@
SupplicantStatus startDppEnrolleeInitiatorInternal(uint32_t peer_bootstrap_id,
uint32_t own_bootstrap_id);
SupplicantStatus stopDppInitiatorInternal();
- std::pair<SupplicantStatus, ConnectionCapabilities> getConnectionCapabilitiesInternal();
+ std::pair<SupplicantStatus, V1_3::ConnectionCapabilities> getConnectionCapabilitiesInternal();
+ std::pair<V1_4::SupplicantStatus, V1_4::ConnectionCapabilities>
+ getConnectionCapabilitiesInternal_1_4();
std::pair<SupplicantStatus, uint32_t> getWpaDriverCapabilitiesInternal();
SupplicantStatus setMboCellularDataStatusInternal(bool available);
std::pair<SupplicantStatus, uint32_t> getKeyMgmtCapabilitiesInternal_1_3();
+ std::pair<V1_4::SupplicantStatus, uint32_t> getWpaDriverCapabilitiesInternal_1_4();
+ std::pair<V1_4::SupplicantStatus, V1_4::DppResponderBootstrapInfo>
+ generateDppBootstrapInfoForResponderInternal(
+ const std::array<uint8_t, 6>& mac_address, const std::string& device_info,
+ DppCurve curve);
+ V1_4::SupplicantStatus startDppEnrolleeResponderInternal(uint32_t listen_channel);
+ V1_4::SupplicantStatus stopDppResponderInternal(uint32_t own_bootstrap_id);
struct wpa_supplicant* retrieveIfacePtr();
@@ -300,7 +330,7 @@
};
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/sta_network.cpp b/wpa_supplicant/hidl/1.4/sta_network.cpp
similarity index 92%
rename from wpa_supplicant/hidl/1.3/sta_network.cpp
rename to wpa_supplicant/hidl/1.4/sta_network.cpp
index d3b120d..3e773de 100644
--- a/wpa_supplicant/hidl/1.3/sta_network.cpp
+++ b/wpa_supplicant/hidl/1.4/sta_network.cpp
@@ -22,6 +22,7 @@
using android::hardware::wifi::supplicant::V1_0::ISupplicantStaNetwork;
using ISupplicantStaNetworkV1_2 = android::hardware::wifi::supplicant::V1_2::ISupplicantStaNetwork;
using ISupplicantStaNetworkV1_3 = android::hardware::wifi::supplicant::V1_3::ISupplicantStaNetwork;
+using ISupplicantStaNetworkV1_4 = android::hardware::wifi::supplicant::V1_4::ISupplicantStaNetwork;
constexpr uint8_t kZeroBssid[6] = {0, 0, 0, 0, 0, 0};
@@ -60,7 +61,8 @@
static_cast<uint32_t>(
ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) |
static_cast<uint32_t>(ISupplicantStaNetworkV1_2::GroupCipherMask::GCMP_256) |
- static_cast<uint32_t>(ISupplicantStaNetworkV1_3::GroupCipherMask::SMS4));
+ static_cast<uint32_t>(ISupplicantStaNetworkV1_3::GroupCipherMask::SMS4) |
+ static_cast<uint32_t>(ISupplicantStaNetworkV1_4::GroupCipherMask::GCMP_128));
constexpr uint32_t kAllowedPairwisewCipherMask =
(static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::NONE) |
static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::TKIP) |
@@ -68,7 +70,8 @@
static_cast<uint32_t>(
ISupplicantStaNetworkV1_2::PairwiseCipherMask::GCMP_256) |
static_cast<uint32_t>(
- ISupplicantStaNetworkV1_3::PairwiseCipherMask::SMS4));
+ ISupplicantStaNetworkV1_3::PairwiseCipherMask::SMS4) |
+ static_cast<uint32_t>(ISupplicantStaNetworkV1_4::PairwiseCipherMask::GCMP_128));
constexpr uint32_t kAllowedGroupMgmtCipherMask =
(static_cast<uint32_t>(
ISupplicantStaNetworkV1_2::GroupMgmtCipherMask::BIP_GMAC_128) |
@@ -93,6 +96,9 @@
constexpr char kNetworkEapSimUmtsAutsResponse[] = "UMTS-AUTS";
constexpr char kNetworkEapSimGsmAuthFailure[] = "GSM-FAIL";
constexpr char kNetworkEapSimUmtsAuthFailure[] = "UMTS-FAIL";
+/* These constants refer definitions in wpa_supplicant/config.h */
+constexpr int SAE_PWE_H2E_ONLY_MODE = 1;
+constexpr int SAE_PWE_HP_H2E_BOTH = 2;
#ifdef CONFIG_WAPI_INTERFACE
std::string dummyWapiCertSuite;
@@ -104,9 +110,10 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using hidl_return_util::validateAndCall;
+using V1_0::SupplicantStatus;
using V1_0::SupplicantStatusCode;
StaNetwork::StaNetwork(
@@ -153,6 +160,15 @@
&StaNetwork::registerCallbackInternal, _hidl_cb, callback);
}
+Return<void> StaNetwork::registerCallback_1_4(
+ const sp<V1_4::ISupplicantStaNetworkCallback> &callback,
+ registerCallback_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::registerCallback_1_4Internal, _hidl_cb, callback);
+}
+
Return<void> StaNetwork::setSsid(
const hidl_vec<uint8_t> &ssid, setSsid_cb _hidl_cb)
{
@@ -409,6 +425,13 @@
&StaNetwork::setWapiCertSuiteInternal, _hidl_cb, suite);
}
+Return<void> StaNetwork::setEdmg(bool enable, setEdmg_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::setEdmgInternal, _hidl_cb, enable);
+}
+
Return<void> StaNetwork::getSsid(getSsid_cb _hidl_cb)
{
return validateAndCall(
@@ -637,6 +660,13 @@
&StaNetwork::getWapiCertSuiteInternal, _hidl_cb);
}
+Return<void> StaNetwork::getEdmg(getEdmg_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::getEdmgInternal, _hidl_cb);
+}
+
Return<void> StaNetwork::enable(bool no_connect, enable_cb _hidl_cb)
{
return validateAndCall(
@@ -822,7 +852,7 @@
}
Return<void> StaNetwork::setOcsp(
- OcspType ocspType, setOcsp_cb _hidl_cb) {
+ V1_3::OcspType ocspType, setOcsp_cb _hidl_cb) {
return validateAndCall(
this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
&StaNetwork::setOcspInternal, _hidl_cb, ocspType);
@@ -928,6 +958,52 @@
&StaNetwork::setEapErpInternal, _hidl_cb, enable);
}
+Return<void> StaNetwork::setGroupCipher_1_4(
+ uint32_t group_cipher_mask, setGroupCipher_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::setGroupCipher_1_4Internal, _hidl_cb, group_cipher_mask);
+}
+
+Return<void> StaNetwork::getGroupCipher_1_4(getGroupCipher_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::getGroupCipher_1_4Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::setPairwiseCipher_1_4(
+ uint32_t pairwise_cipher_mask, setPairwiseCipher_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::setPairwiseCipher_1_4Internal, _hidl_cb,
+ pairwise_cipher_mask);
+}
+
+Return<void> StaNetwork::getPairwiseCipher_1_4(
+ getPairwiseCipher_1_4_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::getPairwiseCipher_1_4Internal, _hidl_cb);
+}
+
+Return<void> StaNetwork::enableSaeH2eOnlyMode(bool enable, enableSaeH2eOnlyMode_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::enableSaeH2eOnlyModeInternal, _hidl_cb, enable);
+}
+
+Return<void> StaNetwork::enableSaePkOnlyMode(bool enable, enableSaePkOnlyMode_cb _hidl_cb)
+{
+ return validateAndCall(
+ this, V1_4::SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+ &StaNetwork::enableSaePkOnlyModeInternal, _hidl_cb, enable);
+}
+
std::pair<SupplicantStatus, uint32_t> StaNetwork::getIdInternal()
{
return {{SupplicantStatusCode::SUCCESS, ""}, network_id_};
@@ -946,12 +1022,18 @@
SupplicantStatus StaNetwork::registerCallbackInternal(
const sp<ISupplicantStaNetworkCallback> &callback)
{
+ return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
+}
+
+V1_4::SupplicantStatus StaNetwork::registerCallback_1_4Internal(
+ const sp<V1_4::ISupplicantStaNetworkCallback> &callback)
+{
HidlManager *hidl_manager = HidlManager::getInstance();
if (!hidl_manager || hidl_manager->addStaNetworkCallbackHidlObject(
ifname_, network_id_, callback)) {
- return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+ return {V1_4::SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
- return {SupplicantStatusCode::SUCCESS, ""};
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
}
SupplicantStatus StaNetwork::setSsidInternal(const std::vector<uint8_t> &ssid)
@@ -1028,6 +1110,14 @@
return {SupplicantStatusCode::SUCCESS, ""};
}
+V1_4::SupplicantStatus StaNetwork::setEdmgInternal(bool enable)
+{
+ struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+ wpa_ssid->enable_edmg = enable ? 1 : 0;
+ resetInternalStateAfterParamsUpdate();
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
SupplicantStatus StaNetwork::setGroupCipherInternal(uint32_t group_cipher_mask)
{
return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
@@ -1794,6 +1884,13 @@
return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->id_str}};
}
+std::pair<V1_4::SupplicantStatus, bool> StaNetwork::getEdmgInternal()
+{
+ struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
+ (wpa_ssid->enable_edmg == 1)};
+}
+
std::pair<SupplicantStatus, std::vector<uint8_t>>
StaNetwork::getWpsNfcConfigurationTokenInternal()
{
@@ -2130,9 +2227,9 @@
wpa_ssid->group_mgmt_cipher & kAllowedGroupMgmtCipherMask};
}
-SupplicantStatus StaNetwork::setOcspInternal(OcspType ocspType) {
+SupplicantStatus StaNetwork::setOcspInternal(V1_3::OcspType ocspType) {
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
- if (ocspType < OcspType::NONE || ocspType > OcspType::REQUIRE_ALL_CERTS_STATUS) {
+ if (ocspType < V1_3::OcspType::NONE || ocspType > V1_3::OcspType::REQUIRE_ALL_CERTS_STATUS) {
return{ SupplicantStatusCode::FAILURE_ARGS_INVALID, "" };
}
wpa_ssid->eap.cert.ocsp = (int) ocspType;
@@ -2142,11 +2239,11 @@
return {SupplicantStatusCode::SUCCESS, ""};
}
-std::pair<SupplicantStatus, OcspType> StaNetwork::getOcspInternal()
+std::pair<SupplicantStatus, V1_3::OcspType> StaNetwork::getOcspInternal()
{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
return {{SupplicantStatusCode::SUCCESS, ""},
- (OcspType) wpa_ssid->eap.cert.ocsp};
+ (V1_3::OcspType) wpa_ssid->eap.cert.ocsp};
}
SupplicantStatus StaNetwork::setPmkCacheInternal(const std::vector<uint8_t>& serializedEntry) {
@@ -2176,6 +2273,11 @@
return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
}
setFastTransitionKeyMgmt(key_mgmt_mask);
+
+ if (key_mgmt_mask & WPA_KEY_MGMT_OWE) {
+ // Do not allow to connect to Open network when OWE is selected
+ wpa_ssid->owe_only = 1;
+ }
wpa_ssid->key_mgmt = key_mgmt_mask;
wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
resetInternalStateAfterParamsUpdate();
@@ -2212,41 +2314,62 @@
SupplicantStatus StaNetwork::setGroupCipher_1_3Internal(uint32_t group_cipher_mask)
{
- struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
- if (group_cipher_mask & ~kAllowedGroupCipherMask) {
- return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
- }
- wpa_ssid->group_cipher = group_cipher_mask;
- wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
- resetInternalStateAfterParamsUpdate();
- return {SupplicantStatusCode::SUCCESS, ""};
+ return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
}
std::pair<SupplicantStatus, uint32_t> StaNetwork::getGroupCipher_1_3Internal()
{
- struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
- return {{SupplicantStatusCode::SUCCESS, ""},
- wpa_ssid->group_cipher & kAllowedGroupCipherMask};
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
}
SupplicantStatus StaNetwork::setPairwiseCipher_1_3Internal(
uint32_t pairwise_cipher_mask)
{
+ return {SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_3Internal()
+{
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN, "deprecated"}, 0};
+}
+
+V1_4::SupplicantStatus StaNetwork::setGroupCipher_1_4Internal(uint32_t group_cipher_mask)
+{
+ struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+ if (group_cipher_mask & ~kAllowedGroupCipherMask) {
+ return {V1_4::SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+ }
+ wpa_ssid->group_cipher = group_cipher_mask;
+ wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
+ resetInternalStateAfterParamsUpdate();
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<V1_4::SupplicantStatus, uint32_t> StaNetwork::getGroupCipher_1_4Internal()
+{
+ struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
+ wpa_ssid->group_cipher & kAllowedGroupCipherMask};
+}
+
+V1_4::SupplicantStatus StaNetwork::setPairwiseCipher_1_4Internal(
+ uint32_t pairwise_cipher_mask)
+{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
if (pairwise_cipher_mask & ~kAllowedPairwisewCipherMask) {
- return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+ return {V1_4::SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
}
wpa_ssid->pairwise_cipher = pairwise_cipher_mask;
wpa_printf(
MSG_MSGDUMP, "pairwise_cipher: 0x%x", wpa_ssid->pairwise_cipher);
resetInternalStateAfterParamsUpdate();
- return {SupplicantStatusCode::SUCCESS, ""};
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
}
-std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_3Internal()
+std::pair<V1_4::SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipher_1_4Internal()
{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
- return {{SupplicantStatusCode::SUCCESS, ""},
+ return {{V1_4::SupplicantStatusCode::SUCCESS, ""},
wpa_ssid->pairwise_cipher & kAllowedPairwisewCipherMask};
}
@@ -2434,6 +2557,10 @@
*/
void StaNetwork::setFastTransitionKeyMgmt(uint32_t &key_mgmt_mask)
{
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ int res;
+ struct wpa_driver_capa capa;
+
if (key_mgmt_mask & WPA_KEY_MGMT_PSK) {
key_mgmt_mask |= WPA_KEY_MGMT_FT_PSK;
}
@@ -2441,6 +2568,19 @@
if (key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X) {
key_mgmt_mask |= WPA_KEY_MGMT_FT_IEEE8021X;
}
+
+ res = wpa_drv_get_capa(wpa_s, &capa);
+ if (res == 0) {
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SAE
+ if ((key_mgmt_mask & WPA_KEY_MGMT_SAE) &&
+ (capa.key_mgmt_iftype[WPA_IF_STATION] & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE)) {
+ key_mgmt_mask |= WPA_KEY_MGMT_FT_SAE;
+ }
+#endif
+#endif
+ }
+
}
/**
@@ -2456,6 +2596,13 @@
if (key_mgmt_mask & WPA_KEY_MGMT_IEEE8021X) {
key_mgmt_mask &= ~WPA_KEY_MGMT_FT_IEEE8021X;
}
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SAE
+ if (key_mgmt_mask & WPA_KEY_MGMT_SAE) {
+ key_mgmt_mask &= ~WPA_KEY_MGMT_FT_SAE;
+ }
+#endif
+#endif
}
/**
@@ -2473,8 +2620,28 @@
#endif /* CONFIG_FILS */
}
+V1_4::SupplicantStatus StaNetwork::enableSaeH2eOnlyModeInternal(bool enable)
+{
+ struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+ wpa_s->conf->sae_pwe = enable ? SAE_PWE_H2E_ONLY_MODE : SAE_PWE_HP_H2E_BOTH;
+ resetInternalStateAfterParamsUpdate();
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+}
+
+V1_4::SupplicantStatus StaNetwork::enableSaePkOnlyModeInternal(bool enable)
+{
+#ifdef CONFIG_SAE_PK
+ struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+ wpa_ssid->sae_pk = enable ? SAE_PK_MODE_ONLY : SAE_PK_MODE_AUTOMATIC;
+ resetInternalStateAfterParamsUpdate();
+ return {V1_4::SupplicantStatusCode::SUCCESS, ""};
+#else
+ return {V1_4::SupplicantStatusCode::FAILURE_UNSUPPORTED, ""};
+#endif
+}
+
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/sta_network.h b/wpa_supplicant/hidl/1.4/sta_network.h
similarity index 91%
rename from wpa_supplicant/hidl/1.3/sta_network.h
rename to wpa_supplicant/hidl/1.4/sta_network.h
index 0057596..48bf77b 100644
--- a/wpa_supplicant/hidl/1.3/sta_network.h
+++ b/wpa_supplicant/hidl/1.4/sta_network.h
@@ -15,7 +15,7 @@
#include <android-base/macros.h>
-#include <android/hardware/wifi/supplicant/1.3/ISupplicantStaNetwork.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicantStaNetwork.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
extern "C"
@@ -34,7 +34,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicantStaNetworkCallback;
using V1_2::DppFailureCode;
@@ -45,7 +45,7 @@
* object is used for control operations on a specific network
* controlled by wpa_supplicant.
*/
-class StaNetwork : public V1_3::ISupplicantStaNetwork
+class StaNetwork : public V1_4::ISupplicantStaNetwork
{
public:
StaNetwork(
@@ -62,6 +62,9 @@
Return<void> registerCallback(
const sp<ISupplicantStaNetworkCallback>& callback,
registerCallback_cb _hidl_cb) override;
+ Return<void> registerCallback_1_4(
+ const sp<V1_4::ISupplicantStaNetworkCallback>& callback,
+ registerCallback_1_4_cb _hidl_cb) override;
Return<void> setSsid(
const hidl_vec<uint8_t>& ssid, setSsid_cb _hidl_cb) override;
Return<void> setBssid(
@@ -133,6 +136,7 @@
const hidl_string& id_str, setIdStr_cb _hidl_cb) override;
Return<void> setUpdateIdentifier(
uint32_t id, setUpdateIdentifier_cb _hidl_cb) override;
+ Return<void> setEdmg(bool enable, setEdmg_cb _hidl_cb) override;
Return<void> getSsid(getSsid_cb _hidl_cb) override;
Return<void> getBssid(getBssid_cb _hidl_cb) override;
Return<void> getScanSsid(getScanSsid_cb _hidl_cb) override;
@@ -172,6 +176,7 @@
Return<void> getIdStr(getIdStr_cb _hidl_cb) override;
Return<void> getWpsNfcConfigurationToken(
getWpsNfcConfigurationToken_cb _hidl_cb) override;
+ Return<void> getEdmg(getEdmg_cb _hidl_cb) override;
Return<void> enable(bool no_connect, enable_cb _hidl_cb) override;
Return<void> disable(disable_cb _hidl_cb) override;
Return<void> select(select_cb _hidl_cb) override;
@@ -227,7 +232,7 @@
const hidl_string& sae_password_id,
setSaePasswordId_cb _hidl_cb) override;
Return<void> setOcsp(
- OcspType ocspType, setOcsp_cb _hidl_cb) override;
+ V1_3::OcspType ocspType, setOcsp_cb _hidl_cb) override;
Return<void> getOcsp(
getOcsp_cb _hidl_cb) override;
Return<void> setPmkCache(const hidl_vec<uint8_t>& serializedEntry,
@@ -256,6 +261,18 @@
std::function<void(const SupplicantStatus &status)> _hidl_cb)
override;
Return<void> setEapErp(bool enable, setEapErp_cb _hidl_cb) override;
+ Return<void> setPairwiseCipher_1_4(
+ uint32_t pairwise_cipher_mask,
+ setPairwiseCipher_1_4_cb _hidl_cb) override;
+ Return<void> getPairwiseCipher_1_4(
+ getPairwiseCipher_1_4_cb _hidl_cb) override;
+ Return<void> setGroupCipher_1_4(
+ uint32_t group_cipher_mask,
+ setGroupCipher_1_4_cb _hidl_cb) override;
+ Return<void> getGroupCipher_1_4(
+ getGroupCipher_1_4_cb _hidl_cb) override;
+ Return<void> enableSaeH2eOnlyMode(bool enable, enableSaeH2eOnlyMode_cb _hidl_cb) override;
+ Return<void> enableSaePkOnlyMode(bool enable, enableSaePkOnlyMode_cb _hidl_cb) override;
private:
// Corresponding worker functions for the HIDL methods.
@@ -264,6 +281,8 @@
std::pair<SupplicantStatus, IfaceType> getTypeInternal();
SupplicantStatus registerCallbackInternal(
const sp<ISupplicantStaNetworkCallback>& callback);
+ V1_4::SupplicantStatus registerCallback_1_4Internal(
+ const sp<V1_4::ISupplicantStaNetworkCallback>& callback);
SupplicantStatus setSsidInternal(const std::vector<uint8_t>& ssid);
SupplicantStatus setBssidInternal(const std::array<uint8_t, 6>& bssid);
SupplicantStatus setScanSsidInternal(bool enable);
@@ -305,6 +324,7 @@
SupplicantStatus setProactiveKeyCachingInternal(bool enable);
SupplicantStatus setIdStrInternal(const std::string& id_str);
SupplicantStatus setUpdateIdentifierInternal(uint32_t id);
+ V1_4::SupplicantStatus setEdmgInternal(bool enable);
std::pair<SupplicantStatus, std::vector<uint8_t>> getSsidInternal();
std::pair<SupplicantStatus, std::array<uint8_t, 6>> getBssidInternal();
std::pair<SupplicantStatus, bool> getScanSsidInternal();
@@ -345,6 +365,7 @@
std::pair<SupplicantStatus, std::string> getIdStrInternal();
std::pair<SupplicantStatus, std::vector<uint8_t>>
getWpsNfcConfigurationTokenInternal();
+ std::pair<V1_4::SupplicantStatus, bool> getEdmgInternal();
SupplicantStatus enableInternal(bool no_connect);
SupplicantStatus disableInternal();
SupplicantStatus selectInternal();
@@ -372,8 +393,8 @@
const std::string& sae_password_id);
SupplicantStatus setGroupMgmtCipherInternal(uint32_t group_mgmt_cipher_mask);
std::pair<SupplicantStatus, uint32_t> getGroupMgmtCipherInternal();
- SupplicantStatus setOcspInternal(OcspType ocspType);
- std::pair<SupplicantStatus, OcspType> getOcspInternal();
+ SupplicantStatus setOcspInternal(V1_3::OcspType ocspType);
+ std::pair<SupplicantStatus, V1_3::OcspType> getOcspInternal();
SupplicantStatus setPmkCacheInternal(const std::vector<uint8_t>& serialziedEntry);
SupplicantStatus setWapiCertSuiteInternal(const std::string& suite);
std::pair<SupplicantStatus, std::string> getWapiCertSuiteInternal();
@@ -388,6 +409,13 @@
uint32_t pairwise_cipher_mask);
SupplicantStatus setWapiPskInternal(const std::vector<uint8_t>& psk);
std::pair<SupplicantStatus, std::vector<uint8_t>> getWapiPskInternal();
+ std::pair<V1_4::SupplicantStatus, uint32_t> getGroupCipher_1_4Internal();
+ V1_4::SupplicantStatus setGroupCipher_1_4Internal(uint32_t group_cipher_mask);
+ std::pair<V1_4::SupplicantStatus, uint32_t> getPairwiseCipher_1_4Internal();
+ V1_4::SupplicantStatus setPairwiseCipher_1_4Internal(
+ uint32_t pairwise_cipher_mask);
+ V1_4::SupplicantStatus enableSaeH2eOnlyModeInternal(bool enable);
+ V1_4::SupplicantStatus enableSaePkOnlyModeInternal(bool enable);
struct wpa_ssid* retrieveNetworkPtr();
struct wpa_supplicant* retrieveIfacePtr();
@@ -427,7 +455,7 @@
};
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/supplicant.cpp b/wpa_supplicant/hidl/1.4/supplicant.cpp
similarity index 88%
rename from wpa_supplicant/hidl/1.3/supplicant.cpp
rename to wpa_supplicant/hidl/1.4/supplicant.cpp
index 50d2343..6f4cde6 100644
--- a/wpa_supplicant/hidl/1.3/supplicant.cpp
+++ b/wpa_supplicant/hidl/1.4/supplicant.cpp
@@ -10,6 +10,7 @@
#include "hidl_manager.h"
#include "hidl_return_util.h"
#include "supplicant.h"
+#include "p2p_iface.h"
#include <android-base/file.h>
#include <fcntl.h>
@@ -156,7 +157,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using hidl_return_util::validateAndCall;
using V1_0::SupplicantStatusCode;
@@ -254,6 +255,39 @@
}
std::pair<SupplicantStatus, sp<ISupplicantIface>>
+Supplicant::addP2pDevInterface(struct wpa_interface iface_params)
+{
+ char primary_ifname[IFNAMSIZ];
+ u32 primary_ifname_len =
+ strlen(iface_params.ifname) - strlen(P2P_MGMT_DEVICE_PREFIX);
+
+ if(primary_ifname_len > IFNAMSIZ) {
+ wpa_printf(MSG_DEBUG, "%s, Invalid primary iface name ", __FUNCTION__);
+ return {{SupplicantStatusCode::FAILURE_ARGS_INVALID, ""}, {}};
+ }
+
+ strncpy(primary_ifname, iface_params.ifname +
+ strlen(P2P_MGMT_DEVICE_PREFIX), primary_ifname_len);
+ wpa_printf(MSG_DEBUG, "%s, Initialize p2p-dev-wlan0 iface with"
+ "primary_iface = %s", __FUNCTION__, primary_ifname);
+ struct wpa_supplicant* wpa_s =
+ wpa_supplicant_get_iface(wpa_global_, primary_ifname);
+ if (!wpa_s) {
+ wpa_printf(MSG_DEBUG, "%s,NULL wpa_s for wlan0", __FUNCTION__);
+ return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""},
+ nullptr};
+ }
+ if (wpas_p2p_add_p2pdev_interface(
+ wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to enable P2P Device");
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN,
+ "Enable P2P Device failed"}, {}};
+ }
+ return {{SupplicantStatusCode::SUCCESS,""}, {}};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantIface>>
Supplicant::addInterfaceInternal(const IfaceInfo& iface_info)
{
android::sp<ISupplicantIface> iface;
@@ -305,10 +339,19 @@
}
}
iface_params.ifname = iface_info.name.c_str();
- struct wpa_supplicant* wpa_s =
- wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
- if (!wpa_s) {
- return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+ if (strncmp(iface_params.ifname, P2P_MGMT_DEVICE_PREFIX,
+ strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
+ std::tie(status, iface) = addP2pDevInterface(iface_params);
+ if (status.code != SupplicantStatusCode::SUCCESS) {
+ return {{status.code,
+ status.debugMessage.c_str()}, iface};
+ }
+ } else {
+ struct wpa_supplicant* wpa_s =
+ wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
+ if (!wpa_s) {
+ return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+ }
}
// The supplicant core creates a corresponding hidl object via
// HidlManager when |wpa_supplicant_add_iface| is called.
@@ -417,7 +460,7 @@
return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""};
}
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hidl/1.3/supplicant.h b/wpa_supplicant/hidl/1.4/supplicant.h
similarity index 92%
rename from wpa_supplicant/hidl/1.3/supplicant.h
rename to wpa_supplicant/hidl/1.4/supplicant.h
index 0c0ac72..2944c50 100644
--- a/wpa_supplicant/hidl/1.3/supplicant.h
+++ b/wpa_supplicant/hidl/1.4/supplicant.h
@@ -13,7 +13,7 @@
#include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
#include <android/hardware/wifi/supplicant/1.0/ISupplicantIface.h>
#include <android/hardware/wifi/supplicant/1.0/types.h>
-#include <android/hardware/wifi/supplicant/1.3/ISupplicant.h>
+#include <android/hardware/wifi/supplicant/1.4/ISupplicant.h>
#include <android-base/macros.h>
#include <hidl/Status.h>
@@ -29,7 +29,7 @@
namespace hardware {
namespace wifi {
namespace supplicant {
-namespace V1_3 {
+namespace V1_4 {
namespace implementation {
using V1_0::ISupplicantCallback;
using V1_0::ISupplicantIface;
@@ -39,7 +39,7 @@
* object is used core for global control operations on
* wpa_supplicant.
*/
-class Supplicant : public V1_3::ISupplicant
+class Supplicant : public V1_4::ISupplicant
{
public:
Supplicant(struct wpa_global* global);
@@ -81,6 +81,8 @@
SupplicantStatus setDebugParamsInternal(
ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys);
SupplicantStatus setConcurrencyPriorityInternal(IfaceType type);
+ std::pair<SupplicantStatus, sp<ISupplicantIface>> addP2pDevInterface(
+ struct wpa_interface iface_params);
// Raw pointer to the global structure maintained by the core.
struct wpa_global* wpa_global_;
@@ -93,7 +95,7 @@
};
} // namespace implementation
-} // namespace V1_3
+} // namespace V1_4
} // namespace supplicant
} // namespace wifi
} // namespace hardware
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index ce5608e..907803b 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -1335,6 +1335,7 @@
}
wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url);
+ wpas_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index c16c2a9..3946252 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -946,7 +946,8 @@
struct wpa_driver_capa capa;
res = wpa_drv_get_capa(wpa_s, &capa);
- if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+ if (res == 0 && capa.key_mgmt_iftype[WPA_IF_STATION] &
+ WPA_DRIVER_CAPA_KEY_MGMT_FT) {
key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
"WPA-EAP WPA-EAP-SHA256 FT-EAP" :
"WPA-EAP FT-EAP";
@@ -958,7 +959,9 @@
"WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 ||
wpa_config_set(ssid, "proto", "RSN", 0) < 0 ||
- wpa_config_set(ssid, "ieee80211w", "1", 0) < 0 ||
+ wpa_config_set(ssid, "ieee80211w",
+ wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_REQUIRED ?
+ "2" : "1", 0) < 0 ||
wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
return -1;
return 0;
@@ -2747,27 +2750,27 @@
}
-int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, int freq,
u16 info_ids[], size_t num_ids, u32 subtypes,
u32 mbo_subtypes)
{
struct wpabuf *buf;
struct wpabuf *extra_buf = NULL;
int ret = 0;
- int freq;
struct wpa_bss *bss;
int res;
bss = wpa_bss_get_bssid(wpa_s, dst);
- if (!bss) {
+ if (!bss && !freq) {
wpa_printf(MSG_WARNING,
- "ANQP: Cannot send query to unknown BSS "
- MACSTR, MAC2STR(dst));
+ "ANQP: Cannot send query without BSS freq info");
return -1;
}
- wpa_bss_anqp_unshare_alloc(bss);
- freq = bss->freq;
+ if (bss)
+ wpa_bss_anqp_unshare_alloc(bss);
+ if (bss && !freq)
+ freq = bss->freq;
wpa_msg(wpa_s, MSG_DEBUG,
"ANQP: Query Request to " MACSTR " for %u id(s)",
@@ -2786,6 +2789,13 @@
if (mbo_subtypes) {
struct wpabuf *mbo;
+ if (!bss) {
+ wpa_printf(MSG_WARNING,
+ "ANQP: Cannot send MBO query to unknown BSS "
+ MACSTR, MAC2STR(dst));
+ return -1;
+ }
+
mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes);
if (mbo) {
if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
@@ -2988,13 +2998,13 @@
case ANQP_VENUE_URL:
wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Venue URL",
MAC2STR(sa));
- anqp_add_extra(wpa_s, anqp, info_id, pos, slen);
if (!pmf_in_use(wpa_s, sa)) {
wpa_printf(MSG_DEBUG,
"ANQP: Ignore Venue URL since PMF was not enabled");
break;
}
+ anqp_add_extra(wpa_s, anqp, info_id, pos, slen);
interworking_parse_venue_url(wpa_s, pos, slen);
break;
case ANQP_VENDOR_SPECIFIC:
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 37ee2e9..77b2c91 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -11,7 +11,7 @@
enum gas_query_result;
-int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, int freq,
u16 info_ids[], size_t num_ids, u32 subtypes,
u32 mbo_subtypes);
void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index c085466..558d87a 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -333,30 +333,6 @@
frequency);
goto out_free;
}
- if (ssid->ht40)
- conf->secondary_channel = ssid->ht40;
- if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) {
- if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH)
- conf->vht_oper_chwidth = ssid->max_oper_chwidth;
- switch (conf->vht_oper_chwidth) {
- case CHANWIDTH_80MHZ:
- case CHANWIDTH_80P80MHZ:
- ieee80211_freq_to_chan(
- frequency,
- &conf->vht_oper_centr_freq_seg0_idx);
- conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
- break;
- case CHANWIDTH_160MHZ:
- ieee80211_freq_to_chan(
- frequency,
- &conf->vht_oper_centr_freq_seg0_idx);
- conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
- conf->vht_oper_centr_freq_seg0_idx += 40 / 5;
- break;
- }
- ieee80211_freq_to_chan(ssid->vht_center_freq2,
- &conf->vht_oper_centr_freq_seg1_idx);
- }
if (ssid->mesh_basic_rates == NULL) {
/*
@@ -387,6 +363,31 @@
conf->basic_rates[rate_len] = -1;
}
+ /* While it can enhance performance to switch the primary channel, which
+ * is also the secondary channel of another network at the same time),
+ * to the other primary channel, problems exist with this in mesh
+ * networks.
+ *
+ * Example with problems:
+ * - 3 mesh nodes M1-M3, freq (5200, 5180)
+ * - other node O1, e.g. AP mode, freq (5180, 5200),
+ * Locations: O1 M1 M2 M3
+ *
+ * M3 can only send frames to M1 over M2, no direct connection is
+ * possible
+ * Start O1, M1 and M3 first, M1 or O1 will switch channels to align
+ * with* each other. M3 does not swap, because M1 or O1 cannot be
+ * reached. M2 is started afterwards and can either connect to M3 or M1
+ * because of this primary secondary channel switch.
+ *
+ * Solutions: (1) central coordination -> not always possible
+ * (2) disable pri/sec channel switch in mesh networks
+ *
+ * In AP mode, when all nodes can work independently, this poses of
+ * course no problem, therefore disable it only in mesh mode. */
+ conf->no_pri_sec_switch = 1;
+ wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
if (wpa_drv_init_mesh(wpa_s)) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
return -1;
@@ -398,8 +399,6 @@
return -1;
}
- wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
-
return 0;
out_free:
wpa_supplicant_mesh_deinit(wpa_s);
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 12aafcb..b6a5e88 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -533,11 +533,14 @@
int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
if (sta) {
+ if (sta->plink_state == PLINK_ESTAB)
+ hapd->num_plinks--;
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
MAC2STR(sta->addr));
eloop_cancel_timeout(plink_timer, wpa_s, sta);
+ eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
return 0;
}
@@ -1290,8 +1293,8 @@
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) !=
- 0) {
- wpa_printf(MSG_WARNING, "MPM: %s",
+ OCI_SUCCESS) {
+ wpa_printf(MSG_WARNING, "MPM: OCV failed: %s",
ocv_errorstr);
return;
}
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index f19bfbf..834c7a1 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -195,7 +195,8 @@
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
rsn->igtk, rsn->igtk_len);
wpa_drv_set_key(rsn->wpa_s,
- wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
+ wpa_cipher_to_alg(rsn->mgmt_group_cipher),
+ broadcast_ether_addr,
rsn->igtk_key_id, 1,
seq, sizeof(seq), rsn->igtk, rsn->igtk_len,
KEY_FLAG_GROUP_TX_DEFAULT);
@@ -204,7 +205,8 @@
/* group privacy / data frames */
wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
rsn->mgtk, rsn->mgtk_len);
- wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
+ wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher),
+ broadcast_ether_addr,
rsn->mgtk_key_id, 1, seq, sizeof(seq),
rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 56eb62a..ac40074 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -148,14 +148,15 @@
}
-void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s)
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE);
- wpas_hidl_notify_assoc_reject(wpa_s);
+ wpas_hidl_notify_assoc_reject(wpa_s, bssid, timed_out);
}
void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s) {
@@ -1006,14 +1007,23 @@
const char *url)
{
#ifdef CONFIG_HS20
- if (!wpa_s || !url)
+ if (!wpa_s)
return;
wpas_hidl_notify_hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay,
- url);
+ url);
#endif /* CONFIG_HS20 */
}
+void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
+ struct wpa_supplicant *wpa_s, const char *url) {
+#ifdef CONFIG_HS20
+ if (!wpa_s || !url)
+ return;
+
+ wpas_hidl_notify_hs20_rx_terms_and_conditions_acceptance(wpa_s, url);
+#endif /* CONFIG_HS20 */
+}
#ifdef CONFIG_MESH
@@ -1207,3 +1217,16 @@
wpas_hidl_notify_pmk_cache_added(wpa_s, entry);
}
+
+void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ u8 bitmap)
+{
+ if (!wpa_s)
+ return;
+
+ if (!ssid)
+ return;
+
+ wpas_hidl_notify_transition_disable(wpa_s, ssid, bitmap);
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index e9e39ee..d75bc77 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -28,7 +28,8 @@
enum wpa_states old_state);
void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s);
-void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s);
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, u8 timed_out);
void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s);
void wpas_notify_roam_time(struct wpa_supplicant *wpa_s);
void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s);
@@ -174,6 +175,8 @@
void wpas_notify_hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s,
u8 code, u16 reauth_delay,
const char *url);
+void wpas_notify_hs20_rx_terms_and_conditions_acceptance(
+ struct wpa_supplicant *wpa_s, const char *url);
void wpas_notify_dpp_config_received(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_dpp_config_sent(struct wpa_supplicant *wpa_s);
@@ -193,5 +196,8 @@
void wpas_notify_dpp_config_rejected(struct wpa_supplicant *wpa_s);
void wpas_notify_pmk_cache_added(struct wpa_supplicant *wpa_s,
struct rsn_pmksa_cache_entry *entry);
+void wpas_notify_transition_disable(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ u8 bitmap);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
index 983801f..91e15e7 100644
--- a/wpa_supplicant/op_classes.c
+++ b/wpa_supplicant/op_classes.c
@@ -22,13 +22,13 @@
unsigned int *flags)
{
int i;
- int is_6ghz = op_class >= 131 && op_class <= 135;
+ int is_6ghz = op_class >= 131 && op_class <= 136;
for (i = 0; i < mode->num_channels; i++) {
int chan_is_6ghz;
- chan_is_6ghz = mode->channels[i].freq > 5940 &&
- mode->channels[i].freq <= 7105;
+ chan_is_6ghz = mode->channels[i].freq >= 5935 &&
+ mode->channels[i].freq <= 7115;
if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
break;
}
@@ -412,9 +412,13 @@
}
*ie_len = wpabuf_len(buf) - 2;
- if (*ie_len < 2 || wpabuf_len(buf) > len) {
+ if (*ie_len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "No supported operating classes IE to add");
+ res = 0;
+ } else if (wpabuf_len(buf) > len) {
wpa_printf(MSG_ERROR,
- "Failed to add supported operating classes IE");
+ "Supported operating classes IE exceeds maximum buffer length");
res = 0;
} else {
os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b0bea61..dc5c16d 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -291,6 +291,41 @@
}
+static int wpas_p2p_add_scan_freq_list(struct wpa_supplicant *wpa_s,
+ enum hostapd_hw_mode band,
+ struct wpa_driver_scan_params *params)
+{
+ struct hostapd_hw_modes *mode;
+ int num_chans = 0;
+ int *freqs, i;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, 0);
+ if (!mode)
+ return -1;
+
+ if (params->freqs) {
+ while (params->freqs[num_chans])
+ num_chans++;
+ }
+
+ freqs = os_realloc(params->freqs,
+ (num_chans + mode->num_channels + 1) * sizeof(int));
+ if (!freqs)
+ return -1;
+
+ params->freqs = freqs;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ params->freqs[num_chans++] = mode->channels[i].freq;
+ }
+ params->freqs[num_chans] = 0;
+
+ return 0;
+}
+
+
static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
@@ -312,6 +347,15 @@
"Request driver to clear scan cache due to local BSS flush");
params->only_new_results = 1;
}
+
+ if (wpa_s->conf->p2p_6ghz_disable && !params->freqs) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: 6 GHz disabled - update the scan frequency list");
+ wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+ params);
+ wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ params);
+ }
ret = wpa_drv_scan(wpa_s, params);
if (ret == 0)
wpa_s->curr_scan_cookie = params->scan_cookie;
@@ -522,7 +566,7 @@
/*
* The calling wpa_s instance is going to be removed. Do that
* from an eloop callback to keep the instance available until
- * the caller has returned. This my be needed, e.g., to provide
+ * the caller has returned. This may be needed, e.g., to provide
* control interface responses on the per-interface socket.
*/
if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
@@ -3736,7 +3780,9 @@
u8 ch;
struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
- if (o->p2p == NO_P2P_SUPP)
+ if (o->p2p == NO_P2P_SUPP ||
+ (is_6ghz_op_class(o->op_class) &&
+ wpa_s->conf->p2p_6ghz_disable))
continue;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
@@ -3747,6 +3793,13 @@
wpa_s->global->p2p_24ghz_social_channels = 1;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
enum chan_allowed res;
+
+ /* Check for non-continuous jump in channel index
+ * incrementation */
+ if ((o->op_class == 128 || o->op_class == 130) &&
+ ch < 149 && ch + o->inc > 149)
+ ch = 149;
+
res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
if (res == ALLOWED) {
if (reg == NULL) {
@@ -3807,7 +3860,9 @@
const struct oper_class_map *o = &global_op_class[op];
u8 ch;
- if (o->p2p == NO_P2P_SUPP)
+ if (o->p2p == NO_P2P_SUPP ||
+ (is_6ghz_op_class(o->op_class) &&
+ wpa_s->conf->p2p_6ghz_disable))
continue;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
@@ -3939,6 +3994,10 @@
wpa_s->ifname);
if (os_snprintf_error(sizeof(ifname), ret))
return -1;
+ /* Cut length at the maximum size. Note that we don't need to ensure
+ * collision free names here as the created interface is not a netdev.
+ */
+ ifname[IFNAMSIZ - 1] = '\0';
force_name[0] = '\0';
wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL,
@@ -4777,6 +4836,7 @@
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
wpas_p2p_remove_pending_group_interface(wpa_s);
eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
wpas_p2p_listen_work_done(wpa_s);
if (wpa_s->p2p_send_action_work) {
os_free(wpa_s->p2p_send_action_work->ctx);
@@ -4925,6 +4985,15 @@
MAC2STR(wpa_s->pending_join_dev_addr));
return;
}
+ if (wpa_s->p2p_fallback_to_go_neg) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Join operating "
+ "failed - fall back to GO Negotiation");
+ wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
+ P2P_EVENT_FALLBACK_TO_GO_NEG
+ "reason=join-failed");
+ wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+ return;
+ }
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
wpas_notify_p2p_group_formation_failure(wpa_s, "");
@@ -5275,6 +5344,13 @@
if (freq > 0) {
freqs[0] = freq;
params.freqs = freqs;
+ } else if (wpa_s->conf->p2p_6ghz_disable) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: 6 GHz disabled - update the scan frequency list");
+ wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
+ ¶ms);
+ wpas_p2p_add_scan_freq_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
+ ¶ms);
}
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
@@ -5305,6 +5381,8 @@
* the new scan results become available.
*/
ret = wpa_drv_scan(wpa_s, ¶ms);
+ if (wpa_s->conf->p2p_6ghz_disable && params.freqs != freqs)
+ os_free(params.freqs);
if (!ret) {
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
@@ -5645,6 +5723,9 @@
return -1;
}
+ if (is_6ghz_freq(freq) && wpa_s->conf->p2p_6ghz_disable)
+ return -2;
+
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
@@ -5871,6 +5952,8 @@
if (os_strcmp(ifname, "*") == 0) {
struct wpa_supplicant *prev;
+ bool calling_wpa_s_group_removed = false;
+
wpa_s = global->ifaces;
while (wpa_s) {
prev = wpa_s;
@@ -5878,9 +5961,23 @@
if (prev->p2p_group_interface !=
NOT_P2P_GROUP_INTERFACE ||
(prev->current_ssid &&
- prev->current_ssid->p2p_group))
+ prev->current_ssid->p2p_group)) {
wpas_p2p_disconnect_safely(prev, calling_wpa_s);
+ if (prev == calling_wpa_s)
+ calling_wpa_s_group_removed = true;
+ }
}
+
+ if (!calling_wpa_s_group_removed &&
+ (calling_wpa_s->p2p_group_interface !=
+ NOT_P2P_GROUP_INTERFACE ||
+ (calling_wpa_s->current_ssid &&
+ calling_wpa_s->current_ssid->p2p_group))) {
+ wpa_printf(MSG_DEBUG, "Remove calling_wpa_s P2P group");
+ wpas_p2p_disconnect_safely(calling_wpa_s,
+ calling_wpa_s);
+ }
+
return 0;
}
@@ -5961,8 +6058,31 @@
} else {
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
return -1;
- freq = 5180 + (r % 4) * 20;
- if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+
+ /*
+ * most of 5G channels are DFS, only operating class 115 and 124
+ * are available possibly, randomly pick a start to check them.
+ */
+ int possible_5g_freqs[] = {
+ /* operating class 115 */
+ 5180, 5200, 5220, 5240,
+ /* operating class 124 */
+ 5745, 5765, 5785, 5805,
+ };
+ int possible_5g_freqs_num =
+ sizeof(possible_5g_freqs)/sizeof(possible_5g_freqs[0]);
+
+ int i;
+ for (i = 0; i < possible_5g_freqs_num; i++, r++) {
+ if (p2p_supported_freq_go(
+ wpa_s->global->p2p,
+ possible_5g_freqs[r % possible_5g_freqs_num])) {
+ freq = possible_5g_freqs[r % possible_5g_freqs_num];
+ break;
+ }
+ }
+
+ if (i >= possible_5g_freqs_num) {
wpa_printf(MSG_DEBUG, "P2P: Could not select "
"5 GHz channel for P2P group");
return -1;
@@ -9374,6 +9494,8 @@
{
struct p2p_go_neg_results params;
struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+ void (*ap_configured_cb)(void *ctx, void *data);
+ void *ap_configured_cb_ctx, *ap_configured_cb_data;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
@@ -9383,6 +9505,13 @@
/* Stop the AP functionality */
/* TODO: Should do this in a way that does not indicated to possible
* P2P Clients in the group that the group is terminated. */
+ /* If this action occurs before a group is started, the callback should
+ * be preserved, or GROUP-STARTED event would be lost. If this action
+ * occurs after a group is started, these pointers are all NULL and
+ * harmless. */
+ ap_configured_cb = wpa_s->ap_configured_cb;
+ ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx;
+ ap_configured_cb_data = wpa_s->ap_configured_cb_data;
wpa_supplicant_ap_deinit(wpa_s);
/* Reselect the GO frequency */
@@ -9406,6 +9535,11 @@
return;
}
+ /* Restore preserved callback parameters */
+ wpa_s->ap_configured_cb = ap_configured_cb;
+ wpa_s->ap_configured_cb_ctx = ap_configured_cb_ctx;
+ wpa_s->ap_configured_cb_data = ap_configured_cb_data;
+
/* Update the frequency */
current_ssid->frequency = params.freq;
wpa_s->connect_without_scan = current_ssid;
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index 4a8f4ff..de49948 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -154,7 +154,8 @@
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len,
- u32 pmk_lifetime, u8 pmk_reauth_threshold)
+ u32 pmk_lifetime, u8 pmk_reauth_threshold,
+ int akmp)
{
printf("%s - not implemented\n", __func__);
return -1;
diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c
new file mode 100644
index 0000000..1280f5d
--- /dev/null
+++ b/wpa_supplicant/robust_av.c
@@ -0,0 +1,160 @@
+/*
+ * wpa_supplicant - Robust AV procedures
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+
+void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
+ struct wpabuf *buf)
+{
+ u8 *len, *len1;
+
+ /* MSCS descriptor element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+ wpabuf_put_u8(buf, robust_av->request_type);
+ wpabuf_put_u8(buf, robust_av->up_bitmap);
+ wpabuf_put_u8(buf, robust_av->up_limit);
+ wpabuf_put_le32(buf, robust_av->stream_timeout);
+
+ if (robust_av->request_type != SCS_REQ_REMOVE) {
+ /* TCLAS mask element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len1 = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
+
+ /* Frame classifier */
+ wpabuf_put_data(buf, robust_av->frame_classifier,
+ robust_av->frame_classifier_len);
+ *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+ }
+
+ *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf;
+ const u8 *ext_capab = NULL;
+ size_t buf_len;
+ int ret;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+ return 0;
+
+ if (wpa_s->current_bss)
+ ext_capab = wpa_bss_get_ie(wpa_s->current_bss,
+ WLAN_EID_EXT_CAPAB);
+
+ if (!ext_capab || ext_capab[1] < 11 || !(ext_capab[12] & 0x20)) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "AP does not support MSCS - could not send MSCS Req");
+ return -1;
+ }
+
+ if (!wpa_s->mscs_setup_done &&
+ wpa_s->robust_av.request_type != SCS_REQ_ADD) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "MSCS: Failed to send MSCS Request: request type invalid");
+ return -1;
+ }
+
+ buf_len = 3 + /* Action frame header */
+ 3 + /* MSCS descriptor IE header */
+ 1 + /* Request type */
+ 2 + /* User priority control */
+ 4 + /* Stream timeout */
+ 3 + /* TCLAS Mask IE header */
+ wpa_s->robust_av.frame_classifier_len;
+
+ buf = wpabuf_alloc(buf_len);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+ wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
+ wpa_s->robust_av.dialog_token++;
+ wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
+
+ /* MSCS descriptor element */
+ wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret < 0)
+ wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *buf, size_t len)
+{
+ u8 dialog_token;
+ u16 status_code;
+
+ if (len < 3)
+ return;
+
+ dialog_token = *buf++;
+ if (dialog_token != wpa_s->robust_av.dialog_token) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
+ dialog_token, wpa_s->robust_av.dialog_token);
+ return;
+ }
+
+ status_code = *buf;
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u", MAC2STR(src), status_code);
+ wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
+}
+
+
+void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *ies, size_t ies_len)
+{
+ const u8 *mscs_desc_ie, *mscs_status;
+ u16 status;
+
+ /* Process optional MSCS Status subelement when MSCS IE is in
+ * (Re)Association Response frame */
+ if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
+ return;
+
+ mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+ if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
+ return;
+
+ /* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
+ * request type(1) + upc(2) + stream timeout(4) =) 10.
+ */
+ mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
+ MCSC_SUBELEM_STATUS);
+ if (!mscs_status || mscs_status[1] < 2)
+ return;
+
+ status = WPA_GET_LE16(mscs_status + 2);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u", MAC2STR(bssid), status);
+ wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
+}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index b475730..b9e4162 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -237,6 +237,10 @@
if (wpa_s->disconnected)
retry = 0;
+ /* do not retry if operation is not supported */
+ if (ret == -EOPNOTSUPP)
+ retry = 0;
+
wpa_supplicant_notify_scanning(wpa_s, 0);
wpas_notify_scan_done(wpa_s, 0);
if (wpa_s->wpa_state == WPA_SCANNING)
@@ -2187,6 +2191,60 @@
}
+/* Minimum SNR required to achieve a certain bitrate. */
+struct minsnr_bitrate_entry {
+ int minsnr;
+ unsigned int bitrate; /* in Mbps */
+};
+
+/* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */
+static const int vht_mcs = 8;
+
+static const struct minsnr_bitrate_entry vht20_table[] = {
+ { 0, 0 },
+ { 2, 6500 }, /* HT20 MCS0 */
+ { 5, 13000 }, /* HT20 MCS1 */
+ { 9, 19500 }, /* HT20 MCS2 */
+ { 11, 26000 }, /* HT20 MCS3 */
+ { 15, 39000 }, /* HT20 MCS4 */
+ { 18, 52000 }, /* HT20 MCS5 */
+ { 20, 58500 }, /* HT20 MCS6 */
+ { 25, 65000 }, /* HT20 MCS7 */
+ { 29, 78000 }, /* VHT20 MCS8 */
+ { -1, 78000 } /* SNR > 29 */
+};
+
+static const struct minsnr_bitrate_entry vht40_table[] = {
+ { 0, 0 },
+ { 5, 13500 }, /* HT40 MCS0 */
+ { 8, 27000 }, /* HT40 MCS1 */
+ { 12, 40500 }, /* HT40 MCS2 */
+ { 14, 54000 }, /* HT40 MCS3 */
+ { 18, 81000 }, /* HT40 MCS4 */
+ { 21, 108000 }, /* HT40 MCS5 */
+ { 23, 121500 }, /* HT40 MCS6 */
+ { 28, 135000 }, /* HT40 MCS7 */
+ { 32, 162000 }, /* VHT40 MCS8 */
+ { 34, 180000 }, /* VHT40 MCS9 */
+ { -1, 180000 } /* SNR > 34 */
+};
+
+static const struct minsnr_bitrate_entry vht80_table[] = {
+ { 0, 0 },
+ { 8, 29300 }, /* VHT80 MCS0 */
+ { 11, 58500 }, /* VHT80 MCS1 */
+ { 15, 87800 }, /* VHT80 MCS2 */
+ { 17, 117000 }, /* VHT80 MCS3 */
+ { 21, 175500 }, /* VHT80 MCS4 */
+ { 24, 234000 }, /* VHT80 MCS5 */
+ { 26, 263300 }, /* VHT80 MCS6 */
+ { 31, 292500 }, /* VHT80 MCS7 */
+ { 35, 351000 }, /* VHT80 MCS8 */
+ { 37, 390000 }, /* VHT80 MCS9 */
+ { -1, 390000 } /* SNR > 37 */
+};
+
+
static unsigned int interpolate_rate(int snr, int snr0, int snr1,
int rate0, int rate1)
{
@@ -2194,68 +2252,42 @@
}
-#define INTERPOLATE_RATE(snr0, snr1, rate0, rate1) \
- if (snr < (snr1)) \
- return interpolate_rate(snr, (snr0), (snr1), (rate0), (rate1))
-
-static unsigned int max_ht20_rate(int snr, int vht)
+static unsigned int max_rate(const struct minsnr_bitrate_entry table[],
+ int snr, bool vht)
{
- if (snr < 0)
- return 0;
- INTERPOLATE_RATE(0, 2, 0, 6500); /* HT20 MCS0 */
- INTERPOLATE_RATE(2, 5, 6500, 13000); /* HT20 MCS1 */
- INTERPOLATE_RATE(5, 9, 13000, 19500); /* HT20 MCS2 */
- INTERPOLATE_RATE(9, 11, 19500, 26000); /* HT20 MCS3 */
- INTERPOLATE_RATE(11, 15, 26000, 39000); /* HT20 MCS4 */
- INTERPOLATE_RATE(15, 18, 39000, 52000); /* HT20 MCS5 */
- INTERPOLATE_RATE(18, 20, 52000, 58500); /* HT20 MCS6 */
- INTERPOLATE_RATE(20, 25, 58500, 65000); /* HT20 MCS7 */
- if (!vht)
- return 65000;
- INTERPOLATE_RATE(25, 29, 65000, 78000); /* VHT20 MCS8 */
- return 78000;
+ const struct minsnr_bitrate_entry *prev, *entry = table;
+
+ while ((entry->minsnr != -1) &&
+ (snr >= entry->minsnr) &&
+ (vht || entry - table <= vht_mcs))
+ entry++;
+ if (entry == table)
+ return entry->bitrate;
+ prev = entry - 1;
+ if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs))
+ return prev->bitrate;
+ return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate,
+ entry->bitrate);
}
-static unsigned int max_ht40_rate(int snr, int vht)
+static unsigned int max_ht20_rate(int snr, bool vht)
{
- if (snr < 0)
- return 0;
- INTERPOLATE_RATE(0, 5, 0, 13500); /* HT40 MCS0 */
- INTERPOLATE_RATE(5, 8, 13500, 27000); /* HT40 MCS1 */
- INTERPOLATE_RATE(8, 12, 27000, 40500); /* HT40 MCS2 */
- INTERPOLATE_RATE(12, 14, 40500, 54000); /* HT40 MCS3 */
- INTERPOLATE_RATE(14, 18, 54000, 81000); /* HT40 MCS4 */
- INTERPOLATE_RATE(18, 21, 81000, 108000); /* HT40 MCS5 */
- INTERPOLATE_RATE(21, 23, 108000, 121500); /* HT40 MCS6 */
- INTERPOLATE_RATE(23, 28, 121500, 135000); /* HT40 MCS7 */
- if (!vht)
- return 135000;
- INTERPOLATE_RATE(28, 32, 135000, 162000); /* VHT40 MCS8 */
- INTERPOLATE_RATE(32, 34, 162000, 180000); /* VHT40 MCS9 */
- return 180000;
+ return max_rate(vht20_table, snr, vht);
+}
+
+
+static unsigned int max_ht40_rate(int snr, bool vht)
+{
+ return max_rate(vht40_table, snr, vht);
}
static unsigned int max_vht80_rate(int snr)
{
- if (snr < 0)
- return 0;
- INTERPOLATE_RATE(0, 8, 0, 29300); /* VHT80 MCS0 */
- INTERPOLATE_RATE(8, 11, 29300, 58500); /* VHT80 MCS1 */
- INTERPOLATE_RATE(11, 15, 58500, 87800); /* VHT80 MCS2 */
- INTERPOLATE_RATE(15, 17, 87800, 117000); /* VHT80 MCS3 */
- INTERPOLATE_RATE(17, 21, 117000, 175500); /* VHT80 MCS4 */
- INTERPOLATE_RATE(21, 24, 175500, 234000); /* VHT80 MCS5 */
- INTERPOLATE_RATE(24, 26, 234000, 263300); /* VHT80 MCS6 */
- INTERPOLATE_RATE(26, 31, 263300, 292500); /* VHT80 MCS7 */
- INTERPOLATE_RATE(31, 35, 292500, 351000); /* VHT80 MCS8 */
- INTERPOLATE_RATE(35, 37, 351000, 390000); /* VHT80 MCS9 */
- return 390000;
+ return max_rate(vht80_table, snr, 1);
}
-#undef INTERPOLATE_RATE
-
unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len, int rate,
@@ -2309,7 +2341,7 @@
if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
if (ie) {
- tmp = max_ht20_rate(snr, 0);
+ tmp = max_ht20_rate(snr, false);
if (tmp > est)
est = tmp;
}
@@ -2319,7 +2351,7 @@
ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
if (ie && ie[1] >= 2 &&
(ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
- tmp = max_ht40_rate(snr, 0);
+ tmp = max_ht40_rate(snr, false);
if (tmp > est)
est = tmp;
}
@@ -2329,7 +2361,7 @@
/* Use +1 to assume VHT is always faster than HT */
ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
if (ie) {
- tmp = max_ht20_rate(snr, 1) + 1;
+ tmp = max_ht20_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
@@ -2337,7 +2369,7 @@
if (ie && ie[1] >= 2 &&
(ie[3] &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
- tmp = max_ht40_rate(snr, 1) + 1;
+ tmp = max_ht40_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
}
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 4eaece0..7fec1cd 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -85,16 +85,21 @@
static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const u8 *bssid, int external,
- int reuse, int *ret_use_pt)
+ int reuse, int *ret_use_pt,
+ bool *ret_use_pk)
{
struct wpabuf *buf;
size_t len;
const char *password;
struct wpa_bss *bss;
int use_pt = 0;
+ bool use_pk = false;
+ u8 rsnxe_capa = 0;
if (ret_use_pt)
*ret_use_pt = 0;
+ if (ret_use_pk)
+ *ret_use_pk = false;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->sae_commit_override) {
@@ -123,7 +128,8 @@
os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"SAE: Reuse previously generated PWE on a retry with the same AP");
- use_pt = wpa_s->sme.sae.tmp->h2e;
+ use_pt = wpa_s->sme.sae.h2e;
+ use_pk = wpa_s->sme.sae.pk;
goto reuse_data;
}
if (sme_set_sae_group(wpa_s) < 0) {
@@ -131,19 +137,37 @@
return NULL;
}
+ bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+ if (bss) {
+ const u8 *rsnxe;
+
+ rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+ if (rsnxe && rsnxe[1] >= 1)
+ rsnxe_capa = rsnxe[2];
+ }
+
if (ssid->sae_password_id && wpa_s->conf->sae_pwe != 3)
use_pt = 1;
+#ifdef CONFIG_SAE_PK
+ if ((rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
+ ssid->sae_pk != SAE_PK_MODE_DISABLED &&
+ ((ssid->sae_password &&
+ sae_pk_valid_password(ssid->sae_password)) ||
+ (!ssid->sae_password && ssid->passphrase &&
+ sae_pk_valid_password(ssid->passphrase)))) {
+ use_pt = 1;
+ use_pk = true;
+ }
+
+ if (ssid->sae_pk == SAE_PK_MODE_ONLY && !use_pk) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Cannot use PK with the selected AP");
+ return NULL;
+ }
+#endif /* CONFIG_SAE_PK */
if (use_pt || wpa_s->conf->sae_pwe == 1 || wpa_s->conf->sae_pwe == 2) {
- bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
- if (bss) {
- const u8 *rsnxe;
-
- rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
- if (rsnxe && rsnxe[1] >= 1)
- use_pt = !!(rsnxe[2] &
- BIT(WLAN_RSNX_CAPAB_SAE_H2E));
- }
+ use_pt = !!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E));
if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
wpa_s->conf->sae_pwe != 3 &&
@@ -157,7 +181,7 @@
if (use_pt &&
sae_prepare_commit_pt(&wpa_s->sme.sae, ssid->pt,
wpa_s->own_addr, bssid,
- wpa_s->sme.sae_rejected_groups) < 0)
+ wpa_s->sme.sae_rejected_groups, NULL) < 0)
return NULL;
if (!use_pt &&
sae_prepare_commit(wpa_s->own_addr, bssid,
@@ -167,8 +191,17 @@
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
- if (wpa_s->sme.sae.tmp)
+ if (wpa_s->sme.sae.tmp) {
os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN);
+ if (use_pt && use_pk)
+ wpa_s->sme.sae.pk = 1;
+#ifdef CONFIG_SAE_PK
+ os_memcpy(wpa_s->sme.sae.tmp->own_addr, wpa_s->own_addr,
+ ETH_ALEN);
+ os_memcpy(wpa_s->sme.sae.tmp->peer_addr, bssid, ETH_ALEN);
+ sae_pk_set_password(&wpa_s->sme.sae, password);
+#endif /* CONFIG_SAE_PK */
+ }
reuse_data:
len = wpa_s->sme.sae_token ? 3 + wpabuf_len(wpa_s->sme.sae_token) : 0;
@@ -179,8 +212,12 @@
return NULL;
if (!external) {
wpabuf_put_le16(buf, 1); /* Transaction seq# */
- wpabuf_put_le16(buf, use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
- WLAN_STATUS_SUCCESS);
+ if (use_pk)
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_PK);
+ else if (use_pt)
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+ else
+ wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS);
}
if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
ssid->sae_password_id) < 0) {
@@ -189,6 +226,8 @@
}
if (ret_use_pt)
*ret_use_pt = use_pt;
+ if (ret_use_pk)
+ *ret_use_pk = use_pk;
return buf;
}
@@ -716,7 +755,8 @@
if (start)
resp = sme_auth_build_sae_commit(wpa_s, ssid,
bss->bssid, 0,
- start == 2, NULL);
+ start == 2, NULL,
+ NULL);
else
resp = sme_auth_build_sae_confirm(wpa_s, 0);
if (resp == NULL) {
@@ -1008,8 +1048,11 @@
{
struct wpabuf *resp, *buf;
int use_pt;
+ bool use_pk;
+ u16 status;
- resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt);
+ resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0, &use_pt,
+ &use_pk);
if (!resp) {
wpa_printf(MSG_DEBUG, "SAE: Failed to build SAE commit");
return -1;
@@ -1023,10 +1066,14 @@
}
wpa_s->sme.seq_num++;
+ if (use_pk)
+ status = WLAN_STATUS_SAE_PK;
+ else if (use_pt)
+ status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
+ else
+ status = WLAN_STATUS_SUCCESS;
sme_external_auth_build_buf(buf, resp, wpa_s->own_addr,
- bssid, 1, wpa_s->sme.seq_num,
- use_pt ? WLAN_STATUS_SAE_HASH_TO_ELEMENT :
- WLAN_STATUS_SUCCESS);
+ bssid, 1, wpa_s->sme.seq_num, status);
wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0, 0);
wpabuf_free(resp);
wpabuf_free(buf);
@@ -1223,8 +1270,7 @@
wpabuf_free(wpa_s->sme.sae_token);
token_pos = data + sizeof(le16);
token_len = len - sizeof(le16);
- if (wpa_s->sme.sae.tmp)
- h2e = wpa_s->sme.sae.tmp->h2e;
+ h2e = wpa_s->sme.sae.h2e;
if (h2e) {
if (token_len < 3) {
wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1287,7 +1333,8 @@
}
if (status_code != WLAN_STATUS_SUCCESS &&
- status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ status_code != WLAN_STATUS_SAE_HASH_TO_ELEMENT &&
+ status_code != WLAN_STATUS_SAE_PK)
return -1;
if (auth_transaction == 1) {
@@ -1304,24 +1351,30 @@
"SAE: Ignore commit message while waiting for confirm");
return 0;
}
- if (wpa_s->sme.sae.tmp && wpa_s->sme.sae.tmp->h2e &&
- status_code == WLAN_STATUS_SUCCESS) {
+ if (wpa_s->sme.sae.h2e && status_code == WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"SAE: Unexpected use of status code 0 in SAE commit when H2E was expected");
return -1;
}
- if (wpa_s->sme.sae.tmp && !wpa_s->sme.sae.tmp->h2e &&
+ if ((!wpa_s->sme.sae.h2e || wpa_s->sme.sae.pk) &&
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
wpa_printf(MSG_DEBUG,
"SAE: Unexpected use of status code for H2E in SAE commit when H2E was not expected");
return -1;
}
+ if (!wpa_s->sme.sae.pk &&
+ status_code == WLAN_STATUS_SAE_PK) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Unexpected use of status code for PK in SAE commit when PK was not expected");
+ return -1;
+ }
if (groups && groups[0] <= 0)
groups = NULL;
res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
groups, status_code ==
- WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+ WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
+ status_code == WLAN_STATUS_SAE_PK);
if (res == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message due to reflection attack");
@@ -1646,6 +1699,7 @@
{
struct wpa_driver_associate_params params;
struct ieee802_11_elems elems;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
#ifdef CONFIG_FILS
u8 nonces[2 * FILS_NONCE_LEN];
#endif /* CONFIG_FILS */
@@ -1755,8 +1809,8 @@
struct wpabuf *owe_ie;
u16 group;
- if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) {
- group = wpa_s->current_ssid->owe_group;
+ if (ssid && ssid->owe_group) {
+ group = ssid->owe_group;
} else if (wpa_s->assoc_status_code ==
WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
if (wpa_s->last_owe_group == 19)
@@ -1792,11 +1846,14 @@
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->current_ssid &&
- wpa_s->current_ssid->dpp_netaccesskey &&
- wpa_s->current_ssid->dpp_pfs != 2 &&
- !wpa_s->current_ssid->dpp_pfs_fallback) {
- struct wpa_ssid *ssid = wpa_s->current_ssid;
+ if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && ssid &&
+ ssid->dpp_netaccesskey && ssid->dpp_pfs != 2 &&
+ !ssid->dpp_pfs_fallback) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get_current(wpa_s->wpa);
+ if (!pmksa || !pmksa->dpp_pfs)
+ goto pfs_fail;
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
@@ -1823,7 +1880,43 @@
pfs_fail:
#endif /* CONFIG_DPP2 */
- if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) {
+ wpa_s->mscs_setup_done = false;
+ if (wpa_s->current_bss && wpa_s->robust_av.valid_config) {
+ struct wpabuf *mscs_ie;
+ size_t mscs_ie_len, buf_len, *wpa_ie_len, max_ie_len;
+
+ if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS))
+ goto mscs_fail;
+
+ buf_len = 3 + /* MSCS descriptor IE header */
+ 1 + /* Request type */
+ 2 + /* User priority control */
+ 4 + /* Stream timeout */
+ 3 + /* TCLAS Mask IE header */
+ wpa_s->robust_av.frame_classifier_len;
+ mscs_ie = wpabuf_alloc(buf_len);
+ if (!mscs_ie) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Failed to allocate MSCS IE");
+ goto mscs_fail;
+ }
+
+ wpa_ie_len = &wpa_s->sme.assoc_req_ie_len;
+ max_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+ wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
+ if ((*wpa_ie_len + wpabuf_len(mscs_ie)) <= max_ie_len) {
+ wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
+ mscs_ie_len = wpabuf_len(mscs_ie);
+ os_memcpy(wpa_s->sme.assoc_req_ie + *wpa_ie_len,
+ wpabuf_head(mscs_ie), mscs_ie_len);
+ *wpa_ie_len += mscs_ie_len;
+ }
+
+ wpabuf_free(mscs_ie);
+ }
+mscs_fail:
+
+ if (ssid && ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
multi_ap_ie_len = add_multi_ap_ie(
@@ -1843,8 +1936,7 @@
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
params.freq.freq = wpa_s->sme.freq;
- params.bg_scan_period = wpa_s->current_ssid ?
- wpa_s->current_ssid->bg_scan_period : -1;
+ params.bg_scan_period = ssid ? ssid->bg_scan_period : -1;
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
@@ -1860,17 +1952,17 @@
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
params.htcaps = (u8 *) &htcaps;
params.htcaps_mask = (u8 *) &htcaps_mask;
- wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, ¶ms);
+ wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
os_memset(&vhtcaps, 0, sizeof(vhtcaps));
os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
params.vhtcaps = &vhtcaps;
params.vhtcaps_mask = &vhtcaps_mask;
- wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, ¶ms);
+ wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
- wpa_supplicant_apply_he_overrides(wpa_s, wpa_s->current_ssid, ¶ms);
+ wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HE_OVERRIDES */
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies &&
@@ -1992,7 +2084,7 @@
elems.rsnxe_len + 2);
else
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
- if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
+ if (ssid && ssid->p2p_group)
params.p2p = 1;
if (wpa_s->p2pdev->set_sta_uapsd)
@@ -2570,6 +2662,16 @@
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->oci_freq_override_saquery_req) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override SA Query Request OCI frequency %d -> %d MHz",
+ ci.frequency,
+ wpa_s->oci_freq_override_saquery_req);
+ ci.frequency = wpa_s->oci_freq_override_saquery_req;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (ocv_insert_extended_oci(&ci, req + req_len) < 0)
return;
@@ -2724,6 +2826,16 @@
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->oci_freq_override_saquery_resp) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override SA Query Response OCI frequency %d -> %d MHz",
+ ci.frequency,
+ wpa_s->oci_freq_override_saquery_resp);
+ ci.frequency = wpa_s->oci_freq_override_saquery_resp;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0)
return;
@@ -2803,8 +2915,11 @@
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(wpa_s, MSG_INFO, OCV_FAILURE "addr=" MACSTR
+ " frame=saquery%s error=%s",
+ MAC2STR(sa), data[0] == WLAN_SA_QUERY_REQUEST ?
+ "req" : "resp", ocv_errorstr);
return;
}
}
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 2e09102..6996b09 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -121,6 +121,15 @@
os_free(wnmtfs_ie);
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->oci_freq_override_wnm_sleep) {
+ wpa_printf(MSG_INFO,
+ "TEST: Override OCI KDE frequency %d -> %d MHz",
+ ci.frequency,
+ wpa_s->oci_freq_override_wnm_sleep);
+ ci.frequency = wpa_s->oci_freq_override_wnm_sleep;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
@@ -374,8 +383,9 @@
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
channel_width_to_int(ci.chanwidth),
- ci.seg1_idx) != 0) {
- wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr);
+ ci.seg1_idx) != OCI_SUCCESS) {
+ wpa_msg(wpa_s, MSG_WARNING, "WNM: OCV failed: %s",
+ ocv_errorstr);
return;
}
}
@@ -1451,6 +1461,22 @@
wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
}
+#ifdef CONFIG_MBO
+ vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+ if (vendor) {
+ wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+ if (wpa_s->conf->btm_offload) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "WNM: Notify BSS Transition Management Request frame status");
+ wpa_s->bss_tm_status = WNM_BSS_TM_ACCEPT;
+ wpas_notify_bss_tm_status(wpa_s);
+ /* since it could be referenced in the scan result logic, initialize it */
+ wpa_s->wnm_mbo_trans_reason_present = 0;
+ return;
+ }
+ }
+#endif /* CONFIG_MBO */
+
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
@@ -1462,12 +1488,6 @@
}
}
-#ifdef CONFIG_MBO
- vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
- if (vendor)
- wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
-#endif /* CONFIG_MBO */
-
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
unsigned int valid_ms;
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 07d5f31..51c9642 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1706,15 +1706,25 @@
static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- if (argc < 1 || argc > 2) {
- printf("Invalid GET_CAPABILITY command: need either one or "
- "two arguments\n");
+ if (argc < 1 || argc > 3) {
+ printf("Invalid GET_CAPABILITY command: need at least one argument and max three arguments\n");
return -1;
}
- if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
- printf("Invalid GET_CAPABILITY command: second argument, "
- "if any, must be 'strict'\n");
+ if (argc > 1 && os_strcmp(argv[0], "key_mgmt") != 0 &&
+ os_strncmp(argv[1], "iftype=", 7) == 0) {
+ printf("Invalid GET_CAPABILITY command: 'iftype=' param is allowed only for 'key_mgmt'\n");
+ return -1;
+ }
+
+ if (argc == 2 && os_strcmp(argv[1], "strict") != 0 &&
+ os_strncmp(argv[1], "iftype=", 7) != 0) {
+ printf("Invalid GET_CAPABILITY command: the second argument, if any, must be 'strict' OR 'iftype=<iftype_name>'\n");
+ return -1;
+ }
+
+ if (argc == 3 && os_strcmp(argv[2], "strict") != 0) {
+ printf("Invalid GET_CAPABILITY command: the third argument, if any, must be 'strict'\n");
return -1;
}
@@ -1741,7 +1751,13 @@
"acs",
#endif /* CONFIG_ACS */
};
+ const char *iftypes[] = {
+ "iftype=STATION", "iftype=AP", "iftype=P2P_CLIENT",
+ "iftype=P2P_GO", "iftype=AP_VLAN", "iftype=IBSS", "iftype=NAN",
+ "iftype=P2P_DEVICE", "iftype=MESH",
+ };
int i, num_fields = ARRAY_SIZE(fields);
+ int num_iftypes = ARRAY_SIZE(iftypes);
char **res = NULL;
if (arg == 1) {
@@ -1755,6 +1771,21 @@
}
}
if (arg == 2) {
+ /* the second argument can be "iftype=<iftype_name>" OR
+ * "strict" */
+ res = os_calloc(num_iftypes + 2, sizeof(char *));
+ if (!res)
+ return NULL;
+ res[0] = os_strdup("strict");
+ if (!res[0])
+ return res;
+ for (i = 0; i < num_iftypes; i++) {
+ res[i + 1] = os_strdup(iftypes[i]);
+ if (!res[i + 1])
+ return res;
+ }
+ }
+ if (arg == 3) {
res = os_calloc(1 + 1, sizeof(char *));
if (res == NULL)
return NULL;
@@ -2963,6 +2994,13 @@
}
+static int wpa_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
+}
+
+
static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -3025,9 +3063,98 @@
return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
+
+#ifdef CONFIG_DPP2
+
+static int wpa_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
+}
+
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
+static int wpa_ctrl_command_bss(struct wpa_ctrl *ctrl, const char *cmd)
+{
+ char buf[512], *pos, *bssid, *freq, *level, *flags, *ssid;
+ size_t len;
+ int ret, id = -1;
+
+ if (!ctrl_conn)
+ return -1;
+ len = sizeof(buf) - 1;
+ ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+ wpa_cli_msg_cb);
+ if (ret == -2) {
+ printf("'%s' command timed out.\n", cmd);
+ return -2;
+ } else if (ret < 0) {
+ printf("'%s' command failed.\n", cmd);
+ return -1;
+ }
+
+ buf[len] = '\0';
+ if (os_memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+
+ pos = buf;
+ while (*pos != '\0') {
+ if (str_starts(pos, "id="))
+ id = atoi(pos + 3);
+ if (str_starts(pos, "bssid="))
+ bssid = pos + 6;
+ if (str_starts(pos, "freq="))
+ freq = pos + 5;
+ if (str_starts(pos, "level="))
+ level = pos + 6;
+ if (str_starts(pos, "flags="))
+ flags = pos + 6;
+ if (str_starts(pos, "ssid="))
+ ssid = pos + 5;
+
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ *pos++ = '\0';
+ }
+ if (id != -1)
+ printf("%s\t%s\t%s\t%s\t%s\n", bssid, freq, level, flags, ssid);
+ return id;
+}
+
+
+static int wpa_cli_cmd_all_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[64];
+ int id = -1;
+ unsigned int mask;
+
+ printf("bssid / frequency / signal level / flags / ssid\n");
+
+ mask = WPA_BSS_MASK_ID | WPA_BSS_MASK_BSSID | WPA_BSS_MASK_FREQ |
+ WPA_BSS_MASK_LEVEL | WPA_BSS_MASK_FLAGS | WPA_BSS_MASK_SSID;
+ do {
+ if (id < 0)
+ os_snprintf(cmd, sizeof(cmd), "BSS FIRST MASK=0x%x",
+ mask);
+ else
+ os_snprintf(cmd, sizeof(cmd), "BSS NEXT-%d MASK=0x%x",
+ id, mask);
+ id = wpa_ctrl_command_bss(ctrl, cmd);
+ } while (id >= 0);
+
+ return 0;
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -3661,6 +3788,9 @@
{ "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
cli_cmd_flag_none,
"<id> = show DPP bootstrap information" },
+ { "dpp_bootstrap_set", wpa_cli_cmd_dpp_bootstrap_set, NULL,
+ cli_cmd_flag_none,
+ "<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
{ "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
"peer=<id> [own=<id>] = initiate DPP bootstrapping" },
{ "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
@@ -3686,7 +3816,17 @@
{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP pkex information" },
+#ifdef CONFIG_DPP2
+ { "dpp_chirp", wpa_cli_cmd_dpp_chirp, NULL,
+ cli_cmd_flag_none,
+ "own=<BI ID> iter=<count> = start DPP chirp" },
+ { "dpp_stop_chirp", wpa_cli_cmd_dpp_stop_chirp, NULL,
+ cli_cmd_flag_none,
+ "= stop DPP chirp" },
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
+ { "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
+ "= list all BSS entries (scan results)" },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/.gitignore b/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
new file mode 100644
index 0000000..8d772cc
--- /dev/null
+++ b/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
@@ -0,0 +1,2 @@
+hicolor
+pixmaps
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile
index 709514c..88efc3c 100644
--- a/wpa_supplicant/wpa_gui-qt4/icons/Makefile
+++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile
@@ -5,14 +5,28 @@
ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name))))
ICONS += $(addsuffix .xpm, $(NAMES))
+ifeq (1, $(shell which inkscape; echo $$?))
+$(error "No inkscape in PATH, it is required for exporting icons.")
+else
+ifeq (0, $(shell inkscape --without-gui 2>&1 > /dev/null; echo $$?))
+# Inkscape < 1.0
+INKSCAPE_GUI_FLAG := --without-gui
+INKSCAPE_OUTPUT_FLAG := --export-png
+else
+# Inkscape >= 1.0
+INKSCAPE_GUI_FLAG :=
+INKSCAPE_OUTPUT_FLAG := --export-filename
+endif
+endif
+
all: $(ICONS)
%.png:
mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/
- inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \
+ inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) $(INKSCAPE_GUI_FLAG) \
--export-width=$(word 1, $(subst x, , $(@))) \
--export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \
- --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
+ $(INKSCAPE_OUTPUT_FLAG)=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
%.xpm:
mkdir -p pixmaps/
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index ea62e59..6ab5485 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -223,10 +223,10 @@
wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
MAC2STR(bssid));
wpa_blacklist_add(wpa_s, bssid);
+ wpas_notify_auth_timeout(wpa_s);
wpa_sm_notify_disassoc(wpa_s->wpa);
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_s->reassociate = 1;
- wpas_notify_auth_timeout(wpa_s);
/*
* If we timed out, the AP or the local radio may be busy.
@@ -804,7 +804,22 @@
#ifdef CONFIG_BGSCAN
-static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->bgscan_ssid) {
+ bgscan_deinit(wpa_s);
+ wpa_s->bgscan_ssid = NULL;
+ }
+}
+
+
+/**
+ * wpa_supplicant_reset_bgscan - Reset the bgscan for the current SSID.
+ * @wpa_s: Pointer to the wpa_supplicant data
+ *
+ * Stop, start, or reconfigure the scan parameters depending on the method.
+ */
+void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s)
{
const char *name;
@@ -812,12 +827,12 @@
name = wpa_s->current_ssid->bgscan;
else
name = wpa_s->conf->bgscan;
- if (name == NULL || name[0] == '\0')
+ if (!name || name[0] == '\0') {
+ wpa_supplicant_stop_bgscan(wpa_s);
return;
+ }
if (wpas_driver_bss_selection(wpa_s))
return;
- if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
- return;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
return;
@@ -847,15 +862,6 @@
wpa_s->bgscan_ssid = NULL;
}
-
-static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
-{
- if (wpa_s->bgscan_ssid != NULL) {
- bgscan_deinit(wpa_s);
- wpa_s->bgscan_ssid = NULL;
- }
-}
-
#endif /* CONFIG_BGSCAN */
@@ -979,8 +985,7 @@
fils_hlp_sent ? " FILS_HLP_SENT" : "");
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
wpas_clear_temp_disabled(wpa_s, ssid, 1);
- wpa_blacklist_clear(wpa_s);
- wpa_s->extra_blacklist_count = 0;
+ wpa_s->consecutive_conn_failures = 0;
wpa_s->new_connection = 0;
wpa_drv_set_operstate(wpa_s, 1);
#ifndef IEEE8021X_EAPOL
@@ -1012,8 +1017,8 @@
wpa_s->wpa_state = state;
#ifdef CONFIG_BGSCAN
- if (state == WPA_COMPLETED)
- wpa_supplicant_start_bgscan(wpa_s);
+ if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
+ wpa_supplicant_reset_bgscan(wpa_s);
else if (state < WPA_ASSOCIATED)
wpa_supplicant_stop_bgscan(wpa_s);
#endif /* CONFIG_BGSCAN */
@@ -1185,6 +1190,7 @@
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
+ wpa_blacklist_clear(wpa_s);
wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
return 0;
}
@@ -1640,9 +1646,26 @@
if (ssid->sae_password_id && sae_pwe != 3)
sae_pwe = 1;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
+#ifdef CONFIG_SAE_PK
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
+ wpa_key_mgmt_sae(ssid->key_mgmt) &&
+ ssid->sae_pk != SAE_PK_MODE_DISABLED &&
+ ((ssid->sae_password &&
+ sae_pk_valid_password(ssid->sae_password)) ||
+ (!ssid->sae_password && ssid->passphrase &&
+ sae_pk_valid_password(ssid->passphrase))));
+#endif /* CONFIG_SAE_PK */
#ifdef CONFIG_TESTING_OPTIONS
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
wpa_s->ft_rsnxe_used);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL,
+ wpa_s->oci_freq_override_eapol);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
+ wpa_s->oci_freq_override_eapol_g2);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
+ wpa_s->oci_freq_override_ft_assoc);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC,
+ wpa_s->oci_freq_override_fils_assoc);
#endif /* CONFIG_TESTING_OPTIONS */
/* Extended Key ID is only supported in infrastructure BSS so far */
@@ -1686,6 +1709,9 @@
} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
/* Use PMK from DPP network introduction (PMKSA entry) */
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+#ifdef CONFIG_DPP2
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DPP_PFS, ssid->dpp_pfs);
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
} else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
int psk_set = 0;
@@ -1880,6 +1906,9 @@
*pos |= 0x01;
#endif /* CONFIG_FILS */
break;
+ case 10: /* Bits 80-87 */
+ *pos |= 0x20; /* Bit 85 - Mirrored SCS */
+ break;
}
}
@@ -1887,7 +1916,7 @@
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
{
u8 *pos = buf;
- u8 len = 10, i;
+ u8 len = 11, i;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
@@ -2057,7 +2086,9 @@
if (!password)
password = ssid->passphrase;
- if ((conf->sae_pwe == 0 && !ssid->sae_password_id) || !password ||
+ if (!password ||
+ (conf->sae_pwe == 0 && !ssid->sae_password_id &&
+ !sae_pk_valid_password(password)) ||
conf->sae_pwe == 3) {
/* PT derivation not needed */
sae_deinit_pt(ssid->pt);
@@ -2739,9 +2770,9 @@
#ifdef CONFIG_MBO
const u8 *mbo_ie;
#endif
-#ifdef CONFIG_SAE
- int sae_pmksa_cached = 0;
-#endif /* CONFIG_SAE */
+#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
+ int pmksa_cached = 0;
+#endif /* CONFIG_SAE || CONFIG_FILS */
#ifdef CONFIG_FILS
const u8 *realm, *username, *rrk;
size_t realm_len, username_len, rrk_len;
@@ -2781,9 +2812,9 @@
ssid, try_opportunistic,
cache_id, 0) == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
-#ifdef CONFIG_SAE
- sae_pmksa_cached = 1;
-#endif /* CONFIG_SAE */
+#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
+ pmksa_cached = 1;
+#endif /* CONFIG_SAE || CONFIG_FILS */
}
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -2882,6 +2913,10 @@
if (mask)
*mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
+ ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
+ pmksa_cached) {
+ algs = WPA_AUTH_ALG_FILS;
}
#endif /* CONFIG_FILS */
#endif /* IEEE8021X_EAPOL */
@@ -2898,7 +2933,7 @@
}
#ifdef CONFIG_SAE
- if (sae_pmksa_cached && algs == WPA_AUTH_ALG_SAE) {
+ if (pmksa_cached && algs == WPA_AUTH_ALG_SAE) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt");
algs = WPA_AUTH_ALG_OPEN;
@@ -3085,9 +3120,16 @@
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
- if (wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP &&
+ if (DPP_VERSION > 1 &&
+ wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP &&
ssid->dpp_netaccesskey &&
ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = pmksa_cache_get_current(wpa_s->wpa);
+ if (!pmksa || !pmksa->dpp_pfs)
+ goto pfs_fail;
+
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
ssid->dpp_netaccesskey_len);
@@ -3157,6 +3199,39 @@
wpa_ie_len += wpa_s->rsnxe_len;
}
+ if (bss && wpa_s->robust_av.valid_config) {
+ struct wpabuf *mscs_ie;
+ size_t mscs_ie_len, buf_len;
+
+ if (!wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS))
+ goto mscs_fail;
+
+ buf_len = 3 + /* MSCS descriptor IE header */
+ 1 + /* Request type */
+ 2 + /* User priority control */
+ 4 + /* Stream timeout */
+ 3 + /* TCLAS Mask IE header */
+ wpa_s->robust_av.frame_classifier_len;
+ mscs_ie = wpabuf_alloc(buf_len);
+ if (!mscs_ie) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Failed to allocate MSCS IE");
+ goto mscs_fail;
+ }
+
+ wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
+ if ((wpa_ie_len + wpabuf_len(mscs_ie)) <= max_wpa_ie_len) {
+ wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
+ mscs_ie_len = wpabuf_len(mscs_ie);
+ os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(mscs_ie),
+ mscs_ie_len);
+ wpa_ie_len += mscs_ie_len;
+ }
+
+ wpabuf_free(mscs_ie);
+ }
+mscs_fail:
+
if (ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
@@ -3471,6 +3546,7 @@
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
wpa_s->rsnxe_len = 0;
+ wpa_s->mscs_setup_done = false;
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL);
if (!wpa_ie) {
@@ -3768,6 +3844,8 @@
* succeed.
*/
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_s->assoc_status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpas_notify_assoc_status_code(wpa_s, wpa_s->pending_bssid, 0);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
@@ -4048,6 +4126,52 @@
/**
+ * wpa_supplicant_remove_all_networks - Remove all configured networks
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: 0 on success (errors are currently ignored)
+ *
+ * This function performs the following operations:
+ * 1. Remove all networks.
+ * 2. Send network removal notifications.
+ * 3. Update internal state machines.
+ * 4. Stop any running sched scans.
+ */
+int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->sched_scanning)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ if (wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ struct wpa_ssid *remove_ssid = ssid;
+ int id;
+
+ id = ssid->id;
+ ssid = ssid->next;
+ if (wpa_s->last_ssid == remove_ssid)
+ wpa_s->last_ssid = NULL;
+ wpas_notify_network_removed(wpa_s, remove_ssid);
+ wpa_config_remove_network(wpa_s->conf, id);
+ }
+ return 0;
+}
+
+
+/**
* wpa_supplicant_enable_network - Mark a configured network as enabled
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL
@@ -4837,6 +4961,65 @@
}
+int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
+ const char *bridge_ifname)
+{
+ if (wpa_s->wpa_state > WPA_SCANNING)
+ return -EBUSY;
+
+ if (bridge_ifname &&
+ os_strlen(bridge_ifname) >= sizeof(wpa_s->bridge_ifname))
+ return -EINVAL;
+
+ if (!bridge_ifname)
+ bridge_ifname = "";
+
+ if (os_strcmp(wpa_s->bridge_ifname, bridge_ifname) == 0)
+ return 0;
+
+ if (wpa_s->l2_br) {
+ l2_packet_deinit(wpa_s->l2_br);
+ wpa_s->l2_br = NULL;
+ }
+
+ os_strlcpy(wpa_s->bridge_ifname, bridge_ifname,
+ sizeof(wpa_s->bridge_ifname));
+
+ if (wpa_s->bridge_ifname[0]) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Receiving packets from bridge interface '%s'",
+ wpa_s->bridge_ifname);
+ wpa_s->l2_br = l2_packet_init_bridge(
+ wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+ ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
+ if (!wpa_s->l2_br) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "Failed to open l2_packet connection for the bridge interface '%s'",
+ wpa_s->bridge_ifname);
+ goto fail;
+ }
+ }
+
+#ifdef CONFIG_TDLS
+ if (!wpa_s->p2p_mgmt && wpa_tdls_init(wpa_s->wpa))
+ goto fail;
+#endif /* CONFIG_TDLS */
+
+ return 0;
+fail:
+ wpa_s->bridge_ifname[0] = 0;
+ if (wpa_s->l2_br) {
+ l2_packet_deinit(wpa_s->l2_br);
+ wpa_s->l2_br = NULL;
+ }
+#ifdef CONFIG_TDLS
+ if (!wpa_s->p2p_mgmt)
+ wpa_tdls_init(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+ return -EIO;
+}
+
+
/**
* wpa_supplicant_driver_init - Initialize driver interface parameters
* @wpa_s: Pointer to wpa_supplicant data
@@ -6020,6 +6203,8 @@
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
if (wpa_s->drv_priv == NULL) {
const char *pos;
+ int level = MSG_ERROR;
+
pos = driver ? os_strchr(driver, ',') : NULL;
if (pos) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
@@ -6027,8 +6212,12 @@
driver = pos + 1;
goto next_driver;
}
- wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
- "interface");
+
+#ifdef CONFIG_MATCH_IFACE
+ if (wpa_s->matched == WPA_IFACE_MATCHED_NULL)
+ level = MSG_DEBUG;
+#endif /* CONFIG_MATCH_IFACE */
+ wpa_msg(wpa_s, level, "Failed to initialize driver interface");
return -1;
}
if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
@@ -6173,6 +6362,9 @@
return -1;
}
os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
+#ifdef CONFIG_MATCH_IFACE
+ wpa_s->matched = iface->matched;
+#endif /* CONFIG_MATCH_IFACE */
if (iface->bridge_ifname) {
if (os_strlen(iface->bridge_ifname) >=
@@ -6564,6 +6756,10 @@
if (!iface)
return NULL;
*iface = *miface;
+ if (!miface->ifname)
+ iface->matched = WPA_IFACE_MATCHED_NULL;
+ else
+ iface->matched = WPA_IFACE_MATCHED;
iface->ifname = ifname;
return iface;
}
@@ -6598,8 +6794,6 @@
if (iface) {
wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
os_free(iface);
- if (wpa_s)
- wpa_s->matched = 1;
}
}
@@ -7129,6 +7323,18 @@
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM)
wpa_supplicant_set_default_scan_ies(wpa_s);
+#ifdef CONFIG_BGSCAN
+ /*
+ * We default to global bgscan parameters only when per-network bgscan
+ * parameters aren't set. Only bother resetting bgscan parameters if
+ * this is the case.
+ */
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_BGSCAN) &&
+ wpa_s->current_ssid && !wpa_s->current_ssid->bgscan &&
+ wpa_s->wpa_state == WPA_COMPLETED)
+ wpa_supplicant_reset_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
@@ -7169,7 +7375,7 @@
continue;
if (bss->ssid_len == cbss->ssid_len &&
os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
- wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+ !wpa_blacklist_is_blacklisted(wpa_s, bss->bssid)) {
add_freq(freqs, &num_freqs, bss->freq);
if (num_freqs == max_freqs)
break;
@@ -7223,10 +7429,6 @@
/*
* Add the failed BSSID into the blacklist and speed up next scan
* attempt if there could be other APs that could accept association.
- * The current blacklist count indicates how many times we have tried
- * connecting to this AP and multiple attempts mean that other APs are
- * either not available or has already been tried, so that we can start
- * increasing the delay here to avoid constant scanning.
*/
count = wpa_blacklist_add(wpa_s, bssid);
if (count == 1 && wpa_s->current_bss) {
@@ -7251,19 +7453,19 @@
}
}
- /*
- * Add previous failure count in case the temporary blacklist was
- * cleared due to no other BSSes being available.
- */
- count += wpa_s->extra_blacklist_count;
+ wpa_s->consecutive_conn_failures++;
- if (count > 3 && wpa_s->current_ssid) {
+ if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "Continuous association failures - "
"consider temporary network disabling");
wpas_auth_failed(wpa_s, "CONN_FAILED");
}
-
- switch (count) {
+ /*
+ * Multiple consecutive connection failures mean that other APs are
+ * either not available or have already been tried, so we can start
+ * increasing the delay here to avoid constant scanning.
+ */
+ switch (wpa_s->consecutive_conn_failures) {
case 1:
timeout = 100;
break;
@@ -7281,8 +7483,9 @@
break;
}
- wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
- "ms", count, timeout);
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Consecutive connection failures: %d --> request scan in %d ms",
+ wpa_s->consecutive_conn_failures, timeout);
/*
* TODO: if more than one possible AP is available in scan results,
@@ -7681,7 +7884,6 @@
wpa_s->normal_scans = 0;
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_reinit_autoscan(wpa_s);
- wpa_s->extra_blacklist_count = 0;
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->last_owe_group = 0;
@@ -7947,7 +8149,7 @@
ETH_ALEN);
num_bssid++;
}
- ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids);
+ ret = wpa_drv_set_bssid_tmp_disallow(wpa_s, num_bssid, bssids);
os_free(bssids);
return ret;
}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 3b90567..2f1e31b 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -843,6 +843,16 @@
# bssid: BSSID (optional); if set, this network block is used only when
# associating with the AP using the configured BSSID
#
+# ignore_broadcast_ssid: SSID broadcast behavior
+# Send empty SSID in beacons and ignore probe request frames that do not
+# specify full SSID, i.e., require stations to know SSID.
+# default: disabled (0)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+# broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+# with some clients that do not support empty SSID) and ignore probe
+# requests for broadcast SSID
+#
# priority: priority group (integer)
# By default, all networks will get same priority group (0). If some of the
# networks are more desirable, this field can be used to change the order in
@@ -1472,6 +1482,58 @@
# 2: do not allow PFS to be used
#dpp_pfs=0
+# Whether Beacon protection is enabled
+# This depends on management frame protection (ieee80211w) being enabled.
+#beacon_prot=0
+
+# OWE DH Group
+# 0: use default (19) first and then try all supported groups one by one if AP
+# rejects the selected group
+# 1-65535: DH Group to use for OWE
+# Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
+# currently supported.
+#owe_group=0
+
+# OWE-only mode (disable transition mode)
+# 0: enable transition mode (allow connection to either OWE or open BSS)
+# 1 = disable transition mode (allow connection only with OWE)
+#owe_only=0
+
+# OWE PTK derivation workaround
+# Initial OWE implementation used SHA256 when deriving the PTK for all
+# OWE groups. This was supposed to change to SHA384 for group 20 and
+# SHA512 for group 21. This parameter can be used to enable older
+# behavior mainly for testing purposes. There is no impact to group 19
+# behavior, but if enabled, this will make group 20 and 21 cases use
+# SHA256-based PTK derivation which will not work with the updated
+# OWE implementation on the AP side.
+#owe_ptk_workaround=0
+
+# Transition Disable indication
+# The AP can notify authenticated stations to disable transition mode
+# in their network profiles when the network has completed transition
+# steps, i.e., once sufficiently large number of APs in the ESS have
+# been updated to support the more secure alternative. When this
+# indication is used, the stations are expected to automatically
+# disable transition mode and less secure security options. This
+# includes use of WEP, TKIP (including use of TKIP as the group
+# cipher), and connections without PMF.
+# Bitmap bits:
+# bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK
+# and only allow SAE to be used)
+# bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
+# bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
+# bit 3 (0x08): Enhanced Open (disable use of open network; require
+# OWE)
+
+# SAE-PK mode
+# 0: automatic SAE/SAE-PK selection based on password; enable
+# transition mode (allow SAE authentication without SAE-PK)
+# 1: SAE-PK only (disable transition mode; allow SAE authentication
+# only with SAE-PK)
+# 2: disable SAE-PK (allow SAE authentication only without SAE-PK)
+#sae_pk=0
+
# MAC address policy
# 0 = use permanent MAC address
# 1 = use random MAC address for each ESS connection
@@ -1584,6 +1646,12 @@
# Set to 1 to disable BSS transition management
#disable_btm=0
+# This value is used to set where to perform roaming logic
+# Set to 0 to handle roaming logic fully in supplicant
+# Set to 1 to skip roaming logic in supplicant and handle it in firmware
+# In supplicant, just parse BTM frame and notify framework
+#btm_offload=0
+
# Enable EDMG capability in STA/AP mode, default value is false
#enable_edmg=1
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 2f95eeb..3fcd95a 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -121,6 +121,18 @@
* interface that is not a network interface.
*/
int p2p_mgmt;
+
+#ifdef CONFIG_MATCH_IFACE
+ /**
+ * matched - Interface was matched rather than specified
+ *
+ */
+ enum {
+ WPA_IFACE_NOT_MATCHED,
+ WPA_IFACE_MATCHED_NULL,
+ WPA_IFACE_MATCHED
+ } matched;
+#endif /* CONFIG_MATCH_IFACE */
};
/**
@@ -491,6 +503,17 @@
int scan_level;
};
+struct robust_av_data {
+ u8 dialog_token;
+ enum scs_request_type request_type;
+ u8 up_bitmap;
+ u8 up_limit;
+ u32 stream_timeout;
+ u8 frame_classifier[48];
+ size_t frame_classifier_len;
+ bool valid_config;
+};
+
/**
* struct wpa_supplicant - Internal data for wpa_supplicant interface
*
@@ -643,17 +666,8 @@
struct wpa_blacklist *blacklist;
- /**
- * extra_blacklist_count - Sum of blacklist counts after last connection
- *
- * This variable is used to maintain a count of temporary blacklisting
- * failures (maximum number for any BSS) over blacklist clear
- * operations. This is needed for figuring out whether there has been
- * failures prior to the last blacklist clear operation which happens
- * whenever no other not-blacklisted BSS candidates are available. This
- * gets cleared whenever a connection has been established successfully.
- */
- int extra_blacklist_count;
+ /* Number of connection failures since last successful connection */
+ unsigned int consecutive_conn_failures;
/**
* scan_req - Type of the scan request
@@ -779,6 +793,7 @@
unsigned int connection_max_nss_tx:4;
unsigned int connection_channel_bandwidth:5;
unsigned int disable_mbo_oce:1;
+ unsigned int connection_11b_only:1;
struct os_reltime last_mac_addr_change;
int last_mac_addr_style;
@@ -1064,8 +1079,6 @@
/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
u16 assoc_status_code;
- /* Indicates if the previous association request timed out. */
- u8 assoc_timed_out;
struct ext_password_data *ext_pw;
@@ -1158,6 +1171,13 @@
struct wpabuf *rsnxe_override_assoc;
struct wpabuf *rsnxe_override_eapol;
struct dl_list drv_signal_override;
+ unsigned int oci_freq_override_eapol;
+ unsigned int oci_freq_override_saquery_req;
+ unsigned int oci_freq_override_saquery_resp;
+ unsigned int oci_freq_override_eapol_g2;
+ unsigned int oci_freq_override_ft_assoc;
+ unsigned int oci_freq_override_fils_assoc;
+ unsigned int oci_freq_override_wnm_sleep;
#endif /* CONFIG_TESTING_OPTIONS */
struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1258,6 +1278,7 @@
struct wpa_radio_work *dpp_listen_work;
unsigned int dpp_pending_listen_freq;
unsigned int dpp_listen_freq;
+ struct os_reltime dpp_listen_end;
u8 dpp_allowed_roles;
int dpp_qr_mutual;
int dpp_netrole;
@@ -1282,6 +1303,7 @@
unsigned int dpp_resp_retry_time;
u8 dpp_last_ssid[SSID_MAX_LEN];
size_t dpp_last_ssid_len;
+ bool dpp_conf_backup_received;
#ifdef CONFIG_DPP2
struct dpp_pfs *dpp_pfs;
int dpp_pfs_fallback;
@@ -1293,6 +1315,9 @@
int dpp_chirp_round;
int dpp_chirp_scan_done;
int dpp_chirp_listen;
+ struct wpa_ssid *dpp_reconfig_ssid;
+ int dpp_reconfig_ssid_id;
+ struct dpp_reconfig_id *dpp_reconfig_id;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
@@ -1312,6 +1337,8 @@
unsigned int multi_ap_ie:1;
unsigned int multi_ap_backhaul:1;
unsigned int multi_ap_fronthaul:1;
+ struct robust_av_data robust_av;
+ bool mscs_setup_done;
};
@@ -1335,6 +1362,8 @@
const char * wpa_supplicant_state_txt(enum wpa_states state);
int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
+ const char *bridge_ifname);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
u8 *wpa_ie, size_t *wpa_ie_len);
@@ -1360,6 +1389,7 @@
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
+int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s);
void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
@@ -1447,6 +1477,7 @@
void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s);
void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx);
+void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s);
/* MBO functions */
@@ -1539,6 +1570,9 @@
int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s,
struct channel_list_changed *info);
+int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *current_bss,
+ struct wpa_bss *seleceted);
/* eap_register.c */
int eap_register_methods(void);
@@ -1630,4 +1664,13 @@
void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s);
+int wpas_send_mscs_req(struct wpa_supplicant *wpa_s);
+void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
+ struct wpabuf *buf);
+void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *buf,
+ size_t len);
+void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *ies, size_t ies_len);
+
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index fce7e5e..7a558f3 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -6,3 +6,4 @@
pmf=1
p2p_add_cli_chan=1
oce=1
+sae_pwe=2
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 6bd271e..4462afc 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -575,7 +575,8 @@
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len,
- u32 pmk_lifetime, u8 pmk_reauth_threshold)
+ u32 pmk_lifetime, u8 pmk_reauth_threshold,
+ int akmp)
{
struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
@@ -583,9 +584,22 @@
os_memset(¶ms, 0, sizeof(params));
ssid = wpas_get_network_ctx(wpa_s, network_ctx);
- if (ssid)
+ if (ssid) {
wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
MAC2STR(bssid), ssid->id);
+ if ((akmp == WPA_KEY_MGMT_FT_IEEE8021X ||
+ akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
+ !ssid->ft_eap_pmksa_caching) {
+ /* Since we will not be using PMKSA caching for FT-EAP
+ * within wpa_supplicant to avoid known interop issues
+ * with APs, do not add this PMKID to the driver either
+ * so that we won't be hitting those interop issues
+ * with driver-based RSNE generation. */
+ wpa_printf(MSG_DEBUG,
+ "FT: Do not add PMKID entry to the driver since FT-EAP PMKSA caching is not enabled in configuration");
+ return 0;
+ }
+ }
if (ssid && fils_cache_id) {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
@@ -1268,6 +1282,7 @@
if (!ssid)
return;
+#ifdef CONFIG_SAE
if ((bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) &&
wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
@@ -1279,6 +1294,24 @@
changed = 1;
}
+ if ((bitmap & TRANSITION_DISABLE_SAE_PK) &&
+ wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
+#ifdef CONFIG_SME
+ wpa_s->sme.sae.state == SAE_ACCEPTED &&
+ wpa_s->sme.sae.pk &&
+#endif /* CONFIG_SME */
+ (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
+ (ssid->sae_pk != SAE_PK_MODE_ONLY ||
+ ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
+ (ssid->group_cipher & WPA_CIPHER_TKIP))) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: SAE authentication without PK disabled based on AP notification");
+ disable_wpa_wpa2(ssid);
+ ssid->sae_pk = SAE_PK_MODE_ONLY;
+ changed = 1;
+ }
+#endif /* CONFIG_SAE */
+
if ((bitmap & TRANSITION_DISABLE_WPA3_ENTERPRISE) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X |
@@ -1298,6 +1331,8 @@
changed = 1;
}
+ wpas_notify_transition_disable(wpa_s, ssid, bitmap);
+
if (!changed)
return;
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index 4e37591..1c136f7 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -59,6 +59,23 @@
wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
goto fail;
+ wpa_blacklist_clear(&wpa_s);
+
+ if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+ !wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111") ||
+ wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+ wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111") ||
+ wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0)
+ goto fail;
+
+ wpa_blacklist_update(&wpa_s);
+
+ if (!wpa_blacklist_is_blacklisted(&wpa_s, (u8 *) "111111"))
+ goto fail;
+
ret = 0;
fail:
wpa_blacklist_clear(&wpa_s);
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 130c278..47d85dc 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -188,6 +188,7 @@
const u8 *ie;
struct wpa_ie_data adv;
int wpa2 = 0, ccmp = 0;
+ enum wpa_driver_if_type iftype;
/*
* Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
@@ -239,9 +240,12 @@
return;
}
+ iftype = ssid->p2p_group ? WPA_IF_P2P_CLIENT : WPA_IF_STATION;
+
if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
(ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
- (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+ (capa.key_mgmt_iftype[iftype] &
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
"based on scan results");
if (wpa_s->conf->ap_scan == 1)
@@ -716,7 +720,7 @@
wpas_notify_wps_event_success(wpa_s);
if (wpa_s->current_ssid)
wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
- wpa_s->extra_blacklist_count = 0;
+ wpa_s->consecutive_conn_failures = 0;
/*
* Enable the networks disabled during wpas_wps_reassoc after 10